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

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

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

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

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

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

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

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

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

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

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

78 79
  fflush(stdout);

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

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

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

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

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

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

  // Check if it is temperory run
110 111 112 113
  if (_args->commands != NULL || _args->file[0] != 0) {
    if (_args->commands != NULL) {
      printf("%s%s\n", PROMPT_HEADER, _args->commands);
      shellRunCommand(con, _args->commands);
H
hzcheng 已提交
114 115
    }

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

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

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

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

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

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

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

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

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

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

188
  if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
189 190 191 192 193 194
    /* If source file. */
    char *c_ptr = strtok(command, " ;");
    assert(c_ptr != NULL);
    c_ptr = strtok(NULL, " ;");
    assert(c_ptr != NULL);
    source_file(con, c_ptr);
195
    return 0;
H
hzcheng 已提交
196
  }
197 198

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

S
Shengliang Guan 已提交
202
int32_t shellRunCommand(TAOS *con, char *command) {
203 204 205 206 207 208 209 210 211 212
  /* If command is empty just return */
  if (isEmptyCommand(command)) {
    return 0;
  }

  /* Update the history vector. */
  if (history.hstart == history.hend ||
      history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL ||
      strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) {
    if (history.hist[history.hend] != NULL) {
S
TD-1848  
Shengliang Guan 已提交
213
      tfree(history.hist[history.hend]);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    }
    history.hist[history.hend] = strdup(command);

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

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

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

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

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

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

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

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

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

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

315 316 317 318 319 320 321 322 323 324
  if ((sptr = strstr(command, "\\G")) != NULL) {
    cptr = strstr(command, ";");
    if (cptr != NULL) {
      *cptr = '\0';
    }

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

H
hzcheng 已提交
325 326
  st = taosGetTimestampUs();

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

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

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

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

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

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

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

  printf("\n");

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

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

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

  /* Compile regular expression */
  if (regcomp(&regex, reg, cflags) != 0) {
    fprintf(stderr, "Fail to compile regex");
    exitShell();
  }

  /* Execute regular expression */
  int reti = regexec(&regex, s, 0, NULL, 0);
  if (!reti) {
    regfree(&regex);
    return 1;
  } else if (reti == REG_NOMATCH) {
    regfree(&regex);
    return 0;
  } else {
    regerror(reti, &regex, msgbuf, sizeof(msgbuf));
    fprintf(stderr, "Regex match failed: %s\n", msgbuf);
    regfree(&regex);
    exitShell();
  }

  return 0;
}

S
Shengliang Guan 已提交
407
static char *formatTimestamp(char *buf, int64_t val, int precision) {
408 409 410 411
  if (args.is_raw_time) {
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
412

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

S
Shengliang Guan 已提交
426 427 428 429 430 431 432
  /* comment out as it make testcases like select_with_tags.sim fail.
    but in windows, this may cause the call to localtime crash if tt < 0,
    need to find a better solution.
    if (tt < 0) {
      tt = 0;
    }
    */
433

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

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

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

  return buf;
}

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

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

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

  wordexp_t full_path;

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

S
Shengliang Guan 已提交
519
  FILE *fp = fopen(full_path.we_wordv[0], "w");
520 521 522 523 524 525 526 527
  if (fp == NULL) {
    fprintf(stderr, "ERROR: failed to open file: %s\n", full_path.we_wordv[0]);
    wordfree(&full_path);
    return -1;
  }

  wordfree(&full_path);

S
Shengliang Guan 已提交
528
  int         num_fields = taos_num_fields(tres);
529
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
530
  int         precision = taos_result_precision(tres);
531 532 533 534 535 536 537 538

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

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

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

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

558 559 560
  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return numOfRows;
}

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

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

  char f_history[TSDB_FILENAME_LEN];
  get_history_path(f_history);

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

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

  free(line);
  fclose(f);
}

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

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

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

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

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

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

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

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

  char *fname = full_path.we_wordv[0];

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

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

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

S
TD-1848  
Shengliang Guan 已提交
1000
  while ((read_len = tgetline(&line, &line_len, f)) != -1) {
H
Hui Li 已提交
1001
    if (read_len >= tsMaxSQLStringLen) continue;
H
hzcheng 已提交
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
    line[--read_len] = '\0';

    if (read_len == 0 || isCommentLine(line)) {  // line starts with #
      continue;
    }

    if (line[read_len - 1] == '\\') {
      line[read_len - 1] = ' ';
      memcpy(cmd + cmd_len, line, read_len);
      cmd_len += read_len;
      continue;
    }

    memcpy(cmd + cmd_len, line, read_len);
    printf("%s%s\n", PROMPT_HEADER, cmd);
    shellRunCommand(con, cmd);
H
Hui Li 已提交
1018
    memset(cmd, 0, tsMaxSQLStringLen);
H
hzcheng 已提交
1019 1020 1021 1022 1023 1024 1025 1026
    cmd_len = 0;
  }

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

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

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

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

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

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

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

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

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

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

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