shellEngine.c 26.5 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
S
Shengliang Guan 已提交
20
#include "shellInt.h"
H
hzcheng 已提交
21

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
static bool    shellIsEmptyCommand(const char *cmd);
static int32_t shellRunSingleCommand(char *command);
static int32_t shellRunCommand(char *command);
static void    shellRunSingleCommandImp(char *command);
static char   *shellFormatTimestamp(char *buf, int64_t val, int32_t precision);
static void    shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length,
                                    int32_t precision);
static int32_t shellDumpResultToFile(const char *fname, TAOS_RES *tres);
static void    shellPrintNChar(const char *str, int32_t length, int32_t width);
static void    shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t length, int32_t precision);
static int32_t shellVerticalPrintResult(TAOS_RES *tres);
static int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision);
static void    shellPrintHeader(TAOS_FIELD *fields, int32_t *width, int32_t num_fields);
static int32_t shellHorizontalPrintResult(TAOS_RES *tres);
static int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical);
static void    shellReadHistory();
static void    shellWriteHistory();
static void    shellPrintError(TAOS_RES *tres, int64_t st);
static bool    shellIsCommentLine(char *line);
static void    shellSourceFile(const char *file);
static void    shellGetGrantInfo();
static void    shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context);
static void    shellCleanup(void *arg);
static void   *shellCancelHandler(void *arg);
static void   *shellThreadLoop(void *arg);

bool shellIsEmptyCommand(const char *cmd) {
49 50 51
  for (char c = *cmd++; c != 0; c = *cmd++) {
    if (c != ' ' && c != '\t' && c != ';') {
      return false;
H
hzcheng 已提交
52 53
    }
  }
54
  return true;
H
hzcheng 已提交
55 56
}

57 58
int32_t shellRunSingleCommand(char *command) {
  if (shellIsEmptyCommand(command)) {
59
    return 0;
H
hzcheng 已提交
60 61
  }

62 63
  if (shellRegexMatch(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
    shellWriteHistory();
64
    return -1;
65 66
  }

67
  if (shellRegexMatch(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
68
    system("clear");
69 70
    return 0;
  }
71

72
  if (shellRegexMatch(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$",
73
                      REG_EXTENDED | REG_ICASE)) {
74 75
    strtok(command, " \t");
    strtok(NULL, " \t");
S
Shengliang Guan 已提交
76
    char *p = strtok(NULL, " \t");
77
    if (strncasecmp(p, "default", 7) == 0) {
78
      shell.args.displayWidth = SHELL_DEFAULT_MAX_BINARY_DISPLAY_WIDTH;
79
    } else {
80 81 82
      int32_t displayWidth = atoi(p);
      displayWidth = TRANGE(displayWidth, 1, 10 * 1024);
      shell.args.displayWidth = displayWidth;
83
    }
84 85
    return 0;
  }
86

87
  if (shellRegexMatch(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
88 89 90 91 92
    /* If source file. */
    char *c_ptr = strtok(command, " ;");
    assert(c_ptr != NULL);
    c_ptr = strtok(NULL, " ;");
    assert(c_ptr != NULL);
93
    shellSourceFile(c_ptr);
94
    return 0;
H
hzcheng 已提交
95
  }
96

97
  shellRunSingleCommandImp(command);
98
  return 0;
H
hzcheng 已提交
99 100
}

101 102
int32_t shellRunCommand(char *command) {
  if (shellIsEmptyCommand(command)) {
103 104 105
    return 0;
  }

106 107 108 109 110 111
  SShellHistory *pHistory = &shell.history;
  if (pHistory->hstart == pHistory->hend ||
      pHistory->hist[(pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE] == NULL ||
      strcmp(command, pHistory->hist[(pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE]) != 0) {
    if (pHistory->hist[pHistory->hend] != NULL) {
      taosMemoryFreeClear(pHistory->hist[pHistory->hend]);
112
    }
113
    pHistory->hist[pHistory->hend] = strdup(command);
114

115 116 117
    pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE;
    if (pHistory->hend == pHistory->hstart) {
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    }
  }

  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;
144 145 146
        default:
          *p++ = '\\';
          break;
147 148 149 150 151
      }
      *p++ = c;
      esc = false;
      continue;
    }
152

153
    if (c == '\\') {
154
      if (quote != 0 && (*command == '_' || *command == '\\')) {
S
Shengliang Guan 已提交
155
        // DO nothing
156 157 158 159
      } else {
        esc = true;
        continue;
      }
160 161 162 163
    }

    if (quote == c) {
      quote = 0;
D
dapan1121 已提交
164
    } else if (quote == 0 && (c == '\'' || c == '"')) {
165 166 167 168
      quote = c;
    }

    *p++ = c;
169
    if (c == ';' && quote == 0) {
170 171
      c = *p;
      *p = 0;
172
      if (shellRunSingleCommand(cmd) < 0) {
173 174 175 176 177 178 179 180
        return -1;
      }
      *p = c;
      p = cmd;
    }
  }

  *p = 0;
181
  return shellRunSingleCommand(cmd);
D
dapan1121 已提交
182 183
}

184
void shellRunSingleCommandImp(char *command) {
185 186 187 188 189
  int64_t st, et;
  char   *sptr = NULL;
  char   *cptr = NULL;
  char   *fname = NULL;
  bool    printMode = false;
H
hzcheng 已提交
190 191 192 193 194 195 196

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

197
    fname = sptr + 2;
H
hzcheng 已提交
198 199 200
    *sptr = '\0';
  }

201 202 203 204 205 206 207 208 209 210
  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 已提交
211 212
  st = taosGetTimestampUs();

213
  TAOS_RES *pSql = taos_query(shell.conn, command);
H
Haojun Liao 已提交
214
  if (taos_errno(pSql)) {
215
    shellPrintError(pSql, st);
H
hzcheng 已提交
216 217 218
    return;
  }

219
  if (shellRegexMatch(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
220 221
    fprintf(stdout, "Database changed.\n\n");
    fflush(stdout);
222

S
Shengliang Guan 已提交
223 224
    taos_free_result(pSql);

H
hzcheng 已提交
225 226 227
    return;
  }

S
Shengliang Guan 已提交
228
  TAOS_FIELD *pFields = taos_fetch_fields(pSql);
H
Haojun Liao 已提交
229
  if (pFields != NULL) {  // select and show kinds of commands
230
    int32_t error_no = 0;
231

232
    int32_t numOfRows = shellDumpResult(pSql, fname, &error_no, printMode);
233
    if (numOfRows < 0) return;
H
hzcheng 已提交
234 235 236 237 238

    et = taosGetTimestampUs();
    if (error_no == 0) {
      printf("Query OK, %d row(s) in set (%.6fs)\n", numOfRows, (et - st) / 1E6);
    } else {
239
      printf("Query interrupted (%s), %d row(s) in set (%.6fs)\n", taos_errstr(pSql), numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
240
    }
S
Shengliang Guan 已提交
241
    taos_free_result(pSql);
H
hzcheng 已提交
242
  } else {
243
    int32_t num_rows_affacted = taos_affected_rows(pSql);
244
    taos_free_result(pSql);
H
hzcheng 已提交
245
    et = taosGetTimestampUs();
D
dapan1121 已提交
246
    printf("Query OK, %d of %d row(s) in database (%.6fs)\n", num_rows_affacted, num_rows_affacted, (et - st) / 1E6);
H
hzcheng 已提交
247 248 249 250 251
  }

  printf("\n");
}

252 253
char *shellFormatTimestamp(char *buf, int64_t val, int32_t precision) {
  if (shell.args.is_raw_time) {
254 255 256
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
257

S
Shengliang Guan 已提交
258
  time_t  tt;
D
fix bug  
dapan1121 已提交
259
  int32_t ms = 0;
260 261 262 263
  if (precision == TSDB_TIME_PRECISION_NANO) {
    tt = (time_t)(val / 1000000000);
    ms = val % 1000000000;
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
264
    tt = (time_t)(val / 1000000);
D
fix bug  
dapan1121 已提交
265
    ms = val % 1000000;
266 267
  } else {
    tt = (time_t)(val / 1000);
D
fix bug  
dapan1121 已提交
268
    ms = val % 1000;
269 270
  }

271 272
  /*
    comment out as it make testcases like select_with_tags.sim fail.
S
Shengliang Guan 已提交
273 274 275 276 277
    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;
    }
278
  */
279

280 281 282
#ifdef WINDOWS
  if (tt < 0) tt = 0;
#endif
S
Shengliang Guan 已提交
283
  if (tt <= 0 && ms < 0) {
D
fix bug  
dapan1121 已提交
284
    tt--;
285 286 287
    if (precision == TSDB_TIME_PRECISION_NANO) {
      ms += 1000000000;
    } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
288 289 290 291 292
      ms += 1000000;
    } else {
      ms += 1000;
    }
  }
293

294
  struct tm *ptm = taosLocalTime(&tt, NULL);
S
Shengliang Guan 已提交
295
  size_t     pos = strftime(buf, 35, "%Y-%m-%d %H:%M:%S", ptm);
296

297 298 299
  if (precision == TSDB_TIME_PRECISION_NANO) {
    sprintf(buf + pos, ".%09d", ms);
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
300
    sprintf(buf + pos, ".%06d", ms);
301
  } else {
D
fix bug  
dapan1121 已提交
302
    sprintf(buf + pos, ".%03d", ms);
303 304 305 306 307
  }

  return buf;
}

308
void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int32_t precision) {
309
  if (val == NULL) {
310
    taosFprintfFile(pFile, "%s", TSDB_DATA_NULL_STR);
311 312 313 314 315 316
    return;
  }

  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
317
      taosFprintfFile(pFile, "%d", ((((int32_t)(*((char *)val))) == 1) ? 1 : 0));
318 319
      break;
    case TSDB_DATA_TYPE_TINYINT:
320
      taosFprintfFile(pFile, "%d", *((int8_t *)val));
321 322
      break;
    case TSDB_DATA_TYPE_SMALLINT:
323
      taosFprintfFile(pFile, "%d", *((int16_t *)val));
324 325
      break;
    case TSDB_DATA_TYPE_INT:
326
      taosFprintfFile(pFile, "%d", *((int32_t *)val));
327 328
      break;
    case TSDB_DATA_TYPE_BIGINT:
329
      taosFprintfFile(pFile, "%" PRId64, *((int64_t *)val));
330 331
      break;
    case TSDB_DATA_TYPE_FLOAT:
332
      taosFprintfFile(pFile, "%.5f", GET_FLOAT_VAL(val));
333 334
      break;
    case TSDB_DATA_TYPE_DOUBLE:
335
      taosFprintfFile(pFile, "%.9f", GET_DOUBLE_VAL(val));
336 337 338 339 340
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
      memcpy(buf, val, length);
      buf[length] = 0;
341
      taosFprintfFile(pFile, "\'%s\'", buf);
342 343
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
344
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
345
      taosFprintfFile(pFile, "'%s'", buf);
346 347 348 349 350 351
      break;
    default:
      break;
  }
}

352
int32_t shellDumpResultToFile(const char *fname, TAOS_RES *tres) {
353 354 355 356 357
  char fullname[PATH_MAX] = {0};
  if (taosExpandDir(fname, fullname, PATH_MAX) != 0) {
    tstrncpy(fullname, fname, PATH_MAX);
  }

358
  TAOS_ROW row = taos_fetch_row(tres);
359 360 361 362
  if (row == NULL) {
    return 0;
  }

363
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
364
  if (pFile == NULL) {
365
    fprintf(stderr, "failed to open file: %s\n", fullname);
366 367 368
    return -1;
  }

369
  TAOS_FIELD *fields = taos_fetch_fields(tres);
370 371
  int32_t     num_fields = taos_num_fields(tres);
  int32_t     precision = taos_result_precision(tres);
372

373
  for (int32_t col = 0; col < num_fields; col++) {
374
    if (col > 0) {
375
      taosFprintfFile(pFile, ",");
376
    }
377
    taosFprintfFile(pFile, "%s", fields[col].name);
378
  }
379
  taosFprintfFile(pFile, "\n");
380

381
  int32_t numOfRows = 0;
382
  do {
S
Shengliang Guan 已提交
383
    int32_t *length = taos_fetch_lengths(tres);
384
    for (int32_t i = 0; i < num_fields; i++) {
385
      if (i > 0) {
386
        taosFprintfFile(pFile, "\n");
387
      }
388
      shellDumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision);
H
hzcheng 已提交
389
    }
390
    taosFprintfFile(pFile, "\n");
391 392

    numOfRows++;
393
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
394
  } while (row != NULL);
395

396
  taosCloseFile(&pFile);
397

398 399 400
  return numOfRows;
}

401
void shellPrintNChar(const char *str, int32_t length, int32_t width) {
wafwerar's avatar
wafwerar 已提交
402
  TdWchar tail[3];
403
  int32_t pos = 0, cols = 0, totalCols = 0, tailLen = 0;
404

405
  while (pos < length) {
wafwerar's avatar
wafwerar 已提交
406
    TdWchar wc;
407
    int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
408 409 410 411 412 413 414 415 416
    if (bytes == 0) {
      break;
    }
    pos += bytes;
    if (pos > length) {
      break;
    }

#ifdef WINDOWS
417
    int32_t w = bytes;
418
#else
419
    int32_t w = taosWcharWidth(wc);
420
#endif
421 422 423 424 425 426 427 428 429 430 431 432 433 434
    if (w <= 0) {
      continue;
    }

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

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
435 436
      printf("%lc", wc);
      cols += w;
437 438 439
    } else {
      tail[tailLen] = wc;
      tailLen++;
440 441 442
    }
  }

443 444
  if (totalCols > width) {
    // width could be 1 or 2, so printf("...") cannot be used
445
    for (int32_t i = 0; i < 3; i++) {
446 447 448 449 450 451 452
      if (cols >= width) {
        break;
      }
      putchar('.');
      ++cols;
    }
  } else {
453
    for (int32_t i = 0; i < tailLen; i++) {
454 455 456 457 458
      printf("%lc", tail[i]);
    }
    cols = totalCols;
  }

459 460 461 462 463
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

464
void shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t length, int32_t precision) {
465
  if (val == NULL) {
466
    int32_t w = width;
467 468
    if (field->type < TSDB_DATA_TYPE_TINYINT || field->type > TSDB_DATA_TYPE_DOUBLE) {
      w = 0;
H
hzcheng 已提交
469
    }
470 471 472 473 474 475
    w = printf("%*s", w, TSDB_DATA_NULL_STR);
    for (; w < width; w++) {
      putchar(' ');
    }
    return;
  }
H
hzcheng 已提交
476

477 478 479
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
S
TD-1530  
Shengliang Guan 已提交
480
      printf("%*s", width, ((((int32_t)(*((char *)val))) == 1) ? "true" : "false"));
481 482
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
483
      printf("%*d", width, *((int8_t *)val));
484
      break;
485 486 487
    case TSDB_DATA_TYPE_UTINYINT:
      printf("%*u", width, *((uint8_t *)val));
      break;
488
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
489
      printf("%*d", width, *((int16_t *)val));
490
      break;
491 492 493
    case TSDB_DATA_TYPE_USMALLINT:
      printf("%*u", width, *((uint16_t *)val));
      break;
494
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
495
      printf("%*d", width, *((int32_t *)val));
496
      break;
497 498 499
    case TSDB_DATA_TYPE_UINT:
      printf("%*u", width, *((uint32_t *)val));
      break;
500 501 502
    case TSDB_DATA_TYPE_BIGINT:
      printf("%*" PRId64, width, *((int64_t *)val));
      break;
503 504 505
    case TSDB_DATA_TYPE_UBIGINT:
      printf("%*" PRIu64, width, *((uint64_t *)val));
      break;
506 507 508 509 510 511 512 513
    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 已提交
514
      shellPrintNChar(val, length, width);
515 516
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
517
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
518 519 520 521
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
522
  }
523
}
H
hzcheng 已提交
524

525
int32_t shellVerticalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
526
  TAOS_ROW row = taos_fetch_row(tres);
527 528 529 530
  if (row == NULL) {
    return 0;
  }

531
  int32_t     num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
532
  TAOS_FIELD *fields = taos_fetch_fields(tres);
533
  int32_t     precision = taos_result_precision(tres);
534

535 536 537
  int32_t maxColNameLen = 0;
  for (int32_t col = 0; col < num_fields; col++) {
    int32_t len = (int32_t)strlen(fields[col].name);
538 539 540 541 542
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
543 544
  uint64_t resShowMaxNum = UINT64_MAX;

545
  if (shell.args.commands == NULL && shell.args.file[0] == 0) {
546
    resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
D
fix bug  
dapan1121 已提交
547 548
  }

549 550
  int32_t numOfRows = 0;
  int32_t showMore = 1;
551
  do {
D
fix bug  
dapan1121 已提交
552
    if (numOfRows < resShowMaxNum) {
D
fix bug  
dapan1121 已提交
553 554
      printf("*************************** %d.row ***************************\n", numOfRows + 1);

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

557
      for (int32_t i = 0; i < num_fields; i++) {
S
Shengliang Guan 已提交
558
        TAOS_FIELD *field = fields + i;
559

560
        int32_t padding = (int32_t)(maxColNameLen - strlen(field->name));
D
fix bug  
dapan1121 已提交
561
        printf("%*.s%s: ", padding, " ", field->name);
562

563
        shellPrintField((const char *)row[i], field, 0, length[i], precision);
D
fix bug  
dapan1121 已提交
564 565
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
566
    } else if (showMore) {
S
Shengliang Guan 已提交
567 568 569
      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;
570 571 572
    }

    numOfRows++;
H
Haojun Liao 已提交
573
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
574
  } while (row != NULL);
575 576 577 578

  return numOfRows;
}

579 580
int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) {
  int32_t width = (int32_t)strlen(field->name);
581 582 583

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

    case TSDB_DATA_TYPE_TINYINT:
587
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
588
      return TMAX(4, width);  // '-127'
589 590

    case TSDB_DATA_TYPE_SMALLINT:
591
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
592
      return TMAX(6, width);  // '-32767'
593 594

    case TSDB_DATA_TYPE_INT:
595
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
596
      return TMAX(11, width);  // '-2147483648'
597 598

    case TSDB_DATA_TYPE_BIGINT:
599
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
600
      return TMAX(21, width);  // '-9223372036854775807'
601 602

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
603
      return TMAX(20, width);
604 605

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
606
      return TMAX(25, width);
607 608

    case TSDB_DATA_TYPE_BINARY:
609 610
      if (field->bytes > shell.args.displayWidth) {
        return TMAX(shell.args.displayWidth, width);
611
      } else {
dengyihao's avatar
dengyihao 已提交
612
        return TMAX(field->bytes, width);
613 614
      }

615 616
    case TSDB_DATA_TYPE_NCHAR: {
      int16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
617 618
      if (bytes > shell.args.displayWidth) {
        return TMAX(shell.args.displayWidth, width);
619
      } else {
dengyihao's avatar
dengyihao 已提交
620
        return TMAX(bytes, width);
621 622 623
      }
    }

624
    case TSDB_DATA_TYPE_TIMESTAMP:
625
      if (shell.args.is_raw_time) {
dengyihao's avatar
dengyihao 已提交
626
        return TMAX(14, width);
S
Shengliang Guan 已提交
627 628
      }
      if (precision == TSDB_TIME_PRECISION_NANO) {
dengyihao's avatar
dengyihao 已提交
629
        return TMAX(29, width);
630
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
dengyihao's avatar
dengyihao 已提交
631
        return TMAX(26, width);  // '2020-01-01 00:00:00.000000'
632
      } else {
dengyihao's avatar
dengyihao 已提交
633
        return TMAX(23, width);  // '2020-01-01 00:00:00.000'
S
slguan 已提交
634
      }
H
hzcheng 已提交
635

636 637
    default:
      assert(false);
H
hzcheng 已提交
638 639
  }

640 641
  return 0;
}
H
hzcheng 已提交
642

643 644 645
void shellPrintHeader(TAOS_FIELD *fields, int32_t *width, int32_t num_fields) {
  int32_t rowWidth = 0;
  for (int32_t col = 0; col < num_fields; col++) {
S
Shengliang Guan 已提交
646
    TAOS_FIELD *field = fields + col;
647 648
    int32_t     padding = (int32_t)(width[col] - strlen(field->name));
    int32_t     left = padding / 2;
649 650 651 652 653
    printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " ");
    rowWidth += width[col] + 3;
  }

  putchar('\n');
654
  for (int32_t i = 0; i < rowWidth; i++) {
655 656 657 658 659
    putchar('=');
  }
  putchar('\n');
}

660
int32_t shellHorizontalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
661
  TAOS_ROW row = taos_fetch_row(tres);
662 663 664 665
  if (row == NULL) {
    return 0;
  }

666
  int32_t     num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
667
  TAOS_FIELD *fields = taos_fetch_fields(tres);
668
  int32_t     precision = taos_result_precision(tres);
669

670 671 672
  int32_t width[TSDB_MAX_COLUMNS];
  for (int32_t col = 0; col < num_fields; col++) {
    width[col] = shellCalcColWidth(fields + col, precision);
673 674
  }

675
  shellPrintHeader(fields, width, num_fields);
676

D
fix bug  
dapan1121 已提交
677 678
  uint64_t resShowMaxNum = UINT64_MAX;

679
  if (shell.args.commands == NULL && shell.args.file[0] == 0) {
680
    resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
D
fix bug  
dapan1121 已提交
681 682
  }

683 684
  int32_t numOfRows = 0;
  int32_t showMore = 1;
685

686
  do {
S
Shengliang Guan 已提交
687
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
688
    if (numOfRows < resShowMaxNum) {
689
      for (int32_t i = 0; i < num_fields; i++) {
D
fix bug  
dapan1121 已提交
690
        putchar(' ');
691
        shellPrintField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
692 693 694 695
        putchar(' ');
        putchar('|');
      }
      putchar('\n');
D
fix bug  
dapan1121 已提交
696
    } else if (showMore) {
S
Shengliang Guan 已提交
697 698 699
      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;
700
    }
701

702
    numOfRows++;
H
Haojun Liao 已提交
703
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
704
  } while (row != NULL);
705 706 707 708

  return numOfRows;
}

709 710
int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical) {
  int32_t numOfRows = 0;
H
hzcheng 已提交
711
  if (fname != NULL) {
712
    numOfRows = shellDumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
713
  } else if (vertical) {
714
    numOfRows = shellVerticalPrintResult(tres);
715
  } else {
716
    numOfRows = shellHorizontalPrintResult(tres);
H
hzcheng 已提交
717 718
  }

H
Haojun Liao 已提交
719
  *error_no = taos_errno(tres);
H
hzcheng 已提交
720 721 722
  return numOfRows;
}

723
void shellReadHistory() {
724 725 726
  SShellHistory *pHistory = &shell.history;
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_READ | TD_FILE_STREAM);
  if (pFile == NULL) return;
H
hzcheng 已提交
727

728 729
  char   *line = NULL;
  int32_t read_size = 0;
730
  while ((read_size = taosGetLineFile(pFile, &line)) != -1) {
H
hzcheng 已提交
731
    line[read_size - 1] = '\0';
732
    pHistory->hist[pHistory->hend] = strdup(line);
H
hzcheng 已提交
733

734
    pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
735

736 737
    if (pHistory->hend == pHistory->hstart) {
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
738 739 740
    }
  }

S
Shengliang Guan 已提交
741
  if (line != NULL) taosMemoryFree(line);
742
  taosCloseFile(&pFile);
H
hzcheng 已提交
743 744
}

745
void shellWriteHistory() {
746
  SShellHistory *pHistory = &shell.history;
S
Shengliang Guan 已提交
747
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_STREAM | TD_FILE_APPEND);
748
  if (pFile == NULL) return;
H
hzcheng 已提交
749

750 751 752 753
  for (int32_t i = pHistory->hstart; i != pHistory->hend;) {
    if (pHistory->hist[i] != NULL) {
      taosFprintfFile(pFile, "%s\n", pHistory->hist[i]);
      taosMemoryFreeClear(pHistory->hist[i]);
H
hzcheng 已提交
754
    }
755
    i = (i + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
756
  }
757
  taosFsyncFile(pFile);
758
  taosCloseFile(&pFile);
H
hzcheng 已提交
759 760
}

761
void shellPrintError(TAOS_RES *tres, int64_t st) {
S
TD-1793  
Shengliang Guan 已提交
762 763
  int64_t et = taosGetTimestampUs();
  fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6);
H
Haojun Liao 已提交
764
  taos_free_result(tres);
H
hzcheng 已提交
765 766
}

767 768
bool shellIsCommentLine(char *line) {
  if (line == NULL) return true;
769
  return shellRegexMatch(line, "^\\s*#.*", REG_EXTENDED);
H
hzcheng 已提交
770 771
}

772 773 774 775 776
void shellSourceFile(const char *file) {
  int32_t read_len = 0;
  char   *cmd = taosMemoryCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1);
  size_t  cmd_len = 0;
  char   *line = NULL;
777 778 779 780 781
  char    fullname[PATH_MAX] = {0};

  if (taosExpandDir(file, fullname, PATH_MAX) != 0) {
    tstrncpy(fullname, file, PATH_MAX);
  }
H
hzcheng 已提交
782

783
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_READ | TD_FILE_STREAM);
784
  if (pFile == NULL) {
785
    fprintf(stderr, "failed to open file %s\n", fullname);
wafwerar's avatar
wafwerar 已提交
786
    taosMemoryFree(cmd);
H
hzcheng 已提交
787 788 789
    return;
  }

790
  while ((read_len = taosGetLineFile(pFile, &line)) != -1) {
H
Haojun Liao 已提交
791
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
792 793
    line[--read_len] = '\0';

794
    if (read_len == 0 || shellIsCommentLine(line)) {  // line starts with #
H
hzcheng 已提交
795 796 797 798 799 800 801 802 803 804 805
      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);
806 807
    printf("%s%s\n", shell.info.promptHeader, cmd);
    shellRunCommand(cmd);
H
Haojun Liao 已提交
808
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
809 810 811
    cmd_len = 0;
  }

wafwerar's avatar
wafwerar 已提交
812
  taosMemoryFree(cmd);
S
Shengliang Guan 已提交
813
  if (line != NULL) taosMemoryFree(line);
814
  taosCloseFile(&pFile);
H
hzcheng 已提交
815
}
S
slguan 已提交
816

817
void shellGetGrantInfo() {
818 819 820 821
  char sinfo[1024] = {0};
  tstrncpy(sinfo, taos_get_server_info(shell.conn), sizeof(sinfo));
  strtok(sinfo, "\n");

S
slguan 已提交
822 823
  char sql[] = "show grants";

824
  TAOS_RES *tres = taos_query(shell.conn, sql);
H
Haojun Liao 已提交
825

826
  int32_t code = taos_errno(tres);
S
slguan 已提交
827
  if (code != TSDB_CODE_SUCCESS) {
828
    if (code == TSDB_CODE_OPS_NOT_SUPPORT) {
829
      fprintf(stdout, "Server is Community Edition, %s\n\n", sinfo);
S
slguan 已提交
830
    } else {
831
      fprintf(stderr, "Failed to check Server Edition, Reason:0x%04x:%s\n\n", taos_errno(shell.conn),
832
              taos_errstr(shell.conn));
S
slguan 已提交
833
    }
S
slguan 已提交
834 835 836
    return;
  }

837
  int32_t num_fields = taos_field_count(tres);
S
slguan 已提交
838 839 840 841
  if (num_fields == 0) {
    fprintf(stderr, "\nInvalid grant information.\n");
    exit(0);
  } else {
842
    if (tres == NULL) {
S
slguan 已提交
843 844 845 846
      fprintf(stderr, "\nGrant information is null.\n");
      exit(0);
    }

847
    TAOS_FIELD *fields = taos_fetch_fields(tres);
848
    TAOS_ROW    row = taos_fetch_row(tres);
S
slguan 已提交
849
    if (row == NULL) {
H
hjxilinx 已提交
850
      fprintf(stderr, "\nFailed to get grant information from server. Abort.\n");
S
slguan 已提交
851 852 853
      exit(0);
    }

S
slguan 已提交
854
    char serverVersion[32] = {0};
S
slguan 已提交
855 856 857
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
858
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
859 860 861 862
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

    if (strcmp(expiretime, "unlimited") == 0) {
863
      fprintf(stdout, "Server is Enterprise %s Edition, %s and will never expire.\n", serverVersion, sinfo);
S
slguan 已提交
864
    } else {
865
      fprintf(stdout, "Server is Enterprise %s Edition, %s and will expire at %s.\n", serverVersion, sinfo, expiretime);
S
slguan 已提交
866 867
    }

868
    taos_free_result(tres);
S
slguan 已提交
869 870 871
  }

  fprintf(stdout, "\n");
872 873 874 875
}

void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) { tsem_post(&shell.cancelSem); }

876 877 878 879
void shellSigintHandler(int32_t signum, void *sigInfo, void *context) {
  // do nothing
}

880 881 882 883 884 885 886 887 888 889 890
void shellCleanup(void *arg) { taosResetTerminalMode(); }

void *shellCancelHandler(void *arg) {
  setThreadName("shellCancelHandler");
  while (1) {
    if (tsem_wait(&shell.cancelSem) != 0) {
      taosMsleep(10);
      continue;
    }

    taosResetTerminalMode();
891
    printf("\nReceive SIGTERM or other signal, quit shell.\n");
892 893
    shellWriteHistory();
    shellExit();
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
  }

  return NULL;
}

void *shellThreadLoop(void *arg) {
  setThreadName("shellThreadLoop");
  taosGetOldTerminalMode();
  taosThreadCleanupPush(shellCleanup, NULL);

  char *command = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE);
  if (command == NULL) {
    printf("failed to malloc command\n");
    return NULL;
  }

  do {
    memset(command, 0, SHELL_MAX_COMMAND_SIZE);
    taosSetTerminalMode();

    if (shellReadCommand(command) != 0) {
      break;
    }

    taosResetTerminalMode();
  } while (shellRunCommand(command) == 0);

  taosMemoryFreeClear(command);
922 923
  shellWriteHistory();
  shellExit();
924

925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
  taosThreadCleanupPop(1);
  return NULL;
}

int32_t shellExecute() {
  printf(shell.info.clientVersion, shell.info.osname, taos_get_client_info());
  fflush(stdout);

  SShellArgs *pArgs = &shell.args;
  if (shell.args.auth == NULL) {
    shell.conn = taos_connect(pArgs->host, pArgs->user, pArgs->password, pArgs->database, pArgs->port);
  } else {
    shell.conn = taos_connect_auth(pArgs->host, pArgs->user, pArgs->auth, pArgs->database, pArgs->port);
  }

  if (shell.conn == NULL) {
    fflush(stdout);
    return -1;
  }

945 946
  shellReadHistory();

947
  if (pArgs->commands != NULL || pArgs->file[0] != 0) {
948 949 950 951 952 953 954
    if (pArgs->commands != NULL) {
      printf("%s%s\n", shell.info.promptHeader, pArgs->commands);
      char *cmd = strdup(pArgs->commands);
      shellRunCommand(cmd);
      taosMemoryFree(cmd);
    }

955
    if (pArgs->file[0] != 0) {
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975
      shellSourceFile(pArgs->file);
    }

    taos_close(shell.conn);
    shellWriteHistory();
    return 0;
  }

  if (tsem_init(&shell.cancelSem, 0, 0) != 0) {
    printf("failed to create cancel semphore\n");
    return -1;
  }

  TdThread spid = {0};
  taosThreadCreate(&spid, NULL, shellCancelHandler, NULL);

  taosSetSignal(SIGTERM, shellQueryInterruptHandler);
  taosSetSignal(SIGHUP, shellQueryInterruptHandler);
  taosSetSignal(SIGABRT, shellQueryInterruptHandler);

976 977
  taosSetSignal(SIGINT, shellSigintHandler);

978 979 980 981 982 983 984 985
  shellGetGrantInfo(shell.conn);

  while (1) {
    taosThreadCreate(&shell.pid, NULL, shellThreadLoop, shell.conn);
    taosThreadJoin(shell.pid, NULL);
  }

  return 0;
S
slguan 已提交
986
}