From c625106609361e172aa2bab1d2271f2ac07367ce Mon Sep 17 00:00:00 2001 From: Mario Peng <48949600+Pengrongkun@users.noreply.github.com> Date: Sat, 22 Mar 2025 21:14:08 +0800 Subject: [PATCH] fix(stmt2):stmt2 get fields return wrong when tag is value (#30283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: add 3.3.6 branch * fix: delete taos-tools repo in Jenkinsfile2 * ci:add 3.3.6 branch to taosd-ci.yml * fix: [TD-34074] compile error (#30220) * fix: add case * fix: stmt2 get fields return wrong when tag is value * enh: replace preCtb bool to flag,handle more situation * fix: some unit test error * diff: add case and fix some problem * fix: remove async test, handle in TD-34077 * fix: [TD-34074] Forbid virtual table in tq and stmt and forbid supertable query's origin table from different databases. fix: [TD-34074] Forbid virtual table in tq and stmt and forbid supertable query's origin table from different databases. * fix(sml): process space in the end if writing raw data in sml & change some log level #30306 fix(sml): process space in the end if writing raw data in sml & change some log level * fix: taosadapter version 3.3.6 (#30324) * docs(stream): document streaming computation support for virtual tables in user manual (#30319) * docs(stream): document streaming computation support for virtual tables in user manual - Added section to user manual describing streaming computation support for virtual tables - Listed known limitations and behavior when using virtual tables in streaming mode * Update 14-stream.md --------- Co-authored-by: Pan Wei <72057773+dapan1121@users.noreply.github.com> * fix: review * ci: add tdgpt .c file into TDengine and TDgpt workflow * ci: add tdgpt .c file into TDengine and TDgpt workflow --------- Signed-off-by: WANG Xu Co-authored-by: WANG Xu Co-authored-by: haoranchen Co-authored-by: Simon Guan Co-authored-by: Feng Chao Co-authored-by: facetosea <285808407@qq.com> Co-authored-by: Jing Sima Co-authored-by: Jinqing Kuang Co-authored-by: dapan1121 Co-authored-by: Pan Wei <72057773+dapan1121@users.noreply.github.com> Co-authored-by: Haojun Liao Co-authored-by: huohong <346479823@qq.com> Co-authored-by: Minglei Jin Co-authored-by: freemine Co-authored-by: Yihao Deng Co-authored-by: 54liuyao <54liuyao@163.com> Co-authored-by: Bomin Zhang Co-authored-by: 蟑螂·魂 Co-authored-by: wangjiaming Co-authored-by: Haojun Liao Co-authored-by: dongming chen Co-authored-by: Zhiyu Yang <69311263+zyyang90@users.noreply.github.com> Co-authored-by: Nie Minhui <143420805+minhuinie@users.noreply.github.com> Co-authored-by: Zhixiao Bao <62235797+xiao-77@users.noreply.github.com> Co-authored-by: Kaili Xu Co-authored-by: Linhe Huo Co-authored-by: tjuzyp Co-authored-by: yanyuxing Co-authored-by: WANG MINGMING Co-authored-by: yihaoDeng Co-authored-by: facetosea <25808407@qq.com> Co-authored-by: Minglei Jin <49711132+stephenkgu@users.noreply.github.com> Co-authored-by: She Yanjie <57549981+sheyanjie-qq@users.noreply.github.com> Co-authored-by: jiajingbin <39030567+jiajingbin@users.noreply.github.com> Co-authored-by: Alex Duan <51781608+DuanKuanJun@users.noreply.github.com> Co-authored-by: kevin men Co-authored-by: Hongze Cheng Co-authored-by: Yaming Pei Co-authored-by: Xuefeng Tan <1172915550@qq.com> --- .github/workflows/tdengine-test.yml | 1 + cmake/taosadapter_CMakeLists.txt.in | 2 +- docs/en/14-reference/03-taos-sql/14-stream.md | 18 ++++ docs/zh/14-reference/03-taos-sql/14-stream.md | 18 ++++ include/libs/parser/parser.h | 10 +- source/client/inc/clientStmt.h | 2 +- source/client/src/clientSmlTelnet.c | 2 +- source/client/src/clientStmt.c | 12 ++- source/client/src/clientStmt2.c | 19 ++-- source/client/test/stmt2Test.cpp | 99 ++++++++++++++----- source/libs/parser/src/parInsertSql.c | 70 ++++++++----- source/libs/parser/src/parInsertStmt.c | 13 +-- source/util/src/tmempool.c | 2 +- source/util/test/memPoolTest.cpp | 2 +- utils/test/c/sml_test.c | 28 +++++- 15 files changed, 222 insertions(+), 76 deletions(-) diff --git a/.github/workflows/tdengine-test.yml b/.github/workflows/tdengine-test.yml index b981d005c4..e5e2cdbfaf 100644 --- a/.github/workflows/tdengine-test.yml +++ b/.github/workflows/tdengine-test.yml @@ -6,6 +6,7 @@ on: - 'main' - '3.0' - '3.1' + - '3.3.6' paths-ignore: - 'packaging/**' - 'docs/**' diff --git a/cmake/taosadapter_CMakeLists.txt.in b/cmake/taosadapter_CMakeLists.txt.in index ef6ed4af1d..a768b332a6 100644 --- a/cmake/taosadapter_CMakeLists.txt.in +++ b/cmake/taosadapter_CMakeLists.txt.in @@ -2,7 +2,7 @@ # taosadapter ExternalProject_Add(taosadapter GIT_REPOSITORY https://github.com/taosdata/taosadapter.git - GIT_TAG 3.0 + GIT_TAG 3.3.6 SOURCE_DIR "${TD_SOURCE_DIR}/tools/taosadapter" BINARY_DIR "" #BUILD_IN_SOURCE TRUE diff --git a/docs/en/14-reference/03-taos-sql/14-stream.md b/docs/en/14-reference/03-taos-sql/14-stream.md index 00e29b0e3d..f8ad80d2a3 100644 --- a/docs/en/14-reference/03-taos-sql/14-stream.md +++ b/docs/en/14-reference/03-taos-sql/14-stream.md @@ -532,6 +532,24 @@ These fields are present only when "windowType" is "Count". #### Fields for Window Invalidation Due to scenarios such as data disorder, updates, or deletions during stream computing, windows that have already been generated might be removed or their results need to be recalculated. In such cases, a notification with the eventType "WINDOW_INVALIDATION" is sent to inform which windows have been invalidated. + For events with "eventType" as "WINDOW_INVALIDATION", the following fields are included: 1. "windowStart": A long integer timestamp representing the start time of the window. 1. "windowEnd": A long integer timestamp representing the end time of the window. + +## Support for Virtual Tables in Stream Computing + +Starting with v3.3.6.0, stream computing can use virtual tables—including virtual regular tables, virtual sub-tables, and virtual super tables—as data sources for computation. The syntax is identical to that for non‑virtual tables. + +However, because the behavior of virtual tables differs from that of non‑virtual tables, the following restrictions apply when using stream computing: + +1. The schema of virtual regular tables/virtual sub-tables involved in stream computing cannot be modified. +1. During stream computing, if the data source corresponding to a column in a virtual table is changed, the stream computation will not pick up the change; it will still read from the old data source. +1. During stream computing, if the original table corresponding to a column in a virtual table is deleted and later a new table with the same name and a column with the same name is created, the stream computation will not read data from the new table. +1. The watermark for stream computing must be 0; otherwise, an error will occur during creation. +1. If the data source for stream computing is a virtual super table, sub-tables that are added after the stream computing task starts will not participate in the computation. +1. The timestamps of different underlying tables in a virtual table may not be completely consistent; merging the data might produce null values, and interpolation is currently not supported. +1. Out-of-order data, updates, or deletions are not handled. In other words, when creating a stream, you cannot specify `ignore update 0` or `ignore expired 0`; otherwise, an error will be reported. +1. Historical data computation is not supported. That is, when creating a stream, you cannot specify `fill_history 1`; otherwise, an error will be reported. +1. The trigger modes MAX_DELAY, CONTINUOUS_WINDOW_CLOSE and FORCE_WINDOW_CLOSE are not supported. +1. The COUNT_WINDOW type is not supported. diff --git a/docs/zh/14-reference/03-taos-sql/14-stream.md b/docs/zh/14-reference/03-taos-sql/14-stream.md index b4c4a76922..a9225a3ab4 100644 --- a/docs/zh/14-reference/03-taos-sql/14-stream.md +++ b/docs/zh/14-reference/03-taos-sql/14-stream.md @@ -527,6 +527,24 @@ CREATE STREAM avg_current_stream FILL_HISTORY 1 #### 窗口失效相关字段 因为流计算过程中会遇到数据乱序、更新、删除等情况,可能造成已生成的窗口被删除,或者结果需要重新计算。此时会向通知地址发送一条 WINDOW_INVALIDATION 的通知,说明哪些窗口已经被删除。 + 这部分是 eventType 为 WINDOW_INVALIDATION 时,event 对象才有的字段。 1. windowStart:长整型时间戳,表示窗口的开始时间,精度与结果表的时间精度一致。 1. windowEnd: 长整型时间戳,表示窗口的结束时间,精度与结果表的时间精度一致。 + +## 流式计算对虚拟表的支持 + +从 v3.3.6.0 开始,流计算能够使用虚拟表(包括虚拟普通表、虚拟子表、虚拟超级表)作为数据源进行计算,语法和非虚拟表完全一致。 + +但是虚拟表的行为与非虚拟表存在差异,所以目前在使用流计算对虚拟表进行计算时存在以下限制: + +1. 流计算中涉及的虚拟普通表/虚拟子表的 schema 不允许更改。 +1. 流计算过程中,如果修改虚拟表某一列对应的数据源,对流计算来说不生效。即:流计算仍只读取老的数据源。 +1. 流计算过程中,如果虚拟表某一列对应的原始表被删除,之后新建了同名的表和同名的列,流计算不会读取新表的数据。 +1. 流计算的 watermark 只能是 0,否则创建时就报错。 +1. 如果流计算的数据源是虚拟超级表,流计算任务启动后新增的子表不参与计算。 +1. 虚拟表的不同原始表的时间戳不完全一致,数据合并后可能会产生空值,暂不支持插值处理。 +1. 不处理数据的乱序、更新或删除。即:流创建时不能指定 `ignore update 0` 或者 `ignore expired 0`,否则报错。 +1. 不支持历史数据计算,即:流创建时不能指定 `fill_history 1`,否则报错。 +1. 不支持触发模式:MAX_DELAY, FORCE_WINDOW_CLOSE, CONTINUOUS_WINDOW_CLOSE。 +1. 不支持窗口类型:COUNT_WINDOW。 diff --git a/include/libs/parser/parser.h b/include/libs/parser/parser.h index 7ce4684f07..4fd6fef81a 100644 --- a/include/libs/parser/parser.h +++ b/include/libs/parser/parser.h @@ -49,10 +49,16 @@ extern "C" { } \ } while (0) +#define HAS_BIND_VALUE ((uint8_t)0x1) +#define IS_FIXED_VALUE ((uint8_t)0x2) +#define USING_CLAUSE ((uint8_t)0x4) +#define IS_FIXED_TAG ((uint8_t)0x8) +#define NO_DATA_USING_CLAUSE ((uint8_t)0x7) + typedef struct SStmtCallback { TAOS_STMT* pStmt; int32_t (*getTbNameFn)(TAOS_STMT*, char**); - int32_t (*setInfoFn)(TAOS_STMT*, STableMeta*, void*, SName*, bool, SHashObj*, SHashObj*, const char*, bool); + int32_t (*setInfoFn)(TAOS_STMT*, STableMeta*, void*, SName*, bool, SHashObj*, SHashObj*, const char*, uint8_t); int32_t (*getExecInfoFn)(TAOS_STMT*, SHashObj**, SHashObj**); } SStmtCallback; @@ -175,7 +181,7 @@ int32_t qBindStmtColsValue(void* pBlock, SArray* pCols, TAOS_MULTI_BIND* bind, c int32_t qBindStmtSingleColValue(void* pBlock, SArray* pCols, TAOS_MULTI_BIND* bind, char* msgBuf, int32_t msgBufLen, int32_t colIdx, int32_t rowNum, void* charsetCxt); int32_t qBuildStmtColFields(void* pDataBlock, int32_t* fieldNum, TAOS_FIELD_E** fields); -int32_t qBuildStmtStbColFields(void* pBlock, void* boundTags, bool hasCtbName, int32_t* fieldNum, +int32_t qBuildStmtStbColFields(void* pBlock, void* boundTags, uint8_t tbNameFlag, int32_t* fieldNum, TAOS_FIELD_ALL** fields); int32_t qBuildStmtTagFields(void* pBlock, void* boundTags, int32_t* fieldNum, TAOS_FIELD_E** fields); int32_t qBindStmtTagsValue(void* pBlock, void* boundTags, int64_t suid, const char* sTableName, char* tName, diff --git a/source/client/inc/clientStmt.h b/source/client/inc/clientStmt.h index 74260c42e0..5d61e8b5d0 100644 --- a/source/client/inc/clientStmt.h +++ b/source/client/inc/clientStmt.h @@ -64,7 +64,7 @@ typedef struct SStmtBindInfo { int32_t sBindLastIdx; int8_t tbType; bool tagsCached; - bool preCtbname; + uint8_t tbNameFlag; void *boundTags; char tbName[TSDB_TABLE_FNAME_LEN]; char tbFName[TSDB_TABLE_FNAME_LEN]; diff --git a/source/client/src/clientSmlTelnet.c b/source/client/src/clientSmlTelnet.c index dd264da11e..0427196b83 100644 --- a/source/client/src/clientSmlTelnet.c +++ b/source/client/src/clientSmlTelnet.c @@ -75,7 +75,7 @@ static int32_t smlProcessTagTelnet(SSmlHandle *info, char *data, char *sqlEnd){ const char *sql = data; while (sql < sqlEnd) { JUMP_SPACE(sql, sqlEnd) - if (unlikely(*sql == '\0')) break; + if (unlikely(*sql == '\0' || *sql == '\n')) break; const char *key = sql; size_t keyLen = 0; diff --git a/source/client/src/clientStmt.c b/source/client/src/clientStmt.c index 2d9001d248..4cef20056b 100644 --- a/source/client/src/clientStmt.c +++ b/source/client/src/clientStmt.c @@ -238,7 +238,7 @@ int32_t stmtRestoreQueryFields(STscStmt* pStmt) { } */ int32_t stmtUpdateBindInfo(TAOS_STMT* stmt, STableMeta* pTableMeta, void* tags, SName* tbName, const char* sTableName, - bool autoCreateTbl) { + bool autoCreateTbl, uint8_t tbNameFlag) { STscStmt* pStmt = (STscStmt*)stmt; char tbFName[TSDB_TABLE_FNAME_LEN]; int32_t code = tNameExtractFullName(tbName, tbFName); @@ -256,6 +256,7 @@ int32_t stmtUpdateBindInfo(TAOS_STMT* stmt, STableMeta* pTableMeta, void* tags, pStmt->bInfo.tbType = pTableMeta->tableType; pStmt->bInfo.boundTags = tags; pStmt->bInfo.tagsCached = false; + pStmt->bInfo.tbNameFlag = tbNameFlag; tstrncpy(pStmt->bInfo.stbFName, sTableName, sizeof(pStmt->bInfo.stbFName)); return TSDB_CODE_SUCCESS; @@ -271,10 +272,10 @@ int32_t stmtUpdateExecInfo(TAOS_STMT* stmt, SHashObj* pVgHash, SHashObj* pBlockH } int32_t stmtUpdateInfo(TAOS_STMT* stmt, STableMeta* pTableMeta, void* tags, SName* tbName, bool autoCreateTbl, - SHashObj* pVgHash, SHashObj* pBlockHash, const char* sTableName, bool preCtbname) { + SHashObj* pVgHash, SHashObj* pBlockHash, const char* sTableName, uint8_t tbNameFlag) { STscStmt* pStmt = (STscStmt*)stmt; - STMT_ERR_RET(stmtUpdateBindInfo(stmt, pTableMeta, tags, tbName, sTableName, autoCreateTbl)); + STMT_ERR_RET(stmtUpdateBindInfo(stmt, pTableMeta, tags, tbName, sTableName, autoCreateTbl, tbNameFlag)); STMT_ERR_RET(stmtUpdateExecInfo(stmt, pVgHash, pBlockHash)); pStmt->sql.autoCreateTbl = autoCreateTbl; @@ -1729,7 +1730,10 @@ int stmtGetTagFields(TAOS_STMT* stmt, int* nums, TAOS_FIELD_E** fields) { STMT_ERRI_JRET(stmtFetchTagFields(stmt, nums, fields)); _return: - + // compatible with previous versions + if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST && (pStmt->bInfo.tbNameFlag & NO_DATA_USING_CLAUSE) == 0x0) { + code = TSDB_CODE_TSC_STMT_TBNAME_ERROR; + } pStmt->errCode = preCode; return code; diff --git a/source/client/src/clientStmt2.c b/source/client/src/clientStmt2.c index cfd302d895..c4f8702f43 100644 --- a/source/client/src/clientStmt2.c +++ b/source/client/src/clientStmt2.c @@ -193,7 +193,7 @@ static int32_t stmtGetTbName(TAOS_STMT2* stmt, char** tbName) { } static int32_t stmtUpdateBindInfo(TAOS_STMT2* stmt, STableMeta* pTableMeta, void* tags, SName* tbName, - const char* sTableName, bool autoCreateTbl, bool preCtbname) { + const char* sTableName, bool autoCreateTbl, int8_t tbNameFlag) { STscStmt2* pStmt = (STscStmt2*)stmt; char tbFName[TSDB_TABLE_FNAME_LEN]; int32_t code = tNameExtractFullName(tbName, tbFName); @@ -217,7 +217,7 @@ static int32_t stmtUpdateBindInfo(TAOS_STMT2* stmt, STableMeta* pTableMeta, void pStmt->bInfo.boundTags = tags; pStmt->bInfo.tagsCached = false; - pStmt->bInfo.preCtbname = preCtbname; + pStmt->bInfo.tbNameFlag = tbNameFlag; tstrncpy(pStmt->bInfo.stbFName, sTableName, sizeof(pStmt->bInfo.stbFName)); return TSDB_CODE_SUCCESS; @@ -233,10 +233,10 @@ static int32_t stmtUpdateExecInfo(TAOS_STMT2* stmt, SHashObj* pVgHash, SHashObj* } static int32_t stmtUpdateInfo(TAOS_STMT2* stmt, STableMeta* pTableMeta, void* tags, SName* tbName, bool autoCreateTbl, - SHashObj* pVgHash, SHashObj* pBlockHash, const char* sTableName, bool preCtbname) { + SHashObj* pVgHash, SHashObj* pBlockHash, const char* sTableName, uint8_t tbNameFlag) { STscStmt2* pStmt = (STscStmt2*)stmt; - STMT_ERR_RET(stmtUpdateBindInfo(stmt, pTableMeta, tags, tbName, sTableName, autoCreateTbl, preCtbname)); + STMT_ERR_RET(stmtUpdateBindInfo(stmt, pTableMeta, tags, tbName, sTableName, autoCreateTbl, tbNameFlag)); STMT_ERR_RET(stmtUpdateExecInfo(stmt, pVgHash, pBlockHash)); pStmt->sql.autoCreateTbl = autoCreateTbl; @@ -1233,7 +1233,8 @@ static int stmtFetchStbColFields2(STscStmt2* pStmt, int32_t* fieldNum, TAOS_FIEL } STMT_ERRI_JRET( - qBuildStmtStbColFields(*pDataBlock, pStmt->bInfo.boundTags, pStmt->bInfo.preCtbname, fieldNum, fields)); + qBuildStmtStbColFields(*pDataBlock, pStmt->bInfo.boundTags, pStmt->bInfo.tbNameFlag, fieldNum, fields)); + if (pStmt->bInfo.tbType == TSDB_SUPER_TABLE && cleanStb) { pStmt->bInfo.needParse = true; qDestroyStmtDataBlock(*pDataBlock); @@ -2022,7 +2023,9 @@ int stmtParseColFields2(TAOS_STMT2* stmt) { STMT_TYPE_MULTI_INSERT != pStmt->sql.type) { pStmt->bInfo.needParse = false; } - + if (pStmt->sql.stbInterlaceMode && pStmt->sql.siInfo.pDataCtx != NULL) { + pStmt->bInfo.needParse = false; + } if (pStmt->exec.pRequest && STMT_TYPE_QUERY == pStmt->sql.type && pStmt->sql.runTimes) { taos_free_result(pStmt->exec.pRequest); pStmt->exec.pRequest = NULL; @@ -2036,6 +2039,10 @@ int stmtParseColFields2(TAOS_STMT2* stmt) { } _return: + // compatible with previous versions + if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST && (pStmt->bInfo.tbNameFlag & NO_DATA_USING_CLAUSE) == 0x0) { + code = TSDB_CODE_TSC_STMT_TBNAME_ERROR; + } pStmt->errCode = preCode; diff --git a/source/client/test/stmt2Test.cpp b/source/client/test/stmt2Test.cpp index cc54cd55d3..5e50760648 100644 --- a/source/client/test/stmt2Test.cpp +++ b/source/client/test/stmt2Test.cpp @@ -534,7 +534,7 @@ TEST(stmt2Case, insert_ctb_using_get_fields_Test) { printf("support case \n"); // case 1 : test child table already exist { - const char* sql = "INSERT INTO stmt2_testdb_3.t0(ts,b)using stmt2_testdb_3.stb (t1,t2) TAGS(?,?) VALUES (?,?)"; + const char* sql = "INSERT INTO stmt2_testdb_3.t0(ts,b)using stmt2_testdb_3.stb (t1,t2) TAGS(?,?) VALUES(?,?)"; TAOS_FIELD_ALL expectedFields[4] = {{"t1", TSDB_DATA_TYPE_INT, 0, 0, 4, TAOS_FIELD_TAG}, {"t2", TSDB_DATA_TYPE_BINARY, 0, 0, 12, TAOS_FIELD_TAG}, {"ts", TSDB_DATA_TYPE_TIMESTAMP, 2, 0, 8, TAOS_FIELD_COL}, @@ -612,7 +612,7 @@ TEST(stmt2Case, insert_ctb_using_get_fields_Test) { // case 8 : 'db' 'stb' { - const char* sql = "INSERT INTO 'stmt2_testdb_3'.? using 'stmt2_testdb_3'.'stb' (t1,t2) TAGS(?,?) (ts,b)VALUES(?,?)"; + const char* sql = "INSERT INTO 'stmt2_testdb_3'.? using 'stmt2_testdb_3'.'stb' (t1,t2) TAGS(?,?)(ts,b)VALUES(?,?)"; TAOS_FIELD_ALL expectedFields[5] = {{"tbname", TSDB_DATA_TYPE_BINARY, 0, 0, 271, TAOS_FIELD_TBNAME}, {"t1", TSDB_DATA_TYPE_INT, 0, 0, 4, TAOS_FIELD_TAG}, {"t2", TSDB_DATA_TYPE_BINARY, 0, 0, 12, TAOS_FIELD_TAG}, @@ -634,9 +634,20 @@ TEST(stmt2Case, insert_ctb_using_get_fields_Test) { printf("case 9 : %s\n", sql); getFieldsSuccess(taos, sql, expectedFields, 5); } + // case 11: TD-34097 + { + do_query(taos, "use stmt2_testdb_3"); + const char* sql = "INSERT INTO ? using stb (t1,t2) TAGS(1,'abc') (ts,b)VALUES(?,?)"; + TAOS_FIELD_ALL expectedFields[3] = {{"tbname", TSDB_DATA_TYPE_BINARY, 0, 0, 271, TAOS_FIELD_TBNAME}, + {"ts", TSDB_DATA_TYPE_TIMESTAMP, 2, 0, 8, TAOS_FIELD_COL}, + {"b", TSDB_DATA_TYPE_BINARY, 0, 0, 12, TAOS_FIELD_COL}}; + printf("case 11 : %s\n", sql); + getFieldsSuccess(taos, sql, expectedFields, 3); + } // case 10 : test all types { + do_query(taos, "use stmt2_testdb_3"); const char* sql = "insert into ? using all_stb tags(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; TAOS_FIELD_ALL expectedFields[33] = {{"tbname", TSDB_DATA_TYPE_BINARY, 0, 0, 271, TAOS_FIELD_TBNAME}, @@ -711,7 +722,27 @@ TEST(stmt2Case, insert_ctb_using_get_fields_Test) { printf("case 5 : %s\n", sql); getFieldsError(taos, sql, TSDB_CODE_TSC_SQL_SYNTAX_ERROR); } - + // case 6 : mix value and ? + { + do_query(taos, "use stmt2_testdb_3"); + const char* sql = "INSERT INTO ? using stb (t1,t2) TAGS(1,?) (ts,b)VALUES(?,?)"; + printf("case 6 : %s\n", sql); + getFieldsError(taos, sql, TSDB_CODE_TSC_SQL_SYNTAX_ERROR); + } + // case 7 : mix value and ? + { + do_query(taos, "use stmt2_testdb_3"); + const char* sql = "INSERT INTO ? using stb (t1,t2) TAGS(?,?) (ts,b)VALUES(15910606280001,?)"; + printf("case 7 : %s\n", sql); + getFieldsError(taos, sql, TSDB_CODE_TSC_INVALID_OPERATION); + } + // case 8 : mix value and ? + { + do_query(taos, "use stmt2_testdb_3"); + const char* sql = "INSERT INTO ? using stb (t1,t2) TAGS(?,?) (ts,b)VALUES(15910606280001,'abc')"; + printf("case 8 : %s\n", sql); + getFieldsError(taos, sql, TSDB_CODE_TSC_INVALID_OPERATION); + } do_query(taos, "drop database if exists stmt2_testdb_3"); taos_close(taos); } @@ -1002,6 +1033,15 @@ TEST(stmt2Case, stmt2_insert_non_statndard) { printf("stmt2 [%s] : %s\n", "less params", sql); int code = taos_stmt2_prepare(stmt, sql, 0); checkError(stmt, code); + // test get fields + int fieldNum = 0; + TAOS_FIELD_ALL* pFields = NULL; + code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields); + checkError(stmt, code); + ASSERT_EQ(fieldNum, 2); + ASSERT_STREQ(pFields[0].name, "tbname"); + ASSERT_STREQ(pFields[1].name, "ts"); + int total_affect_rows = 0; int t64_len[2] = {sizeof(int64_t), sizeof(int64_t)}; @@ -1024,11 +1064,22 @@ TEST(stmt2Case, stmt2_insert_non_statndard) { code = taos_stmt2_bind_param(stmt, &bindv, -1); checkError(stmt, code); + code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields); + checkError(stmt, code); + ASSERT_EQ(fieldNum, 2); + ASSERT_STREQ(pFields[0].name, "tbname"); + ASSERT_STREQ(pFields[1].name, "ts"); + int affected_rows; taos_stmt2_exec(stmt, &affected_rows); total_affect_rows += affected_rows; - checkError(stmt, code); + + code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields); + checkError(stmt, code); + ASSERT_EQ(fieldNum, 2); + ASSERT_STREQ(pFields[0].name, "tbname"); + ASSERT_STREQ(pFields[1].name, "ts"); } ASSERT_EQ(total_affect_rows, 12); @@ -1959,27 +2010,27 @@ void stmt2_async_test(std::atomic& stop_task) { stop_task = true; } -TEST(stmt2Case, async_order) { - std::atomic stop_task(false); - std::thread t(stmt2_async_test, std::ref(stop_task)); +// TEST(stmt2Case, async_order) { +// std::atomic stop_task(false); +// std::thread t(stmt2_async_test, std::ref(stop_task)); - // 等待 60 秒钟 - auto start_time = std::chrono::steady_clock::now(); - while (!stop_task) { - auto elapsed_time = std::chrono::steady_clock::now() - start_time; - if (std::chrono::duration_cast(elapsed_time).count() > 100) { - if (t.joinable()) { - t.detach(); - } - FAIL() << "Test[stmt2_async_test] timed out"; - break; - } - std::this_thread::sleep_for(std::chrono::seconds(1)); // 每 1s 检查一次 - } - if (t.joinable()) { - t.join(); - } -} +// // 等待 60 秒钟 +// auto start_time = std::chrono::steady_clock::now(); +// while (!stop_task) { +// auto elapsed_time = std::chrono::steady_clock::now() - start_time; +// if (std::chrono::duration_cast(elapsed_time).count() > 100) { +// if (t.joinable()) { +// t.detach(); +// } +// FAIL() << "Test[stmt2_async_test] timed out"; +// break; +// } +// std::this_thread::sleep_for(std::chrono::seconds(1)); // 每 1s 检查一次 +// } +// if (t.joinable()) { +// t.join(); +// } +// } TEST(stmt2Case, rowformat_bind) { TAOS* taos = taos_connect("localhost", "root", "taosdata", "", 0); diff --git a/source/libs/parser/src/parInsertSql.c b/source/libs/parser/src/parInsertSql.c index 81129a6f8f..25e8ccc1c6 100644 --- a/source/libs/parser/src/parInsertSql.c +++ b/source/libs/parser/src/parInsertSql.c @@ -32,7 +32,7 @@ typedef struct SInsertParseContext { bool needTableTagVal; bool needRequest; // whether or not request server bool isStmtBind; // whether is stmt bind - bool preCtbname; + uint8_t stmtTbNameFlag; } SInsertParseContext; typedef int32_t (*_row_append_fn_t)(SMsgBuf* pMsgBuf, const void* value, int32_t len, void* param); @@ -993,6 +993,10 @@ static int32_t parseTagsClauseImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt code = buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", token.z); break; } + if (pTagVals->size != 0) { + code = buildSyntaxErrMsg(&pCxt->msg, "no mix usage for ? and tag values", token.z); + break; + } continue; } @@ -1026,6 +1030,10 @@ static int32_t parseTagsClauseImpl(SInsertParseContext* pCxt, SVnodeModifyOpStmt pTag = NULL; } + if (code == TSDB_CODE_SUCCESS && !isParseBindParam) { + pCxt->stmtTbNameFlag |= IS_FIXED_TAG; + } + _exit: for (int32_t i = 0; i < taosArrayGetSize(pTagVals); ++i) { STagVal* p = (STagVal*)TARRAY_GET_ELEM(pTagVals, i); @@ -1416,6 +1424,7 @@ static int32_t parseUsingTableName(SInsertParseContext* pCxt, SVnodeModifyOpStmt return getTargetTableSchema(pCxt, pStmt); } pStmt->usingTableProcessing = true; + pCxt->stmtTbNameFlag |= USING_CLAUSE; // pStmt->pSql -> stb_name [(tag1_name, ...) pStmt->pSql += index; int32_t code = parseDuplicateUsingClause(pCxt, pStmt, &pCxt->usingDuplicateTable); @@ -1465,7 +1474,7 @@ static int32_t getTableDataCxt(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pS char tbFName[TSDB_TABLE_FNAME_LEN]; int32_t code = 0; - if (pCxt->preCtbname) { + if ((pCxt->stmtTbNameFlag & NO_DATA_USING_CLAUSE) == USING_CLAUSE) { tstrncpy(pStmt->targetTableName.tname, pStmt->usingTableName.tname, sizeof(pStmt->targetTableName.tname)); tstrncpy(pStmt->targetTableName.dbname, pStmt->usingTableName.dbname, sizeof(pStmt->targetTableName.dbname)); pStmt->targetTableName.type = TSDB_SUPER_TABLE; @@ -2764,6 +2773,7 @@ static int32_t checkTableClauseFirstToken(SInsertParseContext* pCxt, SVnodeModif } if (TK_NK_QUESTION == pTbName->type) { + pCxt->stmtTbNameFlag &= ~IS_FIXED_VALUE; pCxt->isStmtBind = true; if (NULL == pCxt->pComCxt->pStmtCb) { return buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", pTbName->z); @@ -2772,14 +2782,15 @@ static int32_t checkTableClauseFirstToken(SInsertParseContext* pCxt, SVnodeModif char* tbName = NULL; int32_t code = (*pCxt->pComCxt->pStmtCb->getTbNameFn)(pCxt->pComCxt->pStmtCb->pStmt, &tbName); if (TSDB_CODE_SUCCESS == code) { + pCxt->stmtTbNameFlag |= HAS_BIND_VALUE; pTbName->z = tbName; pTbName->n = strlen(tbName); - } else if (code == TSDB_CODE_TSC_STMT_TBNAME_ERROR) { - pCxt->preCtbname = true; - code = TSDB_CODE_SUCCESS; - } else { - return code; } + if (code == TSDB_CODE_TSC_STMT_TBNAME_ERROR) { + pCxt->stmtTbNameFlag &= ~HAS_BIND_VALUE; + code = TSDB_CODE_SUCCESS; + } + return code; } if (TK_NK_ID != pTbName->type && TK_NK_STRING != pTbName->type && TK_NK_QUESTION != pTbName->type) { @@ -2788,26 +2799,34 @@ static int32_t checkTableClauseFirstToken(SInsertParseContext* pCxt, SVnodeModif // db.? situation,ensure that the only thing following the '.' mark is '?' char* tbNameAfterDbName = strnchr(pTbName->z, '.', pTbName->n, true); - if ((tbNameAfterDbName != NULL) && (*(tbNameAfterDbName + 1) == '?')) { - char* tbName = NULL; - if (NULL == pCxt->pComCxt->pStmtCb) { - return buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", pTbName->z); - } - int32_t code = (*pCxt->pComCxt->pStmtCb->getTbNameFn)(pCxt->pComCxt->pStmtCb->pStmt, &tbName); - if (code != TSDB_CODE_SUCCESS) { - pCxt->preCtbname = true; + if (tbNameAfterDbName != NULL) { + if (*(tbNameAfterDbName + 1) == '?') { + pCxt->stmtTbNameFlag &= ~IS_FIXED_VALUE; + char* tbName = NULL; + if (NULL == pCxt->pComCxt->pStmtCb) { + return buildSyntaxErrMsg(&pCxt->msg, "? only used in stmt", pTbName->z); + } + int32_t code = (*pCxt->pComCxt->pStmtCb->getTbNameFn)(pCxt->pComCxt->pStmtCb->pStmt, &tbName); + if (TSDB_CODE_SUCCESS == code) { + pCxt->stmtTbNameFlag |= HAS_BIND_VALUE; + pTbName->z = tbName; + pTbName->n = strlen(tbName); + } + if (code == TSDB_CODE_TSC_STMT_TBNAME_ERROR) { + pCxt->stmtTbNameFlag &= ~HAS_BIND_VALUE; + code = TSDB_CODE_SUCCESS; + } } else { - pTbName->z = tbName; - pTbName->n = strlen(tbName); - } - } - - if (pCxt->isStmtBind) { - if (TK_NK_ID == pTbName->type || (tbNameAfterDbName != NULL && *(tbNameAfterDbName + 1) != '?')) { - // In SQL statements, the table name has already been specified. + pCxt->stmtTbNameFlag |= IS_FIXED_VALUE; parserWarn("QID:0x%" PRIx64 ", table name is specified in sql, ignore the table name in bind param", pCxt->pComCxt->requestId); + *pHasData = true; } + return TSDB_CODE_SUCCESS; + } + + if (TK_NK_ID == pTbName->type) { + pCxt->stmtTbNameFlag |= IS_FIXED_VALUE; } *pHasData = true; @@ -2824,7 +2843,7 @@ static int32_t setStmtInfo(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pStmt) SStmtCallback* pStmtCb = pCxt->pComCxt->pStmtCb; int32_t code = (*pStmtCb->setInfoFn)(pStmtCb->pStmt, pStmt->pTableMeta, tags, &pStmt->targetTableName, pStmt->usingTableProcessing, pStmt->pVgroupsHashObj, pStmt->pTableBlockHashObj, - pStmt->usingTableName.tname, pCxt->preCtbname); + pStmt->usingTableName.tname, pCxt->stmtTbNameFlag); memset(&pCxt->tags, 0, sizeof(pCxt->tags)); pStmt->pVgroupsHashObj = NULL; @@ -2880,9 +2899,6 @@ static int32_t parseInsertBody(SInsertParseContext* pCxt, SVnodeModifyOpStmt* pS if (TSDB_CODE_SUCCESS == code && hasData) { code = parseInsertTableClause(pCxt, pStmt, &token); } - if (TSDB_CODE_PAR_TABLE_NOT_EXIST == code && pCxt->preCtbname) { - code = TSDB_CODE_TSC_STMT_TBNAME_ERROR; - } } if (TSDB_CODE_SUCCESS == code && !pCxt->missCache) { diff --git a/source/libs/parser/src/parInsertStmt.c b/source/libs/parser/src/parInsertStmt.c index 9bb98eb223..2dc9e96264 100644 --- a/source/libs/parser/src/parInsertStmt.c +++ b/source/libs/parser/src/parInsertStmt.c @@ -1070,10 +1070,11 @@ int32_t buildBoundFields(int32_t numOfBound, int16_t* boundColumns, SSchema* pSc } int32_t buildStbBoundFields(SBoundColInfo boundColsInfo, SSchema* pSchema, int32_t* fieldNum, TAOS_FIELD_ALL** fields, - STableMeta* pMeta, void* boundTags, bool preCtbname) { + STableMeta* pMeta, void* boundTags, uint8_t tbNameFlag) { SBoundColInfo* tags = (SBoundColInfo*)boundTags; - bool hastag = tags != NULL; - int32_t numOfBound = boundColsInfo.numOfBound + (preCtbname ? 1 : 0); + bool hastag = (tags != NULL) && !(tbNameFlag & IS_FIXED_TAG); + int32_t numOfBound = + boundColsInfo.numOfBound + ((tbNameFlag & IS_FIXED_VALUE) == 0 && (tbNameFlag & USING_CLAUSE) != 0 ? 1 : 0); if (hastag) { numOfBound += tags->mixTagsCols ? 0 : tags->numOfBound; } @@ -1084,7 +1085,7 @@ int32_t buildStbBoundFields(SBoundColInfo boundColsInfo, SSchema* pSchema, int32 return terrno; } - if (preCtbname && numOfBound != boundColsInfo.numOfBound) { + if ((tbNameFlag & IS_FIXED_VALUE) == 0 && (tbNameFlag & USING_CLAUSE) != 0) { (*fields)[idx].field_type = TAOS_FIELD_TBNAME; tstrncpy((*fields)[idx].name, "tbname", sizeof((*fields)[idx].name)); (*fields)[idx].type = TSDB_DATA_TYPE_BINARY; @@ -1188,7 +1189,7 @@ int32_t qBuildStmtColFields(void* pBlock, int32_t* fieldNum, TAOS_FIELD_E** fiel return TSDB_CODE_SUCCESS; } -int32_t qBuildStmtStbColFields(void* pBlock, void* boundTags, bool preCtbname, int32_t* fieldNum, +int32_t qBuildStmtStbColFields(void* pBlock, void* boundTags, uint8_t tbNameFlag, int32_t* fieldNum, TAOS_FIELD_ALL** fields) { STableDataCxt* pDataBlock = (STableDataCxt*)pBlock; SSchema* pSchema = getTableColumnSchema(pDataBlock->pMeta); @@ -1202,7 +1203,7 @@ int32_t qBuildStmtStbColFields(void* pBlock, void* boundTags, bool preCtbname, i } CHECK_CODE(buildStbBoundFields(pDataBlock->boundColsInfo, pSchema, fieldNum, fields, pDataBlock->pMeta, boundTags, - preCtbname)); + tbNameFlag)); return TSDB_CODE_SUCCESS; } diff --git a/source/util/src/tmempool.c b/source/util/src/tmempool.c index 0a56c69505..96a9cfcea2 100644 --- a/source/util/src/tmempool.c +++ b/source/util/src/tmempool.c @@ -1029,7 +1029,7 @@ void mpUpdateSystemAvailableMemorySize() { atomic_store_64(&tsCurrentAvailMemorySize, sysAvailSize); - uDebug("system available memory size: %" PRId64, sysAvailSize); + uTrace("system available memory size: %" PRId64, sysAvailSize); } void mpSchedTrim(int64_t* loopTimes) { diff --git a/source/util/test/memPoolTest.cpp b/source/util/test/memPoolTest.cpp index 8af8b01368..a0086b8d1c 100644 --- a/source/util/test/memPoolTest.cpp +++ b/source/util/test/memPoolTest.cpp @@ -729,7 +729,7 @@ int32_t mptGetMemPoolMaxMemSize(void* pHandle, int64_t* maxSize) { } if (TSDB_CODE_SUCCESS != code) { - uError("get system avaiable memory size failed, error: 0x%x", code); + uError("get system available memory size failed, error: 0x%x", code); return code; } diff --git a/utils/test/c/sml_test.c b/utils/test/c/sml_test.c index 0907c2a641..4db5a1908d 100644 --- a/utils/test/c/sml_test.c +++ b/utils/test/c/sml_test.c @@ -87,7 +87,8 @@ int smlProcess_telnet_Test() { const char *sql1[] = {"sys.if.bytes.out 1479496100 1.3E0 host=web01 interface=eth0", "sys.if.bytes.out 1479496101 1.3E1 interface=eth0 host=web01 ", "sys.if.bytes.out 1479496102 1.3E3 network=tcp", - " sys.procs.running 1479496100 42 host=web01 "}; + " sys.procs.running 1479496100 42 host=web01 ", + " newline 1479496100 42 host=web\n01 t=fsb\n "}; // for(int i = 0; i < 4; i++){ // strncpy(sql[i], sql1[i], 128); @@ -2355,12 +2356,35 @@ int sml_td17324_Test() { return code; } +int smlProcess_34114_Test() { + TAOS *taos = taos_connect("localhost", "root", "taosdata", NULL, 0); + + TAOS_RES *pRes = taos_query(taos, "create database if not exists sml_34114_db schemaless 1"); + taos_free_result(pRes); + + pRes = taos_query(taos, "use sml_34114_db"); + taos_free_result(pRes); + + char *sql = {"sys.if.bytes.out 1479496100 1.3E0 host=web01 interface=eth0 \nsys.if.bytes.out 1479496101 1.3E1 interface=eth0 host=web01 "}; + int32_t totalRows = 0; + pRes = taos_schemaless_insert_raw(taos, sql, strlen(sql), &totalRows, TSDB_SML_TELNET_PROTOCOL, + TSDB_SML_TIMESTAMP_NANO_SECONDS); + printf("%s result:%s\n", __FUNCTION__, taos_errstr(pRes)); + int code = taos_errno(pRes); + taos_free_result(pRes); + taos_close(taos); + + return code; +} + int main(int argc, char *argv[]) { if (argc == 2) { taos_options(TSDB_OPTION_CONFIGDIR, argv[1]); } - int ret = smlProcess_json0_Test(); + int ret = smlProcess_34114_Test(); + ASSERT(!ret); + ret = smlProcess_json0_Test(); ASSERT(!ret); ret = sml_ts5528_test(); ASSERT(!ret);