2151 lines
58 KiB
C
2151 lines
58 KiB
C
/*
|
|
* 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/>.
|
|
*/
|
|
|
|
#define __USE_XOPEN
|
|
|
|
#include "shellAuto.h"
|
|
#include "shellInt.h"
|
|
#include "shellTire.h"
|
|
#include "tthread.h"
|
|
|
|
//
|
|
// ------------- define area ---------------
|
|
//
|
|
#define UNION_ALL " union all "
|
|
|
|
// extern function
|
|
void shellClearScreen(int32_t ecmd_pos, int32_t cursor_pos);
|
|
void shellGetPrevCharSize(const char* str, int32_t pos, int32_t* size, int32_t* width);
|
|
void shellShowOnScreen(SShellCmd* cmd);
|
|
void shellInsertChar(SShellCmd* cmd, char* c, int size);
|
|
void shellInsertStr(SShellCmd* cmd, char* str, int size);
|
|
bool appendAfterSelect(TAOS* con, SShellCmd* cmd, char* p, int32_t len);
|
|
char* tireSearchWord(int type, char* pre);
|
|
bool updateTireValue(int type, bool autoFill);
|
|
|
|
typedef struct SAutoPtr {
|
|
STire* p;
|
|
int ref;
|
|
} SAutoPtr;
|
|
|
|
typedef struct SWord {
|
|
int type; // word type , see WT_ define
|
|
char* word;
|
|
int32_t len;
|
|
struct SWord* next;
|
|
bool free; // if true need free
|
|
} SWord;
|
|
|
|
typedef struct {
|
|
char* source;
|
|
int32_t source_len; // valid data length in source
|
|
int32_t count;
|
|
SWord* head;
|
|
// matched information
|
|
int32_t matchIndex; // matched word index in words
|
|
int32_t matchLen; // matched length at matched word
|
|
} SWords;
|
|
|
|
SWords shellCommands[] = {
|
|
{"alter database <db_name> <alter_db_options> <anyword> <alter_db_options> <anyword> <alter_db_options> <anyword> "
|
|
"<alter_db_options> <anyword> <alter_db_options> <anyword> ;",
|
|
0, 0, NULL},
|
|
{"alter dnode <dnode_id> \"resetlog\";", 0, 0, NULL},
|
|
{"alter dnode <dnode_id> \"debugFlag\" \"141\";", 0, 0, NULL},
|
|
{"alter dnode <dnode_id> \"monitor\" \"0\";", 0, 0, NULL},
|
|
{"alter dnode <dnode_id> \"monitor\" \"1\";", 0, 0, NULL},
|
|
{"alter dnode <dnode_id> \"asynclog\" \"0\";", 0, 0, NULL},
|
|
{"alter dnode <dnode_id> \"asynclog\" \"1\";", 0, 0, NULL},
|
|
{"alter all dnodes \"resetlog\";", 0, 0, NULL},
|
|
{"alter all dnodes \"debugFlag\" \"141\";", 0, 0, NULL},
|
|
{"alter all dnodes \"monitor\" \"0\";", 0, 0, NULL},
|
|
{"alter all dnodes \"monitor\" \"1\";", 0, 0, NULL},
|
|
{"alter table <tb_name> <tb_actions> <anyword> ;", 0, 0, NULL},
|
|
{"alter local \"resetlog\";", 0, 0, NULL},
|
|
{"alter local \"DebugFlag\" \"143\";", 0, 0, NULL},
|
|
{"alter local \"cDebugFlag\" \"143\";", 0, 0, NULL},
|
|
{"alter local \"uDebugFlag\" \"143\";", 0, 0, NULL},
|
|
{"alter local \"rpcDebugFlag\" \"143\";", 0, 0, NULL},
|
|
{"alter local \"tmrDebugFlag\" \"143\";", 0, 0, NULL},
|
|
{"alter local \"asynclog\" \"0\";", 0, 0, NULL},
|
|
{"alter local \"asynclog\" \"1\";", 0, 0, NULL},
|
|
{"alter topic", 0, 0, NULL},
|
|
{"alter user <user_name> <user_actions> <anyword> ;", 0, 0, NULL},
|
|
#ifdef TD_ENTERPRISE
|
|
{"balance vgroup ;", 0, 0, NULL},
|
|
{"balance vgroup leader on <vgroup_id>", 0, 0, NULL},
|
|
#endif
|
|
|
|
// 20
|
|
{"create table <anyword> using <stb_name> tags(", 0, 0, NULL},
|
|
{"create database <anyword> <db_options> <anyword> <db_options> <anyword> <db_options> <anyword> <db_options> "
|
|
"<anyword> <db_options> <anyword> <db_options> <anyword> <db_options> <anyword> <db_options> <anyword> "
|
|
"<db_options> <anyword> <db_options> <anyword> ;", 0, 0, NULL},
|
|
{"create dnode <anyword>", 0, 0, NULL},
|
|
{"create index <anyword> on <stb_name> ()", 0, 0, NULL},
|
|
{"create mnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"create qnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"create stream <anyword> into <anyword> as select", 0, 0, NULL}, // 26 append sub sql
|
|
{"create topic <anyword> as select", 0, 0, NULL}, // 27 append sub sql
|
|
{"create function <anyword> as <anyword> outputtype <data_types> language <udf_language>", 0, 0, NULL},
|
|
{"create or replace <anyword> as <anyword> outputtype <data_types> language <udf_language>", 0, 0, NULL},
|
|
{"create aggregate function <anyword> as <anyword> outputtype <data_types> bufsize <anyword> language <udf_language>", 0, 0, NULL},
|
|
{"create or replace aggregate function <anyword> as <anyword> outputtype <data_types> bufsize <anyword> language <udf_language>", 0, 0, NULL},
|
|
{"create user <anyword> pass <anyword> sysinfo 0;", 0, 0, NULL},
|
|
{"create user <anyword> pass <anyword> sysinfo 1;", 0, 0, NULL},
|
|
#ifdef TD_ENTERPRISE
|
|
{"create view <anyword> as select", 0, 0, NULL},
|
|
{"compact database <db_name>", 0, 0, NULL},
|
|
#endif
|
|
{"describe <all_table>", 0, 0, NULL},
|
|
{"delete from <all_table> where ", 0, 0, NULL},
|
|
{"drop database <db_name>", 0, 0, NULL},
|
|
{"drop index <anyword>", 0, 0, NULL},
|
|
{"drop table <all_table>", 0, 0, NULL},
|
|
{"drop dnode <dnode_id>", 0, 0, NULL},
|
|
{"drop mnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"drop qnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"drop user <user_name> ;", 0, 0, NULL},
|
|
// 40
|
|
{"drop function <udf_name> ;", 0, 0, NULL},
|
|
{"drop consumer group <anyword> on ", 0, 0, NULL},
|
|
{"drop topic <topic_name> ;", 0, 0, NULL},
|
|
{"drop stream <stream_name> ;", 0, 0, NULL},
|
|
{"explain select", 0, 0, NULL}, // 44 append sub sql
|
|
{"flush database <db_name> ;", 0, 0, NULL},
|
|
{"help;", 0, 0, NULL},
|
|
{"grant all on <anyword> to <user_name> ;", 0, 0, NULL},
|
|
{"grant read on <anyword> to <user_name> ;", 0, 0, NULL},
|
|
{"grant write on <anyword> to <user_name> ;", 0, 0, NULL},
|
|
{"kill connection <anyword> ;", 0, 0, NULL},
|
|
{"kill query ", 0, 0, NULL},
|
|
{"kill transaction ", 0, 0, NULL},
|
|
#ifdef TD_ENTERPRISE
|
|
{"merge vgroup <vgroup_id> <vgroup_id>", 0, 0, NULL},
|
|
#endif
|
|
{"pause stream <stream_name> ;", 0, 0, NULL},
|
|
#ifdef TD_ENTERPRISE
|
|
{"redistribute vgroup <vgroup_id> dnode <dnode_id> ;", 0, 0, NULL},
|
|
#endif
|
|
{"resume stream <stream_name> ;", 0, 0, NULL},
|
|
{"reset query cache;", 0, 0, NULL},
|
|
{"restore dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"restore vnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"restore mnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"restore qnode on dnode <dnode_id> ;", 0, 0, NULL},
|
|
{"revoke all on <anyword> from <user_name> ;", 0, 0, NULL},
|
|
{"revoke read on <anyword> from <user_name> ;", 0, 0, NULL},
|
|
{"revoke write on <anyword> from <user_name> ;", 0, 0, NULL},
|
|
{"select * from <all_table>", 0, 0, NULL},
|
|
{"select client_version();", 0, 0, NULL},
|
|
// 60
|
|
{"select current_user();", 0, 0, NULL},
|
|
{"select database();", 0, 0, NULL},
|
|
{"select server_version();", 0, 0, NULL},
|
|
{"select server_status();", 0, 0, NULL},
|
|
{"select now();", 0, 0, NULL},
|
|
{"select today();", 0, 0, NULL},
|
|
{"select timezone();", 0, 0, NULL},
|
|
{"set max_binary_display_width ", 0, 0, NULL},
|
|
{"show apps;", 0, 0, NULL},
|
|
{"show create database <db_name> \\G;", 0, 0, NULL},
|
|
{"show create stable <stb_name> \\G;", 0, 0, NULL},
|
|
{"show create table <tb_name> \\G;", 0, 0, NULL},
|
|
#ifdef TD_ENTERPRISE
|
|
{"show create view <all_table> \\G;", 0, 0, NULL},
|
|
#endif
|
|
{"show connections;", 0, 0, NULL},
|
|
{"show compact", 0, 0, NULL},
|
|
{"show compacts;", 0, 0, NULL},
|
|
{"show cluster;", 0, 0, NULL},
|
|
{"show cluster alive;", 0, 0, NULL},
|
|
{"show cluster machines;", 0, 0, NULL},
|
|
{"show databases;", 0, 0, NULL},
|
|
{"show dnodes;", 0, 0, NULL},
|
|
{"show dnode <dnode_id> variables;", 0, 0, NULL},
|
|
{"show functions;", 0, 0, NULL},
|
|
{"show licences;", 0, 0, NULL},
|
|
{"show mnodes;", 0, 0, NULL},
|
|
{"show queries;", 0, 0, NULL},
|
|
// 80
|
|
{"show query <anyword> ;", 0, 0, NULL},
|
|
{"show qnodes;", 0, 0, NULL},
|
|
{"show stables;", 0, 0, NULL},
|
|
{"show stables like ", 0, 0, NULL},
|
|
{"show streams;", 0, 0, NULL},
|
|
{"show scores;", 0, 0, NULL},
|
|
{"show snodes;", 0, 0, NULL},
|
|
{"show subscriptions;", 0, 0, NULL},
|
|
{"show tables;", 0, 0, NULL},
|
|
{"show tables like", 0, 0, NULL},
|
|
{"show table distributed <all_table>", 0, 0, NULL},
|
|
{"show tags from <tb_name>", 0, 0, NULL},
|
|
{"show tags from <db_name>", 0, 0, NULL},
|
|
{"show table tags from <all_table>", 0, 0, NULL},
|
|
{"show topics;", 0, 0, NULL},
|
|
{"show transactions;", 0, 0, NULL},
|
|
{"show users;", 0, 0, NULL},
|
|
{"show variables;", 0, 0, NULL},
|
|
{"show local variables;", 0, 0, NULL},
|
|
{"show vnodes <dnode_id>", 0, 0, NULL},
|
|
{"show vgroups;", 0, 0, NULL},
|
|
{"show consumers;", 0, 0, NULL},
|
|
{"show grants;", 0, 0, NULL},
|
|
{"show grants full;", 0, 0, NULL},
|
|
{"show grants logs;", 0, 0, NULL},
|
|
#ifdef TD_ENTERPRISE
|
|
{"show views;", 0, 0, NULL},
|
|
{"split vgroup <vgroup_id>", 0, 0, NULL},
|
|
#endif
|
|
{"insert into <tb_name> values(", 0, 0, NULL},
|
|
{"insert into <tb_name> using <stb_name> tags(", 0, 0, NULL},
|
|
{"insert into <tb_name> using <stb_name> <anyword> values(", 0, 0, NULL},
|
|
{"insert into <tb_name> file ", 0, 0, NULL},
|
|
{"trim database <db_name>", 0, 0, NULL},
|
|
{"s3migrate database <db_name>", 0, 0, NULL},
|
|
{"use <db_name>", 0, 0, NULL},
|
|
{"quit", 0, 0, NULL}};
|
|
|
|
char* keywords[] = {
|
|
"and ", "asc ", "desc ", "from ", "fill(", "limit ", "where ",
|
|
"interval(", "order by ", "order by ", "offset ", "or ", "group by ", "now()",
|
|
"session(", "sliding ", "slimit ", "soffset ", "state_window(", "today() ", "union all select ",
|
|
"partition by "};
|
|
|
|
char* functions[] = {
|
|
"count(", "sum(",
|
|
"avg(", "last(",
|
|
"last_row(", "top(",
|
|
"interp(", "max(",
|
|
"min(", "now()",
|
|
"today()", "percentile(",
|
|
"tail(", "pow(",
|
|
"abs(", "atan(",
|
|
"acos(", "asin(",
|
|
"apercentile(", "bottom(",
|
|
"cast(", "ceil(",
|
|
"char_length(", "cos(",
|
|
"concat(", "concat_ws(",
|
|
"csum(", "diff(",
|
|
"derivative(", "elapsed(",
|
|
"first(", "floor(",
|
|
"hyperloglog(", "histogram(",
|
|
"irate(", "leastsquares(",
|
|
"length(", "log(",
|
|
"lower(", "ltrim(",
|
|
"mavg(", "mode(",
|
|
"tan(", "round(",
|
|
"rtrim(", "sample(",
|
|
"sin(", "spread(",
|
|
"substr(", "statecount(",
|
|
"stateduration(", "stddev(",
|
|
"sqrt(", "timediff(",
|
|
"timezone(", "timetruncate(",
|
|
"twa(", "to_unixtimestamp(",
|
|
"unique(", "upper(",
|
|
};
|
|
|
|
char* tb_actions[] = {
|
|
"add column ", "modify column ", "drop column ", "rename column ", "add tag ",
|
|
"modify tag ", "drop tag ", "rename tag ", "set tag ",
|
|
};
|
|
|
|
char* user_actions[] = {"pass ", "enable ", "sysinfo "};
|
|
|
|
char* tb_options[] = {"comment ", "watermark ", "max_delay ", "ttl ", "rollup(", "sma("};
|
|
|
|
char* db_options[] = {"keep ",
|
|
"replica ",
|
|
"precision ",
|
|
"strict ",
|
|
"buffer ",
|
|
"cachemodel ",
|
|
"cachesize ",
|
|
"comp ",
|
|
"duration ",
|
|
"wal_fsync_period",
|
|
"maxrows ",
|
|
"minrows ",
|
|
"pages ",
|
|
"pagesize ",
|
|
"retentions ",
|
|
"wal_level ",
|
|
"vgroups ",
|
|
"single_stable ",
|
|
"s3_chunksize ",
|
|
"s3_keeplocal ",
|
|
"s3_compact ",
|
|
"wal_retention_period ",
|
|
"wal_roll_period ",
|
|
"wal_retention_size ",
|
|
"wal_segment_size "};
|
|
|
|
char* alter_db_options[] = {"cachemodel ", "replica ", "keep ", "stt_trigger ",
|
|
"wal_retention_period ", "wal_retention_size ", "cachesize ",
|
|
"s3_keeplocal ", "s3_compact ",
|
|
"wal_fsync_period ", "buffer ", "pages " ,"wal_level "};
|
|
|
|
char* data_types[] = {"timestamp", "int",
|
|
"int unsigned", "varchar(16)",
|
|
"float", "double",
|
|
"binary(16)", "nchar(16)",
|
|
"bigint", "bigint unsigned",
|
|
"smallint", "smallint unsigned",
|
|
"tinyint", "tinyint unsigned",
|
|
"bool", "json"};
|
|
|
|
char* key_tags[] = {"tags("};
|
|
|
|
char* key_select[] = {"select "};
|
|
|
|
char* key_systable[] = {
|
|
"ins_dnodes", "ins_mnodes", "ins_modules", "ins_qnodes", "ins_snodes", "ins_cluster",
|
|
"ins_databases", "ins_functions", "ins_indexes", "ins_stables", "ins_tables", "ins_tags",
|
|
"ins_users", "ins_grants", "ins_vgroups", "ins_configs", "ins_dnode_variables", "ins_topics",
|
|
"ins_subscriptions", "ins_streams", "ins_stream_tasks", "ins_vnodes", "ins_user_privileges", "perf_connections",
|
|
"perf_queries", "perf_consumers", "perf_trans", "perf_apps"};
|
|
|
|
char* udf_language[] = {"\'Python\'", "\'C\'"};
|
|
|
|
// global keys can tips on anywhere
|
|
char* global_keys[] = {
|
|
"tbname",
|
|
"now",
|
|
"_wstart",
|
|
"_wend",
|
|
"_wduration",
|
|
"_qstart",
|
|
"_qend",
|
|
"_qduration",
|
|
"_qtag",
|
|
"_isfilled"
|
|
};
|
|
|
|
//
|
|
// ------- global variant define ---------
|
|
//
|
|
int32_t firstMatchIndex = -1; // first match shellCommands index
|
|
int32_t lastMatchIndex = -1; // last match shellCommands index
|
|
int32_t curMatchIndex = -1; // current match shellCommands index
|
|
int32_t lastWordBytes = -1; // printShow last word length
|
|
bool waitAutoFill = false;
|
|
|
|
//
|
|
// ----------- global var array define -----------
|
|
//
|
|
#define WT_VAR_DBNAME 0
|
|
#define WT_VAR_STABLE 1
|
|
#define WT_VAR_TABLE 2
|
|
#define WT_VAR_DNODEID 3
|
|
#define WT_VAR_USERNAME 4
|
|
#define WT_VAR_TOPIC 5
|
|
#define WT_VAR_STREAM 6
|
|
#define WT_VAR_UDFNAME 7
|
|
#define WT_VAR_VGROUPID 8
|
|
|
|
#define WT_FROM_DB_MAX 8 // max get content from db
|
|
#define WT_FROM_DB_CNT (WT_FROM_DB_MAX + 1)
|
|
|
|
#define WT_VAR_ALLTABLE 9
|
|
#define WT_VAR_FUNC 10
|
|
#define WT_VAR_KEYWORD 11
|
|
#define WT_VAR_TBACTION 12
|
|
#define WT_VAR_DBOPTION 13
|
|
#define WT_VAR_ALTER_DBOPTION 14
|
|
#define WT_VAR_DATATYPE 15
|
|
#define WT_VAR_KEYTAGS 16
|
|
#define WT_VAR_ANYWORD 17
|
|
#define WT_VAR_TBOPTION 18
|
|
#define WT_VAR_USERACTION 19
|
|
#define WT_VAR_KEYSELECT 20
|
|
#define WT_VAR_SYSTABLE 21
|
|
#define WT_VAR_LANGUAGE 22
|
|
#define WT_VAR_GLOBALKEYS 23
|
|
|
|
#define WT_VAR_CNT 24
|
|
|
|
|
|
#define WT_TEXT 0xFF
|
|
|
|
char dbName[256] = ""; // save use database name;
|
|
// tire array
|
|
STire* tires[WT_VAR_CNT];
|
|
TdThreadMutex tiresMutex;
|
|
// save thread handle obtain var name from db server
|
|
TdThread* threads[WT_FROM_DB_CNT];
|
|
// obtain var name with sql from server
|
|
char varTypes[WT_VAR_CNT][64] = {
|
|
"<db_name>", "<stb_name>", "<tb_name>", "<dnode_id>", "<user_name>", "<topic_name>", "<stream_name>",
|
|
"<udf_name>", "<vgroup_id>", "<all_table>", "<function>", "<keyword>", "<tb_actions>", "<db_options>", "<alter_db_options>",
|
|
"<data_types>", "<key_tags>", "<anyword>", "<tb_options>", "<user_actions>", "<key_select>", "<sys_table>", "<udf_language>"};
|
|
|
|
char varSqls[WT_FROM_DB_CNT][64] = {"show databases;", "show stables;", "show tables;", "show dnodes;",
|
|
"show users;", "show topics;", "show streams;", "show functions;", "show vgroups;"};
|
|
|
|
// var words current cursor, if user press any one key except tab, cursorVar can be reset to -1
|
|
int cursorVar = -1;
|
|
bool varMode = false; // enter var names list mode
|
|
|
|
TAOS* varCon = NULL;
|
|
SShellCmd* varCmd = NULL;
|
|
bool varRunOnce = false;
|
|
SMatch* lastMatch = NULL; // save last match result
|
|
int cntDel = 0; // delete byte count after next press tab
|
|
|
|
// show auto tab introduction
|
|
void printfIntroduction(bool community) {
|
|
printf(" ********************************* Tab Completion *************************************\n");
|
|
char secondLine[160] = "\0";
|
|
sprintf(secondLine, " * The %s CLI supports tab completion for a variety of items, ", shell.info.cusName);
|
|
printf("%s", secondLine);
|
|
int secondLineLen = strlen(secondLine);
|
|
while (89 - (secondLineLen++) > 0) {
|
|
printf(" ");
|
|
}
|
|
printf("*\n");
|
|
printf(" * including database names, table names, function names and keywords. *\n");
|
|
printf(" * The full list of shortcut keys is as follows: *\n");
|
|
printf(" * [ TAB ] ...... complete the current word *\n");
|
|
printf(" * ...... if used on a blank line, display all supported commands *\n");
|
|
printf(" * [ Ctrl + A ] ...... move cursor to the st[A]rt of the line *\n");
|
|
printf(" * [ Ctrl + E ] ...... move cursor to the [E]nd of the line *\n");
|
|
printf(" * [ Ctrl + W ] ...... move cursor to the middle of the line *\n");
|
|
printf(" * [ Ctrl + L ] ...... clear the entire screen *\n");
|
|
printf(" * [ Ctrl + K ] ...... clear the screen after the cursor *\n");
|
|
printf(" * [ Ctrl + U ] ...... clear the screen before the cursor *\n");
|
|
if(community) {
|
|
printf(" * ------------------------------------------------------------------------------------ *\n");
|
|
printf(" * You are using TDengine OSS. To experience advanced features, like backup/restore, *\n");
|
|
printf(" * privilege control and more, or receive 7x24 technical support, try TDengine *\n");
|
|
printf(" * Enterprise or TDengine Cloud. Learn more at https://tdengine.com *\n");
|
|
}
|
|
printf(" ****************************************************************************************\n\n");
|
|
}
|
|
|
|
// show enterprise AD
|
|
void showAD(bool end) {
|
|
printf(" You are using TDengine OSS. To experience advanced features, like backup/restore, \n");
|
|
printf(" privilege control and more, or receive 7x24 technical support, try TDengine Enterprise \n");
|
|
printf(" or TDengine Cloud. Learn more at https://tdengine.com \n");
|
|
printf(" \n");
|
|
}
|
|
|
|
void showHelp() {
|
|
printf("\nThe %s CLI supports the following commands:", shell.info.cusName);
|
|
printf(
|
|
"\n\
|
|
----- A ----- \n\
|
|
alter database <db_name> <db_options> \n\
|
|
alter dnode <dnode_id> 'resetlog';\n\
|
|
alter dnode <dnode_id> 'monitor' '0';\n\
|
|
alter dnode <dnode_id> 'monitor' \"1\";\n\
|
|
alter dnode <dnode_id> \"debugflag\" \"143\";\n\
|
|
alter dnode <dnode_id> 'asynclog' '0';\n\
|
|
alter dnode <dnode_id> 'asynclog' \"1\";\n\
|
|
alter all dnodes \"monitor\" \"0\";\n\
|
|
alter all dnodes \"monitor\" \"1\";\n\
|
|
alter all dnodes \"resetlog\";\n\
|
|
alter all dnodes \"debugFlag\" \n\
|
|
alter all dnodes \"asynclog\" \"0\";\n\
|
|
alter all dnodes \"asynclog\" \"1\";\n\
|
|
alter table <tb_name> <tb_actions> ;\n\
|
|
alter local \"resetlog\";\n\
|
|
alter local \"DebugFlag\" \"143\";\n\
|
|
alter local \"asynclog\" \"0\";\n\
|
|
alter local \"asynclog\" \"1\";\n\
|
|
alter topic\n\
|
|
alter user <user_name> <user_actions> ...\n\
|
|
----- C ----- \n\
|
|
create table <tb_name> using <stb_name> tags ...\n\
|
|
create database <db_name> <db_options> ...\n\
|
|
create dnode \"fqdn:port\" ...\n\
|
|
create index <index_name> on <stb_name> (tag_column_name);\n\
|
|
create mnode on dnode <dnode_id> ;\n\
|
|
create qnode on dnode <dnode_id> ;\n\
|
|
create stream <stream_name> into <stb_name> as select ...\n\
|
|
create topic <topic_name> as select ...\n\
|
|
create function <udf_name> as <file_name> outputtype <data_types> language \'C\' | \'Python\' ;\n\
|
|
create aggregate function <udf_name> as <file_name> outputtype <data_types> bufsize <bufsize_bytes> language \'C\' | \'Python\';\n\
|
|
create user <user_name> pass <password> ...\n\
|
|
----- D ----- \n\
|
|
describe <all_table>\n\
|
|
delete from <all_table> where ...\n\
|
|
drop database <db_name>;\n\
|
|
drop table <all_table>;\n\
|
|
drop dnode <dnode_id>;\n\
|
|
drop mnode on dnode <dnode_id> ;\n\
|
|
drop qnode on dnode <dnode_id> ;\n\
|
|
drop user <user_name> ;\n\
|
|
drop function <udf_name>;\n\
|
|
drop consumer group ... \n\
|
|
drop topic <topic_name> ;\n\
|
|
drop stream <stream_name> ;\n\
|
|
drop index <index_name>;\n\
|
|
----- E ----- \n\
|
|
explain select clause ...\n\
|
|
----- F ----- \n\
|
|
flush database <db_name>;\n\
|
|
----- H ----- \n\
|
|
help;\n\
|
|
----- I ----- \n\
|
|
insert into <tb_name> values(...) ;\n\
|
|
insert into <tb_name> using <stb_name> tags(...) values(...) ;\n\
|
|
----- G ----- \n\
|
|
grant all on <priv_level> to <user_name> ;\n\
|
|
grant read on <priv_level> to <user_name> ;\n\
|
|
grant write on <priv_level> to <user_name> ;\n\
|
|
----- K ----- \n\
|
|
kill connection <connection_id>; \n\
|
|
kill query <query_id>; \n\
|
|
kill transaction <transaction_id>;\n\
|
|
----- P ----- \n\
|
|
pause stream <stream_name>;\n\
|
|
----- R ----- \n\
|
|
resume stream <stream_name>;\n\
|
|
reset query cache;\n\
|
|
restore dnode <dnode_id> ;\n\
|
|
restore vnode on dnode <dnode_id> ;\n\
|
|
restore mnode on dnode <dnode_id> ;\n\
|
|
restore qnode on dnode <dnode_id> ;\n\
|
|
revoke all on <priv_level> from <user_name> ;\n\
|
|
revoke read on <priv_level> from <user_name> ;\n\
|
|
revoke write on <priv_level> from <user_name> ;\n\
|
|
----- S ----- \n\
|
|
select * from <all_table> where ... \n\
|
|
select client_version();\n\
|
|
select current_user();\n\
|
|
select database();\n\
|
|
select server_version();\n\
|
|
select server_status();\n\
|
|
select now();\n\
|
|
select today();\n\
|
|
select timezone();\n\
|
|
set max_binary_display_width ...\n\
|
|
show apps;\n\
|
|
show create database <db_name>;\n\
|
|
show create stable <stb_name>;\n\
|
|
show create table <tb_name>;\n\
|
|
show connections;\n\
|
|
show cluster;\n\
|
|
show cluster alive;\n\
|
|
show cluster machines;\n\
|
|
show databases;\n\
|
|
show dnodes;\n\
|
|
show dnode <dnode_id> variables;\n\
|
|
show functions;\n\
|
|
show licences;\n\
|
|
show mnodes;\n\
|
|
show queries;\n\
|
|
show query <query_id> ;\n\
|
|
show qnodes;\n\
|
|
show snodes;\n\
|
|
show stables;\n\
|
|
show stables like \n\
|
|
show streams;\n\
|
|
show scores;\n\
|
|
show subscriptions;\n\
|
|
show tables;\n\
|
|
show tables like\n\
|
|
show table distributed <all_table>;\n\
|
|
show tags from <tb_name>\n\
|
|
show tags from <db_name>\n\
|
|
show table tags from <all_table>\n\
|
|
show topics;\n\
|
|
show transactions;\n\
|
|
show users;\n\
|
|
show variables;\n\
|
|
show local variables;\n\
|
|
show vnodes <dnode_id>\n\
|
|
show vgroups;\n\
|
|
show consumers;\n\
|
|
show grants;\n\
|
|
show grants full;\n\
|
|
show grants logs;\n\
|
|
----- T ----- \n\
|
|
trim database <db_name>;\n\
|
|
----- U ----- \n\
|
|
use <db_name>;");
|
|
|
|
#ifdef TD_ENTERPRISE
|
|
printf(
|
|
"\n\n\
|
|
----- special commands on enterpise version ----- \n\
|
|
balance vgroup ;\n\
|
|
balance vgroup leader on <vgroup_id> \n\
|
|
compact database <db_name>; \n\
|
|
crate view <view_name> as select ...\n\
|
|
redistribute vgroup <vgroup_id> dnode <dnode_id> ;\n\
|
|
split vgroup <vgroup_id>;\n\
|
|
show compacts;\n\
|
|
show compact \n\
|
|
show views;\n\
|
|
show create view <all_table>;");
|
|
#endif
|
|
|
|
printf("\n\n");
|
|
// define in getDuration() function
|
|
printf(
|
|
"\
|
|
Timestamp expression Format:\n\
|
|
b - nanosecond \n\
|
|
u - microsecond \n\
|
|
a - millisecond \n\
|
|
s - second \n\
|
|
m - minute \n\
|
|
h - hour \n\
|
|
d - day \n\
|
|
w - week \n\
|
|
now - current time \n\
|
|
Example : \n\
|
|
select * from t1 where ts > now - 2w + 3d and ts <= now - 1w -2h ;\n");
|
|
printf("\n");
|
|
}
|
|
|
|
//
|
|
// ------------------- parse words --------------------------
|
|
//
|
|
|
|
#define SHELL_COMMAND_COUNT() (sizeof(shellCommands) / sizeof(SWords))
|
|
|
|
// get at
|
|
SWord* atWord(SWords* command, int32_t index) {
|
|
SWord* word = command->head;
|
|
for (int32_t i = 0; i < index; i++) {
|
|
if (word == NULL) return NULL;
|
|
word = word->next;
|
|
}
|
|
|
|
return word;
|
|
}
|
|
|
|
#define MATCH_WORD(x) atWord(x, x->matchIndex)
|
|
|
|
int wordType(const char* p, int32_t len) {
|
|
for (int i = 0; i < WT_VAR_CNT; i++) {
|
|
if (strncmp(p, varTypes[i], len) == 0) return i;
|
|
}
|
|
return WT_TEXT;
|
|
}
|
|
|
|
// add word
|
|
SWord* addWord(const char* p, int32_t len, bool pattern) {
|
|
SWord* word = (SWord*)taosMemoryMalloc(sizeof(SWord));
|
|
memset(word, 0, sizeof(SWord));
|
|
word->word = (char*)p;
|
|
word->len = len;
|
|
|
|
// check format
|
|
if (pattern && len > 0) {
|
|
word->type = wordType(p, len);
|
|
} else {
|
|
word->type = WT_TEXT;
|
|
}
|
|
|
|
return word;
|
|
}
|
|
|
|
// parse one command
|
|
void parseCommand(SWords* command, bool pattern) {
|
|
char* p = command->source;
|
|
int32_t start = 0;
|
|
int32_t size = command->source_len > 0 ? command->source_len : strlen(p);
|
|
|
|
bool lastBlank = false;
|
|
for (int i = 0; i <= size; i++) {
|
|
if (p[i] == ' ' || i == size) {
|
|
// check continue blank like ' '
|
|
if (p[i] == ' ') {
|
|
if (lastBlank) {
|
|
start++;
|
|
continue;
|
|
}
|
|
if (i == 0) { // first blank
|
|
lastBlank = true;
|
|
start++;
|
|
continue;
|
|
}
|
|
lastBlank = true;
|
|
}
|
|
|
|
// found split or string end , append word
|
|
if (command->head == NULL) {
|
|
command->head = addWord(p + start, i - start, pattern);
|
|
command->count = 1;
|
|
} else {
|
|
SWord* word = command->head;
|
|
while (word->next) {
|
|
word = word->next;
|
|
}
|
|
word->next = addWord(p + start, i - start, pattern);
|
|
command->count++;
|
|
}
|
|
start = i + 1;
|
|
} else {
|
|
lastBlank = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// free SShellCmd
|
|
void freeCommand(SWords* command) {
|
|
SWord* item = command->head;
|
|
command->head = NULL;
|
|
// loop
|
|
while (item) {
|
|
SWord* tmp = item;
|
|
item = item->next;
|
|
// if malloc need free
|
|
if (tmp->free && tmp->word) taosMemoryFree(tmp->word);
|
|
taosMemoryFree(tmp);
|
|
}
|
|
}
|
|
|
|
void GenerateVarType(int type, char** p, int count) {
|
|
STire* tire = createTire(TIRE_LIST);
|
|
for (int i = 0; i < count; i++) {
|
|
insertWord(tire, p[i]);
|
|
}
|
|
|
|
taosThreadMutexLock(&tiresMutex);
|
|
tires[type] = tire;
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
}
|
|
|
|
//
|
|
// -------------------- shell auto ----------------
|
|
//
|
|
|
|
// init shell auto function , shell start call once
|
|
bool shellAutoInit() {
|
|
// command
|
|
int32_t count = SHELL_COMMAND_COUNT();
|
|
for (int32_t i = 0; i < count; i++) {
|
|
parseCommand(shellCommands + i, true);
|
|
}
|
|
|
|
// tires
|
|
memset(tires, 0, sizeof(STire*) * WT_VAR_CNT);
|
|
taosThreadMutexInit(&tiresMutex, NULL);
|
|
|
|
// threads
|
|
memset(threads, 0, sizeof(TdThread*) * WT_FROM_DB_CNT);
|
|
|
|
// generate varType
|
|
GenerateVarType(WT_VAR_FUNC, functions, sizeof(functions) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_KEYWORD, keywords, sizeof(keywords) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_TBACTION, tb_actions, sizeof(tb_actions) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_DBOPTION, db_options, sizeof(db_options) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_ALTER_DBOPTION, alter_db_options, sizeof(alter_db_options) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_DATATYPE, data_types, sizeof(data_types) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_KEYTAGS, key_tags, sizeof(key_tags) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_TBOPTION, tb_options, sizeof(tb_options) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_USERACTION, user_actions, sizeof(user_actions) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_KEYSELECT, key_select, sizeof(key_select) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_SYSTABLE, key_systable, sizeof(key_systable) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_LANGUAGE, udf_language, sizeof(udf_language) / sizeof(char*));
|
|
GenerateVarType(WT_VAR_GLOBALKEYS, global_keys, sizeof(global_keys) / sizeof(char*));
|
|
|
|
return true;
|
|
}
|
|
|
|
// set conn
|
|
void shellSetConn(TAOS* conn, bool runOnce) {
|
|
varCon = conn;
|
|
varRunOnce = runOnce;
|
|
// init database and stable
|
|
if (!runOnce) updateTireValue(WT_VAR_DBNAME, false);
|
|
}
|
|
|
|
// exit shell auto function, shell exit call once
|
|
void shellAutoExit() {
|
|
// free command
|
|
int32_t count = SHELL_COMMAND_COUNT();
|
|
for (int32_t i = 0; i < count; i++) {
|
|
freeCommand(shellCommands + i);
|
|
}
|
|
|
|
// free tires
|
|
taosThreadMutexLock(&tiresMutex);
|
|
for (int32_t i = 0; i < WT_VAR_CNT; i++) {
|
|
if (tires[i]) {
|
|
freeTire(tires[i]);
|
|
tires[i] = NULL;
|
|
}
|
|
}
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
// destroy
|
|
taosThreadMutexDestroy(&tiresMutex);
|
|
|
|
// free threads
|
|
for (int32_t i = 0; i < WT_FROM_DB_CNT; i++) {
|
|
if (threads[i]) {
|
|
taosDestroyThread(threads[i]);
|
|
threads[i] = NULL;
|
|
}
|
|
}
|
|
|
|
// free lastMatch
|
|
if (lastMatch) {
|
|
freeMatch(lastMatch);
|
|
lastMatch = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// ------------------- auto ptr for tires --------------------------
|
|
//
|
|
bool setNewAutoPtr(int type, STire* pNew) {
|
|
if (pNew == NULL) return false;
|
|
|
|
taosThreadMutexLock(&tiresMutex);
|
|
STire* pOld = tires[type];
|
|
if (pOld != NULL) {
|
|
// previous have value, release self ref count
|
|
if (--pOld->ref == 0) {
|
|
freeTire(pOld);
|
|
}
|
|
}
|
|
|
|
// set new
|
|
tires[type] = pNew;
|
|
tires[type]->ref = 1;
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return true;
|
|
}
|
|
|
|
// get ptr
|
|
STire* getAutoPtr(int type) {
|
|
if (tires[type] == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
taosThreadMutexLock(&tiresMutex);
|
|
tires[type]->ref++;
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return tires[type];
|
|
}
|
|
|
|
// put back tire to tires[type], if tire not equal tires[type].p, need free tire
|
|
void putBackAutoPtr(int type, STire* tire) {
|
|
if (tire == NULL) {
|
|
return;
|
|
}
|
|
|
|
taosThreadMutexLock(&tiresMutex);
|
|
if (tires[type] != tire) {
|
|
// update by out, can't put back , so free
|
|
if (--tire->ref == 1) {
|
|
// support multi thread getAutoPtr
|
|
freeTire(tire);
|
|
}
|
|
|
|
} else {
|
|
tires[type]->ref--;
|
|
ASSERT(tires[type]->ref > 0);
|
|
}
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// ------------------- var Word --------------------------
|
|
//
|
|
|
|
#define MAX_CACHED_CNT 100000 // max cached rows 10w
|
|
// write sql result to var name, return write rows cnt
|
|
int writeVarNames(int type, TAOS_RES* tres) {
|
|
// fetch row
|
|
TAOS_ROW row = taos_fetch_row(tres);
|
|
if (row == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
TAOS_FIELD* fields = taos_fetch_fields(tres);
|
|
// create new tires
|
|
char tireType = type == WT_VAR_TABLE ? TIRE_TREE : TIRE_LIST;
|
|
STire* tire = createTire(tireType);
|
|
|
|
// enum rows
|
|
char name[1024];
|
|
int numOfRows = 0;
|
|
do {
|
|
int32_t* lengths = taos_fetch_lengths(tres);
|
|
int32_t bytes = lengths[0];
|
|
if (fields[0].type == TSDB_DATA_TYPE_INT) {
|
|
sprintf(name, "%d", *(int16_t*)row[0]);
|
|
} else {
|
|
memcpy(name, row[0], bytes);
|
|
}
|
|
|
|
name[bytes] = 0; // set string end
|
|
// insert to tire
|
|
insertWord(tire, name);
|
|
|
|
if (++numOfRows > MAX_CACHED_CNT) {
|
|
break;
|
|
}
|
|
|
|
row = taos_fetch_row(tres);
|
|
} while (row != NULL);
|
|
|
|
// replace old tire
|
|
setNewAutoPtr(type, tire);
|
|
|
|
return numOfRows;
|
|
}
|
|
|
|
void setThreadNull(int type) {
|
|
taosThreadMutexLock(&tiresMutex);
|
|
if (threads[type]) {
|
|
taosMemoryFree(threads[type]);
|
|
}
|
|
threads[type] = NULL;
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
}
|
|
|
|
bool firstMatchCommand(TAOS* con, SShellCmd* cmd);
|
|
//
|
|
// thread obtain var thread from db server
|
|
//
|
|
void* varObtainThread(void* param) {
|
|
int type = *(int*)param;
|
|
taosMemoryFree(param);
|
|
|
|
if (varCon == NULL || type > WT_FROM_DB_MAX) {
|
|
return NULL;
|
|
}
|
|
|
|
TAOS_RES* pSql = taos_query(varCon, varSqls[type]);
|
|
if (taos_errno(pSql)) {
|
|
taos_free_result(pSql);
|
|
setThreadNull(type);
|
|
return NULL;
|
|
}
|
|
|
|
// write var names from pSql
|
|
int cnt = writeVarNames(type, pSql);
|
|
|
|
// free sql
|
|
taos_free_result(pSql);
|
|
|
|
// check need call auto tab
|
|
if (cnt > 0 && waitAutoFill) {
|
|
// press tab key by program
|
|
firstMatchCommand(varCon, varCmd);
|
|
}
|
|
|
|
setThreadNull(type);
|
|
return NULL;
|
|
}
|
|
|
|
// return true is need update value by async
|
|
bool updateTireValue(int type, bool autoFill) {
|
|
// TYPE CONTEXT GET FROM DB
|
|
taosThreadMutexLock(&tiresMutex);
|
|
|
|
// check need obtain from server
|
|
if (tires[type] == NULL) {
|
|
waitAutoFill = autoFill;
|
|
// need async obtain var names from db sever
|
|
if (threads[type] != NULL) {
|
|
if (taosThreadRunning(threads[type])) {
|
|
// thread running , need not obtain again, return
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
return NULL;
|
|
}
|
|
// destroy previous thread handle for new create thread handle
|
|
taosDestroyThread(threads[type]);
|
|
threads[type] = NULL;
|
|
}
|
|
|
|
// create new
|
|
void* param = taosMemoryMalloc(sizeof(int));
|
|
*((int*)param) = type;
|
|
threads[type] = taosCreateThread(varObtainThread, param);
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
return true;
|
|
}
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return false;
|
|
}
|
|
|
|
// only match next one word from all match words, return valuue must free by caller
|
|
char* matchNextPrefix(STire* tire, char* pre) {
|
|
SMatch* match = NULL;
|
|
if (tire == NULL) return NULL;
|
|
|
|
// re-use last result
|
|
if (lastMatch) {
|
|
if (strcmp(pre, lastMatch->pre) == 0) {
|
|
// same pre
|
|
match = lastMatch;
|
|
}
|
|
}
|
|
|
|
if (match == NULL) {
|
|
// not same with last result
|
|
if (pre[0] == 0) {
|
|
// EMPTY PRE
|
|
match = enumAll(tire);
|
|
} else {
|
|
// NOT EMPTY
|
|
match = (SMatch*)taosMemoryMalloc(sizeof(SMatch));
|
|
memset(match, 0, sizeof(SMatch));
|
|
matchPrefix(tire, pre, match);
|
|
}
|
|
|
|
// save to lastMatch
|
|
if (match) {
|
|
if (lastMatch) freeMatch(lastMatch);
|
|
lastMatch = match;
|
|
}
|
|
}
|
|
|
|
// check valid
|
|
if (match == NULL || match->head == NULL) {
|
|
// no one matched
|
|
return NULL;
|
|
}
|
|
|
|
if (cursorVar == -1) {
|
|
// first
|
|
cursorVar = 0;
|
|
return taosStrdup(match->head->word);
|
|
}
|
|
|
|
// according to cursorVar , calculate next one
|
|
int i = 0;
|
|
SMatchNode* item = match->head;
|
|
while (item) {
|
|
if (i == cursorVar + 1) {
|
|
// found next position ok
|
|
if (item->next == NULL) {
|
|
// match last item, reset cursorVar to head
|
|
cursorVar = -1;
|
|
} else {
|
|
cursorVar = i;
|
|
}
|
|
|
|
return taosStrdup(item->word);
|
|
}
|
|
|
|
// check end item
|
|
if (item->next == NULL) {
|
|
// if cursorVar > var list count, return last and reset cursorVar
|
|
cursorVar = -1;
|
|
|
|
return taosStrdup(item->word);
|
|
}
|
|
|
|
// move next
|
|
item = item->next;
|
|
i++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// search pre word from tire tree, return value must free by caller
|
|
char* tireSearchWord(int type, char* pre) {
|
|
if (type == WT_TEXT) {
|
|
return NULL;
|
|
}
|
|
|
|
if (type > WT_FROM_DB_MAX) {
|
|
// NOT FROM DB , tires[type] alwary not null
|
|
STire* tire = tires[type];
|
|
if (tire == NULL) return NULL;
|
|
return matchNextPrefix(tire, pre);
|
|
}
|
|
|
|
if (updateTireValue(type, true)) {
|
|
return NULL;
|
|
}
|
|
|
|
// can obtain var names from local
|
|
STire* tire = getAutoPtr(type);
|
|
if (tire == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
char* str = matchNextPrefix(tire, pre);
|
|
// used finish, put back pointer to autoptr array
|
|
putBackAutoPtr(type, tire);
|
|
|
|
return str;
|
|
}
|
|
|
|
// match var word, word1 is pattern , word2 is input from shell
|
|
bool matchVarWord(SWord* word1, SWord* word2) {
|
|
// search input word from tire tree
|
|
char pre[512];
|
|
memcpy(pre, word2->word, word2->len);
|
|
pre[word2->len] = 0;
|
|
|
|
char* str = NULL;
|
|
if (word1->type == WT_VAR_ALLTABLE) {
|
|
// ALL_TABLE
|
|
str = tireSearchWord(WT_VAR_STABLE, pre);
|
|
if (str == NULL) {
|
|
str = tireSearchWord(WT_VAR_TABLE, pre);
|
|
if (str == NULL) return false;
|
|
}
|
|
} else {
|
|
// OTHER
|
|
str = tireSearchWord(word1->type, pre);
|
|
if (str == NULL) {
|
|
// not found or word1->type variable list not obtain from server, return not match
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// free previous malloc
|
|
if (word1->free && word1->word) {
|
|
taosMemoryFree(word1->word);
|
|
}
|
|
|
|
// save
|
|
word1->word = str;
|
|
word1->len = strlen(str);
|
|
word1->free = true; // need free
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// ------------------- match words --------------------------
|
|
//
|
|
|
|
// compare command cmdPattern come from shellCommands , cmdInput come from user input
|
|
int32_t compareCommand(SWords* cmdPattern, SWords* cmdInput) {
|
|
SWord* wordPattern = cmdPattern->head;
|
|
SWord* wordInput = cmdInput->head;
|
|
|
|
if (wordPattern == NULL || wordInput == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
for (int32_t i = 0; i < cmdPattern->count; i++) {
|
|
if (wordPattern->type == WT_TEXT) {
|
|
// WT_TEXT match
|
|
if (wordPattern->len == wordInput->len) {
|
|
if (strncasecmp(wordPattern->word, wordInput->word, wordPattern->len) != 0) return -1;
|
|
} else if (wordPattern->len < wordInput->len) {
|
|
return -1;
|
|
} else {
|
|
// wordPattern->len > wordInput->len
|
|
if (strncasecmp(wordPattern->word, wordInput->word, wordInput->len) == 0) {
|
|
if (i + 1 == cmdInput->count) {
|
|
// last word return match
|
|
cmdPattern->matchIndex = i;
|
|
cmdPattern->matchLen = wordInput->len;
|
|
return i;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
} else {
|
|
// WT_VAR auto match any one word
|
|
if (wordInput->next == NULL) { // input words last one
|
|
if (matchVarWord(wordPattern, wordInput)) {
|
|
cmdPattern->matchIndex = i;
|
|
cmdPattern->matchLen = wordInput->len;
|
|
varMode = true;
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// move next
|
|
wordPattern = wordPattern->next;
|
|
wordInput = wordInput->next;
|
|
if (wordPattern == NULL || wordInput == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// match command
|
|
SWords* matchCommand(SWords* input, bool continueSearch) {
|
|
int32_t count = SHELL_COMMAND_COUNT();
|
|
for (int32_t i = 0; i < count; i++) {
|
|
SWords* shellCommand = shellCommands + i;
|
|
if (continueSearch && lastMatchIndex != -1 && i <= lastMatchIndex) {
|
|
// new match must greater than lastMatchIndex
|
|
if (varMode && i == lastMatchIndex) {
|
|
// do nothing, var match on lastMatchIndex
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// command is large
|
|
if (input->count > shellCommand->count) {
|
|
continue;
|
|
}
|
|
|
|
// compare
|
|
int32_t index = compareCommand(shellCommand, input);
|
|
if (index != -1) {
|
|
if (firstMatchIndex == -1) firstMatchIndex = i;
|
|
curMatchIndex = i;
|
|
return &shellCommands[i];
|
|
}
|
|
}
|
|
|
|
// not match
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// ------------------- print screen --------------------------
|
|
//
|
|
|
|
// delete char count
|
|
void deleteCount(SShellCmd* cmd, int count) {
|
|
int size = 0;
|
|
int width = 0;
|
|
int prompt_size = 6;
|
|
shellClearScreen(cmd->endOffset + prompt_size, cmd->screenOffset + prompt_size);
|
|
|
|
// loop delete
|
|
while (--count >= 0 && cmd->cursorOffset > 0) {
|
|
shellGetPrevCharSize(cmd->command, cmd->cursorOffset, &size, &width);
|
|
memmove(cmd->command + cmd->cursorOffset - size, cmd->command + cmd->cursorOffset,
|
|
cmd->commandSize - cmd->cursorOffset);
|
|
cmd->commandSize -= size;
|
|
cmd->cursorOffset -= size;
|
|
cmd->screenOffset -= width;
|
|
cmd->endOffset -= width;
|
|
}
|
|
}
|
|
|
|
// show screen
|
|
void printScreen(TAOS* con, SShellCmd* cmd, SWords* match) {
|
|
// modify SShellCmd
|
|
if (firstMatchIndex == -1 || curMatchIndex == -1) {
|
|
// no match
|
|
return;
|
|
}
|
|
|
|
// first tab press
|
|
const char* str = NULL;
|
|
int strLen = 0;
|
|
|
|
if (firstMatchIndex == curMatchIndex && lastWordBytes == -1) {
|
|
// first press tab
|
|
SWord* word = MATCH_WORD(match);
|
|
str = word->word + match->matchLen;
|
|
strLen = word->len - match->matchLen;
|
|
lastMatchIndex = firstMatchIndex;
|
|
lastWordBytes = word->len;
|
|
} else {
|
|
if (lastWordBytes == -1) return;
|
|
deleteCount(cmd, lastWordBytes);
|
|
|
|
SWord* word = MATCH_WORD(match);
|
|
str = word->word;
|
|
strLen = word->len;
|
|
// set current to last
|
|
lastMatchIndex = curMatchIndex;
|
|
lastWordBytes = word->len;
|
|
}
|
|
|
|
// insert new
|
|
shellInsertStr(cmd, (char*)str, strLen);
|
|
}
|
|
|
|
// main key press tab , matched return true else false
|
|
bool firstMatchCommand(TAOS* con, SShellCmd* cmd) {
|
|
if (con == NULL || cmd == NULL) return false;
|
|
// parse command
|
|
SWords* input = (SWords*)taosMemoryMalloc(sizeof(SWords));
|
|
memset(input, 0, sizeof(SWords));
|
|
input->source = cmd->command;
|
|
input->source_len = cmd->commandSize;
|
|
parseCommand(input, false);
|
|
|
|
// if have many , default match first, if press tab again , switch to next
|
|
curMatchIndex = -1;
|
|
lastMatchIndex = -1;
|
|
SWords* match = matchCommand(input, true);
|
|
if (match == NULL) {
|
|
// not match , nothing to do
|
|
freeCommand(input);
|
|
taosMemoryFree(input);
|
|
return false;
|
|
}
|
|
|
|
// print to screen
|
|
printScreen(con, cmd, match);
|
|
#ifdef WINDOWS
|
|
printf("\r");
|
|
shellShowOnScreen(cmd);
|
|
#endif
|
|
freeCommand(input);
|
|
taosMemoryFree(input);
|
|
return true;
|
|
}
|
|
|
|
// create input source
|
|
void createInputFromFirst(SWords* input, SWords* firstMatch) {
|
|
//
|
|
// if next pressTabKey , input context come from firstMatch, set matched length with source_len
|
|
//
|
|
input->source = (char*)taosMemoryMalloc(1024);
|
|
memset((void*)input->source, 0, 1024);
|
|
|
|
SWord* word = firstMatch->head;
|
|
|
|
// source_len = full match word->len + half match with firstMatch->matchLen
|
|
for (int i = 0; i < firstMatch->matchIndex && word; i++) {
|
|
// combine source from each word
|
|
strncpy(input->source + input->source_len, word->word, word->len);
|
|
strcat(input->source, " "); // append blank space
|
|
input->source_len += word->len + 1; // 1 is blank length
|
|
// move next
|
|
word = word->next;
|
|
}
|
|
// appand half matched word for last
|
|
if (word) {
|
|
strncpy(input->source + input->source_len, word->word, firstMatch->matchLen);
|
|
input->source_len += firstMatch->matchLen;
|
|
}
|
|
}
|
|
|
|
// user press Tabkey again is named next , matched return true else false
|
|
bool nextMatchCommand(TAOS* con, SShellCmd* cmd, SWords* firstMatch) {
|
|
if (firstMatch == NULL || firstMatch->head == NULL) {
|
|
return false;
|
|
}
|
|
SWords* input = (SWords*)taosMemoryMalloc(sizeof(SWords));
|
|
memset(input, 0, sizeof(SWords));
|
|
|
|
// create input from firstMatch
|
|
createInputFromFirst(input, firstMatch);
|
|
|
|
// parse input
|
|
parseCommand(input, false);
|
|
|
|
// if have many , default match first, if press tab again , switch to next
|
|
SWords* match = matchCommand(input, true);
|
|
if (match == NULL) {
|
|
// if not match , reset all index
|
|
firstMatchIndex = -1;
|
|
curMatchIndex = -1;
|
|
match = matchCommand(input, false);
|
|
if (match == NULL) {
|
|
freeCommand(input);
|
|
if (input->source) taosMemoryFree(input->source);
|
|
taosMemoryFree(input);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// print to screen
|
|
printScreen(con, cmd, match);
|
|
#ifdef WINDOWS
|
|
printf("\r");
|
|
shellShowOnScreen(cmd);
|
|
#endif
|
|
|
|
// free
|
|
freeCommand(input);
|
|
if (input->source) {
|
|
taosMemoryFree(input->source);
|
|
input->source = NULL;
|
|
}
|
|
taosMemoryFree(input);
|
|
|
|
return true;
|
|
}
|
|
|
|
// fill with type
|
|
bool fillWithType(TAOS* con, SShellCmd* cmd, char* pre, int type) {
|
|
// get type
|
|
STire* tire = tires[type];
|
|
char* str = matchNextPrefix(tire, pre);
|
|
if (str == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// need insert part string
|
|
char* part = str + strlen(pre);
|
|
|
|
// show
|
|
int count = strlen(part);
|
|
shellInsertStr(cmd, part, count);
|
|
cntDel = count; // next press tab delete current append count
|
|
|
|
taosMemoryFree(str);
|
|
return true;
|
|
}
|
|
|
|
// fill with type
|
|
bool fillTableName(TAOS* con, SShellCmd* cmd, char* pre) {
|
|
// search stable and table
|
|
char* str = tireSearchWord(WT_VAR_STABLE, pre);
|
|
if (str == NULL) {
|
|
str = tireSearchWord(WT_VAR_TABLE, pre);
|
|
if (str == NULL) return false;
|
|
}
|
|
|
|
// need insert part string
|
|
char* part = str + strlen(pre);
|
|
|
|
// delete autofill count last append
|
|
if (cntDel > 0) {
|
|
deleteCount(cmd, cntDel);
|
|
cntDel = 0;
|
|
}
|
|
|
|
// show
|
|
int count = strlen(part);
|
|
shellInsertStr(cmd, part, count);
|
|
cntDel = count; // next press tab delete current append count
|
|
|
|
taosMemoryFree(str);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// find last word from sql select clause
|
|
// example :
|
|
// 1 select cou -> press tab select count(
|
|
// 2 select count(*),su -> select count(*), sum(
|
|
// 3 select count(*), su -> select count(*), sum(
|
|
//
|
|
char* lastWord(char* p) {
|
|
// get near from end revert find ' ' and ','
|
|
char* p1 = strrchr(p, ' ');
|
|
char* p2 = strrchr(p, ',');
|
|
|
|
if (p1 && p2) {
|
|
return p1 > p2 ? p1 + 1 : p2 + 1;
|
|
} else if (p1) {
|
|
return p1 + 1;
|
|
} else if (p2) {
|
|
return p2 + 1;
|
|
} else {
|
|
return p;
|
|
}
|
|
}
|
|
|
|
bool fieldsInputEnd(char* sql) {
|
|
// not in '()'
|
|
char* p1 = strrchr(sql, '(');
|
|
char* p2 = strrchr(sql, ')');
|
|
if (p1 && p2 == NULL) {
|
|
// like select count( ' '
|
|
return false;
|
|
} else if (p1 && p2 && p1 > p2) {
|
|
// like select sum(age), count( ' '
|
|
return false;
|
|
}
|
|
|
|
// not in ','
|
|
char* p3 = strrchr(sql, ',');
|
|
char* p = p3;
|
|
// like select ts, age,' '
|
|
if (p) {
|
|
++p;
|
|
bool allBlank = true; // after last ',' all char is blank
|
|
int cnt = 0; // blank count , like ' ' as one blank
|
|
char* plast = NULL; // last blank position
|
|
while (*p) {
|
|
if (*p == ' ') {
|
|
plast = p;
|
|
cnt++;
|
|
} else {
|
|
allBlank = false;
|
|
}
|
|
++p;
|
|
}
|
|
|
|
// any one word is not blank
|
|
if (allBlank) {
|
|
return false;
|
|
}
|
|
|
|
// like 'select count(*),sum(age) fr' need return true
|
|
if (plast && plast > p3 && p2 > p1 && plast > p2 && p1 > p3) {
|
|
return true;
|
|
}
|
|
|
|
// if last char not ' ', then not end field, like 'select count(*), su' can fill sum(
|
|
if (sql[strlen(sql) - 1] != ' ' && cnt <= 1) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char* p4 = strrchr(sql, ' ');
|
|
if (p4 == NULL) {
|
|
// only one word
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// need insert from
|
|
bool needInsertFrom(char* sql, int len) {
|
|
// last is blank
|
|
if (sql[len - 1] != ' ') {
|
|
// insert from keyword
|
|
return false;
|
|
}
|
|
|
|
// select fields input is end
|
|
if (!fieldsInputEnd(sql)) {
|
|
return false;
|
|
}
|
|
|
|
// can insert from keyword
|
|
return true;
|
|
}
|
|
|
|
// p is string following select keyword
|
|
bool appendAfterSelect(TAOS* con, SShellCmd* cmd, char* sql, int32_t len) {
|
|
char* p = strndup(sql, len);
|
|
|
|
// union all
|
|
char* p1;
|
|
do {
|
|
p1 = strstr(p, UNION_ALL);
|
|
if (p1) {
|
|
p = p1 + strlen(UNION_ALL);
|
|
}
|
|
} while (p1);
|
|
|
|
char* from = strstr(p, " from ");
|
|
// last word , maybe empty string or some letters of a string
|
|
char* last = lastWord(p);
|
|
bool ret = false;
|
|
if (from == NULL) {
|
|
bool fieldEnd = fieldsInputEnd(p);
|
|
// check fields input end then insert from keyword
|
|
if (fieldEnd && p[len - 1] == ' ') {
|
|
shellInsertStr(cmd, "from", 4);
|
|
taosMemoryFree(p);
|
|
return true;
|
|
}
|
|
|
|
// fill function
|
|
if (fieldEnd) {
|
|
// fields is end , need match keyword
|
|
ret = fillWithType(con, cmd, last, WT_VAR_KEYWORD);
|
|
} else {
|
|
ret = fillWithType(con, cmd, last, WT_VAR_FUNC);
|
|
}
|
|
|
|
taosMemoryFree(p);
|
|
return ret;
|
|
}
|
|
|
|
// have from
|
|
char* blank = strstr(from + 6, " ");
|
|
if (blank == NULL) {
|
|
// no table name, need fill
|
|
ret = fillTableName(con, cmd, last);
|
|
} else {
|
|
ret = fillWithType(con, cmd, last, WT_VAR_KEYWORD);
|
|
}
|
|
|
|
taosMemoryFree(p);
|
|
return ret;
|
|
}
|
|
|
|
int32_t searchAfterSelect(char* p, int32_t len) {
|
|
// select * from st;
|
|
if (strncasecmp(p, "select ", 7) == 0) {
|
|
// check nest query
|
|
char* p1 = p + 7;
|
|
while (1) {
|
|
char* p2 = strstr(p1, "select ");
|
|
if (p2 == NULL) break;
|
|
p1 = p2 + 7;
|
|
}
|
|
|
|
return p1 - p;
|
|
}
|
|
|
|
// explain as select * from st;
|
|
if (strncasecmp(p, "explain select ", 15) == 0) {
|
|
return 15;
|
|
}
|
|
|
|
char* as_pos_end = strstr(p, " as select ");
|
|
if (as_pos_end == NULL) return -1;
|
|
as_pos_end += 11;
|
|
|
|
// create stream <stream_name> as select
|
|
if (strncasecmp(p, "create stream ", 14) == 0) {
|
|
return as_pos_end - p;
|
|
;
|
|
}
|
|
|
|
// create topic <topic_name> as select
|
|
if (strncasecmp(p, "create topic ", 13) == 0) {
|
|
return as_pos_end - p;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool matchSelectQuery(TAOS* con, SShellCmd* cmd) {
|
|
// if continue press Tab , delete bytes by previous autofill
|
|
if (cntDel > 0) {
|
|
deleteCount(cmd, cntDel);
|
|
cntDel = 0;
|
|
}
|
|
|
|
// match select ...
|
|
int len = cmd->commandSize;
|
|
char* p = cmd->command;
|
|
|
|
// remove prefix blank
|
|
while (p[0] == ' ' && len > 0) {
|
|
p++;
|
|
len--;
|
|
}
|
|
|
|
// special range
|
|
if (len < 7 || len > 512) {
|
|
return false;
|
|
}
|
|
|
|
// search
|
|
char* sql_cp = strndup(p, len);
|
|
int32_t n = searchAfterSelect(sql_cp, len);
|
|
taosMemoryFree(sql_cp);
|
|
if (n == -1 || n > len) return false;
|
|
p += n;
|
|
len -= n;
|
|
|
|
// append
|
|
return appendAfterSelect(con, cmd, p, len);
|
|
}
|
|
|
|
// if is input create fields or tags area, return true
|
|
bool isCreateFieldsArea(char* p) {
|
|
// put to while, support like create table st(ts timestamp, bin1 binary(16), bin2 + blank + TAB
|
|
char* p1 = taosStrdup(p);
|
|
bool ret = false;
|
|
while (1) {
|
|
char* left = strrchr(p1, '(');
|
|
if (left == NULL) {
|
|
// like 'create table st'
|
|
ret = false;
|
|
break;
|
|
}
|
|
|
|
char* right = strrchr(p1, ')');
|
|
if (right == NULL) {
|
|
// like 'create table st( '
|
|
ret = true;
|
|
break;
|
|
}
|
|
|
|
if (left > right) {
|
|
// like 'create table st( ts timestamp, age int) tags(area '
|
|
ret = true;
|
|
break;
|
|
}
|
|
|
|
// set string end by small for next strrchr search
|
|
*left = 0;
|
|
}
|
|
taosMemoryFree(p1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool matchCreateTable(TAOS* con, SShellCmd* cmd) {
|
|
// if continue press Tab , delete bytes by previous autofill
|
|
if (cntDel > 0) {
|
|
deleteCount(cmd, cntDel);
|
|
cntDel = 0;
|
|
}
|
|
|
|
// match select ...
|
|
int len = cmd->commandSize;
|
|
char* p = cmd->command;
|
|
|
|
// remove prefix blank
|
|
while (p[0] == ' ' && len > 0) {
|
|
p++;
|
|
len--;
|
|
}
|
|
|
|
// special range
|
|
if (len < 7 || len > 1024) {
|
|
return false;
|
|
}
|
|
|
|
// select and from
|
|
if (strncasecmp(p, "create table ", 13) != 0) {
|
|
// not select query clause
|
|
return false;
|
|
}
|
|
p += 13;
|
|
len -= 13;
|
|
|
|
char* ps = strndup(p, len);
|
|
bool ret = false;
|
|
char* last = lastWord(ps);
|
|
|
|
// check in create fields or tags input area
|
|
if (isCreateFieldsArea(ps)) {
|
|
ret = fillWithType(con, cmd, last, WT_VAR_DATATYPE);
|
|
}
|
|
|
|
// tags
|
|
if (!ret) {
|
|
// find only one ')' , can insert tags
|
|
char* p1 = strchr(ps, ')');
|
|
if (p1) {
|
|
if (strchr(p1 + 1, ')') == NULL && strstr(p1 + 1, "tags") == NULL) {
|
|
// can insert tags keyword
|
|
ret = fillWithType(con, cmd, last, WT_VAR_KEYTAGS);
|
|
}
|
|
}
|
|
}
|
|
|
|
// tb options
|
|
if (!ret) {
|
|
// find like create table st (...) tags(..) <here is fill tb option area>
|
|
char* p1 = strchr(ps, ')'); // first ')' end
|
|
if (p1) {
|
|
if (strchr(p1 + 1, ')')) { // second ')' end
|
|
// here is tb options area, can insert option
|
|
ret = fillWithType(con, cmd, last, WT_VAR_TBOPTION);
|
|
}
|
|
}
|
|
}
|
|
|
|
taosMemoryFree(ps);
|
|
return ret;
|
|
}
|
|
|
|
bool matchOther(TAOS* con, SShellCmd* cmd) {
|
|
int len = cmd->commandSize;
|
|
char* p = cmd->command;
|
|
|
|
// '\\'
|
|
if (p[len - 1] == '\\') {
|
|
// append '\G'
|
|
char a[] = "G;";
|
|
shellInsertStr(cmd, a, 2);
|
|
return true;
|
|
}
|
|
|
|
// too small
|
|
if (len < 8) return false;
|
|
|
|
// like 'from ( '
|
|
char* sql = strndup(p, len);
|
|
char* last = lastWord(sql);
|
|
|
|
if (strcmp(last, "from(") == 0) {
|
|
fillWithType(con, cmd, "", WT_VAR_KEYSELECT);
|
|
taosMemoryFree(sql);
|
|
return true;
|
|
}
|
|
if (strncmp(last, "(", 1) == 0) {
|
|
last += 1;
|
|
}
|
|
|
|
char* from = strstr(sql, " from");
|
|
// find last ' from'
|
|
while (from) {
|
|
char* p1 = strstr(from + 5, " from");
|
|
if (p1 == NULL) break;
|
|
from = p1;
|
|
}
|
|
|
|
if (from) {
|
|
// find next is '('
|
|
char* p2 = from + 5;
|
|
bool found = false; // found 'from ... ( ...' ... is any count of blank
|
|
bool found1 = false; // found '('
|
|
while (1) {
|
|
if (p2 == last || *p2 == '\0') {
|
|
// last word or string end
|
|
if (found1) {
|
|
found = true;
|
|
}
|
|
break;
|
|
} else if (*p2 == '(') {
|
|
found1 = true;
|
|
} else if (*p2 == ' ') {
|
|
// do nothing
|
|
} else {
|
|
// have any other char
|
|
break;
|
|
}
|
|
|
|
// move next
|
|
p2++;
|
|
}
|
|
|
|
if (found) {
|
|
fillWithType(con, cmd, last, WT_VAR_KEYSELECT);
|
|
taosMemoryFree(sql);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// INSERT
|
|
|
|
taosMemoryFree(sql);
|
|
|
|
return false;
|
|
}
|
|
|
|
// last match if nothing matched
|
|
bool matchEnd(TAOS* con, SShellCmd* cmd) {
|
|
// str dump
|
|
bool ret = false;
|
|
char* ps = strndup(cmd->command, cmd->commandSize);
|
|
char* last = lastWord(ps);
|
|
char* elast = strrchr(last, '.'); // find end last
|
|
if (elast) {
|
|
last = elast + 1;
|
|
}
|
|
|
|
// less one char can match
|
|
if (strlen(last) == 0) {
|
|
goto _return;
|
|
}
|
|
if (strcmp(last, " ") == 0) {
|
|
goto _return;
|
|
}
|
|
|
|
// match database
|
|
if (elast == NULL) {
|
|
// dot need not completed with dbname
|
|
if (fillWithType(con, cmd, last, WT_VAR_DBNAME)) {
|
|
ret = true;
|
|
goto _return;
|
|
}
|
|
}
|
|
|
|
if (fillWithType(con, cmd, last, WT_VAR_SYSTABLE)) {
|
|
ret = true;
|
|
goto _return;
|
|
}
|
|
|
|
// global keys
|
|
if (fillWithType(con, cmd, last, WT_VAR_GLOBALKEYS)) {
|
|
ret = true;
|
|
goto _return;
|
|
}
|
|
|
|
|
|
_return:
|
|
taosMemoryFree(ps);
|
|
return ret;
|
|
}
|
|
|
|
// main key press tab
|
|
void pressTabKey(SShellCmd* cmd) {
|
|
#ifdef WINDOWS
|
|
return ;
|
|
#endif
|
|
// check empty tab key
|
|
if (cmd->commandSize == 0) {
|
|
// have multi line tab key
|
|
if (cmd->bufferSize == 0) {
|
|
showHelp();
|
|
}
|
|
shellShowOnScreen(cmd);
|
|
return;
|
|
}
|
|
|
|
// save connection to global
|
|
varCmd = cmd;
|
|
bool matched = false;
|
|
|
|
// manual match like create table st( ...
|
|
matched = matchCreateTable(varCon, cmd);
|
|
if (matched) return;
|
|
|
|
// shellCommands match
|
|
if (firstMatchIndex == -1) {
|
|
matched = firstMatchCommand(varCon, cmd);
|
|
} else {
|
|
matched = nextMatchCommand(varCon, cmd, &shellCommands[firstMatchIndex]);
|
|
}
|
|
if (matched) return;
|
|
|
|
// NOT MATCHED ANYONE
|
|
// match other like '\G' ...
|
|
matched = matchOther(varCon, cmd);
|
|
if (matched) return;
|
|
|
|
// manual match like select * from ...
|
|
matched = matchSelectQuery(varCon, cmd);
|
|
if (matched) return;
|
|
|
|
// match end
|
|
matched = matchEnd(varCon, cmd);
|
|
|
|
return;
|
|
}
|
|
|
|
// press othr key
|
|
void pressOtherKey(char c) {
|
|
#ifdef WINDOWS
|
|
return ;
|
|
#endif
|
|
|
|
// reset global variant
|
|
firstMatchIndex = -1;
|
|
lastMatchIndex = -1;
|
|
curMatchIndex = -1;
|
|
lastWordBytes = -1;
|
|
|
|
// var names
|
|
cursorVar = -1;
|
|
varMode = false;
|
|
waitAutoFill = false;
|
|
cntDel = 0;
|
|
|
|
if (lastMatch) {
|
|
freeMatch(lastMatch);
|
|
lastMatch = NULL;
|
|
}
|
|
}
|
|
|
|
// put name into name, return name length
|
|
int getWordName(char* p, char* name, int nameLen) {
|
|
// remove prefix blank
|
|
while (*p == ' ') {
|
|
p++;
|
|
}
|
|
|
|
// get databases name;
|
|
int i = 0;
|
|
while (p[i] != 0 && i < nameLen - 1) {
|
|
name[i] = p[i];
|
|
i++;
|
|
if (p[i] == ' ' || p[i] == ';' || p[i] == '(') {
|
|
// name end
|
|
break;
|
|
}
|
|
}
|
|
name[i] = 0;
|
|
|
|
return i;
|
|
}
|
|
|
|
// deal use db, if have 'use' return true
|
|
bool dealUseDB(char* sql) {
|
|
// check use keyword
|
|
if (strncasecmp(sql, "use ", 4) != 0) {
|
|
return false;
|
|
}
|
|
|
|
char db[256];
|
|
char* p = sql + 4;
|
|
if (getWordName(p, db, sizeof(db)) == 0) {
|
|
// no name , return
|
|
return true;
|
|
}
|
|
|
|
// dbName is previous use open db name
|
|
if (strcasecmp(db, dbName) == 0) {
|
|
// same , no need switch
|
|
return true;
|
|
}
|
|
|
|
// switch new db
|
|
taosThreadMutexLock(&tiresMutex);
|
|
// STABLE set null
|
|
STire* tire = tires[WT_VAR_STABLE];
|
|
tires[WT_VAR_STABLE] = NULL;
|
|
if (tire) {
|
|
freeTire(tire);
|
|
}
|
|
// TABLE set null
|
|
tire = tires[WT_VAR_TABLE];
|
|
tires[WT_VAR_TABLE] = NULL;
|
|
if (tire) {
|
|
freeTire(tire);
|
|
}
|
|
// save
|
|
strcpy(dbName, db);
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return true;
|
|
}
|
|
|
|
// deal create, if have 'create' return true
|
|
bool dealCreateCommand(char* sql) {
|
|
// check keyword
|
|
if (strncasecmp(sql, "create ", 7) != 0) {
|
|
return false;
|
|
}
|
|
|
|
char name[1024];
|
|
char* p = sql + 7;
|
|
if (getWordName(p, name, sizeof(name)) == 0) {
|
|
// no name , return
|
|
return true;
|
|
}
|
|
|
|
int type = -1;
|
|
// dbName is previous use open db name
|
|
if (strcasecmp(name, "database") == 0) {
|
|
type = WT_VAR_DBNAME;
|
|
} else if (strcasecmp(name, "table") == 0) {
|
|
if (strstr(sql, " tags") != NULL && strstr(sql, " using ") == NULL)
|
|
type = WT_VAR_STABLE;
|
|
else
|
|
type = WT_VAR_TABLE;
|
|
} else if (strcasecmp(name, "user") == 0) {
|
|
type = WT_VAR_USERNAME;
|
|
} else if (strcasecmp(name, "topic") == 0) {
|
|
type = WT_VAR_TOPIC;
|
|
} else if (strcasecmp(name, "stream") == 0) {
|
|
type = WT_VAR_STREAM;
|
|
} else {
|
|
// no match , return
|
|
return true;
|
|
}
|
|
|
|
// move next
|
|
p += strlen(name);
|
|
|
|
// get next word , that is table name
|
|
if (getWordName(p, name, sizeof(name)) == 0) {
|
|
// no name , return
|
|
return true;
|
|
}
|
|
|
|
// switch new db
|
|
taosThreadMutexLock(&tiresMutex);
|
|
// STABLE set null
|
|
STire* tire = tires[type];
|
|
if (tire) {
|
|
insertWord(tire, name);
|
|
}
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return true;
|
|
}
|
|
|
|
// deal create, if have 'drop' return true
|
|
bool dealDropCommand(char* sql) {
|
|
// check keyword
|
|
if (strncasecmp(sql, "drop ", 5) != 0) {
|
|
return false;
|
|
}
|
|
|
|
char name[1024];
|
|
char* p = sql + 5;
|
|
if (getWordName(p, name, sizeof(name)) == 0) {
|
|
// no name , return
|
|
return true;
|
|
}
|
|
|
|
int type = -1;
|
|
// dbName is previous use open db name
|
|
if (strcasecmp(name, "database") == 0) {
|
|
type = WT_VAR_DBNAME;
|
|
} else if (strcasecmp(name, "table") == 0) {
|
|
type = WT_VAR_ALLTABLE;
|
|
} else if (strcasecmp(name, "dnode") == 0) {
|
|
type = WT_VAR_DNODEID;
|
|
} else if (strcasecmp(name, "user") == 0) {
|
|
type = WT_VAR_USERNAME;
|
|
} else if (strcasecmp(name, "topic") == 0) {
|
|
type = WT_VAR_TOPIC;
|
|
} else if (strcasecmp(name, "stream") == 0) {
|
|
type = WT_VAR_STREAM;
|
|
} else {
|
|
// no match , return
|
|
return true;
|
|
}
|
|
|
|
// move next
|
|
p += strlen(name);
|
|
|
|
// get next word , that is table name
|
|
if (getWordName(p, name, sizeof(name)) == 0) {
|
|
// no name , return
|
|
return true;
|
|
}
|
|
|
|
// switch new db
|
|
taosThreadMutexLock(&tiresMutex);
|
|
// STABLE set null
|
|
if (type == WT_VAR_ALLTABLE) {
|
|
bool del = false;
|
|
// del in stable
|
|
STire* tire = tires[WT_VAR_STABLE];
|
|
if (tire) del = deleteWord(tire, name);
|
|
// del in table
|
|
if (!del) {
|
|
tire = tires[WT_VAR_TABLE];
|
|
if (tire) del = deleteWord(tire, name);
|
|
}
|
|
} else {
|
|
// OTHER TYPE
|
|
STire* tire = tires[type];
|
|
if (tire) deleteWord(tire, name);
|
|
}
|
|
taosThreadMutexUnlock(&tiresMutex);
|
|
|
|
return true;
|
|
}
|
|
|
|
// callback autotab module after shell sql execute
|
|
void callbackAutoTab(char* sqlstr, TAOS* pSql, bool usedb) {
|
|
char* sql = sqlstr;
|
|
// remove prefix blank
|
|
while (*sql == ' ') {
|
|
sql++;
|
|
}
|
|
|
|
if (dealUseDB(sql)) {
|
|
// change to new db
|
|
if (!varRunOnce) updateTireValue(WT_VAR_STABLE, false);
|
|
return;
|
|
}
|
|
|
|
// create command add name to autotab
|
|
if (dealCreateCommand(sql)) {
|
|
return;
|
|
}
|
|
|
|
// drop command remove name from autotab
|
|
if (dealDropCommand(sql)) {
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|