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

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

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

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

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

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

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

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

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

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

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

82 83
  fflush(stdout);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

S
Shengliang Guan 已提交
206
int32_t shellRunCommand(TAOS *con, char *command) {
207 208 209 210 211 212 213 214 215 216
  /* 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 已提交
217
      taosMemoryFreeClear(history.hist[history.hend]);
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    }
    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;
    }
255

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

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

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

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

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

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

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

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

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

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

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

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

H
hzcheng 已提交
347 348 349
    return;
  }

S
Shengliang Guan 已提交
350
  TAOS_FIELD *pFields = taos_fetch_fields(pSql);
H
Haojun Liao 已提交
351
  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
    }
S
Shengliang Guan 已提交
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

455
  struct tm *ptm = taosLocalTime(&tt, NULL);
S
Shengliang Guan 已提交
456
  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");
S
Shengliang Guan 已提交
527 528
  TdFilePtr pFile =
      taosOpenFile(full_path.we_wordv[0], TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
529
  if (pFile == NULL) {
530 531 532 533 534 535 536
    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 已提交
537
  int         num_fields = taos_num_fields(tres);
538
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
539
  int         precision = taos_result_precision(tres);
540 541 542

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

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

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

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

567 568 569
  return numOfRows;
}

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

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

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

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

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

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
  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;
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

S
Shengliang Guan 已提交
743
        printField((const char *)row[i], field, 0, length[i], precision);
D
fix bug  
dapan1121 已提交
744 745
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
746
    } else if (showMore) {
S
Shengliang Guan 已提交
747 748 749
      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;
750 751 752
    }

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

  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

866
  do {
S
Shengliang Guan 已提交
867
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
868 869 870
    if (numOfRows < resShowMaxNum) {
      for (int i = 0; i < num_fields; i++) {
        putchar(' ');
S
Shengliang Guan 已提交
871
        printField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
872 873 874 875
        putchar(' ');
        putchar('|');
      }
      putchar('\n');
D
fix bug  
dapan1121 已提交
876
    } else if (showMore) {
S
Shengliang Guan 已提交
877 878 879
      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;
880
    }
881

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

  return numOfRows;
}

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

H
Haojun Liao 已提交
899
  *error_no = taos_errno(tres);
H
hzcheng 已提交
900 901 902 903 904 905 906 907
  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 已提交
908 909
  char *line = NULL;
  int   read_size = 0;
H
hzcheng 已提交
910 911 912 913

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

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

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

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

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

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

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

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

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

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

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

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

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

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

  char *fname = full_path.we_wordv[0];

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

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

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

1010
  while ((read_len = taosGetLineFile(pFile, &line)) != -1) {
H
Haojun Liao 已提交
1011
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
    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 已提交
1028
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
1029 1030 1031
    cmd_len = 0;
  }

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

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

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

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

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

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

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

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

    if (strcmp(expiretime, "unlimited") == 0) {
S
slguan 已提交
1081
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, taos_get_server_info(con));
S
slguan 已提交
1082
    } else {
S
slguan 已提交
1083
      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 已提交
1084 1085 1086
    }

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

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