shellEngine.c 26.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"
H
Haojun Liao 已提交
29

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

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

char      CONTINUE_PROMPT[] = "    -> ";
int       prompt_size = 7;
41 42 43 44 45 46 47
#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 已提交
48
#else
S
slguan 已提交
49
char      CLIENT_VERSION[] = "Welcome to the TDengine shell from %s, Client Version:%s\n"
50
                             "Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.\n\n";
51
char      PROMPT_HEADER[] = "taos> ";
H
Hui Li 已提交
52

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

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

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

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

78 79
  fflush(stdout);

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

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

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

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

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

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

  // Check if it is temperory run
110 111 112 113
  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 已提交
114 115
    }

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

120
    taos_close(con);
H
hzcheng 已提交
121 122 123 124
    write_history();
    exit(EXIT_SUCCESS);
  }

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

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

H
hzcheng 已提交
141 142 143
  return con;
}

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

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

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

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

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

188
  if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
189 190 191 192 193 194
    /* 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);
195
    return 0;
H
hzcheng 已提交
196
  }
197 198

  shellRunCommandOnServer(con, command);
199
  return 0;
H
hzcheng 已提交
200 201
}

S
Shengliang Guan 已提交
202
int32_t shellRunCommand(TAOS *con, char *command) {
203 204 205 206 207 208 209 210 211 212
  /* 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) {
S
TD-1848  
Shengliang Guan 已提交
213
      tfree(history.hist[history.hend]);
214 215 216 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
    }
    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;
    }
251

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

    if (quote == c) {
      quote = 0;
D
dapan1121 已提交
263
    } else if (quote == 0 && (c == '\'' || c == '"')) {
264 265 266 267
      quote = c;
    }

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

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

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

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

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

315 316 317 318 319 320 321 322 323 324
  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 已提交
325 326
  st = taosGetTimestampUs();

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

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

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

D
fix bug  
dapan1121 已提交
339
    atomic_store_64(&result, 0);
D
dapan1121 已提交
340
    freeResultWithRid(oresult);
H
hzcheng 已提交
341 342 343
    return;
  }

S
Shengliang Guan 已提交
344
  if (true /*!tscIsUpdateQuery(pSql)*/) {  // select and show kinds of commands
345 346
    int error_no = 0;

H
Haojun Liao 已提交
347 348
    int numOfRows = shellDumpResult(pSql, fname, &error_no, printMode);
    if (numOfRows < 0) {
D
fix bug  
dapan1121 已提交
349
      atomic_store_64(&result, 0);
D
dapan1121 已提交
350
      freeResultWithRid(oresult);
H
Haojun Liao 已提交
351 352
      return;
    }
H
hzcheng 已提交
353 354 355 356 357

    et = taosGetTimestampUs();
    if (error_no == 0) {
      printf("Query OK, %d row(s) in set (%.6fs)\n", numOfRows, (et - st) / 1E6);
    } else {
358
      printf("Query interrupted (%s), %d row(s) in set (%.6fs)\n", taos_errstr(pSql), numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
359 360
    }
  } else {
H
Haojun Liao 已提交
361
    int num_rows_affacted = taos_affected_rows(pSql);
H
hzcheng 已提交
362
    et = taosGetTimestampUs();
D
dapan1121 已提交
363
    printf("Query OK, %d of %d row(s) in database (%.6fs)\n", num_rows_affacted, num_rows_affacted, (et - st) / 1E6);
H
hzcheng 已提交
364 365 366 367 368 369 370
  }

  printf("\n");

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

D
fix bug  
dapan1121 已提交
372
  atomic_store_64(&result, 0);
D
dapan1121 已提交
373
  freeResultWithRid(oresult);
H
hzcheng 已提交
374 375 376 377 378
}

/* Function to do regular expression check */
int regex_match(const char *s, const char *reg, int cflags) {
  regex_t regex;
379
  char    msgbuf[100] = {0};
H
hzcheng 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

  /* 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 已提交
405
static char *formatTimestamp(char *buf, int64_t val, int precision) {
406 407 408 409
  if (args.is_raw_time) {
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
410

S
Shengliang Guan 已提交
411
  time_t  tt;
D
fix bug  
dapan1121 已提交
412
  int32_t ms = 0;
413 414 415 416
  if (precision == TSDB_TIME_PRECISION_NANO) {
    tt = (time_t)(val / 1000000000);
    ms = val % 1000000000;
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
417
    tt = (time_t)(val / 1000000);
D
fix bug  
dapan1121 已提交
418
    ms = val % 1000000;
419 420
  } else {
    tt = (time_t)(val / 1000);
D
fix bug  
dapan1121 已提交
421
    ms = val % 1000;
422 423
  }

S
Shengliang Guan 已提交
424 425 426 427 428 429 430
  /* 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;
    }
    */
431

432 433 434
#ifdef WINDOWS
  if (tt < 0) tt = 0;
#endif
S
Shengliang Guan 已提交
435
  if (tt <= 0 && ms < 0) {
D
fix bug  
dapan1121 已提交
436
    tt--;
437 438 439
    if (precision == TSDB_TIME_PRECISION_NANO) {
      ms += 1000000000;
    } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
440 441 442 443 444
      ms += 1000000;
    } else {
      ms += 1000;
    }
  }
445

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

449 450 451
  if (precision == TSDB_TIME_PRECISION_NANO) {
    sprintf(buf + pos, ".%09d", ms);
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
452
    sprintf(buf + pos, ".%06d", ms);
453
  } else {
D
fix bug  
dapan1121 已提交
454
    sprintf(buf + pos, ".%03d", ms);
455 456 457 458 459
  }

  return buf;
}

S
Shengliang Guan 已提交
460
static void dumpFieldToFile(FILE *fp, const char *val, TAOS_FIELD *field, int32_t length, int precision) {
461 462 463 464 465 466 467 468
  if (val == NULL) {
    fprintf(fp, "%s", TSDB_DATA_NULL_STR);
    return;
  }

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

S
Shengliang Guan 已提交
504
static int dumpResultToFile(const char *fname, TAOS_RES *tres) {
505
  TAOS_ROW row = taos_fetch_row(tres);
506 507 508 509 510 511
  if (row == NULL) {
    return 0;
  }

  wordexp_t full_path;

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

S
Shengliang Guan 已提交
517
  FILE *fp = fopen(full_path.we_wordv[0], "w");
518 519 520 521 522 523 524 525
  if (fp == NULL) {
    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 已提交
526
  int         num_fields = taos_num_fields(tres);
527
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
528
  int         precision = taos_result_precision(tres);
529 530 531 532 533 534 535 536

  for (int col = 0; col < num_fields; col++) {
    if (col > 0) {
      fprintf(fp, ",");
    }
    fprintf(fp, "%s", fields[col].name);
  }
  fputc('\n', fp);
537

538 539
  int numOfRows = 0;
  do {
S
Shengliang Guan 已提交
540
    int32_t *length = taos_fetch_lengths(tres);
541 542 543 544
    for (int i = 0; i < num_fields; i++) {
      if (i > 0) {
        fputc(',', fp);
      }
S
Shengliang Guan 已提交
545
      dumpFieldToFile(fp, (const char *)row[i], fields + i, length[i], precision);
H
hzcheng 已提交
546
    }
547 548 549
    fputc('\n', fp);

    numOfRows++;
550
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
551
  } while (row != NULL);
552

D
fix bug  
dapan1121 已提交
553
  result = 0;
554
  fclose(fp);
555

556 557 558
  return numOfRows;
}

559
static void shellPrintNChar(const char *str, int length, int width) {
560
  wchar_t tail[3];
S
Shengliang Guan 已提交
561
  int     pos = 0, cols = 0, totalCols = 0, tailLen = 0;
562

563 564
  while (pos < length) {
    wchar_t wc;
S
Shengliang Guan 已提交
565
    int     bytes = mbtowc(&wc, str + pos, MB_CUR_MAX);
566 567 568 569 570 571 572 573 574 575 576 577 578
    if (bytes == 0) {
      break;
    }
    pos += bytes;
    if (pos > length) {
      break;
    }

#ifdef WINDOWS
    int w = bytes;
#else
    int w = wcwidth(wc);
#endif
579 580 581 582 583 584 585 586 587 588 589 590 591 592
    if (w <= 0) {
      continue;
    }

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

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
593 594
      printf("%lc", wc);
      cols += w;
595 596 597
    } else {
      tail[tailLen] = wc;
      tailLen++;
598 599 600
    }
  }

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
  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;
  }

617 618 619 620 621
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

S
Shengliang Guan 已提交
622
static void printField(const char *val, TAOS_FIELD *field, int width, int32_t length, int precision) {
623 624 625 626
  if (val == NULL) {
    int w = width;
    if (field->type < TSDB_DATA_TYPE_TINYINT || field->type > TSDB_DATA_TYPE_DOUBLE) {
      w = 0;
H
hzcheng 已提交
627
    }
628 629 630 631 632 633
    w = printf("%*s", w, TSDB_DATA_NULL_STR);
    for (; w < width; w++) {
      putchar(' ');
    }
    return;
  }
H
hzcheng 已提交
634

635 636 637
  char buf[TSDB_MAX_BYTES_PER_ROW];
  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
S
TD-1530  
Shengliang Guan 已提交
638
      printf("%*s", width, ((((int32_t)(*((char *)val))) == 1) ? "true" : "false"));
639 640
      break;
    case TSDB_DATA_TYPE_TINYINT:
S
TD-1530  
Shengliang Guan 已提交
641
      printf("%*d", width, *((int8_t *)val));
642
      break;
643 644 645
    case TSDB_DATA_TYPE_UTINYINT:
      printf("%*u", width, *((uint8_t *)val));
      break;
646
    case TSDB_DATA_TYPE_SMALLINT:
S
TD-1530  
Shengliang Guan 已提交
647
      printf("%*d", width, *((int16_t *)val));
648
      break;
649 650 651
    case TSDB_DATA_TYPE_USMALLINT:
      printf("%*u", width, *((uint16_t *)val));
      break;
652
    case TSDB_DATA_TYPE_INT:
S
TD-1530  
Shengliang Guan 已提交
653
      printf("%*d", width, *((int32_t *)val));
654
      break;
655 656 657
    case TSDB_DATA_TYPE_UINT:
      printf("%*u", width, *((uint32_t *)val));
      break;
658 659 660
    case TSDB_DATA_TYPE_BIGINT:
      printf("%*" PRId64, width, *((int64_t *)val));
      break;
661 662 663
    case TSDB_DATA_TYPE_UBIGINT:
      printf("%*" PRIu64, width, *((uint64_t *)val));
      break;
664 665 666 667 668 669 670 671
    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 已提交
672
      shellPrintNChar(val, length, width);
673 674
      break;
    case TSDB_DATA_TYPE_TIMESTAMP:
S
Shengliang Guan 已提交
675
      formatTimestamp(buf, *(int64_t *)val, precision);
676 677 678 679
      printf("%s", buf);
      break;
    default:
      break;
H
hzcheng 已提交
680
  }
681
}
H
hzcheng 已提交
682

S
Shengliang Guan 已提交
683 684
bool isSelectQuery(TAOS_RES *tres) {
#if 0
D
fix bug  
dapan1121 已提交
685 686 687 688 689
  char *sql = tscGetSqlStr(tres);

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

S
Shengliang Guan 已提交
694
static int verticalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
695
  TAOS_ROW row = taos_fetch_row(tres);
696 697 698 699
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
700
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
701
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
702
  int         precision = taos_result_precision(tres);
703

704 705
  int maxColNameLen = 0;
  for (int col = 0; col < num_fields; col++) {
S
TD-1057  
Shengliang Guan 已提交
706
    int len = (int)strlen(fields[col].name);
707 708 709 710 711
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
712 713
  uint64_t resShowMaxNum = UINT64_MAX;

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

718
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
719
  int showMore = 1;
720
  do {
D
fix bug  
dapan1121 已提交
721
    if (numOfRows < resShowMaxNum) {
D
fix bug  
dapan1121 已提交
722 723
      printf("*************************** %d.row ***************************\n", numOfRows + 1);

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

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

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

S
Shengliang Guan 已提交
732
        printField((const char *)row[i], field, 0, length[i], precision);
D
fix bug  
dapan1121 已提交
733 734
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
735
    } else if (showMore) {
S
Shengliang Guan 已提交
736 737 738
      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;
739 740 741
    }

    numOfRows++;
H
Haojun Liao 已提交
742
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
743
  } while (row != NULL);
744 745 746 747

  return numOfRows;
}

S
Shengliang Guan 已提交
748
static int calcColWidth(TAOS_FIELD *field, int precision) {
S
TD-1057  
Shengliang Guan 已提交
749
  int width = (int)strlen(field->name);
750 751 752

  switch (field->type) {
    case TSDB_DATA_TYPE_BOOL:
S
Shengliang Guan 已提交
753
      return MAX(5, width);  // 'false'
754 755

    case TSDB_DATA_TYPE_TINYINT:
756
    case TSDB_DATA_TYPE_UTINYINT:
S
Shengliang Guan 已提交
757
      return MAX(4, width);  // '-127'
758 759

    case TSDB_DATA_TYPE_SMALLINT:
760
    case TSDB_DATA_TYPE_USMALLINT:
S
Shengliang Guan 已提交
761
      return MAX(6, width);  // '-32767'
762 763

    case TSDB_DATA_TYPE_INT:
764
    case TSDB_DATA_TYPE_UINT:
S
Shengliang Guan 已提交
765
      return MAX(11, width);  // '-2147483648'
766 767

    case TSDB_DATA_TYPE_BIGINT:
768
    case TSDB_DATA_TYPE_UBIGINT:
S
Shengliang Guan 已提交
769
      return MAX(21, width);  // '-9223372036854775807'
770 771 772 773 774 775 776 777 778 779

    case TSDB_DATA_TYPE_FLOAT:
      return MAX(20, width);

    case TSDB_DATA_TYPE_DOUBLE:
      return MAX(25, width);

    case TSDB_DATA_TYPE_BINARY:
      if (field->bytes > tsMaxBinaryDisplayWidth) {
        return MAX(tsMaxBinaryDisplayWidth, width);
780
      } else {
781
        return MAX(field->bytes, width);
782 783
      }

784 785 786 787 788 789 790 791 792
    case TSDB_DATA_TYPE_NCHAR: {
      int16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
      if (bytes > tsMaxBinaryDisplayWidth) {
        return MAX(tsMaxBinaryDisplayWidth, width);
      } else {
        return MAX(bytes, width);
      }
    }

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

805 806
    default:
      assert(false);
H
hzcheng 已提交
807 808
  }

809 810
  return 0;
}
H
hzcheng 已提交
811

S
Shengliang Guan 已提交
812
static void printHeader(TAOS_FIELD *fields, int *width, int num_fields) {
813 814
  int rowWidth = 0;
  for (int col = 0; col < num_fields; col++) {
S
Shengliang Guan 已提交
815 816 817
    TAOS_FIELD *field = fields + col;
    int         padding = (int)(width[col] - strlen(field->name));
    int         left = padding / 2;
818 819 820 821 822 823 824 825 826 827 828
    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 已提交
829
static int horizontalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
830
  TAOS_ROW row = taos_fetch_row(tres);
831 832 833 834
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
835
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
836
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
837
  int         precision = taos_result_precision(tres);
838 839 840 841 842 843 844 845

  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 已提交
846 847
  uint64_t resShowMaxNum = UINT64_MAX;

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

852
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
853
  int showMore = 1;
854

855
  do {
S
Shengliang Guan 已提交
856
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
857 858 859
    if (numOfRows < resShowMaxNum) {
      for (int i = 0; i < num_fields; i++) {
        putchar(' ');
S
Shengliang Guan 已提交
860
        printField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
861 862 863 864
        putchar(' ');
        putchar('|');
      }
      putchar('\n');
D
fix bug  
dapan1121 已提交
865
    } else if (showMore) {
S
Shengliang Guan 已提交
866 867 868
      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;
869
    }
870

871
    numOfRows++;
H
Haojun Liao 已提交
872
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
873
  } while (row != NULL);
874 875 876 877

  return numOfRows;
}

H
Haojun Liao 已提交
878
int shellDumpResult(TAOS_RES *tres, char *fname, int *error_no, bool vertical) {
879
  int numOfRows = 0;
H
hzcheng 已提交
880
  if (fname != NULL) {
H
Haojun Liao 已提交
881
    numOfRows = dumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
882
  } else if (vertical) {
H
Haojun Liao 已提交
883
    numOfRows = verticalPrintResult(tres);
884
  } else {
H
Haojun Liao 已提交
885
    numOfRows = horizontalPrintResult(tres);
H
hzcheng 已提交
886 887
  }

H
Haojun Liao 已提交
888
  *error_no = taos_errno(tres);
H
hzcheng 已提交
889 890 891 892 893 894 895 896
  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 已提交
897
  char  *line = NULL;
H
hzcheng 已提交
898
  size_t line_size = 0;
899
  int    read_size = 0;
H
hzcheng 已提交
900 901 902 903 904 905

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

  FILE *f = fopen(f_history, "r");
  if (f == NULL) {
906
#ifndef WINDOWS
H
Hui Li 已提交
907 908 909
    if (errno != ENOENT) {
      fprintf(stderr, "Failed to open file %s, reason:%s\n", f_history, strerror(errno));
    }
910
#endif
H
hzcheng 已提交
911 912 913
    return;
  }

S
TD-1848  
Shengliang Guan 已提交
914
  while ((read_size = tgetline(&line, &line_size, f)) != -1) {
H
hzcheng 已提交
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
    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;
    }
  }

  free(line);
  fclose(f);
}

void write_history() {
930
  char f_history[TSDB_FILENAME_LEN];
H
hzcheng 已提交
931 932 933 934
  get_history_path(f_history);

  FILE *f = fopen(f_history, "w");
  if (f == NULL) {
935
#ifndef WINDOWS
H
Hui Li 已提交
936
    fprintf(stderr, "Failed to open file %s for write, reason:%s\n", f_history, strerror(errno));
937
#endif
H
hzcheng 已提交
938 939 940 941 942 943
    return;
  }

  for (int i = history.hstart; i != history.hend;) {
    if (history.hist[i] != NULL) {
      fprintf(f, "%s\n", history.hist[i]);
S
TD-1848  
Shengliang Guan 已提交
944
      tfree(history.hist[i]);
H
hzcheng 已提交
945 946 947 948 949 950
    }
    i = (i + 1) % MAX_HISTORY_SIZE;
  }
  fclose(f);
}

S
TD-1793  
Shengliang Guan 已提交
951 952
void taos_error(TAOS_RES *tres, int64_t st) {
  int64_t et = taosGetTimestampUs();
H
Haojun Liao 已提交
953
  atomic_store_ptr(&result, 0);
S
TD-1793  
Shengliang Guan 已提交
954
  fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6);
H
Haojun Liao 已提交
955
  taos_free_result(tres);
H
hzcheng 已提交
956 957
}

S
#928  
slguan 已提交
958
int isCommentLine(char *line) {
H
hzcheng 已提交
959 960 961 962 963 964 965
  if (line == NULL) return 1;

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

void source_file(TAOS *con, char *fptr) {
  wordexp_t full_path;
966
  int       read_len = 0;
S
Shengliang Guan 已提交
967
  char     *cmd = calloc(1, tsMaxSQLStringLen + 1);
968
  size_t    cmd_len = 0;
S
Shengliang Guan 已提交
969
  char     *line = NULL;
970
  size_t    line_len = 0;
H
hzcheng 已提交
971 972 973

  if (wordexp(fptr, &full_path, 0) != 0) {
    fprintf(stderr, "ERROR: illegal file name\n");
974
    free(cmd);
H
hzcheng 已提交
975 976 977 978 979
    return;
  }

  char *fname = full_path.we_wordv[0];

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

984 985 986 987
    wordfree(&full_path);
    free(cmd);
    return;
  }
H
Hui Li 已提交
988
  */
989

H
hzcheng 已提交
990 991 992 993
  FILE *f = fopen(fname, "r");
  if (f == NULL) {
    fprintf(stderr, "ERROR: failed to open file %s\n", fname);
    wordfree(&full_path);
994
    free(cmd);
H
hzcheng 已提交
995 996 997
    return;
  }

S
TD-1848  
Shengliang Guan 已提交
998
  while ((read_len = tgetline(&line, &line_len, f)) != -1) {
H
Hui Li 已提交
999
    if (read_len >= tsMaxSQLStringLen) continue;
H
hzcheng 已提交
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
    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
Hui Li 已提交
1016
    memset(cmd, 0, tsMaxSQLStringLen);
H
hzcheng 已提交
1017 1018 1019 1020 1021 1022 1023 1024
    cmd_len = 0;
  }

  free(cmd);
  if (line) free(line);
  wordfree(&full_path);
  fclose(f);
}
S
slguan 已提交
1025 1026

void shellGetGrantInfo(void *con) {
S
slguan 已提交
1027
  return;
H
Hui Li 已提交
1028
#if 0
S
slguan 已提交
1029 1030
  char sql[] = "show grants";

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

1033
  int code = taos_errno(tres);
S
slguan 已提交
1034
  if (code != TSDB_CODE_SUCCESS) {
1035
    if (code == TSDB_CODE_COM_OPS_NOT_SUPPORT) {
S
slguan 已提交
1036
      fprintf(stdout, "Server is Community Edition, version is %s\n\n", taos_get_server_info(con));
S
slguan 已提交
1037 1038 1039
    } else {
      fprintf(stderr, "Failed to check Server Edition, Reason:%d:%s\n\n", taos_errno(con), taos_errstr(con));
    }
S
slguan 已提交
1040 1041 1042
    return;
  }

1043
  int num_fields = taos_field_count(tres);
S
slguan 已提交
1044 1045 1046 1047
  if (num_fields == 0) {
    fprintf(stderr, "\nInvalid grant information.\n");
    exit(0);
  } else {
1048
    if (tres == NULL) {
S
slguan 已提交
1049 1050 1051 1052
      fprintf(stderr, "\nGrant information is null.\n");
      exit(0);
    }

1053 1054
    TAOS_FIELD *fields = taos_fetch_fields(tres);
    TAOS_ROW row = taos_fetch_row(tres);
S
slguan 已提交
1055
    if (row == NULL) {
H
hjxilinx 已提交
1056
      fprintf(stderr, "\nFailed to get grant information from server. Abort.\n");
S
slguan 已提交
1057 1058 1059
      exit(0);
    }

S
slguan 已提交
1060
    char serverVersion[32] = {0};
S
slguan 已提交
1061 1062 1063
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
1064
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
1065 1066 1067 1068
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

    if (strcmp(expiretime, "unlimited") == 0) {
S
slguan 已提交
1069
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, taos_get_server_info(con));
S
slguan 已提交
1070
    } else {
S
slguan 已提交
1071
      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 已提交
1072 1073 1074
    }

    result = NULL;
1075
    taos_free_result(tres);
S
slguan 已提交
1076 1077 1078
  }

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