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

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

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

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

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

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

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

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

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

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

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

79 80
  fflush(stdout);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

H
hzcheng 已提交
331 332
  st = taosGetTimestampUs();

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

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

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

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

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

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

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

  printf("\n");

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

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

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

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

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

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

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

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

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

  return buf;
}

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

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

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

  wordexp_t full_path;

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

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

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

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

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

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

568 569 570
  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

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

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

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

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

wafwerar's avatar
wafwerar 已提交
937
  if(line != NULL) taosMemoryFree(line);
938
  taosCloseFile(&pFile);
H
hzcheng 已提交
939 940 941
}

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

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

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

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

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

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

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

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

  char *fname = full_path.we_wordv[0];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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