shellEngine.c 27.3 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
  if (!_args->is_use_passwd) {
#ifdef TD_WINDOWS
S
os env  
Shengliang Guan 已提交
71
    strcpy(tsOsName, "Windows");
72
#elif defined(TD_DARWIN)
S
os env  
Shengliang Guan 已提交
73
    strcpy(tsOsName, "Darwin");
74
#endif
S
os env  
Shengliang Guan 已提交
75
    printf(CLIENT_VERSION, tsOsName, taos_get_client_info());
76 77
  }

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

H
Haojun Liao 已提交
344 345
  TAOS_FIELD* pFields = taos_fetch_fields(pSql);
  if (pFields != NULL) {  // select and show kinds of commands
346 347
    int error_no = 0;

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

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

  printf("\n");

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

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

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

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

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

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

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

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

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

  return buf;
}

462
static void dumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int precision) {
463
  if (val == NULL) {
464
    taosFprintfFile(pFile, "%s", TSDB_DATA_NULL_STR);
465 466 467 468 469 470
    return;
  }

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

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

  wordexp_t full_path;

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

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

  for (int col = 0; col < num_fields; col++) {
    if (col > 0) {
535
      taosFprintfFile(pFile, ",");
536
    }
537
    taosFprintfFile(pFile, "%s", fields[col].name);
538
  }
539
  taosFprintfFile(pFile, "\n");
540

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

    numOfRows++;
553
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
554
  } while (row != NULL);
555

D
fix bug  
dapan1121 已提交
556
  result = 0;
557
  taosCloseFile(&pFile);
558

559 560 561
  return numOfRows;
}

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

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

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

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

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

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
  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;
  }

620 621 622 623 624
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

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

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

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

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

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

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

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

D
fix bug  
dapan1121 已提交
715 716
  uint64_t resShowMaxNum = UINT64_MAX;

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

    case TSDB_DATA_TYPE_TINYINT:
759
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
760
      return TMAX(4, width);  // '-127'
761 762

    case TSDB_DATA_TYPE_SMALLINT:
763
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
764
      return TMAX(6, width);  // '-32767'
765 766

    case TSDB_DATA_TYPE_INT:
767
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
768
      return TMAX(11, width);  // '-2147483648'
769 770

    case TSDB_DATA_TYPE_BIGINT:
771
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
772
      return TMAX(21, width);  // '-9223372036854775807'
773 774

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
775
      return TMAX(20, width);
776 777

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
778
      return TMAX(25, width);
779 780 781

    case TSDB_DATA_TYPE_BINARY:
      if (field->bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
782
        return TMAX(tsMaxBinaryDisplayWidth, width);
783
      } else {
dengyihao's avatar
dengyihao 已提交
784
        return TMAX(field->bytes, width);
785 786
      }

787 788 789
    case TSDB_DATA_TYPE_NCHAR: {
      int16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
      if (bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
790
        return TMAX(tsMaxBinaryDisplayWidth, width);
791
      } else {
dengyihao's avatar
dengyihao 已提交
792
        return TMAX(bytes, width);
793 794 795
      }
    }

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

808 809
    default:
      assert(false);
H
hzcheng 已提交
810 811
  }

812 813
  return 0;
}
H
hzcheng 已提交
814

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

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

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

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

855
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
856
  int showMore = 1;
857

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

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

  return numOfRows;
}

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

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

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

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

917
  while ((read_size = taosGetLineFile(pFile, &line)) != -1) {
H
hzcheng 已提交
918 919 920 921 922 923 924 925 926 927
    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;
    }
  }

928 929
  if(line != NULL) free(line);
  taosCloseFile(&pFile);
H
hzcheng 已提交
930 931 932
}

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

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

  for (int i = history.hstart; i != history.hend;) {
    if (history.hist[i] != NULL) {
947
      taosFprintfFile(pFile, "%s\n", history.hist[i]);
S
TD-1848  
Shengliang Guan 已提交
948
      tfree(history.hist[i]);
H
hzcheng 已提交
949 950 951
    }
    i = (i + 1) % MAX_HISTORY_SIZE;
  }
952
  taosCloseFile(&pFile);
H
hzcheng 已提交
953 954
}

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

S
#928  
slguan 已提交
962
int isCommentLine(char *line) {
H
hzcheng 已提交
963 964 965 966 967 968 969
  if (line == NULL) return 1;

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

void source_file(TAOS *con, char *fptr) {
  wordexp_t full_path;
970
  int       read_len = 0;
H
Haojun Liao 已提交
971
  char     *cmd = calloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1);
972
  size_t    cmd_len = 0;
S
Shengliang Guan 已提交
973
  char     *line = NULL;
H
hzcheng 已提交
974 975 976

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

  char *fname = full_path.we_wordv[0];

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

987 988 989 990
    wordfree(&full_path);
    free(cmd);
    return;
  }
H
Hui Li 已提交
991
  */
992

993
  // FILE *f = fopen(fname, "r");
994
  TdFilePtr pFile = taosOpenFile(fname, TD_FILE_READ | TD_FILE_STREAM);
995
  if (pFile == NULL) {
H
hzcheng 已提交
996 997
    fprintf(stderr, "ERROR: failed to open file %s\n", fname);
    wordfree(&full_path);
998
    free(cmd);
H
hzcheng 已提交
999 1000 1001
    return;
  }

1002
  while ((read_len = taosGetLineFile(pFile, &line)) != -1) {
H
Haojun Liao 已提交
1003
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
    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 已提交
1020
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
1021 1022 1023 1024
    cmd_len = 0;
  }

  free(cmd);
1025
  if(line != NULL) free(line);
H
hzcheng 已提交
1026
  wordfree(&full_path);
1027
  taosCloseFile(&pFile);
H
hzcheng 已提交
1028
}
S
slguan 已提交
1029 1030

void shellGetGrantInfo(void *con) {
S
slguan 已提交
1031
  return;
H
Hui Li 已提交
1032
#if 0
S
slguan 已提交
1033 1034
  char sql[] = "show grants";

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

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

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

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

S
slguan 已提交
1064
    char serverVersion[32] = {0};
S
slguan 已提交
1065 1066 1067
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
1068
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
1069 1070 1071 1072
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

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

    result = NULL;
1079
    taos_free_result(tres);
S
slguan 已提交
1080 1081 1082
  }

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