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, "%*e", width, GET_FLOAT_VAL(val));
365
      } else {
366 367
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.5f", width, GET_FLOAT_VAL(val));
        if (n > SHELL_FLOAT_WIDTH) {
368
          taosFprintfFile(pFile, "%*e", 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, "%.9e", GET_DOUBLE_VAL(val));
        taosFprintfFile(pFile, "%*s", width, buf);
379
      } else {
380 381
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.9f", width, GET_DOUBLE_VAL(val));
        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 615 616
      if (tsEnableScience) {
        printf("%*e", width, GET_FLOAT_VAL(val));
      } else {
A
Alex Duan 已提交
617
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.5f", width, GET_FLOAT_VAL(val));
618
        if (n > SHELL_FLOAT_WIDTH) {
A
Alex Duan 已提交
619 620 621 622
            printf("%*e", width, GET_FLOAT_VAL(val));
        } else {
            printf("%s", buf);
        }
623
      }
624 625
      break;
    case TSDB_DATA_TYPE_DOUBLE:
626 627 628 629
      if (tsEnableScience) {
        snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%.9e", GET_DOUBLE_VAL(val));
        printf("%*s", width, buf);
      } else {
A
Alex Duan 已提交
630
        n = snprintf(buf, TSDB_MAX_BYTES_PER_ROW, "%*.9f", width, GET_DOUBLE_VAL(val));
631
        if (n > SHELL_DOUBLE_WIDTH) {
A
Alex Duan 已提交
632
            printf("%*.15e", width, GET_DOUBLE_VAL(val));
633 634 635 636
        } else {
            printf("%s", buf);
        }
      }
637 638 639
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
wmmhello's avatar
wmmhello 已提交
640
    case TSDB_DATA_TYPE_JSON:
B
Bomin Zhang 已提交
641
      shellPrintNChar(val, length, width);
642
      break;
D
Dingle Zhang 已提交
643 644 645
    case TSDB_DATA_TYPE_GEOMETRY:
      shellPrintGeometry(val, length, width);
      break;
646
    case TSDB_DATA_TYPE_TIMESTAMP:
647
      shellFormatTimestamp(buf, *(int64_t *)val, precision);
648 649 650 651
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
652
  }
653
}
H
hzcheng 已提交
654

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

  return false;
}

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

  return false;
}

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

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

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

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

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

844
  shellPrintHeader(fields, width, num_fields);
845

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

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

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

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

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

  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return NULL;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1261
  return 0;
S
slguan 已提交
1262
}