shellEngine.c 26.7 KB
Newer Older
H
hzcheng 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
 *
 * This program is free software: you can use, redistribute, and/or modify
 * it under the terms of the GNU Affero General Public License, version 3
 * or later ("AGPL"), as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

16 17
#define _BSD_SOURCE
#define _GNU_SOURCE
H
hzcheng 已提交
18
#define _XOPEN_SOURCE
S
slguan 已提交
19
#define _DEFAULT_SOURCE
H
hzcheng 已提交
20

S
slguan 已提交
21
#include "os.h"
H
hzcheng 已提交
22 23
#include "shell.h"
#include "shellCommand.h"
H
hzcheng 已提交
24
#include "taosdef.h"
S
slguan 已提交
25
#include "taoserror.h"
S
slguan 已提交
26
#include "tglobal.h"
S
Shengliang Guan 已提交
27 28
#include "ttypes.h"
#include "tutil.h"
H
Haojun Liao 已提交
29

30
#include <regex.h>
S
Shengliang Guan 已提交
31
#include <wordexp.h>
H
hzcheng 已提交
32 33

/**************** Global variables ****************/
H
Hui Li 已提交
34
#ifdef _TD_POWER_
H
Hui Li 已提交
35
char      CLIENT_VERSION[] = "Welcome to the PowerDB shell from %s, Client Version:%s\n"
36
                             "Copyright (c) 2020 by PowerDB, Inc. All rights reserved.\n\n";
H
Hui Li 已提交
37 38 39 40
char      PROMPT_HEADER[] = "power> ";

char      CONTINUE_PROMPT[] = "    -> ";
int       prompt_size = 7;
41 42 43 44 45 46 47
#elif (_TD_TQ_ == true)
char      CLIENT_VERSION[] = "Welcome to the TQ shell from %s, Client Version:%s\n"
                             "Copyright (c) 2020 by TQ, Inc. All rights reserved.\n\n";
char      PROMPT_HEADER[] = "tq> ";

char      CONTINUE_PROMPT[] = "    -> ";
int       prompt_size = 4;
H
Hui Li 已提交
48
#else
S
slguan 已提交
49
char      CLIENT_VERSION[] = "Welcome to the TDengine shell from %s, Client Version:%s\n"
50
                             "Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n";
51
char      PROMPT_HEADER[] = "taos> ";
H
Hui Li 已提交
52

53 54
char      CONTINUE_PROMPT[] = "   -> ";
int       prompt_size = 6;
H
Hui Li 已提交
55 56
#endif

D
fix bug  
dapan1121 已提交
57
int64_t result = 0;
58
SShellHistory   history;
H
hzcheng 已提交
59

60 61
#define DEFAULT_MAX_BINARY_DISPLAY_WIDTH 30
extern int32_t tsMaxBinaryDisplayWidth;
S
Shengliang Guan 已提交
62
extern TAOS   *taos_connect_auth(const char *ip, const char *user, const char *auth, const char *db, uint16_t port);
63

H
hzcheng 已提交
64 65 66
/*
 * FUNCTION: Initialize the shell.
 */
67
TAOS *shellInit(SShellArguments *_args) {
68
  printf("\n");
69 70 71 72 73 74 75 76 77
  if (!_args->is_use_passwd) {
#ifdef TD_WINDOWS
    strcpy(tsOsName, "Windows");
#elif defined(TD_DARWIN)
    strcpy(tsOsName, "Darwin");
#endif
    printf(CLIENT_VERSION, tsOsName, taos_get_client_info());
  }

78 79
  fflush(stdout);

H
hzcheng 已提交
80
  // set options before initializing
81 82
  if (_args->timezone != NULL) {
    taos_options(TSDB_OPTION_TIMEZONE, _args->timezone);
H
hzcheng 已提交
83 84
  }

85
  if (!_args->is_use_passwd) {
86
    _args->password = TSDB_DEFAULT_PASS;
H
hzcheng 已提交
87 88
  }

89 90
  if (_args->user == NULL) {
    _args->user = TSDB_DEFAULT_USER;
H
hzcheng 已提交
91 92 93
  }

  // Connect to the database.
94
  TAOS *con = NULL;
95 96
  if (_args->auth == NULL) {
    con = taos_connect(_args->host, _args->user, _args->password, _args->database, _args->port);
97
  } else {
98
    con = taos_connect_auth(_args->host, _args->user, _args->auth, _args->database, _args->port);
99 100
  }

H
hzcheng 已提交
101
  if (con == NULL) {
H
Hui Li 已提交
102
    fflush(stdout);
H
hzcheng 已提交
103 104 105 106 107 108 109
    return con;
  }

  /* Read history TODO : release resources here*/
  read_history();

  // Check if it is temperory run
110 111 112 113
  if (_args->commands != NULL || _args->file[0] != 0) {
    if (_args->commands != NULL) {
      printf("%s%s\n", PROMPT_HEADER, _args->commands);
      shellRunCommand(con, _args->commands);
H
hzcheng 已提交
114 115
    }

116 117
    if (_args->file[0] != 0) {
      source_file(con, _args->file);
H
hzcheng 已提交
118 119
    }

120
    taos_close(con);
H
hzcheng 已提交
121 122 123 124
    write_history();
    exit(EXIT_SUCCESS);
  }

S
Shengliang Guan 已提交
125
#if 0
S
slguan 已提交
126
#ifndef WINDOWS
127 128
  if (_args->dir[0] != 0) {
    source_dir(con, _args);
S
#928  
slguan 已提交
129 130 131
    taos_close(con);
    exit(EXIT_SUCCESS);
  }
S
Shengliang Guan 已提交
132

133 134
  if (_args->check != 0) {
    shellCheck(con, _args);
S
Shengliang Guan 已提交
135 136 137
    taos_close(con);
    exit(EXIT_SUCCESS);
  }
S
Shengliang Guan 已提交
138
#endif
S
#928  
slguan 已提交
139 140
#endif

H
hzcheng 已提交
141 142 143
  return con;
}

S
Shengliang Guan 已提交
144
static bool isEmptyCommand(const char *cmd) {
145 146 147
  for (char c = *cmd++; c != 0; c = *cmd++) {
    if (c != ' ' && c != '\t' && c != ';') {
      return false;
H
hzcheng 已提交
148 149
    }
  }
150
  return true;
H
hzcheng 已提交
151 152
}

153
static int32_t shellRunSingleCommand(TAOS *con, char *command) {
H
hzcheng 已提交
154
  /* If command is empty just return */
155
  if (isEmptyCommand(command)) {
156
    return 0;
H
hzcheng 已提交
157 158 159 160 161 162
  }

  // Analyse the command.
  if (regex_match(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
    taos_close(con);
    write_history();
S
Shengliang Guan 已提交
163 164 165
#ifdef WINDOWS
    exit(EXIT_SUCCESS);
#endif
166
    return -1;
167 168 169
  }

  if (regex_match(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
170 171
    // If clear the screen.
    system("clear");
172 173
    return 0;
  }
174

S
Shengliang Guan 已提交
175 176
  if (regex_match(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$",
                  REG_EXTENDED | REG_ICASE)) {
177 178
    strtok(command, " \t");
    strtok(NULL, " \t");
S
Shengliang Guan 已提交
179
    char *p = strtok(NULL, " \t");
180 181 182 183 184
    if (strcasecmp(p, "default") == 0) {
      tsMaxBinaryDisplayWidth = DEFAULT_MAX_BINARY_DISPLAY_WIDTH;
    } else {
      tsMaxBinaryDisplayWidth = atoi(p);
    }
185 186
    return 0;
  }
187

188
  if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
189 190 191 192 193 194
    /* If source file. */
    char *c_ptr = strtok(command, " ;");
    assert(c_ptr != NULL);
    c_ptr = strtok(NULL, " ;");
    assert(c_ptr != NULL);
    source_file(con, c_ptr);
195
    return 0;
H
hzcheng 已提交
196
  }
197 198

  shellRunCommandOnServer(con, command);
199
  return 0;
H
hzcheng 已提交
200 201
}

S
Shengliang Guan 已提交
202
int32_t shellRunCommand(TAOS *con, char *command) {
203 204 205 206 207 208 209 210 211 212
  /* If command is empty just return */
  if (isEmptyCommand(command)) {
    return 0;
  }

  /* Update the history vector. */
  if (history.hstart == history.hend ||
      history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL ||
      strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) {
    if (history.hist[history.hend] != NULL) {
S
TD-1848  
Shengliang Guan 已提交
213
      tfree(history.hist[history.hend]);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    }
    history.hist[history.hend] = strdup(command);

    history.hend = (history.hend + 1) % MAX_HISTORY_SIZE;
    if (history.hend == history.hstart) {
      history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE;
    }
  }

  bool esc = false;
  char quote = 0, *cmd = command, *p = command;
  for (char c = *command++; c != 0; c = *command++) {
    if (esc) {
      switch (c) {
        case 'n':
          c = '\n';
          break;
        case 'r':
          c = '\r';
          break;
        case 't':
          c = '\t';
          break;
        case 'G':
          *p++ = '\\';
          break;
        case '\'':
        case '"':
          if (quote) {
            *p++ = '\\';
          }
          break;
      }
      *p++ = c;
      esc = false;
      continue;
    }
251

252
    if (c == '\\') {
253
      if (quote != 0 && (*command == '_' || *command == '\\')) {
S
Shengliang Guan 已提交
254
        // DO nothing
255 256 257 258
      } else {
        esc = true;
        continue;
      }
259 260 261 262
    }

    if (quote == c) {
      quote = 0;
D
dapan1121 已提交
263
    } else if (quote == 0 && (c == '\'' || c == '"')) {
264 265 266 267
      quote = c;
    }

    *p++ = c;
268
    if (c == ';' && quote == 0) {
269 270 271 272 273 274 275 276 277 278 279 280 281 282
      c = *p;
      *p = 0;
      if (shellRunSingleCommand(con, cmd) < 0) {
        return -1;
      }
      *p = c;
      p = cmd;
    }
  }

  *p = 0;
  return shellRunSingleCommand(con, cmd);
}

D
dapan1121 已提交
283
void freeResultWithRid(int64_t rid) {
S
Shengliang Guan 已提交
284
#if 0
D
dapan1121 已提交
285 286 287 288 289
  SSqlObj* pSql = taosAcquireRef(tscObjRef, rid);
  if(pSql){
    taos_free_result(pSql);
    taosReleaseRef(tscObjRef, rid);
  }
S
Shengliang Guan 已提交
290
#endif
D
dapan1121 已提交
291 292
}

H
hzcheng 已提交
293
void shellRunCommandOnServer(TAOS *con, char command[]) {
294
  int64_t   st, et;
H
hzcheng 已提交
295
  wordexp_t full_path;
S
Shengliang Guan 已提交
296 297 298
  char     *sptr = NULL;
  char     *cptr = NULL;
  char     *fname = NULL;
299
  bool      printMode = false;
H
hzcheng 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

  if ((sptr = strstr(command, ">>")) != NULL) {
    cptr = strstr(command, ";");
    if (cptr != NULL) {
      *cptr = '\0';
    }

    if (wordexp(sptr + 2, &full_path, 0) != 0) {
      fprintf(stderr, "ERROR: invalid filename: %s\n", sptr + 2);
      return;
    }
    *sptr = '\0';
    fname = full_path.we_wordv[0];
  }

315 316 317 318 319 320 321 322 323 324
  if ((sptr = strstr(command, "\\G")) != NULL) {
    cptr = strstr(command, ";");
    if (cptr != NULL) {
      *cptr = '\0';
    }

    *sptr = '\0';
    printMode = true;  // When output to a file, the switch does not work.
  }

H
hzcheng 已提交
325 326
  st = taosGetTimestampUs();

S
Shengliang Guan 已提交
327
  TAOS_RES *pSql = taos_query(con, command);
H
Haojun Liao 已提交
328
  if (taos_errno(pSql)) {
S
TD-1793  
Shengliang Guan 已提交
329
    taos_error(pSql, st);
H
hzcheng 已提交
330 331 332
    return;
  }

D
dapan1121 已提交
333
  int64_t oresult = atomic_load_64(&result);
D
fix bug  
dapan1121 已提交
334

L
lihui 已提交
335
  if (regex_match(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
336 337
    fprintf(stdout, "Database changed.\n\n");
    fflush(stdout);
338

D
fix bug  
dapan1121 已提交
339
    atomic_store_64(&result, 0);
D
dapan1121 已提交
340
    freeResultWithRid(oresult);
H
hzcheng 已提交
341 342 343
    return;
  }

H
Haojun Liao 已提交
344 345
  TAOS_FIELD* pFields = taos_fetch_fields(pSql);
  if (pFields != NULL) {  // select and show kinds of commands
346 347
    int error_no = 0;

H
Haojun Liao 已提交
348 349
    int numOfRows = shellDumpResult(pSql, fname, &error_no, printMode);
    if (numOfRows < 0) {
D
fix bug  
dapan1121 已提交
350
      atomic_store_64(&result, 0);
D
dapan1121 已提交
351
      freeResultWithRid(oresult);
H
Haojun Liao 已提交
352 353
      return;
    }
H
hzcheng 已提交
354 355 356 357 358

    et = taosGetTimestampUs();
    if (error_no == 0) {
      printf("Query OK, %d row(s) in set (%.6fs)\n", numOfRows, (et - st) / 1E6);
    } else {
359
      printf("Query interrupted (%s), %d row(s) in set (%.6fs)\n", taos_errstr(pSql), numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
360 361
    }
  } else {
H
Haojun Liao 已提交
362
    int num_rows_affacted = taos_affected_rows(pSql);
H
hzcheng 已提交
363
    et = taosGetTimestampUs();
D
dapan1121 已提交
364
    printf("Query OK, %d of %d row(s) in database (%.6fs)\n", num_rows_affacted, num_rows_affacted, (et - st) / 1E6);
H
hzcheng 已提交
365 366 367 368 369 370 371
  }

  printf("\n");

  if (fname != NULL) {
    wordfree(&full_path);
  }
372

D
fix bug  
dapan1121 已提交
373
  atomic_store_64(&result, 0);
D
dapan1121 已提交
374
  freeResultWithRid(oresult);
H
hzcheng 已提交
375 376 377 378 379
}

/* Function to do regular expression check */
int regex_match(const char *s, const char *reg, int cflags) {
  regex_t regex;
380
  char    msgbuf[100] = {0};
H
hzcheng 已提交
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

  /* Compile regular expression */
  if (regcomp(&regex, reg, cflags) != 0) {
    fprintf(stderr, "Fail to compile regex");
    exitShell();
  }

  /* Execute regular expression */
  int reti = regexec(&regex, s, 0, NULL, 0);
  if (!reti) {
    regfree(&regex);
    return 1;
  } else if (reti == REG_NOMATCH) {
    regfree(&regex);
    return 0;
  } else {
    regerror(reti, &regex, msgbuf, sizeof(msgbuf));
    fprintf(stderr, "Regex match failed: %s\n", msgbuf);
    regfree(&regex);
    exitShell();
  }

  return 0;
}

S
Shengliang Guan 已提交
406
static char *formatTimestamp(char *buf, int64_t val, int precision) {
407 408 409 410
  if (args.is_raw_time) {
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
411

S
Shengliang Guan 已提交
412
  time_t  tt;
D
fix bug  
dapan1121 已提交
413
  int32_t ms = 0;
414 415 416 417
  if (precision == TSDB_TIME_PRECISION_NANO) {
    tt = (time_t)(val / 1000000000);
    ms = val % 1000000000;
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
418
    tt = (time_t)(val / 1000000);
D
fix bug  
dapan1121 已提交
419
    ms = val % 1000000;
420 421
  } else {
    tt = (time_t)(val / 1000);
D
fix bug  
dapan1121 已提交
422
    ms = val % 1000;
423 424
  }

S
Shengliang Guan 已提交
425 426 427 428 429 430 431
  /* comment out as it make testcases like select_with_tags.sim fail.
    but in windows, this may cause the call to localtime crash if tt < 0,
    need to find a better solution.
    if (tt < 0) {
      tt = 0;
    }
    */
432

433 434 435
#ifdef WINDOWS
  if (tt < 0) tt = 0;
#endif
S
Shengliang Guan 已提交
436
  if (tt <= 0 && ms < 0) {
D
fix bug  
dapan1121 已提交
437
    tt--;
438 439 440
    if (precision == TSDB_TIME_PRECISION_NANO) {
      ms += 1000000000;
    } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
441 442 443 444 445
      ms += 1000000;
    } else {
      ms += 1000;
    }
  }
446

S
Shengliang Guan 已提交
447 448
  struct tm *ptm = localtime(&tt);
  size_t     pos = strftime(buf, 35, "%Y-%m-%d %H:%M:%S", ptm);
449

450 451 452
  if (precision == TSDB_TIME_PRECISION_NANO) {
    sprintf(buf + pos, ".%09d", ms);
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
453
    sprintf(buf + pos, ".%06d", ms);
454
  } else {
D
fix bug  
dapan1121 已提交
455
    sprintf(buf + pos, ".%03d", ms);
456 457 458 459 460
  }

  return buf;
}

S
Shengliang Guan 已提交
461
static void dumpFieldToFile(FILE *fp, const char *val, TAOS_FIELD *field, int32_t length, int precision) {
462 463 464 465 466 467 468 469
  if (val == NULL) {
    fprintf(fp, "%s", TSDB_DATA_NULL_STR);
    return;
  }

  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
S
TD-1530  
Shengliang Guan 已提交
470
      fprintf(fp, "%d", ((((int32_t)(*((char *)val))) == 1) ? 1 : 0));
471 472
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
473
      fprintf(fp, "%d", *((int8_t *)val));
474 475
      break;
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
476
      fprintf(fp, "%d", *((int16_t *)val));
477 478
      break;
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
479
      fprintf(fp, "%d", *((int32_t *)val));
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
      break;
    case TSDB_DATA_TYPE_BIGINT:
      fprintf(fp, "%" PRId64, *((int64_t *)val));
      break;
    case TSDB_DATA_TYPE_FLOAT:
      fprintf(fp, "%.5f", GET_FLOAT_VAL(val));
      break;
    case TSDB_DATA_TYPE_DOUBLE:
      fprintf(fp, "%.9f", GET_DOUBLE_VAL(val));
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
      memcpy(buf, val, length);
      buf[length] = 0;
      fprintf(fp, "\'%s\'", buf);
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
S
Shengliang Guan 已提交
497
      formatTimestamp(buf, *(int64_t *)val, precision);
498 499 500 501 502 503 504
      fprintf(fp, "'%s'", buf);
      break;
    default:
      break;
  }
}

S
Shengliang Guan 已提交
505
static int dumpResultToFile(const char *fname, TAOS_RES *tres) {
506
  TAOS_ROW row = taos_fetch_row(tres);
507 508 509 510 511 512
  if (row == NULL) {
    return 0;
  }

  wordexp_t full_path;

S
TD-1207  
Shengliang Guan 已提交
513
  if (wordexp((char *)fname, &full_path, 0) != 0) {
514
    fprintf(stderr, "ERROR: invalid file name: %s\n", fname);
H
hzcheng 已提交
515 516 517
    return -1;
  }

S
Shengliang Guan 已提交
518
  FILE *fp = fopen(full_path.we_wordv[0], "w");
519 520 521 522 523 524 525 526
  if (fp == NULL) {
    fprintf(stderr, "ERROR: failed to open file: %s\n", full_path.we_wordv[0]);
    wordfree(&full_path);
    return -1;
  }

  wordfree(&full_path);

S
Shengliang Guan 已提交
527
  int         num_fields = taos_num_fields(tres);
528
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
529
  int         precision = taos_result_precision(tres);
530 531 532 533 534 535 536 537

  for (int col = 0; col < num_fields; col++) {
    if (col > 0) {
      fprintf(fp, ",");
    }
    fprintf(fp, "%s", fields[col].name);
  }
  fputc('\n', fp);
538

539 540
  int numOfRows = 0;
  do {
S
Shengliang Guan 已提交
541
    int32_t *length = taos_fetch_lengths(tres);
542 543 544 545
    for (int i = 0; i < num_fields; i++) {
      if (i > 0) {
        fputc(',', fp);
      }
S
Shengliang Guan 已提交
546
      dumpFieldToFile(fp, (const char *)row[i], fields + i, length[i], precision);
H
hzcheng 已提交
547
    }
548 549 550
    fputc('\n', fp);

    numOfRows++;
551
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
552
  } while (row != NULL);
553

D
fix bug  
dapan1121 已提交
554
  result = 0;
555
  fclose(fp);
556

557 558 559
  return numOfRows;
}

560
static void shellPrintNChar(const char *str, int length, int width) {
561
  wchar_t tail[3];
S
Shengliang Guan 已提交
562
  int     pos = 0, cols = 0, totalCols = 0, tailLen = 0;
563

564 565
  while (pos < length) {
    wchar_t wc;
S
Shengliang Guan 已提交
566
    int     bytes = mbtowc(&wc, str + pos, MB_CUR_MAX);
567 568 569 570 571 572 573 574 575 576 577 578 579
    if (bytes == 0) {
      break;
    }
    pos += bytes;
    if (pos > length) {
      break;
    }

#ifdef WINDOWS
    int w = bytes;
#else
    int w = wcwidth(wc);
#endif
580 581 582 583 584 585 586 587 588 589 590 591 592 593
    if (w <= 0) {
      continue;
    }

    if (width <= 0) {
      printf("%lc", wc);
      continue;
    }

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
594 595
      printf("%lc", wc);
      cols += w;
596 597 598
    } else {
      tail[tailLen] = wc;
      tailLen++;
599 600 601
    }
  }

602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
  if (totalCols > width) {
    // width could be 1 or 2, so printf("...") cannot be used
    for (int i = 0; i < 3; i++) {
      if (cols >= width) {
        break;
      }
      putchar('.');
      ++cols;
    }
  } else {
    for (int i = 0; i < tailLen; i++) {
      printf("%lc", tail[i]);
    }
    cols = totalCols;
  }

618 619 620 621 622
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

S
Shengliang Guan 已提交
623
static void printField(const char *val, TAOS_FIELD *field, int width, int32_t length, int precision) {
624 625 626 627
  if (val == NULL) {
    int w = width;
    if (field->type < TSDB_DATA_TYPE_TINYINT || field->type > TSDB_DATA_TYPE_DOUBLE) {
      w = 0;
H
hzcheng 已提交
628
    }
629 630 631 632 633 634
    w = printf("%*s", w, TSDB_DATA_NULL_STR);
    for (; w < width; w++) {
      putchar(' ');
    }
    return;
  }
H
hzcheng 已提交
635

636 637 638
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
S
TD-1530  
Shengliang Guan 已提交
639
      printf("%*s", width, ((((int32_t)(*((char *)val))) == 1) ? "true" : "false"));
640 641
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
642
      printf("%*d", width, *((int8_t *)val));
643
      break;
644 645 646
    case TSDB_DATA_TYPE_UTINYINT:
      printf("%*u", width, *((uint8_t *)val));
      break;
647
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
648
      printf("%*d", width, *((int16_t *)val));
649
      break;
650 651 652
    case TSDB_DATA_TYPE_USMALLINT:
      printf("%*u", width, *((uint16_t *)val));
      break;
653
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
654
      printf("%*d", width, *((int32_t *)val));
655
      break;
656 657 658
    case TSDB_DATA_TYPE_UINT:
      printf("%*u", width, *((uint32_t *)val));
      break;
659 660 661
    case TSDB_DATA_TYPE_BIGINT:
      printf("%*" PRId64, width, *((int64_t *)val));
      break;
662 663 664
    case TSDB_DATA_TYPE_UBIGINT:
      printf("%*" PRIu64, width, *((uint64_t *)val));
      break;
665 666 667 668 669 670 671 672
    case TSDB_DATA_TYPE_FLOAT:
      printf("%*.5f", width, GET_FLOAT_VAL(val));
      break;
    case TSDB_DATA_TYPE_DOUBLE:
      printf("%*.9f", width, GET_DOUBLE_VAL(val));
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
B
Bomin Zhang 已提交
673
      shellPrintNChar(val, length, width);
674 675
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
S
Shengliang Guan 已提交
676
      formatTimestamp(buf, *(int64_t *)val, precision);
677 678 679 680
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
681
  }
682
}
H
hzcheng 已提交
683

S
Shengliang Guan 已提交
684 685
bool isSelectQuery(TAOS_RES *tres) {
#if 0
D
fix bug  
dapan1121 已提交
686 687 688 689 690
  char *sql = tscGetSqlStr(tres);

  if (regex_match(sql, "^[\t ]*select[ \t]*", REG_EXTENDED | REG_ICASE)) {
    return true;
  }
S
Shengliang Guan 已提交
691
#endif
D
fix bug  
dapan1121 已提交
692 693 694
  return false;
}

S
Shengliang Guan 已提交
695
static int verticalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
696
  TAOS_ROW row = taos_fetch_row(tres);
697 698 699 700
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
701
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
702
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
703
  int         precision = taos_result_precision(tres);
704

705 706
  int maxColNameLen = 0;
  for (int col = 0; col < num_fields; col++) {
S
TD-1057  
Shengliang Guan 已提交
707
    int len = (int)strlen(fields[col].name);
708 709 710 711 712
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
713 714
  uint64_t resShowMaxNum = UINT64_MAX;

S
Shengliang Guan 已提交
715
  if (args.commands == NULL && args.file[0] == 0 && isSelectQuery(tres) /*&& !tscIsQueryWithLimit(tres)*/) {
D
fix bug  
dapan1121 已提交
716 717 718
    resShowMaxNum = DEFAULT_RES_SHOW_NUM;
  }

719
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
720
  int showMore = 1;
721
  do {
D
fix bug  
dapan1121 已提交
722
    if (numOfRows < resShowMaxNum) {
D
fix bug  
dapan1121 已提交
723 724
      printf("*************************** %d.row ***************************\n", numOfRows + 1);

S
Shengliang Guan 已提交
725
      int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
726

D
fix bug  
dapan1121 已提交
727
      for (int i = 0; i < num_fields; i++) {
S
Shengliang Guan 已提交
728
        TAOS_FIELD *field = fields + i;
729

D
fix bug  
dapan1121 已提交
730 731
        int padding = (int)(maxColNameLen - strlen(field->name));
        printf("%*.s%s: ", padding, " ", field->name);
732

S
Shengliang Guan 已提交
733
        printField((const char *)row[i], field, 0, length[i], precision);
D
fix bug  
dapan1121 已提交
734 735
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
736
    } else if (showMore) {
S
Shengliang Guan 已提交
737 738 739
      printf("[100 Rows showed, and more rows are fetching but will not be showed. You can ctrl+c to stop or wait.]\n");
      printf("[You can add limit statement to get more or redirect results to specific file to get all.]\n");
      showMore = 0;
740 741 742
    }

    numOfRows++;
H
Haojun Liao 已提交
743
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
744
  } while (row != NULL);
745 746 747 748

  return numOfRows;
}

S
Shengliang Guan 已提交
749
static int calcColWidth(TAOS_FIELD *field, int precision) {
S
TD-1057  
Shengliang Guan 已提交
750
  int width = (int)strlen(field->name);
751 752 753

  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
dengyihao's avatar
dengyihao 已提交
754
      return TMAX(5, width);  // 'false'
755 756

    case TSDB_DATA_TYPE_TINYINT:
757
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
758
      return TMAX(4, width);  // '-127'
759 760

    case TSDB_DATA_TYPE_SMALLINT:
761
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
762
      return TMAX(6, width);  // '-32767'
763 764

    case TSDB_DATA_TYPE_INT:
765
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
766
      return TMAX(11, width);  // '-2147483648'
767 768

    case TSDB_DATA_TYPE_BIGINT:
769
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
770
      return TMAX(21, width);  // '-9223372036854775807'
771 772

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
773
      return TMAX(20, width);
774 775

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
776
      return TMAX(25, width);
777 778 779

    case TSDB_DATA_TYPE_BINARY:
      if (field->bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
780
        return TMAX(tsMaxBinaryDisplayWidth, width);
781
      } else {
dengyihao's avatar
dengyihao 已提交
782
        return TMAX(field->bytes, width);
783 784
      }

785 786 787
    case TSDB_DATA_TYPE_NCHAR: {
      int16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
      if (bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
788
        return TMAX(tsMaxBinaryDisplayWidth, width);
789
      } else {
dengyihao's avatar
dengyihao 已提交
790
        return TMAX(bytes, width);
791 792 793
      }
    }

794 795
    case TSDB_DATA_TYPE_TIMESTAMP:
      if (args.is_raw_time) {
dengyihao's avatar
dengyihao 已提交
796
        return TMAX(14, width);
S
Shengliang Guan 已提交
797 798
      }
      if (precision == TSDB_TIME_PRECISION_NANO) {
dengyihao's avatar
dengyihao 已提交
799
        return TMAX(29, width);
800
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
dengyihao's avatar
dengyihao 已提交
801
        return TMAX(26, width);  // '2020-01-01 00:00:00.000000'
802
      } else {
dengyihao's avatar
dengyihao 已提交
803
        return TMAX(23, width);  // '2020-01-01 00:00:00.000'
S
slguan 已提交
804
      }
H
hzcheng 已提交
805

806 807
    default:
      assert(false);
H
hzcheng 已提交
808 809
  }

810 811
  return 0;
}
H
hzcheng 已提交
812

S
Shengliang Guan 已提交
813
static void printHeader(TAOS_FIELD *fields, int *width, int num_fields) {
814 815
  int rowWidth = 0;
  for (int col = 0; col < num_fields; col++) {
S
Shengliang Guan 已提交
816 817 818
    TAOS_FIELD *field = fields + col;
    int         padding = (int)(width[col] - strlen(field->name));
    int         left = padding / 2;
819 820 821 822 823 824 825 826 827 828 829
    printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " ");
    rowWidth += width[col] + 3;
  }

  putchar('\n');
  for (int i = 0; i < rowWidth; i++) {
    putchar('=');
  }
  putchar('\n');
}

S
Shengliang Guan 已提交
830
static int horizontalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
831
  TAOS_ROW row = taos_fetch_row(tres);
832 833 834 835
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
836
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
837
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
838
  int         precision = taos_result_precision(tres);
839 840 841 842 843 844 845 846

  int width[TSDB_MAX_COLUMNS];
  for (int col = 0; col < num_fields; col++) {
    width[col] = calcColWidth(fields + col, precision);
  }

  printHeader(fields, width, num_fields);

D
fix bug  
dapan1121 已提交
847 848
  uint64_t resShowMaxNum = UINT64_MAX;

S
Shengliang Guan 已提交
849
  if (args.commands == NULL && args.file[0] == 0 && isSelectQuery(tres) /* && !tscIsQueryWithLimit(tres)*/) {
D
fix bug  
dapan1121 已提交
850 851 852
    resShowMaxNum = DEFAULT_RES_SHOW_NUM;
  }

853
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
854
  int showMore = 1;
855

856
  do {
S
Shengliang Guan 已提交
857
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
858 859 860
    if (numOfRows < resShowMaxNum) {
      for (int i = 0; i < num_fields; i++) {
        putchar(' ');
S
Shengliang Guan 已提交
861
        printField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
862 863 864 865
        putchar(' ');
        putchar('|');
      }
      putchar('\n');
D
fix bug  
dapan1121 已提交
866
    } else if (showMore) {
S
Shengliang Guan 已提交
867 868 869
      printf("[100 Rows showed, and more rows are fetching but will not be showed. You can ctrl+c to stop or wait.]\n");
      printf("[You can add limit statement to show more or redirect results to specific file to get all.]\n");
      showMore = 0;
870
    }
871

872
    numOfRows++;
H
Haojun Liao 已提交
873
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
874
  } while (row != NULL);
875 876 877 878

  return numOfRows;
}

H
Haojun Liao 已提交
879
int shellDumpResult(TAOS_RES *tres, char *fname, int *error_no, bool vertical) {
880
  int numOfRows = 0;
H
hzcheng 已提交
881
  if (fname != NULL) {
H
Haojun Liao 已提交
882
    numOfRows = dumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
883
  } else if (vertical) {
H
Haojun Liao 已提交
884
    numOfRows = verticalPrintResult(tres);
885
  } else {
H
Haojun Liao 已提交
886
    numOfRows = horizontalPrintResult(tres);
H
hzcheng 已提交
887 888
  }

H
Haojun Liao 已提交
889
  *error_no = taos_errno(tres);
H
hzcheng 已提交
890 891 892 893 894 895 896 897
  return numOfRows;
}

void read_history() {
  // Initialize history
  memset(history.hist, 0, sizeof(char *) * MAX_HISTORY_SIZE);
  history.hstart = 0;
  history.hend = 0;
S
Shengliang Guan 已提交
898
  char  *line = NULL;
H
hzcheng 已提交
899
  size_t line_size = 0;
900
  int    read_size = 0;
H
hzcheng 已提交
901 902 903 904 905 906

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

  FILE *f = fopen(f_history, "r");
  if (f == NULL) {
907
#ifndef WINDOWS
H
Hui Li 已提交
908 909 910
    if (errno != ENOENT) {
      fprintf(stderr, "Failed to open file %s, reason:%s\n", f_history, strerror(errno));
    }
911
#endif
H
hzcheng 已提交
912 913 914
    return;
  }

S
TD-1848  
Shengliang Guan 已提交
915
  while ((read_size = tgetline(&line, &line_size, f)) != -1) {
H
hzcheng 已提交
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
    line[read_size - 1] = '\0';
    history.hist[history.hend] = strdup(line);

    history.hend = (history.hend + 1) % MAX_HISTORY_SIZE;

    if (history.hend == history.hstart) {
      history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE;
    }
  }

  free(line);
  fclose(f);
}

void write_history() {
931
  char f_history[TSDB_FILENAME_LEN];
H
hzcheng 已提交
932 933 934 935
  get_history_path(f_history);

  FILE *f = fopen(f_history, "w");
  if (f == NULL) {
936
#ifndef WINDOWS
H
Hui Li 已提交
937
    fprintf(stderr, "Failed to open file %s for write, reason:%s\n", f_history, strerror(errno));
938
#endif
H
hzcheng 已提交
939 940 941 942 943 944
    return;
  }

  for (int i = history.hstart; i != history.hend;) {
    if (history.hist[i] != NULL) {
      fprintf(f, "%s\n", history.hist[i]);
S
TD-1848  
Shengliang Guan 已提交
945
      tfree(history.hist[i]);
H
hzcheng 已提交
946 947 948 949 950 951
    }
    i = (i + 1) % MAX_HISTORY_SIZE;
  }
  fclose(f);
}

S
TD-1793  
Shengliang Guan 已提交
952 953
void taos_error(TAOS_RES *tres, int64_t st) {
  int64_t et = taosGetTimestampUs();
H
Haojun Liao 已提交
954
  atomic_store_ptr(&result, 0);
S
TD-1793  
Shengliang Guan 已提交
955
  fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6);
H
Haojun Liao 已提交
956
  taos_free_result(tres);
H
hzcheng 已提交
957 958
}

S
#928  
slguan 已提交
959
int isCommentLine(char *line) {
H
hzcheng 已提交
960 961 962 963 964 965 966
  if (line == NULL) return 1;

  return regex_match(line, "^\\s*#.*", REG_EXTENDED);
}

void source_file(TAOS *con, char *fptr) {
  wordexp_t full_path;
967
  int       read_len = 0;
S
Shengliang Guan 已提交
968
  char     *cmd = calloc(1, tsMaxSQLStringLen + 1);
969
  size_t    cmd_len = 0;
S
Shengliang Guan 已提交
970
  char     *line = NULL;
971
  size_t    line_len = 0;
H
hzcheng 已提交
972 973 974

  if (wordexp(fptr, &full_path, 0) != 0) {
    fprintf(stderr, "ERROR: illegal file name\n");
975
    free(cmd);
H
hzcheng 已提交
976 977 978 979 980
    return;
  }

  char *fname = full_path.we_wordv[0];

H
Hui Li 已提交
981
  /*
982 983
  if (access(fname, F_OK) != 0) {
    fprintf(stderr, "ERROR: file %s is not exist\n", fptr);
984

985 986 987 988
    wordfree(&full_path);
    free(cmd);
    return;
  }
H
Hui Li 已提交
989
  */
990

H
hzcheng 已提交
991 992 993 994
  FILE *f = fopen(fname, "r");
  if (f == NULL) {
    fprintf(stderr, "ERROR: failed to open file %s\n", fname);
    wordfree(&full_path);
995
    free(cmd);
H
hzcheng 已提交
996 997 998
    return;
  }

S
TD-1848  
Shengliang Guan 已提交
999
  while ((read_len = tgetline(&line, &line_len, f)) != -1) {
H
Hui Li 已提交
1000
    if (read_len >= tsMaxSQLStringLen) continue;
H
hzcheng 已提交
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
    line[--read_len] = '\0';

    if (read_len == 0 || isCommentLine(line)) {  // line starts with #
      continue;
    }

    if (line[read_len - 1] == '\\') {
      line[read_len - 1] = ' ';
      memcpy(cmd + cmd_len, line, read_len);
      cmd_len += read_len;
      continue;
    }

    memcpy(cmd + cmd_len, line, read_len);
    printf("%s%s\n", PROMPT_HEADER, cmd);
    shellRunCommand(con, cmd);
H
Hui Li 已提交
1017
    memset(cmd, 0, tsMaxSQLStringLen);
H
hzcheng 已提交
1018 1019 1020 1021 1022 1023 1024 1025
    cmd_len = 0;
  }

  free(cmd);
  if (line) free(line);
  wordfree(&full_path);
  fclose(f);
}
S
slguan 已提交
1026 1027

void shellGetGrantInfo(void *con) {
S
slguan 已提交
1028
  return;
H
Hui Li 已提交
1029
#if 0
S
slguan 已提交
1030 1031
  char sql[] = "show grants";

1032
  TAOS_RES* tres = taos_query(con, sql);
H
Haojun Liao 已提交
1033

1034
  int code = taos_errno(tres);
S
slguan 已提交
1035
  if (code != TSDB_CODE_SUCCESS) {
1036
    if (code == TSDB_CODE_COM_OPS_NOT_SUPPORT) {
S
slguan 已提交
1037
      fprintf(stdout, "Server is Community Edition, version is %s\n\n", taos_get_server_info(con));
S
slguan 已提交
1038 1039 1040
    } else {
      fprintf(stderr, "Failed to check Server Edition, Reason:%d:%s\n\n", taos_errno(con), taos_errstr(con));
    }
S
slguan 已提交
1041 1042 1043
    return;
  }

1044
  int num_fields = taos_field_count(tres);
S
slguan 已提交
1045 1046 1047 1048
  if (num_fields == 0) {
    fprintf(stderr, "\nInvalid grant information.\n");
    exit(0);
  } else {
1049
    if (tres == NULL) {
S
slguan 已提交
1050 1051 1052 1053
      fprintf(stderr, "\nGrant information is null.\n");
      exit(0);
    }

1054 1055
    TAOS_FIELD *fields = taos_fetch_fields(tres);
    TAOS_ROW row = taos_fetch_row(tres);
S
slguan 已提交
1056
    if (row == NULL) {
H
hjxilinx 已提交
1057
      fprintf(stderr, "\nFailed to get grant information from server. Abort.\n");
S
slguan 已提交
1058 1059 1060
      exit(0);
    }

S
slguan 已提交
1061
    char serverVersion[32] = {0};
S
slguan 已提交
1062 1063 1064
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
1065
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
1066 1067 1068 1069
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

    if (strcmp(expiretime, "unlimited") == 0) {
S
slguan 已提交
1070
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, taos_get_server_info(con));
S
slguan 已提交
1071
    } else {
S
slguan 已提交
1072
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will expire at %s.\n", serverVersion, taos_get_server_info(con), expiretime);
S
slguan 已提交
1073 1074 1075
    }

    result = NULL;
1076
    taos_free_result(tres);
S
slguan 已提交
1077 1078 1079
  }

  fprintf(stdout, "\n");
S
Shengliang Guan 已提交
1080
#endif
S
slguan 已提交
1081
}