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
  char quotationStr[2];
  quotationStr[0] = '\"';
  quotationStr[1] = 0;
329
  int32_t width;
X
Xiaoyu Wang 已提交
330

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

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

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

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

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

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

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

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

463
  taosCloseFile(&pFile);
464

465 466 467
  return numOfRows;
}

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

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

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

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

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

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

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

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

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

583
  int  n;
584 585 586
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
D
Dingle Zhang 已提交
587
      shellPrintString(((((int32_t)(*((char *)val))) == 1) ? "true" : "false"), width);
588 589
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
590
      printf("%*d", width, *((int8_t *)val));
591
      break;
592 593 594
    case TSDB_DATA_TYPE_UTINYINT:
      printf("%*u", width, *((uint8_t *)val));
      break;
595
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
596
      printf("%*d", width, *((int16_t *)val));
597
      break;
598 599 600
    case TSDB_DATA_TYPE_USMALLINT:
      printf("%*u", width, *((uint16_t *)val));
      break;
601
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
602
      printf("%*d", width, *((int32_t *)val));
603
      break;
604 605 606
    case TSDB_DATA_TYPE_UINT:
      printf("%*u", width, *((uint32_t *)val));
      break;
607 608 609
    case TSDB_DATA_TYPE_BIGINT:
      printf("%*" PRId64, width, *((int64_t *)val));
      break;
610 611 612
    case TSDB_DATA_TYPE_UBIGINT:
      printf("%*" PRIu64, width, *((uint64_t *)val));
      break;
613
    case TSDB_DATA_TYPE_FLOAT:
614
      if (tsEnableScience) {
615
        printf("%*.7e",width,GET_FLOAT_VAL(val));
616
      } else {
617
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.7f", width, GET_FLOAT_VAL(val));
618
        if (n > SHELL_FLOAT_WIDTH) {
619 620

            printf("%*.7e", width,GET_FLOAT_VAL(val));
A
Alex Duan 已提交
621 622 623
        } else {
            printf("%s", buf);
        }
624
      }
625 626
      break;
    case TSDB_DATA_TYPE_DOUBLE:
627
      if (tsEnableScience) {
628 629
        snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.15e", width,GET_DOUBLE_VAL(val));
        printf("%s", buf);
630
      } else {
631
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.15f", width, GET_DOUBLE_VAL(val));
632
        if (n > SHELL_DOUBLE_WIDTH) {
A
Alex Duan 已提交
633
            printf("%*.15e", width, GET_DOUBLE_VAL(val));
634
        } else {
635
            printf("%*s", width,buf);
636 637
        }
      }
638 639 640
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
wmmhello's avatar
wmmhello 已提交
641
    case TSDB_DATA_TYPE_JSON:
B
Bomin Zhang 已提交
642
      shellPrintNChar(val, length, width);
643
      break;
D
Dingle Zhang 已提交
644 645 646
    case TSDB_DATA_TYPE_GEOMETRY:
      shellPrintGeometry(val, length, width);
      break;
647
    case TSDB_DATA_TYPE_TIMESTAMP:
648
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
649 650 651 652
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
653
  }
654
}
H
hzcheng 已提交
655

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

  return false;
}

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

  return false;
}

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

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

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

D
fix bug  
dapan1121 已提交
701 702
  uint64_t resShowMaxNum = UINT64_MAX;

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

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

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

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

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

    case TSDB_DATA_TYPE_FLOAT:
769
      return TMAX(SHELL_FLOAT_WIDTH, width);
770 771

    case TSDB_DATA_TYPE_DOUBLE:
772
      return TMAX(SHELL_DOUBLE_WIDTH, width);
773 774

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

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

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

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

808 809
  return 0;
}
H
hzcheng 已提交
810

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

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

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

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

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

845
  shellPrintHeader(fields, width, num_fields);
846

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

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

853 854
  int32_t numOfRows = 0;
  int32_t showMore = 1;
855

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

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

  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

994 995 996
  sprintf(sourceFileCommand, "source %s;",fullname);
  shellRecordCommandToHistory(sourceFileCommand);

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

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

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

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

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

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

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

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

S
slguan 已提交
1041 1042
  char sql[] = "show grants";

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

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

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

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

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

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

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

1086
    taos_free_result(tres);
S
slguan 已提交
1087 1088
  }

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

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

1101 1102 1103 1104 1105
void shellCleanup(void *arg) { taosResetTerminalMode(); }

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

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

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

  return NULL;
}

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

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

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

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

1154 1155 1156
      taosResetTerminalMode();
    } while (shellRunCommand(command, true) == 0);

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

1163 1164 1165 1166 1167
  taosThreadCleanupPop(1);
  return NULL;
}

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

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

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

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

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

    shellWriteHistory();
1221
    shellCleanupHistory();
1222 1223 1224 1225
    return 0;
  }

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

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

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

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

1262
  return 0;
S
slguan 已提交
1263
}