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

16 17
#define _BSD_SOURCE
#define _GNU_SOURCE
H
hzcheng 已提交
18
#define _XOPEN_SOURCE
S
slguan 已提交
19
#define _DEFAULT_SOURCE
H
hzcheng 已提交
20

S
slguan 已提交
21
#include "os.h"
H
hzcheng 已提交
22 23
#include "shell.h"
#include "shellCommand.h"
H
hzcheng 已提交
24
#include "taosdef.h"
S
slguan 已提交
25
#include "taoserror.h"
S
Shengliang Guan 已提交
26
#include "tconfig.h"
S
slguan 已提交
27
#include "tglobal.h"
S
Shengliang Guan 已提交
28 29
#include "ttypes.h"
#include "tutil.h"
H
Haojun Liao 已提交
30

31
#include <regex.h>
H
hzcheng 已提交
32 33

/**************** Global variables ****************/
H
Hui Li 已提交
34
#ifdef _TD_POWER_
S
Shengliang Guan 已提交
35 36 37 38
char CLIENT_VERSION[] =
    "Welcome to the PowerDB shell from %s, Client Version:%s\n"
    "Copyright (c) 2020 by PowerDB, Inc. All rights reserved.\n\n";
char PROMPT_HEADER[] = "power> ";
H
Hui Li 已提交
39

S
Shengliang Guan 已提交
40 41
char CONTINUE_PROMPT[] = "    -> ";
int  prompt_size = 7;
42
#elif (_TD_TQ_ == true)
S
Shengliang Guan 已提交
43 44 45 46
char CLIENT_VERSION[] =
    "Welcome to the TQ shell from %s, Client Version:%s\n"
    "Copyright (c) 2020 by TQ, Inc. All rights reserved.\n\n";
char PROMPT_HEADER[] = "tq> ";
47

S
Shengliang Guan 已提交
48 49
char CONTINUE_PROMPT[] = "    -> ";
int  prompt_size = 4;
H
Hui Li 已提交
50
#else
S
Shengliang Guan 已提交
51 52 53 54
char CLIENT_VERSION[] =
    "Welcome to the TDengine shell from %s, Client Version:%s\n"
    "Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n";
char PROMPT_HEADER[] = "taos> ";
H
Hui Li 已提交
55

S
Shengliang Guan 已提交
56 57
char CONTINUE_PROMPT[] = "   -> ";
int  prompt_size = 6;
H
Hui Li 已提交
58 59
#endif

S
Shengliang Guan 已提交
60 61
int64_t       result = 0;
SShellHistory history;
H
hzcheng 已提交
62

63 64
#define DEFAULT_MAX_BINARY_DISPLAY_WIDTH 30
extern int32_t tsMaxBinaryDisplayWidth;
S
Shengliang Guan 已提交
65
extern TAOS   *taos_connect_auth(const char *ip, const char *user, const char *auth, const char *db, uint16_t port);
66

H
hzcheng 已提交
67 68 69
/*
 * FUNCTION: Initialize the shell.
 */
70
TAOS *shellInit(SShellArguments *_args) {
71
  printf("\n");
72 73
  if (!_args->is_use_passwd) {
#ifdef TD_WINDOWS
S
os env  
Shengliang Guan 已提交
74
    strcpy(tsOsName, "Windows");
75
#elif defined(TD_DARWIN)
S
os env  
Shengliang Guan 已提交
76
    strcpy(tsOsName, "Darwin");
77
#endif
S
os env  
Shengliang Guan 已提交
78
    printf(CLIENT_VERSION, tsOsName, taos_get_client_info());
79 80
  }

81 82
  fflush(stdout);

H
hzcheng 已提交
83
  // set options before initializing
84 85
  if (_args->timezone != NULL) {
    taos_options(TSDB_OPTION_TIMEZONE, _args->timezone);
H
hzcheng 已提交
86 87
  }

88
  if (!_args->is_use_passwd) {
89
    _args->password = TSDB_DEFAULT_PASS;
H
hzcheng 已提交
90 91
  }

92 93
  if (_args->user == NULL) {
    _args->user = TSDB_DEFAULT_USER;
H
hzcheng 已提交
94 95 96
  }

  // Connect to the database.
97
  TAOS *con = NULL;
98 99
  if (_args->auth == NULL) {
    con = taos_connect(_args->host, _args->user, _args->password, _args->database, _args->port);
100
  } else {
101
    con = taos_connect_auth(_args->host, _args->user, _args->auth, _args->database, _args->port);
102 103
  }

H
hzcheng 已提交
104
  if (con == NULL) {
H
Hui Li 已提交
105
    fflush(stdout);
H
hzcheng 已提交
106 107 108 109 110 111 112
    return con;
  }

  /* Read history TODO : release resources here*/
  read_history();

  // Check if it is temperory run
113 114 115 116
  if (_args->commands != NULL || _args->file[0] != 0) {
    if (_args->commands != NULL) {
      printf("%s%s\n", PROMPT_HEADER, _args->commands);
      shellRunCommand(con, _args->commands);
H
hzcheng 已提交
117 118
    }

119 120
    if (_args->file[0] != 0) {
      source_file(con, _args->file);
H
hzcheng 已提交
121 122
    }

123
    taos_close(con);
H
hzcheng 已提交
124 125 126 127
    write_history();
    exit(EXIT_SUCCESS);
  }

S
Shengliang Guan 已提交
128
#if 0
S
slguan 已提交
129
#ifndef WINDOWS
130 131
  if (_args->dir[0] != 0) {
    source_dir(con, _args);
S
#928  
slguan 已提交
132 133 134
    taos_close(con);
    exit(EXIT_SUCCESS);
  }
S
Shengliang Guan 已提交
135

136 137
  if (_args->check != 0) {
    shellCheck(con, _args);
S
Shengliang Guan 已提交
138 139 140
    taos_close(con);
    exit(EXIT_SUCCESS);
  }
S
Shengliang Guan 已提交
141
#endif
S
#928  
slguan 已提交
142 143
#endif

H
hzcheng 已提交
144 145 146
  return con;
}

S
Shengliang Guan 已提交
147
static bool isEmptyCommand(const char *cmd) {
148 149 150
  for (char c = *cmd++; c != 0; c = *cmd++) {
    if (c != ' ' && c != '\t' && c != ';') {
      return false;
H
hzcheng 已提交
151 152
    }
  }
153
  return true;
H
hzcheng 已提交
154 155
}

156
static int32_t shellRunSingleCommand(TAOS *con, char *command) {
H
hzcheng 已提交
157
  /* If command is empty just return */
158
  if (isEmptyCommand(command)) {
159
    return 0;
H
hzcheng 已提交
160 161 162 163 164 165
  }

  // Analyse the command.
  if (regex_match(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
    taos_close(con);
    write_history();
S
Shengliang Guan 已提交
166 167 168
#ifdef WINDOWS
    exit(EXIT_SUCCESS);
#endif
169
    return -1;
170 171 172
  }

  if (regex_match(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
173 174
    // If clear the screen.
    system("clear");
175 176
    return 0;
  }
177

S
Shengliang Guan 已提交
178 179
  if (regex_match(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$",
                  REG_EXTENDED | REG_ICASE)) {
180 181
    strtok(command, " \t");
    strtok(NULL, " \t");
S
Shengliang Guan 已提交
182
    char *p = strtok(NULL, " \t");
183 184 185 186 187
    if (strcasecmp(p, "default") == 0) {
      tsMaxBinaryDisplayWidth = DEFAULT_MAX_BINARY_DISPLAY_WIDTH;
    } else {
      tsMaxBinaryDisplayWidth = atoi(p);
    }
188 189
    return 0;
  }
190

191
  if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
192 193 194 195 196 197
    /* If source file. */
    char *c_ptr = strtok(command, " ;");
    assert(c_ptr != NULL);
    c_ptr = strtok(NULL, " ;");
    assert(c_ptr != NULL);
    source_file(con, c_ptr);
198
    return 0;
H
hzcheng 已提交
199
  }
200 201

  shellRunCommandOnServer(con, command);
202
  return 0;
H
hzcheng 已提交
203 204
}

S
Shengliang Guan 已提交
205
int32_t shellRunCommand(TAOS *con, char *command) {
206 207 208 209 210 211 212 213 214 215
  /* If command is empty just return */
  if (isEmptyCommand(command)) {
    return 0;
  }

  /* Update the history vector. */
  if (history.hstart == history.hend ||
      history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL ||
      strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) {
    if (history.hist[history.hend] != NULL) {
wafwerar's avatar
wafwerar 已提交
216
      taosMemoryFreeClear(history.hist[history.hend]);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    }
    history.hist[history.hend] = strdup(command);

    history.hend = (history.hend + 1) % MAX_HISTORY_SIZE;
    if (history.hend == history.hstart) {
      history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE;
    }
  }

  bool esc = false;
  char quote = 0, *cmd = command, *p = command;
  for (char c = *command++; c != 0; c = *command++) {
    if (esc) {
      switch (c) {
        case 'n':
          c = '\n';
          break;
        case 'r':
          c = '\r';
          break;
        case 't':
          c = '\t';
          break;
        case 'G':
          *p++ = '\\';
          break;
        case '\'':
        case '"':
          if (quote) {
            *p++ = '\\';
          }
          break;
      }
      *p++ = c;
      esc = false;
      continue;
    }
254

255
    if (c == '\\') {
256
      if (quote != 0 && (*command == '_' || *command == '\\')) {
S
Shengliang Guan 已提交
257
        // DO nothing
258 259 260 261
      } else {
        esc = true;
        continue;
      }
262 263 264 265
    }

    if (quote == c) {
      quote = 0;
D
dapan1121 已提交
266
    } else if (quote == 0 && (c == '\'' || c == '"')) {
267 268 269 270
      quote = c;
    }

    *p++ = c;
271
    if (c == ';' && quote == 0) {
272 273 274 275 276 277 278 279 280 281 282 283 284 285
      c = *p;
      *p = 0;
      if (shellRunSingleCommand(con, cmd) < 0) {
        return -1;
      }
      *p = c;
      p = cmd;
    }
  }

  *p = 0;
  return shellRunSingleCommand(con, cmd);
}

D
dapan1121 已提交
286
void freeResultWithRid(int64_t rid) {
S
Shengliang Guan 已提交
287
#if 0
D
dapan1121 已提交
288 289 290 291 292
  SSqlObj* pSql = taosAcquireRef(tscObjRef, rid);
  if(pSql){
    taos_free_result(pSql);
    taosReleaseRef(tscObjRef, rid);
  }
S
Shengliang Guan 已提交
293
#endif
D
dapan1121 已提交
294 295
}

H
hzcheng 已提交
296
void shellRunCommandOnServer(TAOS *con, char command[]) {
297
  int64_t   st, et;
H
hzcheng 已提交
298
  wordexp_t full_path;
S
Shengliang Guan 已提交
299 300 301
  char     *sptr = NULL;
  char     *cptr = NULL;
  char     *fname = NULL;
302
  bool      printMode = false;
H
hzcheng 已提交
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

  if ((sptr = strstr(command, ">>")) != NULL) {
    cptr = strstr(command, ";");
    if (cptr != NULL) {
      *cptr = '\0';
    }

    if (wordexp(sptr + 2, &full_path, 0) != 0) {
      fprintf(stderr, "ERROR: invalid filename: %s\n", sptr + 2);
      return;
    }
    *sptr = '\0';
    fname = full_path.we_wordv[0];
  }

318 319 320 321 322 323 324 325 326 327
  if ((sptr = strstr(command, "\\G")) != NULL) {
    cptr = strstr(command, ";");
    if (cptr != NULL) {
      *cptr = '\0';
    }

    *sptr = '\0';
    printMode = true;  // When output to a file, the switch does not work.
  }

H
hzcheng 已提交
328 329
  st = taosGetTimestampUs();

S
Shengliang Guan 已提交
330
  TAOS_RES *pSql = taos_query(con, command);
H
Haojun Liao 已提交
331
  if (taos_errno(pSql)) {
S
TD-1793  
Shengliang Guan 已提交
332
    taos_error(pSql, st);
H
hzcheng 已提交
333 334 335
    return;
  }

D
dapan1121 已提交
336
  int64_t oresult = atomic_load_64(&result);
D
fix bug  
dapan1121 已提交
337

L
lihui 已提交
338
  if (regex_match(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
339 340
    fprintf(stdout, "Database changed.\n\n");
    fflush(stdout);
341

D
fix bug  
dapan1121 已提交
342
    atomic_store_64(&result, 0);
D
dapan1121 已提交
343
    freeResultWithRid(oresult);
S
Shengliang Guan 已提交
344 345
    taos_free_result(pSql);

H
hzcheng 已提交
346 347 348
    return;
  }

S
Shengliang Guan 已提交
349
  TAOS_FIELD *pFields = taos_fetch_fields(pSql);
H
Haojun Liao 已提交
350
  if (pFields != NULL) {  // select and show kinds of commands
351 352
    int error_no = 0;

H
Haojun Liao 已提交
353 354
    int numOfRows = shellDumpResult(pSql, fname, &error_no, printMode);
    if (numOfRows < 0) {
D
fix bug  
dapan1121 已提交
355
      atomic_store_64(&result, 0);
D
dapan1121 已提交
356
      freeResultWithRid(oresult);
H
Haojun Liao 已提交
357 358
      return;
    }
H
hzcheng 已提交
359 360 361 362 363

    et = taosGetTimestampUs();
    if (error_no == 0) {
      printf("Query OK, %d row(s) in set (%.6fs)\n", numOfRows, (et - st) / 1E6);
    } else {
364
      printf("Query interrupted (%s), %d row(s) in set (%.6fs)\n", taos_errstr(pSql), numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
365
    }
S
Shengliang Guan 已提交
366
    taos_free_result(pSql);
H
hzcheng 已提交
367
  } else {
H
Haojun Liao 已提交
368
    int num_rows_affacted = taos_affected_rows(pSql);
369
    taos_free_result(pSql);
H
hzcheng 已提交
370
    et = taosGetTimestampUs();
D
dapan1121 已提交
371
    printf("Query OK, %d of %d row(s) in database (%.6fs)\n", num_rows_affacted, num_rows_affacted, (et - st) / 1E6);
H
hzcheng 已提交
372 373 374 375 376 377 378
  }

  printf("\n");

  if (fname != NULL) {
    wordfree(&full_path);
  }
379

D
fix bug  
dapan1121 已提交
380
  atomic_store_64(&result, 0);
D
dapan1121 已提交
381
  freeResultWithRid(oresult);
H
hzcheng 已提交
382 383 384 385 386
}

/* Function to do regular expression check */
int regex_match(const char *s, const char *reg, int cflags) {
  regex_t regex;
387
  char    msgbuf[100] = {0};
H
hzcheng 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

  /* Compile regular expression */
  if (regcomp(&regex, reg, cflags) != 0) {
    fprintf(stderr, "Fail to compile regex");
    exitShell();
  }

  /* Execute regular expression */
  int reti = regexec(&regex, s, 0, NULL, 0);
  if (!reti) {
    regfree(&regex);
    return 1;
  } else if (reti == REG_NOMATCH) {
    regfree(&regex);
    return 0;
  } else {
    regerror(reti, &regex, msgbuf, sizeof(msgbuf));
    fprintf(stderr, "Regex match failed: %s\n", msgbuf);
    regfree(&regex);
    exitShell();
  }

  return 0;
}

S
Shengliang Guan 已提交
413
static char *formatTimestamp(char *buf, int64_t val, int precision) {
414 415 416 417
  if (args.is_raw_time) {
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
418

S
Shengliang Guan 已提交
419
  time_t  tt;
D
fix bug  
dapan1121 已提交
420
  int32_t ms = 0;
421 422 423 424
  if (precision == TSDB_TIME_PRECISION_NANO) {
    tt = (time_t)(val / 1000000000);
    ms = val % 1000000000;
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
425
    tt = (time_t)(val / 1000000);
D
fix bug  
dapan1121 已提交
426
    ms = val % 1000000;
427 428
  } else {
    tt = (time_t)(val / 1000);
D
fix bug  
dapan1121 已提交
429
    ms = val % 1000;
430 431
  }

S
Shengliang Guan 已提交
432 433 434 435 436 437 438
  /* comment out as it make testcases like select_with_tags.sim fail.
    but in windows, this may cause the call to localtime crash if tt < 0,
    need to find a better solution.
    if (tt < 0) {
      tt = 0;
    }
    */
439

440 441 442
#ifdef WINDOWS
  if (tt < 0) tt = 0;
#endif
S
Shengliang Guan 已提交
443
  if (tt <= 0 && ms < 0) {
D
fix bug  
dapan1121 已提交
444
    tt--;
445 446 447
    if (precision == TSDB_TIME_PRECISION_NANO) {
      ms += 1000000000;
    } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
448 449 450 451 452
      ms += 1000000;
    } else {
      ms += 1000;
    }
  }
453

454
  struct tm *ptm = taosLocalTime(&tt, NULL);
S
Shengliang Guan 已提交
455
  size_t     pos = strftime(buf, 35, "%Y-%m-%d %H:%M:%S", ptm);
456

457 458 459
  if (precision == TSDB_TIME_PRECISION_NANO) {
    sprintf(buf + pos, ".%09d", ms);
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
460
    sprintf(buf + pos, ".%06d", ms);
461
  } else {
D
fix bug  
dapan1121 已提交
462
    sprintf(buf + pos, ".%03d", ms);
463 464 465 466 467
  }

  return buf;
}

468
static void dumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int precision) {
469
  if (val == NULL) {
470
    taosFprintfFile(pFile, "%s", TSDB_DATA_NULL_STR);
471 472 473 474 475 476
    return;
  }

  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
477
      taosFprintfFile(pFile, "%d", ((((int32_t)(*((char *)val))) == 1) ? 1 : 0));
478 479
      break;
    case TSDB_DATA_TYPE_TINYINT:
480
      taosFprintfFile(pFile, "%d", *((int8_t *)val));
481 482
      break;
    case TSDB_DATA_TYPE_SMALLINT:
483
      taosFprintfFile(pFile, "%d", *((int16_t *)val));
484 485
      break;
    case TSDB_DATA_TYPE_INT:
486
      taosFprintfFile(pFile, "%d", *((int32_t *)val));
487 488
      break;
    case TSDB_DATA_TYPE_BIGINT:
489
      taosFprintfFile(pFile, "%" PRId64, *((int64_t *)val));
490 491
      break;
    case TSDB_DATA_TYPE_FLOAT:
492
      taosFprintfFile(pFile, "%.5f", GET_FLOAT_VAL(val));
493 494
      break;
    case TSDB_DATA_TYPE_DOUBLE:
495
      taosFprintfFile(pFile, "%.9f", GET_DOUBLE_VAL(val));
496 497 498 499 500
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
      memcpy(buf, val, length);
      buf[length] = 0;
501
      taosFprintfFile(pFile, "\'%s\'", buf);
502 503
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
S
Shengliang Guan 已提交
504
      formatTimestamp(buf, *(int64_t *)val, precision);
505
      taosFprintfFile(pFile, "'%s'", buf);
506 507 508 509 510 511
      break;
    default:
      break;
  }
}

S
Shengliang Guan 已提交
512
static int dumpResultToFile(const char *fname, TAOS_RES *tres) {
513
  TAOS_ROW row = taos_fetch_row(tres);
514 515 516 517 518 519
  if (row == NULL) {
    return 0;
  }

  wordexp_t full_path;

S
TD-1207  
Shengliang Guan 已提交
520
  if (wordexp((char *)fname, &full_path, 0) != 0) {
521
    fprintf(stderr, "ERROR: invalid file name: %s\n", fname);
H
hzcheng 已提交
522 523 524
    return -1;
  }

525
  // FILE *fp = fopen(full_path.we_wordv[0], "w");
S
Shengliang Guan 已提交
526 527
  TdFilePtr pFile =
      taosOpenFile(full_path.we_wordv[0], TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
528
  if (pFile == NULL) {
529 530 531 532 533 534 535
    fprintf(stderr, "ERROR: failed to open file: %s\n", full_path.we_wordv[0]);
    wordfree(&full_path);
    return -1;
  }

  wordfree(&full_path);

S
Shengliang Guan 已提交
536
  int         num_fields = taos_num_fields(tres);
537
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
538
  int         precision = taos_result_precision(tres);
539 540 541

  for (int col = 0; col < num_fields; col++) {
    if (col > 0) {
542
      taosFprintfFile(pFile, ",");
543
    }
544
    taosFprintfFile(pFile, "%s", fields[col].name);
545
  }
546
  taosFprintfFile(pFile, "\n");
547

548 549
  int numOfRows = 0;
  do {
S
Shengliang Guan 已提交
550
    int32_t *length = taos_fetch_lengths(tres);
551 552
    for (int i = 0; i < num_fields; i++) {
      if (i > 0) {
553
        taosFprintfFile(pFile, "\n");
554
      }
555
      dumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision);
H
hzcheng 已提交
556
    }
557
    taosFprintfFile(pFile, "\n");
558 559

    numOfRows++;
560
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
561
  } while (row != NULL);
562

D
fix bug  
dapan1121 已提交
563
  result = 0;
564
  taosCloseFile(&pFile);
565

566 567 568
  return numOfRows;
}

569
static void shellPrintNChar(const char *str, int length, int width) {
wafwerar's avatar
wafwerar 已提交
570
  TdWchar tail[3];
S
Shengliang Guan 已提交
571
  int     pos = 0, cols = 0, totalCols = 0, tailLen = 0;
572

573
  while (pos < length) {
wafwerar's avatar
wafwerar 已提交
574 575
    TdWchar wc;
    int     bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
576 577 578 579 580 581 582 583 584 585 586
    if (bytes == 0) {
      break;
    }
    pos += bytes;
    if (pos > length) {
      break;
    }

#ifdef WINDOWS
    int w = bytes;
#else
wafwerar's avatar
wafwerar 已提交
587
    int w = taosWcharWidth(wc);
588
#endif
589 590 591 592 593 594 595 596 597 598 599 600 601 602
    if (w <= 0) {
      continue;
    }

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

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
603 604
      printf("%lc", wc);
      cols += w;
605 606 607
    } else {
      tail[tailLen] = wc;
      tailLen++;
608 609 610
    }
  }

611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
  if (totalCols > width) {
    // width could be 1 or 2, so printf("...") cannot be used
    for (int i = 0; i < 3; i++) {
      if (cols >= width) {
        break;
      }
      putchar('.');
      ++cols;
    }
  } else {
    for (int i = 0; i < tailLen; i++) {
      printf("%lc", tail[i]);
    }
    cols = totalCols;
  }

627 628 629 630 631
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

S
Shengliang Guan 已提交
632
static void printField(const char *val, TAOS_FIELD *field, int width, int32_t length, int precision) {
633 634 635 636
  if (val == NULL) {
    int w = width;
    if (field->type < TSDB_DATA_TYPE_TINYINT || field->type > TSDB_DATA_TYPE_DOUBLE) {
      w = 0;
H
hzcheng 已提交
637
    }
638 639 640 641 642 643
    w = printf("%*s", w, TSDB_DATA_NULL_STR);
    for (; w < width; w++) {
      putchar(' ');
    }
    return;
  }
H
hzcheng 已提交
644

645 646 647
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
S
TD-1530  
Shengliang Guan 已提交
648
      printf("%*s", width, ((((int32_t)(*((char *)val))) == 1) ? "true" : "false"));
649 650
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
651
      printf("%*d", width, *((int8_t *)val));
652
      break;
653 654 655
    case TSDB_DATA_TYPE_UTINYINT:
      printf("%*u", width, *((uint8_t *)val));
      break;
656
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
657
      printf("%*d", width, *((int16_t *)val));
658
      break;
659 660 661
    case TSDB_DATA_TYPE_USMALLINT:
      printf("%*u", width, *((uint16_t *)val));
      break;
662
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
663
      printf("%*d", width, *((int32_t *)val));
664
      break;
665 666 667
    case TSDB_DATA_TYPE_UINT:
      printf("%*u", width, *((uint32_t *)val));
      break;
668 669 670
    case TSDB_DATA_TYPE_BIGINT:
      printf("%*" PRId64, width, *((int64_t *)val));
      break;
671 672 673
    case TSDB_DATA_TYPE_UBIGINT:
      printf("%*" PRIu64, width, *((uint64_t *)val));
      break;
674 675 676 677 678 679 680 681
    case TSDB_DATA_TYPE_FLOAT:
      printf("%*.5f", width, GET_FLOAT_VAL(val));
      break;
    case TSDB_DATA_TYPE_DOUBLE:
      printf("%*.9f", width, GET_DOUBLE_VAL(val));
      break;
    case TSDB_DATA_TYPE_BINARY:
    case TSDB_DATA_TYPE_NCHAR:
B
Bomin Zhang 已提交
682
      shellPrintNChar(val, length, width);
683 684
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
S
Shengliang Guan 已提交
685
      formatTimestamp(buf, *(int64_t *)val, precision);
686 687 688 689
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
690
  }
691
}
H
hzcheng 已提交
692

S
Shengliang Guan 已提交
693 694
bool isSelectQuery(TAOS_RES *tres) {
#if 0
D
fix bug  
dapan1121 已提交
695 696 697 698 699
  char *sql = tscGetSqlStr(tres);

  if (regex_match(sql, "^[\t ]*select[ \t]*", REG_EXTENDED | REG_ICASE)) {
    return true;
  }
S
Shengliang Guan 已提交
700
#endif
D
fix bug  
dapan1121 已提交
701 702 703
  return false;
}

S
Shengliang Guan 已提交
704
static int verticalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
705
  TAOS_ROW row = taos_fetch_row(tres);
706 707 708 709
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
710
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
711
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
712
  int         precision = taos_result_precision(tres);
713

714 715
  int maxColNameLen = 0;
  for (int col = 0; col < num_fields; col++) {
S
TD-1057  
Shengliang Guan 已提交
716
    int len = (int)strlen(fields[col].name);
717 718 719 720 721
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
722 723
  uint64_t resShowMaxNum = UINT64_MAX;

S
Shengliang Guan 已提交
724
  if (args.commands == NULL && args.file[0] == 0 && isSelectQuery(tres) /*&& !tscIsQueryWithLimit(tres)*/) {
D
fix bug  
dapan1121 已提交
725 726 727
    resShowMaxNum = DEFAULT_RES_SHOW_NUM;
  }

728
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
729
  int showMore = 1;
730
  do {
D
fix bug  
dapan1121 已提交
731
    if (numOfRows < resShowMaxNum) {
D
fix bug  
dapan1121 已提交
732 733
      printf("*************************** %d.row ***************************\n", numOfRows + 1);

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

D
fix bug  
dapan1121 已提交
736
      for (int i = 0; i < num_fields; i++) {
S
Shengliang Guan 已提交
737
        TAOS_FIELD *field = fields + i;
738

D
fix bug  
dapan1121 已提交
739 740
        int padding = (int)(maxColNameLen - strlen(field->name));
        printf("%*.s%s: ", padding, " ", field->name);
741

S
Shengliang Guan 已提交
742
        printField((const char *)row[i], field, 0, length[i], precision);
D
fix bug  
dapan1121 已提交
743 744
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
745
    } else if (showMore) {
S
Shengliang Guan 已提交
746 747 748
      printf("[100 Rows showed, and more rows are fetching but will not be showed. You can ctrl+c to stop or wait.]\n");
      printf("[You can add limit statement to get more or redirect results to specific file to get all.]\n");
      showMore = 0;
749 750 751
    }

    numOfRows++;
H
Haojun Liao 已提交
752
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
753
  } while (row != NULL);
754 755 756 757

  return numOfRows;
}

S
Shengliang Guan 已提交
758
static int calcColWidth(TAOS_FIELD *field, int precision) {
S
TD-1057  
Shengliang Guan 已提交
759
  int width = (int)strlen(field->name);
760 761 762

  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
dengyihao's avatar
dengyihao 已提交
763
      return TMAX(5, width);  // 'false'
764 765

    case TSDB_DATA_TYPE_TINYINT:
766
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
767
      return TMAX(4, width);  // '-127'
768 769

    case TSDB_DATA_TYPE_SMALLINT:
770
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
771
      return TMAX(6, width);  // '-32767'
772 773

    case TSDB_DATA_TYPE_INT:
774
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
775
      return TMAX(11, width);  // '-2147483648'
776 777

    case TSDB_DATA_TYPE_BIGINT:
778
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
779
      return TMAX(21, width);  // '-9223372036854775807'
780 781

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
782
      return TMAX(20, width);
783 784

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
785
      return TMAX(25, width);
786 787 788

    case TSDB_DATA_TYPE_BINARY:
      if (field->bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
789
        return TMAX(tsMaxBinaryDisplayWidth, width);
790
      } else {
dengyihao's avatar
dengyihao 已提交
791
        return TMAX(field->bytes, width);
792 793
      }

794 795 796
    case TSDB_DATA_TYPE_NCHAR: {
      int16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
      if (bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
797
        return TMAX(tsMaxBinaryDisplayWidth, width);
798
      } else {
dengyihao's avatar
dengyihao 已提交
799
        return TMAX(bytes, width);
800 801 802
      }
    }

803 804
    case TSDB_DATA_TYPE_TIMESTAMP:
      if (args.is_raw_time) {
dengyihao's avatar
dengyihao 已提交
805
        return TMAX(14, width);
S
Shengliang Guan 已提交
806 807
      }
      if (precision == TSDB_TIME_PRECISION_NANO) {
dengyihao's avatar
dengyihao 已提交
808
        return TMAX(29, width);
809
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
dengyihao's avatar
dengyihao 已提交
810
        return TMAX(26, width);  // '2020-01-01 00:00:00.000000'
811
      } else {
dengyihao's avatar
dengyihao 已提交
812
        return TMAX(23, width);  // '2020-01-01 00:00:00.000'
S
slguan 已提交
813
      }
H
hzcheng 已提交
814

815 816
    default:
      assert(false);
H
hzcheng 已提交
817 818
  }

819 820
  return 0;
}
H
hzcheng 已提交
821

S
Shengliang Guan 已提交
822
static void printHeader(TAOS_FIELD *fields, int *width, int num_fields) {
823 824
  int rowWidth = 0;
  for (int col = 0; col < num_fields; col++) {
S
Shengliang Guan 已提交
825 826 827
    TAOS_FIELD *field = fields + col;
    int         padding = (int)(width[col] - strlen(field->name));
    int         left = padding / 2;
828 829 830 831 832 833 834 835 836 837 838
    printf(" %*.s%s%*.s |", left, " ", field->name, padding - left, " ");
    rowWidth += width[col] + 3;
  }

  putchar('\n');
  for (int i = 0; i < rowWidth; i++) {
    putchar('=');
  }
  putchar('\n');
}

S
Shengliang Guan 已提交
839
static int horizontalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
840
  TAOS_ROW row = taos_fetch_row(tres);
841 842 843 844
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
845
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
846
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
847
  int         precision = taos_result_precision(tres);
848 849 850 851 852 853 854 855

  int width[TSDB_MAX_COLUMNS];
  for (int col = 0; col < num_fields; col++) {
    width[col] = calcColWidth(fields + col, precision);
  }

  printHeader(fields, width, num_fields);

D
fix bug  
dapan1121 已提交
856 857
  uint64_t resShowMaxNum = UINT64_MAX;

S
Shengliang Guan 已提交
858
  if (args.commands == NULL && args.file[0] == 0 && isSelectQuery(tres) /* && !tscIsQueryWithLimit(tres)*/) {
D
fix bug  
dapan1121 已提交
859 860 861
    resShowMaxNum = DEFAULT_RES_SHOW_NUM;
  }

862
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
863
  int showMore = 1;
864

865
  do {
S
Shengliang Guan 已提交
866
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
867 868 869
    if (numOfRows < resShowMaxNum) {
      for (int i = 0; i < num_fields; i++) {
        putchar(' ');
S
Shengliang Guan 已提交
870
        printField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
871 872 873 874
        putchar(' ');
        putchar('|');
      }
      putchar('\n');
D
fix bug  
dapan1121 已提交
875
    } else if (showMore) {
S
Shengliang Guan 已提交
876 877 878
      printf("[100 Rows showed, and more rows are fetching but will not be showed. You can ctrl+c to stop or wait.]\n");
      printf("[You can add limit statement to show more or redirect results to specific file to get all.]\n");
      showMore = 0;
879
    }
880

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

  return numOfRows;
}

H
Haojun Liao 已提交
888
int shellDumpResult(TAOS_RES *tres, char *fname, int *error_no, bool vertical) {
889
  int numOfRows = 0;
H
hzcheng 已提交
890
  if (fname != NULL) {
H
Haojun Liao 已提交
891
    numOfRows = dumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
892
  } else if (vertical) {
H
Haojun Liao 已提交
893
    numOfRows = verticalPrintResult(tres);
894
  } else {
H
Haojun Liao 已提交
895
    numOfRows = horizontalPrintResult(tres);
H
hzcheng 已提交
896 897
  }

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

void read_history() {
  // Initialize history
  memset(history.hist, 0, sizeof(char *) * MAX_HISTORY_SIZE);
  history.hstart = 0;
  history.hend = 0;
S
Shengliang Guan 已提交
907 908
  char *line = NULL;
  int   read_size = 0;
H
hzcheng 已提交
909 910 911 912

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

913
  // FILE *f = fopen(f_history, "r");
914
  TdFilePtr pFile = taosOpenFile(f_history, TD_FILE_READ | TD_FILE_STREAM);
915
  if (pFile == NULL) {
916
#ifndef WINDOWS
H
Hui Li 已提交
917 918 919
    if (errno != ENOENT) {
      fprintf(stderr, "Failed to open file %s, reason:%s\n", f_history, strerror(errno));
    }
920
#endif
H
hzcheng 已提交
921 922 923
    return;
  }

924
  while ((read_size = taosGetLineFile(pFile, &line)) != -1) {
H
hzcheng 已提交
925 926 927 928 929 930 931 932 933 934
    line[read_size - 1] = '\0';
    history.hist[history.hend] = strdup(line);

    history.hend = (history.hend + 1) % MAX_HISTORY_SIZE;

    if (history.hend == history.hstart) {
      history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE;
    }
  }

S
Shengliang Guan 已提交
935
  if (line != NULL) taosMemoryFree(line);
936
  taosCloseFile(&pFile);
H
hzcheng 已提交
937 938 939
}

void write_history() {
940
  char f_history[TSDB_FILENAME_LEN];
H
hzcheng 已提交
941 942
  get_history_path(f_history);

943
  // FILE *f = fopen(f_history, "w");
944
  TdFilePtr pFile = taosOpenFile(f_history, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
945
  if (pFile == NULL) {
946
#ifndef WINDOWS
H
Hui Li 已提交
947
    fprintf(stderr, "Failed to open file %s for write, reason:%s\n", f_history, strerror(errno));
948
#endif
H
hzcheng 已提交
949 950 951 952 953
    return;
  }

  for (int i = history.hstart; i != history.hend;) {
    if (history.hist[i] != NULL) {
954
      taosFprintfFile(pFile, "%s\n", history.hist[i]);
wafwerar's avatar
wafwerar 已提交
955
      taosMemoryFreeClear(history.hist[i]);
H
hzcheng 已提交
956 957 958
    }
    i = (i + 1) % MAX_HISTORY_SIZE;
  }
959
  taosCloseFile(&pFile);
H
hzcheng 已提交
960 961
}

S
TD-1793  
Shengliang Guan 已提交
962 963
void taos_error(TAOS_RES *tres, int64_t st) {
  int64_t et = taosGetTimestampUs();
H
Haojun Liao 已提交
964
  atomic_store_ptr(&result, 0);
S
TD-1793  
Shengliang Guan 已提交
965
  fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6);
H
Haojun Liao 已提交
966
  taos_free_result(tres);
H
hzcheng 已提交
967 968
}

S
#928  
slguan 已提交
969
int isCommentLine(char *line) {
H
hzcheng 已提交
970 971 972 973 974 975 976
  if (line == NULL) return 1;

  return regex_match(line, "^\\s*#.*", REG_EXTENDED);
}

void source_file(TAOS *con, char *fptr) {
  wordexp_t full_path;
977
  int       read_len = 0;
wafwerar's avatar
wafwerar 已提交
978
  char     *cmd = taosMemoryCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1);
979
  size_t    cmd_len = 0;
S
Shengliang Guan 已提交
980
  char     *line = NULL;
H
hzcheng 已提交
981 982 983

  if (wordexp(fptr, &full_path, 0) != 0) {
    fprintf(stderr, "ERROR: illegal file name\n");
wafwerar's avatar
wafwerar 已提交
984
    taosMemoryFree(cmd);
H
hzcheng 已提交
985 986 987 988 989
    return;
  }

  char *fname = full_path.we_wordv[0];

H
Hui Li 已提交
990
  /*
991 992
  if (access(fname, F_OK) != 0) {
    fprintf(stderr, "ERROR: file %s is not exist\n", fptr);
993

994
    wordfree(&full_path);
wafwerar's avatar
wafwerar 已提交
995
    taosMemoryFree(cmd);
996 997
    return;
  }
H
Hui Li 已提交
998
  */
999

1000
  // FILE *f = fopen(fname, "r");
1001
  TdFilePtr pFile = taosOpenFile(fname, TD_FILE_READ | TD_FILE_STREAM);
1002
  if (pFile == NULL) {
H
hzcheng 已提交
1003 1004
    fprintf(stderr, "ERROR: failed to open file %s\n", fname);
    wordfree(&full_path);
wafwerar's avatar
wafwerar 已提交
1005
    taosMemoryFree(cmd);
H
hzcheng 已提交
1006 1007 1008
    return;
  }

1009
  while ((read_len = taosGetLineFile(pFile, &line)) != -1) {
H
Haojun Liao 已提交
1010
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    line[--read_len] = '\0';

    if (read_len == 0 || isCommentLine(line)) {  // line starts with #
      continue;
    }

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

    memcpy(cmd + cmd_len, line, read_len);
    printf("%s%s\n", PROMPT_HEADER, cmd);
    shellRunCommand(con, cmd);
H
Haojun Liao 已提交
1027
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
1028 1029 1030
    cmd_len = 0;
  }

wafwerar's avatar
wafwerar 已提交
1031
  taosMemoryFree(cmd);
S
Shengliang Guan 已提交
1032
  if (line != NULL) taosMemoryFree(line);
H
hzcheng 已提交
1033
  wordfree(&full_path);
1034
  taosCloseFile(&pFile);
H
hzcheng 已提交
1035
}
S
slguan 已提交
1036 1037

void shellGetGrantInfo(void *con) {
S
slguan 已提交
1038
  return;
H
Hui Li 已提交
1039
#if 0
S
slguan 已提交
1040 1041
  char sql[] = "show grants";

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

1044
  int code = taos_errno(tres);
S
slguan 已提交
1045
  if (code != TSDB_CODE_SUCCESS) {
1046
    if (code == TSDB_CODE_COM_OPS_NOT_SUPPORT) {
S
slguan 已提交
1047
      fprintf(stdout, "Server is Community Edition, version is %s\n\n", taos_get_server_info(con));
S
slguan 已提交
1048 1049 1050
    } else {
      fprintf(stderr, "Failed to check Server Edition, Reason:%d:%s\n\n", taos_errno(con), taos_errstr(con));
    }
S
slguan 已提交
1051 1052 1053
    return;
  }

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

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

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

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

    if (strcmp(expiretime, "unlimited") == 0) {
S
slguan 已提交
1080
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, taos_get_server_info(con));
S
slguan 已提交
1081
    } else {
S
slguan 已提交
1082
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will expire at %s.\n", serverVersion, taos_get_server_info(con), expiretime);
S
slguan 已提交
1083 1084 1085
    }

    result = NULL;
1086
    taos_free_result(tres);
S
slguan 已提交
1087 1088 1089
  }

  fprintf(stdout, "\n");
S
Shengliang Guan 已提交
1090
#endif
S
slguan 已提交
1091
}