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

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
slguan 已提交
26
#include "tglobal.h"
S
Shengliang Guan 已提交
27 28
#include "ttypes.h"
#include "tutil.h"
29
#include "tconfig.h"
H
Haojun Liao 已提交
30

31
#include <regex.h>
S
Shengliang Guan 已提交
32
#include <wordexp.h>
H
hzcheng 已提交
33 34

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

char      CONTINUE_PROMPT[] = "    -> ";
int       prompt_size = 7;
42 43 44 45 46 47 48
#elif (_TD_TQ_ == true)
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> ";

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

54 55
char      CONTINUE_PROMPT[] = "   -> ";
int       prompt_size = 6;
H
Hui Li 已提交
56 57
#endif

D
fix bug  
dapan1121 已提交
58
int64_t result = 0;
59
SShellHistory   history;
H
hzcheng 已提交
60

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

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

79 80
  fflush(stdout);

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

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

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

94 95 96 97 98
  SConfig *pCfg = cfgInit();
  if (NULL == pCfg) return NULL;

  if (0 != taosAddClientLogCfg(pCfg)) return NULL;

H
hzcheng 已提交
99
  // Connect to the database.
100
  TAOS *con = NULL;
101 102
  if (_args->auth == NULL) {
    con = taos_connect(_args->host, _args->user, _args->password, _args->database, _args->port);
103
  } else {
104
    con = taos_connect_auth(_args->host, _args->user, _args->auth, _args->database, _args->port);
105 106
  }

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

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

  // Check if it is temperory run
116 117 118 119
  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 已提交
120 121
    }

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

126
    taos_close(con);
H
hzcheng 已提交
127 128 129 130
    write_history();
    exit(EXIT_SUCCESS);
  }

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

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

H
hzcheng 已提交
147 148 149
  return con;
}

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

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

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

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

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

194
  if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
195 196 197 198 199 200
    /* 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);
201
    return 0;
H
hzcheng 已提交
202
  }
203 204

  shellRunCommandOnServer(con, command);
205
  return 0;
H
hzcheng 已提交
206 207
}

S
Shengliang Guan 已提交
208
int32_t shellRunCommand(TAOS *con, char *command) {
209 210 211 212 213 214 215 216 217 218
  /* 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 已提交
219
      taosMemoryFreeClear(history.hist[history.hend]);
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 254 255 256
    }
    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;
    }
257

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

    if (quote == c) {
      quote = 0;
D
dapan1121 已提交
269
    } else if (quote == 0 && (c == '\'' || c == '"')) {
270 271 272 273
      quote = c;
    }

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

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

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

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

  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];
  }

321 322 323 324 325 326 327 328 329 330
  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 已提交
331 332
  st = taosGetTimestampUs();

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

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

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

D
fix bug  
dapan1121 已提交
345
    atomic_store_64(&result, 0);
D
dapan1121 已提交
346
    freeResultWithRid(oresult);
H
hzcheng 已提交
347 348 349
    return;
  }

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

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

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

  printf("\n");

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

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

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

  /* 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 已提交
414
static char *formatTimestamp(char *buf, int64_t val, int precision) {
415 416 417 418
  if (args.is_raw_time) {
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
419

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

S
Shengliang Guan 已提交
433 434 435 436 437 438 439
  /* 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;
    }
    */
440

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

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

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

  return buf;
}

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

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

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

  wordexp_t full_path;

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

526
  // FILE *fp = fopen(full_path.we_wordv[0], "w");
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
  char  *line = NULL;
908
  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;
    }
  }

wafwerar's avatar
wafwerar 已提交
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 1032
  taosMemoryFree(cmd);
  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
}