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

taosdata / TDengine / #3660

15 Mar 2025 09:06AM UTC coverage: 62.039% (-1.3%) from 63.314%
#3660

push

travis-ci

web-flow
feat(stream): support stream processing for virtual tables (#30144)

* enh: add client processing

* enh: add mnode vtables processing

* enh: add mnode vtable processing

* enh: add normal child vtable support

* fix: compile issues

* fix: compile issues

* fix: create stream issues

* fix: multi stream scan issue

* fix: remove debug info

* fix: agg task and task level issues

* fix: correct task output type

* fix: split vtablescan from agg

* fix: memory leak issues

* fix: add limitations

* Update 09-error-code.md

* Update 09-error-code.md

* fix: remove usless case

* feat(stream): extract original table data in source scan task

Implemented functionality in the source task to extract data
corresponding to the virtual table from the original table using WAL.
The extracted data is then sent to the downstream merge task for further
processing.

* feat(stream): multi-way merge using loser tree in virtual merge task

Implemented multi-way merge in the merge task using a loser tree to
combine data from multiple original table into a single virtual table.
The merged virtual table data is then pushed downstream for further
processing.  Introduced memory limit handling during the merge process
with configurable behavior when the memory limit is reached.

* fix(test): remove useless cases

---------

Co-authored-by: dapan1121 <wpan@taosdata.com>
Co-authored-by: Pan Wei <72057773+dapan1121@users.noreply.github.com>

154078 of 317582 branches covered (48.52%)

Branch coverage included in aggregate %.

313 of 2391 new or added lines in 34 files covered. (13.09%)

26134 existing lines in 205 files now uncovered.

240261 of 318051 relevant lines covered (75.54%)

16655189.27 hits per line

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

76.81
/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) {
820,452✔
70
  for (char c = *cmd++; c != 0; c = *cmd++) {
1,002,206✔
71
    if (c != ' ' && c != '\t' && c != ';') {
621,323!
72
      return false;
439,569✔
73
    }
74
  }
75
  return true;
380,883✔
76
}
77

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

81
  if (shellIsEmptyCommand(command)) {
600,680✔
82
    return 0;
380,883✔
83
  }
84

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

89
  if (shellRegexMatch(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
219,796!
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;]*$",
219,796✔
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)) {
219,793✔
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) {
219,789✔
133
    shellRunSingleCommandWebsocketImp(command);
91✔
134
  } else {
135
#endif
136
    shellRunSingleCommandImp(command);
219,698✔
137
#ifdef WEBSOCKET
138
  }
139
#endif
140
  return 0;
219,789✔
141
}
142

143
void shellRecordCommandToHistory(char *command) {
13,547✔
144
  if (strncasecmp(command, "create user ", 12) == 0 || strncasecmp(command, "alter user ", 11) == 0) {
13,547!
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,547✔
152
  if (pHistory->hstart == pHistory->hend ||
13,547✔
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,547!
156
      taosMemoryFreeClear(pHistory->hist[pHistory->hend]);
13,547!
157
    }
158
    pHistory->hist[pHistory->hend] = taosStrdup(command);
13,547!
159

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

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

172
  // add help or help;
173
  if (strncasecmp(command, "help", 4) == 0) {
219,772✔
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);
219,771✔
181

182
  char quote = 0, *cmd = command;
219,771✔
183
  for (char c = *command++; c != 0; c = *command++) {
157,952,579✔
184
    if (c == '\\' && (*command == '\'' || *command == '"' || *command == '`')) {
157,732,809!
185
      command++;
2✔
186
      continue;
2✔
187
    }
188

189
    if (quote == c) {
157,732,807✔
190
      quote = 0;
2,190,383✔
191
    } else if (quote == 0 && (c == '\'' || c == '"' || c == '`')) {
155,542,424✔
192
      quote = c;
2,190,383✔
193
    } else if (c == ';' && quote == 0) {
153,352,041✔
194
      c = *command;
380,910✔
195
      *command = 0;
380,910✔
196
      if (shellRunSingleCommand(cmd) < 0) {
380,910✔
197
        return -1;
1✔
198
      }
199
      *command = c;
380,909✔
200
      cmd = command;
380,909✔
201
    }
202
  }
203
  return shellRunSingleCommand(cmd);
219,770✔
204
}
205

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

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

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

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

226
  return NULL;
219,719✔
227
}
228

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

236
  if ((sptr = strstr(command, ">>")) != NULL) {
219,698✔
237
    fname = sptr + 2;
12,470✔
238
    while (*fname == ' ') fname++;
24,927✔
239
    *sptr = '\0';
12,470✔
240

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

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

252
  st = taosGetTimestampUs();
219,698✔
253

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

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

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

266
    taos_free_result(pSql);
158✔
267

268
    return;
158✔
269
  }
270

271
  // pre string
272
  char *pre = "Query OK";
218,665✔
273
  if (shellRegexMatch(command, "^\\s*delete\\s*from\\s*.*", REG_EXTENDED | REG_ICASE)) {
218,665✔
274
    pre = "Delete OK";
26✔
275
  } else if (shellRegexMatch(command, "^\\s*insert\\s*into\\s*.*", REG_EXTENDED | REG_ICASE)) {
218,639✔
276
    pre = "Insert OK";
117,989✔
277
  } else if (shellRegexMatch(command, "^\\s*create\\s*.*", REG_EXTENDED | REG_ICASE)) {
100,650✔
278
    pre = "Create OK";
4,130✔
279
  } else if (shellRegexMatch(command, "^\\s*drop\\s*.*", REG_EXTENDED | REG_ICASE)) {
96,520✔
280
    pre = "Drop OK";
155✔
281
  }
282

283
  TAOS_FIELD *pFields = taos_fetch_fields(pSql);
218,665✔
284
  if (pFields != NULL) {  // select and show kinds of commands
218,665✔
285
    int32_t error_no = 0;
93,269✔
286

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

290
    et = taosGetTimestampUs();
93,269✔
291
    if (error_no == 0) {
93,269!
292
      printf("Query OK, %" PRId64 " row(s) in set (%.6fs)\r\n", numOfRows, (et - st) / 1E6);
93,269✔
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);
93,269✔
299
  } else {
300
    int64_t num_rows_affacted = taos_affected_rows64(pSql);
125,396✔
301
    taos_free_result(pSql);
125,396✔
302
    et = taosGetTimestampUs();
125,396✔
303
    printf("%s, %" PRId64 " row(s) affected (%.6fs)\r\n", pre, num_rows_affacted, (et - st) / 1E6);
125,396✔
304

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

309
  printf("\r\n");
218,665✔
310
}
311

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

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

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

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

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

356
  return buf;
4,391,423✔
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) {
12,444,056✔
369
  if (val == NULL) {
12,444,056✔
370
    taosFprintfFile(pFile, "NULL");
75,298✔
371
    return;
75,298✔
372
  }
373

374
  char    quotationStr[2] = {'"', 0};
12,368,758✔
375
  int32_t width;
376

377
  int n = 0;
12,368,758✔
378
#define LENGTH 64
379
  char buf[LENGTH] = {0};
12,368,758✔
380
  switch (field->type) {
12,368,758!
381
    case TSDB_DATA_TYPE_BOOL:
3,607,922✔
382
      taosFprintfFile(pFile, "%d", ((((int32_t)(*((char *)val))) == 1) ? 1 : 0));
3,607,922✔
383
      break;
3,607,922✔
384
    case TSDB_DATA_TYPE_TINYINT:
116,939✔
385
      taosFprintfFile(pFile, "%d", *((int8_t *)val));
116,939✔
386
      break;
116,939✔
387
    case TSDB_DATA_TYPE_UTINYINT:
7,894✔
388
      taosFprintfFile(pFile, "%u", *((uint8_t *)val));
7,894✔
389
      break;
7,894✔
390
    case TSDB_DATA_TYPE_SMALLINT:
9,868✔
391
      taosFprintfFile(pFile, "%d", *((int16_t *)val));
9,868✔
392
      break;
9,868✔
393
    case TSDB_DATA_TYPE_USMALLINT:
9,865✔
394
      taosFprintfFile(pFile, "%u", *((uint16_t *)val));
9,865✔
395
      break;
9,865✔
396
    case TSDB_DATA_TYPE_INT:
409,736✔
397
      taosFprintfFile(pFile, "%d", *((int32_t *)val));
409,736✔
398
      break;
409,736✔
399
    case TSDB_DATA_TYPE_UINT:
8,865✔
400
      taosFprintfFile(pFile, "%u", *((uint32_t *)val));
8,865✔
401
      break;
8,865✔
402
    case TSDB_DATA_TYPE_BIGINT:
727,262✔
403
      taosFprintfFile(pFile, "%" PRId64, *((int64_t *)val));
727,262✔
404
      break;
727,262✔
405
    case TSDB_DATA_TYPE_UBIGINT:
4,932✔
406
      taosFprintfFile(pFile, "%" PRIu64, *((uint64_t *)val));
4,932✔
407
      break;
4,932✔
408
    case TSDB_DATA_TYPE_FLOAT:
115,947✔
409
      width = SHELL_FLOAT_WIDTH;
115,947✔
410
      if (tsEnableScience) {
115,947!
411
        taosFprintfFile(pFile, "%*.7e", width, GET_FLOAT_VAL(val));
×
412
      } else {
413
        n = tsnprintf(buf, LENGTH, "%*.7f", width, GET_FLOAT_VAL(val));
115,947✔
414
        if (n > SHELL_FLOAT_WIDTH) {
115,947✔
415
          taosFprintfFile(pFile, "%*.7e", width, GET_FLOAT_VAL(val));
1,600✔
416
        } else {
417
          taosFprintfFile(pFile, "%s", buf);
114,347✔
418
        }
419
      }
420
      break;
115,947✔
421
    case TSDB_DATA_TYPE_DOUBLE:
1,765,518✔
422
      width = SHELL_DOUBLE_WIDTH;
1,765,518✔
423
      if (tsEnableScience) {
1,765,518!
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,765,518✔
428
        if (n > SHELL_DOUBLE_WIDTH) {
1,765,518✔
429
          taosFprintfFile(pFile, "%*.15e", width, GET_DOUBLE_VAL(val));
529,693✔
430
        } else {
431
          taosFprintfFile(pFile, "%s", buf);
1,235,825✔
432
        }
433
      }
434
      break;
1,765,518✔
435
    case TSDB_DATA_TYPE_BINARY:
710,939✔
436
    case TSDB_DATA_TYPE_NCHAR:
437
    case TSDB_DATA_TYPE_JSON: {
438
      int32_t bufIndex = 0;
710,939✔
439
      char   *tmp = (char *)taosMemoryCalloc(length * 2 + 1, 1);
710,939!
440
      if (tmp == NULL) break;
710,939!
441
      for (int32_t i = 0; i < length; i++) {
8,212,368✔
442
        tmp[bufIndex] = val[i];
7,501,429✔
443
        bufIndex++;
7,501,429✔
444
        if (val[i] == '\"') {
7,501,429!
445
          tmp[bufIndex] = val[i];
×
446
          bufIndex++;
×
447
        }
448
      }
449
      tmp[bufIndex] = 0;
710,939✔
450

451
      taosFprintfFile(pFile, "%s%s%s", quotationStr, tmp, quotationStr);
710,939✔
452
      taosMemoryFree(tmp);
710,939!
453
    } break;
710,939✔
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:
553,454✔
473
      shellFormatTimestamp(buf, sizeof(buf), *(int64_t *)val, precision);
553,454✔
474
      taosFprintfFile(pFile, "%s%s%s", quotationStr, buf, quotationStr);
553,454✔
475
      break;
553,454✔
476
    case TSDB_DATA_TYPE_DECIMAL64:
4,319,617✔
477
    case TSDB_DATA_TYPE_DECIMAL:
478
      taosFprintfFile(pFile, "%s", val);
4,319,617✔
479
    default:
4,319,617✔
480
      break;
4,319,617✔
481
  }
482
}
483

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

490
  TAOS_ROW row = taos_fetch_row(tres);
11,612✔
491
  if (row == NULL) {
11,612✔
492
    return 0;
756✔
493
  }
494

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

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

505
  for (int32_t col = 0; col < num_fields; col++) {
23,229✔
506
    if (col > 0) {
12,373✔
507
      taosFprintfFile(pFile, ",");
1,517✔
508
    }
509
    taosFprintfFile(pFile, "%s", fields[col].name);
12,373✔
510
  }
511
  taosFprintfFile(pFile, "\r\n");
10,856✔
512

513
  int64_t numOfRows = 0;
10,856✔
514
  do {
515
    int32_t *length = taos_fetch_lengths(tres);
9,411,285✔
516
    for (int32_t i = 0; i < num_fields; i++) {
21,855,301✔
517
      if (i > 0) {
12,444,016✔
518
        taosFprintfFile(pFile, ",");
3,032,731✔
519
      }
520
      shellDumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision);
12,444,016✔
521
    }
522
    taosFprintfFile(pFile, "\r\n");
9,411,285✔
523

524
    numOfRows++;
9,411,285✔
525
    row = taos_fetch_row(tres);
9,411,285✔
526
  } while (row != NULL);
9,411,285✔
527

528
  taosCloseFile(&pFile);
10,856✔
529

530
  return numOfRows;
10,856✔
531
}
532

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

537
  while (pos < length) {
47,757,246✔
538
    TdWchar wc;
539
    int32_t bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
46,510,321✔
540
    if (bytes <= 0) {
46,510,321✔
541
      break;
718,081✔
542
    }
543

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

555
    if (w <= 0) {
46,510,257✔
556
      continue;
92,247✔
557
    }
558

559
    if (width <= 0) {
46,510,239✔
560
      printf("%lc", wc);
92,229✔
561
      continue;
92,229✔
562
    }
563

564
    totalCols += w;
46,418,010✔
565
    if (totalCols > width) {
46,418,010✔
566
      break;
718,017✔
567
    }
568
    if (totalCols <= (width - 3)) {
45,699,993✔
569
      printf("%lc", wc);
43,154,165✔
570
      cols += w;
43,154,165✔
571
    } else {
572
      tail[tailLen] = wc;
2,545,828✔
573
      tailLen++;
2,545,828✔
574
    }
575
  }
576

577
  if (totalCols > width) {
1,965,006✔
578
    // width could be 1 or 2, so printf("...") cannot be used
579
    for (int32_t i = 0; i < 3; i++) {
2,872,068✔
580
      if (cols >= width) {
2,154,051!
UNCOV
581
        break;
×
582
      }
583
      putchar('.');
2,154,051✔
584
      ++cols;
2,154,051✔
585
    }
586
  } else {
587
    for (int32_t i = 0; i < tailLen; i++) {
1,638,775✔
588
      printf("%lc", tail[i]);
391,786✔
589
    }
590
    cols = totalCols;
1,246,989✔
591
  }
592

593
  for (; cols < width; cols++) {
14,368,384✔
594
    putchar(' ');
12,403,378✔
595
  }
596
}
1,965,006✔
597

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

601
  if (width == 0) {
2,422,068✔
602
    printf("%s", str);
120✔
603
  } else if (len > width) {
2,421,948✔
604
    if (width <= 3) {
60!
UNCOV
605
      printf("%.*s.", width - 1, str);
×
606
    } else {
607
      printf("%.*s...", width - 3, str);
60✔
608
    }
609
  } else {
610
    printf("%s%*.s", str, width - len, "");
2,421,888✔
611
  }
612
}
2,422,068✔
613

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

620
  int32_t code = TSDB_CODE_FAILED;
120✔
621

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

628
  char *outputWKT = NULL;
120✔
629
  code = doAsText(val, length, &outputWKT);
120✔
630
  if (code != TSDB_CODE_SUCCESS) {
120!
UNCOV
631
    shellPrintString(getGeosErrMsg(code), width);  // should NOT happen
×
UNCOV
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,778,115✔
641
  if (val == NULL) {
10,778,115✔
642
    shellPrintString(TSDB_DATA_NULL_STR, width);
2,279,381✔
643
    return;
2,279,381✔
644
  }
645

646
  int n = 0;
8,498,734✔
647
#define LENGTH 64
648
  char buf[LENGTH] = {0};
8,498,734✔
649
  switch (field->type) {
8,498,734!
650
    case TSDB_DATA_TYPE_BOOL:
142,567✔
651
      shellPrintString(((((int32_t)(*((char *)val))) == TSDB_FALSE) ? "false" : "true"), width);
142,567✔
652
      break;
142,567✔
653
    case TSDB_DATA_TYPE_TINYINT:
152,783✔
654
      printf("%*d", width, *((int8_t *)val));
152,783✔
655
      break;
152,783✔
656
    case TSDB_DATA_TYPE_UTINYINT:
74,486✔
657
      printf("%*u", width, *((uint8_t *)val));
74,486✔
658
      break;
74,486✔
659
    case TSDB_DATA_TYPE_SMALLINT:
161,643✔
660
      printf("%*d", width, *((int16_t *)val));
161,643✔
661
      break;
161,643✔
662
    case TSDB_DATA_TYPE_USMALLINT:
80,488✔
663
      printf("%*u", width, *((uint16_t *)val));
80,488✔
664
      break;
80,488✔
665
    case TSDB_DATA_TYPE_INT:
177,835✔
666
      printf("%*d", width, *((int32_t *)val));
177,835✔
667
      break;
177,835✔
668
    case TSDB_DATA_TYPE_UINT:
51,322✔
669
      printf("%*u", width, *((uint32_t *)val));
51,322✔
670
      break;
51,322✔
671
    case TSDB_DATA_TYPE_BIGINT:
1,127,315✔
672
      printf("%*" PRId64, width, taosGetInt64Aligned((int64_t *)val));
1,127,315✔
673
      break;
1,127,315✔
674
    case TSDB_DATA_TYPE_UBIGINT:
36,899✔
675
      printf("%*" PRIu64, width, taosGetUInt64Aligned((uint64_t *)val));
36,899✔
676
      break;
36,899✔
677
    case TSDB_DATA_TYPE_FLOAT:
154,790✔
678
      width = width >= LENGTH ? LENGTH - 1 : width;
154,790✔
679
      if (tsEnableScience) {
154,790!
UNCOV
680
        printf("%*.7e", width, taosGetFloatAligned((float *)val));
×
681
      } else {
682
        snprintf(buf, LENGTH, "%*.*g", width, FLT_DIG, taosGetFloatAligned((float *)val));
154,790✔
683
        printf("%s", buf);
154,790✔
684
      }
685
      break;
154,790✔
686
    case TSDB_DATA_TYPE_DOUBLE:
535,511✔
687
      width = width >= LENGTH ? LENGTH - 1 : width;
535,511✔
688
      if (tsEnableScience) {
535,511!
UNCOV
689
        snprintf(buf, LENGTH, "%*.15e", width, taosGetDoubleAligned((double *)val));
×
UNCOV
690
        printf("%s", buf);
×
691
      } else {
692
        snprintf(buf, LENGTH, "%*.*g", width, DBL_DIG, taosGetDoubleAligned((double *)val));
535,511✔
693
        printf("%*s", width, buf);
535,511✔
694
      }
695
      break;
535,511✔
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!
UNCOV
700
        break;
×
701
      }
702
      shellPrintNChar(data, size, width);
120✔
703
      taosMemoryFree(data);
120!
704
      break;
120✔
705
    }
706
    case TSDB_DATA_TYPE_BINARY:
1,964,886✔
707
    case TSDB_DATA_TYPE_NCHAR:
708
    case TSDB_DATA_TYPE_JSON:
709
      shellPrintNChar(val, length, width);
1,964,886✔
710
      break;
1,964,886✔
711
    case TSDB_DATA_TYPE_GEOMETRY:
120✔
712
      shellPrintGeometry(val, length, width);
120✔
713
      break;
120✔
714
    case TSDB_DATA_TYPE_TIMESTAMP:
3,837,969✔
715
      shellFormatTimestamp(buf, sizeof(buf), taosGetInt64Aligned((int64_t *)val), precision);
3,837,969✔
716
      printf("%s", buf);
3,837,969✔
717
      break;
3,837,969✔
UNCOV
718
    case TSDB_DATA_TYPE_DECIMAL:
×
719
    case TSDB_DATA_TYPE_DECIMAL64:
UNCOV
720
      printf("%*s", width, val);
×
721
    default:
×
UNCOV
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
UNCOV
729
  if (taosStrCaseStr(sql, " limit ") != NULL) {
×
UNCOV
730
    return true;
×
731
  }
732
  // describe
UNCOV
733
  if (taosStrCaseStr(sql, "describe ") != NULL) {
×
UNCOV
734
    return true;
×
735
  }
736
  // desc
UNCOV
737
  if (taosStrCaseStr(sql, "desc ") != NULL) {
×
UNCOV
738
    return true;
×
739
  }
740
  // show
UNCOV
741
  if (taosStrCaseStr(sql, "show ") != NULL) {
×
UNCOV
742
    return true;
×
743
  }
744
  // explain
UNCOV
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
UNCOV
754
  if (taosStrCaseStr(sql, "show ") != NULL) {
×
UNCOV
755
    return true;
×
756
  }
757

UNCOV
758
  return false;
×
759
}
760

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

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

771
  dump_info->resShowMaxNum = UINT64_MAX;
81,657✔
772

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

777
  if (vertical) {
81,657✔
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++) {
241,491✔
787
      dump_info->width[col] = shellCalcColWidth(dump_info->fields + col, dump_info->precision);
159,898✔
788
    }
789
  }
790
}
81,657✔
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!
UNCOV
795
    printf("\033[31mtaos_fetch_row failed.\033[0m\n");
×
UNCOV
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);
×
UNCOV
824
      printf("         You can use the `LIMIT` clause to get fewer result to show.\r\n");
×
UNCOV
825
      printf("           Or use '>>' to redirect the whole set of the result to a specified file.\r\n");
×
UNCOV
826
      printf("\r\n");
×
UNCOV
827
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
×
UNCOV
828
      printf("\r\n");
×
UNCOV
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
  }
UNCOV
838
  return;
×
839
}
840

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

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

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

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

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

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

866
    case TSDB_DATA_TYPE_FLOAT:
1,699✔
867
      return TMAX(SHELL_FLOAT_WIDTH, width);
1,699✔
868

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

872
    case TSDB_DATA_TYPE_BINARY:
46,180✔
873
    case TSDB_DATA_TYPE_GEOMETRY:
874
      if (field->bytes > shell.args.displayWidth) {
46,180✔
875
        return TMAX(shell.args.displayWidth, width);
38,828✔
876
      } else {
877
        return TMAX(field->bytes + 2, width);
7,352✔
878
      }
879
    case TSDB_DATA_TYPE_VARBINARY: {
10✔
880
      int32_t bytes = field->bytes * 2 + 2;
10✔
881
      if (bytes > shell.args.displayWidth) {
10!
UNCOV
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,413✔
888
    case TSDB_DATA_TYPE_JSON: {
889
      uint16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
10,413✔
890
      if (bytes > shell.args.displayWidth) {
10,413!
891
        return TMAX(shell.args.displayWidth, width);
10,413✔
892
      } else {
UNCOV
893
        return TMAX(bytes + 2, width);
×
894
      }
895
    }
896

897
    case TSDB_DATA_TYPE_TIMESTAMP:
34,816✔
898
      if (shell.args.is_raw_time) {
34,816✔
899
        return TMAX(14, width);
1✔
900
      }
901
      if (precision == TSDB_TIME_PRECISION_NANO) {
34,815✔
902
        return TMAX(29, width);
3✔
903
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
34,812✔
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'
34,809✔
907
      }
UNCOV
908
    case TSDB_DATA_TYPE_DECIMAL64:
×
UNCOV
909
      return TMAX(width, 20);
×
UNCOV
910
    case TSDB_DATA_TYPE_DECIMAL:
×
UNCOV
911
      return TMAX(width, 40);
×
UNCOV
912
    default:
×
UNCOV
913
      ASSERT(false);
×
914
  }
915

UNCOV
916
  return 0;
×
917
}
918

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

929
  putchar('\r');
76,462✔
930
  putchar('\n');
76,462✔
931
  for (int32_t i = 0; i < rowWidth; i++) {
4,138,949✔
932
    putchar('=');
4,062,487✔
933
  }
934
  putchar('\r');
76,462✔
935
  putchar('\n');
76,462✔
936
}
76,462✔
937

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

945
  int64_t numOfPintRows = dump_info->numOfAllRows;
79,954✔
946
  int     numOfPrintRowsThisOne = 0;
79,954✔
947
  if (numOfPintRows == 0) {
79,954✔
948
    shellPrintHeader(dump_info->fields, dump_info->width, dump_info->numFields);
76,440✔
949
  }
950

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

963
    numOfPintRows++;
3,110,993✔
964
    numOfPrintRowsThisOne++;
3,110,993✔
965

966
    if (numOfPintRows == dump_info->resShowMaxNum) {
3,110,993!
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)) {
×
UNCOV
970
        printf("         You can use '>>' to redirect the whole set of the result to a specified file.\r\n");
×
971
      } else {
UNCOV
972
        printf("         You can use the `LIMIT` clause to get fewer result to show.\r\n");
×
UNCOV
973
        printf("           Or use '>>' to redirect the whole set of the result to a specified file.\r\n");
×
974
      }
UNCOV
975
      printf("\r\n");
×
UNCOV
976
      printf("         You can use Ctrl+C to stop the underway fetching.\r\n");
×
UNCOV
977
      printf("\r\n");
×
978
      return;
×
979
    }
980

981
    if (numOfPrintRowsThisOne == dump_info->numOfRows) {
3,110,993✔
982
      return;
79,954✔
983
    }
984

985
    row = taos_fetch_row(tres);
3,031,039✔
986
  }
UNCOV
987
  return;
×
988
}
989

990
void shellDumpResultCallback(void *param, TAOS_RES *tres, int num_of_rows) {
161,675✔
991
  tsDumpInfo *dump_info = (tsDumpInfo *)param;
161,675✔
992
  if (num_of_rows > 0) {
161,675✔
993
    dump_info->numOfRows = num_of_rows;
80,018✔
994
    if (dump_info->numOfAllRows < dump_info->resShowMaxNum) {
80,018!
995
      if (dump_info->vertical) {
80,018✔
996
        shellVerticalPrintResult(tres, dump_info);
64✔
997
      } else {
998
        shellHorizontalPrintResult(tres, dump_info);
79,954✔
999
      }
1000
    }
1001
    dump_info->numOfAllRows += num_of_rows;
80,018✔
1002
    if (!shellCmdkilled) {
80,018!
1003
      taos_fetch_rows_a(tres, shellDumpResultCallback, param);
80,018✔
1004
    } else {
UNCOV
1005
      tsem_post(&dump_info->sem);
×
1006
    }
1007
  } else {
1008
    if (num_of_rows < 0) {
81,657!
UNCOV
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);
81,657✔
1012
  }
1013
}
161,675✔
1014

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

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

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

1038
  char   *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
13,544!
1039
  int32_t read_size = 0;
13,544✔
1040
  while ((read_size = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) > 0) {
102,413,210✔
1041
    line[read_size - 1] = '\0';
102,399,666✔
1042
    taosMemoryFree(pHistory->hist[pHistory->hend]);
102,399,666!
1043
    pHistory->hist[pHistory->hend] = taosStrdup(line);
102,399,666!
1044

1045
    pHistory->hend = (pHistory->hend + 1) % SHELL_MAX_HISTORY_SIZE;
102,399,666✔
1046

1047
    if (pHistory->hend == pHistory->hstart) {
102,399,666✔
1048
      pHistory->hstart = (pHistory->hstart + 1) % SHELL_MAX_HISTORY_SIZE;
88,869,210✔
1049
    }
1050
  }
1051

1052
  taosMemoryFreeClear(line);
13,544!
1053
  taosCloseFile(&pFile);
13,544✔
1054
  int64_t file_size;
1055
  if (taosStatFile(pHistory->file, &file_size, NULL, NULL) == 0 && file_size > SHELL_MAX_COMMAND_SIZE) {
13,544!
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,544✔
1073
}
1074

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

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

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

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

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

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

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

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

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

1134
  char *line = taosMemoryMalloc(TSDB_MAX_ALLOWED_SQL_LEN + 1);
829!
1135
  while ((read_len = taosGetsFile(pFile, TSDB_MAX_ALLOWED_SQL_LEN, line)) > 0) {
208,097✔
1136
    if (cmd_len + read_len >= TSDB_MAX_ALLOWED_SQL_LEN) {
207,268!
UNCOV
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);
UNCOV
1139
      cmd_len = 0;
×
UNCOV
1140
      memset(line, 0, TSDB_MAX_ALLOWED_SQL_LEN + 1);
×
1141
      continue;
×
1142
    }
1143
    line[--read_len] = '\0';
207,268✔
1144

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

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

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

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

1167
  taosMemoryFree(cmd);
829!
1168
  taosMemoryFreeClear(line);
829!
1169
  taosCloseFile(&pFile);
829✔
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!
UNCOV
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
    }
UNCOV
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!
UNCOV
1195
    fprintf(stderr, "\r\nInvalid grant information.\r\n");
×
UNCOV
1196
    exit(0);
×
1197
  } else {
1198
    if (tres == NULL) {
5!
UNCOV
1199
      fprintf(stderr, "\r\nGrant information is null.\r\n");
×
UNCOV
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!
UNCOV
1206
      fprintf(stderr, "\r\nFailed to get grant information from server. Abort.\r\n");
×
UNCOV
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!
UNCOV
1218
      verType = TSDB_VERSION_OSS;
×
1219
    } else if (strcmp(expiretime, "unlimited") == 0) {
5!
UNCOV
1220
      verType = TSDB_VERSION_ENTERPRISE;
×
UNCOV
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); }
7✔
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!
UNCOV
1257
      taosMsleep(10);
×
UNCOV
1258
      continue;
×
1259
    }
1260

1261
#ifdef WEBSOCKET
1262
    if (shell.args.restful || shell.args.cloud) {
10!
UNCOV
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!
UNCOV
1292
      printf("failed to malloc command\r\n");
×
UNCOV
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,572✔
1318
  int32_t code = 0;
13,572✔
1319
  printf(shell.info.clientVersion, shell.info.cusName, taos_get_client_info(), shell.info.cusName);
13,572✔
1320
  fflush(stdout);
13,572✔
1321

1322
  SShellArgs *pArgs = &shell.args;
13,572✔
1323
#ifdef WEBSOCKET
1324
  if (shell.args.restful || shell.args.cloud) {
13,572✔
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,553✔
1333
      shell.conn = taos_connect(pArgs->host, pArgs->user, pArgs->password, pArgs->database, pArgs->port);
13,549✔
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,553✔
1339
      printf("failed to connect to server, reason: %s[0x%08X]\n%s", taos_errstr(NULL), taos_errno(NULL), ERROR_CODE_DETAIL);
24✔
1340
      fflush(stdout);
24✔
1341
      return -1;
24✔
1342
    }
1343
#ifdef WEBSOCKET
1344
  }
1345
#endif
1346

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

1351
  if (shell.args.is_bi_mode) {
13,544✔
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,544✔
1360
    if (pArgs->commands != NULL) {
13,539✔
1361
      printf("%s%s\r\n", shell.info.promptHeader, pArgs->commands);
12,696✔
1362
      char *cmd = taosStrdup(pArgs->commands);
12,696!
1363
      shellRunCommand(cmd, true);
12,696✔
1364
      taosMemoryFree(cmd);
12,696!
1365
    }
1366

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

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

1385
  if ((code = tsem_init(&shell.cancelSem, 0, 0)) != 0) {
5!
UNCOV
1386
    printf("failed to create cancel semaphore since %s\r\n", tstrerror(code));
×
UNCOV
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