shellEngine.c 27.2 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"
S
Shengliang Guan 已提交
22
#include "shellInt.h"
H
hzcheng 已提交
23
#include "shellCommand.h"
H
hzcheng 已提交
24
#include "taosdef.h"
S
slguan 已提交
25
#include "taoserror.h"
S
Shengliang Guan 已提交
26
#include "tconfig.h"
S
slguan 已提交
27
#include "tglobal.h"
S
Shengliang Guan 已提交
28 29
#include "ttypes.h"
#include "tutil.h"
H
Haojun Liao 已提交
30

H
hzcheng 已提交
31 32

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

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

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

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

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

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

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

80 81
  fflush(stdout);

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

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

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

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

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

  /* Read history TODO : release resources here*/
109
  shellReadHistory();
H
hzcheng 已提交
110 111

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

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

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

S
#928  
slguan 已提交
127

H
hzcheng 已提交
128 129 130
  return con;
}

S
Shengliang Guan 已提交
131
static bool isEmptyCommand(const char *cmd) {
132 133 134
  for (char c = *cmd++; c != 0; c = *cmd++) {
    if (c != ' ' && c != '\t' && c != ';') {
      return false;
H
hzcheng 已提交
135 136
    }
  }
137
  return true;
H
hzcheng 已提交
138 139
}

140
static int32_t shellRunSingleCommand(TAOS *con, char *command) {
H
hzcheng 已提交
141
  /* If command is empty just return */
142
  if (isEmptyCommand(command)) {
143
    return 0;
H
hzcheng 已提交
144 145 146
  }

  // Analyse the command.
147
  if (shellRegexMatch(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
148
    taos_close(con);
149
    shellWriteHistory();
S
Shengliang Guan 已提交
150 151 152
#ifdef WINDOWS
    exit(EXIT_SUCCESS);
#endif
153
    return -1;
154 155
  }

156
  if (shellRegexMatch(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
157 158
    // If clear the screen.
    system("clear");
159 160
    return 0;
  }
161

162
  if (shellRegexMatch(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$",
S
Shengliang Guan 已提交
163
                  REG_EXTENDED | REG_ICASE)) {
164 165
    strtok(command, " \t");
    strtok(NULL, " \t");
S
Shengliang Guan 已提交
166
    char *p = strtok(NULL, " \t");
167 168 169 170 171
    if (strcasecmp(p, "default") == 0) {
      tsMaxBinaryDisplayWidth = DEFAULT_MAX_BINARY_DISPLAY_WIDTH;
    } else {
      tsMaxBinaryDisplayWidth = atoi(p);
    }
172 173
    return 0;
  }
174

175
  if (shellRegexMatch(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
176 177 178 179 180
    /* If source file. */
    char *c_ptr = strtok(command, " ;");
    assert(c_ptr != NULL);
    c_ptr = strtok(NULL, " ;");
    assert(c_ptr != NULL);
181
    shellSourceFile(con, c_ptr);
182
    return 0;
H
hzcheng 已提交
183
  }
184

185
  shellRunCommandImp(con, command);
186
  return 0;
H
hzcheng 已提交
187 188
}

S
Shengliang Guan 已提交
189
int32_t shellRunCommand(TAOS *con, char *command) {
190 191 192 193 194 195 196 197 198 199
  /* 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 已提交
200
      taosMemoryFreeClear(history.hist[history.hend]);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    }
    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;
    }
238

239
    if (c == '\\') {
240
      if (quote != 0 && (*command == '_' || *command == '\\')) {
S
Shengliang Guan 已提交
241
        // DO nothing
242 243 244 245
      } else {
        esc = true;
        continue;
      }
246 247 248 249
    }

    if (quote == c) {
      quote = 0;
D
dapan1121 已提交
250
    } else if (quote == 0 && (c == '\'' || c == '"')) {
251 252 253 254
      quote = c;
    }

    *p++ = c;
255
    if (c == ';' && quote == 0) {
256 257 258 259 260 261 262 263 264 265 266 267 268 269
      c = *p;
      *p = 0;
      if (shellRunSingleCommand(con, cmd) < 0) {
        return -1;
      }
      *p = c;
      p = cmd;
    }
  }

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

D
dapan1121 已提交
270
void freeResultWithRid(int64_t rid) {
S
Shengliang Guan 已提交
271
#if 0
D
dapan1121 已提交
272 273 274 275 276
  SSqlObj* pSql = taosAcquireRef(tscObjRef, rid);
  if(pSql){
    taos_free_result(pSql);
    taosReleaseRef(tscObjRef, rid);
  }
S
Shengliang Guan 已提交
277
#endif
D
dapan1121 已提交
278 279
}

280
void shellRunCommandImp(TAOS *con, char command[]) {
281
  int64_t   st, et;
H
hzcheng 已提交
282
  wordexp_t full_path;
S
Shengliang Guan 已提交
283 284 285
  char     *sptr = NULL;
  char     *cptr = NULL;
  char     *fname = NULL;
286
  bool      printMode = false;
H
hzcheng 已提交
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

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

302 303 304 305 306 307 308 309 310 311
  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 已提交
312 313
  st = taosGetTimestampUs();

S
Shengliang Guan 已提交
314
  TAOS_RES *pSql = taos_query(con, command);
H
Haojun Liao 已提交
315
  if (taos_errno(pSql)) {
316
    shellPrintError(pSql, st);
H
hzcheng 已提交
317 318 319
    return;
  }

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

322
  if (shellRegexMatch(command, "^\\s*use\\s+[a-zA-Z0-9_]+\\s*;\\s*$", REG_EXTENDED | REG_ICASE)) {
H
hzcheng 已提交
323 324
    fprintf(stdout, "Database changed.\n\n");
    fflush(stdout);
325

D
fix bug  
dapan1121 已提交
326
    atomic_store_64(&result, 0);
D
dapan1121 已提交
327
    freeResultWithRid(oresult);
S
Shengliang Guan 已提交
328 329
    taos_free_result(pSql);

H
hzcheng 已提交
330 331 332
    return;
  }

S
Shengliang Guan 已提交
333
  TAOS_FIELD *pFields = taos_fetch_fields(pSql);
H
Haojun Liao 已提交
334
  if (pFields != NULL) {  // select and show kinds of commands
335 336
    int error_no = 0;

H
Haojun Liao 已提交
337 338
    int numOfRows = shellDumpResult(pSql, fname, &error_no, printMode);
    if (numOfRows < 0) {
D
fix bug  
dapan1121 已提交
339
      atomic_store_64(&result, 0);
D
dapan1121 已提交
340
      freeResultWithRid(oresult);
H
Haojun Liao 已提交
341 342
      return;
    }
H
hzcheng 已提交
343 344 345 346 347

    et = taosGetTimestampUs();
    if (error_no == 0) {
      printf("Query OK, %d row(s) in set (%.6fs)\n", numOfRows, (et - st) / 1E6);
    } else {
348
      printf("Query interrupted (%s), %d row(s) in set (%.6fs)\n", taos_errstr(pSql), numOfRows, (et - st) / 1E6);
H
hzcheng 已提交
349
    }
S
Shengliang Guan 已提交
350
    taos_free_result(pSql);
H
hzcheng 已提交
351
  } else {
H
Haojun Liao 已提交
352
    int num_rows_affacted = taos_affected_rows(pSql);
353
    taos_free_result(pSql);
H
hzcheng 已提交
354
    et = taosGetTimestampUs();
D
dapan1121 已提交
355
    printf("Query OK, %d of %d row(s) in database (%.6fs)\n", num_rows_affacted, num_rows_affacted, (et - st) / 1E6);
H
hzcheng 已提交
356 357 358 359 360 361 362
  }

  printf("\n");

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

D
fix bug  
dapan1121 已提交
364
  atomic_store_64(&result, 0);
D
dapan1121 已提交
365
  freeResultWithRid(oresult);
H
hzcheng 已提交
366 367 368
}

/* Function to do regular expression check */
369
int shellRegexMatch(const char *s, const char *reg, int cflags) {
H
hzcheng 已提交
370
  regex_t regex;
371
  char    msgbuf[100] = {0};
H
hzcheng 已提交
372 373 374 375

  /* Compile regular expression */
  if (regcomp(&regex, reg, cflags) != 0) {
    fprintf(stderr, "Fail to compile regex");
376
    shellExit();
H
hzcheng 已提交
377 378 379 380 381 382 383 384 385 386 387 388 389 390
  }

  /* 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);
391
    shellExit();
H
hzcheng 已提交
392 393 394 395 396
  }

  return 0;
}

S
Shengliang Guan 已提交
397
static char *formatTimestamp(char *buf, int64_t val, int precision) {
398 399 400 401
  if (args.is_raw_time) {
    sprintf(buf, "%" PRId64, val);
    return buf;
  }
H
hzcheng 已提交
402

S
Shengliang Guan 已提交
403
  time_t  tt;
D
fix bug  
dapan1121 已提交
404
  int32_t ms = 0;
405 406 407 408
  if (precision == TSDB_TIME_PRECISION_NANO) {
    tt = (time_t)(val / 1000000000);
    ms = val % 1000000000;
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
409
    tt = (time_t)(val / 1000000);
D
fix bug  
dapan1121 已提交
410
    ms = val % 1000000;
411 412
  } else {
    tt = (time_t)(val / 1000);
D
fix bug  
dapan1121 已提交
413
    ms = val % 1000;
414 415
  }

S
Shengliang Guan 已提交
416 417 418 419 420 421 422
  /* 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;
    }
    */
423

424 425 426
#ifdef WINDOWS
  if (tt < 0) tt = 0;
#endif
S
Shengliang Guan 已提交
427
  if (tt <= 0 && ms < 0) {
D
fix bug  
dapan1121 已提交
428
    tt--;
429 430 431
    if (precision == TSDB_TIME_PRECISION_NANO) {
      ms += 1000000000;
    } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
432 433 434 435 436
      ms += 1000000;
    } else {
      ms += 1000;
    }
  }
437

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

441 442 443
  if (precision == TSDB_TIME_PRECISION_NANO) {
    sprintf(buf + pos, ".%09d", ms);
  } else if (precision == TSDB_TIME_PRECISION_MICRO) {
D
fix bug  
dapan1121 已提交
444
    sprintf(buf + pos, ".%06d", ms);
445
  } else {
D
fix bug  
dapan1121 已提交
446
    sprintf(buf + pos, ".%03d", ms);
447 448 449 450 451
  }

  return buf;
}

452
static void dumpFieldToFile(TdFilePtr pFile, const char *val, TAOS_FIELD *field, int32_t length, int precision) {
453
  if (val == NULL) {
454
    taosFprintfFile(pFile, "%s", TSDB_DATA_NULL_STR);
455 456 457 458 459 460
    return;
  }

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

S
Shengliang Guan 已提交
496
static int dumpResultToFile(const char *fname, TAOS_RES *tres) {
497
  TAOS_ROW row = taos_fetch_row(tres);
498 499 500 501 502 503
  if (row == NULL) {
    return 0;
  }

  wordexp_t full_path;

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

509
  // FILE *fp = fopen(full_path.we_wordv[0], "w");
S
Shengliang Guan 已提交
510 511
  TdFilePtr pFile =
      taosOpenFile(full_path.we_wordv[0], TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
512
  if (pFile == NULL) {
513 514 515 516 517 518 519
    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 已提交
520
  int         num_fields = taos_num_fields(tres);
521
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
522
  int         precision = taos_result_precision(tres);
523 524 525

  for (int col = 0; col < num_fields; col++) {
    if (col > 0) {
526
      taosFprintfFile(pFile, ",");
527
    }
528
    taosFprintfFile(pFile, "%s", fields[col].name);
529
  }
530
  taosFprintfFile(pFile, "\n");
531

532 533
  int numOfRows = 0;
  do {
S
Shengliang Guan 已提交
534
    int32_t *length = taos_fetch_lengths(tres);
535 536
    for (int i = 0; i < num_fields; i++) {
      if (i > 0) {
537
        taosFprintfFile(pFile, "\n");
538
      }
539
      dumpFieldToFile(pFile, (const char *)row[i], fields + i, length[i], precision);
H
hzcheng 已提交
540
    }
541
    taosFprintfFile(pFile, "\n");
542 543

    numOfRows++;
544
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
545
  } while (row != NULL);
546

D
fix bug  
dapan1121 已提交
547
  result = 0;
548
  taosCloseFile(&pFile);
549

550 551 552
  return numOfRows;
}

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

557
  while (pos < length) {
wafwerar's avatar
wafwerar 已提交
558 559
    TdWchar wc;
    int     bytes = taosMbToWchar(&wc, str + pos, MB_CUR_MAX);
560 561 562 563 564 565 566 567 568 569 570
    if (bytes == 0) {
      break;
    }
    pos += bytes;
    if (pos > length) {
      break;
    }

#ifdef WINDOWS
    int w = bytes;
#else
wafwerar's avatar
wafwerar 已提交
571
    int w = taosWcharWidth(wc);
572
#endif
573 574 575 576 577 578 579 580 581 582 583 584 585 586
    if (w <= 0) {
      continue;
    }

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

    totalCols += w;
    if (totalCols > width) {
      break;
    }
    if (totalCols <= (width - 3)) {
587 588
      printf("%lc", wc);
      cols += w;
589 590 591
    } else {
      tail[tailLen] = wc;
      tailLen++;
592 593 594
    }
  }

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
  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;
  }

611 612 613 614 615
  for (; cols < width; cols++) {
    putchar(' ');
  }
}

S
Shengliang Guan 已提交
616
static void printField(const char *val, TAOS_FIELD *field, int width, int32_t length, int precision) {
617 618 619 620
  if (val == NULL) {
    int w = width;
    if (field->type < TSDB_DATA_TYPE_TINYINT || field->type > TSDB_DATA_TYPE_DOUBLE) {
      w = 0;
H
hzcheng 已提交
621
    }
622 623 624 625 626 627
    w = printf("%*s", w, TSDB_DATA_NULL_STR);
    for (; w < width; w++) {
      putchar(' ');
    }
    return;
  }
H
hzcheng 已提交
628

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

S
Shengliang Guan 已提交
677 678
bool isSelectQuery(TAOS_RES *tres) {
#if 0
D
fix bug  
dapan1121 已提交
679 680
  char *sql = tscGetSqlStr(tres);

681
  if (shellRegexMatch(sql, "^[\t ]*select[ \t]*", REG_EXTENDED | REG_ICASE)) {
D
fix bug  
dapan1121 已提交
682 683
    return true;
  }
S
Shengliang Guan 已提交
684
#endif
D
fix bug  
dapan1121 已提交
685 686 687
  return false;
}

S
Shengliang Guan 已提交
688
static int verticalPrintResult(TAOS_RES *tres) {
H
Haojun Liao 已提交
689
  TAOS_ROW row = taos_fetch_row(tres);
690 691 692 693
  if (row == NULL) {
    return 0;
  }

S
Shengliang Guan 已提交
694
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
695
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
696
  int         precision = taos_result_precision(tres);
697

698 699
  int maxColNameLen = 0;
  for (int col = 0; col < num_fields; col++) {
S
TD-1057  
Shengliang Guan 已提交
700
    int len = (int)strlen(fields[col].name);
701 702 703 704 705
    if (len > maxColNameLen) {
      maxColNameLen = len;
    }
  }

D
fix bug  
dapan1121 已提交
706 707
  uint64_t resShowMaxNum = UINT64_MAX;

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

712
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
713
  int showMore = 1;
714
  do {
D
fix bug  
dapan1121 已提交
715
    if (numOfRows < resShowMaxNum) {
D
fix bug  
dapan1121 已提交
716 717
      printf("*************************** %d.row ***************************\n", numOfRows + 1);

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

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

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

S
Shengliang Guan 已提交
726
        printField((const char *)row[i], field, 0, length[i], precision);
D
fix bug  
dapan1121 已提交
727 728
        putchar('\n');
      }
D
fix bug  
dapan1121 已提交
729
    } else if (showMore) {
S
Shengliang Guan 已提交
730 731 732
      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;
733 734 735
    }

    numOfRows++;
H
Haojun Liao 已提交
736
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
737
  } while (row != NULL);
738 739 740 741

  return numOfRows;
}

S
Shengliang Guan 已提交
742
static int calcColWidth(TAOS_FIELD *field, int precision) {
S
TD-1057  
Shengliang Guan 已提交
743
  int width = (int)strlen(field->name);
744 745 746

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

    case TSDB_DATA_TYPE_TINYINT:
750
    case TSDB_DATA_TYPE_UTINYINT:
dengyihao's avatar
dengyihao 已提交
751
      return TMAX(4, width);  // '-127'
752 753

    case TSDB_DATA_TYPE_SMALLINT:
754
    case TSDB_DATA_TYPE_USMALLINT:
dengyihao's avatar
dengyihao 已提交
755
      return TMAX(6, width);  // '-32767'
756 757

    case TSDB_DATA_TYPE_INT:
758
    case TSDB_DATA_TYPE_UINT:
dengyihao's avatar
dengyihao 已提交
759
      return TMAX(11, width);  // '-2147483648'
760 761

    case TSDB_DATA_TYPE_BIGINT:
762
    case TSDB_DATA_TYPE_UBIGINT:
dengyihao's avatar
dengyihao 已提交
763
      return TMAX(21, width);  // '-9223372036854775807'
764 765

    case TSDB_DATA_TYPE_FLOAT:
dengyihao's avatar
dengyihao 已提交
766
      return TMAX(20, width);
767 768

    case TSDB_DATA_TYPE_DOUBLE:
dengyihao's avatar
dengyihao 已提交
769
      return TMAX(25, width);
770 771 772

    case TSDB_DATA_TYPE_BINARY:
      if (field->bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
773
        return TMAX(tsMaxBinaryDisplayWidth, width);
774
      } else {
dengyihao's avatar
dengyihao 已提交
775
        return TMAX(field->bytes, width);
776 777
      }

778 779 780
    case TSDB_DATA_TYPE_NCHAR: {
      int16_t bytes = field->bytes * TSDB_NCHAR_SIZE;
      if (bytes > tsMaxBinaryDisplayWidth) {
dengyihao's avatar
dengyihao 已提交
781
        return TMAX(tsMaxBinaryDisplayWidth, width);
782
      } else {
dengyihao's avatar
dengyihao 已提交
783
        return TMAX(bytes, width);
784 785 786
      }
    }

787 788
    case TSDB_DATA_TYPE_TIMESTAMP:
      if (args.is_raw_time) {
dengyihao's avatar
dengyihao 已提交
789
        return TMAX(14, width);
S
Shengliang Guan 已提交
790 791
      }
      if (precision == TSDB_TIME_PRECISION_NANO) {
dengyihao's avatar
dengyihao 已提交
792
        return TMAX(29, width);
793
      } else if (precision == TSDB_TIME_PRECISION_MICRO) {
dengyihao's avatar
dengyihao 已提交
794
        return TMAX(26, width);  // '2020-01-01 00:00:00.000000'
795
      } else {
dengyihao's avatar
dengyihao 已提交
796
        return TMAX(23, width);  // '2020-01-01 00:00:00.000'
S
slguan 已提交
797
      }
H
hzcheng 已提交
798

799 800
    default:
      assert(false);
H
hzcheng 已提交
801 802
  }

803 804
  return 0;
}
H
hzcheng 已提交
805

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

S
Shengliang Guan 已提交
829
  int         num_fields = taos_num_fields(tres);
H
Haojun Liao 已提交
830
  TAOS_FIELD *fields = taos_fetch_fields(tres);
S
Shengliang Guan 已提交
831
  int         precision = taos_result_precision(tres);
832 833 834 835 836 837 838 839

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

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

846
  int numOfRows = 0;
D
fix bug  
dapan1121 已提交
847
  int showMore = 1;
848

849
  do {
S
Shengliang Guan 已提交
850
    int32_t *length = taos_fetch_lengths(tres);
D
fix bug  
dapan1121 已提交
851 852 853
    if (numOfRows < resShowMaxNum) {
      for (int i = 0; i < num_fields; i++) {
        putchar(' ');
S
Shengliang Guan 已提交
854
        printField((const char *)row[i], fields + i, width[i], length[i], precision);
D
fix bug  
dapan1121 已提交
855 856 857 858
        putchar(' ');
        putchar('|');
      }
      putchar('\n');
D
fix bug  
dapan1121 已提交
859
    } else if (showMore) {
S
Shengliang Guan 已提交
860 861 862
      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;
863
    }
864

865
    numOfRows++;
H
Haojun Liao 已提交
866
    row = taos_fetch_row(tres);
S
Shengliang Guan 已提交
867
  } while (row != NULL);
868 869 870 871

  return numOfRows;
}

H
Haojun Liao 已提交
872
int shellDumpResult(TAOS_RES *tres, char *fname, int *error_no, bool vertical) {
873
  int numOfRows = 0;
H
hzcheng 已提交
874
  if (fname != NULL) {
H
Haojun Liao 已提交
875
    numOfRows = dumpResultToFile(fname, tres);
S
Shengliang Guan 已提交
876
  } else if (vertical) {
H
Haojun Liao 已提交
877
    numOfRows = verticalPrintResult(tres);
878
  } else {
H
Haojun Liao 已提交
879
    numOfRows = horizontalPrintResult(tres);
H
hzcheng 已提交
880 881
  }

H
Haojun Liao 已提交
882
  *error_no = taos_errno(tres);
H
hzcheng 已提交
883 884 885
  return numOfRows;
}

886
void shellReadHistory() {
H
hzcheng 已提交
887 888 889 890
  // Initialize history
  memset(history.hist, 0, sizeof(char *) * MAX_HISTORY_SIZE);
  history.hstart = 0;
  history.hend = 0;
S
Shengliang Guan 已提交
891 892
  char *line = NULL;
  int   read_size = 0;
H
hzcheng 已提交
893 894

  char f_history[TSDB_FILENAME_LEN];
895
  shellHistoryPath(f_history);
H
hzcheng 已提交
896

897
  // FILE *f = fopen(f_history, "r");
898
  TdFilePtr pFile = taosOpenFile(f_history, TD_FILE_READ | TD_FILE_STREAM);
899
  if (pFile == NULL) {
900
#ifndef WINDOWS
H
Hui Li 已提交
901 902 903
    if (errno != ENOENT) {
      fprintf(stderr, "Failed to open file %s, reason:%s\n", f_history, strerror(errno));
    }
904
#endif
H
hzcheng 已提交
905 906 907
    return;
  }

908
  while ((read_size = taosGetLineFile(pFile, &line)) != -1) {
H
hzcheng 已提交
909 910 911 912 913 914 915 916 917 918
    line[read_size - 1] = '\0';
    history.hist[history.hend] = strdup(line);

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

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

S
Shengliang Guan 已提交
919
  if (line != NULL) taosMemoryFree(line);
920
  taosCloseFile(&pFile);
H
hzcheng 已提交
921 922
}

923
void shellWriteHistory() {
924
  char f_history[TSDB_FILENAME_LEN];
925
  shellHistoryPath(f_history);
H
hzcheng 已提交
926

927
  // FILE *f = fopen(f_history, "w");
928
  TdFilePtr pFile = taosOpenFile(f_history, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_TRUNC | TD_FILE_STREAM);
929
  if (pFile == NULL) {
930
#ifndef WINDOWS
H
Hui Li 已提交
931
    fprintf(stderr, "Failed to open file %s for write, reason:%s\n", f_history, strerror(errno));
932
#endif
H
hzcheng 已提交
933 934 935 936 937
    return;
  }

  for (int i = history.hstart; i != history.hend;) {
    if (history.hist[i] != NULL) {
938
      taosFprintfFile(pFile, "%s\n", history.hist[i]);
wafwerar's avatar
wafwerar 已提交
939
      taosMemoryFreeClear(history.hist[i]);
H
hzcheng 已提交
940 941 942
    }
    i = (i + 1) % MAX_HISTORY_SIZE;
  }
943
  taosCloseFile(&pFile);
H
hzcheng 已提交
944 945
}

946
void shellPrintError(TAOS_RES *tres, int64_t st) {
S
TD-1793  
Shengliang Guan 已提交
947
  int64_t et = taosGetTimestampUs();
H
Haojun Liao 已提交
948
  atomic_store_ptr(&result, 0);
S
TD-1793  
Shengliang Guan 已提交
949
  fprintf(stderr, "\nDB error: %s (%.6fs)\n", taos_errstr(tres), (et - st) / 1E6);
H
Haojun Liao 已提交
950
  taos_free_result(tres);
H
hzcheng 已提交
951 952
}

S
#928  
slguan 已提交
953
int isCommentLine(char *line) {
H
hzcheng 已提交
954 955
  if (line == NULL) return 1;

956
  return shellRegexMatch(line, "^\\s*#.*", REG_EXTENDED);
H
hzcheng 已提交
957 958
}

959
void shellSourceFile(TAOS *con, char *fptr) {
H
hzcheng 已提交
960
  wordexp_t full_path;
961
  int       read_len = 0;
wafwerar's avatar
wafwerar 已提交
962
  char     *cmd = taosMemoryCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN + 1);
963
  size_t    cmd_len = 0;
S
Shengliang Guan 已提交
964
  char     *line = NULL;
H
hzcheng 已提交
965 966 967

  if (wordexp(fptr, &full_path, 0) != 0) {
    fprintf(stderr, "ERROR: illegal file name\n");
wafwerar's avatar
wafwerar 已提交
968
    taosMemoryFree(cmd);
H
hzcheng 已提交
969 970 971 972 973
    return;
  }

  char *fname = full_path.we_wordv[0];

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

978
    wordfree(&full_path);
wafwerar's avatar
wafwerar 已提交
979
    taosMemoryFree(cmd);
980 981
    return;
  }
H
Hui Li 已提交
982
  */
983

984
  // FILE *f = fopen(fname, "r");
985
  TdFilePtr pFile = taosOpenFile(fname, TD_FILE_READ | TD_FILE_STREAM);
986
  if (pFile == NULL) {
H
hzcheng 已提交
987 988
    fprintf(stderr, "ERROR: failed to open file %s\n", fname);
    wordfree(&full_path);
wafwerar's avatar
wafwerar 已提交
989
    taosMemoryFree(cmd);
H
hzcheng 已提交
990 991 992
    return;
  }

993
  while ((read_len = taosGetLineFile(pFile, &line)) != -1) {
H
Haojun Liao 已提交
994
    if (read_len >= TSDB_MAX_ALLOWED_SQL_LEN) continue;
H
hzcheng 已提交
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
    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 已提交
1011
    memset(cmd, 0, TSDB_MAX_ALLOWED_SQL_LEN);
H
hzcheng 已提交
1012 1013 1014
    cmd_len = 0;
  }

wafwerar's avatar
wafwerar 已提交
1015
  taosMemoryFree(cmd);
S
Shengliang Guan 已提交
1016
  if (line != NULL) taosMemoryFree(line);
H
hzcheng 已提交
1017
  wordfree(&full_path);
1018
  taosCloseFile(&pFile);
H
hzcheng 已提交
1019
}
S
slguan 已提交
1020 1021

void shellGetGrantInfo(void *con) {
S
slguan 已提交
1022
  return;
H
Hui Li 已提交
1023
#if 0
S
slguan 已提交
1024 1025
  char sql[] = "show grants";

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

1028
  int code = taos_errno(tres);
S
slguan 已提交
1029
  if (code != TSDB_CODE_SUCCESS) {
1030
    if (code == TSDB_CODE_COM_OPS_NOT_SUPPORT) {
S
slguan 已提交
1031
      fprintf(stdout, "Server is Community Edition, version is %s\n\n", taos_get_server_info(con));
S
slguan 已提交
1032 1033 1034
    } else {
      fprintf(stderr, "Failed to check Server Edition, Reason:%d:%s\n\n", taos_errno(con), taos_errstr(con));
    }
S
slguan 已提交
1035 1036 1037
    return;
  }

1038
  int num_fields = taos_field_count(tres);
S
slguan 已提交
1039 1040 1041 1042
  if (num_fields == 0) {
    fprintf(stderr, "\nInvalid grant information.\n");
    exit(0);
  } else {
1043
    if (tres == NULL) {
S
slguan 已提交
1044 1045 1046 1047
      fprintf(stderr, "\nGrant information is null.\n");
      exit(0);
    }

1048 1049
    TAOS_FIELD *fields = taos_fetch_fields(tres);
    TAOS_ROW row = taos_fetch_row(tres);
S
slguan 已提交
1050
    if (row == NULL) {
H
hjxilinx 已提交
1051
      fprintf(stderr, "\nFailed to get grant information from server. Abort.\n");
S
slguan 已提交
1052 1053 1054
      exit(0);
    }

S
slguan 已提交
1055
    char serverVersion[32] = {0};
S
slguan 已提交
1056 1057 1058
    char expiretime[32] = {0};
    char expired[32] = {0};

S
slguan 已提交
1059
    memcpy(serverVersion, row[0], fields[0].bytes);
S
slguan 已提交
1060 1061 1062 1063
    memcpy(expiretime, row[1], fields[1].bytes);
    memcpy(expired, row[2], fields[2].bytes);

    if (strcmp(expiretime, "unlimited") == 0) {
S
slguan 已提交
1064
      fprintf(stdout, "Server is Enterprise %s Edition, version is %s and will never expire.\n", serverVersion, taos_get_server_info(con));
S
slguan 已提交
1065
    } else {
S
slguan 已提交
1066
      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 已提交
1067 1068 1069
    }

    result = NULL;
1070
    taos_free_result(tres);
S
slguan 已提交
1071 1072 1073
  }

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