shellEngine.c 34.3 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/>.
 */

wafwerar's avatar
wafwerar 已提交
16
#define ALLOW_FORBID_FUNC
17 18
#define _BSD_SOURCE
#define _GNU_SOURCE
H
hzcheng 已提交
19
#define _XOPEN_SOURCE
S
slguan 已提交
20
#define _DEFAULT_SOURCE
S
Shengliang Guan 已提交
21
#include "shellInt.h"
A
Alex Duan 已提交
22
#include "shellAuto.h"
D
Dingle Zhang 已提交
23
#include "geosWrapper.h"
H
hzcheng 已提交
24

25 26
static bool    shellIsEmptyCommand(const char *cmd);
static int32_t shellRunSingleCommand(char *command);
27 28
static void    shellRecordCommandToHistory(char *command);
static int32_t shellRunCommand(char *command, bool recordHistory);
29 30 31 32
static void    shellRunSingleCommandImp(char *command);
static char   *shellFormatTimestamp(char *buf, int64_t val, 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);
D
Dingle Zhang 已提交
33
static void    shellPrintGeometry(const unsigned char *str, int32_t length, int32_t width);
S
Shengliang Guan 已提交
34 35 36
static int32_t shellVerticalPrintResult(TAOS_RES *tres, const char *sql);
static int32_t shellHorizontalPrintResult(TAOS_RES *tres, const char *sql);
static int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical, const char *sql);
37 38 39 40 41 42
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();
43

44 45 46 47 48
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
  if (shellRegexMatch(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
63
    return -1;
64 65
  }

66
  if (shellRegexMatch(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
67 68 69 70
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result"
          system("clear");
#pragma GCC diagnostic pop
71 72
    return 0;
  }
73

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

89
  if (shellRegexMatch(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
90 91
    /* If source file. */
    char *c_ptr = strtok(command, " ;");
wafwerar's avatar
wafwerar 已提交
92 93 94 95
    if (c_ptr == NULL) {
      shellRunSingleCommandImp(command);
      return 0;
    }
H
hzcheng 已提交
96
    c_ptr = strtok(NULL, " ;");
wafwerar's avatar
wafwerar 已提交
97 98 99 100
    if (c_ptr == NULL) {
      shellRunSingleCommandImp(command);
      return 0;
    }
101
    shellSourceFile(c_ptr);
102
    return 0;
H
hzcheng 已提交
103
  }
Y
Yang Zhao 已提交
104 105
#ifdef WEBSOCKET
  if (shell.args.restful || shell.args.cloud) {
D
dapan1121 已提交
106
    shellRunSingleCommandWebsocketImp(command);
Y
Yang Zhao 已提交
107 108
  } else {
#endif
D
dapan1121 已提交
109
    shellRunSingleCommandImp(command);
Y
Yang Zhao 已提交
110 111 112
#ifdef WEBSOCKET
  }
#endif
113
  return 0;
H
hzcheng 已提交
114 115
}

116
void shellRecordCommandToHistory(char *command) {
117 118 119 120 121 122 123
  if (strncasecmp(command, "create user ", 12) == 0 || strncasecmp(command, "alter user ", 11) == 0) {
    if (taosStrCaseStr(command, " pass ")) {
      // have password command forbid record to history because security
      return;
    }
  }

124 125 126 127 128 129
  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]);
130
    }
131
    pHistory->hist[pHistory->hend] = taosStrdup(command);
132

133 134 135
    pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE;
    if (pHistory->hend == pHistory->hstart) {
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
136 137
    }
  }
138 139 140 141 142 143 144
}

int32_t shellRunCommand(char *command, bool recordHistory) {
  if (shellIsEmptyCommand(command)) {
    return 0;
  }

145
  // add help or help; 
146
  if(strncasecmp(command, "help;", 5) == 0) {
147 148 149 150
    showHelp();
    return 0;
  }

151
  if (recordHistory) shellRecordCommandToHistory(command);
152

wmmhello's avatar
wmmhello 已提交
153
  char quote = 0, *cmd = command;
154
  for (char c = *command++; c != 0; c = *command++) {
wmmhello's avatar
wmmhello 已提交
155
    if (c == '\\' && (*command == '\'' || *command == '"' || *command == '`')) {
S
Shengliang Guan 已提交
156
      command++;
157 158
      continue;
    }
159

160 161
    if (quote == c) {
      quote = 0;
wmmhello's avatar
wmmhello 已提交
162
    } else if (quote == 0 && (c == '\'' || c == '"' || c == '`')) {
163
      quote = c;
wmmhello's avatar
wmmhello 已提交
164 165 166
    } else if (c == ';' && quote == 0) {
      c = *command;
      *command = 0;
167
      if (shellRunSingleCommand(cmd) < 0) {
168 169
        return -1;
      }
wmmhello's avatar
wmmhello 已提交
170 171
      *command = c;
      cmd = command;
172 173
    }
  }
174
  return shellRunSingleCommand(cmd);
D
dapan1121 已提交
175 176
}

177
void shellRunSingleCommandImp(char *command) {
178 179 180 181 182
  int64_t st, et;
  char   *sptr = NULL;
  char   *cptr = NULL;
  char   *fname = NULL;
  bool    printMode = false;
H
hzcheng 已提交
183 184 185 186 187 188 189

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

190
    fname = sptr + 2;
wafwerar's avatar
wafwerar 已提交
191
    while (*fname == ' ') fname++;
H
hzcheng 已提交
192 193 194
    *sptr = '\0';
  }

195 196 197 198 199 200 201 202 203 204
  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 已提交
205 206
  st = taosGetTimestampUs();

207
  TAOS_RES *pSql = taos_query(shell.conn, command);
H
Haojun Liao 已提交
208
  if (taos_errno(pSql)) {
209
    shellPrintError(pSql, st);
H
hzcheng 已提交
210 211 212
    return;
  }

213
  if (shellRegexMatch(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) {
wafwerar's avatar
wafwerar 已提交
214
    fprintf(stdout, "Database changed.\r\n\r\n");
H
hzcheng 已提交
215
    fflush(stdout);
216

A
Alex Duan 已提交
217 218 219
    // call back auto tab module
    callbackAutoTab(command, pSql, true);

S
Shengliang Guan 已提交
220 221
    taos_free_result(pSql);

H
hzcheng 已提交
222 223 224
    return;
  }

225 226 227
  // pre string
  char * pre = "Query OK";
  if (shellRegexMatch(command, "^\\s*delete\\s*from\\s*.*", REG_EXTENDED | REG_ICASE)) {
A
Alex Duan 已提交
228
    pre = "Delete OK";
229
  } else if(shellRegexMatch(command, "^\\s*insert\\s*into\\s*.*", REG_EXTENDED | REG_ICASE)) {
A
Alex Duan 已提交
230
    pre = "Insert OK";
231
  } else if(shellRegexMatch(command, "^\\s*create\\s*.*", REG_EXTENDED | REG_ICASE)) {
A
Alex Duan 已提交
232
    pre = "Create OK";
233
  } else if(shellRegexMatch(command, "^\\s*drop\\s*.*", REG_EXTENDED | REG_ICASE)) {
A
Alex Duan 已提交
234
    pre = "Drop OK";
235 236
  }

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

S
Shengliang Guan 已提交
241
    int32_t numOfRows = shellDumpResult(pSql, fname, &error_no, printMode, command);
242
    if (numOfRows < 0) return;
H
hzcheng 已提交
243 244 245

    et = taosGetTimestampUs();
    if (error_no == 0) {
246
      printf("Query OK, %d row(s) in set (%.6fs)\r\n", numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
247
    } else {
248
      printf("Query interrupted (%s), %d row(s) in set (%.6fs)\r\n", taos_errstr(pSql), numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
249
    }
S
Shengliang Guan 已提交
250
    taos_free_result(pSql);
H
hzcheng 已提交
251
  } else {
252
    int64_t num_rows_affacted = taos_affected_rows64(pSql);
253
    taos_free_result(pSql);
H
hzcheng 已提交
254
    et = taosGetTimestampUs();
A
Alex Duan 已提交
255
    printf("%s, %" PRId64 " row(s) affected (%.6fs)\r\n", pre, num_rows_affacted, (et - st) / 1E6);
A
Alex Duan 已提交
256 257

    // call auto tab
H
Haojun Liao 已提交
258
    callbackAutoTab(command, NULL, false);
H
hzcheng 已提交
259 260
  }

wafwerar's avatar
wafwerar 已提交
261
  printf("\r\n");
H
hzcheng 已提交
262 263
}

264 265
char *shellFormatTimestamp(char *buf, int64_t val, int32_t precision) {
  if (shell.args.is_raw_time) {
266 267 268
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
269

S
Shengliang Guan 已提交
270
  time_t  tt;
D
fix bug  
dapan1121 已提交
271
  int32_t ms = 0;
272 273 274 275
  if (precision == TSDB_TIME_PRECISION_NANO) {
    tt = (time_t)(val / 1000000000);
    ms = val % 1000000000;
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
276
    tt = (time_t)(val / 1000000);
D
fix bug  
dapan1121 已提交
277
    ms = val % 1000000;
278 279
  } else {
    tt = (time_t)(val / 1000);
D
fix bug  
dapan1121 已提交
280
    ms = val % 1000;
281 282
  }

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 = {0};
295
  if (taosLocalTime(&tt, &ptm, buf) == NULL) {
296 297
    return buf;
  }
298
  size_t     pos = strftime(buf, 35, "%Y-%m-%d %H:%M:%S", &ptm);
299

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

  return buf;
}

D
Dingle Zhang 已提交
311 312 313 314 315 316 317 318 319
char *shellDumpHexValue(char *buf, const char *val, int32_t length) {
  for (int32_t i = 0; i < length; i++) {
    sprintf(buf + (i * 2), "%02X", val[i]);
  }
  buf[length * 2] = 0;

  return buf;
}

wafwerar's avatar
wafwerar 已提交
320
void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int32_t precision) {
321
  if (val == NULL) {
X
Xiaoyu Wang 已提交
322
    taosFprintfFile(pFile, "NULL");
323 324 325
    return;
  }

X
Xiaoyu Wang 已提交
326 327 328 329
  char quotationStr[2];
  quotationStr[0] = '\"';
  quotationStr[1] = 0;

330
  int  n;
331 332 333
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
wafwerar's avatar
wafwerar 已提交
334
      taosFprintfFile(pFile, "%d", ((((int32_t)(*((char *)val))) == 1) ? 1 : 0));
335 336
      break;
    case TSDB_DATA_TYPE_TINYINT:
wafwerar's avatar
wafwerar 已提交
337
      taosFprintfFile(pFile, "%d", *((int8_t *)val));
338
      break;
S
Shengliang Guan 已提交
339
    case TSDB_DATA_TYPE_UTINYINT:
wafwerar's avatar
wafwerar 已提交
340
      taosFprintfFile(pFile, "%u", *((uint8_t *)val));
S
Shengliang Guan 已提交
341
      break;
342
    case TSDB_DATA_TYPE_SMALLINT:
wafwerar's avatar
wafwerar 已提交
343
      taosFprintfFile(pFile, "%d", *((int16_t *)val));
344
      break;
S
Shengliang Guan 已提交
345
    case TSDB_DATA_TYPE_USMALLINT:
wafwerar's avatar
wafwerar 已提交
346
      taosFprintfFile(pFile, "%u", *((uint16_t *)val));
S
Shengliang Guan 已提交
347
      break;
348
    case TSDB_DATA_TYPE_INT:
wafwerar's avatar
wafwerar 已提交
349
      taosFprintfFile(pFile, "%d", *((int32_t *)val));
350
      break;
S
Shengliang Guan 已提交
351
    case TSDB_DATA_TYPE_UINT:
wafwerar's avatar
wafwerar 已提交
352
      taosFprintfFile(pFile, "%u", *((uint32_t *)val));
S
Shengliang Guan 已提交
353
      break;
354
    case TSDB_DATA_TYPE_BIGINT:
wafwerar's avatar
wafwerar 已提交
355
      taosFprintfFile(pFile, "%" PRId64, *((int64_t *)val));
356
      break;
S
Shengliang Guan 已提交
357
    case TSDB_DATA_TYPE_UBIGINT:
wafwerar's avatar
wafwerar 已提交
358
      taosFprintfFile(pFile, "%" PRIu64, *((uint64_t *)val));
S
Shengliang Guan 已提交
359
      break;
360
    case TSDB_DATA_TYPE_FLOAT:
361 362 363 364 365
      if (tsEnableScience) {
        taosFprintfFile(pFile, "%e", GET_FLOAT_VAL(val));
      } else {
        taosFprintfFile(pFile, "%.5f", GET_FLOAT_VAL(val));
      }
366 367
      break;
    case TSDB_DATA_TYPE_DOUBLE:
368 369 370 371 372 373 374 375 376 377 378
      if (tsEnableScience) {
        snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.9e", 23, GET_DOUBLE_VAL(val));
        taosFprintfFile(pFile, "%s", buf);
      } else {
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.9f", length, GET_DOUBLE_VAL(val));
        if (n > TMAX(25, length)) {
          taosFprintfFile(pFile, "%*.15e", length, GET_DOUBLE_VAL(val));
        } else {
          taosFprintfFile(pFile, "%s", buf);
        }
      }
379 380 381
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
wmmhello's avatar
wmmhello 已提交
382
    case TSDB_DATA_TYPE_JSON:
wafwerar's avatar
wafwerar 已提交
383 384 385
      {
        int32_t bufIndex = 0;
        for (int32_t i = 0; i < length; i++) {
wafwerar's avatar
wafwerar 已提交
386 387
          buf[bufIndex] = val[i];
          bufIndex++;
wafwerar's avatar
wafwerar 已提交
388 389 390 391
          if (val[i] == '\"') {
            buf[bufIndex] = val[i];
            bufIndex++;
          }
wafwerar's avatar
wafwerar 已提交
392
        }
wafwerar's avatar
wafwerar 已提交
393
        buf[bufIndex] = 0;
wafwerar's avatar
wafwerar 已提交
394
        
wafwerar's avatar
wafwerar 已提交
395
        taosFprintfFile(pFile, "%s%s%s", quotationStr, buf, quotationStr);
wafwerar's avatar
wafwerar 已提交
396
      }
397
      break;
D
Dingle Zhang 已提交
398 399 400 401
    case TSDB_DATA_TYPE_GEOMETRY:
      shellDumpHexValue(buf, val, length);
      taosFprintfFile(pFile, "%s", buf);
      break;
402
    case TSDB_DATA_TYPE_TIMESTAMP:
403
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
X
Xiaoyu Wang 已提交
404
      taosFprintfFile(pFile, "%s%s%s", quotationStr, buf, quotationStr);
405 406 407 408 409 410
      break;
    default:
      break;
  }
}

411
int32_t shellDumpResultToFile(const char *fname, TAOS_RES *tres) {
412 413 414 415 416
  char fullname[PATH_MAX] = {0};
  if (taosExpandDir(fname, fullname, PATH_MAX) != 0) {
    tstrncpy(fullname, fname, PATH_MAX);
  }

417
  TAOS_ROW row = taos_fetch_row(tres);
418 419 420 421
  if (row == NULL) {
    return 0;
  }

422
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
423
  if (pFile == NULL) {
wafwerar's avatar
wafwerar 已提交
424
    fprintf(stderr, "failed to open file: %s\r\n", fullname);
425 426 427
    return -1;
  }

428
  TAOS_FIELD *fields = taos_fetch_fields(tres);
429 430
  int32_t     num_fields = taos_num_fields(tres);
  int32_t     precision = taos_result_precision(tres);
431

432
  for (int32_t col = 0; col < num_fields; col++) {
433
    if (col > 0) {
434
      taosFprintfFile(pFile, ",");
435
    }
436
    taosFprintfFile(pFile, "%s", fields[col].name);
437
  }
wafwerar's avatar
wafwerar 已提交
438
  taosFprintfFile(pFile, "\r\n");
439

440
  int32_t numOfRows = 0;
441
  do {
S
Shengliang Guan 已提交
442
    int32_t *length = taos_fetch_lengths(tres);
443
    for (int32_t i = 0; i < num_fields; i++) {
444
      if (i > 0) {
X
Xiaoyu Wang 已提交
445
        taosFprintfFile(pFile, ",");
446
      }
wafwerar's avatar
wafwerar 已提交
447
      shellDumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision);
H
hzcheng 已提交
448
    }
wafwerar's avatar
wafwerar 已提交
449
    taosFprintfFile(pFile, "\r\n");
450 451

    numOfRows++;
452
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
453
  } while (row != NULL);
454

455
  taosCloseFile(&pFile);
456

457 458 459
  return numOfRows;
}

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

464
  while (pos < length) {
wafwerar's avatar
wafwerar 已提交
465
    TdWchar wc;
466
    int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
wmmhello's avatar
wmmhello 已提交
467
    if (bytes <= 0) {
468 469
      break;
    }
wmmhello's avatar
wmmhello 已提交
470 471

    if (pos + bytes > length) {
472 473
      break;
    }
wmmhello's avatar
wmmhello 已提交
474
    int w = 0;
X
Xiaoyu Wang 已提交
475
    if (*(str + pos) == '\t' || *(str + pos) == '\n' || *(str + pos) == '\r') {
wmmhello's avatar
wmmhello 已提交
476
      w = bytes;
X
Xiaoyu Wang 已提交
477
    } else {
wmmhello's avatar
wmmhello 已提交
478 479 480 481
      w = taosWcharWidth(wc);
    }
    pos += bytes;

482 483 484 485 486 487 488 489 490 491 492 493 494 495
    if (w <= 0) {
      continue;
    }

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

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
496 497
      printf("%lc", wc);
      cols += w;
498 499 500
    } else {
      tail[tailLen] = wc;
      tailLen++;
501 502 503
    }
  }

504 505
  if (totalCols > width) {
    // width could be 1 or 2, so printf("...") cannot be used
506
    for (int32_t i = 0; i < 3; i++) {
507 508 509 510 511 512 513
      if (cols >= width) {
        break;
      }
      putchar('.');
      ++cols;
    }
  } else {
514
    for (int32_t i = 0; i < tailLen; i++) {
515 516 517 518 519
      printf("%lc", tail[i]);
    }
    cols = totalCols;
  }

520 521 522 523 524
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

D
Dingle Zhang 已提交
525 526 527 528 529 530 531 532 533
void shellPrintString(const char *str, int32_t width) {
  int32_t len = strlen(str);

  if (width == 0) {
    printf("%s", str);
  }
  else if (len > width) {
    if (width <= 3) {
      printf("%.*s.", width - 1, str);
H
hzcheng 已提交
534
    }
D
Dingle Zhang 已提交
535 536
    else {
      printf("%.*s...", width - 3, str);
537
    }
D
Dingle Zhang 已提交
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
  } else {
    printf("%s%*.s", str, width - len, "");
  }
}

void shellPrintGeometry(const unsigned char *val, int32_t length, int32_t width) {
  if (length == 0) {  //empty value
    shellPrintString("", width);
    return;
  }

  int32_t code = TSDB_CODE_FAILED;

  code = initCtxAsText();
  if (code != TSDB_CODE_SUCCESS) {
    shellPrintString(getThreadLocalGeosCtx()->errMsg, width);
    return;
  }

  char *outputWKT = NULL;
  code = doAsText(val, length, &outputWKT);
  if (code != TSDB_CODE_SUCCESS) {
    shellPrintString(getThreadLocalGeosCtx()->errMsg, width);  //should NOT happen
    return;
  }

  shellPrintString(outputWKT, width);

  geosFreeBuffer(outputWKT);
}

void shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t length, int32_t precision) {
  if (val == NULL) {
    shellPrintString(TSDB_DATA_NULL_STR, width);
572 573
    return;
  }
H
hzcheng 已提交
574

575
  int  n;
576 577 578
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
D
Dingle Zhang 已提交
579
      shellPrintString(((((int32_t)(*((char *)val))) == 1) ? "true" : "false"), width);
580 581
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
582
      printf("%*d", width, *((int8_t *)val));
583
      break;
584 585 586
    case TSDB_DATA_TYPE_UTINYINT:
      printf("%*u", width, *((uint8_t *)val));
      break;
587
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
588
      printf("%*d", width, *((int16_t *)val));
589
      break;
590 591 592
    case TSDB_DATA_TYPE_USMALLINT:
      printf("%*u", width, *((uint16_t *)val));
      break;
593
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
594
      printf("%*d", width, *((int32_t *)val));
595
      break;
596 597 598
    case TSDB_DATA_TYPE_UINT:
      printf("%*u", width, *((uint32_t *)val));
      break;
599 600 601
    case TSDB_DATA_TYPE_BIGINT:
      printf("%*" PRId64, width, *((int64_t *)val));
      break;
602 603 604
    case TSDB_DATA_TYPE_UBIGINT:
      printf("%*" PRIu64, width, *((uint64_t *)val));
      break;
605
    case TSDB_DATA_TYPE_FLOAT:
606 607 608
      if (tsEnableScience) {
        printf("%*e", width, GET_FLOAT_VAL(val));
      } else {
A
Alex Duan 已提交
609 610 611 612 613 614
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.5f", width, GET_FLOAT_VAL(val));
        if (n > TMAX(20, width)) {
            printf("%*e", width, GET_FLOAT_VAL(val));
        } else {
            printf("%s", buf);
        }
615
      }
616 617
      break;
    case TSDB_DATA_TYPE_DOUBLE:
618 619 620 621
      if (tsEnableScience) {
        snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%.9e", GET_DOUBLE_VAL(val));
        printf("%*s", width, buf);
      } else {
A
Alex Duan 已提交
622 623 624
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.9f", width, GET_DOUBLE_VAL(val));
        if (n > TMAX(25, width)) {
            printf("%*.15e", width, GET_DOUBLE_VAL(val));
625 626 627 628
        } else {
            printf("%s", buf);
        }
      }
629 630 631
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
wmmhello's avatar
wmmhello 已提交
632
    case TSDB_DATA_TYPE_JSON:
B
Bomin Zhang 已提交
633
      shellPrintNChar(val, length, width);
634
      break;
D
Dingle Zhang 已提交
635 636 637
    case TSDB_DATA_TYPE_GEOMETRY:
      shellPrintGeometry(val, length, width);
      break;
638
    case TSDB_DATA_TYPE_TIMESTAMP:
639
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
640 641 642 643
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
644
  }
645
}
H
hzcheng 已提交
646

647 648 649
// show whole result for this query return true, like limit or describe
bool shellIsShowWhole(const char *sql) {
  // limit
wafwerar's avatar
wafwerar 已提交
650
  if (taosStrCaseStr(sql, " limit ") != NULL) {
S
Shengliang Guan 已提交
651 652
    return true;
  }
653 654 655 656
  // describe
  if (taosStrCaseStr(sql, "describe ") != NULL) {
    return true;
  }
657
  // show
658 659 660
  if (taosStrCaseStr(sql, "show ") != NULL) {
    return true;
  }
S
Shengliang Guan 已提交
661 662 663 664

  return false;
}

D
dapan1121 已提交
665
bool shellIsShowQuery(const char *sql) {
X
Xiaoyu Wang 已提交
666
  // todo refactor
D
dapan1121 已提交
667 668 669 670 671 672 673
  if (taosStrCaseStr(sql, "show ") != NULL) {
    return true;
  }

  return false;
}

S
Shengliang Guan 已提交
674
int32_t shellVerticalPrintResult(TAOS_RES *tres, const char *sql) {
H
Haojun Liao 已提交
675
  TAOS_ROW row = taos_fetch_row(tres);
676 677 678 679
  if (row == NULL) {
    return 0;
  }

680
  int32_t     num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
681
  TAOS_FIELD *fields = taos_fetch_fields(tres);
682
  int32_t     precision = taos_result_precision(tres);
683

684 685 686
  int32_t maxColNameLen = 0;
  for (int32_t col = 0; col < num_fields; col++) {
    int32_t len = (int32_t)strlen(fields[col].name);
687 688 689 690 691
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
692 693
  uint64_t resShowMaxNum = UINT64_MAX;

694
  if (shell.args.commands == NULL && shell.args.file[0] == 0 && !shellIsShowWhole(sql)) {
695
    resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
D
fix bug  
dapan1121 已提交
696 697
  }

698 699
  int32_t numOfRows = 0;
  int32_t showMore = 1;
700
  do {
D
fix bug  
dapan1121 已提交
701
    if (numOfRows < resShowMaxNum) {
wafwerar's avatar
wafwerar 已提交
702
      printf("*************************** %d.row ***************************\r\n", numOfRows + 1);
D
fix bug  
dapan1121 已提交
703

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

706
      for (int32_t i = 0; i < num_fields; i++) {
S
Shengliang Guan 已提交
707
        TAOS_FIELD *field = fields + i;
708

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

712
        shellPrintField((const char *)row[i], field, 0, length[i], precision);
wafwerar's avatar
wafwerar 已提交
713
        putchar('\r');
D
fix bug  
dapan1121 已提交
714 715
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
716
    } else if (showMore) {
wafwerar's avatar
wafwerar 已提交
717 718 719 720 721 722 723
      printf("\r\n");
      printf(" Notice: The result shows only the first %d rows.\r\n", SHELL_DEFAULT_RES_SHOW_NUM);
      printf("         You can use the `LIMIT` clause to get fewer result to show.\r\n");
      printf("           Or use '>>' to redirect the whole set of the result to a specified file.\r\n");
      printf("\r\n");
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
      printf("\r\n");
S
Shengliang Guan 已提交
724
      showMore = 0;
725 726 727
    }

    numOfRows++;
H
Haojun Liao 已提交
728
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
729
  } while (row != NULL);
730 731 732 733

  return numOfRows;
}

734 735
int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) {
  int32_t width = (int32_t)strlen(field->name);
736 737

  switch (field->type) {
D
dapan1121 已提交
738 739
    case TSDB_DATA_TYPE_NULL:
      return TMAX(4, width);  // null
740
    case TSDB_DATA_TYPE_BOOL:
dengyihao's avatar
dengyihao 已提交
741
      return TMAX(5, width);  // 'false'
742 743

    case TSDB_DATA_TYPE_TINYINT:
744
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
745
      return TMAX(4, width);  // '-127'
746 747

    case TSDB_DATA_TYPE_SMALLINT:
748
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
749
      return TMAX(6, width);  // '-32767'
750 751

    case TSDB_DATA_TYPE_INT:
752
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
753
      return TMAX(11, width);  // '-2147483648'
754 755

    case TSDB_DATA_TYPE_BIGINT:
756
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
757
      return TMAX(21, width);  // '-9223372036854775807'
758 759

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
760
      return TMAX(20, width);
761 762

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
763
      return TMAX(25, width);
764 765

    case TSDB_DATA_TYPE_BINARY:
D
Dingle Zhang 已提交
766
    case TSDB_DATA_TYPE_GEOMETRY:
767 768
      if (field->bytes > shell.args.displayWidth) {
        return TMAX(shell.args.displayWidth, width);
769
      } else {
dengyihao's avatar
dengyihao 已提交
770
        return TMAX(field->bytes, width);
771 772
      }

wmmhello's avatar
wmmhello 已提交
773 774
    case TSDB_DATA_TYPE_NCHAR:
    case TSDB_DATA_TYPE_JSON: {
775
      uint16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
776 777
      if (bytes > shell.args.displayWidth) {
        return TMAX(shell.args.displayWidth, width);
778
      } else {
dengyihao's avatar
dengyihao 已提交
779
        return TMAX(bytes, width);
780 781 782
      }
    }

783
    case TSDB_DATA_TYPE_TIMESTAMP:
784
      if (shell.args.is_raw_time) {
dengyihao's avatar
dengyihao 已提交
785
        return TMAX(14, width);
S
Shengliang Guan 已提交
786 787
      }
      if (precision == TSDB_TIME_PRECISION_NANO) {
dengyihao's avatar
dengyihao 已提交
788
        return TMAX(29, width);
789
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
dengyihao's avatar
dengyihao 已提交
790
        return TMAX(26, width);  // '2020-01-01 00:00:00.000000'
791
      } else {
dengyihao's avatar
dengyihao 已提交
792
        return TMAX(23, width);  // '2020-01-01 00:00:00.000'
S
slguan 已提交
793
      }
H
hzcheng 已提交
794

795
    default:
X
xinsheng Ren 已提交
796
      ASSERT(false);
H
hzcheng 已提交
797 798
  }

799 800
  return 0;
}
H
hzcheng 已提交
801

802 803 804
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 已提交
805
    TAOS_FIELD *field = fields + col;
806 807
    int32_t     padding = (int32_t)(width[col] - strlen(field->name));
    int32_t     left = padding / 2;
808 809 810 811
    printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " ");
    rowWidth += width[col] + 3;
  }

wafwerar's avatar
wafwerar 已提交
812
  putchar('\r');
813
  putchar('\n');
814
  for (int32_t i = 0; i < rowWidth; i++) {
815 816
    putchar('=');
  }
wafwerar's avatar
wafwerar 已提交
817
  putchar('\r');
818 819 820
  putchar('\n');
}

S
Shengliang Guan 已提交
821
int32_t shellHorizontalPrintResult(TAOS_RES *tres, const char *sql) {
H
Haojun Liao 已提交
822
  TAOS_ROW row = taos_fetch_row(tres);
823 824 825 826
  if (row == NULL) {
    return 0;
  }

827
  int32_t     num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
828
  TAOS_FIELD *fields = taos_fetch_fields(tres);
829
  int32_t     precision = taos_result_precision(tres);
830

831 832 833
  int32_t width[TSDB_MAX_COLUMNS];
  for (int32_t col = 0; col < num_fields; col++) {
    width[col] = shellCalcColWidth(fields + col, precision);
834 835
  }

836
  shellPrintHeader(fields, width, num_fields);
837

D
fix bug  
dapan1121 已提交
838 839
  uint64_t resShowMaxNum = UINT64_MAX;

840
  if (shell.args.commands == NULL && shell.args.file[0] == 0 && !shellIsShowWhole(sql)) {
841
    resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
D
fix bug  
dapan1121 已提交
842 843
  }

844 845
  int32_t numOfRows = 0;
  int32_t showMore = 1;
846

847
  do {
S
Shengliang Guan 已提交
848
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
849
    if (numOfRows < resShowMaxNum) {
850
      for (int32_t i = 0; i < num_fields; i++) {
D
fix bug  
dapan1121 已提交
851
        putchar(' ');
852
        shellPrintField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
853 854 855
        putchar(' ');
        putchar('|');
      }
wafwerar's avatar
wafwerar 已提交
856
      putchar('\r');
D
fix bug  
dapan1121 已提交
857
      putchar('\n');
D
fix bug  
dapan1121 已提交
858
    } else if (showMore) {
wafwerar's avatar
wafwerar 已提交
859 860
      printf("\r\n");
      printf(" Notice: The result shows only the first %d rows.\r\n", SHELL_DEFAULT_RES_SHOW_NUM);
wafwerar's avatar
wafwerar 已提交
861 862 863 864 865 866
      if (shellIsShowQuery(sql)) {
        printf("         You can use '>>' to redirect the whole set of the result to a specified file.\r\n");
      } else {
        printf("         You can use the `LIMIT` clause to get fewer result to show.\r\n");
        printf("           Or use '>>' to redirect the whole set of the result to a specified file.\r\n");
      }
wafwerar's avatar
wafwerar 已提交
867 868 869
      printf("\r\n");
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
      printf("\r\n");
S
Shengliang Guan 已提交
870
      showMore = 0;
871
    }
872

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

  return numOfRows;
}

S
Shengliang Guan 已提交
880
int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical, const char *sql) {
881
  int32_t numOfRows = 0;
H
hzcheng 已提交
882
  if (fname != NULL) {
883
    numOfRows = shellDumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
884
  } else if (vertical) {
S
Shengliang Guan 已提交
885
    numOfRows = shellVerticalPrintResult(tres, sql);
886
  } else {
S
Shengliang Guan 已提交
887
    numOfRows = shellHorizontalPrintResult(tres, sql);
H
hzcheng 已提交
888 889
  }

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

894
void shellReadHistory() {
895 896 897
  SShellHistory *pHistory = &shell.history;
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_READ | TD_FILE_STREAM);
  if (pFile == NULL) return;
H
hzcheng 已提交
898

wafwerar's avatar
wafwerar 已提交
899
  char    *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
900
  int32_t read_size = 0;
wafwerar's avatar
wafwerar 已提交
901
  while ((read_size = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) != -1) {
H
hzcheng 已提交
902
    line[read_size - 1] = '\0';
903
    taosMemoryFree(pHistory->hist[pHistory->hend]);
904
    pHistory->hist[pHistory->hend] = taosStrdup(line);
H
hzcheng 已提交
905

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

908 909
    if (pHistory->hend == pHistory->hstart) {
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
910 911 912
    }
  }

wafwerar's avatar
wafwerar 已提交
913
  taosMemoryFreeClear(line);
914
  taosCloseFile(&pFile);
915 916 917 918 919 920 921 922 923 924 925 926 927
  int64_t file_size;
  if (taosStatFile(pHistory->file, &file_size, NULL) == 0 && file_size > SHELL_MAX_COMMAND_SIZE) {
    TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_STREAM | TD_FILE_TRUNC);
    if (pFile == NULL) return;
    int32_t endIndex = pHistory->hstart;
    if (endIndex != 0) {
      endIndex = pHistory->hend;
    }
    for (int32_t i = (pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE; i != endIndex;) {
      taosFprintfFile(pFile, "%s\n", pHistory->hist[i]);
      i = (i + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE;
    }
    taosFprintfFile(pFile, "%s\n", pHistory->hist[endIndex]);
928 929

    /* coverity[+retval] */
930 931 932
    taosFsyncFile(pFile);
    taosCloseFile(&pFile);
  }
wafwerar's avatar
wafwerar 已提交
933
  pHistory->hstart = pHistory->hend;
H
hzcheng 已提交
934 935
}

936
void shellWriteHistory() {
937
  SShellHistory *pHistory = &shell.history;
938
  if (pHistory->hend == pHistory->hstart) return;
S
Shengliang Guan 已提交
939
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_STREAM | TD_FILE_APPEND);
940
  if (pFile == NULL) return;
H
hzcheng 已提交
941

942 943 944
  for (int32_t i = pHistory->hstart; i != pHistory->hend;) {
    if (pHistory->hist[i] != NULL) {
      taosFprintfFile(pFile, "%s\n", pHistory->hist[i]);
945 946
      taosMemoryFree(pHistory->hist[i]);
      pHistory->hist[i] = NULL;
H
hzcheng 已提交
947
    }
948
    i = (i + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
949
  }
950
  taosCloseFile(&pFile);
H
hzcheng 已提交
951 952
}

953 954 955 956 957 958 959 960 961 962
void shellCleanupHistory() {
  SShellHistory *pHistory = &shell.history;
  for (int32_t i = 0; i < SHELL_MAX_HISTORY_SIZE; ++i) {
    if (pHistory->hist[i] != NULL) {
      taosMemoryFree(pHistory->hist[i]);
      pHistory->hist[i] = NULL;
    }
  }
}

963
void shellPrintError(TAOS_RES *tres, int64_t st) {
S
TD-1793  
Shengliang Guan 已提交
964
  int64_t et = taosGetTimestampUs();
wafwerar's avatar
wafwerar 已提交
965
  fprintf(stderr, "\r\nDB error: %s (%.6fs)\r\n", taos_errstr(tres), (et - st) / 1E6);
H
Haojun Liao 已提交
966
  taos_free_result(tres);
H
hzcheng 已提交
967 968
}

969 970
bool shellIsCommentLine(char *line) {
  if (line == NULL) return true;
971
  return shellRegexMatch(line, "^\\s*#.*", REG_EXTENDED);
H
hzcheng 已提交
972 973
}

974 975 976 977
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;
978
  char    fullname[PATH_MAX] = {0};
979
  char    sourceFileCommand[PATH_MAX + 8] = {0};
H
hzcheng 已提交
980

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

985 986 987
  sprintf(sourceFileCommand, "source %s;",fullname);
  shellRecordCommandToHistory(sourceFileCommand);

988
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_READ | TD_FILE_STREAM);
989
  if (pFile == NULL) {
wafwerar's avatar
wafwerar 已提交
990
    fprintf(stderr, "failed to open file %s\r\n", fullname);
wafwerar's avatar
wafwerar 已提交
991
    taosMemoryFree(cmd);
H
hzcheng 已提交
992 993 994
    return;
  }

wafwerar's avatar
wafwerar 已提交
995 996
  char   *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
  while ((read_len = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) != -1) {
H
Haojun Liao 已提交
997
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
998 999
    line[--read_len] = '\0';

1000
    if (read_len == 0 || shellIsCommentLine(line)) {  // line starts with #
H
hzcheng 已提交
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
      continue;
    }

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

1011 1012 1013 1014
    if (line[read_len - 1] == '\r') {
      line[read_len - 1] = ' ';
    }

H
hzcheng 已提交
1015
    memcpy(cmd + cmd_len, line, read_len);
wafwerar's avatar
wafwerar 已提交
1016
    printf("%s%s\r\n", shell.info.promptHeader, cmd);
1017
    shellRunCommand(cmd, false);
H
Haojun Liao 已提交
1018
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
1019 1020 1021
    cmd_len = 0;
  }

wafwerar's avatar
wafwerar 已提交
1022
  taosMemoryFree(cmd);
wafwerar's avatar
wafwerar 已提交
1023
  taosMemoryFreeClear(line);
1024
  taosCloseFile(&pFile);
H
hzcheng 已提交
1025
}
S
slguan 已提交
1026

1027
void shellGetGrantInfo() {
1028 1029
  char sinfo[1024] = {0};
  tstrncpy(sinfo, taos_get_server_info(shell.conn), sizeof(sinfo));
wafwerar's avatar
wafwerar 已提交
1030
  strtok(sinfo, "\r\n");
1031

S
slguan 已提交
1032 1033
  char sql[] = "show grants";

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

1036
  int32_t code = taos_errno(tres);
S
slguan 已提交
1037
  if (code != TSDB_CODE_SUCCESS) {
1038
    if (code != TSDB_CODE_OPS_NOT_SUPPORT && code != TSDB_CODE_MND_NO_RIGHTS && code != TSDB_CODE_PAR_PERMISSION_DENIED) {
wafwerar's avatar
wafwerar 已提交
1039
      fprintf(stderr, "Failed to check Server Edition, Reason:0x%04x:%s\r\n\r\n", code, taos_errstr(tres));
S
slguan 已提交
1040
    }
S
slguan 已提交
1041 1042 1043
    return;
  }

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

1054
    TAOS_FIELD *fields = taos_fetch_fields(tres);
1055
    TAOS_ROW    row = taos_fetch_row(tres);
S
slguan 已提交
1056
    if (row == NULL) {
wafwerar's avatar
wafwerar 已提交
1057
      fprintf(stderr, "\r\nFailed to get grant information from server. Abort.\r\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
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

1069
    if (strcmp(serverVersion, "community") == 0) {
wafwerar's avatar
wafwerar 已提交
1070
      fprintf(stdout, "Server is Community Edition.\r\n");
1071
    } else if (strcmp(expiretime, "unlimited") == 0) {
wafwerar's avatar
wafwerar 已提交
1072
      fprintf(stdout, "Server is Enterprise %s Edition, %s and will never expire.\r\n", serverVersion, sinfo);
S
slguan 已提交
1073
    } else {
wafwerar's avatar
wafwerar 已提交
1074
      fprintf(stdout, "Server is Enterprise %s Edition, %s and will expire at %s.\r\n", serverVersion, sinfo, expiretime);
S
slguan 已提交
1075 1076
    }

1077
    taos_free_result(tres);
S
slguan 已提交
1078 1079
  }

wafwerar's avatar
wafwerar 已提交
1080
  fprintf(stdout, "\r\n");
1081 1082
}

1083 1084 1085 1086
#ifdef WINDOWS
BOOL shellQueryInterruptHandler(DWORD fdwCtrlType) {
  tsem_post(&shell.cancelSem);
  return TRUE;
1087
}
1088 1089 1090
#else
void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) { tsem_post(&shell.cancelSem); }
#endif
1091

1092 1093 1094 1095 1096
void shellCleanup(void *arg) { taosResetTerminalMode(); }

void *shellCancelHandler(void *arg) {
  setThreadName("shellCancelHandler");
  while (1) {
1097 1098 1099 1100
    if (shell.exit == true) {
      break;
    }

1101 1102 1103 1104
    if (tsem_wait(&shell.cancelSem) != 0) {
      taosMsleep(10);
      continue;
    }
Y
Yang Zhao 已提交
1105 1106

#ifdef WEBSOCKET
D
dapan1121 已提交
1107 1108 1109
    if (shell.args.restful || shell.args.cloud) {
      shell.stop_query = true;
    } else {
Y
Yang Zhao 已提交
1110
#endif
D
dapan1121 已提交
1111 1112 1113
      if (shell.conn) {
        taos_kill_query(shell.conn);
      }
Y
Yang Zhao 已提交
1114
#ifdef WEBSOCKET
D
dapan1121 已提交
1115
    }
1116
#endif
1117 1118 1119
  #ifdef WINDOWS
    printf("\n%s", shell.info.promptHeader);
  #endif
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
  }

  return NULL;
}

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

  do {
1131 1132 1133
    char *command = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE);
    if (command == NULL) {
      printf("failed to malloc command\r\n");
1134 1135 1136
      break;
    }

1137 1138 1139 1140 1141 1142 1143
    do {
      memset(command, 0, SHELL_MAX_COMMAND_SIZE);
      taosSetTerminalMode();

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

1145 1146 1147
      taosResetTerminalMode();
    } while (shellRunCommand(command, true) == 0);

D
Dingle Zhang 已提交
1148
    destroyThreadLocalGeosCtx();
1149 1150 1151 1152
    taosMemoryFreeClear(command);
    shellWriteHistory();
    shellExit();
  } while (0);
1153

1154 1155 1156 1157 1158
  taosThreadCleanupPop(1);
  return NULL;
}

int32_t shellExecute() {
sangshuduo's avatar
sangshuduo 已提交
1159 1160
  printf(shell.info.clientVersion, shell.info.cusName,
         taos_get_client_info(), shell.info.cusName);
1161 1162 1163
  fflush(stdout);

  SShellArgs *pArgs = &shell.args;
Y
Yang Zhao 已提交
1164 1165
#ifdef WEBSOCKET
  if (shell.args.restful || shell.args.cloud) {
D
dapan1121 已提交
1166 1167 1168
    if (shell_conn_ws_server(1)) {
      return -1;
    }
1169
  } else {
Y
Yang Zhao 已提交
1170
#endif
D
dapan1121 已提交
1171 1172 1173 1174 1175 1176 1177
    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) {
1178
      printf("failed to connect to server, reason: %s\n", taos_errstr(NULL));
D
dapan1121 已提交
1179 1180 1181
      fflush(stdout);
      return -1;
    }
Y
Yang Zhao 已提交
1182
#ifdef WEBSOCKET
1183
  }
Y
Yang Zhao 已提交
1184
#endif
1185

1186 1187
  bool runOnce = pArgs->commands != NULL || pArgs->file[0] != 0;
  shellSetConn(shell.conn, runOnce);
1188 1189
  shellReadHistory();

1190
  if (runOnce) {
1191
    if (pArgs->commands != NULL) {
wafwerar's avatar
wafwerar 已提交
1192
      printf("%s%s\r\n", shell.info.promptHeader, pArgs->commands);
1193
      char *cmd = taosStrdup(pArgs->commands);
1194
      shellRunCommand(cmd, true);
1195 1196 1197
      taosMemoryFree(cmd);
    }

1198
    if (pArgs->file[0] != 0) {
1199 1200
      shellSourceFile(pArgs->file);
    }
Y
Yang Zhao 已提交
1201
#ifdef WEBSOCKET
D
dapan1121 已提交
1202 1203 1204
    if (shell.args.restful || shell.args.cloud) {
      ws_close(shell.ws_conn);
    } else {
1205
#endif
D
dapan1121 已提交
1206
      taos_close(shell.conn);
Y
Yang Zhao 已提交
1207
#ifdef WEBSOCKET
D
dapan1121 已提交
1208
    }
Y
Yang Zhao 已提交
1209
#endif
1210 1211

    shellWriteHistory();
1212
    shellCleanupHistory();
1213 1214 1215 1216
    return 0;
  }

  if (tsem_init(&shell.cancelSem, 0, 0) != 0) {
sangshuduo's avatar
sangshuduo 已提交
1217
    printf("failed to create cancel semaphore\r\n");
1218 1219 1220 1221 1222 1223 1224 1225
    return -1;
  }

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

  taosSetSignal(SIGTERM, shellQueryInterruptHandler);
  taosSetSignal(SIGHUP, shellQueryInterruptHandler);
1226
  taosSetSignal(SIGINT, shellQueryInterruptHandler);
D
dapan1121 已提交
1227
  
Y
Yang Zhao 已提交
1228 1229 1230
#ifdef WEBSOCKET
  if (!shell.args.restful && !shell.args.cloud) {
#endif
1231
#ifndef WINDOWS
D
dapan1121 已提交
1232
    printfIntroduction();
1233
#endif  
D
dapan1121 已提交
1234
    shellGetGrantInfo();
Y
Yang Zhao 已提交
1235 1236 1237
#ifdef WEBSOCKET
  }
#endif
1238
  while (1) {
Y
Yang Zhao 已提交
1239
    taosThreadCreate(&shell.pid, NULL, shellThreadLoop, NULL);
1240
    taosThreadJoin(shell.pid, NULL);
1241
    taosThreadClear(&shell.pid);
1242 1243 1244 1245
    if (shell.exit) {
      tsem_post(&shell.cancelSem);
      break;
    }
1246
  }
1247
  taosThreadJoin(spid, NULL);
1248

1249
  shellCleanupHistory();
A
Alex Duan 已提交
1250 1251 1252
  taos_kill_query(shell.conn);
  taos_close(shell.conn);

1253
  return 0;
S
slguan 已提交
1254
}