diff --git a/documentation/webdocs/markdowndocs/Connector.md b/documentation/webdocs/markdowndocs/Connector.md index a0433d1f09d7c5f2ec1205f89d2efe638703dc7d..563d306128d5fe27959b67d845aab4ae66988a80 100644 --- a/documentation/webdocs/markdowndocs/Connector.md +++ b/documentation/webdocs/markdowndocs/Connector.md @@ -2,6 +2,8 @@ TDengine provides many connectors for development, including C/C++, JAVA, Python, RESTful, Go, Node.JS, etc. +NOTE: All APIs which require a SQL string as parameter, including but not limit to `taos_query`, `taos_query_a`, `taos_subscribe` in the C/C++ Connector and their counterparts in other connectors, can ONLY process one SQL statement at a time. If more than one SQL statements are provided, their behaviors are undefined. + ## C/C++ API C/C++ APIs are similar to the MySQL APIs. Applications should include TDengine head file _taos.h_ to use C/C++ APIs by adding the following line in code: diff --git a/documentation/webdocs/markdowndocs/connector-ch.md b/documentation/webdocs/markdowndocs/connector-ch.md index b5d8fb5afb12ede82f2cdcd9ea29e20e8a82d6b8..47c8381f69c3b582ede9f1ae54ea2da27e6851d1 100644 --- a/documentation/webdocs/markdowndocs/connector-ch.md +++ b/documentation/webdocs/markdowndocs/connector-ch.md @@ -2,6 +2,8 @@ TDengine提供了丰富的应用程序开发接口,其中包括C/C++、JAVA、Python、RESTful、Go等,便于用户快速开发应用。 +注意:所以执行 SQL 语句的 API,例如 C/C++ Connector 中的 `tao_query`、`taos_query_a`、`taos_subscribe` 等,以及其它语言中与它们对应的API,每次都只能执行一条 SQL 语句,如果实际参数中包含了多条语句,它们的行为是未定义的。 + ## C/C++ Connector C/C++的API类似于MySQL的C API。应用程序使用时,需要包含TDengine头文件 _taos.h_(安装后,位于 _/usr/local/taos/include_): diff --git a/src/kit/shell/src/shellDarwin.c b/src/kit/shell/src/shellDarwin.c index e4ef06c4036ebd8f8c1638b63407889686c4c2d4..987087d71f801d351799966fe8b47d347c6bb2d2 100644 --- a/src/kit/shell/src/shellDarwin.c +++ b/src/kit/shell/src/shellDarwin.c @@ -335,17 +335,14 @@ void *shellLoopQuery(void *arg) { tscError("failed to malloc command"); return NULL; } - while (1) { - // Read command from shell. + do { + // Read command from shell. memset(command, 0, MAX_COMMAND_SIZE); set_terminal_mode(); shellReadCommand(con, command); reset_terminal_mode(); - - // Run the command - shellRunCommand(con, command); - } + } while (shellRunCommand(con, command) == 0); pthread_cleanup_pop(1); diff --git a/src/kit/shell/src/shellEngine.c b/src/kit/shell/src/shellEngine.c index 93818d7d73ce29391e1260a5d95f41ad1b0c4fb9..4ef51eaa5a8ff7e073fa421f0efcd137974ec694 100644 --- a/src/kit/shell/src/shellEngine.c +++ b/src/kit/shell/src/shellEngine.c @@ -82,20 +82,15 @@ TAOS *shellInit(SShellArguments *args) { // Check if it is temperory run if (args->commands != NULL || args->file[0] != 0) { if (args->commands != NULL) { - char *token; - token = strtok(args->commands, ";"); - while (token != NULL) { - printf("%s%s\n", PROMPT_HEADER, token); - shellRunCommand(con, token); - token = strtok(NULL, ";"); - } + printf("%s%s\n", PROMPT_HEADER, args->commands); + shellRunCommand(con, args->commands); } if (args->file[0] != 0) { source_file(con, args->file); } - taos_close(con); + taos_close(con); write_history(); exit(EXIT_SUCCESS); } @@ -111,96 +106,37 @@ TAOS *shellInit(SShellArguments *args) { return con; } -void shellReplaceCtrlChar(char *str) { - _Bool ctrlOn = false; - char *pstr = NULL; - char quote = 0; - for (pstr = str; *str != '\0'; ++str) { - if (ctrlOn) { - switch (*str) { - case 'n': - *pstr = '\n'; - pstr++; - break; - case 'r': - *pstr = '\r'; - pstr++; - break; - case 't': - *pstr = '\t'; - pstr++; - break; - case 'G': - *pstr++ = '\\'; - *pstr++ = *str; - break; - case '\\': - *pstr = '\\'; - pstr++; - break; - case '\'': - case '"': - if (quote) { - *pstr++ = '\\'; - *pstr++ = *str; - } - break; - default: - *pstr = *str; - pstr++; - break; - } - ctrlOn = false; - } else { - if (*str == '\\') { - ctrlOn = true; - } else { - if (quote == *str) { - quote = 0; - } else if (*str == '\'' || *str == '"') { - quote = *str; - } - *pstr = *str; - pstr++; - } +static bool isEmptyCommand(const char* cmd) { + for (char c = *cmd++; c != 0; c = *cmd++) { + if (c != ' ' && c != '\t' && c != ';') { + return false; } } - *pstr = '\0'; + return true; } -int32_t shellRunCommand(TAOS *con, char *command) { + +static int32_t shellRunSingleCommand(TAOS *con, char *command) { /* If command is empty just return */ - if (regex_match(command, "^[ \t;]*$", REG_EXTENDED)) { + if (isEmptyCommand(command)) { return 0; } - /* Update the history vector. */ - if (history.hstart == history.hend || - history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL || - strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) { - if (history.hist[history.hend] != NULL) { - tfree(history.hist[history.hend]); - } - history.hist[history.hend] = strdup(command); - - history.hend = (history.hend + 1) % MAX_HISTORY_SIZE; - if (history.hend == history.hstart) { - history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE; - } - } - - shellReplaceCtrlChar(command); - // Analyse the command. if (regex_match(command, "^[ \t]*(quit|q|exit)[ \t;]*$", REG_EXTENDED | REG_ICASE)) { taos_close(con); write_history(); return -1; - } else if (regex_match(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) { + } + + if (regex_match(command, "^[\t ]*clear[ \t;]*$", REG_EXTENDED | REG_ICASE)) { // If clear the screen. system("clear"); - } else if (regex_match(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$", REG_EXTENDED | REG_ICASE)) { + return 0; + } + + if (regex_match(command, "^[\t ]*set[ \t]+max_binary_display_width[ \t]+(default|[1-9][0-9]*)[ \t;]*$", REG_EXTENDED | REG_ICASE)) { strtok(command, " \t"); strtok(NULL, " \t"); char* p = strtok(NULL, " \t"); @@ -209,21 +145,102 @@ int32_t shellRunCommand(TAOS *con, char *command) { } else { tsMaxBinaryDisplayWidth = atoi(p); } - } else if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) { + return 0; + } + + if (regex_match(command, "^[ \t]*source[\t ]+[^ ]+[ \t;]*$", REG_EXTENDED | REG_ICASE)) { /* If source file. */ char *c_ptr = strtok(command, " ;"); assert(c_ptr != NULL); c_ptr = strtok(NULL, " ;"); assert(c_ptr != NULL); - source_file(con, c_ptr); - } else { - shellRunCommandOnServer(con, command); + return 0; } - + + shellRunCommandOnServer(con, command); return 0; } + +int32_t shellRunCommand(TAOS* con, char* command) { + /* If command is empty just return */ + if (isEmptyCommand(command)) { + return 0; + } + + /* Update the history vector. */ + if (history.hstart == history.hend || + history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE] == NULL || + strcmp(command, history.hist[(history.hend + MAX_HISTORY_SIZE - 1) % MAX_HISTORY_SIZE]) != 0) { + if (history.hist[history.hend] != NULL) { + tfree(history.hist[history.hend]); + } + history.hist[history.hend] = strdup(command); + + history.hend = (history.hend + 1) % MAX_HISTORY_SIZE; + if (history.hend == history.hstart) { + history.hstart = (history.hstart + 1) % MAX_HISTORY_SIZE; + } + } + + bool esc = false; + char quote = 0, *cmd = command, *p = command; + for (char c = *command++; c != 0; c = *command++) { + if (esc) { + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'G': + *p++ = '\\'; + break; + case '\'': + case '"': + if (quote) { + *p++ = '\\'; + } + break; + } + *p++ = c; + esc = false; + continue; + } + + if (c == '\\') { + esc = true; + continue; + } + + if (quote == c) { + quote = 0; + } else if (c == '\'' || c == '"') { + quote = c; + } + + *p++ = c; + if (c == ';') { + c = *p; + *p = 0; + if (shellRunSingleCommand(con, cmd) < 0) { + return -1; + } + *p = c; + p = cmd; + } + } + + *p = 0; + return shellRunSingleCommand(con, cmd); +} + + void shellRunCommandOnServer(TAOS *con, char command[]) { int64_t st, et; wordexp_t full_path; diff --git a/src/kit/shell/src/shellLinux.c b/src/kit/shell/src/shellLinux.c index 856e011a78bb63b112239f644d1423c0d21b8d04..d8b3e9bb4da07d0c6d333584ddaeee53655a263b 100644 --- a/src/kit/shell/src/shellLinux.c +++ b/src/kit/shell/src/shellLinux.c @@ -307,19 +307,13 @@ void *shellLoopQuery(void *arg) { return NULL; } - while (1) { + do { // Read command from shell. - memset(command, 0, MAX_COMMAND_SIZE); set_terminal_mode(); shellReadCommand(con, command); reset_terminal_mode(); - - // Run the command - if (shellRunCommand(con, command) != 0) { - break; - } - } + } while (shellRunCommand(con, command) == 0); tfree(command); exitShell(); diff --git a/src/kit/shell/src/shellWindows.c b/src/kit/shell/src/shellWindows.c index c4466772766863bccd66869d434b38a1b9a47222..440aa508ab493510d3a7771165f1be8f4fde4a79 100644 --- a/src/kit/shell/src/shellWindows.c +++ b/src/kit/shell/src/shellWindows.c @@ -203,16 +203,13 @@ void *shellLoopQuery(void *arg) { char *command = malloc(MAX_COMMAND_SIZE); if (command == NULL) return NULL; - while (1) { + do { memset(command, 0, MAX_COMMAND_SIZE); shellPrintPrompt(); // Read command from shell. shellReadCommand(con, command); - - // Run the command - shellRunCommand(con, command); - } + } while (shellRunCommand(con, command) == 0); return NULL; }