shellEngine.c 34.6 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
      int32_t width = SHELL_FLOAT_WIDTH;
362
      if (tsEnableScience) {
363
        taosFprintfFile(pFile, "%*e", width, GET_FLOAT_VAL(val));
364
      } else {
365 366
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.5f", width, GET_FLOAT_VAL(val));
        if (n > SHELL_FLOAT_WIDTH) {
367
          taosFprintfFile(pFile, "%*e", width, GET_FLOAT_VAL(val));
368
        } else {
369
          taosFprintfFile(pFile, "%s", buf);
370
        }
371
      }
372 373
      break;
    case TSDB_DATA_TYPE_DOUBLE:
374
      int32_t width = SHELL_DOUBLE_WIDTH;
375
      if (tsEnableScience) {
376 377
        snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%.9e", GET_DOUBLE_VAL(val));
        taosFprintfFile(pFile, "%*s", width, buf);
378
      } else {
379 380
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.9f", width, GET_DOUBLE_VAL(val));
        if (n > SHELL_DOUBLE_WIDTH) {
381
          taosFprintfFile(pFile, "%*.15e", width, GET_DOUBLE_VAL(val));
382
        } else {
383
          taosFprintfFile(pFile, "%s", buf);
384 385
        }
      }
386 387 388
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
wmmhello's avatar
wmmhello 已提交
389
    case TSDB_DATA_TYPE_JSON:
wafwerar's avatar
wafwerar 已提交
390 391 392
      {
        int32_t bufIndex = 0;
        for (int32_t i = 0; i < length; i++) {
wafwerar's avatar
wafwerar 已提交
393 394
          buf[bufIndex] = val[i];
          bufIndex++;
wafwerar's avatar
wafwerar 已提交
395 396 397 398
          if (val[i] == '\"') {
            buf[bufIndex] = val[i];
            bufIndex++;
          }
wafwerar's avatar
wafwerar 已提交
399
        }
wafwerar's avatar
wafwerar 已提交
400
        buf[bufIndex] = 0;
wafwerar's avatar
wafwerar 已提交
401
        
wafwerar's avatar
wafwerar 已提交
402
        taosFprintfFile(pFile, "%s%s%s", quotationStr, buf, quotationStr);
wafwerar's avatar
wafwerar 已提交
403
      }
404
      break;
D
Dingle Zhang 已提交
405 406 407 408
    case TSDB_DATA_TYPE_GEOMETRY:
      shellDumpHexValue(buf, val, length);
      taosFprintfFile(pFile, "%s", buf);
      break;
409
    case TSDB_DATA_TYPE_TIMESTAMP:
410
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
X
Xiaoyu Wang 已提交
411
      taosFprintfFile(pFile, "%s%s%s", quotationStr, buf, quotationStr);
412 413 414 415 416 417
      break;
    default:
      break;
  }
}

418
int32_t shellDumpResultToFile(const char *fname, TAOS_RES *tres) {
419 420 421 422 423
  char fullname[PATH_MAX] = {0};
  if (taosExpandDir(fname, fullname, PATH_MAX) != 0) {
    tstrncpy(fullname, fname, PATH_MAX);
  }

424
  TAOS_ROW row = taos_fetch_row(tres);
425 426 427 428
  if (row == NULL) {
    return 0;
  }

429
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
430
  if (pFile == NULL) {
wafwerar's avatar
wafwerar 已提交
431
    fprintf(stderr, "failed to open file: %s\r\n", fullname);
432 433 434
    return -1;
  }

435
  TAOS_FIELD *fields = taos_fetch_fields(tres);
436 437
  int32_t     num_fields = taos_num_fields(tres);
  int32_t     precision = taos_result_precision(tres);
438

439
  for (int32_t col = 0; col < num_fields; col++) {
440
    if (col > 0) {
441
      taosFprintfFile(pFile, ",");
442
    }
443
    taosFprintfFile(pFile, "%s", fields[col].name);
444
  }
wafwerar's avatar
wafwerar 已提交
445
  taosFprintfFile(pFile, "\r\n");
446

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

    numOfRows++;
459
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
460
  } while (row != NULL);
461

462
  taosCloseFile(&pFile);
463

464 465 466
  return numOfRows;
}

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

471
  while (pos < length) {
wafwerar's avatar
wafwerar 已提交
472
    TdWchar wc;
473
    int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
wmmhello's avatar
wmmhello 已提交
474
    if (bytes <= 0) {
475 476
      break;
    }
wmmhello's avatar
wmmhello 已提交
477 478

    if (pos + bytes > length) {
479 480
      break;
    }
wmmhello's avatar
wmmhello 已提交
481
    int w = 0;
X
Xiaoyu Wang 已提交
482
    if (*(str + pos) == '\t' || *(str + pos) == '\n' || *(str + pos) == '\r') {
wmmhello's avatar
wmmhello 已提交
483
      w = bytes;
X
Xiaoyu Wang 已提交
484
    } else {
wmmhello's avatar
wmmhello 已提交
485 486 487 488
      w = taosWcharWidth(wc);
    }
    pos += bytes;

489 490 491 492 493 494 495 496 497 498 499 500 501 502
    if (w <= 0) {
      continue;
    }

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

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
503 504
      printf("%lc", wc);
      cols += w;
505 506 507
    } else {
      tail[tailLen] = wc;
      tailLen++;
508 509 510
    }
  }

511 512
  if (totalCols > width) {
    // width could be 1 or 2, so printf("...") cannot be used
513
    for (int32_t i = 0; i < 3; i++) {
514 515 516 517 518 519 520
      if (cols >= width) {
        break;
      }
      putchar('.');
      ++cols;
    }
  } else {
521
    for (int32_t i = 0; i < tailLen; i++) {
522 523 524 525 526
      printf("%lc", tail[i]);
    }
    cols = totalCols;
  }

527 528 529 530 531
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

D
Dingle Zhang 已提交
532 533 534 535 536 537 538 539 540
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 已提交
541
    }
D
Dingle Zhang 已提交
542 543
    else {
      printf("%.*s...", width - 3, str);
544
    }
D
Dingle Zhang 已提交
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 572 573 574 575 576 577 578
  } 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);
579 580
    return;
  }
H
hzcheng 已提交
581

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

654 655 656
// show whole result for this query return true, like limit or describe
bool shellIsShowWhole(const char *sql) {
  // limit
wafwerar's avatar
wafwerar 已提交
657
  if (taosStrCaseStr(sql, " limit ") != NULL) {
S
Shengliang Guan 已提交
658 659
    return true;
  }
660 661 662 663
  // describe
  if (taosStrCaseStr(sql, "describe ") != NULL) {
    return true;
  }
664
  // show
665 666 667
  if (taosStrCaseStr(sql, "show ") != NULL) {
    return true;
  }
S
Shengliang Guan 已提交
668 669 670 671

  return false;
}

D
dapan1121 已提交
672
bool shellIsShowQuery(const char *sql) {
X
Xiaoyu Wang 已提交
673
  // todo refactor
D
dapan1121 已提交
674 675 676 677 678 679 680
  if (taosStrCaseStr(sql, "show ") != NULL) {
    return true;
  }

  return false;
}

S
Shengliang Guan 已提交
681
int32_t shellVerticalPrintResult(TAOS_RES *tres, const char *sql) {
H
Haojun Liao 已提交
682
  TAOS_ROW row = taos_fetch_row(tres);
683 684 685 686
  if (row == NULL) {
    return 0;
  }

687
  int32_t     num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
688
  TAOS_FIELD *fields = taos_fetch_fields(tres);
689
  int32_t     precision = taos_result_precision(tres);
690

691 692 693
  int32_t maxColNameLen = 0;
  for (int32_t col = 0; col < num_fields; col++) {
    int32_t len = (int32_t)strlen(fields[col].name);
694 695 696 697 698
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
699 700
  uint64_t resShowMaxNum = UINT64_MAX;

701
  if (shell.args.commands == NULL && shell.args.file[0] == 0 && !shellIsShowWhole(sql)) {
702
    resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
D
fix bug  
dapan1121 已提交
703 704
  }

705 706
  int32_t numOfRows = 0;
  int32_t showMore = 1;
707
  do {
D
fix bug  
dapan1121 已提交
708
    if (numOfRows < resShowMaxNum) {
wafwerar's avatar
wafwerar 已提交
709
      printf("*************************** %d.row ***************************\r\n", numOfRows + 1);
D
fix bug  
dapan1121 已提交
710

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

713
      for (int32_t i = 0; i < num_fields; i++) {
S
Shengliang Guan 已提交
714
        TAOS_FIELD *field = fields + i;
715

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

719
        shellPrintField((const char *)row[i], field, 0, length[i], precision);
wafwerar's avatar
wafwerar 已提交
720
        putchar('\r');
D
fix bug  
dapan1121 已提交
721 722
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
723
    } else if (showMore) {
wafwerar's avatar
wafwerar 已提交
724 725 726 727 728 729 730
      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 已提交
731
      showMore = 0;
732 733 734
    }

    numOfRows++;
H
Haojun Liao 已提交
735
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
736
  } while (row != NULL);
737 738 739 740

  return numOfRows;
}

741 742
int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) {
  int32_t width = (int32_t)strlen(field->name);
743 744

  switch (field->type) {
D
dapan1121 已提交
745 746
    case TSDB_DATA_TYPE_NULL:
      return TMAX(4, width);  // null
747
    case TSDB_DATA_TYPE_BOOL:
dengyihao's avatar
dengyihao 已提交
748
      return TMAX(5, width);  // 'false'
749 750

    case TSDB_DATA_TYPE_TINYINT:
751
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
752
      return TMAX(4, width);  // '-127'
753 754

    case TSDB_DATA_TYPE_SMALLINT:
755
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
756
      return TMAX(6, width);  // '-32767'
757 758

    case TSDB_DATA_TYPE_INT:
759
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
760
      return TMAX(11, width);  // '-2147483648'
761 762

    case TSDB_DATA_TYPE_BIGINT:
763
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
764
      return TMAX(21, width);  // '-9223372036854775807'
765 766

    case TSDB_DATA_TYPE_FLOAT:
767
      return TMAX(SHELL_FLOAT_WIDTH, width);
768 769

    case TSDB_DATA_TYPE_DOUBLE:
770
      return TMAX(SHELL_DOUBLE_WIDTH, width);
771 772

    case TSDB_DATA_TYPE_BINARY:
D
Dingle Zhang 已提交
773
    case TSDB_DATA_TYPE_GEOMETRY:
774 775
      if (field->bytes > shell.args.displayWidth) {
        return TMAX(shell.args.displayWidth, width);
776
      } else {
dengyihao's avatar
dengyihao 已提交
777
        return TMAX(field->bytes, width);
778 779
      }

wmmhello's avatar
wmmhello 已提交
780 781
    case TSDB_DATA_TYPE_NCHAR:
    case TSDB_DATA_TYPE_JSON: {
782
      uint16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
783 784
      if (bytes > shell.args.displayWidth) {
        return TMAX(shell.args.displayWidth, width);
785
      } else {
dengyihao's avatar
dengyihao 已提交
786
        return TMAX(bytes, width);
787 788 789
      }
    }

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

802
    default:
X
xinsheng Ren 已提交
803
      ASSERT(false);
H
hzcheng 已提交
804 805
  }

806 807
  return 0;
}
H
hzcheng 已提交
808

809 810 811
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 已提交
812
    TAOS_FIELD *field = fields + col;
813 814
    int32_t     padding = (int32_t)(width[col] - strlen(field->name));
    int32_t     left = padding / 2;
815 816 817 818
    printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " ");
    rowWidth += width[col] + 3;
  }

wafwerar's avatar
wafwerar 已提交
819
  putchar('\r');
820
  putchar('\n');
821
  for (int32_t i = 0; i < rowWidth; i++) {
822 823
    putchar('=');
  }
wafwerar's avatar
wafwerar 已提交
824
  putchar('\r');
825 826 827
  putchar('\n');
}

S
Shengliang Guan 已提交
828
int32_t shellHorizontalPrintResult(TAOS_RES *tres, const char *sql) {
H
Haojun Liao 已提交
829
  TAOS_ROW row = taos_fetch_row(tres);
830 831 832 833
  if (row == NULL) {
    return 0;
  }

834
  int32_t     num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
835
  TAOS_FIELD *fields = taos_fetch_fields(tres);
836
  int32_t     precision = taos_result_precision(tres);
837

838 839 840
  int32_t width[TSDB_MAX_COLUMNS];
  for (int32_t col = 0; col < num_fields; col++) {
    width[col] = shellCalcColWidth(fields + col, precision);
841 842
  }

843
  shellPrintHeader(fields, width, num_fields);
844

D
fix bug  
dapan1121 已提交
845 846
  uint64_t resShowMaxNum = UINT64_MAX;

847
  if (shell.args.commands == NULL && shell.args.file[0] == 0 && !shellIsShowWhole(sql)) {
848
    resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
D
fix bug  
dapan1121 已提交
849 850
  }

851 852
  int32_t numOfRows = 0;
  int32_t showMore = 1;
853

854
  do {
S
Shengliang Guan 已提交
855
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
856
    if (numOfRows < resShowMaxNum) {
857
      for (int32_t i = 0; i < num_fields; i++) {
D
fix bug  
dapan1121 已提交
858
        putchar(' ');
859
        shellPrintField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
860 861 862
        putchar(' ');
        putchar('|');
      }
wafwerar's avatar
wafwerar 已提交
863
      putchar('\r');
D
fix bug  
dapan1121 已提交
864
      putchar('\n');
D
fix bug  
dapan1121 已提交
865
    } else if (showMore) {
wafwerar's avatar
wafwerar 已提交
866 867
      printf("\r\n");
      printf(" Notice: The result shows only the first %d rows.\r\n", SHELL_DEFAULT_RES_SHOW_NUM);
wafwerar's avatar
wafwerar 已提交
868 869 870 871 872 873
      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 已提交
874 875 876
      printf("\r\n");
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
      printf("\r\n");
S
Shengliang Guan 已提交
877
      showMore = 0;
878
    }
879

880
    numOfRows++;
H
Haojun Liao 已提交
881
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
882
  } while (row != NULL);
883 884 885 886

  return numOfRows;
}

S
Shengliang Guan 已提交
887
int32_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical, const char *sql) {
888
  int32_t numOfRows = 0;
H
hzcheng 已提交
889
  if (fname != NULL) {
890
    numOfRows = shellDumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
891
  } else if (vertical) {
S
Shengliang Guan 已提交
892
    numOfRows = shellVerticalPrintResult(tres, sql);
893
  } else {
S
Shengliang Guan 已提交
894
    numOfRows = shellHorizontalPrintResult(tres, sql);
H
hzcheng 已提交
895 896
  }

H
Haojun Liao 已提交
897
  *error_no = taos_errno(tres);
H
hzcheng 已提交
898 899 900
  return numOfRows;
}

901
void shellReadHistory() {
902 903 904
  SShellHistory *pHistory = &shell.history;
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_READ | TD_FILE_STREAM);
  if (pFile == NULL) return;
H
hzcheng 已提交
905

wafwerar's avatar
wafwerar 已提交
906
  char    *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
907
  int32_t read_size = 0;
wafwerar's avatar
wafwerar 已提交
908
  while ((read_size = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) != -1) {
H
hzcheng 已提交
909
    line[read_size - 1] = '\0';
910
    taosMemoryFree(pHistory->hist[pHistory->hend]);
911
    pHistory->hist[pHistory->hend] = taosStrdup(line);
H
hzcheng 已提交
912

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

915 916
    if (pHistory->hend == pHistory->hstart) {
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
917 918 919
    }
  }

wafwerar's avatar
wafwerar 已提交
920
  taosMemoryFreeClear(line);
921
  taosCloseFile(&pFile);
922 923 924 925 926 927 928 929 930 931 932 933 934
  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]);
935 936

    /* coverity[+retval] */
937 938 939
    taosFsyncFile(pFile);
    taosCloseFile(&pFile);
  }
wafwerar's avatar
wafwerar 已提交
940
  pHistory->hstart = pHistory->hend;
H
hzcheng 已提交
941 942
}

943
void shellWriteHistory() {
944
  SShellHistory *pHistory = &shell.history;
945
  if (pHistory->hend == pHistory->hstart) return;
S
Shengliang Guan 已提交
946
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_STREAM | TD_FILE_APPEND);
947
  if (pFile == NULL) return;
H
hzcheng 已提交
948

949 950 951
  for (int32_t i = pHistory->hstart; i != pHistory->hend;) {
    if (pHistory->hist[i] != NULL) {
      taosFprintfFile(pFile, "%s\n", pHistory->hist[i]);
952 953
      taosMemoryFree(pHistory->hist[i]);
      pHistory->hist[i] = NULL;
H
hzcheng 已提交
954
    }
955
    i = (i + 1) % SHELL_MAX_HISTORY_SIZE;
H
hzcheng 已提交
956
  }
957
  taosCloseFile(&pFile);
H
hzcheng 已提交
958 959
}

960 961 962 963 964 965 966 967 968 969
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;
    }
  }
}

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

976 977
bool shellIsCommentLine(char *line) {
  if (line == NULL) return true;
978
  return shellRegexMatch(line, "^\\s*#.*", REG_EXTENDED);
H
hzcheng 已提交
979 980
}

981 982 983 984
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;
985
  char    fullname[PATH_MAX] = {0};
986
  char    sourceFileCommand[PATH_MAX + 8] = {0};
H
hzcheng 已提交
987

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

992 993 994
  sprintf(sourceFileCommand, "source %s;",fullname);
  shellRecordCommandToHistory(sourceFileCommand);

995
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_READ | TD_FILE_STREAM);
996
  if (pFile == NULL) {
wafwerar's avatar
wafwerar 已提交
997
    fprintf(stderr, "failed to open file %s\r\n", fullname);
wafwerar's avatar
wafwerar 已提交
998
    taosMemoryFree(cmd);
H
hzcheng 已提交
999 1000 1001
    return;
  }

wafwerar's avatar
wafwerar 已提交
1002 1003
  char   *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
  while ((read_len = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) != -1) {
H
Haojun Liao 已提交
1004
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
1005 1006
    line[--read_len] = '\0';

1007
    if (read_len == 0 || shellIsCommentLine(line)) {  // line starts with #
H
hzcheng 已提交
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
      continue;
    }

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

1018 1019 1020 1021
    if (line[read_len - 1] == '\r') {
      line[read_len - 1] = ' ';
    }

H
hzcheng 已提交
1022
    memcpy(cmd + cmd_len, line, read_len);
wafwerar's avatar
wafwerar 已提交
1023
    printf("%s%s\r\n", shell.info.promptHeader, cmd);
1024
    shellRunCommand(cmd, false);
H
Haojun Liao 已提交
1025
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
1026 1027 1028
    cmd_len = 0;
  }

wafwerar's avatar
wafwerar 已提交
1029
  taosMemoryFree(cmd);
wafwerar's avatar
wafwerar 已提交
1030
  taosMemoryFreeClear(line);
1031
  taosCloseFile(&pFile);
H
hzcheng 已提交
1032
}
S
slguan 已提交
1033

1034
void shellGetGrantInfo() {
1035 1036
  char sinfo[1024] = {0};
  tstrncpy(sinfo, taos_get_server_info(shell.conn), sizeof(sinfo));
wafwerar's avatar
wafwerar 已提交
1037
  strtok(sinfo, "\r\n");
1038

S
slguan 已提交
1039 1040
  char sql[] = "show grants";

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

1043
  int32_t code = taos_errno(tres);
S
slguan 已提交
1044
  if (code != TSDB_CODE_SUCCESS) {
1045
    if (code != TSDB_CODE_OPS_NOT_SUPPORT && code != TSDB_CODE_MND_NO_RIGHTS && code != TSDB_CODE_PAR_PERMISSION_DENIED) {
wafwerar's avatar
wafwerar 已提交
1046
      fprintf(stderr, "Failed to check Server Edition, Reason:0x%04x:%s\r\n\r\n", code, taos_errstr(tres));
S
slguan 已提交
1047
    }
S
slguan 已提交
1048 1049 1050
    return;
  }

1051
  int32_t num_fields = taos_field_count(tres);
S
slguan 已提交
1052
  if (num_fields == 0) {
wafwerar's avatar
wafwerar 已提交
1053
    fprintf(stderr, "\r\nInvalid grant information.\r\n");
S
slguan 已提交
1054 1055
    exit(0);
  } else {
1056
    if (tres == NULL) {
wafwerar's avatar
wafwerar 已提交
1057
      fprintf(stderr, "\r\nGrant information is null.\r\n");
S
slguan 已提交
1058 1059 1060
      exit(0);
    }

1061
    TAOS_FIELD *fields = taos_fetch_fields(tres);
1062
    TAOS_ROW    row = taos_fetch_row(tres);
S
slguan 已提交
1063
    if (row == NULL) {
wafwerar's avatar
wafwerar 已提交
1064
      fprintf(stderr, "\r\nFailed to get grant information from server. Abort.\r\n");
S
slguan 已提交
1065 1066 1067
      exit(0);
    }

S
slguan 已提交
1068
    char serverVersion[32] = {0};
S
slguan 已提交
1069 1070 1071
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
1072
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
1073 1074 1075
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

1076
    if (strcmp(serverVersion, "community") == 0) {
wafwerar's avatar
wafwerar 已提交
1077
      fprintf(stdout, "Server is Community Edition.\r\n");
1078
    } else if (strcmp(expiretime, "unlimited") == 0) {
wafwerar's avatar
wafwerar 已提交
1079
      fprintf(stdout, "Server is Enterprise %s Edition, %s and will never expire.\r\n", serverVersion, sinfo);
S
slguan 已提交
1080
    } else {
wafwerar's avatar
wafwerar 已提交
1081
      fprintf(stdout, "Server is Enterprise %s Edition, %s and will expire at %s.\r\n", serverVersion, sinfo, expiretime);
S
slguan 已提交
1082 1083
    }

1084
    taos_free_result(tres);
S
slguan 已提交
1085 1086
  }

wafwerar's avatar
wafwerar 已提交
1087
  fprintf(stdout, "\r\n");
1088 1089
}

1090 1091 1092 1093
#ifdef WINDOWS
BOOL shellQueryInterruptHandler(DWORD fdwCtrlType) {
  tsem_post(&shell.cancelSem);
  return TRUE;
1094
}
1095 1096 1097
#else
void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) { tsem_post(&shell.cancelSem); }
#endif
1098

1099 1100 1101 1102 1103
void shellCleanup(void *arg) { taosResetTerminalMode(); }

void *shellCancelHandler(void *arg) {
  setThreadName("shellCancelHandler");
  while (1) {
1104 1105 1106 1107
    if (shell.exit == true) {
      break;
    }

1108 1109 1110 1111
    if (tsem_wait(&shell.cancelSem) != 0) {
      taosMsleep(10);
      continue;
    }
Y
Yang Zhao 已提交
1112 1113

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

  return NULL;
}

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

  do {
1138 1139 1140
    char *command = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE);
    if (command == NULL) {
      printf("failed to malloc command\r\n");
1141 1142 1143
      break;
    }

1144 1145 1146 1147 1148 1149 1150
    do {
      memset(command, 0, SHELL_MAX_COMMAND_SIZE);
      taosSetTerminalMode();

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

1152 1153 1154
      taosResetTerminalMode();
    } while (shellRunCommand(command, true) == 0);

D
Dingle Zhang 已提交
1155
    destroyThreadLocalGeosCtx();
1156 1157 1158 1159
    taosMemoryFreeClear(command);
    shellWriteHistory();
    shellExit();
  } while (0);
1160

1161 1162 1163 1164 1165
  taosThreadCleanupPop(1);
  return NULL;
}

int32_t shellExecute() {
sangshuduo's avatar
sangshuduo 已提交
1166 1167
  printf(shell.info.clientVersion, shell.info.cusName,
         taos_get_client_info(), shell.info.cusName);
1168 1169 1170
  fflush(stdout);

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

1193 1194
  bool runOnce = pArgs->commands != NULL || pArgs->file[0] != 0;
  shellSetConn(shell.conn, runOnce);
1195 1196
  shellReadHistory();

1197
  if (runOnce) {
1198
    if (pArgs->commands != NULL) {
wafwerar's avatar
wafwerar 已提交
1199
      printf("%s%s\r\n", shell.info.promptHeader, pArgs->commands);
1200
      char *cmd = taosStrdup(pArgs->commands);
1201
      shellRunCommand(cmd, true);
1202 1203 1204
      taosMemoryFree(cmd);
    }

1205
    if (pArgs->file[0] != 0) {
1206 1207
      shellSourceFile(pArgs->file);
    }
Y
Yang Zhao 已提交
1208
#ifdef WEBSOCKET
D
dapan1121 已提交
1209 1210 1211
    if (shell.args.restful || shell.args.cloud) {
      ws_close(shell.ws_conn);
    } else {
1212
#endif
D
dapan1121 已提交
1213
      taos_close(shell.conn);
Y
Yang Zhao 已提交
1214
#ifdef WEBSOCKET
D
dapan1121 已提交
1215
    }
Y
Yang Zhao 已提交
1216
#endif
1217 1218

    shellWriteHistory();
1219
    shellCleanupHistory();
1220 1221 1222 1223
    return 0;
  }

  if (tsem_init(&shell.cancelSem, 0, 0) != 0) {
sangshuduo's avatar
sangshuduo 已提交
1224
    printf("failed to create cancel semaphore\r\n");
1225 1226 1227 1228 1229 1230 1231 1232
    return -1;
  }

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

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

1256
  shellCleanupHistory();
A
Alex Duan 已提交
1257 1258 1259
  taos_kill_query(shell.conn);
  taos_close(shell.conn);

1260
  return 0;
S
slguan 已提交
1261
}