shellEngine.c 27.5 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
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  printf("\n");

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

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

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

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

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

S
Shengliang Guan 已提交
430 431 432 433 434 435 436
  /* 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;
    }
    */
437

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

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

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

  return buf;
}

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

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

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

  wordexp_t full_path;

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

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

  for (int col = 0; col < num_fields; col++) {
    if (col > 0) {
539
      taosFprintfFile(pFile, ",");
540
    }
541
    taosFprintfFile(pFile, "%s", fields[col].name);
542
  }
543
  taosFprintfFile(pFile, "\n");
544

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

    numOfRows++;
557
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
558
  } while (row != NULL);
559

D
fix bug  
dapan1121 已提交
560
  result = 0;
561
  taosCloseFile(&pFile);
562

563 564 565
  return numOfRows;
}

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

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

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

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

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

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
  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;
  }

624 625 626 627 628
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

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

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

S
Shengliang Guan 已提交
690 691
bool isSelectQuery(TAOS_RES *tres) {
#if 0
D
fix bug  
dapan1121 已提交
692 693 694 695 696
  char *sql = tscGetSqlStr(tres);

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

S
Shengliang Guan 已提交
701
static int verticalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
702
  TAOS_ROW row = taos_fetch_row(tres);
703 704 705 706
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
707
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
708
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
709
  int         precision = taos_result_precision(tres);
710

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

D
fix bug  
dapan1121 已提交
719 720
  uint64_t resShowMaxNum = UINT64_MAX;

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

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

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

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

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

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

    numOfRows++;
H
Haojun Liao 已提交
749
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
750
  } while (row != NULL);
751 752 753 754

  return numOfRows;
}

S
Shengliang Guan 已提交
755
static int calcColWidth(TAOS_FIELD *field, int precision) {
S
TD-1057  
Shengliang Guan 已提交
756
  int width = (int)strlen(field->name);
757 758 759

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

    case TSDB_DATA_TYPE_TINYINT:
763
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
764
      return TMAX(4, width);  // '-127'
765 766

    case TSDB_DATA_TYPE_SMALLINT:
767
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
768
      return TMAX(6, width);  // '-32767'
769 770

    case TSDB_DATA_TYPE_INT:
771
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
772
      return TMAX(11, width);  // '-2147483648'
773 774

    case TSDB_DATA_TYPE_BIGINT:
775
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
776
      return TMAX(21, width);  // '-9223372036854775807'
777 778

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
779
      return TMAX(20, width);
780 781

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
782
      return TMAX(25, width);
783 784 785

    case TSDB_DATA_TYPE_BINARY:
      if (field->bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
786
        return TMAX(tsMaxBinaryDisplayWidth, width);
787
      } else {
dengyihao's avatar
dengyihao 已提交
788
        return TMAX(field->bytes, width);
789 790
      }

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

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

812 813
    default:
      assert(false);
H
hzcheng 已提交
814 815
  }

816 817
  return 0;
}
H
hzcheng 已提交
818

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

S
Shengliang Guan 已提交
842
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
843
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
844
  int         precision = taos_result_precision(tres);
845 846 847 848 849 850 851 852

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

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

859
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
860
  int showMore = 1;
861

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

878
    numOfRows++;
H
Haojun Liao 已提交
879
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
880
  } while (row != NULL);
881 882 883 884

  return numOfRows;
}

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

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

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

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

921
  while ((read_size = taosGetLineFile(pFile, &line)) != -1) {
H
hzcheng 已提交
922 923 924 925 926 927 928 929 930 931
    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 已提交
932
  if(line != NULL) taosMemoryFree(line);
933
  taosCloseFile(&pFile);
H
hzcheng 已提交
934 935 936
}

void write_history() {
937
  char f_history[TSDB_FILENAME_LEN];
H
hzcheng 已提交
938 939
  get_history_path(f_history);

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

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

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

S
#928  
slguan 已提交
966
int isCommentLine(char *line) {
H
hzcheng 已提交
967 968 969 970 971 972 973
  if (line == NULL) return 1;

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

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

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

  char *fname = full_path.we_wordv[0];

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

991
    wordfree(&full_path);
wafwerar's avatar
wafwerar 已提交
992
    taosMemoryFree(cmd);
993 994
    return;
  }
H
Hui Li 已提交
995
  */
996

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

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

wafwerar's avatar
wafwerar 已提交
1028 1029
  taosMemoryFree(cmd);
  if(line != NULL) taosMemoryFree(line);
H
hzcheng 已提交
1030
  wordfree(&full_path);
1031
  taosCloseFile(&pFile);
H
hzcheng 已提交
1032
}
S
slguan 已提交
1033 1034

void shellGetGrantInfo(void *con) {
S
slguan 已提交
1035
  return;
H
Hui Li 已提交
1036
#if 0
S
slguan 已提交
1037 1038
  char sql[] = "show grants";

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

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

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

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

S
slguan 已提交
1068
    char serverVersion[32] = {0};
S
slguan 已提交
1069 1070 1071
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
1072
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
1073 1074 1075 1076
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

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

    result = NULL;
1083
    taos_free_result(tres);
S
slguan 已提交
1084 1085 1086
  }

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