• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #4890

19 Dec 2025 11:37AM UTC coverage: 62.824% (-2.7%) from 65.487%
#4890

push

travis-ci

web-flow
feat: support TOTP code login and password expired tip (#33969)

22 of 26 new or added lines in 3 files covered. (84.62%)

1989 existing lines in 120 files now uncovered.

63068 of 100389 relevant lines covered (62.82%)

286770988.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

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

16
#define __USE_XOPEN
17

18
#include "shellAuto.h"
19
#include "shellInt.h"
20
#include "shellTire.h"
21
#include "tthread.h"
22

23
//
24
// ------------- define area  ---------------
25
//
26
#define UNION_ALL " union all "
27

28
// extern function
29
void  shellClearScreen(int32_t ecmd_pos, int32_t cursor_pos);
30
void  shellGetPrevCharSize(const char* str, int32_t pos, int32_t* size, int32_t* width);
31
void  shellShowOnScreen(SShellCmd* cmd);
32
void  shellInsertChar(SShellCmd* cmd, char* c, int size);
33
void  shellInsertStr(SShellCmd* cmd, char* str, int size);
34
bool  appendAfterSelect(TAOS* con, SShellCmd* cmd, char* p, int32_t len);
35
char* tireSearchWord(int type, char* pre);
36
bool  updateTireValue(int type, bool autoFill);
37

38
typedef struct SAutoPtr {
39
  STire* p;
40
  int    ref;
41
} SAutoPtr;
42

43
typedef struct SWord {
44
  int           type;  // word type , see WT_ define
45
  char*         word;
46
  int32_t       len;
47
  struct SWord* next;
48
  bool          free;  // if true need free
49
  bool          end;   // if true is last keyword
50
} SWord;
51

52
typedef struct {
53
  char*   source;
54
  int32_t source_len;  // valid data length in source
55
  int32_t count;
56
  SWord*  head;
57
  // matched information
58
  int32_t matchIndex;  // matched word index in words
59
  int32_t matchLen;    // matched length at matched word
60
} SWords;
61

62
SWords shellCommands[] = {
63
    {"alter database <db_name> <alter_db_options> <anyword> <alter_db_options> <anyword> <alter_db_options> <anyword> "
64
     "<alter_db_options> <anyword> <alter_db_options> <anyword> ;",
65
     0, 0, NULL},
66
    {"alter dnode <dnode_id> \"resetlog\";", 0, 0, NULL},
67
    {"alter dnode <dnode_id> \"debugFlag\" \"141\";", 0, 0, NULL},
68
    {"alter dnode <dnode_id> \"monitor\" \"0\";", 0, 0, NULL},
69
    {"alter dnode <dnode_id> \"monitor\" \"1\";", 0, 0, NULL},
70
    {"alter dnode <dnode_id> \"asynclog\" \"0\";", 0, 0, NULL},
71
    {"alter dnode <dnode_id> \"asynclog\" \"1\";", 0, 0, NULL},
72
    {"alter all dnodes \"resetlog\";", 0, 0, NULL},
73
    {"alter all dnodes \"debugFlag\" \"141\";", 0, 0, NULL},
74
    {"alter all dnodes \"monitor\" \"0\";", 0, 0, NULL},
75
    {"alter all dnodes \"monitor\" \"1\";", 0, 0, NULL},
76
    {"alter table <tb_name> <tb_actions> <anyword> ;", 0, 0, NULL},
77
    {"alter local \"resetlog\";", 0, 0, NULL},
78
    {"alter local \"DebugFlag\" \"143\";", 0, 0, NULL},
79
    {"alter local \"cDebugFlag\" \"143\";", 0, 0, NULL},
80
    {"alter local \"uDebugFlag\" \"143\";", 0, 0, NULL},
81
    {"alter local \"rpcDebugFlag\" \"143\";", 0, 0, NULL},
82
    {"alter local \"tmrDebugFlag\" \"143\";", 0, 0, NULL},
83
    {"alter local \"asynclog\" \"0\";", 0, 0, NULL},
84
    {"alter local \"asynclog\" \"1\";", 0, 0, NULL},
85
    {"alter topic", 0, 0, NULL},
86
#ifdef TD_ENTERPRISE
87
    {"balance vgroup ;", 0, 0, NULL},
88
    {"balance vgroup leader on <vgroup_id>", 0, 0, NULL},
89
    {"alter user <user_name> <alter_uopt> <anyword> <alter_uopt> <anyword> <alter_uopt> <anyword> <alter_uopt> <anyword> <alter_uopt> <anyword> <alter_uopt> <anyword>", 0, 0, NULL},
90
#else
91
   {"alter user <user_name> <user_actions> <anyword> ;", 0, 0, NULL},
92
#endif
93

94
    // 20
95
    {"create table <anyword> using <stb_name> tags(", 0, 0, NULL},
96
    {"create vtable <anyword> using <stb_name> tags(", 0, 0, NULL},
97
    {"create database <anyword> <db_options> <anyword> <db_options> <anyword> <db_options> <anyword> <db_options> "
98
     "<anyword> <db_options> <anyword> <db_options> <anyword> <db_options> <anyword> <db_options> <anyword> "
99
     "<db_options> <anyword> <db_options> <anyword> ;", 0, 0, NULL},
100
    {"create dnode <anyword>", 0, 0, NULL},
101
    {"create index <anyword> on <stb_name> ()", 0, 0, NULL},
102
    {"create mnode on dnode <dnode_id>;", 0, 0, NULL},
103
    {"create qnode on dnode <dnode_id>;", 0, 0, NULL},
104
    {"create bnode on dnode <dnode_id>;", 0, 0, NULL},
105
    {"create anode <anyword>", 0, 0, NULL},
106
    {"create stream <anyword> into <anyword> as select", 0, 0, NULL},  // 26 append sub sql
107
    {"create topic <anyword> as select", 0, 0, NULL},                  // 27 append sub sql
108
    {"create rsma <anyword> on <all_table> function interval <anyword>", 0, 0, NULL},
109
    {"create tsma <anyword> on <all_table> function", 0, 0, NULL},
110
    {"create recursive tsma <anyword> on <tsma_name> interval(", 0, 0, NULL},
111
    {"create function <anyword> as <anyword> outputtype <data_types> language <udf_language>;", 0, 0, NULL},
112
    {"create or replace <anyword> as <anyword> outputtype <data_types> language <udf_language>;", 0, 0, NULL},
113
    {"create aggregate function  <anyword> as <anyword> outputtype <data_types> bufsize <anyword> language <udf_language>;", 0, 0, NULL},
114
    {"create or replace aggregate function  <anyword> as <anyword> outputtype <data_types> bufsize <anyword> language <udf_language>;", 0, 0, NULL},
115
#ifdef TD_ENTERPRISE    
116
    {"create view <anyword> as select", 0, 0, NULL},
117
    {"compact database <db_name>", 0, 0, NULL},
118
    {"compact vgroups in( <anyword>", 0, 0, NULL},
119
    {"create mount <mount_name> on dnode <dnode_id> from <path>;", 0, 0, NULL},
120
    {"create user <anyword> <create_uopt> <anyword> <create_uopt> <anyword> <create_uopt> <anyword> <create_uopt> <anyword> <create_uopt> <anyword> <create_uopt> <anyword>", 0, 0, NULL},    
121
#else
122
    {"create user <anyword> pass <anyword> createdb 1;", 0, 0, NULL},
123
    {"create user <anyword> pass <anyword> createdb 0;", 0, 0, NULL},
124
    {"create user <anyword> pass <anyword> sysinfo 1;", 0, 0, NULL},
125
    {"create user <anyword> pass <anyword> sysinfo 0;", 0, 0, NULL},
126
#endif
127
    {"scan database <db_name>", 0, 0, NULL},
128
    {"desc <all_table>;", 0, 0, NULL},
129
    {"describe <all_table>;", 0, 0, NULL},
130
    {"delete from <all_table> where ", 0, 0, NULL},
131
    {"drop database <db_name>;", 0, 0, NULL},
132
    {"drop index <anyword>;", 0, 0, NULL},
133
    {"drop table <all_table>;", 0, 0, NULL},
134
    {"drop dnode <dnode_id>;", 0, 0, NULL},
135
    {"drop mnode on dnode <dnode_id>;", 0, 0, NULL},
136
    {"drop qnode on dnode <dnode_id>;", 0, 0, NULL},
137
    {"drop bnode on dnode <dnode_id>;", 0, 0, NULL},
138
    {"drop anode <anode_id>;", 0, 0, NULL},
139
    {"drop user <user_name>;", 0, 0, NULL},
140
    // 40
141
    {"drop function <udf_name>;", 0, 0, NULL},
142
    {"drop consumer group <anyword> on ", 0, 0, NULL},
143
    {"drop topic <topic_name>;", 0, 0, NULL},
144
    {"drop stream <stream_name>;", 0, 0, NULL},
145
    {"drop tsma <tsma_name>;", 0, 0, NULL},
146
    {"drop rsma <rsma_name>;", 0, 0, NULL},
147
    {"explain select ", 0, 0, NULL},  // 44 append sub sql
148
    {"flush database <db_name>;", 0, 0, NULL},
149
    {"help;", 0, 0, NULL},
150
    {"grant all on <anyword> to <user_name>;", 0, 0, NULL},
151
    {"grant read on <anyword> to <user_name>;", 0, 0, NULL},
152
    {"grant write on <anyword> to <user_name>;", 0, 0, NULL},
153
    {"kill connection <anyword>;", 0, 0, NULL},
154
    {"kill retention ", 0, 0, NULL},
155
    {"kill query ", 0, 0, NULL},
156
    {"kill transaction ", 0, 0, NULL},
157
#ifdef TD_ENTERPRISE
158
    {"merge vgroup <vgroup_id> <vgroup_id>;", 0, 0, NULL},
159
    {"drop mount <mount_name>;", 0, 0, NULL},
160
#endif
161
    {"pause stream <stream_name>;", 0, 0, NULL},
162
#ifdef TD_ENTERPRISE
163
    {"redistribute vgroup <vgroup_id> dnode <dnode_id>;", 0, 0, NULL},
164
#endif
165
    {"resume stream <stream_name>;", 0, 0, NULL},
166
    {"reset query cache;", 0, 0, NULL},
167
    {"restore dnode <dnode_id>;", 0, 0, NULL},
168
    {"restore vnode on dnode <dnode_id>;", 0, 0, NULL},
169
    {"restore mnode on dnode <dnode_id>;", 0, 0, NULL},
170
    {"restore qnode on dnode <dnode_id>;", 0, 0, NULL},
171
    {"revoke all on <anyword> from <user_name>;", 0, 0, NULL},
172
    {"revoke read on <anyword> from <user_name>;", 0, 0, NULL},
173
    {"revoke write on <anyword> from <user_name>;", 0, 0, NULL},
174
    {"rollup database <db_name>;", 0, 0, NULL},
175
    {"rollup database <db_name> vgroups in( <anyword>", 0, 0, NULL},
176
    {"select * from <all_table>", 0, 0, NULL},
177
    {"select client_version();", 0, 0, NULL},
178
    // 60
179
    {"select current_user();", 0, 0, NULL},
180
    {"select database();", 0, 0, NULL},
181
    {"select server_version();", 0, 0, NULL},
182
    {"select server_status();", 0, 0, NULL},
183
    {"select now();", 0, 0, NULL},
184
    {"select today();", 0, 0, NULL},
185
    {"select timezone();", 0, 0, NULL},
186
    {"set max_binary_display_width ", 0, 0, NULL},
187
    {"show apps;", 0, 0, NULL},
188
    {"show alive;", 0, 0, NULL},
189
    {"show anodes;", 0, 0, NULL},
190
    {"show anodes full;", 0, 0, NULL},
191
    {"show create database <db_name> \\G;", 0, 0, NULL},
192
    {"show create stable <stb_name> \\G;", 0, 0, NULL},
193
    {"show create table <tb_name> \\G;", 0, 0, NULL},
194
#ifdef TD_ENTERPRISE    
195
    {"show create view <all_table> \\G;", 0, 0, NULL},
196
    {"show compact", 0, 0, NULL},
197
    {"show compacts;", 0, 0, NULL},
198
#endif
199
    {"show connections;", 0, 0, NULL},
200
    {"show cluster;", 0, 0, NULL},
201
    {"show cluster alive;", 0, 0, NULL},
202
    {"show cluster machines;", 0, 0, NULL},
203
    {"show databases;", 0, 0, NULL},
204
    {"show dnodes;", 0, 0, NULL},
205
    {"show dnode <dnode_id> variables;", 0, 0, NULL},
206
    {"show disk_info;", 0, 0, NULL},
207
    {"show functions;", 0, 0, NULL},
208
    {"show licences;", 0, 0, NULL},
209
    {"show mnodes;", 0, 0, NULL},
210
    {"show queries;", 0, 0, NULL},
211
    // 80
212
    {"show query <anyword> ;", 0, 0, NULL},
213
    {"show qnodes;", 0, 0, NULL},
214
    {"show bnodes;", 0, 0, NULL},
215
    {"show retentions;", 0, 0, NULL},
216
    {"show retention <retention_id>;", 0, 0, NULL},
217
    {"show stables;", 0, 0, NULL},
218
    {"show stables like ", 0, 0, NULL},
219
    {"show streams;", 0, 0, NULL},
220
    {"show scores;", 0, 0, NULL},
221
    {"show snodes;", 0, 0, NULL},
222
    {"show subscriptions;", 0, 0, NULL},
223
    {"show scans;", 0, 0, NULL},
224
    {"show scan <scan_id>;", 0, 0, NULL},
225
    {"show tables;", 0, 0, NULL},
226
    {"show tables like", 0, 0, NULL},
227
    {"show table distributed <all_table>;", 0, 0, NULL},
228
    {"show tags from <tb_name>;", 0, 0, NULL},
229
    {"show table tags from <all_table>;", 0, 0, NULL},
230
    {"show topics;", 0, 0, NULL},
231
    {"show transactions;", 0, 0, NULL},
232
    {"show tsmas;", 0, 0, NULL},
233
    {"show rsmas;", 0, 0, NULL},
234
    {"show users;", 0, 0, NULL},
235
    {"show variables;", 0, 0, NULL},
236
    {"show local variables;", 0, 0, NULL},
237
    {"show vnodes;", 0, 0, NULL},
238
    {"show vnodes on dnode <dnode_id>;", 0, 0, NULL},
239
    {"show vgroups;", 0, 0, NULL},
240
    {"show vtables;", 0, 0, NULL},
241
    {"show consumers;", 0, 0, NULL},
242
    {"show grants;", 0, 0, NULL},
243
    {"show grants full;", 0, 0, NULL},
244
    {"show grants logs;", 0, 0, NULL},
245
#ifdef TD_ENTERPRISE
246
    {"show views;", 0, 0, NULL},
247
    {"show arbgroups;", 0, 0, NULL},
248
    {"split vgroup <vgroup_id>;", 0, 0, NULL},
249
    {"ssmigrate database <db_name>;", 0, 0, NULL},
250
    {"show mounts;", 0, 0, NULL},
251
    {"show ssmigrates;", 0, 0, NULL},
252
#endif
253
    {"insert into <tb_name> values(", 0, 0, NULL},
254
    {"insert into <tb_name> using <stb_name> tags(", 0, 0, NULL},
255
    {"insert into <tb_name> using <stb_name> <anyword> values(", 0, 0, NULL},
256
    {"insert into <tb_name> file ", 0, 0, NULL},
257
    {"trim database <db_name>;", 0, 0, NULL},
258
    {"use <db_name>;", 0, 0, NULL},
259
    {"update all anodes;", 0, 0, NULL},
260
    {"update anode <anyword>;", 0, 0, NULL},
261
    {"quit", 0, 0, NULL}};
262

263
// where keyword
264
char* keywords[] = {
265
    "where ",       "and ",      "asc ",      "desc ",    "from ",         "fill(",     "limit ",
266
    "interval(",    "order by ", "order by ", "offset ",  "or ",           "group by ", "now()",
267
    "session(",     "sliding ",  "slimit ",   "soffset ", "state_window(", "today() ",  "union all select ",
268
    "partition by ", "match",    "nmatch ",    "between ",  "like ",           "is null ",   "is not null ",
269
    "event_window ",  "count_window(", "anomaly_window("
270
};
271

272
char* functions[] = {
273
    "count(",         "sum(",
274
    "avg(",           "last(",
275
    "last_row(",      "top(",
276
    "interp(",        "max(",
277
    "min(",           "now()",
278
    "today()",        "percentile(",
279
    "tail(",          "pow(",
280
    "abs(",           "atan(",
281
    "acos(",          "asin(",
282
    "apercentile(",   "bottom(",
283
    "cast(",          "ceil(",
284
    "char_length(",   "cos(",
285
    "concat(",        "concat_ws(",
286
    "csum(",          "diff(",
287
    "derivative(",    "elapsed(",
288
    "first(",         "floor(",
289
    "hyperloglog(",   "histogram(",
290
    "irate(",         "leastsquares(",
291
    "length(",        "log(",
292
    "lower(",         "ltrim(",
293
    "mavg(",          "mode(",
294
    "tan(",           "round(",
295
    "rtrim(",         "sample(",
296
    "sin(",           "spread(",
297
    "substr(",        "statecount(",
298
    "stateduration(", "stddev(",
299
    "sqrt(",          "timediff(",
300
    "timezone(",      "timetruncate(",
301
    "twa(",           "to_unixtimestamp(",
302
    "unique(",        "upper(",
303
    "pi(",            "round(",
304
    "truncate(",      "exp(",
305
    "ln(",            "mod(",
306
    "rand(",          "sign(",
307
    "degrees(",       "radians(",
308
    "greatest(",      "least(",
309
    "char_length(",   "char(",
310
    "ascii(",         "position(",
311
    "trim(",           "replace(",
312
    "repeat(",         "substring(",
313
    "substring_index(","timediff(",
314
    "week(",           "weekday(",
315
    "weekofyear(",     "dayofweek(",
316
    "stddev_pop(",     "var_pop(",
317
    "forecast(",       "imputation(",
318
    "std(",            "variance(",
319
    "stddev_samp(",    "var_samp(",
320
    "group_concat(",   "if(",
321
    "ifnull(",         "nvl(",
322
    "nvl2(",           "isnull(",
323
    "isnotnull(",      "coalesce(",
324
    "date(",           "corr(",
325
    "cols(",           "find_in_set(",
326
    "like_in_set(",    "regexp_in_set(",
327
    "generate_totp_secret(",
328
    "case ",           "when "
329
};
330

331
char* tb_actions[] = {
332
    "add column ", "modify column ", "drop column ", "rename column ", "add tag ",
333
    "modify tag ", "drop tag ",      "rename tag ",  "set tag ",
334
};
335

336
char* user_actions[] = {"pass ", "enable ", "sysinfo ", "createdb "};
337

338
char* tb_options[] = {"comment ", "watermark ", "max_delay ", "ttl ", "rollup(", "sma(", "virtual 1"};
339

340
char* db_options[] = {"keep ",
341
                      "replica ",
342
                      "precision ",
343
                      "strict ",
344
                      "buffer ",
345
                      "cachemodel ",
346
                      "cachesize ",
347
                      "comp ",
348
                      "duration ",
349
                      "dnodes ",
350
                      "wal_fsync_period ",
351
                      "maxrows ",
352
                      "minrows ",
353
                      "pages ",
354
                      "pagesize ",
355
                      "retentions ",
356
                      "wal_level ",
357
                      "vgroups ",
358
                      "single_stable ",
359
                      "ss_chunksize ",
360
                      "ss_keeplocal ",
361
                      "ss_compact ",
362
                      "wal_retention_period ",
363
                      "wal_roll_period ",
364
                      "wal_retention_size ",
365
#ifdef TD_ENTERPRISE                      
366
                      "encrypt_algorithm ",
367
                      "compact_interval ",
368
                      "compact_time_range ",
369
                      "compact_time_offset ",
370
#endif
371
                      "keep_time_offset ",
372
                      "wal_segment_size "
373
};
374

375
char* alter_db_options[] = {"cachemodel ", "replica ", "keep ", "stt_trigger ",
376
                            "wal_retention_period ", "wal_retention_size ", "cachesize ", 
377
                                              "ss_keeplocal ", "ss_compact ",
378
                            "wal_fsync_period ", "buffer ", "pages " ,"wal_level "};
379

380
char* data_types[] = {"timestamp",    "int",
381
                      "int unsigned", "varchar(16)",
382
                      "float",        "double",
383
                      "binary(16)",   "nchar(16)",
384
                      "bigint",       "bigint unsigned",
385
                      "smallint",     "smallint unsigned",
386
                      "tinyint",      "tinyint unsigned",
387
                      "geometry(64)", "varbinary(16)",
388
                      "decimal(10,2)", "blob",
389
                      "bool",         "json"};
390

391
char* key_tags[] = {"tags("};
392

393
char* key_select[] = {"select "};
394

395
char* key_systable[] = {
396
    "ins_dnodes",        "ins_mnodes",     "ins_modules",      "ins_qnodes",  "ins_snodes",          "ins_cluster",
397
    "ins_databases",     "ins_functions",  "ins_indexes",      "ins_stables", "ins_tables",          "ins_tags",
398
    "ins_users",         "ins_grants",     "ins_vgroups",      "ins_configs", "ins_dnode_variables", "ins_topics",
399
    "ins_subscriptions", "ins_streams",    "ins_stream_tasks", "ins_vnodes",  "ins_user_privileges", "ins_filesets",
400
    "ins_bnodes",        "ins_disk_usage", "ins_retentions",
401
    "perf_connections", "perf_queries",    "perf_consumers", "perf_trans",       "perf_apps"};
402

403
char* udf_language[] = {"\'Python\'", "\'C\'"};
404

405
char* field_options[] = {
406
    "encode ", "compress ", "level ", 
407
    "\'lz4\' ", "\'zlib\' ", "\'zstd\' ", "\'xz\' ", "\'tsz\' ", "\'disabled\' ", // compress
408
    "\'simple8b\' ", "\'delta-i\' ", "\'delta-d\' ", "\'bit-packing\' ", "\'bss\' ",
409
    "\'high\' ", "\'medium\' ", "\'low\' ",
410
    "comment ",
411
    "primary key "
412
};
413

414
// create user options
415
char * create_uopt[] = {
416
    "pass ",
417
    "totpseed ",
418
    "account ",
419
    "sysinfo ",
420
    "createdb ",
421
    "changepass ",
422
    "session_per_user ",
423
    "connect_idle_time ",
424
    "connect_time ",
425
    "call_per_session ",
426
    "vnode_per_call ",
427
    "failed_login_attempts ",
428
    "password_life_time ",
429
    "password_reuse_time ",
430
    "password_reuse_max ",
431
    "password_lock_time ",
432
    "password_grace_time ",
433
    "inactive_account_time ",
434
    "allow_token_num ",
435
    "host ",
436
    "not_allow_host ",
437
    "allow_datetime ",
438
    "not_allow_datetime "
439
};
440

441
// alter user options
442
char * alter_uopt[] = {
443
    "pass ",
444
    "totpseed ",
445
    "account ",
446
    "sysinfo ",
447
    "createdb ",
448
    "changepass ",
449
    "session_per_user ",
450
    "connect_idle_time ",
451
    "connect_time ",
452
    "call_per_session ",
453
    "vnode_per_call ",
454
    "failed_login_attempts ",
455
    "password_life_time ",
456
    "password_reuse_time ",
457
    "password_reuse_max ",
458
    "password_lock_time ",
459
    "password_grace_time ",
460
    "inactive_account_time ",
461
    "allow_token_num ",
462
    "enable ",
463
    "add host ",
464
    "add not_allow_host ",
465
    "drop host ",
466
    "drop not_allow_host ",
467
    "add allow_datetime ",
468
    "add not_allow_datetime ",
469
    "drop allow_datetime ",
470
    "drop not_allow_datetime "
471
};
472

473
// global keys can tips on anywhere
474
char* global_keys[] = {
475
    "tbname",         
476
    "now",
477
    "vgroups",
478
    "if exists",
479
    "if not exists",
480
    "distinct",
481
    "like",
482
    "_wstart",      
483
    "_wend",
484
    "_wduration",
485
    "_qstart",          
486
    "_qend",
487
    "_qduration",
488
    "_qtag",
489
    "_isfilled"
490
  };
491

492
//
493
//  ------- global variant define ---------
494
//
495
int32_t firstMatchIndex = -1;  // first match shellCommands index
496
int32_t lastMatchIndex = -1;   // last match shellCommands index
497
int32_t curMatchIndex = -1;    // current match shellCommands index
498
int32_t lastWordBytes = -1;    // printShow last word length
499
bool    waitAutoFill = false;
500

501
//
502
//   ----------- global var array define -----------
503
//
504
#define WT_VAR_DBNAME         0
505
#define WT_VAR_STABLE         1
506
#define WT_VAR_TABLE          2
507
#define WT_VAR_DNODEID        3
508
#define WT_VAR_USERNAME       4
509
#define WT_VAR_TOPIC          5
510
#define WT_VAR_STREAM         6
511
#define WT_VAR_UDFNAME        7
512
#define WT_VAR_VGROUPID       8
513
#define WT_VAR_TSMA           9
514
#define WT_VAR_RSMA           10
515
#define WT_VAR_ANODE          11
516

517
#define WT_FROM_DB_MAX        11  // max get content from db
518
#define WT_FROM_DB_CNT (WT_FROM_DB_MAX + 1)
519

520
#define WT_VAR_ALLTABLE       12
521
#define WT_VAR_FUNC           13
522
#define WT_VAR_KEYWORD        14
523
#define WT_VAR_TBACTION       15
524
#define WT_VAR_DBOPTION       16
525
#define WT_VAR_ALTER_DBOPTION 17
526
#define WT_VAR_DATATYPE       18
527
#define WT_VAR_KEYTAGS        19
528
#define WT_VAR_ANYWORD        20
529
#define WT_VAR_TBOPTION       21
530
#define WT_VAR_USERACTION     22
531
#define WT_VAR_KEYSELECT      23
532
#define WT_VAR_SYSTABLE       24
533
#define WT_VAR_LANGUAGE       25
534
#define WT_VAR_GLOBALKEYS     26
535
#define WT_VAR_FIELD_OPTIONS  27
536
#define WT_VAR_CREATE_USER_OPT 28
537
#define WT_VAR_ALTER_USER_OPT 29
538

539
#define WT_VAR_CNT 30
540

541

542
#define WT_TEXT 0xFF
543

544
char dbName[256] = "";  // save use database name;
545
// tire array
546
STire*        tires[WT_VAR_CNT];
547
TdThreadMutex tiresMutex;
548
// save thread handle obtain var name from db server
549
TdThread* threads[WT_FROM_DB_CNT];
550
// obtain var name  with sql from server
551
char varTypes[WT_VAR_CNT][64] = {
552
    // get from db
553
    "<db_name>",    "<stb_name>",  "<tb_name>",  "<dnode_id>",  "<user_name>",    "<topic_name>", "<stream_name>",
554
    "<udf_name>",   "<vgroup_id>", "<tsma_name>", "<rsma_name>", "<anode_id>", 
555
    // get from code
556
    "<all_table>",  "<function>",  "<keyword>",  "<tb_actions>",   "<db_options>", "<alter_db_options>",
557
    "<data_types>", "<key_tags>",  "<anyword>",  "<tb_options>", "<user_actions>", "<key_select>", "<sys_table>", 
558
    "<udf_language>", "<global_keys>", "<field_options>", "<create_uopt>", "<alter_uopt>"};
559

560
char varSqls[WT_FROM_DB_CNT][64] = {"show databases;", "show stables;", "show tables;", "show dnodes;",
561
                                    "show users;",     "show topics;",  "show streams;", "show functions;", 
562
                                    "show vgroups;",   "show tsmas;",   "show rsmas;",  "show anodes;"};
563

564
// var words current cursor, if user press any one key except tab, cursorVar can be reset to -1
565
int  cursorVar = -1;
566
bool varMode = false;  // enter var names list mode
567

568
TAOS*      varCon = NULL;
569
SShellCmd* varCmd = NULL;
570
bool       varRunOnce = false;
571
SMatch*    lastMatch = NULL;  // save last match result
572
int        cntDel = 0;        // delete byte count after next press tab
573

574
// show auto tab introduction
575
void printfIntroduction(EVersionType type) {
637✔
576
  printf("  *********************************  Tab Completion  *************************************\n");
637✔
577
  char secondLine[160] = "\0";
637✔
578
  sprintf(secondLine, "  *   The %s CLI supports tab completion for a variety of items, ", shell.info.cusName);
637✔
579
  printf("%s", secondLine);
637✔
580
  int secondLineLen = strlen(secondLine);
637✔
581
  while (89 - (secondLineLen++) > 0) {
8,918✔
582
    printf(" ");
8,281✔
583
  }
584
  printf("*\n");
637✔
585
  printf("  *   including database names, table names, function names and keywords.                *\n");
637✔
586
  printf("  *   The full list of shortcut keys is as follows:                                      *\n");
637✔
587
  printf("  *    [ TAB ]        ......  complete the current word                                  *\n");
637✔
588
  printf("  *                   ......  if used on a blank line, display all supported commands    *\n");
637✔
589
  printf("  *    [ Ctrl + A ]   ......  move cursor to the st[A]rt of the line                     *\n");
637✔
590
  printf("  *    [ Ctrl + E ]   ......  move cursor to the [E]nd of the line                       *\n");
637✔
591
  printf("  *    [ Ctrl + W ]   ......  move cursor to the middle of the line                      *\n");
637✔
592
  printf("  *    [ Ctrl + L ]   ......  clear the entire screen                                    *\n");
637✔
593
  printf("  *    [ Ctrl + K ]   ......  clear the screen after the cursor                          *\n");
637✔
594
  printf("  *    [ Ctrl + U ]   ......  clear the screen before the cursor                         *\n");
637✔
595
  if (type == TSDB_VERSION_OSS) {
637✔
596
    printf("  * ------------------------------------------------------------------------------------ *\n");
×
597
    printf("  *   You are using TDengine OSS. To experience advanced features, like backup/restore,  *\n");
×
598
    printf("  *   privilege control and more, or receive 7x24 technical support, try TDengine        *\n");
×
599
    printf("  *   Enterprise or TDengine Cloud. Learn more at https://tdengine.com                   *\n");
×
600
  }
601
  printf("  ****************************************************************************************\n\n");
637✔
602
}
637✔
603

604
// show enterprise AD
605
void showAD(bool end) {
×
606
  printf("  You are using TDengine OSS. To experience advanced features, like backup/restore,  \n");
×
607
  printf("  privilege control and more, or receive 7x24 technical support, try TDengine Enterprise \n");
×
608
  printf("  or TDengine Cloud. Learn more at https://tdengine.com   \n");
×
609
  printf("  \n");
×
610
}
×
611

612
void showHelp() {
66✔
613
  printf("\nThe %s CLI supports the following commands:", shell.info.cusName);
66✔
614
  printf(
66✔
615
      "\n\
616
  ----- A ----- \n\
617
    alter database <db_name> <db_options> \n\
618
    alter dnode <dnode_id> 'resetlog';\n\
619
    alter dnode <dnode_id> 'monitor' '0';\n\
620
    alter dnode <dnode_id> 'monitor' \"1\";\n\
621
    alter dnode <dnode_id> \"debugflag\" \"143\";\n\
622
    alter dnode <dnode_id> 'asynclog' '0';\n\
623
    alter dnode <dnode_id> 'asynclog' \"1\";\n\
624
    alter all dnodes \"monitor\" \"0\";\n\
625
    alter all dnodes \"monitor\" \"1\";\n\
626
    alter all dnodes \"resetlog\";\n\
627
    alter all dnodes \"debugFlag\" \n\
628
    alter all dnodes \"asynclog\" \"0\";\n\
629
    alter all dnodes \"asynclog\" \"1\";\n\
630
    alter table <tb_name> <tb_actions> ;\n\
631
    alter local \"resetlog\";\n\
632
    alter local \"DebugFlag\" \"143\";\n\
633
    alter local \"asynclog\" \"0\";\n\
634
    alter local \"asynclog\" \"1\";\n\
635
    alter topic\n\
636
    alter user <user_name> <user_actions> ...\n\
637
  ----- C ----- \n\
638
    create table <tb_name> using <stb_name> tags ...\n\
639
    create vtable <tb_name> using <stb_name> tags ...\n\
640
    create database <db_name> <db_options>  ...\n\
641
    create dnode \"fqdn:port\" ...\n\
642
    create index <index_name> on <stb_name> (tag_column_name);\n\
643
    create mnode on dnode <dnode_id> ;\n\
644
    create qnode on dnode <dnode_id> ;\n\
645
    create bnode on dnode <dnode_id> ;\n\
646
    create anode \"node_url\" ;\n\
647
    create stream <stream_name> into <stb_name> as select ...\n\
648
    create topic <topic_name> as select ...\n\
649
    create function <udf_name> as <file_name> outputtype <data_types> language \'C\' | \'Python\' ;\n\
650
    create aggregate function  <udf_name> as <file_name> outputtype <data_types> bufsize <bufsize_bytes> language \'C\' | \'Python\';\n\
651
    create user <user_name> pass <password> ...\n\
652
  ----- D ----- \n\
653
    describe <all_table>\n\
654
    delete from <all_table> where ...\n\
655
    drop database <db_name>;\n\
656
    drop table <all_table>;\n\
657
    drop dnode <dnode_id>;\n\
658
    drop mnode on dnode <dnode_id> ;\n\
659
    drop qnode on dnode <dnode_id> ;\n\
660
    drop bnode on dnode <dnode_id> ;\n\
661
    drop anode <anode_id> ;\n\
662
    drop user <user_name> ;\n\
663
    drop function <udf_name>;\n\
664
    drop consumer group ... \n\
665
    drop topic <topic_name> ;\n\
666
    drop stream <stream_name> ;\n\
667
    drop index <index_name>;\n\
668
    drop tsma <tsma_name> ;\n\
669
    drop rsma <rsma_name> ;\n\
670
  ----- E ----- \n\
671
    explain select clause ...\n\
672
  ----- F ----- \n\
673
    flush database <db_name>;\n\
674
  ----- H ----- \n\
675
    help;\n\
676
  ----- I ----- \n\
677
    insert into <tb_name> values(...) ;\n\
678
    insert into <tb_name> using <stb_name> tags(...) values(...) ;\n\
679
  ----- G ----- \n\
680
    grant all   on <priv_level> to <user_name> ;\n\
681
    grant read  on <priv_level> to <user_name> ;\n\
682
    grant write on <priv_level> to <user_name> ;\n\
683
  ----- K ----- \n\
684
    kill connection <connection_id>; \n\
685
    kill query <query_id>; \n\
686
    kill retention <retention_id>; \n\
687
    kill transaction <transaction_id>;\n\
688
  ----- P ----- \n\
689
    pause stream <stream_name>;\n\
690
  ----- R ----- \n\
691
    resume stream <stream_name>;\n\
692
    reset query cache;\n\
693
    restore dnode <dnode_id> ;\n\
694
    restore vnode on dnode <dnode_id> ;\n\
695
    restore mnode on dnode <dnode_id> ;\n\
696
    restore qnode on dnode <dnode_id> ;\n\
697
    revoke all   on <priv_level> from <user_name> ;\n\
698
    revoke read  on <priv_level> from <user_name> ;\n\
699
    revoke write on <priv_level> from <user_name> ;\n\
700
  ----- S ----- \n\
701
    select * from <all_table> where ... \n\
702
    select client_version();\n\
703
    select current_user();\n\
704
    select database();\n\
705
    select server_version();\n\
706
    select server_status();\n\
707
    select now();\n\
708
    select today();\n\
709
    select timezone();\n\
710
    set max_binary_display_width ...\n\
711
    show apps;\n\
712
    show alive;\n\
713
    show anodes;\n\
714
    show anodes full;\n\
715
    show create database <db_name>;\n\
716
    show create stable <stb_name>;\n\
717
    show create table <tb_name>;\n\
718
    show connections;\n\
719
    show cluster;\n\
720
    show cluster alive;\n\
721
    show cluster machines;\n\
722
    show databases;\n\
723
    show dnodes;\n\
724
    show dnode <dnode_id> variables;\n\
725
    show disk_info;\n\
726
    show functions;\n\
727
    show licences;\n\
728
    show mnodes;\n\
729
    show queries;\n\
730
    show query <query_id> ;\n\
731
    show qnodes;\n\
732
    show bnodes;\n\
733
    show retentions;\n\
734
    show retention <retention_id>;\n\
735
    show scans;\n\
736
    show scan <scan_id>;\n\
737
    show snodes;\n\
738
    show stables;\n\
739
    show stables like \n\
740
    show streams;\n\
741
    show scores;\n\
742
    show subscriptions;\n\
743
    show tables;\n\
744
    show tables like\n\
745
    show table distributed <all_table>;\n\
746
    show tags from <tb_name>\n\
747
    show tags from <db_name>\n\
748
    show table tags from <all_table>\n\
749
    show topics;\n\
750
    show transactions;\n\
751
    show tsmas;\n\
752
    show rsmas;\n\
753
    show users;\n\
754
    show variables;\n\
755
    show local variables;\n\
756
    show vnodes;\n\
757
    show vnodes on dnode <dnode_id>;\n\
758
    show vgroups;\n\
759
    show vtables;\n\
760
    show consumers;\n\
761
    show grants;\n\
762
    show grants full;\n\
763
    show grants logs;\n\
764
  ----- T ----- \n\
765
    trim database <db_name>;\n\
766
  ----- U ----- \n\
767
    update all anodes;\n\
768
    update anode <anode_id>;\n\
769
    use <db_name>;");
770

771
#ifdef TD_ENTERPRISE
772
  printf(
66✔
773
      "\n\n\
774
  ----- special commands on enterpise version ----- \n\
775
    balance vgroup ;\n\
776
    balance vgroup leader on <vgroup_id> \n\
777
    compact database <db_name>; \n\
778
    compact vgroups in (vgroupId,vgroupId, ...); \n\
779
    create view <view_name> as select ...\n\
780
    create mount <mount_name> on dnode <dnode_id> from <path>;\n\
781
    drop mount <mount_name>;\n\
782
    redistribute vgroup <vgroup_id> dnode <dnode_id> ;\n\
783
    split vgroup <vgroup_id>;\n\
784
    ssmigrate database <db_name>;\n\
785
    show compacts;\n\
786
    show compact \n\
787
    show ssmigrates;\n\
788
    show arbgroups;\n\
789
    show mounts;\n\
790
    show views;\n\
791
    show create view <all_table>;");
792
#endif
793

794
  printf("\n\n");
66✔
795
  // define in getDuration() function
796
  printf(
66✔
797
      "\
798
  Timestamp expression Format:\n\
799
    b - nanosecond \n\
800
    u - microsecond \n\
801
    a - millisecond \n\
802
    s - second \n\
803
    m - minute \n\
804
    h - hour \n\
805
    d - day \n\
806
    w - week \n\
807
    now - current time \n\
808
  Example : \n\
809
    select * from t1 where ts > now - 2w + 3d and ts <= now - 1w -2h ;\n");
810
  printf(ERROR_CODE_DETAIL);
66✔
811
  printf("\n");
66✔
812
}
66✔
813

814
//
815
//  -------------------  parse words --------------------------
816
//
817

818
#define SHELL_COMMAND_COUNT() (sizeof(shellCommands) / sizeof(SWords))
819

820
// get at
821
SWord* atWord(SWords* command, int32_t index) {
×
822
  SWord* word = command->head;
×
823
  for (int32_t i = 0; i < index; i++) {
×
824
    if (word == NULL) return NULL;
×
825
    word = word->next;
×
826
  }
827

828
  return word;
×
829
}
830

831
#define MATCH_WORD(x) atWord(x, x->matchIndex)
832

833
int wordType(const char* p, int32_t len) {
491,242,188✔
834
  for (int i = 0; i < WT_VAR_CNT; i++) {
2,147,483,647✔
835
    if (strncmp(p, varTypes[i], len) == 0) return i;
2,147,483,647✔
836
  }
837
  return WT_TEXT;
357,201,178✔
838
}
839

840
// add word
841
SWord* addWord(const char* p, int32_t len, bool pattern) {
497,763,102✔
842
  SWord* word = (SWord*)taosMemoryMalloc(sizeof(SWord));
497,763,102✔
843
  memset(word, 0, sizeof(SWord));
497,763,102✔
844
  word->word = (char*)p;
497,763,102✔
845
  word->len = len;
497,763,102✔
846

847
  // check format
848
  if (pattern && len > 0) {
497,763,102✔
849
    if (p[len - 1] == ';') {
491,242,188✔
850
      word->type = wordType(p, len - 1);
98,538,256✔
851
      word->end = true;
98,538,256✔
852
    } else {
853
      word->type = wordType(p, len);
392,703,932✔
854
    }
855
  } else {
856
    word->type = WT_TEXT;
6,520,914✔
857
  }
858

859
  return word;
497,763,102✔
860
}
861

862
// parse one command
863
void parseCommand(SWords* command, bool pattern) {
123,897,366✔
864
  char*   p = command->source;
123,897,366✔
865
  int32_t start = 0;
123,897,366✔
866
  int32_t size = command->source_len > 0 ? command->source_len : strlen(p);
123,897,366✔
867

868
  bool lastBlank = false;
123,897,366✔
869
  for (int i = 0; i <= size; i++) {
2,147,483,647✔
870
    if (p[i] == ' ' || i == size) {
2,147,483,647✔
871
      // check continue blank like '    '
872
      if (p[i] == ' ') {
499,212,194✔
873
        if (lastBlank) {
375,314,828✔
874
          start++;
1,449,092✔
875
          continue;
1,449,092✔
876
        }
877
        if (i == 0) {  // first blank
373,865,736✔
878
          lastBlank = true;
×
879
          start++;
×
880
          continue;
×
881
        }
882
        lastBlank = true;
373,865,736✔
883
      }
884

885
      // found split or string end , append word
886
      if (command->head == NULL) {
497,763,102✔
887
        command->head = addWord(p + start, i - start, pattern);
123,897,366✔
888
        command->count = 1;
123,897,366✔
889
      } else {
890
        SWord* word = command->head;
373,865,736✔
891
        while (word->next) {
1,249,841,850✔
892
          word = word->next;
875,976,114✔
893
        }
894
        word->next = addWord(p + start, i - start, pattern);
373,865,736✔
895
        command->count++;
373,865,736✔
896
      }
897
      start = i + 1;
497,763,102✔
898
    } else {
899
      lastBlank = false;
2,147,483,647✔
900
    }
901
  }
902
}
123,897,366✔
903

904
// free SShellCmd
905
void freeCommand(SWords* command) {
123,897,366✔
906
  SWord* item = command->head;
123,897,366✔
907
  command->head = NULL;
123,897,366✔
908
  // loop
909
  while (item) {
621,660,468✔
910
    SWord* tmp = item;
497,763,102✔
911
    item = item->next;
497,763,102✔
912
    // if malloc need free
913
    if (tmp->free && tmp->word) taosMemoryFree(tmp->word);
497,763,102✔
914
    taosMemoryFree(tmp);
497,763,102✔
915
  }
916
}
123,897,366✔
917

918
void GenerateVarType(int type, char** p, int count) {
11,592,736✔
919
  STire* tire = createTire(TIRE_LIST);
11,592,736✔
920
  for (int i = 0; i < count; i++) {
263,010,198✔
921
    insertWord(tire, p[i]);
251,417,462✔
922
  }
923

924
  taosThreadMutexLock(&tiresMutex);
11,592,736✔
925
  tires[type] = tire;
11,592,736✔
926
  taosThreadMutexUnlock(&tiresMutex);
11,592,736✔
927
}
11,592,736✔
928

929
//
930
//  -------------------- shell auto ----------------
931
//
932

933
// init shell auto function , shell start call once
934
void shellAutoInit() {
724,546✔
935
  // command
936
  int32_t count = SHELL_COMMAND_COUNT();
724,546✔
937
  for (int32_t i = 0; i < count; i++) {
124,621,912✔
938
    parseCommand(shellCommands + i, true);
123,897,366✔
939
  }
940

941
  // tires
942
  memset(tires, 0, sizeof(STire*) * WT_VAR_CNT);
724,546✔
943
  taosThreadMutexInit(&tiresMutex, NULL);
724,546✔
944

945
  // threads
946
  memset(threads, 0, sizeof(TdThread*) * WT_FROM_DB_CNT);
724,546✔
947

948
  // generate varType
949
  GenerateVarType(WT_VAR_FUNC, functions, sizeof(functions) / sizeof(char*));
724,546✔
950
  GenerateVarType(WT_VAR_KEYWORD, keywords, sizeof(keywords) / sizeof(char*));
724,546✔
951
  GenerateVarType(WT_VAR_TBACTION, tb_actions, sizeof(tb_actions) / sizeof(char*));
724,546✔
952
  GenerateVarType(WT_VAR_DBOPTION, db_options, sizeof(db_options) / sizeof(char*));
724,546✔
953
  GenerateVarType(WT_VAR_ALTER_DBOPTION, alter_db_options, sizeof(alter_db_options) / sizeof(char*));
724,546✔
954
  GenerateVarType(WT_VAR_DATATYPE, data_types, sizeof(data_types) / sizeof(char*));
724,546✔
955
  GenerateVarType(WT_VAR_KEYTAGS, key_tags, sizeof(key_tags) / sizeof(char*));
724,546✔
956
  GenerateVarType(WT_VAR_TBOPTION, tb_options, sizeof(tb_options) / sizeof(char*));
724,546✔
957
  GenerateVarType(WT_VAR_USERACTION, user_actions, sizeof(user_actions) / sizeof(char*));
724,546✔
958
  GenerateVarType(WT_VAR_KEYSELECT, key_select, sizeof(key_select) / sizeof(char*));
724,546✔
959
  GenerateVarType(WT_VAR_SYSTABLE, key_systable, sizeof(key_systable) / sizeof(char*));
724,546✔
960
  GenerateVarType(WT_VAR_LANGUAGE, udf_language, sizeof(udf_language) / sizeof(char*));
724,546✔
961
  GenerateVarType(WT_VAR_GLOBALKEYS, global_keys, sizeof(global_keys) / sizeof(char*));
724,546✔
962
  GenerateVarType(WT_VAR_FIELD_OPTIONS, field_options, sizeof(field_options) / sizeof(char*));
724,546✔
963
  GenerateVarType(WT_VAR_CREATE_USER_OPT, create_uopt, sizeof(create_uopt) / sizeof(char*));
724,546✔
964
  GenerateVarType(WT_VAR_ALTER_USER_OPT, alter_uopt, sizeof(alter_uopt) / sizeof(char*));
724,546✔
965
}
724,546✔
966

967
// set conn
968
void shellSetConn(TAOS* conn, bool runOnce) {
722,573✔
969
  varCon = conn;
722,573✔
970
  varRunOnce = runOnce;
722,573✔
971
  // init database and stable
972
  if (!runOnce) updateTireValue(WT_VAR_DBNAME, false);
722,573✔
973
}
722,573✔
974

975
// exit shell auto function, shell exit call once
976
void shellAutoExit() {
724,546✔
977
  // free command
978
  int32_t count = SHELL_COMMAND_COUNT();
724,546✔
979
  for (int32_t i = 0; i < count; i++) {
124,621,912✔
980
    freeCommand(shellCommands + i);
123,897,366✔
981
  }
982

983
  // free tires
984
  taosThreadMutexLock(&tiresMutex);
724,546✔
985
  for (int32_t i = 0; i < WT_VAR_CNT; i++) {
22,460,926✔
986
    if (tires[i]) {
21,736,380✔
987
      freeTire(tires[i]);
11,593,373✔
988
      tires[i] = NULL;
11,593,373✔
989
    }
990
  }
991
  taosThreadMutexUnlock(&tiresMutex);
724,546✔
992
  // destroy
993
  taosThreadMutexDestroy(&tiresMutex);
724,546✔
994

995
  // free threads
996
  for (int32_t i = 0; i < WT_FROM_DB_CNT; i++) {
9,419,098✔
997
    if (threads[i]) {
8,694,552✔
998
      taosDestroyThread(threads[i]);
×
999
      threads[i] = NULL;
×
1000
    }
1001
  }
1002

1003
  // free lastMatch
1004
  if (lastMatch) {
724,546✔
1005
    freeMatch(lastMatch);
×
1006
    lastMatch = NULL;
×
1007
  }
1008
}
724,546✔
1009

1010
//
1011
//  -------------------  auto ptr for tires --------------------------
1012
//
1013
bool setNewAutoPtr(int type, STire* pNew) {
637✔
1014
  if (pNew == NULL) return false;
637✔
1015

1016
  taosThreadMutexLock(&tiresMutex);
637✔
1017
  STire* pOld = tires[type];
637✔
1018
  if (pOld != NULL) {
637✔
1019
    // previous have value, release self ref count
1020
    if (--pOld->ref == 0) {
×
1021
      freeTire(pOld);
×
1022
    }
1023
  }
1024

1025
  // set new
1026
  tires[type] = pNew;
637✔
1027
  tires[type]->ref = 1;
637✔
1028
  taosThreadMutexUnlock(&tiresMutex);
637✔
1029

1030
  return true;
637✔
1031
}
1032

1033
// get ptr
1034
STire* getAutoPtr(int type) {
×
1035
  if (tires[type] == NULL) {
×
1036
    return NULL;
×
1037
  }
1038

1039
  taosThreadMutexLock(&tiresMutex);
×
1040
  tires[type]->ref++;
×
1041
  taosThreadMutexUnlock(&tiresMutex);
×
1042

1043
  return tires[type];
×
1044
}
1045

1046
// put back tire to tires[type], if tire not equal tires[type].p, need free tire
1047
void putBackAutoPtr(int type, STire* tire) {
×
1048
  if (tire == NULL) {
×
1049
    return;
×
1050
  }
1051

1052
  taosThreadMutexLock(&tiresMutex);
×
1053
  if (tires[type] != tire) {
×
1054
    // update by out,  can't put back , so free
1055
    if (--tire->ref == 1) {
×
1056
      // support multi thread getAutoPtr
1057
      freeTire(tire);
×
1058
    }
1059

1060
  } else {
1061
    tires[type]->ref--;
×
1062
    ASSERT(tires[type]->ref > 0);
×
1063
  }
1064
  taosThreadMutexUnlock(&tiresMutex);
×
1065

1066
  return;
×
1067
}
1068

1069
//
1070
//  -------------------  var Word --------------------------
1071
//
1072

1073
#define MAX_CACHED_CNT 100000  // max cached rows 10w
1074
// write sql result to var name, return write rows cnt
1075
int writeVarNames(int type, TAOS_RES* tres) {
637✔
1076
  // fetch row
1077
  TAOS_ROW row = taos_fetch_row(tres);
637✔
1078
  if (row == NULL) {
637✔
1079
    return 0;
×
1080
  }
1081

1082
  TAOS_FIELD* fields = taos_fetch_fields(tres);
637✔
1083
  // create new tires
1084
  char   tireType = type == WT_VAR_TABLE ? TIRE_TREE : TIRE_LIST;
637✔
1085
  STire* tire = createTire(tireType);
637✔
1086

1087
  // enum rows
1088
  char name[1024];
637✔
1089
  int  numOfRows = 0;
637✔
1090
  do {
1091
    int32_t* lengths = taos_fetch_lengths(tres);
1,655✔
1092
    int32_t  bytes = lengths[0];
1,655✔
1093
    if (fields[0].type == TSDB_DATA_TYPE_INT) {
1,655✔
1094
      sprintf(name, "%d", *(int16_t*)row[0]);
×
1095
    } else {
1096
      memcpy(name, row[0], bytes);
1,655✔
1097
    }
1098

1099
    name[bytes] = 0;  // set string end
1,655✔
1100
    // insert to tire
1101
    insertWord(tire, name);
1,655✔
1102

1103
    if (++numOfRows > MAX_CACHED_CNT) {
1,655✔
1104
      break;
×
1105
    }
1106

1107
    row = taos_fetch_row(tres);
1,655✔
1108
  } while (row != NULL);
1,655✔
1109

1110
  // replace old tire
1111
  setNewAutoPtr(type, tire);
637✔
1112

1113
  return numOfRows;
637✔
1114
}
1115

1116
void setThreadNull(int type) {
637✔
1117
  taosThreadMutexLock(&tiresMutex);
637✔
1118
  if (threads[type]) {
637✔
1119
    taosMemoryFree(threads[type]);
637✔
1120
  }
1121
  threads[type] = NULL;
637✔
1122
  taosThreadMutexUnlock(&tiresMutex);
637✔
1123
}
637✔
1124

1125
bool firstMatchCommand(TAOS* con, SShellCmd* cmd);
1126
//
1127
//  thread obtain var thread from db server
1128
//
1129
void* varObtainThread(void* param) {
637✔
1130
  int type = *(int*)param;
637✔
1131
  taosMemoryFree(param);
637✔
1132

1133
  if (varCon == NULL || type > WT_FROM_DB_MAX) {
637✔
1134
    return NULL;
×
1135
  }
1136

1137
  TAOS_RES* pSql = taos_query(varCon, varSqls[type]);
637✔
1138
  if (taos_errno(pSql)) {
637✔
1139
    taos_free_result(pSql);
×
1140
    setThreadNull(type);
×
1141
    return NULL;
×
1142
  }
1143

1144
  // write var names from pSql
1145
  int cnt = writeVarNames(type, pSql);
637✔
1146

1147
  // free sql
1148
  taos_free_result(pSql);
637✔
1149

1150
  // check need call auto tab
1151
  if (cnt > 0 && waitAutoFill) {
637✔
1152
    // press tab key by program
1153
    firstMatchCommand(varCon, varCmd);
×
1154
  }
1155

1156
  setThreadNull(type);
637✔
1157
  return NULL;
637✔
1158
}
1159

1160
// return true is need update value by async
1161
bool updateTireValue(int type, bool autoFill) {
637✔
1162
  // TYPE CONTEXT GET FROM DB
1163
  taosThreadMutexLock(&tiresMutex);
637✔
1164

1165
  // check need obtain from server
1166
  if (tires[type] == NULL) {
637✔
1167
    waitAutoFill = autoFill;
637✔
1168
    // need async obtain var names from db sever
1169
    if (threads[type] != NULL) {
637✔
1170
      if (taosThreadRunning(threads[type])) {
×
1171
        // thread running , need not obtain again, return
1172
        taosThreadMutexUnlock(&tiresMutex);
×
1173
        return NULL;
×
1174
      }
1175
      // destroy previous thread handle for new create thread handle
1176
      taosDestroyThread(threads[type]);
×
1177
      threads[type] = NULL;
×
1178
    }
1179

1180
    // create new
1181
    void* param = taosMemoryMalloc(sizeof(int));
637✔
1182
    *((int*)param) = type;
637✔
1183
    threads[type] = taosCreateThread(varObtainThread, param);
637✔
1184
    taosThreadMutexUnlock(&tiresMutex);
637✔
1185
    return true;
637✔
1186
  }
1187
  taosThreadMutexUnlock(&tiresMutex);
×
1188

1189
  return false;
×
1190
}
1191

1192
// only match next one word from all match words, return valuue must free by caller
1193
char* matchNextPrefix(STire* tire, char* pre) {
×
1194
  SMatch* match = NULL;
×
1195
  if (tire == NULL) return NULL;
×
1196

1197
  // re-use last result
1198
  if (lastMatch) {
×
1199
    if (strcmp(pre, lastMatch->pre) == 0) {
×
1200
      // same pre
1201
      match = lastMatch;
×
1202
    }
1203
  }
1204

1205
  if (match == NULL) {
×
1206
    // not same with last result
1207
    if (pre[0] == 0) {
×
1208
      // EMPTY PRE
1209
      match = enumAll(tire);
×
1210
    } else {
1211
      // NOT EMPTY
1212
      match = (SMatch*)taosMemoryMalloc(sizeof(SMatch));
×
1213
      memset(match, 0, sizeof(SMatch));
×
1214
      matchPrefix(tire, pre, match);
×
1215
    }
1216

1217
    // save to lastMatch
1218
    if (match) {
×
1219
      if (lastMatch) freeMatch(lastMatch);
×
1220
      lastMatch = match;
×
1221
    }
1222
  }
1223

1224
  // check valid
1225
  if (match == NULL || match->head == NULL) {
×
1226
    // no one matched
1227
    return NULL;
×
1228
  }
1229

1230
  if (cursorVar == -1) {
×
1231
    // first
1232
    cursorVar = 0;
×
1233
    return taosStrdup(match->head->word);
×
1234
  }
1235

1236
  // according to cursorVar , calculate next one
1237
  int         i = 0;
×
1238
  SMatchNode* item = match->head;
×
1239
  while (item) {
×
1240
    if (i == cursorVar + 1) {
×
1241
      // found next position ok
1242
      if (item->next == NULL) {
×
1243
        // match last item, reset cursorVar to head
1244
        cursorVar = -1;
×
1245
      } else {
1246
        cursorVar = i;
×
1247
      }
1248

1249
      return taosStrdup(item->word);
×
1250
    }
1251

1252
    // check end item
1253
    if (item->next == NULL) {
×
1254
      // if cursorVar > var list count, return last and reset cursorVar
1255
      cursorVar = -1;
×
1256

1257
      return taosStrdup(item->word);
×
1258
    }
1259

1260
    // move next
1261
    item = item->next;
×
1262
    i++;
×
1263
  }
1264

1265
  return NULL;
×
1266
}
1267

1268
// search pre word from tire tree, return value must free by caller
1269
char* tireSearchWord(int type, char* pre) {
×
1270
  if (type == WT_TEXT) {
×
1271
    return NULL;
×
1272
  }
1273

1274
  if (type > WT_FROM_DB_MAX) {
×
1275
    // NOT FROM DB , tires[type] alwary not null
1276
    STire* tire = tires[type];
×
1277
    if (tire == NULL) return NULL;
×
1278
    return matchNextPrefix(tire, pre);
×
1279
  }
1280

1281
  if (updateTireValue(type, true)) {
×
1282
    return NULL;
×
1283
  }
1284

1285
  // can obtain var names from local
1286
  STire* tire = getAutoPtr(type);
×
1287
  if (tire == NULL) {
×
1288
    return NULL;
×
1289
  }
1290

1291
  char* str = matchNextPrefix(tire, pre);
×
1292
  // used finish, put back pointer to autoptr array
1293
  putBackAutoPtr(type, tire);
×
1294

1295
  return str;
×
1296
}
1297

1298
// match var word, word1 is pattern , word2 is input from shell
1299
bool matchVarWord(SWord* word1, SWord* word2) {
×
1300
  // search input word from tire tree
1301
  char pre[512];
×
1302
  memcpy(pre, word2->word, word2->len);
×
1303
  pre[word2->len] = 0;
×
1304

1305
  char* str = NULL;
×
1306
  if (word1->type == WT_VAR_ALLTABLE) {
×
1307
    // ALL_TABLE
1308
    str = tireSearchWord(WT_VAR_STABLE, pre);
×
1309
    if (str == NULL) {
×
1310
      str = tireSearchWord(WT_VAR_TABLE, pre);
×
1311
      if (str == NULL) return false;
×
1312
    }
1313
  } else {
1314
    // OTHER
1315
    str = tireSearchWord(word1->type, pre);
×
1316
    if (str == NULL) {
×
1317
      // not found or word1->type variable list not obtain from server, return not match
1318
      return false;
×
1319
    }
1320
  }
1321

1322
  // free previous malloc
1323
  if (word1->free && word1->word) {
×
1324
    taosMemoryFree(word1->word);
×
1325
  }
1326

1327
  // save
1328
  word1->word = str;
×
1329
  word1->len = strlen(str);
×
1330
  word1->free = true;  // need free
×
1331

1332
  return true;
×
1333
}
1334

1335
//
1336
//  -------------------  match words --------------------------
1337
//
1338

1339
// compare command cmdPattern come from shellCommands , cmdInput come from user input
1340
int32_t compareCommand(SWords* cmdPattern, SWords* cmdInput) {
×
1341
  SWord* wordPattern = cmdPattern->head;
×
1342
  SWord* wordInput = cmdInput->head;
×
1343

1344
  if (wordPattern == NULL || wordInput == NULL) {
×
1345
    return -1;
×
1346
  }
1347

1348
  for (int32_t i = 0; i < cmdPattern->count; i++) {
×
1349
    if (wordPattern->type == WT_TEXT) {
×
1350
      // WT_TEXT match
1351
      if (wordPattern->len == wordInput->len) {
×
1352
        if (strncasecmp(wordPattern->word, wordInput->word, wordPattern->len) != 0) return -1;
×
1353
      } else if (wordPattern->len < wordInput->len) {
×
1354
        return -1;
×
1355
      } else {
1356
        // wordPattern->len > wordInput->len
1357
        if (strncasecmp(wordPattern->word, wordInput->word, wordInput->len) == 0) {
×
1358
          if (i + 1 == cmdInput->count) {
×
1359
            // last word return match
1360
            cmdPattern->matchIndex = i;
×
1361
            cmdPattern->matchLen = wordInput->len;
×
1362
            return i;
×
1363
          } else {
1364
            return -1;
×
1365
          }
1366
        } else {
1367
          return -1;
×
1368
        }
1369
      }
1370
    } else {
1371
      // WT_VAR auto match any one word
1372
      if (wordInput->next == NULL) {  // input words last one
×
1373
        if (matchVarWord(wordPattern, wordInput)) {
×
1374
          cmdPattern->matchIndex = i;
×
1375
          cmdPattern->matchLen = wordInput->len;
×
1376
          varMode = true;
×
1377
          return i;
×
1378
        }
1379
        return -1;
×
1380
      }
1381
    }
1382

1383
    // move next
1384
    wordPattern = wordPattern->next;
×
1385
    wordInput = wordInput->next;
×
1386
    if (wordPattern == NULL || wordInput == NULL) {
×
1387
      return -1;
×
1388
    }
1389
  }
1390

1391
  return -1;
×
1392
}
1393

1394
// match command
1395
SWords* matchCommand(SWords* input, bool continueSearch) {
×
1396
  int32_t count = SHELL_COMMAND_COUNT();
×
1397
  for (int32_t i = 0; i < count; i++) {
×
1398
    SWords* shellCommand = shellCommands + i;
×
1399
    if (continueSearch && lastMatchIndex != -1 && i <= lastMatchIndex) {
×
1400
      // new match must greater than lastMatchIndex
1401
      if (varMode && i == lastMatchIndex) {
×
1402
        // do nothing, var match on lastMatchIndex
1403
      } else {
1404
        continue;
×
1405
      }
1406
    }
1407

1408
    // command is large
1409
    if (input->count > shellCommand->count) {
×
1410
      continue;
×
1411
    }
1412

1413
    // compare
1414
    int32_t index = compareCommand(shellCommand, input);
×
1415
    if (index != -1) {
×
1416
      if (firstMatchIndex == -1) firstMatchIndex = i;
×
1417
      curMatchIndex = i;
×
1418
      return &shellCommands[i];
×
1419
    }
1420
  }
1421

1422
  // not match
1423
  return NULL;
×
1424
}
1425

1426
//
1427
//  -------------------  print screen --------------------------
1428
//
1429

1430
// delete char count
1431
void deleteCount(SShellCmd* cmd, int count) {
×
1432
  int size = 0;
×
1433
  int width = 0;
×
1434
  int prompt_size = 6;
×
1435
  shellClearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size);
×
1436

1437
  // loop delete
1438
  while (--count >= 0 && cmd->cursorOffset > 0) {
×
1439
    shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width);
×
1440
    memmove(cmd->command + cmd->cursorOffset - size, cmd->command + cmd->cursorOffset,
×
1441
            cmd->commandSize - cmd->cursorOffset);
×
1442
    cmd->commandSize -= size;
×
1443
    cmd->cursorOffset -= size;
×
1444
    cmd->screenOffset -= width;
×
1445
    cmd->endOffset -= width;
×
1446
  }
1447
}
×
1448

1449
// show screen
1450
void printScreen(TAOS* con, SShellCmd* cmd, SWords* match) {
×
1451
  // modify SShellCmd
1452
  if (firstMatchIndex == -1 || curMatchIndex == -1) {
×
1453
    // no match
1454
    return;
×
1455
  }
1456

1457
  // first tab press
1458
  const char* str = NULL;
×
1459
  int         strLen = 0;
×
1460

1461
  SWord* word = MATCH_WORD(match);
×
1462
  if (firstMatchIndex == curMatchIndex && lastWordBytes == -1) {
×
1463
    // first press tab
1464
    str = word->word + match->matchLen;
×
1465
    strLen = word->len - match->matchLen;
×
1466
    lastMatchIndex = firstMatchIndex;
×
1467
    lastWordBytes = word->len;
×
1468
  } else {
1469
    if (lastWordBytes == -1) return;
×
1470
    deleteCount(cmd, lastWordBytes);
×
1471
    str = word->word;
×
1472
    strLen = word->len;
×
1473
    // set current to last
1474
    lastMatchIndex = curMatchIndex;
×
1475
    lastWordBytes = word->len;
×
1476
  }
1477

1478
  if (word->end && str[strLen - 1] != ';') {
×
1479
    // append end ';'
1480
    char* p = taosMemoryCalloc(strLen + 8, 1);
×
1481
    if (p) {
×
1482
      tstrncpy(p, str, strLen + 1);
×
1483
      tstrncpy(p + strLen, ";", 1 + 1);
×
1484
      lastWordBytes += 1;
×
1485
      shellInsertStr(cmd, (char*)p, strLen + 1);
×
1486
      taosMemoryFree(p);
×
1487
    } else {
1488
      shellInsertStr(cmd, (char*)str, strLen);
×
1489
    }
1490
  } else {
1491
    // insert new
1492
    shellInsertStr(cmd, (char*)str, strLen);
×
1493
  }
1494
}
1495

1496
// main key press tab , matched return true else false
1497
bool firstMatchCommand(TAOS* con, SShellCmd* cmd) {
×
1498
  if (con == NULL || cmd == NULL) return false;
×
1499
  // parse command
1500
  SWords* input = (SWords*)taosMemoryMalloc(sizeof(SWords));
×
1501
  memset(input, 0, sizeof(SWords));
×
1502
  input->source = cmd->command;
×
1503
  input->source_len = cmd->commandSize;
×
1504
  parseCommand(input, false);
×
1505

1506
  // if have many , default match first, if press tab again , switch to next
1507
  curMatchIndex = -1;
×
1508
  lastMatchIndex = -1;
×
1509
  SWords* match = matchCommand(input, true);
×
1510
  if (match == NULL) {
×
1511
    // not match , nothing to do
1512
    freeCommand(input);
×
1513
    taosMemoryFree(input);
×
1514
    return false;
×
1515
  }
1516

1517
  // print to screen
1518
  printScreen(con, cmd, match);
×
1519
#ifdef WINDOWS
1520
  printf("\r");
1521
  shellShowOnScreen(cmd);
1522
#endif
1523
  freeCommand(input);
×
1524
  taosMemoryFree(input);
×
1525
  return true;
×
1526
}
1527

1528
// create input source
1529
void createInputFromFirst(SWords* input, SWords* firstMatch) {
×
1530
  //
1531
  // if next pressTabKey , input context come from firstMatch, set matched length with source_len
1532
  //
1533
  input->source = (char*)taosMemoryMalloc(1024);
×
1534
  memset((void*)input->source, 0, 1024);
×
1535

1536
  SWord* word = firstMatch->head;
×
1537

1538
  // source_len = full match word->len + half match with firstMatch->matchLen
1539
  for (int i = 0; i < firstMatch->matchIndex && word; i++) {
×
1540
    // combine source from each word
1541
    strncpy(input->source + input->source_len, word->word, word->len);
×
1542
    strcat(input->source, " ");          // append blank space
×
1543
    input->source_len += word->len + 1;  // 1 is blank length
×
1544
    // move next
1545
    word = word->next;
×
1546
  }
1547
  // appand half matched word for last
1548
  if (word) {
×
1549
    strncpy(input->source + input->source_len, word->word, firstMatch->matchLen);
×
1550
    input->source_len += firstMatch->matchLen;
×
1551
  }
1552
}
×
1553

1554
// user press Tabkey again is named next , matched return true else false
1555
bool nextMatchCommand(TAOS* con, SShellCmd* cmd, SWords* firstMatch) {
×
1556
  if (firstMatch == NULL || firstMatch->head == NULL) {
×
1557
    return false;
×
1558
  }
1559
  SWords* input = (SWords*)taosMemoryMalloc(sizeof(SWords));
×
1560
  memset(input, 0, sizeof(SWords));
×
1561

1562
  // create input from firstMatch
1563
  createInputFromFirst(input, firstMatch);
×
1564

1565
  // parse input
1566
  parseCommand(input, false);
×
1567

1568
  // if have many , default match first, if press tab again , switch to next
1569
  SWords* match = matchCommand(input, true);
×
1570
  if (match == NULL) {
×
1571
    // if not match , reset all index
1572
    firstMatchIndex = -1;
×
1573
    curMatchIndex = -1;
×
1574
    match = matchCommand(input, false);
×
1575
    if (match == NULL) {
×
1576
      freeCommand(input);
×
1577
      if (input->source) taosMemoryFree(input->source);
×
1578
      taosMemoryFree(input);
×
1579
      return false;
×
1580
    }
1581
  }
1582

1583
  // print to screen
1584
  printScreen(con, cmd, match);
×
1585
#ifdef WINDOWS
1586
  printf("\r");
1587
  shellShowOnScreen(cmd);
1588
#endif
1589

1590
  // free
1591
  freeCommand(input);
×
1592
  if (input->source) {
×
1593
    taosMemoryFree(input->source);
×
1594
    input->source = NULL;
×
1595
  }
1596
  taosMemoryFree(input);
×
1597

1598
  return true;
×
1599
}
1600

1601
// fill with type
1602
bool fillWithType(TAOS* con, SShellCmd* cmd, char* pre, int type) {
×
1603
  // get type
1604
  STire* tire = tires[type];
×
1605
  char*  str = matchNextPrefix(tire, pre);
×
1606
  if (str == NULL) {
×
1607
    return false;
×
1608
  }
1609

1610
  // need insert part string
1611
  char* part = str + strlen(pre);
×
1612

1613
  // show
1614
  int count = strlen(part);
×
1615
  shellInsertStr(cmd, part, count);
×
1616
  cntDel = count;  // next press tab delete current append count
×
1617

1618
  taosMemoryFree(str);
×
1619
  return true;
×
1620
}
1621

1622
// fill with type
1623
bool fillTableName(TAOS* con, SShellCmd* cmd, char* pre) {
×
1624
  // search stable and table
1625
  char* str = tireSearchWord(WT_VAR_STABLE, pre);
×
1626
  if (str == NULL) {
×
1627
    str = tireSearchWord(WT_VAR_TABLE, pre);
×
1628
    if (str == NULL) return false;
×
1629
  }
1630

1631
  // need insert part string
1632
  char* part = str + strlen(pre);
×
1633

1634
  // delete autofill count last append
1635
  if (cntDel > 0) {
×
1636
    deleteCount(cmd, cntDel);
×
1637
    cntDel = 0;
×
1638
  }
1639

1640
  // show
1641
  int count = strlen(part);
×
1642
  shellInsertStr(cmd, part, count);
×
1643
  cntDel = count;  // next press tab delete current append count
×
1644

1645
  taosMemoryFree(str);
×
1646
  return true;
×
1647
}
1648

1649
//
1650
// find last word from sql select clause
1651
//  example :
1652
//  1 select cou -> press tab  select count(
1653
//  2 select count(*),su -> select count(*), sum(
1654
//  3 select count(*), su -> select count(*), sum(
1655
//
1656
char* lastWord(char* p) {
×
1657
  // get near from end revert find ' ' and ','
1658
  char* p1 = strrchr(p, ' ');
×
1659
  char* p2 = strrchr(p, ',');
×
1660

1661
  if (p1 && p2) {
×
1662
    return p1 > p2 ? p1 + 1 : p2 + 1;
×
1663
  } else if (p1) {
×
1664
    return p1 + 1;
×
1665
  } else if (p2) {
×
1666
    return p2 + 1;
×
1667
  } else {
1668
    return p;
×
1669
  }
1670
}
1671

1672
bool fieldsInputEnd(char* sql) {
×
1673
  // not in '()'
1674
  char* p1 = strrchr(sql, '(');
×
1675
  char* p2 = strrchr(sql, ')');
×
1676
  if (p1 && p2 == NULL) {
×
1677
    // like select count( '  '
1678
    return false;
×
1679
  } else if (p1 && p2 && p1 > p2) {
×
1680
    // like select sum(age), count( ' '
1681
    return false;
×
1682
  }
1683

1684
  // not in ','
1685
  char* p3 = strrchr(sql, ',');
×
1686
  char* p = p3;
×
1687
  // like select ts, age,'    '
1688
  if (p) {
×
1689
    ++p;
×
1690
    bool  allBlank = true;  // after last ','  all char is blank
×
1691
    int   cnt = 0;          // blank count , like '    ' as one blank
×
1692
    char* plast = NULL;     // last blank position
×
1693
    while (*p) {
×
1694
      if (*p == ' ') {
×
1695
        plast = p;
×
1696
        cnt++;
×
1697
      } else {
1698
        allBlank = false;
×
1699
      }
1700
      ++p;
×
1701
    }
1702

1703
    // any one word is not blank
1704
    if (allBlank) {
×
1705
      return false;
×
1706
    }
1707

1708
    // like 'select count(*),sum(age) fr' need return true
1709
    if (plast && plast > p3 && p2 > p1 && plast > p2 && p1 > p3) {
×
1710
      return true;
×
1711
    }
1712

1713
    // if last char not ' ', then not end field, like 'select count(*), su' can fill sum(
1714
    if (sql[strlen(sql) - 1] != ' ' && cnt <= 1) {
×
1715
      return false;
×
1716
    }
1717
  }
1718

1719
  char* p4 = strrchr(sql, ' ');
×
1720
  if (p4 == NULL) {
×
1721
    // only one word
1722
    return false;
×
1723
  }
1724

1725
  return true;
×
1726
}
1727

1728
// need insert from
1729
bool needInsertFrom(char* sql, int len) {
×
1730
  // last is blank
1731
  if (sql[len - 1] != ' ') {
×
1732
    // insert from keyword
1733
    return false;
×
1734
  }
1735

1736
  //  select fields input is end
1737
  if (!fieldsInputEnd(sql)) {
×
1738
    return false;
×
1739
  }
1740

1741
  // can insert from keyword
1742
  return true;
×
1743
}
1744

1745
// p is string following select keyword
1746
bool appendAfterSelect(TAOS* con, SShellCmd* cmd, char* sql, int32_t len) {
×
1747
  char* p = taosStrndup(sql, len);
×
1748

1749
  // union all
1750
  char* p1;
1751
  do {
1752
    p1 = strstr(p, UNION_ALL);
×
1753
    if (p1) {
×
1754
      p = p1 + strlen(UNION_ALL);
×
1755
    }
1756
  } while (p1);
×
1757

1758
  char* from = strstr(p, " from ");
×
1759
  // last word , maybe empty string or some letters of a string
1760
  char* last = lastWord(p);
×
1761
  bool  ret = false;
×
1762
  if (from == NULL) {
×
1763
    bool fieldEnd = fieldsInputEnd(p);
×
1764
    // check fields input end then insert from keyword
1765
    if (fieldEnd && p[len - 1] == ' ') {
×
1766
      shellInsertStr(cmd, "from", 4);
×
1767
      taosMemoryFree(p);
×
1768
      return true;
×
1769
    }
1770

1771
    // fill function
1772
    if (fieldEnd) {
×
1773
      // fields is end , need match keyword
1774
      ret = fillWithType(con, cmd, last, WT_VAR_KEYWORD);
×
1775
    } else {
1776
      ret = fillWithType(con, cmd, last, WT_VAR_FUNC);
×
1777
    }
1778

1779
    taosMemoryFree(p);
×
1780
    return ret;
×
1781
  }
1782

1783
  // have from
1784
  char* blank = strstr(from + 6, " ");
×
1785
  if (blank == NULL) {
×
1786
    // no table name, need fill
1787
    ret = fillTableName(con, cmd, last);
×
1788
  } else {
1789
    ret = fillWithType(con, cmd, last, WT_VAR_KEYWORD);
×
1790
  }
1791

1792
  taosMemoryFree(p);
×
1793
  return ret;
×
1794
}
1795

1796
int32_t searchAfterSelect(char* p, int32_t len) {
×
1797
  // select * from st;
1798
  if (strncasecmp(p, "select ", 7) == 0) {
×
1799
    // check nest query
1800
    char* p1 = p + 7;
×
1801
    while (1) {
×
1802
      char* p2 = strstr(p1, "select ");
×
1803
      if (p2 == NULL) break;
×
1804
      p1 = p2 + 7;
×
1805
    }
1806

1807
    return p1 - p;
×
1808
  }
1809

1810
  // explain as select * from st;
1811
  if (strncasecmp(p, "explain select ", 15) == 0) {
×
1812
    return 15;
×
1813
  }
1814

1815
  char* as_pos_end = strstr(p, " as select ");
×
1816
  if (as_pos_end == NULL) return -1;
×
1817
  as_pos_end += 11;
×
1818

1819
  // create stream <stream_name> as select
1820
  if (strncasecmp(p, "create stream ", 14) == 0) {
×
1821
    return as_pos_end - p;
×
1822
    ;
1823
  }
1824

1825
  // create topic <topic_name> as select
1826
  if (strncasecmp(p, "create topic ", 13) == 0) {
×
1827
    return as_pos_end - p;
×
1828
  }
1829

1830
  return -1;
×
1831
}
1832

1833
bool matchSelectQuery(TAOS* con, SShellCmd* cmd) {
×
1834
  // if continue press Tab , delete bytes by previous autofill
1835
  if (cntDel > 0) {
×
1836
    deleteCount(cmd, cntDel);
×
1837
    cntDel = 0;
×
1838
  }
1839

1840
  // match select ...
1841
  int   len = cmd->commandSize;
×
1842
  char* p = cmd->command;
×
1843

1844
  // remove prefix blank
1845
  while (p[0] == ' ' && len > 0) {
×
1846
    p++;
×
1847
    len--;
×
1848
  }
1849

1850
  // special range
1851
  if (len < 7 || len > 512) {
×
1852
    return false;
×
1853
  }
1854

1855
  // search
1856
  char*   sql_cp = taosStrndup(p, len);
×
1857
  int32_t n = searchAfterSelect(sql_cp, len);
×
1858
  taosMemoryFree(sql_cp);
×
1859
  if (n == -1 || n > len) return false;
×
1860
  p += n;
×
1861
  len -= n;
×
1862

1863
  // append
1864
  return appendAfterSelect(con, cmd, p, len);
×
1865
}
1866

1867
// is fields option area
1868
bool fieldOptionsArea(char* p) {
×
1869
  char* p1 = strrchr(p, '(');
×
1870
  char* p2 = strrchr(p, ',');
×
1871
  if (p1 == NULL && p2 == NULL) {
×
1872
    return false;
×
1873
  }
1874

1875
  // find tags
1876
  if (strstr(p, " tags") != NULL) {
×
1877
    return false;
×
1878
  }
1879

1880
  if (p2 == NULL) {
×
1881
    // first field area
1882
    p2 = p1;
×
1883
  }
1884

1885
  // find blank count
1886
  int32_t cnt = 0;
×
1887
  while (p2) {
×
1888
    p2 = strchr(p2, ' ');
×
1889
    if (p2) {
×
1890
      // get prev char
1891
      char prec = *(p2 - 1);
×
1892
      if (prec != ',' && prec != '(') {
×
1893
        // blank if before comma, not calc count.  like st(ts timestamp,  age int + BLANK + TAB only two blank
1894
        cnt++;
×
1895
      }
1896

1897
      // continue blank is one blank
1898
      while (p2[1] != 0 && p2[1] == ' ') {
×
1899
        // move next if blank again
1900
        p2 += 1;
×
1901
      }
1902
      p2 += 1;
×
1903
    }
1904
  }
1905

1906
  // like  create table st(ts timestamp TAB-KEY or  st(ts timestamp , age int TAB-KEY
1907
  return cnt >= 2;
×
1908
}
1909

1910
// if is input create fields or tags area, return true
1911
bool isCreateFieldsArea(char* p) {
×
1912
  int32_t n = 0;  // count
×
1913
  char*  p1 = p;
×
1914
  while (*p1 != 0) {
×
1915
    switch (*p1) {
×
1916
      case '(':
×
1917
        ++n;
×
1918
        break;
×
1919
      case ')':
×
1920
        --n;
×
1921
        break;
×
1922
      default:
×
1923
        break;
×
1924
    }
1925
    // move next
1926
    ++p1;
×
1927
  }
1928

1929
  return n > 0;
×
1930
}
1931

1932
bool matchCreateTable(TAOS* con, SShellCmd* cmd) {
×
1933
  // if continue press Tab , delete bytes by previous autofill
1934
  if (cntDel > 0) {
×
1935
    deleteCount(cmd, cntDel);
×
1936
    cntDel = 0;
×
1937
  }
1938

1939
  // match select ...
1940
  int   len = cmd->commandSize;
×
1941
  char* p = cmd->command;
×
1942

1943
  // remove prefix blank
1944
  while (p[0] == ' ' && len > 0) {
×
1945
    p++;
×
1946
    len--;
×
1947
  }
1948

1949
  // special range
1950
  if (len < 7 || len > 1024) {
×
1951
    return false;
×
1952
  }
1953

1954
  // select and from
1955
  if (strncasecmp(p, "create table ", 13) != 0) {
×
1956
    // not select query clause
1957
    return false;
×
1958
  }
1959
  p += 13;
×
1960
  len -= 13;
×
1961

1962
  char* ps = taosStrndup(p, len);
×
1963
  bool  ret = false;
×
1964
  char* last = lastWord(ps);
×
1965

1966
  // check in create fields or tags input area
1967
  if (isCreateFieldsArea(ps)) {
×
1968
    if (fieldOptionsArea(ps)) {
×
1969
      // fill field options
1970
      ret = fillWithType(con, cmd, last, WT_VAR_FIELD_OPTIONS);
×
1971
    } else {
1972
      // fill field
1973
      ret = fillWithType(con, cmd, last, WT_VAR_DATATYPE);
×
1974
    }
1975
  }
1976

1977
  // tags
1978
  if (!ret) {
×
1979
    // find only one ')' , can insert tags
1980
    char* p1 = strchr(ps, ')');
×
1981
    if (p1) {
×
1982
      if (strstr(p1 + 1, "tags") == NULL) {
×
1983
        // can insert tags keyword
1984
        ret = fillWithType(con, cmd, last, WT_VAR_KEYTAGS);
×
1985
      }
1986
    }
1987
  }
1988

1989
  // tb options
1990
  if (!ret) {
×
1991
    // find like create table st (...) tags(..)  <here is fill tb option area>
1992
    char* p1 = strchr(ps, ')');  // first ')' end
×
1993
    if (p1) {
×
1994
      if (strchr(p1 + 1, ')')) {  // second ')' end
×
1995
        // here is tb options area, can insert option
1996
        ret = fillWithType(con, cmd, last, WT_VAR_TBOPTION);
×
1997
      }
1998
    }
1999
  }
2000

2001
  taosMemoryFree(ps);
×
2002
  return ret;
×
2003
}
2004

2005
bool matchOther(TAOS* con, SShellCmd* cmd) {
×
2006
  int   len = cmd->commandSize;
×
2007
  char* p = cmd->command;
×
2008

2009
  // '\\'
2010
  if (p[len - 1] == '\\') {
×
2011
    // append '\G'
2012
    char a[] = "G;";
×
2013
    shellInsertStr(cmd, a, 2);
×
2014
    return true;
×
2015
  }
2016

2017
  // too small
2018
  if (len < 8) return false;
×
2019

2020
  // like 'from ( '
2021
  char* sql = taosStrndup(p, len);
×
2022
  char* last = lastWord(sql);
×
2023

2024
  if (strcmp(last, "from(") == 0) {
×
2025
    fillWithType(con, cmd, "", WT_VAR_KEYSELECT);
×
2026
    taosMemoryFree(sql);
×
2027
    return true;
×
2028
  }
2029
  if (strncmp(last, "(", 1) == 0) {
×
2030
    last += 1;
×
2031
  }
2032

2033
  char* from = strstr(sql, " from");
×
2034
  // find last ' from'
2035
  while (from) {
×
2036
    char* p1 = strstr(from + 5, " from");
×
2037
    if (p1 == NULL) break;
×
2038
    from = p1;
×
2039
  }
2040

2041
  if (from) {
×
2042
    // find next is '('
2043
    char* p2 = from + 5;
×
2044
    bool  found = false;   // found 'from ... ( ...'  ... is any count of blank
×
2045
    bool  found1 = false;  // found '('
×
2046
    while (1) {
2047
      if (p2 == last || *p2 == '\0') {
×
2048
        // last word or string end
2049
        if (found1) {
×
2050
          found = true;
×
2051
        }
2052
        break;
×
2053
      } else if (*p2 == '(') {
×
2054
        found1 = true;
×
2055
      } else if (*p2 == ' ') {
×
2056
        // do nothing
2057
      } else {
2058
        // have any other char
2059
        break;
×
2060
      }
2061

2062
      // move next
2063
      p2++;
×
2064
    }
2065

2066
    if (found) {
×
2067
      fillWithType(con, cmd, last, WT_VAR_KEYSELECT);
×
2068
      taosMemoryFree(sql);
×
2069
      return true;
×
2070
    }
2071
  }
2072

2073
  // INSERT
2074

2075
  taosMemoryFree(sql);
×
2076

2077
  return false;
×
2078
}
2079

2080
// last match if nothing matched
2081
bool matchEnd(TAOS* con, SShellCmd* cmd) {
×
2082
  // str dump
2083
  bool  ret = false;
×
2084
  char* ps = taosStrndup(cmd->command, cmd->commandSize);
×
2085
  char* last = lastWord(ps);
×
2086
  char* elast = strrchr(last, '.');  // find end last
×
2087
  if (elast) {
×
2088
    last = elast + 1;
×
2089
  }
2090

2091
  // less one char can match
2092
  if (strlen(last) == 0) {
×
2093
    goto _return;
×
2094
  }
2095
  if (strcmp(last, " ") == 0) {
×
2096
    goto _return;
×
2097
  }
2098

2099
  // match database
2100
  if (elast == NULL) {
×
2101
    // dot need not completed with dbname
2102
    if (fillWithType(con, cmd, last, WT_VAR_DBNAME)) {
×
2103
      ret = true;
×
2104
      goto _return;
×
2105
    }
2106
  }
2107

2108
  if (fillWithType(con, cmd, last, WT_VAR_SYSTABLE)) {
×
2109
    ret = true;
×
2110
    goto _return;
×
2111
  }
2112

2113
  // global keys
2114
  if (fillWithType(con, cmd, last, WT_VAR_GLOBALKEYS)) {
×
2115
    ret = true;
×
2116
    goto _return;
×
2117
  }
2118

2119

2120
_return:
×
2121
  taosMemoryFree(ps);
×
2122
  return ret;
×
2123
}
2124

2125
// main key press tab
2126
void pressTabKey(SShellCmd* cmd) {
×
2127
#ifdef WINDOWS
2128
  return ;
2129
#endif
2130
  // check empty tab key
2131
  if (cmd->commandSize == 0) {
×
2132
    // have multi line tab key
2133
    if (cmd->bufferSize == 0) {
×
2134
      showHelp();
×
2135
    }
2136
    shellShowOnScreen(cmd);
×
2137
    return;
×
2138
  }
2139

2140
  // save connection to global
2141
  varCmd = cmd;
×
2142
  bool matched = false;
×
2143

2144
  // manual match like create table st( ...
2145
  matched = matchCreateTable(varCon, cmd);
×
2146
  if (matched) return;
×
2147

2148
  // shellCommands match
2149
  if (firstMatchIndex == -1) {
×
2150
    matched = firstMatchCommand(varCon, cmd);
×
2151
  } else {
2152
    matched = nextMatchCommand(varCon, cmd, &shellCommands[firstMatchIndex]);
×
2153
  }
2154
  if (matched) return;
×
2155

2156
  // NOT MATCHED ANYONE
2157
  // match other like '\G' ...
2158
  matched = matchOther(varCon, cmd);
×
2159
  if (matched) return;
×
2160

2161
  // manual match like select * from ...
2162
  matched = matchSelectQuery(varCon, cmd);
×
2163
  if (matched) return;
×
2164

2165
  // match end
2166
  matched = matchEnd(varCon, cmd);
×
2167

2168
  return;
×
2169
}
2170

2171
// press othr key
2172
void pressOtherKey(char c) {
13,633✔
2173
#ifdef WINDOWS
2174
  return ;
2175
#endif
2176

2177
  // reset global variant
2178
  firstMatchIndex = -1;
13,633✔
2179
  lastMatchIndex = -1;
13,633✔
2180
  curMatchIndex = -1;
13,633✔
2181
  lastWordBytes = -1;
13,633✔
2182

2183
  // var names
2184
  cursorVar = -1;
13,633✔
2185
  varMode = false;
13,633✔
2186
  waitAutoFill = false;
13,633✔
2187
  cntDel = 0;
13,633✔
2188

2189
  if (lastMatch) {
13,633✔
2190
    freeMatch(lastMatch);
×
2191
    lastMatch = NULL;
×
2192
  }
2193
}
13,633✔
2194

2195
// put name into name, return name length
2196
int getWordName(char* p, char* name, int nameLen) {
20,943✔
2197
  // remove prefix blank
2198
  while (*p == ' ') {
25,637✔
2199
    p++;
4,694✔
2200
  }
2201

2202
  // get databases name;
2203
  int i = 0;
20,943✔
2204
  while (p[i] != 0 && i < nameLen - 1) {
83,482✔
2205
    name[i] = p[i];
83,265✔
2206
    i++;
83,265✔
2207
    if (p[i] == ' ' || p[i] == ';' || p[i] == '(') {
83,265✔
2208
      // name end
2209
      break;
2210
    }
2211
  }
2212
  name[i] = 0;
20,943✔
2213

2214
  return i;
20,943✔
2215
}
2216

2217
// deal use db, if have  'use' return true
2218
bool dealUseDB(char* sql) {
1,305,034✔
2219
  // check use keyword
2220
  if (strncasecmp(sql, "use ", 4) != 0) {
1,305,034✔
2221
    return false;
1,295,240✔
2222
  }
2223

2224
  char  db[256];
9,626✔
2225
  char* p = sql + 4;
9,794✔
2226
  if (getWordName(p, db, sizeof(db)) == 0) {
9,794✔
2227
    // no name , return
2228
    return true;
×
2229
  }
2230

2231
  //  dbName is previous use open db name
2232
  if (strcasecmp(db, dbName) == 0) {
9,794✔
2233
    // same , no need switch
2234
    return true;
×
2235
  }
2236

2237
  // switch new db
2238
  taosThreadMutexLock(&tiresMutex);
9,794✔
2239
  // STABLE set null
2240
  STire* tire = tires[WT_VAR_STABLE];
9,794✔
2241
  tires[WT_VAR_STABLE] = NULL;
9,794✔
2242
  if (tire) {
9,794✔
2243
    freeTire(tire);
×
2244
  }
2245
  // TABLE set null
2246
  tire = tires[WT_VAR_TABLE];
9,794✔
2247
  tires[WT_VAR_TABLE] = NULL;
9,794✔
2248
  if (tire) {
9,794✔
2249
    freeTire(tire);
×
2250
  }
2251
  // save
2252
  strcpy(dbName, db);
9,794✔
2253
  taosThreadMutexUnlock(&tiresMutex);
9,794✔
2254

2255
  return true;
9,794✔
2256
}
2257

2258
// deal create, if have 'create' return true
2259
bool dealCreateCommand(char* sql) {
1,295,240✔
2260
  // check keyword
2261
  if (strncasecmp(sql, "create ", 7) != 0) {
1,295,240✔
2262
    return false;
1,290,290✔
2263
  }
2264

2265
  char  name[1024];
4,498✔
2266
  char* p = sql + 7;
4,950✔
2267
  if (getWordName(p, name, sizeof(name)) == 0) {
4,950✔
2268
    // no name , return
2269
    return true;
×
2270
  }
2271

2272
  int type = -1;
4,950✔
2273
  //  dbName is previous use open db name
2274
  if (strcasecmp(name, "database") == 0) {
4,950✔
2275
    type = WT_VAR_DBNAME;
1,350✔
2276
  } else if (strcasecmp(name, "table") == 0) {
3,600✔
2277
    if (strstr(sql, " tags") != NULL && strstr(sql, " using ") == NULL)
1,839✔
2278
      type = WT_VAR_STABLE;
132✔
2279
    else
2280
      type = WT_VAR_TABLE;
1,707✔
2281
  } else if (strcasecmp(name, "user") == 0) {
1,761✔
2282
    type = WT_VAR_USERNAME;
×
2283
  } else if (strcasecmp(name, "topic") == 0) {
1,761✔
2284
    type = WT_VAR_TOPIC;
×
2285
  } else if (strcasecmp(name, "stream") == 0) {
1,761✔
2286
    type = WT_VAR_STREAM;
×
2287
  } else {
2288
    // no match , return
2289
    return true;
1,761✔
2290
  }
2291

2292
  // move next
2293
  p += strlen(name);
3,189✔
2294

2295
  // get next word , that is table name
2296
  if (getWordName(p, name, sizeof(name)) == 0) {
3,189✔
2297
    // no name , return
2298
    return true;
×
2299
  }
2300

2301
  // switch new db
2302
  taosThreadMutexLock(&tiresMutex);
3,189✔
2303
  // STABLE set null
2304
  STire* tire = tires[type];
3,189✔
2305
  if (tire) {
3,189✔
2306
    insertWord(tire, name);
637✔
2307
  }
2308
  taosThreadMutexUnlock(&tiresMutex);
3,189✔
2309

2310
  return true;
3,189✔
2311
}
2312

2313
// deal create, if have 'drop' return true
2314
bool dealDropCommand(char* sql) {
1,290,290✔
2315
  // check keyword
2316
  if (strncasecmp(sql, "drop ", 5) != 0) {
1,290,290✔
2317
    return false;
1,288,785✔
2318
  }
2319

UNCOV
2320
  char  name[1024];
×
2321
  char* p = sql + 5;
1,505✔
2322
  if (getWordName(p, name, sizeof(name)) == 0) {
1,505✔
2323
    // no name , return
2324
    return true;
×
2325
  }
2326

2327
  int type = -1;
1,505✔
2328
  //  dbName is previous use open db name
2329
  if (strcasecmp(name, "database") == 0) {
1,505✔
2330
    type = WT_VAR_DBNAME;
1,313✔
2331
  } else if (strcasecmp(name, "table") == 0) {
192✔
2332
    type = WT_VAR_ALLTABLE;
×
2333
  } else if (strcasecmp(name, "dnode") == 0) {
192✔
2334
    type = WT_VAR_DNODEID;
×
2335
  } else if (strcasecmp(name, "user") == 0) {
192✔
2336
    type = WT_VAR_USERNAME;
×
2337
  } else if (strcasecmp(name, "topic") == 0) {
192✔
2338
    type = WT_VAR_TOPIC;
192✔
2339
  } else if (strcasecmp(name, "stream") == 0) {
×
2340
    type = WT_VAR_STREAM;
×
2341
  } else {
2342
    // no match , return
2343
    return true;
×
2344
  }
2345

2346
  // move next
2347
  p += strlen(name);
1,505✔
2348

2349
  // get next word , that is table name
2350
  if (getWordName(p, name, sizeof(name)) == 0) {
1,505✔
2351
    // no name , return
2352
    return true;
×
2353
  }
2354

2355
  // switch new db
2356
  taosThreadMutexLock(&tiresMutex);
1,505✔
2357
  // STABLE set null
2358
  if (type == WT_VAR_ALLTABLE) {
1,505✔
2359
    bool del = false;
×
2360
    // del in stable
2361
    STire* tire = tires[WT_VAR_STABLE];
×
2362
    if (tire) del = deleteWord(tire, name);
×
2363
    // del in table
2364
    if (!del) {
×
2365
      tire = tires[WT_VAR_TABLE];
×
2366
      if (tire) del = deleteWord(tire, name);
×
2367
    }
2368
  } else {
2369
    // OTHER TYPE
2370
    STire* tire = tires[type];
1,505✔
2371
    if (tire) deleteWord(tire, name);
1,505✔
2372
  }
2373
  taosThreadMutexUnlock(&tiresMutex);
1,505✔
2374

2375
  return true;
1,505✔
2376
}
2377

2378
// callback autotab module after shell sql execute
2379
void callbackAutoTab(char* sqlstr, TAOS* pSql, bool usedb) {
1,305,034✔
2380
  char* sql = sqlstr;
1,305,034✔
2381
  // remove prefix blank
2382
  while (*sql == ' ') {
1,305,873✔
2383
    sql++;
839✔
2384
  }
2385

2386
  if (dealUseDB(sql)) {
1,305,034✔
2387
    // change to new db
2388
    if (!varRunOnce) updateTireValue(WT_VAR_STABLE, false);
9,794✔
2389
    return;
9,794✔
2390
  }
2391

2392
  // create command add name to autotab
2393
  if (dealCreateCommand(sql)) {
1,295,240✔
2394
    return;
4,950✔
2395
  }
2396

2397
  // drop command remove name from autotab
2398
  if (dealDropCommand(sql)) {
1,290,290✔
2399
    return;
1,505✔
2400
  }
2401

2402
  return;
1,288,785✔
2403
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc