• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #3663

19 Mar 2025 09:21AM UTC coverage: 61.664% (-0.6%) from 62.28%
#3663

push

travis-ci

web-flow
docs: add defination of tmq_config_res_t & fix spell error (#30271)

153169 of 318241 branches covered (48.13%)

Branch coverage included in aggregate %.

239405 of 318390 relevant lines covered (75.19%)

5762846.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.53
/tools/shell/src/shellEngine.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#define ALLOW_FORBID_FUNC
17
#define _BSD_SOURCE
18
#define _GNU_SOURCE
19
#define _XOPEN_SOURCE
20
#define _DEFAULT_SOURCE
21
#include "geosWrapper.h"
22
#include "shellAuto.h"
23
#include "shellInt.h"
24

25
SShellObj shell = {0};
26

27
typedef struct {
28
  const char *sql;
29
  bool        vertical;
30
  tsem_t      sem;
31
  int64_t     numOfRows;  // the num of this batch
32
  int64_t     numOfAllRows;
33

34
  int32_t     numFields;
35
  TAOS_FIELD *fields;
36
  int32_t     precision;
37

38
  int32_t maxColNameLen;            // for vertical print
39
  int32_t width[TSDB_MAX_COLUMNS];  // for horizontal print
40

41
  uint64_t resShowMaxNum;
42
} tsDumpInfo;
43

44
static bool    shellIsEmptyCommand(const char *cmd);
45
static int32_t shellRunSingleCommand(char *command);
46
static void    shellRecordCommandToHistory(char *command);
47
static int32_t shellRunCommand(char *command, bool recordHistory);
48
static void    shellRunSingleCommandImp(char *command);
49
static char   *shellFormatTimestamp(char *buf, int32_t bufSize, int64_t val, int32_t precision);
50
static int64_t shellDumpResultToFile(const char *fname, TAOS_RES *tres);
51
static void    shellPrintNChar(const char *str, int32_t length, int32_t width);
52
static void    shellPrintGeometry(const unsigned char *str, int32_t length, int32_t width);
53
static void    shellVerticalPrintResult(TAOS_RES *tres, tsDumpInfo *dump_info);
54
static void    shellHorizontalPrintResult(TAOS_RES *tres, tsDumpInfo *dump_info);
55
static int64_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical, const char *sql);
56
static void    shellReadHistory();
57
static void    shellWriteHistory();
58
static void    shellPrintError(TAOS_RES *tres, int64_t st);
59
static bool    shellIsCommentLine(char *line);
60
static void    shellSourceFile(const char *file);
61
static int32_t shellGetGrantInfo(char* buf);
62

63
static void  shellCleanup(void *arg);
64
static void *shellCancelHandler(void *arg);
65
static void *shellThreadLoop(void *arg);
66

67
static bool shellCmdkilled = false;
68

69
bool shellIsEmptyCommand(const char *cmd) {
824,585✔
70
  for (char c = *cmd++; c != 0; c = *cmd++) {
1,005,002✔
71
    if (c != ' ' && c != '\t' && c != ';') {
623,672!
72
      return false;
443,255✔
73
    }
74
  }
75
  return true;
381,330✔
76
}
77

78
int32_t shellRunSingleCommand(char *command) {
602,970✔
79
  shellCmdkilled = false;
602,970✔
80

81
  if (shellIsEmptyCommand(command)) {
602,970✔
82
    return 0;
381,330✔
83
  }
84

85
  if (shellRegexMatch(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
221,640✔
86
    return -1;
1✔
87
  }
88

89
  if (shellRegexMatch(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
221,639!
90
#pragma GCC diagnostic push
91
#pragma GCC diagnostic ignored "-Wunused-result"
92
#ifndef TD_ASTRA
93
    system("clear");
×
94
#else
95
    printf("\033[2J\033[H");
96
#endif
97
#pragma GCC diagnostic pop
98
    return 0;
×
99
  }
100

101
  if (shellRegexMatch(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$",
221,639✔
102
                      REG_EXTENDED | REG_ICASE)) {
103
    strtok(command, " \t");
3✔
104
    strtok(NULL, " \t");
3✔
105
    char *p = strtok(NULL, " \t");
3✔
106
    if (strncasecmp(p, "default", 7) == 0) {
3!
107
      shell.args.displayWidth = SHELL_DEFAULT_MAX_BINARY_DISPLAY_WIDTH;
×
108
    } else {
109
      int32_t displayWidth = atoi(p);
3✔
110
      displayWidth = TRANGE(displayWidth, 1, 10 * 1024);
3✔
111
      shell.args.displayWidth = displayWidth;
3✔
112
    }
113
    return 0;
3✔
114
  }
115

116
  if (shellRegexMatch(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
221,636✔
117
    /* If source file. */
118
    char *c_ptr = strtok(command, " ;");
4✔
119
    if (c_ptr == NULL) {
4!
120
      shellRunSingleCommandImp(command);
×
121
      return 0;
×
122
    }
123
    c_ptr = strtok(NULL, " ;");
4✔
124
    if (c_ptr == NULL) {
4!
125
      shellRunSingleCommandImp(command);
×
126
      return 0;
×
127
    }
128
    shellSourceFile(c_ptr);
4✔
129
    return 0;
4✔
130
  }
131
#ifdef WEBSOCKET
132
  if (shell.args.restful || shell.args.cloud) {
221,632✔
133
    shellRunSingleCommandWebsocketImp(command);
91✔
134
  } else {
135
#endif
136
    shellRunSingleCommandImp(command);
221,541✔
137
#ifdef WEBSOCKET
138
  }
139
#endif
140
  return 0;
221,632✔
141
}
142

143
void shellRecordCommandToHistory(char *command) {
13,541✔
144
  if (strncasecmp(command, "create user ", 12) == 0 || strncasecmp(command, "alter user ", 11) == 0) {
13,541!
145
    if (taosStrCaseStr(command, " pass ")) {
6!
146
      // have password command forbid record to history because security
147
      return;
×
148
    }
149
  }
150

151
  SShellHistory *pHistory = &shell.history;
13,541✔
152
  if (pHistory->hstart == pHistory->hend ||
13,541✔
153
      pHistory->hist[(pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE] == NULL ||
4!
154
      strcmp(command, pHistory->hist[(pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE]) != 0) {
4!
155
    if (pHistory->hist[pHistory->hend] != NULL) {
13,541!
156
      taosMemoryFreeClear(pHistory->hist[pHistory->hend]);
13,541!
157
    }
158
    pHistory->hist[pHistory->hend] = taosStrdup(command);
13,541!
159

160
    pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE;
13,541✔
161
    if (pHistory->hend == pHistory->hstart) {
13,541!
162
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
×
163
    }
164
  }
165
}
166

167
int32_t shellRunCommand(char *command, bool recordHistory) {
221,615✔
168
  if (shellIsEmptyCommand(command)) {
221,615!
169
    return 0;
×
170
  }
171

172
  // add help or help;
173
  if (strncasecmp(command, "help", 4) == 0) {
221,615✔
174
    if (command[4] == ';' || command[4] == ' ' || command[4] == 0) {
1!
175
      showHelp();
1✔
176
      return 0;
1✔
177
    }
178
  }
179

180
  if (recordHistory) shellRecordCommandToHistory(command);
221,614✔
181

182
  char quote = 0, *cmd = command;
221,614✔
183
  for (char c = *command++; c != 0; c = *command++) {
158,479,930✔
184
    if (c == '\\' && (*command == '\'' || *command == '"' || *command == '`')) {
158,258,317!
185
      command++;
2✔
186
      continue;
2✔
187
    }
188

189
    if (quote == c) {
158,258,315✔
190
      quote = 0;
2,194,477✔
191
    } else if (quote == 0 && (c == '\'' || c == '"' || c == '`')) {
156,063,838✔
192
      quote = c;
2,194,477✔
193
    } else if (c == ';' && quote == 0) {
153,869,361✔
194
      c = *command;
381,357✔
195
      *command = 0;
381,357✔
196
      if (shellRunSingleCommand(cmd) < 0) {
381,357✔
197
        return -1;
1✔
198
      }
199
      *command = c;
381,356✔
200
      cmd = command;
381,356✔
201
    }
202
  }
203
  return shellRunSingleCommand(cmd);
221,613✔
204
}
205

206
char *strendG(const char *pstr) {
221,632✔
207
  if (pstr == NULL) {
221,632!
208
    return NULL;
×
209
  }
210

211
  size_t len = strlen(pstr);
221,632✔
212
  if (len < 4) {
221,632!
213
    return NULL;
×
214
  }
215

216
  char *p = (char *)pstr + len - 2;
221,632✔
217
  if (strcmp(p, "\\G") == 0) {
221,632✔
218
    return p;
10✔
219
  }
220

221
  p = (char *)pstr + len - 3;
221,622✔
222
  if (strcmp(p, "\\G;") == 0) {
221,622✔
223
    return p;
60✔
224
  }
225

226
  return NULL;
221,562✔
227
}
228

229
void shellRunSingleCommandImp(char *command) {
221,541✔
230
  int64_t st, et;
231
  char   *sptr = NULL;
221,541✔
232
  char   *cptr = NULL;
221,541✔
233
  char   *fname = NULL;
221,541✔
234
  bool    printMode = false;
221,541✔
235

236
  if ((sptr = strstr(command, ">>")) != NULL) {
221,541✔
237
    fname = sptr + 2;
13,158✔
238
    while (*fname == ' ') fname++;
26,303✔
239
    *sptr = '\0';
13,158✔
240

241
    cptr = strstr(fname, ";");
13,158✔
242
    if (cptr != NULL) {
13,158✔
243
      *cptr = '\0';
29✔
244
    }
245
  }
246

247
  if ((sptr = strendG(command)) != NULL) {
221,541✔
248
    *sptr = '\0';
64✔
249
    printMode = true;  // When output to a file, the switch does not work.
64✔
250
  }
251

252
  st = taosGetTimestampUs();
221,541✔
253

254
  TAOS_RES *pSql = taos_query(shell.conn, command);
221,541✔
255
  if (taos_errno(pSql)) {
221,541✔
256
    shellPrintError(pSql, st);
881✔
257
    return;
881✔
258
  }
259

260
  if (shellRegexMatch(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) {
220,660✔
261
    printf("Database changed.\r\n\r\n");
178✔
262

263
    // call back auto tab module
264
    callbackAutoTab(command, pSql, true);
178✔
265

266
    taos_free_result(pSql);
178✔
267

268
    return;
178✔
269
  }
270

271
  // pre string
272
  char *pre = "Query OK";
220,482✔
273
  if (shellRegexMatch(command, "^\\s*delete\\s*from\\s*.*", REG_EXTENDED | REG_ICASE)) {
220,482✔
274
    pre = "Delete OK";
26✔
275
  } else if (shellRegexMatch(command, "^\\s*insert\\s*into\\s*.*", REG_EXTENDED | REG_ICASE)) {
220,456✔
276
    pre = "Insert OK";
117,989✔
277
  } else if (shellRegexMatch(command, "^\\s*create\\s*.*", REG_EXTENDED | REG_ICASE)) {
102,467✔
278
    pre = "Create OK";
4,095✔
279
  } else if (shellRegexMatch(command, "^\\s*drop\\s*.*", REG_EXTENDED | REG_ICASE)) {
98,372✔
280
    pre = "Drop OK";
155✔
281
  }
282

283
  TAOS_FIELD *pFields = taos_fetch_fields(pSql);
220,482✔
284
  if (pFields != NULL) {  // select and show kinds of commands
220,482✔
285
    int32_t error_no = 0;
95,391✔
286

287
    int64_t numOfRows = shellDumpResult(pSql, fname, &error_no, printMode, command);
95,391✔
288
    if (numOfRows < 0) return;
95,391!
289

290
    et = taosGetTimestampUs();
95,391✔
291
    if (error_no == 0) {
95,391!
292
      printf("Query OK, %" PRId64 " row(s) in set (%.6fs)\r\n", numOfRows, (et - st) / 1E6);
95,391✔
293
    } else {
294
      terrno = error_no;
×
295
      printf("Query interrupted (%s), %" PRId64 " row(s) in set (%.6fs)\r\n", taos_errstr(NULL), numOfRows,
×
296
             (et - st) / 1E6);
×
297
    }
298
    taos_free_result(pSql);
95,391✔
299
  } else {
300
    int64_t num_rows_affacted = taos_affected_rows64(pSql);
125,091✔
301
    taos_free_result(pSql);
125,091✔
302
    et = taosGetTimestampUs();
125,091✔
303
    printf("%s, %" PRId64 " row(s) affected (%.6fs)\r\n", pre, num_rows_affacted, (et - st) / 1E6);
125,091✔
304

305
    // call auto tab
306
    callbackAutoTab(command, NULL, false);
125,091✔
307
  }
308

309
  printf("\r\n");
220,482✔
310
}
311

312
char *shellFormatTimestamp(char *buf, int32_t bufSize, int64_t val, int32_t precision) {
4,422,320✔
313
  if (shell.args.is_raw_time) {
4,422,320!
314
    sprintf(buf, "%" PRId64, val);
×
315
    return buf;
×
316
  }
317

318
  time_t  tt;
319
  int32_t ms = 0;
4,422,320✔
320
  if (precision == TSDB_TIME_PRECISION_NANO) {
4,422,320✔
321
    tt = (time_t)(val / 1000000000);
30✔
322
    ms = val % 1000000000;
30✔
323
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
4,422,290✔
324
    tt = (time_t)(val / 1000000);
30✔
325
    ms = val % 1000000;
30✔
326
  } else {
327
    tt = (time_t)(val / 1000);
4,422,260✔
328
    ms = val % 1000;
4,422,260✔
329
  }
330

331
  if (tt <= 0 && ms < 0) {
4,422,320✔
332
    tt--;
6,961✔
333
    if (precision == TSDB_TIME_PRECISION_NANO) {
6,961!
334
      ms += 1000000000;
×
335
    } else if (precision == TSDB_TIME_PRECISION_MICRO) {
6,961!
336
      ms += 1000000;
×
337
    } else {
338
      ms += 1000;
6,961✔
339
    }
340
  }
341

342
  struct tm ptm = {0};
4,422,320✔
343
  if (taosLocalTime(&tt, &ptm, buf, bufSize, NULL) == NULL) {
4,422,320!
344
    return buf;
×
345
  }
346
  size_t pos = strftime(buf, 35, "%Y-%m-%d %H:%M:%S", &ptm);
4,422,320✔
347

348
  if (precision == TSDB_TIME_PRECISION_NANO) {
4,422,320✔
349
    sprintf(buf + pos, ".%09d", ms);
30✔
350
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
4,422,290✔
351
    sprintf(buf + pos, ".%06d", ms);
30✔
352
  } else {
353
    sprintf(buf + pos, ".%03d", ms);
4,422,260✔
354
  }
355

356
  return buf;
4,422,320✔
357
}
358

359
char *shellDumpHexValue(char *buf, const char *val, int32_t length) {
×
360
  for (int32_t i = 0; i < length; i++) {
×
361
    sprintf(buf + (i * 2), "%02X", val[i]);
×
362
  }
363
  buf[length * 2] = 0;
×
364

365
  return buf;
×
366
}
367

368
void shellDumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int32_t precision) {
14,860,711✔
369
  if (val == NULL) {
14,860,711✔
370
    taosFprintfFile(pFile, "NULL");
57,081✔
371
    return;
57,081✔
372
  }
373

374
  char    quotationStr[2] = {'"', 0};
14,803,630✔
375
  int32_t width;
376

377
  int n = 0;
14,803,630✔
378
#define LENGTH 64
379
  char buf[LENGTH] = {0};
14,803,630✔
380
  switch (field->type) {
14,803,630!
381
    case TSDB_DATA_TYPE_BOOL:
3,610,869✔
382
      taosFprintfFile(pFile, "%d", ((((int32_t)(*((char *)val))) == 1) ? 1 : 0));
3,610,869✔
383
      break;
3,610,869✔
384
    case TSDB_DATA_TYPE_TINYINT:
122,823✔
385
      taosFprintfFile(pFile, "%d", *((int8_t *)val));
122,823✔
386
      break;
122,823✔
387
    case TSDB_DATA_TYPE_UTINYINT:
8,854✔
388
      taosFprintfFile(pFile, "%u", *((uint8_t *)val));
8,854✔
389
      break;
8,854✔
390
    case TSDB_DATA_TYPE_SMALLINT:
9,834✔
391
      taosFprintfFile(pFile, "%d", *((int16_t *)val));
9,834✔
392
      break;
9,834✔
393
    case TSDB_DATA_TYPE_USMALLINT:
5,916✔
394
      taosFprintfFile(pFile, "%u", *((uint16_t *)val));
5,916✔
395
      break;
5,916✔
396
    case TSDB_DATA_TYPE_INT:
410,695✔
397
      taosFprintfFile(pFile, "%d", *((int32_t *)val));
410,695✔
398
      break;
410,695✔
399
    case TSDB_DATA_TYPE_UINT:
6,876✔
400
      taosFprintfFile(pFile, "%u", *((uint32_t *)val));
6,876✔
401
      break;
6,876✔
402
    case TSDB_DATA_TYPE_BIGINT:
1,157,093✔
403
      taosFprintfFile(pFile, "%" PRId64, *((int64_t *)val));
1,157,093✔
404
      break;
1,157,093✔
405
    case TSDB_DATA_TYPE_UBIGINT:
11,791✔
406
      taosFprintfFile(pFile, "%" PRIu64, *((uint64_t *)val));
11,791✔
407
      break;
11,791✔
408
    case TSDB_DATA_TYPE_FLOAT:
121,840✔
409
      width = SHELL_FLOAT_WIDTH;
121,840✔
410
      if (tsEnableScience) {
121,840!
411
        taosFprintfFile(pFile, "%*.7e", width, GET_FLOAT_VAL(val));
×
412
      } else {
413
        n = tsnprintf(buf, LENGTH, "%*.7f", width, GET_FLOAT_VAL(val));
121,840✔
414
        if (n > SHELL_FLOAT_WIDTH) {
121,840✔
415
          taosFprintfFile(pFile, "%*.7e", width, GET_FLOAT_VAL(val));
3,200✔
416
        } else {
417
          taosFprintfFile(pFile, "%s", buf);
118,640✔
418
        }
419
      }
420
      break;
121,840✔
421
    case TSDB_DATA_TYPE_DOUBLE:
1,468,005✔
422
      width = SHELL_DOUBLE_WIDTH;
1,468,005✔
423
      if (tsEnableScience) {
1,468,005!
424
        snprintf(buf, LENGTH, "%*.15e", width, GET_DOUBLE_VAL(val));
×
425
        taosFprintfFile(pFile, "%s", buf);
×
426
      } else {
427
        n = tsnprintf(buf, LENGTH, "%*.15f", width, GET_DOUBLE_VAL(val));
1,468,005✔
428
        if (n > SHELL_DOUBLE_WIDTH) {
1,468,005✔
429
          taosFprintfFile(pFile, "%*.15e", width, GET_DOUBLE_VAL(val));
1,096,544✔
430
        } else {
431
          taosFprintfFile(pFile, "%s", buf);
371,461✔
432
        }
433
      }
434
      break;
1,468,005✔
435
    case TSDB_DATA_TYPE_BINARY:
884,052✔
436
    case TSDB_DATA_TYPE_NCHAR:
437
    case TSDB_DATA_TYPE_JSON: {
438
      int32_t bufIndex = 0;
884,052✔
439
      char   *tmp = (char *)taosMemoryCalloc(length * 2 + 1, 1);
884,052!
440
      if (tmp == NULL) break;
884,052!
441
      for (int32_t i = 0; i < length; i++) {
14,547,677✔
442
        tmp[bufIndex] = val[i];
13,663,625✔
443
        bufIndex++;
13,663,625✔
444
        if (val[i] == '\"') {
13,663,625!
445
          tmp[bufIndex] = val[i];
×
446
          bufIndex++;
×
447
        }
448
      }
449
      tmp[bufIndex] = 0;
884,052✔
450

451
      taosFprintfFile(pFile, "%s%s%s", quotationStr, tmp, quotationStr);
884,052✔
452
      taosMemoryFree(tmp);
884,052!
453
    } break;
884,052✔
454
    case TSDB_DATA_TYPE_VARBINARY: {
×
455
      void    *tmp = NULL;
×
456
      uint32_t size = 0;
×
457
      if (taosAscii2Hex(val, length, &tmp, &size) < 0) {
×
458
        break;
×
459
      }
460
      taosFprintfFile(pFile, "%s%s%s", quotationStr, tmp, quotationStr);
×
461
      taosMemoryFree(tmp);
×
462
      break;
×
463
    }
464
    case TSDB_DATA_TYPE_GEOMETRY: {
×
465
      char *tmp = (char *)taosMemoryCalloc(length * 2 + 1, 1);
×
466
      if (tmp == NULL) break;
×
467
      shellDumpHexValue(tmp, val, length);
×
468
      taosFprintfFile(pFile, "%s", buf);
×
469
      taosMemoryFree(tmp);
×
470
      break;
×
471
    }
472
    case TSDB_DATA_TYPE_TIMESTAMP:
556,205✔
473
      shellFormatTimestamp(buf, sizeof(buf), *(int64_t *)val, precision);
556,205✔
474
      taosFprintfFile(pFile, "%s%s%s", quotationStr, buf, quotationStr);
556,205✔
475
      break;
556,205✔
476
    case TSDB_DATA_TYPE_DECIMAL64:
6,428,777✔
477
    case TSDB_DATA_TYPE_DECIMAL:
478
      taosFprintfFile(pFile, "%s", val);
6,428,777✔
479
    default:
6,428,777✔
480
      break;
6,428,777✔
481
  }
482
}
483

484
int64_t shellDumpResultToFile(const char *fname, TAOS_RES *tres) {
12,301✔
485
  char fullname[PATH_MAX] = {0};
12,301✔
486
  if (taosExpandDir(fname, fullname, PATH_MAX) != 0) {
12,301!
487
    tstrncpy(fullname, fname, PATH_MAX);
×
488
  }
489

490
  TAOS_ROW row = taos_fetch_row(tres);
12,301✔
491
  if (row == NULL) {
12,301✔
492
    return 0;
1,035✔
493
  }
494

495
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
11,266✔
496
  if (pFile == NULL) {
11,266!
497
    fprintf(stderr, "failed to open file: %s\r\n", fullname);
×
498
    return -1;
×
499
  }
500

501
  TAOS_FIELD *fields = taos_fetch_fields(tres);
11,266✔
502
  int32_t     num_fields = taos_num_fields(tres);
11,266✔
503
  int32_t     precision = taos_result_precision(tres);
11,266✔
504

505
  for (int32_t col = 0; col < num_fields; col++) {
24,209✔
506
    if (col > 0) {
12,943✔
507
      taosFprintfFile(pFile, ",");
1,677✔
508
    }
509
    taosFprintfFile(pFile, "%s", fields[col].name);
12,943✔
510
  }
511
  taosFprintfFile(pFile, "\r\n");
11,266✔
512

513
  int64_t numOfRows = 0;
11,266✔
514
  do {
515
    int32_t *length = taos_fetch_lengths(tres);
11,474,914✔
516
    for (int32_t i = 0; i < num_fields; i++) {
26,335,585✔
517
      if (i > 0) {
14,860,671✔
518
        taosFprintfFile(pFile, ",");
3,385,757✔
519
      }
520
      shellDumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision);
14,860,671✔
521
    }
522
    taosFprintfFile(pFile, "\r\n");
11,474,914✔
523

524
    numOfRows++;
11,474,914✔
525
    row = taos_fetch_row(tres);
11,474,914✔
526
  } while (row != NULL);
11,474,914✔
527

528
  taosCloseFile(&pFile);
11,266✔
529

530
  return numOfRows;
11,266✔
531
}
532

533
void shellPrintNChar(const char *str, int32_t length, int32_t width) {
2,056,362✔
534
  TdWchar tail[3];
535
  int32_t pos = 0, cols = 0, totalCols = 0, tailLen = 0;
2,056,362✔
536

537
  while (pos < length) {
51,879,075✔
538
    TdWchar wc;
539
    int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
50,665,705✔
540
    if (bytes <= 0) {
50,665,705✔
541
      break;
842,992✔
542
    }
543

544
    if (pos + bytes > length) {
50,665,641!
545
      break;
×
546
    }
547
    int w = 0;
50,665,641✔
548
    if (*(str + pos) == '\t' || *(str + pos) == '\n' || *(str + pos) == '\r') {
50,665,641!
549
      w = bytes;
×
550
    } else {
551
      w = taosWcharWidth(wc);
50,665,641✔
552
    }
553
    pos += bytes;
50,665,641✔
554

555
    if (w <= 0) {
50,665,641✔
556
      continue;
92,067✔
557
    }
558

559
    if (width <= 0) {
50,665,623✔
560
      printf("%lc", wc);
92,049✔
561
      continue;
92,049✔
562
    }
563

564
    totalCols += w;
50,573,574✔
565
    if (totalCols > width) {
50,573,574✔
566
      break;
842,928✔
567
    }
568
    if (totalCols <= (width - 3)) {
49,730,646✔
569
      printf("%lc", wc);
46,814,093✔
570
      cols += w;
46,814,093✔
571
    } else {
572
      tail[tailLen] = wc;
2,916,553✔
573
      tailLen++;
2,916,553✔
574
    }
575
  }
576

577
  if (totalCols > width) {
2,056,362✔
578
    // width could be 1 or 2, so printf("...") cannot be used
579
    for (int32_t i = 0; i < 3; i++) {
3,371,712✔
580
      if (cols >= width) {
2,528,784!
581
        break;
×
582
      }
583
      putchar('.');
2,528,784✔
584
      ++cols;
2,528,784✔
585
    }
586
  } else {
587
    for (int32_t i = 0; i < tailLen; i++) {
1,601,212✔
588
      printf("%lc", tail[i]);
387,778✔
589
    }
590
    cols = totalCols;
1,213,434✔
591
  }
592

593
  for (; cols < width; cols++) {
13,090,919✔
594
    putchar(' ');
11,034,557✔
595
  }
596
}
2,056,362✔
597

598
void shellPrintString(const char *str, int32_t width) {
2,476,261✔
599
  int32_t len = strlen(str);
2,476,261✔
600

601
  if (width == 0) {
2,476,261✔
602
    printf("%s", str);
120✔
603
  } else if (len > width) {
2,476,141✔
604
    if (width <= 3) {
586✔
605
      printf("%.*s.", width - 1, str);
532✔
606
    } else {
607
      printf("%.*s...", width - 3, str);
54✔
608
    }
609
  } else {
610
    printf("%s%*.s", str, width - len, "");
2,475,555✔
611
  }
612
}
2,476,261✔
613

614
void shellPrintGeometry(const unsigned char *val, int32_t length, int32_t width) {
120✔
615
  if (length == 0) {  // empty value
120!
616
    shellPrintString("", width);
×
617
    return;
×
618
  }
619

620
  int32_t code = TSDB_CODE_FAILED;
120✔
621

622
  code = initCtxAsText();
120✔
623
  if (code != TSDB_CODE_SUCCESS) {
120!
624
    shellPrintString(getGeosErrMsg(code), width);
×
625
    return;
×
626
  }
627

628
  char *outputWKT = NULL;
120✔
629
  code = doAsText(val, length, &outputWKT);
120✔
630
  if (code != TSDB_CODE_SUCCESS) {
120!
631
    shellPrintString(getGeosErrMsg(code), width);  // should NOT happen
×
632
    return;
×
633
  }
634

635
  shellPrintString(outputWKT, width);
120✔
636

637
  geosFreeBuffer(outputWKT);
120✔
638
}
639

640
void shellPrintField(const char *val, TAOS_FIELD *field, int32_t width, int32_t length, int32_t precision) {
10,839,921✔
641
  if (val == NULL) {
10,839,921✔
642
    shellPrintString(TSDB_DATA_NULL_STR, width);
2,317,360✔
643
    return;
2,317,360✔
644
  }
645

646
  int n = 0;
8,522,561✔
647
#define LENGTH 64
648
  char buf[LENGTH] = {0};
8,522,561✔
649
  switch (field->type) {
8,522,561!
650
    case TSDB_DATA_TYPE_BOOL:
158,781✔
651
      shellPrintString(((((int32_t)(*((char *)val))) == TSDB_FALSE) ? "false" : "true"), width);
158,781✔
652
      break;
158,781✔
653
    case TSDB_DATA_TYPE_TINYINT:
129,385✔
654
      printf("%*d", width, *((int8_t *)val));
129,385✔
655
      break;
129,385✔
656
    case TSDB_DATA_TYPE_UTINYINT:
84,711✔
657
      printf("%*u", width, *((uint8_t *)val));
84,711✔
658
      break;
84,711✔
659
    case TSDB_DATA_TYPE_SMALLINT:
126,387✔
660
      printf("%*d", width, *((int16_t *)val));
126,387✔
661
      break;
126,387✔
662
    case TSDB_DATA_TYPE_USMALLINT:
87,758✔
663
      printf("%*u", width, *((uint16_t *)val));
87,758✔
664
      break;
87,758✔
665
    case TSDB_DATA_TYPE_INT:
169,818✔
666
      printf("%*d", width, *((int32_t *)val));
169,818✔
667
      break;
169,818✔
668
    case TSDB_DATA_TYPE_UINT:
55,177✔
669
      printf("%*u", width, *((uint32_t *)val));
55,177✔
670
      break;
55,177✔
671
    case TSDB_DATA_TYPE_BIGINT:
1,072,686✔
672
      printf("%*" PRId64, width, taosGetInt64Aligned((int64_t *)val));
1,072,686✔
673
      break;
1,072,686✔
674
    case TSDB_DATA_TYPE_UBIGINT:
38,882✔
675
      printf("%*" PRIu64, width, taosGetUInt64Aligned((uint64_t *)val));
38,882✔
676
      break;
38,882✔
677
    case TSDB_DATA_TYPE_FLOAT:
168,292✔
678
      width = width >= LENGTH ? LENGTH - 1 : width;
168,292✔
679
      if (tsEnableScience) {
168,292!
680
        printf("%*.7e", width, taosGetFloatAligned((float *)val));
×
681
      } else {
682
        snprintf(buf, LENGTH, "%*.*g", width, FLT_DIG, taosGetFloatAligned((float *)val));
168,292✔
683
        printf("%s", buf);
168,292✔
684
      }
685
      break;
168,292✔
686
    case TSDB_DATA_TYPE_DOUBLE:
508,087✔
687
      width = width >= LENGTH ? LENGTH - 1 : width;
508,087✔
688
      if (tsEnableScience) {
508,087!
689
        snprintf(buf, LENGTH, "%*.15e", width, taosGetDoubleAligned((double *)val));
×
690
        printf("%s", buf);
×
691
      } else {
692
        snprintf(buf, LENGTH, "%*.*g", width, DBL_DIG, taosGetDoubleAligned((double *)val));
508,087✔
693
        printf("%*s", width, buf);
508,087✔
694
      }
695
      break;
508,087✔
696
    case TSDB_DATA_TYPE_VARBINARY: {
120✔
697
      void    *data = NULL;
120✔
698
      uint32_t size = 0;
120✔
699
      if (taosAscii2Hex(val, length, &data, &size) < 0) {
120!
700
        break;
×
701
      }
702
      shellPrintNChar(data, size, width);
120✔
703
      taosMemoryFree(data);
120!
704
      break;
120✔
705
    }
706
    case TSDB_DATA_TYPE_BINARY:
2,056,242✔
707
    case TSDB_DATA_TYPE_NCHAR:
708
    case TSDB_DATA_TYPE_JSON:
709
      shellPrintNChar(val, length, width);
2,056,242✔
710
      break;
2,056,242✔
711
    case TSDB_DATA_TYPE_GEOMETRY:
120✔
712
      shellPrintGeometry(val, length, width);
120✔
713
      break;
120✔
714
    case TSDB_DATA_TYPE_TIMESTAMP:
3,866,115✔
715
      shellFormatTimestamp(buf, sizeof(buf), taosGetInt64Aligned((int64_t *)val), precision);
3,866,115✔
716
      printf("%s", buf);
3,866,115✔
717
      break;
3,866,115✔
718
    case TSDB_DATA_TYPE_DECIMAL:
×
719
    case TSDB_DATA_TYPE_DECIMAL64:
720
      printf("%*s", width, val);
×
721
    default:
×
722
      break;
×
723
  }
724
}
725

726
// show whole result for this query return true, like limit or describe
727
bool shellIsShowWhole(const char *sql) {
×
728
  // limit
729
  if (taosStrCaseStr(sql, " limit ") != NULL) {
×
730
    return true;
×
731
  }
732
  // describe
733
  if (taosStrCaseStr(sql, "describe ") != NULL) {
×
734
    return true;
×
735
  }
736
  // desc
737
  if (taosStrCaseStr(sql, "desc ") != NULL) {
×
738
    return true;
×
739
  }
740
  // show
741
  if (taosStrCaseStr(sql, "show ") != NULL) {
×
742
    return true;
×
743
  }
744
  // explain
745
  if (taosStrCaseStr(sql, "explain ") != NULL) {
×
746
    return true;
×
747
  }
748

749
  return false;
×
750
}
751

752
bool shellIsShowQuery(const char *sql) {
×
753
  // todo refactor
754
  if (taosStrCaseStr(sql, "show ") != NULL) {
×
755
    return true;
×
756
  }
757

758
  return false;
×
759
}
760

761
void init_dump_info(tsDumpInfo *dump_info, TAOS_RES *tres, const char *sql, bool vertical) {
83,090✔
762
  dump_info->sql = sql;
83,090✔
763
  dump_info->vertical = vertical;
83,090✔
764
  tsem_init(&dump_info->sem, 0, 0);
83,090✔
765
  dump_info->numOfAllRows = 0;
83,090✔
766

767
  dump_info->numFields = taos_num_fields(tres);
83,090✔
768
  dump_info->fields = taos_fetch_fields(tres);
83,090✔
769
  dump_info->precision = taos_result_precision(tres);
83,090✔
770

771
  dump_info->resShowMaxNum = UINT64_MAX;
83,090✔
772

773
  if (shell.args.commands == NULL && shell.args.file[0] == 0 && !shellIsShowWhole(dump_info->sql)) {
83,090!
774
    dump_info->resShowMaxNum = SHELL_DEFAULT_RES_SHOW_NUM;
×
775
  }
776

777
  if (vertical) {
83,090✔
778
    dump_info->maxColNameLen = 0;
64✔
779
    for (int32_t col = 0; col < dump_info->numFields; col++) {
174✔
780
      int32_t len = (int32_t)strlen(dump_info->fields[col].name);
110✔
781
      if (len > dump_info->maxColNameLen) {
110✔
782
        dump_info->maxColNameLen = len;
74✔
783
      }
784
    }
785
  } else {
786
    for (int32_t col = 0; col < dump_info->numFields; col++) {
249,546✔
787
      dump_info->width[col] = shellCalcColWidth(dump_info->fields + col, dump_info->precision);
166,520✔
788
    }
789
  }
790
}
83,090✔
791

792
void shellVerticalPrintResult(TAOS_RES *tres, tsDumpInfo *dump_info) {
64✔
793
  TAOS_ROW row = taos_fetch_row(tres);
64✔
794
  if (row == NULL) {
64!
795
    printf("\033[31mtaos_fetch_row failed.\033[0m\n");
×
796
    return;
×
797
  }
798

799
  int64_t numOfPintRows = dump_info->numOfAllRows;
64✔
800
  int     numOfPrintRowsThisOne = 0;
64✔
801

802
  while (row != NULL) {
1,551!
803
    printf("*************************** %" PRId64 ".row ***************************\r\n", numOfPintRows + 1);
1,551✔
804

805
    int32_t *length = taos_fetch_lengths(tres);
1,551✔
806

807
    for (int32_t i = 0; i < dump_info->numFields; i++) {
3,478✔
808
      TAOS_FIELD *field = dump_info->fields + i;
1,927✔
809

810
      int32_t padding = (int32_t)(dump_info->maxColNameLen - strlen(field->name));
1,927✔
811
      printf("%*.s%s: ", padding, " ", field->name);
1,927✔
812

813
      shellPrintField((const char *)row[i], field, 0, length[i], dump_info->precision);
1,927✔
814
      putchar('\r');
1,927✔
815
      putchar('\n');
1,927✔
816
    }
817

818
    numOfPintRows++;
1,551✔
819
    numOfPrintRowsThisOne++;
1,551✔
820

821
    if (numOfPintRows == dump_info->resShowMaxNum) {
1,551!
822
      printf("\r\n");
×
823
      printf(" Notice: The result shows only the first %d rows.\r\n", SHELL_DEFAULT_RES_SHOW_NUM);
×
824
      printf("         You can use the `LIMIT` clause to get fewer result to show.\r\n");
×
825
      printf("           Or use '>>' to redirect the whole set of the result to a specified file.\r\n");
×
826
      printf("\r\n");
×
827
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
×
828
      printf("\r\n");
×
829
      return;
×
830
    }
831

832
    if (numOfPrintRowsThisOne == dump_info->numOfRows) {
1,551✔
833
      return;
64✔
834
    }
835

836
    row = taos_fetch_row(tres);
1,487✔
837
  }
838
  return;
×
839
}
840

841
int32_t shellCalcColWidth(TAOS_FIELD *field, int32_t precision) {
166,664✔
842
  int32_t width = (int32_t)strlen(field->name);
166,664✔
843

844
  switch (field->type) {
166,664!
845
    case TSDB_DATA_TYPE_NULL:
×
846
      return TMAX(4, width);  // null
×
847
    case TSDB_DATA_TYPE_BOOL:
1,721✔
848
      return TMAX(5, width);  // 'false'
1,721✔
849

850
    case TSDB_DATA_TYPE_TINYINT:
2,887✔
851
    case TSDB_DATA_TYPE_UTINYINT:
852
      return TMAX(4, width);  // '-127'
2,887✔
853

854
    case TSDB_DATA_TYPE_SMALLINT:
4,669✔
855
    case TSDB_DATA_TYPE_USMALLINT:
856
      return TMAX(6, width);  // '-32767'
4,669✔
857

858
    case TSDB_DATA_TYPE_INT:
8,106✔
859
    case TSDB_DATA_TYPE_UINT:
860
      return TMAX(11, width);  // '-2147483648'
8,106✔
861

862
    case TSDB_DATA_TYPE_BIGINT:
32,367✔
863
    case TSDB_DATA_TYPE_UBIGINT:
864
      return TMAX(21, width);  // '-9223372036854775807'
32,367✔
865

866
    case TSDB_DATA_TYPE_FLOAT:
2,039✔
867
      return TMAX(SHELL_FLOAT_WIDTH, width);
2,039✔
868

869
    case TSDB_DATA_TYPE_DOUBLE:
20,125✔
870
      return TMAX(SHELL_DOUBLE_WIDTH, width);
20,125✔
871

872
    case TSDB_DATA_TYPE_BINARY:
46,531✔
873
    case TSDB_DATA_TYPE_GEOMETRY:
874
      if (field->bytes > shell.args.displayWidth) {
46,531✔
875
        return TMAX(shell.args.displayWidth, width);
38,584✔
876
      } else {
877
        return TMAX(field->bytes + 2, width);
7,947✔
878
      }
879
    case TSDB_DATA_TYPE_VARBINARY: {
10✔
880
      int32_t bytes = field->bytes * 2 + 2;
10✔
881
      if (bytes > shell.args.displayWidth) {
10!
882
        return TMAX(shell.args.displayWidth, width);
×
883
      } else {
884
        return TMAX(bytes + 2, width);
10✔
885
      }
886
    }
887
    case TSDB_DATA_TYPE_NCHAR:
10,910✔
888
    case TSDB_DATA_TYPE_JSON: {
889
      uint16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
10,910✔
890
      if (bytes > shell.args.displayWidth) {
10,910!
891
        return TMAX(shell.args.displayWidth, width);
10,910✔
892
      } else {
893
        return TMAX(bytes + 2, width);
×
894
      }
895
    }
896

897
    case TSDB_DATA_TYPE_TIMESTAMP:
37,299✔
898
      if (shell.args.is_raw_time) {
37,299✔
899
        return TMAX(14, width);
1✔
900
      }
901
      if (precision == TSDB_TIME_PRECISION_NANO) {
37,298✔
902
        return TMAX(29, width);
3✔
903
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
37,295✔
904
        return TMAX(26, width);  // '2020-01-01 00:00:00.000000'
3✔
905
      } else {
906
        return TMAX(23, width);  // '2020-01-01 00:00:00.000'
37,292✔
907
      }
908
    case TSDB_DATA_TYPE_DECIMAL64:
×
909
      return TMAX(width, 20);
×
910
    case TSDB_DATA_TYPE_DECIMAL:
×
911
      return TMAX(width, 40);
×
912
    default:
×
913
      ASSERT(false);
×
914
  }
915

916
  return 0;
×
917
}
918

919
void shellPrintHeader(TAOS_FIELD *fields, int32_t *width, int32_t num_fields) {
77,378✔
920
  int32_t rowWidth = 0;
77,378✔
921
  for (int32_t col = 0; col < num_fields; col++) {
225,289✔
922
    TAOS_FIELD *field = fields + col;
147,911✔
923
    int32_t     padding = (int32_t)(width[col] - strlen(field->name));
147,911✔
924
    int32_t     left = padding / 2;
147,911✔
925
    printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " ");
147,911✔
926
    rowWidth += width[col] + 3;
147,911✔
927
  }
928

929
  putchar('\r');
77,378✔
930
  putchar('\n');
77,378✔
931
  for (int32_t i = 0; i < rowWidth; i++) {
4,211,127✔
932
    putchar('=');
4,133,749✔
933
  }
934
  putchar('\r');
77,378✔
935
  putchar('\n');
77,378✔
936
}
77,378✔
937

938
void shellHorizontalPrintResult(TAOS_RES *tres, tsDumpInfo *dump_info) {
80,904✔
939
  TAOS_ROW row = taos_fetch_row(tres);
80,904✔
940
  if (row == NULL) {
80,904!
941
    printf("\033[31mtaos_fetch_row failed.\033[0m\n");
×
942
    return;
×
943
  }
944

945
  int64_t numOfPintRows = dump_info->numOfAllRows;
80,904✔
946
  int     numOfPrintRowsThisOne = 0;
80,904✔
947
  if (numOfPintRows == 0) {
80,904✔
948
    shellPrintHeader(dump_info->fields, dump_info->width, dump_info->numFields);
77,356✔
949
  }
950

951
  while (row != NULL) {
3,094,814!
952
    int32_t *length = taos_fetch_lengths(tres);
3,094,814✔
953
    for (int32_t i = 0; i < dump_info->numFields; i++) {
13,931,084✔
954
      putchar(' ');
10,836,270✔
955
      shellPrintField((const char *)row[i], dump_info->fields + i, dump_info->width[i], length[i],
10,836,270✔
956
                      dump_info->precision);
957
      putchar(' ');
10,836,270✔
958
      putchar('|');
10,836,270✔
959
    }
960
    putchar('\r');
3,094,814✔
961
    putchar('\n');
3,094,814✔
962

963
    numOfPintRows++;
3,094,814✔
964
    numOfPrintRowsThisOne++;
3,094,814✔
965

966
    if (numOfPintRows == dump_info->resShowMaxNum) {
3,094,814!
967
      printf("\r\n");
×
968
      printf(" Notice: The result shows only the first %d rows.\r\n", SHELL_DEFAULT_RES_SHOW_NUM);
×
969
      if (shellIsShowQuery(dump_info->sql)) {
×
970
        printf("         You can use '>>' to redirect the whole set of the result to a specified file.\r\n");
×
971
      } else {
972
        printf("         You can use the `LIMIT` clause to get fewer result to show.\r\n");
×
973
        printf("           Or use '>>' to redirect the whole set of the result to a specified file.\r\n");
×
974
      }
975
      printf("\r\n");
×
976
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
×
977
      printf("\r\n");
×
978
      return;
×
979
    }
980

981
    if (numOfPrintRowsThisOne == dump_info->numOfRows) {
3,094,814✔
982
      return;
80,904✔
983
    }
984

985
    row = taos_fetch_row(tres);
3,013,910✔
986
  }
987
  return;
×
988
}
989

990
void shellDumpResultCallback(void *param, TAOS_RES *tres, int num_of_rows) {
164,058✔
991
  tsDumpInfo *dump_info = (tsDumpInfo *)param;
164,058✔
992
  if (num_of_rows > 0) {
164,058✔
993
    dump_info->numOfRows = num_of_rows;
80,968✔
994
    if (dump_info->numOfAllRows < dump_info->resShowMaxNum) {
80,968!
995
      if (dump_info->vertical) {
80,968✔
996
        shellVerticalPrintResult(tres, dump_info);
64✔
997
      } else {
998
        shellHorizontalPrintResult(tres, dump_info);
80,904✔
999
      }
1000
    }
1001
    dump_info->numOfAllRows += num_of_rows;
80,968✔
1002
    if (!shellCmdkilled) {
80,968!
1003
      taos_fetch_rows_a(tres, shellDumpResultCallback, param);
80,968✔
1004
    } else {
1005
      tsem_post(&dump_info->sem);
×
1006
    }
1007
  } else {
1008
    if (num_of_rows < 0) {
83,090!
1009
      printf("\033[31masync retrieve failed, code: %d\033, %s[0m\n", num_of_rows, tstrerror(num_of_rows));
×
1010
    }
1011
    tsem_post(&dump_info->sem);
83,090✔
1012
  }
1013
}
164,058✔
1014

1015
int64_t shellDumpResult(TAOS_RES *tres, char *fname, int32_t *error_no, bool vertical, const char *sql) {
95,391✔
1016
  int64_t num_of_rows = 0;
95,391✔
1017
  if (fname != NULL) {
95,391✔
1018
    num_of_rows = shellDumpResultToFile(fname, tres);
12,301✔
1019
  } else {
1020
    tsDumpInfo dump_info;
1021
    if (!shellCmdkilled) {
83,090!
1022
      init_dump_info(&dump_info, tres, sql, vertical);
83,090✔
1023
      taos_fetch_rows_a(tres, shellDumpResultCallback, &dump_info);
83,090✔
1024
      tsem_wait(&dump_info.sem);
83,090✔
1025
      num_of_rows = dump_info.numOfAllRows;
83,090✔
1026
    }
1027
  }
1028

1029
  *error_no = shellCmdkilled ? TSDB_CODE_TSC_QUERY_KILLED : taos_errno(tres);
95,391!
1030
  return num_of_rows;
95,391✔
1031
}
1032

1033
void shellReadHistory() {
13,538✔
1034
  SShellHistory *pHistory = &shell.history;
13,538✔
1035
  TdFilePtr      pFile = taosOpenFile(pHistory->file, TD_FILE_READ | TD_FILE_STREAM);
13,538✔
1036
  if (pFile == NULL) return;
13,538!
1037

1038
  char   *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
13,538!
1039
  int32_t read_size = 0;
13,538✔
1040
  while ((read_size = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) > 0) {
96,136,952✔
1041
    line[read_size - 1] = '\0';
96,123,414✔
1042
    taosMemoryFree(pHistory->hist[pHistory->hend]);
96,123,414!
1043
    pHistory->hist[pHistory->hend] = taosStrdup(line);
96,123,414!
1044

1045
    pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE;
96,123,414✔
1046

1047
    if (pHistory->hend == pHistory->hstart) {
96,123,414✔
1048
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
82,598,952✔
1049
    }
1050
  }
1051

1052
  taosMemoryFreeClear(line);
13,538!
1053
  taosCloseFile(&pFile);
13,538✔
1054
  int64_t file_size;
1055
  if (taosStatFile(pHistory->file, &file_size, NULL, NULL) == 0 && file_size > SHELL_MAX_COMMAND_SIZE) {
13,538!
1056
    TdFilePtr pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_STREAM | TD_FILE_TRUNC);
1✔
1057
    if (pFile == NULL) return;
1!
1058
    int32_t endIndex = pHistory->hstart;
1✔
1059
    if (endIndex != 0) {
1!
1060
      endIndex = pHistory->hend;
1✔
1061
    }
1062
    for (int32_t i = (pHistory->hend + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE; i != endIndex;) {
1,000✔
1063
      taosFprintfFile(pFile, "%s\n", pHistory->hist[i]);
999✔
1064
      i = (i + SHELL_MAX_HISTORY_SIZE - 1) % SHELL_MAX_HISTORY_SIZE;
999✔
1065
    }
1066
    taosFprintfFile(pFile, "%s\n", pHistory->hist[endIndex]);
1✔
1067

1068
    /* coverity[+retval] */
1069
    taosFsyncFile(pFile);
1✔
1070
    taosCloseFile(&pFile);
1✔
1071
  }
1072
  pHistory->hstart = pHistory->hend;
13,538✔
1073
}
1074

1075
void shellWriteHistory() {
13,538✔
1076
  SShellHistory *pHistory = &shell.history;
13,538✔
1077
  if (pHistory->hend == pHistory->hstart) return;
13,538✔
1078
  TdFilePtr pFile = taosOpenFile(pHistory->file, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_STREAM | TD_FILE_APPEND);
13,537✔
1079
  if (pFile == NULL) return;
13,537!
1080

1081
  for (int32_t i = pHistory->hstart; i != pHistory->hend;) {
27,078✔
1082
    if (pHistory->hist[i] != NULL) {
13,541!
1083
      taosFprintfFile(pFile, "%s\n", pHistory->hist[i]);
13,541✔
1084
      taosMemoryFree(pHistory->hist[i]);
13,541!
1085
      pHistory->hist[i] = NULL;
13,541✔
1086
    }
1087
    i = (i + 1) % SHELL_MAX_HISTORY_SIZE;
13,541✔
1088
  }
1089
  taosCloseFile(&pFile);
13,537✔
1090
}
1091

1092
void shellCleanupHistory() {
13,538✔
1093
  SShellHistory *pHistory = &shell.history;
13,538✔
1094
  for (int32_t i = 0; i < SHELL_MAX_HISTORY_SIZE; ++i) {
13,551,538✔
1095
    if (pHistory->hist[i] != NULL) {
13,538,000✔
1096
      taosMemoryFree(pHistory->hist[i]);
13,524,459!
1097
      pHistory->hist[i] = NULL;
13,524,459✔
1098
    }
1099
  }
1100
}
13,538✔
1101

1102
void shellPrintError(TAOS_RES *tres, int64_t st) {
881✔
1103
  int64_t et = taosGetTimestampUs();
881✔
1104
  fprintf(stderr, "\r\nDB error: %s[0x%08X] (%.6fs)\r\n", taos_errstr(tres), taos_errno(tres), (et - st) / 1E6);
881✔
1105
  taos_free_result(tres);
881✔
1106
}
881✔
1107

1108
bool shellIsCommentLine(char *line) {
208,307✔
1109
  if (line == NULL) return true;
208,307!
1110
  return shellRegexMatch(line, "^\\s*#.*", REG_EXTENDED);
208,307✔
1111
}
1112

1113
void shellSourceFile(const char *file) {
234✔
1114
  int32_t read_len = 0;
234✔
1115
  char   *cmd = taosMemoryCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1);
234!
1116
  size_t  cmd_len = 0;
234✔
1117
  char    fullname[PATH_MAX] = {0};
234✔
1118
  char    sourceFileCommand[PATH_MAX + 8] = {0};
234✔
1119

1120
  if (taosExpandDir(file, fullname, PATH_MAX) != 0) {
234!
1121
    tstrncpy(fullname, file, PATH_MAX);
×
1122
  }
1123

1124
  sprintf(sourceFileCommand, "source %s;", fullname);
234✔
1125
  shellRecordCommandToHistory(sourceFileCommand);
234✔
1126

1127
  TdFilePtr pFile = taosOpenFile(fullname, TD_FILE_READ | TD_FILE_STREAM);
234✔
1128
  if (pFile == NULL) {
234!
1129
    fprintf(stderr, "failed to open file %s\r\n", fullname);
×
1130
    taosMemoryFree(cmd);
×
1131
    return;
×
1132
  }
1133

1134
  char *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
234!
1135
  while ((read_len = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) > 0) {
208,884✔
1136
    if (cmd_len + read_len >= TSDB_MAX_ALLOWED_SQL_LEN) {
208,650!
1137
      printf("read command line too long over 1M, ignore this line. cmd_len = %d read_len=%d \n", (int32_t)cmd_len,
×
1138
             read_len);
1139
      cmd_len = 0;
×
1140
      memset(line, 0, TSDB_MAX_ALLOWED_SQL_LEN + 1);
×
1141
      continue;
×
1142
    }
1143
    line[--read_len] = '\0';
208,650✔
1144

1145
    if (read_len == 0 || shellIsCommentLine(line)) {  // line starts with #
208,650!
1146
      continue;
343✔
1147
    }
1148

1149
    if (line[read_len - 1] == '\\') {
208,307!
1150
      line[read_len - 1] = ' ';
×
1151
      memcpy(cmd + cmd_len, line, read_len);
×
1152
      cmd_len += read_len;
×
1153
      continue;
×
1154
    }
1155

1156
    if (line[read_len - 1] == '\r') {
208,307!
1157
      line[read_len - 1] = ' ';
×
1158
    }
1159

1160
    memcpy(cmd + cmd_len, line, read_len);
208,307✔
1161
    printf("%s%s\r\n", shell.info.promptHeader, cmd);
208,307✔
1162
    shellRunCommand(cmd, false);
208,307✔
1163
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
208,307✔
1164
    cmd_len = 0;
208,307✔
1165
  }
1166

1167
  taosMemoryFree(cmd);
234!
1168
  taosMemoryFreeClear(line);
234!
1169
  taosCloseFile(&pFile);
234✔
1170
}
1171

1172
int32_t shellGetGrantInfo(char *buf) {
5✔
1173
  int32_t verType = TSDB_VERSION_UNKNOWN;
5✔
1174
  char    sinfo[256] = {0};
5✔
1175
  tstrncpy(sinfo, taos_get_server_info(shell.conn), sizeof(sinfo));
5✔
1176
  strtok(sinfo, "\r\n");
5✔
1177

1178
#ifndef TD_ASTRA
1179
  char sql[] = "show grants";
5✔
1180

1181
  TAOS_RES *tres = taos_query(shell.conn, sql);
5✔
1182

1183
  int32_t code = taos_errno(tres);
5✔
1184
  if (code != TSDB_CODE_SUCCESS) {
5!
1185
    if (code != TSDB_CODE_OPS_NOT_SUPPORT && code != TSDB_CODE_MND_NO_RIGHTS &&
×
1186
        code != TSDB_CODE_PAR_PERMISSION_DENIED) {
1187
      fprintf(stderr, "Failed to check Server Edition, Reason:0x%04x:%s\r\n\r\n", code, taos_errstr(tres));
×
1188
    }
1189
    taos_free_result(tres);
×
1190
    return verType;
×
1191
  }
1192

1193
  int32_t num_fields = taos_field_count(tres);
5✔
1194
  if (num_fields == 0) {
5!
1195
    fprintf(stderr, "\r\nInvalid grant information.\r\n");
×
1196
    exit(0);
×
1197
  } else {
1198
    if (tres == NULL) {
5!
1199
      fprintf(stderr, "\r\nGrant information is null.\r\n");
×
1200
      exit(0);
×
1201
    }
1202

1203
    TAOS_FIELD *fields = taos_fetch_fields(tres);
5✔
1204
    TAOS_ROW    row = taos_fetch_row(tres);
5✔
1205
    if (row == NULL) {
5!
1206
      fprintf(stderr, "\r\nFailed to get grant information from server. Abort.\r\n");
×
1207
      exit(0);
×
1208
    }
1209
    char serverVersion[64] = {0};
5✔
1210
    char expiretime[32] = {0};
5✔
1211
    char expired[32] = {0};
5✔
1212

1213
    tstrncpy(serverVersion, row[0], 64);
5✔
1214
    memcpy(expiretime, row[1], fields[1].bytes);
5✔
1215
    memcpy(expired, row[2], fields[2].bytes);
5✔
1216

1217
    if (strcmp(serverVersion, "community") == 0) {
5!
1218
      verType = TSDB_VERSION_OSS;
×
1219
    } else if (strcmp(expiretime, "unlimited") == 0) {
5!
1220
      verType = TSDB_VERSION_ENTERPRISE;
×
1221
      sprintf(buf, "Server is %s, %s and will never expire.\r\n", serverVersion, sinfo);
×
1222
    } else {
1223
      verType = TSDB_VERSION_ENTERPRISE;
5✔
1224
      sprintf(buf, "Server is %s, %s and will expire at %s.\r\n", serverVersion, sinfo, expiretime);
5✔
1225
    }
1226

1227
    taos_free_result(tres);
5✔
1228
  }
1229

1230
  fprintf(stdout, "\r\n");
5✔
1231
#else
1232
  verType = TSDB_VERSION_ENTERPRISE;
1233
  sprintf(buf, "Server is %s, %s and will never expire.\r\n", TD_PRODUCT_NAME, sinfo);
1234
#endif
1235
  return verType;
5✔
1236
}
1237

1238
#ifdef WINDOWS
1239
BOOL shellQueryInterruptHandler(DWORD fdwCtrlType) {
1240
  tsem_post(&shell.cancelSem);
1241
  return TRUE;
1242
}
1243
#else
1244
void shellQueryInterruptHandler(int32_t signum, void *sigInfo, void *context) { tsem_post(&shell.cancelSem); }
6✔
1245
#endif
1246

1247
void shellCleanup(void *arg) { taosResetTerminalMode(); }
5✔
1248

1249
void *shellCancelHandler(void *arg) {
5✔
1250
  setThreadName("shellCancelHandler");
5✔
1251
  while (1) {
1252
    if (shell.exit == true) {
15✔
1253
      break;
5✔
1254
    }
1255

1256
    if (tsem_wait(&shell.cancelSem) != 0) {
10!
1257
      taosMsleep(10);
×
1258
      continue;
×
1259
    }
1260

1261
#ifdef WEBSOCKET
1262
    if (shell.args.restful || shell.args.cloud) {
10!
1263
      shell.stop_query = true;
×
1264
    } else {
1265
#endif
1266
      if (shell.conn) {
10✔
1267
        shellCmdkilled = true;
5✔
1268
        taos_kill_query(shell.conn);
5✔
1269
      }
1270
#ifdef WEBSOCKET
1271
    }
1272
#endif
1273
#ifdef WINDOWS
1274
    printf("\n%s", shell.info.promptHeader);
1275
#endif
1276
  }
1277

1278
  return NULL;
5✔
1279
}
1280

1281
#pragma GCC diagnostic push
1282
#pragma GCC diagnostic ignored "-Wstringop-overflow"
1283

1284
void *shellThreadLoop(void *arg) {
5✔
1285
  setThreadName("shellThreadLoop");
5✔
1286
  taosGetOldTerminalMode();
5✔
1287
  taosThreadCleanupPush(shellCleanup, NULL);
5!
1288

1289
  do {
1290
    char *command = taosMemoryMalloc(SHELL_MAX_COMMAND_SIZE);
5!
1291
    if (command == NULL) {
5!
1292
      printf("failed to malloc command\r\n");
×
1293
      break;
×
1294
    }
1295

1296
    do {
1297
      memset(command, 0, SHELL_MAX_COMMAND_SIZE);
10✔
1298
      taosSetTerminalMode();
10✔
1299

1300
      if (shellReadCommand(command) != 0) {
10✔
1301
        break;
5✔
1302
      }
1303

1304
      taosResetTerminalMode();
5✔
1305
    } while (shellRunCommand(command, true) == 0);
5!
1306

1307
    taosMemoryFreeClear(command);
5!
1308
    shellWriteHistory();
5✔
1309
    shellExit();
5✔
1310
  } while (0);
1311

1312
  taosThreadCleanupPop(1);
5✔
1313
  return NULL;
5✔
1314
}
1315
#pragma GCC diagnostic pop
1316

1317
int32_t shellExecute(int argc, char *argv[]) {
13,567✔
1318
  int32_t code = 0;
13,567✔
1319
  printf(shell.info.clientVersion, shell.info.cusName, taos_get_client_info(), shell.info.cusName);
13,567✔
1320
  fflush(stdout);
13,567✔
1321

1322
  SShellArgs *pArgs = &shell.args;
13,567✔
1323
#ifdef WEBSOCKET
1324
  if (shell.args.restful || shell.args.cloud) {
13,567✔
1325
    if (shell_conn_ws_server(1)) {
19✔
1326
      printf("failed to connect to server, reason: %s[0x%08X]\n%s", ws_errstr(NULL), ws_errno(NULL), ERROR_CODE_DETAIL);
4✔
1327
      fflush(stdout);
4✔
1328
      return -1;
4✔
1329
    }
1330
  } else {
1331
#endif
1332
    if (shell.args.auth == NULL) {
13,548✔
1333
      shell.conn = taos_connect(pArgs->host, pArgs->user, pArgs->password, pArgs->database, pArgs->port);
13,544✔
1334
    } else {
1335
      shell.conn = taos_connect_auth(pArgs->host, pArgs->user, pArgs->auth, pArgs->database, pArgs->port);
4✔
1336
    }
1337

1338
    if (shell.conn == NULL) {
13,548✔
1339
      printf("failed to connect to server, reason: %s[0x%08X]\n%s", taos_errstr(NULL), taos_errno(NULL), ERROR_CODE_DETAIL);
25✔
1340
      fflush(stdout);
25✔
1341
      return -1;
25✔
1342
    }
1343
#ifdef WEBSOCKET
1344
  }
1345
#endif
1346

1347
  bool runOnce = pArgs->commands != NULL || pArgs->file[0] != 0;
13,538✔
1348
  shellSetConn(shell.conn, runOnce);
13,538✔
1349
  shellReadHistory();
13,538✔
1350

1351
  if (shell.args.is_bi_mode) {
13,538✔
1352
    // need set bi mode
1353
    printf("Set BI mode is true.\n");
4✔
1354
#ifndef WEBSOCKET
1355
    taos_set_conn_mode(shell.conn, TAOS_CONN_MODE_BI, 1);
1356
#endif
1357
  }
1358

1359
  if (runOnce) {
13,538✔
1360
    if (pArgs->commands != NULL) {
13,533✔
1361
      printf("%s%s\r\n", shell.info.promptHeader, pArgs->commands);
13,303✔
1362
      char *cmd = taosStrdup(pArgs->commands);
13,303!
1363
      shellRunCommand(cmd, true);
13,303✔
1364
      taosMemoryFree(cmd);
13,303!
1365
    }
1366

1367
    if (pArgs->file[0] != 0) {
13,533✔
1368
      shellSourceFile(pArgs->file);
230✔
1369
    }
1370
#ifdef WEBSOCKET
1371
    if (shell.args.restful || shell.args.cloud) {
13,533✔
1372
      ws_close(shell.ws_conn);
15✔
1373
    } else {
1374
#endif
1375
      taos_close(shell.conn);
13,518✔
1376
#ifdef WEBSOCKET
1377
    }
1378
#endif
1379

1380
    shellWriteHistory();
13,533✔
1381
    shellCleanupHistory();
13,533✔
1382
    return 0;
13,533✔
1383
  }
1384

1385
  if ((code = tsem_init(&shell.cancelSem, 0, 0)) != 0) {
5!
1386
    printf("failed to create cancel semaphore since %s\r\n", tstrerror(code));
×
1387
    return code;
×
1388
  }
1389

1390
  TdThread spid = {0};
5✔
1391
  taosThreadCreate(&spid, NULL, shellCancelHandler, NULL);
5✔
1392

1393
  taosSetSignal(SIGTERM, shellQueryInterruptHandler);
5✔
1394
  taosSetSignal(SIGHUP, shellQueryInterruptHandler);
5✔
1395
  taosSetSignal(SIGINT, shellQueryInterruptHandler);
5✔
1396

1397
#ifdef WEBSOCKET
1398
  if (!shell.args.restful && !shell.args.cloud) {
5!
1399
#endif
1400
    char    buf[512] = {0};
5✔
1401
    int32_t verType = shellGetGrantInfo(buf);
5✔
1402
#ifndef WINDOWS
1403
    printfIntroduction(verType);
5✔
1404
#else
1405
#ifndef WEBSOCKET
1406
  if (verType == TSDB_VERSION_OSS) {
1407
    showAD(false);
1408
  }
1409
#endif
1410
#endif
1411
    // printf version
1412
    if (verType == TSDB_VERSION_ENTERPRISE || verType == TSDB_VERSION_CLOUD) {
5!
1413
      printf("%s\n", buf);
5✔
1414
    }
1415

1416
#ifdef WEBSOCKET
1417
  }
1418
#endif
1419
  while (1) {
1420
    taosThreadCreate(&shell.pid, NULL, shellThreadLoop, NULL);
5✔
1421
    taosThreadJoin(shell.pid, NULL);
5✔
1422
    taosThreadClear(&shell.pid);
5✔
1423
    if (shell.exit) {
5!
1424
      tsem_post(&shell.cancelSem);
5✔
1425
      break;
5✔
1426
    }
1427
  }
1428
#ifndef WEBSOCKET
1429
  // commnuity
1430
  if (verType == TSDB_VERSION_OSS) {
1431
    showAD(true);
1432
  }
1433
#endif
1434

1435
  taosThreadJoin(spid, NULL);
5✔
1436

1437
  shellCleanupHistory();
5✔
1438
  taos_kill_query(shell.conn);
5✔
1439
  taos_close(shell.conn);
5✔
1440

1441
  TAOS_RETURN(code);
5✔
1442
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc