From 9ce0c184a169af5be88ec5c50147b27db363186e Mon Sep 17 00:00:00 2001 From: kailixu Date: Thu, 8 Aug 2024 16:44:15 +0800 Subject: [PATCH 01/55] enh: add passwdTest --- source/client/test/CMakeLists.txt | 11 +++++++++++ tests/script/api/passwdTest.c | 18 +++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/source/client/test/CMakeLists.txt b/source/client/test/CMakeLists.txt index 3d70c67661..8e7056821d 100644 --- a/source/client/test/CMakeLists.txt +++ b/source/client/test/CMakeLists.txt @@ -29,6 +29,12 @@ TARGET_LINK_LIBRARIES( # PUBLIC os util common transport monitor parser catalog scheduler function gtest taos_static qcom executor #) +ADD_EXECUTABLE(passwdTest ../../../tests/script/api/passwdTest.c) +TARGET_LINK_LIBRARIES( + passwdTest + PUBLIC taos +) + TARGET_INCLUDE_DIRECTORIES( clientTest PUBLIC "${TD_SOURCE_DIR}/include/client/" @@ -62,3 +68,8 @@ add_test( # NAME clientMonitorTest # COMMAND clientMonitorTest # ) + +add_test( + NAME passwdTest + COMMAND passwdTest +) diff --git a/tests/script/api/passwdTest.c b/tests/script/api/passwdTest.c index 928525750e..bd5e3af755 100644 --- a/tests/script/api/passwdTest.c +++ b/tests/script/api/passwdTest.c @@ -59,7 +59,8 @@ void __taos_notify_cb(void *param, void *ext, int type) { switch (type) { case TAOS_NOTIFY_PASSVER: { ++nPassVerNotified; - printf("%s:%d type:%d user:%s passVer:%d\n", __func__, __LINE__, type, param ? (char *)param : "NULL", *(int *)ext); + printf("%s:%d type:%d user:%s passVer:%d\n", __func__, __LINE__, type, param ? (char *)param : "NULL", + *(int *)ext); break; } case TAOS_NOTIFY_USER_DROPPED: { @@ -191,11 +192,11 @@ static int printResult(TAOS_RES *res, char *output) { printRow(temp, row, fields, numFields); puts(temp); } + return 0; } int main(int argc, char *argv[]) { char qstr[1024]; - // connect to server if (argc < 2) { printf("please input server-ip \n"); @@ -215,6 +216,7 @@ int main(int argc, char *argv[]) { taos_close(taos); taos_cleanup(); + exit(EXIT_SUCCESS); } void createUsers(TAOS *taos, const char *host, char *qstr) { @@ -234,6 +236,7 @@ void createUsers(TAOS *taos, const char *host, char *qstr) { if (code != 0) { fprintf(stderr, "failed to run: taos_set_notify_cb(TAOS_NOTIFY_PASSVER) for user:%s since %d\n", users[i], code); + exit(EXIT_FAILURE); } else { fprintf(stderr, "success to run: taos_set_notify_cb(TAOS_NOTIFY_PASSVER) for user:%s\n", users[i]); } @@ -260,6 +263,7 @@ void passVerTestMulti(const char *host, char *qstr) { if (code != 0) { fprintf(stderr, "failed to run: taos_set_notify_cb since %d\n", code); + exit(EXIT_FAILURE); } else { fprintf(stderr, "success to run: taos_set_notify_cb\n"); } @@ -301,8 +305,7 @@ void passVerTestMulti(const char *host, char *qstr) { fprintf(stderr, "######## %s #########\n", __func__); if (nPassVerNotified == nConn) { - fprintf(stderr, ">>> succeed to get passVer notification since nNotify %d == nConn %d\n", nPassVerNotified, - nConn); + fprintf(stderr, ">>> succeed to get passVer notification since nNotify %d == nConn %d\n", nPassVerNotified, nConn); } else { fprintf(stderr, ">>> failed to get passVer notification since nNotify %d != nConn %d\n", nPassVerNotified, nConn); exit(1); @@ -337,7 +340,7 @@ void sysInfoTest(TAOS *taosRoot, const char *host, char *qstr) { TAOS_RES *res = NULL; int32_t nRep = 0; -_REP: +_REP: fprintf(stderr, "######## %s loop:%d #########\n", __func__, nRep); res = taos_query(taos[0], qstr); if (taos_errno(res) != 0) { @@ -375,7 +378,7 @@ _REP: sleep(1); } - if(++nRep < 5) { + if (++nRep < 5) { goto _REP; } @@ -390,7 +393,7 @@ _REP: fprintf(stderr, "######## %s #########\n", __func__); } static bool isDropUser = true; -void userDroppedTest(TAOS *taos, const char *host, char *qstr) { +void userDroppedTest(TAOS *taos, const char *host, char *qstr) { // users int nTestUsers = nUser; int nLoop = 0; @@ -408,6 +411,7 @@ _loop: if (code != 0) { fprintf(stderr, "failed to run: taos_set_notify_cb:%d for user:%s since %d\n", TAOS_NOTIFY_USER_DROPPED, users[i], code); + exit(EXIT_FAILURE); } else { fprintf(stderr, "success to run: taos_set_notify_cb:%d for user:%s\n", TAOS_NOTIFY_USER_DROPPED, users[i]); } From a23c187674a5200e74d70a39913edcf4d3437713 Mon Sep 17 00:00:00 2001 From: kailixu Date: Thu, 8 Aug 2024 16:55:17 +0800 Subject: [PATCH 02/55] enh: rename passwdTest --- source/client/test/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/client/test/CMakeLists.txt b/source/client/test/CMakeLists.txt index 8e7056821d..c8b13f6532 100644 --- a/source/client/test/CMakeLists.txt +++ b/source/client/test/CMakeLists.txt @@ -29,9 +29,9 @@ TARGET_LINK_LIBRARIES( # PUBLIC os util common transport monitor parser catalog scheduler function gtest taos_static qcom executor #) -ADD_EXECUTABLE(passwdTest ../../../tests/script/api/passwdTest.c) +ADD_EXECUTABLE(userOperTest ../../../tests/script/api/passwdTest.c) TARGET_LINK_LIBRARIES( - passwdTest + userOperTest PUBLIC taos ) @@ -70,6 +70,6 @@ add_test( # ) add_test( - NAME passwdTest - COMMAND passwdTest + NAME userOperTest + COMMAND userOperTest ) From f887ec2a0d6d4506f2e307273ea03503ce3b20cc Mon Sep 17 00:00:00 2001 From: charles Date: Fri, 9 Aug 2024 09:26:54 +0800 Subject: [PATCH 03/55] add test case test_passwd by charles --- tests/army/user/test_passwd.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/army/user/test_passwd.py diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py new file mode 100644 index 0000000000..2d448b8f0e --- /dev/null +++ b/tests/army/user/test_passwd.py @@ -0,0 +1,20 @@ +import subprocess +from frame.log import * +from frame.cases import * +from frame.sql import * +from frame.caseBase import * +from frame.epath import * +from frame import * + + +class TDTestCase(TBase): + def run(self): + c_file_path = os.sep.join([binPath(), "passwdTest localhost"]) + p = subprocess.Popen(c_file_path, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if 0 != p.returncode: + tdLog.error("Failed to run passwd test with output: %s \n error: %s" % (out, err)) + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) From b330edf535ec3f91aae49abfa0bafa81011be557 Mon Sep 17 00:00:00 2001 From: charles Date: Fri, 9 Aug 2024 09:26:54 +0800 Subject: [PATCH 04/55] add test case test_passwd by charles --- tests/army/user/test_passwd.py | 20 ++++++++++++++++++++ tests/parallel_test/cases.task | 1 + 2 files changed, 21 insertions(+) create mode 100644 tests/army/user/test_passwd.py diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py new file mode 100644 index 0000000000..2d448b8f0e --- /dev/null +++ b/tests/army/user/test_passwd.py @@ -0,0 +1,20 @@ +import subprocess +from frame.log import * +from frame.cases import * +from frame.sql import * +from frame.caseBase import * +from frame.epath import * +from frame import * + + +class TDTestCase(TBase): + def run(self): + c_file_path = os.sep.join([binPath(), "passwdTest localhost"]) + p = subprocess.Popen(c_file_path, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if 0 != p.returncode: + tdLog.error("Failed to run passwd test with output: %s \n error: %s" % (out, err)) + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index e70042001d..bc30701e82 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -35,6 +35,7 @@ ,,y,army,./pytest.sh python3 ./test.py -f storage/compressBasic.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f grant/grantBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/queryBugs.py -N 3 +,,y,army,./pytest.sh python3 ./test.py -f user/test_passwd.py # # system test From 1d6f90f5ff4816cacb92efc929d0a7521ae50cc5 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Thu, 17 Oct 2024 11:33:27 +0800 Subject: [PATCH 05/55] fix:[TD-32563] remove config variable slowLogThresholdTestg --- include/common/tglobal.h | 1 - include/common/tmsg.h | 2 +- source/client/src/clientEnv.c | 3 +-- source/common/src/tglobal.c | 6 ------ source/common/src/tmsg.c | 4 ++-- source/dnode/mgmt/mgmt_dnode/src/dmHandle.c | 1 - source/dnode/mnode/impl/src/mndDnode.c | 1 - source/dnode/mnode/impl/src/mndProfile.c | 2 -- 8 files changed, 4 insertions(+), 16 deletions(-) diff --git a/include/common/tglobal.h b/include/common/tglobal.h index cf918c6e0d..b7b89e4f30 100644 --- a/include/common/tglobal.h +++ b/include/common/tglobal.h @@ -179,7 +179,6 @@ extern int32_t tsMaxRetryWaitTime; extern bool tsUseAdapter; extern int32_t tsMetaCacheMaxSize; extern int32_t tsSlowLogThreshold; -extern int32_t tsSlowLogThresholdTest; extern char tsSlowLogExceptDb[]; extern int32_t tsSlowLogScope; extern int32_t tsSlowLogMaxLen; diff --git a/include/common/tmsg.h b/include/common/tmsg.h index 1a10f02c96..5953ed3e21 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -662,7 +662,7 @@ typedef struct { int32_t tsSlowLogThreshold; int32_t tsSlowLogMaxLen; int32_t tsSlowLogScope; - int32_t tsSlowLogThresholdTest; + int32_t tsSlowLogThresholdTest; //Obsolete char tsSlowLogExceptDb[TSDB_DB_NAME_LEN]; } SMonitorParas; diff --git a/source/client/src/clientEnv.c b/source/client/src/clientEnv.c index 21f7c93036..dd681f8a46 100644 --- a/source/client/src/clientEnv.c +++ b/source/client/src/clientEnv.c @@ -294,8 +294,7 @@ static void deregisterRequest(SRequestObj *pRequest) { } } - if ((duration >= pTscObj->pAppInfo->monitorParas.tsSlowLogThreshold * 1000000UL || - duration >= pTscObj->pAppInfo->monitorParas.tsSlowLogThresholdTest * 1000000UL) && + if ((duration >= pTscObj->pAppInfo->monitorParas.tsSlowLogThreshold * 1000000UL) && checkSlowLogExceptDb(pRequest, pTscObj->pAppInfo->monitorParas.tsSlowLogExceptDb)) { (void)atomic_add_fetch_64((int64_t *)&pActivity->numOfSlowQueries, 1); if (pTscObj->pAppInfo->monitorParas.tsSlowLogScope & reqType) { diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index 3c05294264..9866b3f760 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -181,7 +181,6 @@ int32_t tsMaxRetryWaitTime = 10000; bool tsUseAdapter = false; int32_t tsMetaCacheMaxSize = -1; // MB int32_t tsSlowLogThreshold = 10; // seconds -int32_t tsSlowLogThresholdTest = INT32_MAX; // seconds char tsSlowLogExceptDb[TSDB_DB_NAME_LEN] = ""; // seconds int32_t tsSlowLogScope = SLOW_LOG_TYPE_QUERY; char* tsSlowLogScopeString = "query"; @@ -749,7 +748,6 @@ static int32_t taosAddServerCfg(SConfig *pCfg) { TAOS_CHECK_RETURN(cfgAddBool(pCfg, "monitor", tsEnableMonitor, CFG_SCOPE_SERVER, CFG_DYN_SERVER)); TAOS_CHECK_RETURN(cfgAddInt32(pCfg, "monitorInterval", tsMonitorInterval, 1, 86400, CFG_SCOPE_SERVER, CFG_DYN_SERVER)); - TAOS_CHECK_RETURN(cfgAddInt32(pCfg, "slowLogThresholdTest", tsSlowLogThresholdTest, 0, INT32_MAX, CFG_SCOPE_SERVER, CFG_DYN_SERVER)); TAOS_CHECK_RETURN(cfgAddInt32(pCfg, "slowLogThreshold", tsSlowLogThreshold, 1, INT32_MAX, CFG_SCOPE_SERVER, CFG_DYN_SERVER)); TAOS_CHECK_RETURN(cfgAddInt32(pCfg, "slowLogMaxLen", tsSlowLogMaxLen, 1, 16384, CFG_SCOPE_SERVER, CFG_DYN_SERVER)); TAOS_CHECK_RETURN(cfgAddString(pCfg, "slowLogScope", tsSlowLogScopeString, CFG_SCOPE_SERVER, CFG_DYN_SERVER)); @@ -1401,9 +1399,6 @@ static int32_t taosSetServerCfg(SConfig *pCfg) { TAOS_CHECK_GET_CFG_ITEM(pCfg, pItem, "slowLogExceptDb"); tstrncpy(tsSlowLogExceptDb, pItem->str, TSDB_DB_NAME_LEN); - TAOS_CHECK_GET_CFG_ITEM(pCfg, pItem, "slowLogThresholdTest"); - tsSlowLogThresholdTest = pItem->i32; - TAOS_CHECK_GET_CFG_ITEM(pCfg, pItem, "slowLogThreshold"); tsSlowLogThreshold = pItem->i32; @@ -1968,7 +1963,6 @@ static int32_t taosCfgDynamicOptionsForServer(SConfig *pCfg, const char *name) { {"monitor", &tsEnableMonitor}, {"monitorInterval", &tsMonitorInterval}, {"slowLogThreshold", &tsSlowLogThreshold}, - {"slowLogThresholdTest", &tsSlowLogThresholdTest}, {"slowLogMaxLen", &tsSlowLogMaxLen}, {"mndSdbWriteDelta", &tsMndSdbWriteDelta}, diff --git a/source/common/src/tmsg.c b/source/common/src/tmsg.c index 4c4b78278e..f2151faec3 100644 --- a/source/common/src/tmsg.c +++ b/source/common/src/tmsg.c @@ -75,7 +75,7 @@ static int32_t tSerializeSMonitorParas(SEncoder *encoder, const SMonitorParas *p TAOS_CHECK_RETURN(tEncodeI32(encoder, pMonitorParas->tsSlowLogScope)); TAOS_CHECK_RETURN(tEncodeI32(encoder, pMonitorParas->tsSlowLogMaxLen)); TAOS_CHECK_RETURN(tEncodeI32(encoder, pMonitorParas->tsSlowLogThreshold)); - TAOS_CHECK_RETURN(tEncodeI32(encoder, pMonitorParas->tsSlowLogThresholdTest)); + TAOS_CHECK_RETURN(tEncodeI32(encoder, pMonitorParas->tsSlowLogThresholdTest)); //Obsolete TAOS_CHECK_RETURN(tEncodeCStr(encoder, pMonitorParas->tsSlowLogExceptDb)); return 0; } @@ -86,7 +86,7 @@ static int32_t tDeserializeSMonitorParas(SDecoder *decoder, SMonitorParas *pMoni TAOS_CHECK_RETURN(tDecodeI32(decoder, &pMonitorParas->tsSlowLogScope)); TAOS_CHECK_RETURN(tDecodeI32(decoder, &pMonitorParas->tsSlowLogMaxLen)); TAOS_CHECK_RETURN(tDecodeI32(decoder, &pMonitorParas->tsSlowLogThreshold)); - TAOS_CHECK_RETURN(tDecodeI32(decoder, &pMonitorParas->tsSlowLogThresholdTest)); + TAOS_CHECK_RETURN(tDecodeI32(decoder, &pMonitorParas->tsSlowLogThresholdTest)); //Obsolete TAOS_CHECK_RETURN(tDecodeCStrTo(decoder, pMonitorParas->tsSlowLogExceptDb)); return 0; } diff --git a/source/dnode/mgmt/mgmt_dnode/src/dmHandle.c b/source/dnode/mgmt/mgmt_dnode/src/dmHandle.c index 87b1ae0efa..666b916115 100644 --- a/source/dnode/mgmt/mgmt_dnode/src/dmHandle.c +++ b/source/dnode/mgmt/mgmt_dnode/src/dmHandle.c @@ -151,7 +151,6 @@ void dmSendStatusReq(SDnodeMgmt *pMgmt) { req.clusterCfg.monitorParas.tsSlowLogScope = tsSlowLogScope; req.clusterCfg.monitorParas.tsSlowLogMaxLen = tsSlowLogMaxLen; req.clusterCfg.monitorParas.tsSlowLogThreshold = tsSlowLogThreshold; - req.clusterCfg.monitorParas.tsSlowLogThresholdTest = tsSlowLogThresholdTest; tstrncpy(req.clusterCfg.monitorParas.tsSlowLogExceptDb, tsSlowLogExceptDb, TSDB_DB_NAME_LEN); char timestr[32] = "1970-01-01 00:00:00.00"; if (taosParseTime(timestr, &req.clusterCfg.checkTime, (int32_t)strlen(timestr), TSDB_TIME_PRECISION_MILLI, 0) != 0) { diff --git a/source/dnode/mnode/impl/src/mndDnode.c b/source/dnode/mnode/impl/src/mndDnode.c index 04041646eb..f1c6765c9b 100644 --- a/source/dnode/mnode/impl/src/mndDnode.c +++ b/source/dnode/mnode/impl/src/mndDnode.c @@ -480,7 +480,6 @@ static int32_t mndCheckClusterCfgPara(SMnode *pMnode, SDnodeObj *pDnode, const S CHECK_MONITOR_PARA(tsEnableMonitor, DND_REASON_STATUS_MONITOR_SWITCH_NOT_MATCH); CHECK_MONITOR_PARA(tsMonitorInterval, DND_REASON_STATUS_MONITOR_INTERVAL_NOT_MATCH); CHECK_MONITOR_PARA(tsSlowLogThreshold, DND_REASON_STATUS_MONITOR_SLOW_LOG_THRESHOLD_NOT_MATCH); - CHECK_MONITOR_PARA(tsSlowLogThresholdTest, DND_REASON_STATUS_MONITOR_NOT_MATCH); CHECK_MONITOR_PARA(tsSlowLogMaxLen, DND_REASON_STATUS_MONITOR_SLOW_LOG_SQL_MAX_LEN_NOT_MATCH); CHECK_MONITOR_PARA(tsSlowLogScope, DND_REASON_STATUS_MONITOR_SLOW_LOG_SCOPE_NOT_MATCH); diff --git a/source/dnode/mnode/impl/src/mndProfile.c b/source/dnode/mnode/impl/src/mndProfile.c index a1ffee9b06..fa98f91ab1 100644 --- a/source/dnode/mnode/impl/src/mndProfile.c +++ b/source/dnode/mnode/impl/src/mndProfile.c @@ -304,7 +304,6 @@ static int32_t mndProcessConnectReq(SRpcMsg *pReq) { connectRsp.monitorParas.tsSlowLogScope = tsSlowLogScope; connectRsp.monitorParas.tsSlowLogMaxLen = tsSlowLogMaxLen; connectRsp.monitorParas.tsSlowLogThreshold = tsSlowLogThreshold; - connectRsp.monitorParas.tsSlowLogThresholdTest = tsSlowLogThresholdTest; tstrncpy(connectRsp.monitorParas.tsSlowLogExceptDb, tsSlowLogExceptDb, TSDB_DB_NAME_LEN); connectRsp.whiteListVer = pUser->ipWhiteListVer; @@ -705,7 +704,6 @@ static int32_t mndProcessHeartBeatReq(SRpcMsg *pReq) { batchRsp.monitorParas.tsEnableMonitor = tsEnableMonitor; batchRsp.monitorParas.tsMonitorInterval = tsMonitorInterval; batchRsp.monitorParas.tsSlowLogThreshold = tsSlowLogThreshold; - batchRsp.monitorParas.tsSlowLogThresholdTest = tsSlowLogThresholdTest; tstrncpy(batchRsp.monitorParas.tsSlowLogExceptDb, tsSlowLogExceptDb, TSDB_DB_NAME_LEN); batchRsp.monitorParas.tsSlowLogMaxLen = tsSlowLogMaxLen; batchRsp.monitorParas.tsSlowLogScope = tsSlowLogScope; From 7ff0fb497a7a924a1665c4dfd966b51457b81a64 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Tue, 5 Nov 2024 15:46:36 +0800 Subject: [PATCH 06/55] Update test_passwd.py to replace tdLog.error with tdLog.exit --- tests/army/user/test_passwd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index 2d448b8f0e..ac82ee0d55 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -13,7 +13,7 @@ class TDTestCase(TBase): p = subprocess.Popen(c_file_path, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if 0 != p.returncode: - tdLog.error("Failed to run passwd test with output: %s \n error: %s" % (out, err)) + tdLog.exit("Failed to run passwd test with output: %s \n error: %s" % (out, err)) tdLog.success(f"{__file__} successfully executed") tdCases.addLinux(__file__, TDTestCase()) From 9003883edc83320300ef2ebf56e89870b7d4d6cc Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Tue, 5 Nov 2024 20:23:45 +0800 Subject: [PATCH 07/55] Update makefile to remove the stmt.c which doesn't exist --- tests/script/api/makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/script/api/makefile b/tests/script/api/makefile index 9c2bb6be3d..6b8a920e33 100644 --- a/tests/script/api/makefile +++ b/tests/script/api/makefile @@ -22,7 +22,7 @@ exe: gcc $(CFLAGS) ./insert_stb.c -o $(ROOT)insert_stb $(LFLAGS) gcc $(CFLAGS) ./tmqViewTest.c -o $(ROOT)tmqViewTest $(LFLAGS) gcc $(CFLAGS) ./stmtQuery.c -o $(ROOT)stmtQuery $(LFLAGS) - gcc $(CFLAGS) ./stmt.c -o $(ROOT)stmt $(LFLAGS) + # gcc $(CFLAGS) ./stmt.c -o $(ROOT)stmt $(LFLAGS) gcc $(CFLAGS) ./stmt2.c -o $(ROOT)stmt2 $(LFLAGS) gcc $(CFLAGS) ./stmt2-example.c -o $(ROOT)stmt2-example $(LFLAGS) gcc $(CFLAGS) ./stmt2-get-fields.c -o $(ROOT)stmt2-get-fields $(LFLAGS) From 0d5d88086ae1a2f48309720c2afb0b7f80be697b Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Tue, 5 Nov 2024 20:25:02 +0800 Subject: [PATCH 08/55] Update test_passwd.py --- tests/army/user/test_passwd.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index ac82ee0d55..cc0ab1a113 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -6,15 +6,37 @@ from frame.caseBase import * from frame.epath import * from frame import * - class TDTestCase(TBase): + def apiPath(self): + apiPath = None + currentFilePath = os.path.dirname(os.path.realpath(__file__)) + if ("community/tests" in currentFilePath): + testFilePath = currentFilePath[:currentFilePath.find("community/tests")] + else: + testFilePath = currentFilePath[:currentFilePath.find("TDengine/tests")] + + for root, dirs, files in os.walk(testFilePath): + if ("passwdTest.c" in files): + apiPath = root + break + return apiPath + def run(self): - c_file_path = os.sep.join([binPath(), "passwdTest localhost"]) - p = subprocess.Popen(c_file_path, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + apiPath = self.apiPath() + if apiPath: + p = subprocess.Popen(f"cd {apiPath} && make", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if 0 != p.returncode: + tdLog.exit("Test script passwdTest.c make failed") + test_file_cmd = os.sep.join([apiPath, "passwdTest localhost"]) + else: + tdLog.exit("passwdTest.c not found") + p = subprocess.Popen(test_file_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if 0 != p.returncode: tdLog.exit("Failed to run passwd test with output: %s \n error: %s" % (out, err)) tdLog.success(f"{__file__} successfully executed") + tdCases.addLinux(__file__, TDTestCase()) tdCases.addWindows(__file__, TDTestCase()) From dcb63d054fb9ee566c754d06ca91b331860eaec3 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Wed, 6 Nov 2024 08:05:51 +0800 Subject: [PATCH 09/55] Update makefile to remove the failed test cases --- tests/script/api/makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/script/api/makefile b/tests/script/api/makefile index 6b8a920e33..ce5980b37a 100644 --- a/tests/script/api/makefile +++ b/tests/script/api/makefile @@ -13,7 +13,7 @@ all: $(TARGET) exe: gcc $(CFLAGS) ./batchprepare.c -o $(ROOT)batchprepare $(LFLAGS) - gcc $(CFLAGS) ./stmt2-test.c -o $(ROOT)stmt2-test $(LFLAGS) + # gcc $(CFLAGS) ./stmt2-test.c -o $(ROOT)stmt2-test $(LFLAGS) gcc $(CFLAGS) ./stopquery.c -o $(ROOT)stopquery $(LFLAGS) gcc $(CFLAGS) ./dbTableRoute.c -o $(ROOT)dbTableRoute $(LFLAGS) gcc $(CFLAGS) ./insertSameTs.c -o $(ROOT)insertSameTs $(LFLAGS) @@ -23,10 +23,10 @@ exe: gcc $(CFLAGS) ./tmqViewTest.c -o $(ROOT)tmqViewTest $(LFLAGS) gcc $(CFLAGS) ./stmtQuery.c -o $(ROOT)stmtQuery $(LFLAGS) # gcc $(CFLAGS) ./stmt.c -o $(ROOT)stmt $(LFLAGS) - gcc $(CFLAGS) ./stmt2.c -o $(ROOT)stmt2 $(LFLAGS) - gcc $(CFLAGS) ./stmt2-example.c -o $(ROOT)stmt2-example $(LFLAGS) - gcc $(CFLAGS) ./stmt2-get-fields.c -o $(ROOT)stmt2-get-fields $(LFLAGS) - gcc $(CFLAGS) ./stmt2-nohole.c -o $(ROOT)stmt2-nohole $(LFLAGS) + # gcc $(CFLAGS) ./stmt2.c -o $(ROOT)stmt2 $(LFLAGS) + # gcc $(CFLAGS) ./stmt2-example.c -o $(ROOT)stmt2-example $(LFLAGS) + # gcc $(CFLAGS) ./stmt2-get-fields.c -o $(ROOT)stmt2-get-fields $(LFLAGS) + # gcc $(CFLAGS) ./stmt2-nohole.c -o $(ROOT)stmt2-nohole $(LFLAGS) gcc $(CFLAGS) ./stmt-crash.c -o $(ROOT)stmt-crash $(LFLAGS) clean: From ffe1776f3e2032f5f74875d5f03a665303de9f24 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Wed, 6 Nov 2024 09:45:19 +0800 Subject: [PATCH 10/55] Update test_passwd.py to add some log for debug --- tests/army/user/test_passwd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index cc0ab1a113..1017805105 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -23,11 +23,12 @@ class TDTestCase(TBase): def run(self): apiPath = self.apiPath() + tdLog.info(f"api path: {apiPath}") if apiPath: p = subprocess.Popen(f"cd {apiPath} && make", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if 0 != p.returncode: - tdLog.exit("Test script passwdTest.c make failed") + tdLog.exit(f"Test script passwdTest.c make failed with error: {err}") test_file_cmd = os.sep.join([apiPath, "passwdTest localhost"]) else: tdLog.exit("passwdTest.c not found") From eea5eea7b1af125a17e13c1252dd45e1878e36c4 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Wed, 6 Nov 2024 11:26:22 +0800 Subject: [PATCH 11/55] Update cases.task to close santilizer for case test_passwd.py --- tests/parallel_test/cases.task | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index c85073c951..3dc64e9de1 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -44,7 +44,7 @@ ,,y,army,./pytest.sh python3 ./test.py -f storage/compressBasic.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f grant/grantBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/queryBugs.py -N 3 -,,y,army,./pytest.sh python3 ./test.py -f user/test_passwd.py +,,n,army,./pytest.sh python3 ./test.py -f user/test_passwd.py ,,y,army,./pytest.sh python3 ./test.py -f tmq/tmqBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/fill/fill_compare_asc_desc.py ,,y,army,./pytest.sh python3 ./test.py -f query/last/test_last.py From e5098f82fa3d1ef6e48beba0e40613d8174e9fee Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Wed, 6 Nov 2024 11:33:25 +0800 Subject: [PATCH 12/55] Update cases.task --- tests/parallel_test/cases.task | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 3dc64e9de1..c85073c951 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -44,7 +44,7 @@ ,,y,army,./pytest.sh python3 ./test.py -f storage/compressBasic.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f grant/grantBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/queryBugs.py -N 3 -,,n,army,./pytest.sh python3 ./test.py -f user/test_passwd.py +,,y,army,./pytest.sh python3 ./test.py -f user/test_passwd.py ,,y,army,./pytest.sh python3 ./test.py -f tmq/tmqBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/fill/fill_compare_asc_desc.py ,,y,army,./pytest.sh python3 ./test.py -f query/last/test_last.py From aaddf18e0091a762281afaa07b15b32b45300dbe Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Wed, 6 Nov 2024 11:35:42 +0800 Subject: [PATCH 13/55] Update test_passwd.py --- tests/army/user/test_passwd.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index 1017805105..62dc5782b7 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -28,16 +28,18 @@ class TDTestCase(TBase): p = subprocess.Popen(f"cd {apiPath} && make", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() if 0 != p.returncode: - tdLog.exit(f"Test script passwdTest.c make failed with error: {err}") + tdLog.info(f"Test script passwdTest.c make failed with error: {err}") test_file_cmd = os.sep.join([apiPath, "passwdTest localhost"]) else: tdLog.exit("passwdTest.c not found") - p = subprocess.Popen(test_file_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if 0 != p.returncode: - tdLog.exit("Failed to run passwd test with output: %s \n error: %s" % (out, err)) - tdLog.success(f"{__file__} successfully executed") - + try: + p = subprocess.Popen(test_file_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if 0 != p.returncode: + tdLog.exit("Failed to run passwd test with output: %s \n error: %s" % (out, err)) + tdLog.success(f"{__file__} successfully executed") + except Exception as e: + tdLog.exit(f"Failed to execute {__file__} with error: {e}") tdCases.addLinux(__file__, TDTestCase()) tdCases.addWindows(__file__, TDTestCase()) From f78124ff3a9675750d840ba37d4c44f58f695a9d Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Wed, 6 Nov 2024 15:05:39 +0800 Subject: [PATCH 14/55] Update cases.task to remove santilizer to avoid the make failed --- tests/parallel_test/cases.task | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index c85073c951..3dc64e9de1 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -44,7 +44,7 @@ ,,y,army,./pytest.sh python3 ./test.py -f storage/compressBasic.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f grant/grantBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/queryBugs.py -N 3 -,,y,army,./pytest.sh python3 ./test.py -f user/test_passwd.py +,,n,army,./pytest.sh python3 ./test.py -f user/test_passwd.py ,,y,army,./pytest.sh python3 ./test.py -f tmq/tmqBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/fill/fill_compare_asc_desc.py ,,y,army,./pytest.sh python3 ./test.py -f query/last/test_last.py From c196dd86e08e91d396696385aca9d8d2bce8a44f Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Thu, 7 Nov 2024 07:55:47 +0800 Subject: [PATCH 15/55] Update cases.task --- tests/parallel_test/cases.task | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 3dc64e9de1..cf3452b056 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -44,7 +44,7 @@ ,,y,army,./pytest.sh python3 ./test.py -f storage/compressBasic.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f grant/grantBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/queryBugs.py -N 3 -,,n,army,./pytest.sh python3 ./test.py -f user/test_passwd.py +,,n,army,python3 ./test.py -f user/test_passwd.py ,,y,army,./pytest.sh python3 ./test.py -f tmq/tmqBugs.py -N 3 ,,y,army,./pytest.sh python3 ./test.py -f query/fill/fill_compare_asc_desc.py ,,y,army,./pytest.sh python3 ./test.py -f query/last/test_last.py From 43a337ef748d3f7187702772f318731107b46e32 Mon Sep 17 00:00:00 2001 From: Jinqing Kuang Date: Tue, 5 Nov 2024 10:57:44 +0800 Subject: [PATCH 16/55] enh(query)[TD-32732]: add client Id generation and usage To address the issue of non-unique identifiers for query tasks, a unique client ID (cid) is now generated during client initialization. This cid is included in every task message sent to the server. The server will use a composite key of to uniquely identify each query task. --- include/common/tmsg.h | 9 + include/libs/executor/executor.h | 2 +- include/libs/nodes/plannodes.h | 1 + include/libs/qworker/qworker.h | 8 +- include/libs/scheduler/scheduler.h | 3 + source/client/src/clientEnv.c | 1 + source/common/src/tmsg.c | 40 ++ source/libs/executor/src/exchangeoperator.c | 67 ++-- source/libs/nodes/src/nodesCloneFuncs.c | 1 + source/libs/nodes/src/nodesCodeFuncs.c | 7 + source/libs/nodes/src/nodesMsgFuncs.c | 6 + source/libs/qworker/inc/qwInt.h | 64 +-- source/libs/qworker/src/qwDbg.c | 8 +- source/libs/qworker/src/qwMsg.c | 13 + source/libs/qworker/src/qwUtil.c | 37 +- source/libs/qworker/src/qworker.c | 14 +- source/libs/scheduler/inc/schInt.h | 38 +- source/libs/scheduler/src/schRemote.c | 14 +- source/libs/scheduler/src/schTask.c | 25 +- source/libs/scheduler/src/schUtil.c | 12 + tests/script/api/sameReqidTest.c | 406 ++++++++++++++++++++ 21 files changed, 650 insertions(+), 126 deletions(-) create mode 100644 tests/script/api/sameReqidTest.c diff --git a/include/common/tmsg.h b/include/common/tmsg.h index 7ff70b243a..a7da778513 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -2307,6 +2307,7 @@ typedef struct { typedef struct { SExplainRsp rsp; uint64_t qId; + uint64_t cId; uint64_t tId; int64_t rId; int32_t eId; @@ -2660,6 +2661,7 @@ typedef struct SSubQueryMsg { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; int64_t refId; int32_t execId; @@ -2689,6 +2691,7 @@ typedef struct { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; int32_t execId; } SQueryContinueReq; @@ -2723,6 +2726,7 @@ typedef struct { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; int32_t execId; SOperatorParam* pOpParam; @@ -2738,6 +2742,7 @@ typedef struct { typedef struct { uint64_t queryId; + uint64_t clientId; uint64_t taskId; int64_t refId; int32_t execId; @@ -2784,6 +2789,7 @@ typedef struct { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; int64_t refId; int32_t execId; @@ -2797,6 +2803,7 @@ typedef struct { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; int64_t refId; int32_t execId; @@ -2813,6 +2820,7 @@ typedef struct { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; int64_t refId; int32_t execId; @@ -4261,6 +4269,7 @@ typedef struct { SMsgHead header; uint64_t sId; uint64_t queryId; + uint64_t clientId; uint64_t taskId; uint32_t sqlLen; uint32_t phyLen; diff --git a/include/libs/executor/executor.h b/include/libs/executor/executor.h index d955a7b3b9..82cb899cb6 100644 --- a/include/libs/executor/executor.h +++ b/include/libs/executor/executor.h @@ -31,7 +31,7 @@ typedef void* DataSinkHandle; struct SRpcMsg; struct SSubplan; -typedef int32_t (*localFetchFp)(void*, uint64_t, uint64_t, uint64_t, int64_t, int32_t, void**, SArray*); +typedef int32_t (*localFetchFp)(void*, uint64_t, uint64_t, uint64_t, uint64_t, int64_t, int32_t, void**, SArray*); typedef struct { void* handle; diff --git a/include/libs/nodes/plannodes.h b/include/libs/nodes/plannodes.h index cfd9c1a422..48852e5552 100644 --- a/include/libs/nodes/plannodes.h +++ b/include/libs/nodes/plannodes.h @@ -624,6 +624,7 @@ typedef struct SAggPhysiNode { typedef struct SDownstreamSourceNode { ENodeType type; SQueryNodeAddr addr; + uint64_t clientId; uint64_t taskId; uint64_t schedId; int32_t execId; diff --git a/include/libs/qworker/qworker.h b/include/libs/qworker/qworker.h index 83daf0376c..cb4e359727 100644 --- a/include/libs/qworker/qworker.h +++ b/include/libs/qworker/qworker.h @@ -105,11 +105,11 @@ void qWorkerDestroy(void **qWorkerMgmt); int32_t qWorkerGetStat(SReadHandle *handle, void *qWorkerMgmt, SQWorkerStat *pStat); -int32_t qWorkerProcessLocalQuery(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId, - SQWMsg *qwMsg, SArray *explainRes); +int32_t qWorkerProcessLocalQuery(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId, + int32_t eId, SQWMsg *qwMsg, SArray *explainRes); -int32_t qWorkerProcessLocalFetch(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId, - void **pRsp, SArray *explainRes); +int32_t qWorkerProcessLocalFetch(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId, + int32_t eId, void **pRsp, SArray *explainRes); int32_t qWorkerDbgEnableDebug(char *option); diff --git a/include/libs/scheduler/scheduler.h b/include/libs/scheduler/scheduler.h index af8deff1a0..2988ffc4b1 100644 --- a/include/libs/scheduler/scheduler.h +++ b/include/libs/scheduler/scheduler.h @@ -83,6 +83,9 @@ void schedulerStopQueryHb(void* pTrans); int32_t schedulerUpdatePolicy(int32_t policy); int32_t schedulerEnableReSchedule(bool enableResche); +int32_t initClientId(void); +uint64_t getClientId(void); + /** * Cancel query job * @param pJob diff --git a/source/client/src/clientEnv.c b/source/client/src/clientEnv.c index c56a627ec7..fa9df5be73 100644 --- a/source/client/src/clientEnv.c +++ b/source/client/src/clientEnv.c @@ -983,6 +983,7 @@ void taos_init_imp(void) { SCatalogCfg cfg = {.maxDBCacheNum = 100, .maxTblCacheNum = 100}; ENV_ERR_RET(catalogInit(&cfg), "failed to init catalog"); ENV_ERR_RET(schedulerInit(), "failed to init scheduler"); + ENV_ERR_RET(initClientId(), "failed to init clientId"); tscDebug("starting to initialize TAOS driver"); diff --git a/source/common/src/tmsg.c b/source/common/src/tmsg.c index 458badc764..8b837a2dc3 100644 --- a/source/common/src/tmsg.c +++ b/source/common/src/tmsg.c @@ -8717,6 +8717,7 @@ int32_t tSerializeSSubQueryMsg(void *buf, int32_t bufLen, SSubQueryMsg *pReq) { TAOS_CHECK_EXIT(tEncodeCStrWithLen(&encoder, pReq->sql, pReq->sqlLen)); TAOS_CHECK_EXIT(tEncodeU32(&encoder, pReq->msgLen)); TAOS_CHECK_EXIT(tEncodeBinary(&encoder, (uint8_t *)pReq->msg, pReq->msgLen)); + TAOS_CHECK_EXIT(tEncodeU64(&encoder, pReq->clientId)); tEndEncode(&encoder); @@ -8765,6 +8766,11 @@ int32_t tDeserializeSSubQueryMsg(void *buf, int32_t bufLen, SSubQueryMsg *pReq) TAOS_CHECK_EXIT(tDecodeCStrAlloc(&decoder, &pReq->sql)); TAOS_CHECK_EXIT(tDecodeU32(&decoder, &pReq->msgLen)); TAOS_CHECK_EXIT(tDecodeBinaryAlloc(&decoder, (void **)&pReq->msg, NULL)); + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeU64(&decoder, &pReq->clientId)); + } else { + pReq->clientId = 0; + } tEndDecode(&decoder); @@ -8894,6 +8900,7 @@ int32_t tSerializeSResFetchReq(void *buf, int32_t bufLen, SResFetchReq *pReq) { } else { TAOS_CHECK_EXIT(tEncodeI32(&encoder, 0)); } + TAOS_CHECK_EXIT(tEncodeU64(&encoder, pReq->clientId)); tEndEncode(&encoder); @@ -8943,6 +8950,11 @@ int32_t tDeserializeSResFetchReq(void *buf, int32_t bufLen, SResFetchReq *pReq) } TAOS_CHECK_EXIT(tDeserializeSOperatorParam(&decoder, pReq->pOpParam)); } + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeU64(&decoder, &pReq->clientId)); + } else { + pReq->clientId = 0; + } tEndDecode(&decoder); @@ -9055,6 +9067,7 @@ int32_t tSerializeSTaskDropReq(void *buf, int32_t bufLen, STaskDropReq *pReq) { TAOS_CHECK_EXIT(tEncodeU64(&encoder, pReq->taskId)); TAOS_CHECK_EXIT(tEncodeI64(&encoder, pReq->refId)); TAOS_CHECK_EXIT(tEncodeI32(&encoder, pReq->execId)); + TAOS_CHECK_EXIT(tEncodeU64(&encoder, pReq->clientId)); tEndEncode(&encoder); @@ -9095,6 +9108,11 @@ int32_t tDeserializeSTaskDropReq(void *buf, int32_t bufLen, STaskDropReq *pReq) TAOS_CHECK_EXIT(tDecodeU64(&decoder, &pReq->taskId)); TAOS_CHECK_EXIT(tDecodeI64(&decoder, &pReq->refId)); TAOS_CHECK_EXIT(tDecodeI32(&decoder, &pReq->execId)); + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeU64(&decoder, &pReq->clientId)); + } else { + pReq->clientId = 0; + } tEndDecode(&decoder); @@ -9123,6 +9141,7 @@ int32_t tSerializeSTaskNotifyReq(void *buf, int32_t bufLen, STaskNotifyReq *pReq TAOS_CHECK_EXIT(tEncodeI64(&encoder, pReq->refId)); TAOS_CHECK_EXIT(tEncodeI32(&encoder, pReq->execId)); TAOS_CHECK_EXIT(tEncodeI32(&encoder, pReq->type)); + TAOS_CHECK_EXIT(tEncodeU64(&encoder, pReq->clientId)); tEndEncode(&encoder); @@ -9164,6 +9183,11 @@ int32_t tDeserializeSTaskNotifyReq(void *buf, int32_t bufLen, STaskNotifyReq *pR TAOS_CHECK_EXIT(tDecodeI64(&decoder, &pReq->refId)); TAOS_CHECK_EXIT(tDecodeI32(&decoder, &pReq->execId)); TAOS_CHECK_EXIT(tDecodeI32(&decoder, (int32_t *)&pReq->type)); + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeU64(&decoder, &pReq->clientId)); + } else { + pReq->clientId = 0; + } tEndDecode(&decoder); @@ -9353,6 +9377,10 @@ int32_t tSerializeSSchedulerHbRsp(void *buf, int32_t bufLen, SSchedulerHbRsp *pR TAOS_CHECK_EXIT(tEncodeI32(&encoder, status->execId)); TAOS_CHECK_EXIT(tEncodeI8(&encoder, status->status)); } + for (int32_t i = 0; i < num; ++i) { + STaskStatus *status = taosArrayGet(pRsp->taskStatus, i); + TAOS_CHECK_EXIT(tEncodeU64(&encoder, status->clientId)); + } } else { TAOS_CHECK_EXIT(tEncodeI32(&encoder, 0)); } @@ -9396,6 +9424,12 @@ int32_t tDeserializeSSchedulerHbRsp(void *buf, int32_t bufLen, SSchedulerHbRsp * TAOS_CHECK_EXIT(terrno); } } + if (!tDecodeIsEnd(&decoder)) { + for (int32_t i = 0; i < num; ++i) { + STaskStatus *status = taosArrayGet(pRsp->taskStatus, i); + TAOS_CHECK_EXIT(tDecodeU64(&decoder, &status->clientId)); + } + } } else { pRsp->taskStatus = NULL; } @@ -9560,6 +9594,7 @@ int32_t tSerializeSVDeleteReq(void *buf, int32_t bufLen, SVDeleteReq *pReq) { TAOS_CHECK_EXIT(tEncodeCStr(&encoder, pReq->sql)); TAOS_CHECK_EXIT(tEncodeBinary(&encoder, pReq->msg, pReq->phyLen)); TAOS_CHECK_EXIT(tEncodeI8(&encoder, pReq->source)); + TAOS_CHECK_EXIT(tEncodeU64(&encoder, pReq->clientId)); tEndEncode(&encoder); _exit: @@ -9608,6 +9643,11 @@ int32_t tDeserializeSVDeleteReq(void *buf, int32_t bufLen, SVDeleteReq *pReq) { if (!tDecodeIsEnd(&decoder)) { TAOS_CHECK_EXIT(tDecodeI8(&decoder, &pReq->source)); } + if (!tDecodeIsEnd(&decoder)) { + TAOS_CHECK_EXIT(tDecodeU64(&decoder, &pReq->clientId)); + } else { + pReq->clientId = 0; + } tEndDecode(&decoder); _exit: diff --git a/source/libs/executor/src/exchangeoperator.c b/source/libs/executor/src/exchangeoperator.c index 042fcf0120..7222f2d297 100644 --- a/source/libs/executor/src/exchangeoperator.c +++ b/source/libs/executor/src/exchangeoperator.c @@ -121,10 +121,10 @@ static void concurrentlyLoadRemoteDataImpl(SOperatorInfo* pOperator, SExchangeIn } } else { pDataInfo->status = EX_SOURCE_DATA_EXHAUSTED; - qDebug("%s vgId:%d, taskId:0x%" PRIx64 " execId:%d index:%d completed, rowsOfSource:%" PRIu64 - ", totalRows:%" PRIu64 ", try next %d/%" PRIzu, - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->taskId, pSource->execId, i, pDataInfo->totalRows, - pExchangeInfo->loadInfo.totalRows, i + 1, totalSources); + qDebug("%s vgId:%d, clientId:0x%" PRIx64 " taskId:0x%" PRIx64 + " execId:%d index:%d completed, rowsOfSource:%" PRIu64 ", totalRows:%" PRIu64 ", try next %d/%" PRIzu, + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, i, + pDataInfo->totalRows, pExchangeInfo->loadInfo.totalRows, i + 1, totalSources); taosMemoryFreeClear(pDataInfo->pRsp); } break; @@ -141,17 +141,17 @@ static void concurrentlyLoadRemoteDataImpl(SOperatorInfo* pOperator, SExchangeIn if (pRsp->completed == 1) { pDataInfo->status = EX_SOURCE_DATA_EXHAUSTED; - qDebug("%s fetch msg rsp from vgId:%d, taskId:0x%" PRIx64 + qDebug("%s fetch msg rsp from vgId:%d, clientId:0x%" PRIx64 " taskId:0x%" PRIx64 " execId:%d index:%d completed, blocks:%d, numOfRows:%" PRId64 ", rowsOfSource:%" PRIu64 ", totalRows:%" PRIu64 ", total:%.2f Kb, try next %d/%" PRIzu, - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->taskId, pSource->execId, i, pRsp->numOfBlocks, - pRsp->numOfRows, pDataInfo->totalRows, pLoadInfo->totalRows, pLoadInfo->totalSize / 1024.0, i + 1, - totalSources); + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, i, + pRsp->numOfBlocks, pRsp->numOfRows, pDataInfo->totalRows, pLoadInfo->totalRows, + pLoadInfo->totalSize / 1024.0, i + 1, totalSources); } else { - qDebug("%s fetch msg rsp from vgId:%d, taskId:0x%" PRIx64 " execId:%d blocks:%d, numOfRows:%" PRId64 - ", totalRows:%" PRIu64 ", total:%.2f Kb", - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->taskId, pSource->execId, pRsp->numOfBlocks, - pRsp->numOfRows, pLoadInfo->totalRows, pLoadInfo->totalSize / 1024.0); + qDebug("%s fetch msg rsp from vgId:%d, clientId:0x%" PRIx64 " taskId:0x%" PRIx64 + " execId:%d blocks:%d, numOfRows:%" PRId64 ", totalRows:%" PRIu64 ", total:%.2f Kb", + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, + pRsp->numOfBlocks, pRsp->numOfRows, pLoadInfo->totalRows, pLoadInfo->totalSize / 1024.0); } taosMemoryFreeClear(pDataInfo->pRsp); @@ -640,9 +640,9 @@ int32_t doSendFetchDataRequest(SExchangeInfo* pExchangeInfo, SExecTaskInfo* pTas if (pSource->localExec) { SDataBuf pBuf = {0}; - int32_t code = - (*pTaskInfo->localFetch.fp)(pTaskInfo->localFetch.handle, pSource->schedId, pTaskInfo->id.queryId, - pSource->taskId, 0, pSource->execId, &pBuf.pData, pTaskInfo->localFetch.explainRes); + int32_t code = (*pTaskInfo->localFetch.fp)(pTaskInfo->localFetch.handle, pSource->schedId, pTaskInfo->id.queryId, + pSource->clientId, pSource->taskId, 0, pSource->execId, &pBuf.pData, + pTaskInfo->localFetch.explainRes); code = loadRemoteDataCallback(pWrapper, &pBuf, code); QUERY_CHECK_CODE(code, lino, _end); taosMemoryFree(pWrapper); @@ -650,6 +650,7 @@ int32_t doSendFetchDataRequest(SExchangeInfo* pExchangeInfo, SExecTaskInfo* pTas SResFetchReq req = {0}; req.header.vgId = pSource->addr.nodeId; req.sId = pSource->schedId; + req.clientId = pSource->clientId; req.taskId = pSource->taskId; req.queryId = pTaskInfo->id.queryId; req.execId = pSource->execId; @@ -691,9 +692,10 @@ int32_t doSendFetchDataRequest(SExchangeInfo* pExchangeInfo, SExecTaskInfo* pTas freeOperatorParam(req.pOpParam, OP_GET_PARAM); - qDebug("%s build fetch msg and send to vgId:%d, ep:%s, taskId:0x%" PRIx64 ", execId:%d, %p, %d/%" PRIzu, - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->addr.epSet.eps[0].fqdn, pSource->taskId, - pSource->execId, pExchangeInfo, sourceIndex, totalSources); + qDebug("%s build fetch msg and send to vgId:%d, ep:%s, clientId:0x%" PRIx64 " taskId:0x%" PRIx64 + ", execId:%d, %p, %d/%" PRIzu, + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->addr.epSet.eps[0].fqdn, pSource->clientId, + pSource->taskId, pSource->execId, pExchangeInfo, sourceIndex, totalSources); // send the fetch remote task result reques SMsgSendInfo* pMsgSendInfo = taosMemoryCalloc(1, sizeof(SMsgSendInfo)); @@ -974,8 +976,9 @@ int32_t seqLoadRemoteData(SOperatorInfo* pOperator) { } if (pDataInfo->code != TSDB_CODE_SUCCESS) { - qError("%s vgId:%d, taskID:0x%" PRIx64 " execId:%d error happens, code:%s", GET_TASKID(pTaskInfo), - pSource->addr.nodeId, pSource->taskId, pSource->execId, tstrerror(pDataInfo->code)); + qError("%s vgId:%d, clientId:0x%" PRIx64 " taskID:0x%" PRIx64 " execId:%d error happens, code:%s", + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, + tstrerror(pDataInfo->code)); pOperator->pTaskInfo->code = pDataInfo->code; return pOperator->pTaskInfo->code; } @@ -984,10 +987,10 @@ int32_t seqLoadRemoteData(SOperatorInfo* pOperator) { SLoadRemoteDataInfo* pLoadInfo = &pExchangeInfo->loadInfo; if (pRsp->numOfRows == 0) { - qDebug("%s vgId:%d, taskID:0x%" PRIx64 " execId:%d %d of total completed, rowsOfSource:%" PRIu64 - ", totalRows:%" PRIu64 " try next", - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->taskId, pSource->execId, pExchangeInfo->current + 1, - pDataInfo->totalRows, pLoadInfo->totalRows); + qDebug("%s vgId:%d, clientId:0x%" PRIx64 " taskID:0x%" PRIx64 + " execId:%d %d of total completed, rowsOfSource:%" PRIu64 ", totalRows:%" PRIu64 " try next", + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, + pExchangeInfo->current + 1, pDataInfo->totalRows, pLoadInfo->totalRows); pDataInfo->status = EX_SOURCE_DATA_EXHAUSTED; pExchangeInfo->current += 1; @@ -1002,19 +1005,19 @@ int32_t seqLoadRemoteData(SOperatorInfo* pOperator) { SRetrieveTableRsp* pRetrieveRsp = pDataInfo->pRsp; if (pRsp->completed == 1) { - qDebug("%s fetch msg rsp from vgId:%d, taskId:0x%" PRIx64 " execId:%d numOfRows:%" PRId64 + qDebug("%s fetch msg rsp from vgId:%d, clientId:0x%" PRIx64 " taskId:0x%" PRIx64 " execId:%d numOfRows:%" PRId64 ", rowsOfSource:%" PRIu64 ", totalRows:%" PRIu64 ", totalBytes:%" PRIu64 " try next %d/%" PRIzu, - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->taskId, pSource->execId, pRetrieveRsp->numOfRows, - pDataInfo->totalRows, pLoadInfo->totalRows, pLoadInfo->totalSize, pExchangeInfo->current + 1, - totalSources); + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, + pRetrieveRsp->numOfRows, pDataInfo->totalRows, pLoadInfo->totalRows, pLoadInfo->totalSize, + pExchangeInfo->current + 1, totalSources); pDataInfo->status = EX_SOURCE_DATA_EXHAUSTED; pExchangeInfo->current += 1; } else { - qDebug("%s fetch msg rsp from vgId:%d, taskId:0x%" PRIx64 " execId:%d numOfRows:%" PRId64 ", totalRows:%" PRIu64 - ", totalBytes:%" PRIu64, - GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->taskId, pSource->execId, pRetrieveRsp->numOfRows, - pLoadInfo->totalRows, pLoadInfo->totalSize); + qDebug("%s fetch msg rsp from vgId:%d, clientId:0x%" PRIx64 " taskId:0x%" PRIx64 " execId:%d numOfRows:%" PRId64 + ", totalRows:%" PRIu64 ", totalBytes:%" PRIu64, + GET_TASKID(pTaskInfo), pSource->addr.nodeId, pSource->clientId, pSource->taskId, pSource->execId, + pRetrieveRsp->numOfRows, pLoadInfo->totalRows, pLoadInfo->totalSize); } updateLoadRemoteInfo(pLoadInfo, pRetrieveRsp->numOfRows, pRetrieveRsp->compLen, startTs, pOperator); diff --git a/source/libs/nodes/src/nodesCloneFuncs.c b/source/libs/nodes/src/nodesCloneFuncs.c index 1a5785190b..ba87912670 100644 --- a/source/libs/nodes/src/nodesCloneFuncs.c +++ b/source/libs/nodes/src/nodesCloneFuncs.c @@ -851,6 +851,7 @@ static int32_t slotDescCopy(const SSlotDescNode* pSrc, SSlotDescNode* pDst) { static int32_t downstreamSourceCopy(const SDownstreamSourceNode* pSrc, SDownstreamSourceNode* pDst) { COPY_OBJECT_FIELD(addr, sizeof(SQueryNodeAddr)); + COPY_SCALAR_FIELD(clientId); COPY_SCALAR_FIELD(taskId); COPY_SCALAR_FIELD(schedId); COPY_SCALAR_FIELD(execId); diff --git a/source/libs/nodes/src/nodesCodeFuncs.c b/source/libs/nodes/src/nodesCodeFuncs.c index 3275cfd838..f7f858db78 100644 --- a/source/libs/nodes/src/nodesCodeFuncs.c +++ b/source/libs/nodes/src/nodesCodeFuncs.c @@ -5259,6 +5259,7 @@ static int32_t jsonToColumnDefNode(const SJson* pJson, void* pObj) { } static const char* jkDownstreamSourceAddr = "Addr"; +static const char* jkDownstreamSourceClientId = "ClientId"; static const char* jkDownstreamSourceTaskId = "TaskId"; static const char* jkDownstreamSourceSchedId = "SchedId"; static const char* jkDownstreamSourceExecId = "ExecId"; @@ -5268,6 +5269,9 @@ static int32_t downstreamSourceNodeToJson(const void* pObj, SJson* pJson) { const SDownstreamSourceNode* pNode = (const SDownstreamSourceNode*)pObj; int32_t code = tjsonAddObject(pJson, jkDownstreamSourceAddr, queryNodeAddrToJson, &pNode->addr); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonAddIntegerToObject(pJson, jkDownstreamSourceClientId, pNode->clientId); + } if (TSDB_CODE_SUCCESS == code) { code = tjsonAddIntegerToObject(pJson, jkDownstreamSourceTaskId, pNode->taskId); } @@ -5288,6 +5292,9 @@ static int32_t jsonToDownstreamSourceNode(const SJson* pJson, void* pObj) { SDownstreamSourceNode* pNode = (SDownstreamSourceNode*)pObj; int32_t code = tjsonToObject(pJson, jkDownstreamSourceAddr, jsonToQueryNodeAddr, &pNode->addr); + if (TSDB_CODE_SUCCESS == code) { + code = tjsonGetUBigIntValue(pJson, jkDownstreamSourceClientId, &pNode->clientId); + } if (TSDB_CODE_SUCCESS == code) { code = tjsonGetUBigIntValue(pJson, jkDownstreamSourceTaskId, &pNode->taskId); } diff --git a/source/libs/nodes/src/nodesMsgFuncs.c b/source/libs/nodes/src/nodesMsgFuncs.c index 28d0b9fbd4..bf3ea66e47 100644 --- a/source/libs/nodes/src/nodesMsgFuncs.c +++ b/source/libs/nodes/src/nodesMsgFuncs.c @@ -1769,6 +1769,9 @@ static int32_t downstreamSourceNodeInlineToMsg(const void* pObj, STlvEncoder* pE if (TSDB_CODE_SUCCESS == code) { code = tlvEncodeValueI32(pEncoder, pNode->fetchMsgType); } + if (TSDB_CODE_SUCCESS == code) { + code = tlvEncodeValueU64(pEncoder, pNode->clientId); + } return code; } @@ -1793,6 +1796,9 @@ static int32_t msgToDownstreamSourceNodeInlineToMsg(STlvDecoder* pDecoder, void* if (TSDB_CODE_SUCCESS == code) { code = tlvDecodeValueI32(pDecoder, &pNode->fetchMsgType); } + if (TSDB_CODE_SUCCESS == code && !tlvDecodeEnd(pDecoder)) { + code = tlvDecodeValueU64(pDecoder, &pNode->clientId); + } return code; } diff --git a/source/libs/qworker/inc/qwInt.h b/source/libs/qworker/inc/qwInt.h index 7a902bdd66..708c285aea 100644 --- a/source/libs/qworker/inc/qwInt.h +++ b/source/libs/qworker/inc/qwInt.h @@ -215,8 +215,8 @@ typedef struct SQWorkerMgmt { #define QW_CTX_NOT_EXISTS_ERR_CODE(mgmt) \ (atomic_load_8(&(mgmt)->nodeStopped) ? TSDB_CODE_VND_STOPPED : TSDB_CODE_QRY_TASK_CTX_NOT_EXIST) -#define QW_FPARAMS_DEF SQWorker *mgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId -#define QW_IDS() sId, qId, tId, rId, eId +#define QW_FPARAMS_DEF SQWorker *mgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId, int32_t eId +#define QW_IDS() sId, qId, cId, tId, rId, eId #define QW_FPARAMS() mgmt, QW_IDS() #define QW_STAT_INC(_item, _n) (void)atomic_add_fetch_64(&(_item), _n) @@ -257,18 +257,20 @@ typedef struct SQWorkerMgmt { #define QW_FETCH_RUNNING(ctx) ((ctx)->inFetch) #define QW_QUERY_NOT_STARTED(ctx) (QW_GET_PHASE(ctx) == -1) -#define QW_SET_QTID(id, qId, tId, eId) \ - do { \ - *(uint64_t *)(id) = (qId); \ - *(uint64_t *)((char *)(id) + sizeof(qId)) = (tId); \ - *(int32_t *)((char *)(id) + sizeof(qId) + sizeof(tId)) = (eId); \ +#define QW_SET_QTID(id, qId, cId, tId, eId) \ + do { \ + *(uint64_t *)(id) = (qId); \ + *(uint64_t *)((char *)(id) + sizeof(qId)) = (cId); \ + *(uint64_t *)((char *)(id) + sizeof(qId) + sizeof(cId)) = (tId); \ + *(int32_t *)((char *)(id) + sizeof(qId) + sizeof(cId) + sizeof(tId)) = (eId); \ } while (0) -#define QW_GET_QTID(id, qId, tId, eId) \ - do { \ - (qId) = *(uint64_t *)(id); \ - (tId) = *(uint64_t *)((char *)(id) + sizeof(qId)); \ - (eId) = *(int32_t *)((char *)(id) + sizeof(qId) + sizeof(tId)); \ +#define QW_GET_QTID(id, qId, cId, tId, eId) \ + do { \ + (qId) = *(uint64_t *)(id); \ + (cId) = *(uint64_t *)((char *)(id) + sizeof(qId)); \ + (tId) = *(uint64_t *)((char *)(id) + sizeof(qId) + sizeof(cId)); \ + (eId) = *(int32_t *)((char *)(id) + sizeof(qId) + sizeof(cId) + sizeof(tId)); \ } while (0) #define QW_ERR_RET(c) \ @@ -310,25 +312,31 @@ typedef struct SQWorkerMgmt { #define QW_SCH_ELOG(param, ...) qError("QW:%p SID:%" PRIx64 " " param, mgmt, sId, __VA_ARGS__) #define QW_SCH_DLOG(param, ...) qDebug("QW:%p SID:%" PRIx64 " " param, mgmt, sId, __VA_ARGS__) -#define QW_TASK_ELOG(param, ...) qError("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId, __VA_ARGS__) -#define QW_TASK_WLOG(param, ...) qWarn("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId, __VA_ARGS__) -#define QW_TASK_DLOG(param, ...) qDebug("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId, __VA_ARGS__) +#define QW_TASK_ELOG(param, ...) \ + qError("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) +#define QW_TASK_WLOG(param, ...) \ + qWarn("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) +#define QW_TASK_DLOG(param, ...) \ + qDebug("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) #define QW_TASK_DLOGL(param, ...) \ - qDebugL("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId, __VA_ARGS__) + qDebugL("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId, __VA_ARGS__) -#define QW_TASK_ELOG_E(param) qError("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId) -#define QW_TASK_WLOG_E(param) qWarn("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId) -#define QW_TASK_DLOG_E(param) qDebug("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, tId, eId) +#define QW_TASK_ELOG_E(param) \ + qError("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) +#define QW_TASK_WLOG_E(param) \ + qWarn("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) +#define QW_TASK_DLOG_E(param) \ + qDebug("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, qId, cId, tId, eId) -#define QW_SCH_TASK_ELOG(param, ...) \ - qError("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, qId, tId, eId, \ - __VA_ARGS__) -#define QW_SCH_TASK_WLOG(param, ...) \ - qWarn("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, qId, tId, eId, \ - __VA_ARGS__) -#define QW_SCH_TASK_DLOG(param, ...) \ - qDebug("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, qId, tId, eId, \ - __VA_ARGS__) +#define QW_SCH_TASK_ELOG(param, ...) \ + qError("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, \ + qId, cId, tId, eId, __VA_ARGS__) +#define QW_SCH_TASK_WLOG(param, ...) \ + qWarn("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, qId, \ + cId, tId, eId, __VA_ARGS__) +#define QW_SCH_TASK_DLOG(param, ...) \ + qDebug("QW:%p SID:0x%" PRIx64 ",qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, mgmt, sId, \ + qId, cId, tId, eId, __VA_ARGS__) #define QW_LOCK_DEBUG(...) \ do { \ diff --git a/source/libs/qworker/src/qwDbg.c b/source/libs/qworker/src/qwDbg.c index d3b8d36b25..897080df3e 100644 --- a/source/libs/qworker/src/qwDbg.c +++ b/source/libs/qworker/src/qwDbg.c @@ -96,14 +96,14 @@ void qwDbgDumpSchInfo(SQWorker *mgmt, SQWSchStatus *sch, int32_t i) { int32_t taskNum = taosHashGetSize(sch->tasksHash); QW_DLOG("***The %dth scheduler status, hbBrokenTs:%" PRId64 ",taskNum:%d", i, sch->hbBrokenTs, taskNum); - uint64_t qId, tId; + uint64_t qId, cId, tId; int32_t eId; SQWTaskStatus *pTask = NULL; void *pIter = taosHashIterate(sch->tasksHash, NULL); while (pIter) { pTask = (SQWTaskStatus *)pIter; void *key = taosHashGetKey(pIter, NULL); - QW_GET_QTID(key, qId, tId, eId); + QW_GET_QTID(key, qId, cId, tId, eId); QW_TASK_DLOG("job refId:%" PRIx64 ", code:%x, task status:%d", pTask->refId, pTask->code, pTask->status); @@ -118,13 +118,13 @@ void qwDbgDumpTasksInfo(SQWorker *mgmt) { int32_t i = 0; SQWTaskCtx *ctx = NULL; - uint64_t qId, tId; + uint64_t qId, cId, tId; int32_t eId; void *pIter = taosHashIterate(mgmt->ctxHash, NULL); while (pIter) { ctx = (SQWTaskCtx *)pIter; void *key = taosHashGetKey(pIter, NULL); - QW_GET_QTID(key, qId, tId, eId); + QW_GET_QTID(key, qId, cId, tId, eId); QW_TASK_DLOG("%p lock:%x, phase:%d, type:%d, explain:%d, needFetch:%d, localExec:%d, queryMsgType:%d, " "sId:%" PRId64 ", level:%d, queryGotData:%d, queryRsped:%d, queryEnd:%d, queryContinue:%d, queryInQueue:%d, " diff --git a/source/libs/qworker/src/qwMsg.c b/source/libs/qworker/src/qwMsg.c index 20b81bfc14..7dbad90cc0 100644 --- a/source/libs/qworker/src/qwMsg.c +++ b/source/libs/qworker/src/qwMsg.c @@ -233,6 +233,7 @@ int32_t qwBuildAndSendDropMsg(QW_FPARAMS_DEF, SRpcHandleInfo *pConn) { qMsg.header.contLen = 0; qMsg.sId = sId; qMsg.queryId = qId; + qMsg.clientId = cId; qMsg.taskId = tId; qMsg.refId = rId; qMsg.execId = eId; @@ -284,6 +285,7 @@ int32_t qwBuildAndSendCQueryMsg(QW_FPARAMS_DEF, SRpcHandleInfo *pConn) { req->header.vgId = mgmt->nodeId; req->sId = sId; req->queryId = qId; + req->clientId = cId; req->taskId = tId; req->execId = eId; @@ -312,6 +314,7 @@ int32_t qwRegisterQueryBrokenLinkArg(QW_FPARAMS_DEF, SRpcHandleInfo *pConn) { qMsg.header.contLen = 0; qMsg.sId = sId; qMsg.queryId = qId; + qMsg.clientId = cId; qMsg.taskId = tId; qMsg.refId = rId; qMsg.execId = eId; @@ -416,6 +419,7 @@ int32_t qWorkerPreprocessQueryMsg(void *qWorkerMgmt, SRpcMsg *pMsg, bool chkGran uint64_t sId = msg.sId; uint64_t qId = msg.queryId; + uint64_t cId = msg.clientId; uint64_t tId = msg.taskId; int64_t rId = msg.refId; int32_t eId = msg.execId; @@ -447,6 +451,7 @@ int32_t qWorkerAbortPreprocessQueryMsg(void *qWorkerMgmt, SRpcMsg *pMsg) { uint64_t sId = msg.sId; uint64_t qId = msg.queryId; + uint64_t cId = msg.clientId; uint64_t tId = msg.taskId; int64_t rId = msg.refId; int32_t eId = msg.execId; @@ -479,6 +484,7 @@ int32_t qWorkerProcessQueryMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, int uint64_t sId = msg.sId; uint64_t qId = msg.queryId; + uint64_t cId = msg.clientId; uint64_t tId = msg.taskId; int64_t rId = msg.refId; int32_t eId = msg.execId; @@ -524,6 +530,7 @@ int32_t qWorkerProcessCQueryMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, in uint64_t sId = msg->sId; uint64_t qId = msg->queryId; + uint64_t cId = msg->clientId; uint64_t tId = msg->taskId; int64_t rId = 0; int32_t eId = msg->execId; @@ -557,6 +564,7 @@ int32_t qWorkerProcessFetchMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, int uint64_t sId = req.sId; uint64_t qId = req.queryId; + uint64_t cId = req.clientId; uint64_t tId = req.taskId; int64_t rId = 0; int32_t eId = req.execId; @@ -604,12 +612,14 @@ int32_t qWorkerProcessCancelMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, in msg->sId = be64toh(msg->sId); msg->queryId = be64toh(msg->queryId); + msg->clientId = be64toh(msg->clientId); msg->taskId = be64toh(msg->taskId); msg->refId = be64toh(msg->refId); msg->execId = ntohl(msg->execId); uint64_t sId = msg->sId; uint64_t qId = msg->queryId; + uint64_t cId = msg->clientId; uint64_t tId = msg->taskId; int64_t rId = msg->refId; int32_t eId = msg->execId; @@ -646,6 +656,7 @@ int32_t qWorkerProcessDropMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, int6 uint64_t sId = msg.sId; uint64_t qId = msg.queryId; + uint64_t cId = msg.clientId; uint64_t tId = msg.taskId; int64_t rId = msg.refId; int32_t eId = msg.execId; @@ -684,6 +695,7 @@ int32_t qWorkerProcessNotifyMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, in uint64_t sId = msg.sId; uint64_t qId = msg.queryId; + uint64_t cId = msg.clientId; uint64_t tId = msg.taskId; int64_t rId = msg.refId; int32_t eId = msg.execId; @@ -753,6 +765,7 @@ int32_t qWorkerProcessDeleteMsg(void *node, void *qWorkerMgmt, SRpcMsg *pMsg, SD uint64_t sId = req.sId; uint64_t qId = req.queryId; + uint64_t cId = req.clientId; uint64_t tId = req.taskId; int64_t rId = 0; int32_t eId = -1; diff --git a/source/libs/qworker/src/qwUtil.c b/source/libs/qworker/src/qwUtil.c index ef07a42629..917579deb0 100644 --- a/source/libs/qworker/src/qwUtil.c +++ b/source/libs/qworker/src/qwUtil.c @@ -137,8 +137,8 @@ int32_t qwAcquireScheduler(SQWorker *mgmt, uint64_t sId, int32_t rwType, SQWSchS void qwReleaseScheduler(int32_t rwType, SQWorker *mgmt) { QW_UNLOCK(rwType, &mgmt->schLock); } int32_t qwAcquireTaskStatus(QW_FPARAMS_DEF, int32_t rwType, SQWSchStatus *sch, SQWTaskStatus **task) { - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); QW_LOCK(rwType, &sch->tasksLock); *task = taosHashGet(sch->tasksHash, id, sizeof(id)); @@ -153,8 +153,8 @@ int32_t qwAcquireTaskStatus(QW_FPARAMS_DEF, int32_t rwType, SQWSchStatus *sch, S int32_t qwAddTaskStatusImpl(QW_FPARAMS_DEF, SQWSchStatus *sch, int32_t rwType, int32_t status, SQWTaskStatus **task) { int32_t code = 0; - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); SQWTaskStatus ntask = {0}; ntask.status = status; @@ -209,8 +209,8 @@ int32_t qwAddAcquireTaskStatus(QW_FPARAMS_DEF, int32_t rwType, SQWSchStatus *sch void qwReleaseTaskStatus(int32_t rwType, SQWSchStatus *sch) { QW_UNLOCK(rwType, &sch->tasksLock); } int32_t qwAcquireTaskCtx(QW_FPARAMS_DEF, SQWTaskCtx **ctx) { - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); *ctx = taosHashAcquire(mgmt->ctxHash, id, sizeof(id)); if (NULL == (*ctx)) { @@ -222,8 +222,8 @@ int32_t qwAcquireTaskCtx(QW_FPARAMS_DEF, SQWTaskCtx **ctx) { } int32_t qwGetTaskCtx(QW_FPARAMS_DEF, SQWTaskCtx **ctx) { - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); *ctx = taosHashGet(mgmt->ctxHash, id, sizeof(id)); if (NULL == (*ctx)) { @@ -235,8 +235,8 @@ int32_t qwGetTaskCtx(QW_FPARAMS_DEF, SQWTaskCtx **ctx) { } int32_t qwAddTaskCtxImpl(QW_FPARAMS_DEF, bool acquire, SQWTaskCtx **ctx) { - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); SQWTaskCtx nctx = {0}; @@ -347,6 +347,7 @@ int32_t qwSendExplainResponse(QW_FPARAMS_DEF, SQWTaskCtx *ctx) { (void)memcpy(pExec, taosArrayGet(execInfoList, 0), localRsp.rsp.numOfPlans * sizeof(SExplainExecInfo)); localRsp.rsp.subplanInfo = pExec; localRsp.qId = qId; + localRsp.cId = cId; localRsp.tId = tId; localRsp.rId = rId; localRsp.eId = eId; @@ -376,8 +377,8 @@ _return: int32_t qwDropTaskCtx(QW_FPARAMS_DEF) { - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); SQWTaskCtx octx; SQWTaskCtx *ctx = taosHashGet(mgmt->ctxHash, id, sizeof(id)); @@ -411,8 +412,8 @@ int32_t qwDropTaskStatus(QW_FPARAMS_DEF) { SQWTaskStatus *task = NULL; int32_t code = 0; - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); if (qwAcquireScheduler(mgmt, sId, QW_WRITE, &sch)) { QW_TASK_WLOG_E("scheduler does not exist"); @@ -465,8 +466,8 @@ _return: int32_t qwHandleDynamicTaskEnd(QW_FPARAMS_DEF) { - char id[sizeof(qId) + sizeof(tId) + sizeof(eId)] = {0}; - QW_SET_QTID(id, qId, tId, eId); + char id[sizeof(qId) + sizeof(cId) + sizeof(tId) + sizeof(eId)] = {0}; + QW_SET_QTID(id, qId, cId, tId, eId); SQWTaskCtx octx; SQWTaskCtx *ctx = taosHashGet(mgmt->ctxHash, id, sizeof(id)); @@ -588,14 +589,14 @@ void qwDestroyImpl(void *pMgmt) { mgmt->hbTimer = NULL; taosTmrCleanUp(mgmt->timer); - uint64_t qId, tId; + uint64_t qId, cId, tId; int32_t eId; void *pIter = taosHashIterate(mgmt->ctxHash, NULL); while (pIter) { SQWTaskCtx *ctx = (SQWTaskCtx *)pIter; void *key = taosHashGetKey(pIter, NULL); - QW_GET_QTID(key, qId, tId, eId); + QW_GET_QTID(key, qId, cId, tId, eId); qwFreeTaskCtx(ctx); QW_TASK_DLOG_E("task ctx freed"); diff --git a/source/libs/qworker/src/qworker.c b/source/libs/qworker/src/qworker.c index 9b96c1e519..13e1d0e231 100644 --- a/source/libs/qworker/src/qworker.c +++ b/source/libs/qworker/src/qworker.c @@ -19,7 +19,7 @@ SQWorkerMgmt gQwMgmt = { }; void qwStopAllTasks(SQWorker *mgmt) { - uint64_t qId, tId, sId; + uint64_t qId, cId, tId, sId; int32_t eId; int64_t rId = 0; int32_t code = TSDB_CODE_SUCCESS; @@ -28,7 +28,7 @@ void qwStopAllTasks(SQWorker *mgmt) { while (pIter) { SQWTaskCtx *ctx = (SQWTaskCtx *)pIter; void *key = taosHashGetKey(pIter, NULL); - QW_GET_QTID(key, qId, tId, eId); + QW_GET_QTID(key, qId, cId, tId, eId); QW_LOCK(QW_WRITE, &ctx->lock); @@ -288,7 +288,7 @@ int32_t qwGenerateSchHbRsp(SQWorker *mgmt, SQWSchStatus *sch, SQWHbInfo *hbInfo) // TODO GET EXECUTOR API TO GET MORE INFO - QW_GET_QTID(key, status.queryId, status.taskId, status.execId); + QW_GET_QTID(key, status.queryId, status.clientId, status.taskId, status.execId); status.status = taskStatus->status; status.refId = taskStatus->refId; @@ -1473,8 +1473,8 @@ int32_t qWorkerGetStat(SReadHandle *handle, void *qWorkerMgmt, SQWorkerStat *pSt return TSDB_CODE_SUCCESS; } -int32_t qWorkerProcessLocalQuery(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId, - SQWMsg *qwMsg, SArray *explainRes) { +int32_t qWorkerProcessLocalQuery(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId, + int32_t eId, SQWMsg *qwMsg, SArray *explainRes) { SQWorker *mgmt = (SQWorker *)pMgmt; int32_t code = 0; SQWTaskCtx *ctx = NULL; @@ -1538,8 +1538,8 @@ _return: QW_RET(code); } -int32_t qWorkerProcessLocalFetch(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t tId, int64_t rId, int32_t eId, - void **pRsp, SArray *explainRes) { +int32_t qWorkerProcessLocalFetch(void *pMgmt, uint64_t sId, uint64_t qId, uint64_t cId, uint64_t tId, int64_t rId, + int32_t eId, void **pRsp, SArray *explainRes) { SQWorker *mgmt = (SQWorker *)pMgmt; int32_t code = 0; int32_t dataLen = 0; diff --git a/source/libs/scheduler/inc/schInt.h b/source/libs/scheduler/inc/schInt.h index 96b9d2da8d..6a910453f0 100644 --- a/source/libs/scheduler/inc/schInt.h +++ b/source/libs/scheduler/inc/schInt.h @@ -142,8 +142,9 @@ typedef struct SSchedulerCfg { } SSchedulerCfg; typedef struct SSchedulerMgmt { - uint64_t taskId; // sequential taksId - uint64_t sId; // schedulerId + uint64_t clientId; // unique clientId + uint64_t taskId; // sequential taksId + uint64_t sId; // schedulerId SSchedulerCfg cfg; bool exit; int32_t jobRef; @@ -163,6 +164,7 @@ typedef struct SSchTaskCallbackParam { SSchCallbackParamHeader head; uint64_t queryId; int64_t refId; + uint64_t clientId; uint64_t taskId; int32_t execId; void *pTrans; @@ -222,6 +224,7 @@ typedef struct SSchTimerParam { } SSchTimerParam; typedef struct SSchTask { + uint64_t clientId; // current client id uint64_t taskId; // task id SRWLatch lock; // task reentrant lock int32_t maxExecTimes; // task max exec times @@ -329,6 +332,7 @@ extern SSchedulerMgmt schMgmt; #define SCH_LOCK_TASK(_task) SCH_LOCK(SCH_WRITE, &(_task)->lock) #define SCH_UNLOCK_TASK(_task) SCH_UNLOCK(SCH_WRITE, &(_task)->lock) +#define SCH_CLIENT_ID(_task) ((_task) ? (_task)->clientId : -1) #define SCH_TASK_ID(_task) ((_task) ? (_task)->taskId : -1) #define SCH_TASK_EID(_task) ((_task) ? (_task)->execId : -1) @@ -449,21 +453,21 @@ extern SSchedulerMgmt schMgmt; #define SCH_JOB_ELOG(param, ...) qError("qid:0x%" PRIx64 " " param, pJob->queryId, __VA_ARGS__) #define SCH_JOB_DLOG(param, ...) qDebug("qid:0x%" PRIx64 " " param, pJob->queryId, __VA_ARGS__) -#define SCH_TASK_ELOG(param, ...) \ - qError("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), \ - __VA_ARGS__) -#define SCH_TASK_DLOG(param, ...) \ - qDebug("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), \ - __VA_ARGS__) -#define SCH_TASK_TLOG(param, ...) \ - qTrace("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), \ - __VA_ARGS__) -#define SCH_TASK_DLOGL(param, ...) \ - qDebugL("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), \ - __VA_ARGS__) -#define SCH_TASK_WLOG(param, ...) \ - qWarn("qid:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), \ - __VA_ARGS__) +#define SCH_TASK_ELOG(param, ...) \ + qError("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) +#define SCH_TASK_DLOG(param, ...) \ + qDebug("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) +#define SCH_TASK_TLOG(param, ...) \ + qTrace("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) +#define SCH_TASK_DLOGL(param, ...) \ + qDebugL("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) +#define SCH_TASK_WLOG(param, ...) \ + qWarn("qid:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d " param, pJob->queryId, SCH_CLIENT_ID(pTask), \ + SCH_TASK_ID(pTask), SCH_TASK_EID(pTask), __VA_ARGS__) #define SCH_SET_ERRNO(_err) \ do { \ diff --git a/source/libs/scheduler/src/schRemote.c b/source/libs/scheduler/src/schRemote.c index b15a6a09d3..eefb32f783 100644 --- a/source/libs/scheduler/src/schRemote.c +++ b/source/libs/scheduler/src/schRemote.c @@ -500,8 +500,8 @@ _return: int32_t schHandleDropCallback(void *param, SDataBuf *pMsg, int32_t code) { SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; - qDebug("QID:0x%" PRIx64 ",TID:0x%" PRIx64 " drop task rsp received, code:0x%x", pParam->queryId, pParam->taskId, - code); + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 " drop task rsp received, code:0x%x", pParam->queryId, + pParam->clientId, pParam->taskId, code); // called if drop task rsp received code (void)rpcReleaseHandle(pMsg->handle, TAOS_CONN_CLIENT); // ignore error @@ -517,8 +517,8 @@ int32_t schHandleDropCallback(void *param, SDataBuf *pMsg, int32_t code) { int32_t schHandleNotifyCallback(void *param, SDataBuf *pMsg, int32_t code) { SSchTaskCallbackParam *pParam = (SSchTaskCallbackParam *)param; - qDebug("QID:0x%" PRIx64 ",TID:0x%" PRIx64 " task notify rsp received, code:0x%x", pParam->queryId, pParam->taskId, - code); + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 " task notify rsp received, code:0x%x", pParam->queryId, + pParam->clientId, pParam->taskId, code); if (pMsg) { taosMemoryFree(pMsg->pData); taosMemoryFree(pMsg->pEpSet); @@ -595,6 +595,7 @@ int32_t schMakeCallbackParam(SSchJob *pJob, SSchTask *pTask, int32_t msgType, bo param->queryId = pJob->queryId; param->refId = pJob->refId; + param->clientId = SCH_CLIENT_ID(pTask); param->taskId = SCH_TASK_ID(pTask); param->pTrans = pJob->conn.pTrans; param->execId = pTask->execId; @@ -1138,6 +1139,7 @@ int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, req.header.vgId = addr->nodeId; req.sId = schMgmt.sId; req.queryId = pJob->queryId; + req.clientId = pTask->clientId; req.taskId = pTask->taskId; req.phyLen = pTask->msgLen; req.sqlLen = strlen(pJob->sql); @@ -1171,6 +1173,7 @@ int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, qMsg.header.contLen = 0; qMsg.sId = schMgmt.sId; qMsg.queryId = pJob->queryId; + qMsg.clientId = pTask->clientId; qMsg.taskId = pTask->taskId; qMsg.refId = pJob->refId; qMsg.execId = pTask->execId; @@ -1226,6 +1229,7 @@ int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, req.header.vgId = addr->nodeId; req.sId = schMgmt.sId; req.queryId = pJob->queryId; + req.clientId = pTask->clientId; req.taskId = pTask->taskId; req.execId = pTask->execId; @@ -1253,6 +1257,7 @@ int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, qMsg.header.contLen = 0; qMsg.sId = schMgmt.sId; qMsg.queryId = pJob->queryId; + qMsg.clientId = pTask->clientId; qMsg.taskId = pTask->taskId; qMsg.refId = pJob->refId; qMsg.execId = *(int32_t*)param; @@ -1310,6 +1315,7 @@ int32_t schBuildAndSendMsg(SSchJob *pJob, SSchTask *pTask, SQueryNodeAddr *addr, qMsg.header.contLen = 0; qMsg.sId = schMgmt.sId; qMsg.queryId = pJob->queryId; + qMsg.clientId = pTask->clientId; qMsg.taskId = pTask->taskId; qMsg.refId = pJob->refId; qMsg.execId = pTask->execId; diff --git a/source/libs/scheduler/src/schTask.c b/source/libs/scheduler/src/schTask.c index fe24633c12..9be0e3fc40 100644 --- a/source/libs/scheduler/src/schTask.c +++ b/source/libs/scheduler/src/schTask.c @@ -66,6 +66,7 @@ int32_t schInitTask(SSchJob *pJob, SSchTask *pTask, SSubplan *pPlan, SSchLevel * pTask->execId = -1; pTask->failedExecId = -2; pTask->timeoutUsec = SCH_DEFAULT_TASK_TIMEOUT_USEC; + pTask->clientId = getClientId(); pTask->taskId = schGenTaskId(); schInitTaskRetryTimes(pJob, pTask, pLevel); @@ -305,6 +306,7 @@ int32_t schProcessOnTaskSuccess(SSchJob *pJob, SSchTask *pTask) { SCH_LOCK(SCH_WRITE, &parent->planLock); SDownstreamSourceNode source = { .type = QUERY_NODE_DOWNSTREAM_SOURCE, + .clientId = pTask->clientId, .taskId = pTask->taskId, .schedId = schMgmt.sId, .execId = pTask->execId, @@ -996,8 +998,8 @@ int32_t schProcessOnTaskStatusRsp(SQueryNodeEpId *pEpId, SArray *pStatusList) { int32_t code = 0; - qDebug("QID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d task status in server: %s", pStatus->queryId, pStatus->taskId, - pStatus->execId, jobTaskStatusStr(pStatus->status)); + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ",EID:%d task status in server: %s", pStatus->queryId, + pStatus->clientId, pStatus->taskId, pStatus->execId, jobTaskStatusStr(pStatus->status)); if (schProcessOnCbBegin(&pJob, &pTask, pStatus->queryId, pStatus->refId, pStatus->taskId)) { continue; @@ -1043,13 +1045,14 @@ int32_t schHandleExplainRes(SArray *pExplainRes) { continue; } - qDebug("QID:0x%" PRIx64 ",TID:0x%" PRIx64 ", begin to handle LOCAL explain rsp msg", localRsp->qId, localRsp->tId); + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ", begin to handle LOCAL explain rsp msg", + localRsp->qId, localRsp->cId, localRsp->tId); pJob = NULL; (void)schAcquireJob(localRsp->rId, &pJob); if (NULL == pJob) { - qWarn("QID:0x%" PRIx64 ",TID:0x%" PRIx64 "job no exist, may be dropped, refId:0x%" PRIx64, localRsp->qId, - localRsp->tId, localRsp->rId); + qWarn("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 "job no exist, may be dropped, refId:0x%" PRIx64, + localRsp->qId, localRsp->cId, localRsp->tId, localRsp->rId); SCH_ERR_JRET(TSDB_CODE_QRY_JOB_NOT_EXIST); } @@ -1068,8 +1071,8 @@ int32_t schHandleExplainRes(SArray *pExplainRes) { (void)schReleaseJob(pJob->refId); - qDebug("QID:0x%" PRIx64 ",TID:0x%" PRIx64 ", end to handle LOCAL explain rsp msg, code:%x", localRsp->qId, - localRsp->tId, code); + qDebug("QID:0x%" PRIx64 ",CID:0x%" PRIx64 ",TID:0x%" PRIx64 ", end to handle LOCAL explain rsp msg, code:%x", + localRsp->qId, localRsp->cId, localRsp->tId, code); SCH_ERR_JRET(code); @@ -1147,8 +1150,8 @@ int32_t schLaunchLocalTask(SSchJob *pJob, SSchTask *pTask) { } } - SCH_ERR_JRET(qWorkerProcessLocalQuery(schMgmt.queryMgmt, schMgmt.sId, pJob->queryId, pTask->taskId, pJob->refId, - pTask->execId, &qwMsg, explainRes)); + SCH_ERR_JRET(qWorkerProcessLocalQuery(schMgmt.queryMgmt, schMgmt.sId, pJob->queryId, pTask->clientId, pTask->taskId, + pJob->refId, pTask->execId, &qwMsg, explainRes)); if (SCH_IS_EXPLAIN_JOB(pJob)) { SCH_ERR_RET(schHandleExplainRes(explainRes)); @@ -1407,8 +1410,8 @@ int32_t schExecLocalFetch(SSchJob *pJob, SSchTask *pTask) { } } - SCH_ERR_JRET(qWorkerProcessLocalFetch(schMgmt.queryMgmt, schMgmt.sId, pJob->queryId, pTask->taskId, pJob->refId, - pTask->execId, &pRsp, explainRes)); + SCH_ERR_JRET(qWorkerProcessLocalFetch(schMgmt.queryMgmt, schMgmt.sId, pJob->queryId, pTask->clientId, pTask->taskId, + pJob->refId, pTask->execId, &pRsp, explainRes)); if (SCH_IS_EXPLAIN_JOB(pJob)) { SCH_ERR_RET(schHandleExplainRes(explainRes)); diff --git a/source/libs/scheduler/src/schUtil.c b/source/libs/scheduler/src/schUtil.c index 4697de6f28..ac34099417 100644 --- a/source/libs/scheduler/src/schUtil.c +++ b/source/libs/scheduler/src/schUtil.c @@ -293,6 +293,18 @@ void schCloseJobRef(void) { } } +int32_t initClientId(void) { + int32_t code = taosGetSystemUUIDU64(&schMgmt.clientId); + if (code != TSDB_CODE_SUCCESS) { + qError("failed to generate clientId since %s", tstrerror(code)); + SCH_ERR_RET(code); + } + qInfo("initialize"); + return TSDB_CODE_SUCCESS; +} + +uint64_t getClientId(void) { return schMgmt.clientId; } + uint64_t schGenTaskId(void) { return atomic_add_fetch_64(&schMgmt.taskId, 1); } #ifdef BUILD_NO_CALL diff --git a/tests/script/api/sameReqidTest.c b/tests/script/api/sameReqidTest.c new file mode 100644 index 0000000000..7507619886 --- /dev/null +++ b/tests/script/api/sameReqidTest.c @@ -0,0 +1,406 @@ +// sample code to verify multiple queries with the same reqid +// to compile: gcc -o sameReqdiTest sameReqidTest.c -ltaos + +#include +#include +#include +#include +#include "taos.h" + +#define NUM_ROUNDS 10 +#define CONST_REQ_ID 12345 +#define TEST_DB "test" + +#define CHECK_CONDITION(condition, prompt, errstr) \ + do { \ + if (!(condition)) { \ + printf("\033[31m[%s:%d] failed to " prompt ", reason: %s\033[0m\n", __func__, __LINE__, errstr); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define CHECK_RES(res, prompt) CHECK_CONDITION(taos_errno(res) == 0, prompt, taos_errstr(res)) +#define CHECK_CODE(code, prompt) CHECK_CONDITION(code == 0, prompt, taos_errstr(NULL)) + +static TAOS* getNewConnection() { + const char* host = "127.0.0.1"; + const char* user = "root"; + const char* passwd = "taosdata"; + TAOS* taos = NULL; + + taos_options(TSDB_OPTION_TIMEZONE, "GMT-8"); + taos = taos_connect(host, user, passwd, "", 0); + CHECK_CONDITION(taos != NULL, "connect to db", taos_errstr(NULL)); + return taos; +} + +static void prepareData(TAOS* taos) { + TAOS_RES* res = NULL; + int32_t code = 0; + + res = taos_query(taos, "create database if not exists " TEST_DB " precision 'ns'"); + CHECK_RES(res, "create database"); + taos_free_result(res); + usleep(100000); + + code = taos_select_db(taos, TEST_DB); + CHECK_CODE(code, "switch to database"); + + res = taos_query(taos, "create table if not exists meters(ts timestamp, a int) tags(area int)"); + CHECK_RES(res, "create stable meters"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t0 using meters tags(0)"); + CHECK_RES(res, "create table t0"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t1 using meters tags(1)"); + CHECK_RES(res, "create table t1"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t2 using meters tags(2)"); + CHECK_RES(res, "create table t2"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t3 using meters tags(3)"); + CHECK_RES(res, "create table t3"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t4 using meters tags(4)"); + CHECK_RES(res, "create table t4"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t5 using meters tags(5)"); + CHECK_RES(res, "create table t5"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t6 using meters tags(6)"); + CHECK_RES(res, "create table t6"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t7 using meters tags(7)"); + CHECK_RES(res, "create table t7"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t8 using meters tags(8)"); + CHECK_RES(res, "create table t8"); + taos_free_result(res); + + res = taos_query(taos, "create table if not exists t9 using meters tags(9)"); + CHECK_RES(res, "create table t9"); + taos_free_result(res); + + res = taos_query(taos, + "insert into t0 values('2020-01-01 00:00:00.000', 0)" + " ('2020-01-01 00:01:00.000', 0)" + " ('2020-01-01 00:02:00.000', 0)" + " t1 values('2020-01-01 00:00:00.000', 1)" + " ('2020-01-01 00:01:00.000', 1)" + " ('2020-01-01 00:02:00.000', 1)" + " ('2020-01-01 00:03:00.000', 1)" + " t2 values('2020-01-01 00:00:00.000', 2)" + " ('2020-01-01 00:01:00.000', 2)" + " ('2020-01-01 00:01:01.000', 2)" + " ('2020-01-01 00:01:02.000', 2)" + " t3 values('2020-01-01 00:01:02.000', 3)" + " t4 values('2020-01-01 00:01:02.000', 4)" + " t5 values('2020-01-01 00:01:02.000', 5)" + " t6 values('2020-01-01 00:01:02.000', 6)" + " t7 values('2020-01-01 00:01:02.000', 7)" + " t8 values('2020-01-01 00:01:02.000', 8)" + " t9 values('2020-01-01 00:01:02.000', 9)"); + CHECK_RES(res, "insert into meters"); + CHECK_CONDITION(taos_affected_rows(res), "insert into meters", "insufficient count"); + taos_free_result(res); + + res = taos_query( + taos, + "create table if not exists m1 (ts timestamp, b bool, v1 tinyint, v2 smallint, v4 int, v8 bigint, f4 float, f8 " + "double, bin binary(40), blob nchar(10))"); + CHECK_RES(res, "create table m1"); + taos_free_result(res); + + usleep(1000000); +} + +static void verifySchemaLess(TAOS* taos) { + TAOS_RES* res = NULL; + char* lines[] = { + "st,t1=3i64,t2=4f64,t3=L\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000", + "st,t1=4i64,t3=L\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin\",c2=true,c4=5f64,c5=5f64 1626006833640000000", + "st,t2=5f64,t3=L\"ste\" c1=4i64,c2=true,c3=L\"iam\" 1626056811823316532", + "st,t1=4i64,t2=5f64,t3=L\"t4\" c1=3i64,c3=L\"passitagain\",c2=true,c4=5f64 1626006833642000000", + "st,t2=5f64,t3=L\"ste2\" c3=L\"iamszhou\",c2=false 1626056811843316532", + "st,t2=5f64,t3=L\"ste2\" c3=L\"iamszhou\",c2=false,c5=5f64,c6=7u64,c7=32i32,c8=88.88f32 1626056812843316532", + "st,t1=4i64,t3=L\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin\",c2=true,c4=5f64,c5=5f64,c6=7u64 " + "1626006933640000000", + "st,t1=4i64,t3=L\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin\",c2=true,c4=5f64,c5=5f64,c6=7u64 " + "1626006933640000000", + "st,t1=4i64,t3=L\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin_stf\",c2=false,c5=5f64,c6=7u64 " + "1626006933641000000"}; + + res = taos_schemaless_insert_with_reqid(taos, lines, sizeof(lines) / sizeof(char*), TSDB_SML_LINE_PROTOCOL, + TSDB_SML_TIMESTAMP_NANO_SECONDS, CONST_REQ_ID); + CHECK_RES(res, "insert schema-less data"); + printf("successfully inserted %d rows\n", taos_affected_rows(res)); + taos_free_result(res); +} + +static int32_t printResult(TAOS_RES* res, int32_t nlimit) { + TAOS_ROW row = NULL; + TAOS_FIELD* fields = NULL; + int32_t numFields = 0; + int32_t nRows = 0; + + numFields = taos_num_fields(res); + fields = taos_fetch_fields(res); + while ((nlimit-- > 0) && (row = taos_fetch_row(res))) { + char temp[256] = {0}; + taos_print_row(temp, row, fields, numFields); + puts(temp); + nRows++; + } + return nRows; +} + +static void verifyQuery(TAOS* taos) { + TAOS_RES* res = NULL; + + res = taos_query_with_reqid(taos, "select * from meters", CONST_REQ_ID); + CHECK_RES(res, "select from meters"); + printResult(res, INT32_MAX); + taos_free_result(res); + + res = taos_query_with_reqid(taos, "select * from t0", CONST_REQ_ID); + CHECK_RES(res, "select from t0"); + printResult(res, INT32_MAX); + taos_free_result(res); + + res = taos_query_with_reqid(taos, "select * from t1", CONST_REQ_ID); + CHECK_RES(res, "select from t1"); + printResult(res, INT32_MAX); + taos_free_result(res); + + res = taos_query_with_reqid(taos, "select * from t2", CONST_REQ_ID); + CHECK_RES(res, "select from t2"); + printResult(res, INT32_MAX); + taos_free_result(res); + + res = taos_query_with_reqid(taos, "select * from t3", CONST_REQ_ID); + CHECK_RES(res, "select from t3"); + printResult(res, INT32_MAX); + taos_free_result(res); + + printf("succeed to read from meters\n"); +} + +void retrieveCallback(void* param, TAOS_RES* res, int32_t nrows) { + if (nrows == 0) { + taos_free_result(res); + } else { + printResult(res, nrows); + taos_fetch_rows_a(res, retrieveCallback, param); + } +} + +void selectCallback(void* param, TAOS_RES* res, int32_t code) { + CHECK_CODE(code, "read async from table"); + taos_fetch_rows_a(res, retrieveCallback, param); +} + +static void verifyQueryAsync(TAOS* taos) { + taos_query_a_with_reqid(taos, "select *from meters", selectCallback, NULL, CONST_REQ_ID); + taos_query_a_with_reqid(taos, "select *from t0", selectCallback, NULL, CONST_REQ_ID); + taos_query_a_with_reqid(taos, "select *from t1", selectCallback, NULL, CONST_REQ_ID); + taos_query_a_with_reqid(taos, "select *from t2", selectCallback, NULL, CONST_REQ_ID); + taos_query_a_with_reqid(taos, "select *from t3", selectCallback, NULL, CONST_REQ_ID); + + sleep(1); +} + +void veriryStmt(TAOS* taos) { + // insert 10 records + struct { + int64_t ts[10]; + int8_t b[10]; + int8_t v1[10]; + int16_t v2[10]; + int32_t v4[10]; + int64_t v8[10]; + float f4[10]; + double f8[10]; + char bin[10][40]; + char blob[10][80]; + } v; + + int32_t* t8_len = malloc(sizeof(int32_t) * 10); + int32_t* t16_len = malloc(sizeof(int32_t) * 10); + int32_t* t32_len = malloc(sizeof(int32_t) * 10); + int32_t* t64_len = malloc(sizeof(int32_t) * 10); + int32_t* float_len = malloc(sizeof(int32_t) * 10); + int32_t* double_len = malloc(sizeof(int32_t) * 10); + int32_t* bin_len = malloc(sizeof(int32_t) * 10); + int32_t* blob_len = malloc(sizeof(int32_t) * 10); + + TAOS_STMT* stmt = taos_stmt_init_with_reqid(taos, CONST_REQ_ID); + TAOS_MULTI_BIND params[10]; + char is_null[10] = {0}; + + params[0].buffer_type = TSDB_DATA_TYPE_TIMESTAMP; + params[0].buffer_length = sizeof(v.ts[0]); + params[0].buffer = v.ts; + params[0].length = t64_len; + params[0].is_null = is_null; + params[0].num = 10; + + params[1].buffer_type = TSDB_DATA_TYPE_BOOL; + params[1].buffer_length = sizeof(v.b[0]); + params[1].buffer = v.b; + params[1].length = t8_len; + params[1].is_null = is_null; + params[1].num = 10; + + params[2].buffer_type = TSDB_DATA_TYPE_TINYINT; + params[2].buffer_length = sizeof(v.v1[0]); + params[2].buffer = v.v1; + params[2].length = t8_len; + params[2].is_null = is_null; + params[2].num = 10; + + params[3].buffer_type = TSDB_DATA_TYPE_SMALLINT; + params[3].buffer_length = sizeof(v.v2[0]); + params[3].buffer = v.v2; + params[3].length = t16_len; + params[3].is_null = is_null; + params[3].num = 10; + + params[4].buffer_type = TSDB_DATA_TYPE_INT; + params[4].buffer_length = sizeof(v.v4[0]); + params[4].buffer = v.v4; + params[4].length = t32_len; + params[4].is_null = is_null; + params[4].num = 10; + + params[5].buffer_type = TSDB_DATA_TYPE_BIGINT; + params[5].buffer_length = sizeof(v.v8[0]); + params[5].buffer = v.v8; + params[5].length = t64_len; + params[5].is_null = is_null; + params[5].num = 10; + + params[6].buffer_type = TSDB_DATA_TYPE_FLOAT; + params[6].buffer_length = sizeof(v.f4[0]); + params[6].buffer = v.f4; + params[6].length = float_len; + params[6].is_null = is_null; + params[6].num = 10; + + params[7].buffer_type = TSDB_DATA_TYPE_DOUBLE; + params[7].buffer_length = sizeof(v.f8[0]); + params[7].buffer = v.f8; + params[7].length = double_len; + params[7].is_null = is_null; + params[7].num = 10; + + params[8].buffer_type = TSDB_DATA_TYPE_BINARY; + params[8].buffer_length = sizeof(v.bin[0]); + params[8].buffer = v.bin; + params[8].length = bin_len; + params[8].is_null = is_null; + params[8].num = 10; + + params[9].buffer_type = TSDB_DATA_TYPE_NCHAR; + params[9].buffer_length = sizeof(v.blob[0]); + params[9].buffer = v.blob; + params[9].length = blob_len; + params[9].is_null = is_null; + params[9].num = 10; + + int32_t code = taos_stmt_prepare( + stmt, "insert into ? (ts, b, v1, v2, v4, v8, f4, f8, bin, blob) values(?,?,?,?,?,?,?,?,?,?)", 0); + CHECK_CODE(code, "taos_stmt_prepare"); + + code = taos_stmt_set_tbname(stmt, "m1"); + CHECK_CODE(code, "taos_stmt_set_tbname"); + + int64_t ts = 1591060628000000000; + for (int i = 0; i < 10; ++i) { + v.ts[i] = ts; + ts += 1000000; + is_null[i] = 0; + + v.b[i] = (int8_t)i % 2; + v.v1[i] = (int8_t)i; + v.v2[i] = (int16_t)(i * 2); + v.v4[i] = (int32_t)(i * 4); + v.v8[i] = (int64_t)(i * 8); + v.f4[i] = (float)(i * 40); + v.f8[i] = (double)(i * 80); + for (int j = 0; j < sizeof(v.bin[0]); ++j) { + v.bin[i][j] = (char)(i + '0'); + } + strcpy(v.blob[i], "一二三四五六七八九十"); + + t8_len[i] = sizeof(int8_t); + t16_len[i] = sizeof(int16_t); + t32_len[i] = sizeof(int32_t); + t64_len[i] = sizeof(int64_t); + float_len[i] = sizeof(float); + double_len[i] = sizeof(double); + bin_len[i] = sizeof(v.bin[0]); + blob_len[i] = (int32_t)strlen(v.blob[i]); + } + + code = taos_stmt_bind_param_batch(stmt, params); + CHECK_CODE(code, "taos_stmt_bind_param_batch"); + + code = taos_stmt_add_batch(stmt); + CHECK_CODE(code, "taos_stmt_add_batch"); + + code = taos_stmt_execute(stmt); + CHECK_CODE(code, "taos_stmt_execute"); + + taos_stmt_close(stmt); + + free(t8_len); + free(t16_len); + free(t32_len); + free(t64_len); + free(float_len); + free(double_len); + free(bin_len); + free(blob_len); +} + +int main(int argc, char* argv[]) { + TAOS* taos = NULL; + int32_t code = 0; + + taos = getNewConnection(); + taos_select_db(taos, TEST_DB); + CHECK_CODE(code, "switch to database"); + + printf("************ prepare data *************\n"); + prepareData(taos); + + for (int32_t i = 0; i < NUM_ROUNDS; ++i) { + printf("************ verify schema-less *************\n"); + verifySchemaLess(taos); + + printf("************ verify query *************\n"); + verifyQuery(taos); + + printf("********* verify async query **********\n"); + verifyQueryAsync(taos); + + printf("********* verify stmt query **********\n"); + veriryStmt(taos); + + printf("done\n"); + } + + taos_close(taos); + taos_cleanup(); + + return 0; +} From 04565b23efdf7c463bba6c005c0b85e62fc800c6 Mon Sep 17 00:00:00 2001 From: xsren <285808407@qq.com> Date: Thu, 7 Nov 2024 13:36:53 +0800 Subject: [PATCH 17/55] Parameter validation --- include/common/ttime.h | 3 ++- include/os/osTime.h | 2 +- source/common/src/ttime.c | 2 +- source/libs/function/src/builtins.c | 8 ++++++-- source/libs/parser/src/parInsertSql.c | 2 +- source/os/src/osTime.c | 13 +++++++++--- source/util/src/tlog.c | 29 ++++++++++++++++++++++----- 7 files changed, 45 insertions(+), 14 deletions(-) diff --git a/include/common/ttime.h b/include/common/ttime.h index 65bb763b1f..1ffcc29eca 100644 --- a/include/common/ttime.h +++ b/include/common/ttime.h @@ -62,7 +62,8 @@ static FORCE_INLINE int64_t taosGetTimestampToday(int32_t precision) { int64_t factor = (precision == TSDB_TIME_PRECISION_MILLI) ? 1000 : (precision == TSDB_TIME_PRECISION_MICRO) ? 1000000 : 1000000000; - time_t t = taosTime(NULL); + time_t t; + (void) taosTime(&t); struct tm tm; (void) taosLocalTime(&t, &tm, NULL, 0); tm.tm_hour = 0; diff --git a/include/os/osTime.h b/include/os/osTime.h index 5d74146e9c..7a65efe28d 100644 --- a/include/os/osTime.h +++ b/include/os/osTime.h @@ -93,7 +93,7 @@ static FORCE_INLINE int64_t taosGetMonoTimestampMs() { char *taosStrpTime(const char *buf, const char *fmt, struct tm *tm); struct tm *taosLocalTime(const time_t *timep, struct tm *result, char *buf, int32_t bufSize); struct tm *taosLocalTimeNolock(struct tm *result, const time_t *timep, int dst); -time_t taosTime(time_t *t); +int32_t taosTime(time_t *t); time_t taosMktime(struct tm *timep); int64_t user_mktime64(const uint32_t year, const uint32_t mon, const uint32_t day, const uint32_t hour, const uint32_t min, const uint32_t sec, int64_t time_zone); diff --git a/source/common/src/ttime.c b/source/common/src/ttime.c index 75624593d9..ecdb3de9a2 100644 --- a/source/common/src/ttime.c +++ b/source/common/src/ttime.c @@ -30,7 +30,7 @@ static int64_t m_deltaUtc = 0; void deltaToUtcInitOnce() { struct tm tm = {0}; - if (taosStrpTime("1970-01-01 00:00:00", (const char*)("%Y-%m-%d %H:%M:%S"), &tm) != 0) { + if (taosStrpTime("1970-01-01 00:00:00", (const char*)("%Y-%m-%d %H:%M:%S"), &tm) == NULL) { uError("failed to parse time string"); } m_deltaUtc = (int64_t)taosMktime(&tm); diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index 552933dcad..b369ee794c 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -188,7 +188,11 @@ static int32_t countTrailingSpaces(const SValueNode* pVal, bool isLtrim) { static int32_t addTimezoneParam(SNodeList* pList) { char buf[TD_TIME_STR_LEN] = {0}; - time_t t = taosTime(NULL); + time_t t; + int32_t code = taosTime(&t); + if (code != 0) { + return code; + } struct tm tmInfo; if (taosLocalTime(&t, &tmInfo, buf, sizeof(buf)) != NULL) { (void)strftime(buf, sizeof(buf), "%z", &tmInfo); @@ -196,7 +200,7 @@ static int32_t addTimezoneParam(SNodeList* pList) { int32_t len = (int32_t)strlen(buf); SValueNode* pVal = NULL; - int32_t code = nodesMakeNode(QUERY_NODE_VALUE, (SNode**)&pVal); + code = nodesMakeNode(QUERY_NODE_VALUE, (SNode**)&pVal); if (pVal == NULL) { return code; } diff --git a/source/libs/parser/src/parInsertSql.c b/source/libs/parser/src/parInsertSql.c index 4b91f01a8c..750621bf66 100644 --- a/source/libs/parser/src/parInsertSql.c +++ b/source/libs/parser/src/parInsertSql.c @@ -246,7 +246,7 @@ static int32_t parseBoundColumns(SInsertParseContext* pCxt, const char** pSql, E return code; } -static int parseTimestampOrInterval(const char** end, SToken* pToken, int16_t timePrec, int64_t* ts, int64_t* interval, +static int32_t parseTimestampOrInterval(const char** end, SToken* pToken, int16_t timePrec, int64_t* ts, int64_t* interval, SMsgBuf* pMsgBuf, bool* isTs) { if (pToken->type == TK_NOW) { *isTs = true; diff --git a/source/os/src/osTime.c b/source/os/src/osTime.c index d4d9936154..60339fc646 100644 --- a/source/os/src/osTime.c +++ b/source/os/src/osTime.c @@ -81,6 +81,7 @@ static const char *am_pm[2] = {"AM", "PM"}; #endif char *taosStrpTime(const char *buf, const char *fmt, struct tm *tm) { + if (!buf || !fmt || !tm) return NULL; #ifdef WINDOWS char c; const char *bp; @@ -345,6 +346,9 @@ char *taosStrpTime(const char *buf, const char *fmt, struct tm *tm) { } int32_t taosGetTimeOfDay(struct timeval *tv) { + if (tv == NULL) { + return TSDB_CODE_INVALID_PARA; + } int32_t code = 0; #ifdef WINDOWS LARGE_INTEGER t; @@ -365,12 +369,15 @@ int32_t taosGetTimeOfDay(struct timeval *tv) { #endif } -time_t taosTime(time_t *t) { +int32_t taosTime(time_t *t) { + if (t == NULL) { + return TSDB_CODE_INVALID_PARA; + } time_t r = time(t); if (r == (time_t)-1) { - terrno = TAOS_SYSTEM_ERROR(errno); + return TAOS_SYSTEM_ERROR(errno); } - return r; + return 0; } /* diff --git a/source/util/src/tlog.c b/source/util/src/tlog.c index 76d0139521..f70b145dbc 100644 --- a/source/util/src/tlog.c +++ b/source/util/src/tlog.c @@ -154,16 +154,26 @@ static int32_t taosStartLog() { return 0; } -static void getDay(char *buf, int32_t bufSize) { - time_t t = taosTime(NULL); +static int32_t getDay(char *buf, int32_t bufSize) { + time_t t; + int32_t code = taosTime(&t); + if(code != 0) { + return code; + } struct tm tmInfo; if (taosLocalTime(&t, &tmInfo, buf, bufSize) != NULL) { TAOS_UNUSED(strftime(buf, bufSize, "%Y-%m-%d", &tmInfo)); } + return 0; } static int64_t getTimestampToday() { - time_t t = taosTime(NULL); + time_t t; + int32_t code = taosTime(&t); + if (code != 0) { + uError("failed to get time, reason:%s", tstrerror(code)); + return 0; + } struct tm tm; if (taosLocalTime(&t, &tm, NULL, 0) == NULL) { return 0; @@ -203,7 +213,11 @@ int32_t taosInitSlowLog() { char name[PATH_MAX + TD_TIME_STR_LEN] = {0}; char day[TD_TIME_STR_LEN] = {0}; - getDay(day, sizeof(day)); + int32_t code = getDay(day, sizeof(day)); + if (code != 0) { + (void)printf("failed to get day, reason:%s\n", tstrerror(code)); + return code; + } (void)snprintf(name, PATH_MAX + TD_TIME_STR_LEN, "%s.%s", tsLogObj.slowLogName, day); tsLogObj.timestampToday = getTimestampToday(); @@ -434,7 +448,12 @@ static void taosOpenNewSlowLogFile() { atomic_store_32(&tsLogObj.slowHandle->lock, 0); char day[TD_TIME_STR_LEN] = {0}; - getDay(day, sizeof(day)); + int32_t code = getDay(day, sizeof(day)); + if (code != 0) { + uError("failed to get day, reason:%s", tstrerror(code)); + (void)taosThreadMutexUnlock(&tsLogObj.logMutex); + return; + } TdFilePtr pFile = NULL; char name[PATH_MAX + TD_TIME_STR_LEN] = {0}; (void)snprintf(name, PATH_MAX + TD_TIME_STR_LEN, "%s.%s", tsLogObj.slowLogName, day); From 089456fa5be415b4c7edfee3e01f74144b02c6ba Mon Sep 17 00:00:00 2001 From: kailixu Date: Thu, 7 Nov 2024 13:43:08 +0800 Subject: [PATCH 18/55] enh: add makefile of passwdTest.c for windows --- tests/script/api/makefile_win64.mak | 20 +++++++++++++++ tests/script/api/passwdTest.c | 39 +++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 tests/script/api/makefile_win64.mak diff --git a/tests/script/api/makefile_win64.mak b/tests/script/api/makefile_win64.mak new file mode 100644 index 0000000000..9963ef4133 --- /dev/null +++ b/tests/script/api/makefile_win64.mak @@ -0,0 +1,20 @@ +# Makefile.mak for win64 + +TARGET = passwdTest.exe +CC = cl +CFLAGS = /W4 /EHsc /I"C:\TDengine\include" /D_WINDOWS +LDFLAGS = /link /LIBPATH:"C:\TDengine\driver" taos.lib + +SRCS = passwdTest.c +OBJS = $(SRCS:.c=.obj) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(OBJS) $(LDFLAGS) + +.c.obj: + $(CC) $(CFLAGS) /c $< + +clean: + del $(OBJS) $(TARGET) \ No newline at end of file diff --git a/tests/script/api/passwdTest.c b/tests/script/api/passwdTest.c index 928525750e..7d35b8cc19 100644 --- a/tests/script/api/passwdTest.c +++ b/tests/script/api/passwdTest.c @@ -20,12 +20,27 @@ * passwdTest.c * - Run the test case in clear TDengine environment with default root passwd 'taosdata' */ +#ifdef WINDOWS +#include +#include +#include +#ifndef PRId64 +#define PRId64 "I64d" +#endif + +#ifndef PRIu64 +#define PRIu64 "I64u" +#endif + +#else #include +#include +#endif + #include #include #include -#include #include "taos.h" // TAOS header file #define nDup 1 @@ -50,6 +65,16 @@ void sysInfoTest(TAOS *taos, const char *host, char *qstr); void userDroppedTest(TAOS *taos, const char *host, char *qstr); void clearTestEnv(TAOS *taos, const char *host, char *qstr); +void taosMsleep(int64_t ms) { + if (ms < 0) return; +#ifdef WINDOWS + Sleep(ms); +#else + usleep(ms * 1000); +#endif +} + + int nPassVerNotified = 0; int nUserDropped = 0; TAOS *taosu[nRoot] = {0}; @@ -283,20 +308,20 @@ void passVerTestMulti(const char *host, char *qstr) { printf("%s:%d [%d] second(s) elasped, passVer notification received:%d, total:%d\n", __func__, __LINE__, i, nPassVerNotified, nConn); if (nPassVerNotified >= nConn) break; - sleep(1); + taosMsleep(1000); } // close the taos_conn for (int i = 0; i < nRoot; ++i) { taos_close(taos[i]); printf("%s:%d close taos[%d]\n", __func__, __LINE__, i); - // sleep(1); + // taosMsleep(1000); } for (int i = 0; i < nUser; ++i) { taos_close(taosu[i]); printf("%s:%d close taosu[%d]\n", __func__, __LINE__, i); - // sleep(1); + // taosMsleep(1000); } fprintf(stderr, "######## %s #########\n", __func__); @@ -356,7 +381,7 @@ _REP: fprintf(stderr, "%s:%d sleep 2 seconds to wait HB take effect\n", __func__, __LINE__); for (int i = 1; i <= 2; ++i) { - sleep(1); + taosMsleep(1000); } res = taos_query(taos[0], qstr); @@ -372,7 +397,7 @@ _REP: queryDB(taosRoot, "alter user user0 sysinfo 1"); fprintf(stderr, "%s:%d sleep 2 seconds to wait HB take effect\n", __func__, __LINE__); for (int i = 1; i <= 2; ++i) { - sleep(1); + taosMsleep(1000); } if(++nRep < 5) { @@ -426,7 +451,7 @@ _loop: printf("%s:%d [%d] second(s) elasped, user dropped notification received:%d, total:%d\n", __func__, __LINE__, i, nUserDropped, nConn); if (nUserDropped >= nConn) break; - sleep(1); + taosMsleep(1000); } for (int i = 0; i < nTestUsers; ++i) { From 907cbe99edcfdca2d581989ccca1442b2914cbe4 Mon Sep 17 00:00:00 2001 From: kailixu Date: Thu, 7 Nov 2024 14:38:51 +0800 Subject: [PATCH 19/55] enh: adjust macro for makefile_win64.mak --- tests/script/api/makefile_win64.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/script/api/makefile_win64.mak b/tests/script/api/makefile_win64.mak index 9963ef4133..50a2447a06 100644 --- a/tests/script/api/makefile_win64.mak +++ b/tests/script/api/makefile_win64.mak @@ -2,7 +2,7 @@ TARGET = passwdTest.exe CC = cl -CFLAGS = /W4 /EHsc /I"C:\TDengine\include" /D_WINDOWS +CFLAGS = /W4 /EHsc /I"C:\TDengine\include" /DWINDOWS LDFLAGS = /link /LIBPATH:"C:\TDengine\driver" taos.lib SRCS = passwdTest.c From 21ceac423f087e7e21a867824f1855fae4984424 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Thu, 7 Nov 2024 15:14:47 +0800 Subject: [PATCH 20/55] Update test_passwd.py to remove make operation --- tests/army/user/test_passwd.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index 62dc5782b7..08445b4523 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -1,3 +1,4 @@ +import os import subprocess from frame.log import * from frame.cases import * @@ -10,11 +11,11 @@ class TDTestCase(TBase): def apiPath(self): apiPath = None currentFilePath = os.path.dirname(os.path.realpath(__file__)) - if ("community/tests" in currentFilePath): - testFilePath = currentFilePath[:currentFilePath.find("community/tests")] + if (os.sep.join(["community", "tests"]) in currentFilePath): + testFilePath = currentFilePath[:currentFilePath.find(os.sep.join(["community", "tests"]))] else: - testFilePath = currentFilePath[:currentFilePath.find("TDengine/tests")] - + testFilePath = currentFilePath[:currentFilePath.find(os.sep.join(["TDengine", "tests"]))] + for root, dirs, files in os.walk(testFilePath): if ("passwdTest.c" in files): apiPath = root @@ -25,21 +26,20 @@ class TDTestCase(TBase): apiPath = self.apiPath() tdLog.info(f"api path: {apiPath}") if apiPath: - p = subprocess.Popen(f"cd {apiPath} && make", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if 0 != p.returncode: - tdLog.info(f"Test script passwdTest.c make failed with error: {err}") test_file_cmd = os.sep.join([apiPath, "passwdTest localhost"]) + try: + p = subprocess.Popen(test_file_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if 0 != p.returncode: + tdLog.exit("Failed to run passwd test with output: %s \n error: %s" % (out, err)) + else: + tdLog.info(out) + tdLog.success(f"{__file__} successfully executed") + except Exception as e: + tdLog.exit(f"Failed to execute {__file__} with error: {e}") else: tdLog.exit("passwdTest.c not found") - try: - p = subprocess.Popen(test_file_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - if 0 != p.returncode: - tdLog.exit("Failed to run passwd test with output: %s \n error: %s" % (out, err)) - tdLog.success(f"{__file__} successfully executed") - except Exception as e: - tdLog.exit(f"Failed to execute {__file__} with error: {e}") + tdCases.addLinux(__file__, TDTestCase()) tdCases.addWindows(__file__, TDTestCase()) From 5c01cff369a7882818cd8c44d3bd56b04f24c640 Mon Sep 17 00:00:00 2001 From: xsren <285808407@qq.com> Date: Thu, 7 Nov 2024 16:19:57 +0800 Subject: [PATCH 21/55] enh: param --- source/common/src/tcol.c | 4 ++++ source/libs/command/src/command.c | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/source/common/src/tcol.c b/source/common/src/tcol.c index 923aab12ca..a23385aba0 100644 --- a/source/common/src/tcol.c +++ b/source/common/src/tcol.c @@ -166,6 +166,7 @@ const char* columnCompressStr(uint16_t type) { } uint8_t columnLevelVal(const char* level) { + if (level == NULL) return TSDB_COLVAL_LEVEL_NOCHANGE; uint8_t l = TSDB_COLVAL_LEVEL_MEDIUM; if (0 == strcmp(level, "h") || 0 == strcmp(level, TSDB_COLUMN_LEVEL_HIGH)) { l = TSDB_COLVAL_LEVEL_HIGH; @@ -180,6 +181,7 @@ uint8_t columnLevelVal(const char* level) { } uint16_t columnCompressVal(const char* compress) { + if (compress == NULL) return TSDB_COLVAL_COMPRESS_NOCHANGE; uint16_t c = TSDB_COLVAL_COMPRESS_LZ4; if (0 == strcmp(compress, TSDB_COLUMN_COMPRESS_LZ4)) { c = TSDB_COLVAL_COMPRESS_LZ4; @@ -200,6 +202,7 @@ uint16_t columnCompressVal(const char* compress) { } uint8_t columnEncodeVal(const char* encode) { + if (encode == NULL) return TSDB_COLVAL_ENCODE_NOCHANGE; uint8_t e = TSDB_COLVAL_ENCODE_SIMPLE8B; if (0 == strcmp(encode, TSDB_COLUMN_ENCODE_SIMPLE8B)) { e = TSDB_COLVAL_ENCODE_SIMPLE8B; @@ -311,6 +314,7 @@ void setColLevel(uint32_t* compress, uint8_t level) { int32_t setColCompressByOption(uint8_t type, uint8_t encode, uint16_t compressType, uint8_t level, bool check, uint32_t* compress) { + if(compress == NULL) return TSDB_CODE_TSC_ENCODE_PARAM_ERROR; if (check && !validColEncode(type, encode)) return TSDB_CODE_TSC_ENCODE_PARAM_ERROR; setColEncode(compress, encode); diff --git a/source/libs/command/src/command.c b/source/libs/command/src/command.c index 6272ac7049..5afdf87afb 100644 --- a/source/libs/command/src/command.c +++ b/source/libs/command/src/command.c @@ -35,6 +35,9 @@ extern SConfig* tsCfg; static int32_t buildRetrieveTableRsp(SSDataBlock* pBlock, int32_t numOfCols, SRetrieveTableRsp** pRsp) { + if (NULL == pBlock || NULL == pRsp) { + return TSDB_CODE_INVALID_PARA; + } size_t dataEncodeBufSize = blockGetEncodeSize(pBlock); size_t rspSize = sizeof(SRetrieveTableRsp) + dataEncodeBufSize + PAYLOAD_PREFIX_LEN; *pRsp = taosMemoryCalloc(1, rspSize); @@ -216,6 +219,9 @@ static int32_t setDescResultIntoDataBlock(bool sysInfoUser, SSDataBlock* pBlock, static int32_t execDescribe(bool sysInfoUser, SNode* pStmt, SRetrieveTableRsp** pRsp, int8_t biMode) { SDescribeStmt* pDesc = (SDescribeStmt*)pStmt; + if (NULL == pDesc || NULL == pDesc->pMeta) { + return TSDB_CODE_INVALID_PARA; + } int32_t numOfRows = TABLE_TOTAL_COL_NUM(pDesc->pMeta); SSDataBlock* pBlock = NULL; @@ -505,7 +511,7 @@ static int32_t buildCreateViewResultDataBlock(SSDataBlock** pOutput) { return code; } -void appendColumnFields(char* buf, int32_t* len, STableCfg* pCfg) { +static void appendColumnFields(char* buf, int32_t* len, STableCfg* pCfg) { for (int32_t i = 0; i < pCfg->numOfColumns; ++i) { SSchema* pSchema = pCfg->pSchemas + i; #define LTYPE_LEN (32 + 60) // 60 byte for compress info @@ -539,7 +545,7 @@ void appendColumnFields(char* buf, int32_t* len, STableCfg* pCfg) { } } -void appendTagFields(char* buf, int32_t* len, STableCfg* pCfg) { +static void appendTagFields(char* buf, int32_t* len, STableCfg* pCfg) { for (int32_t i = 0; i < pCfg->numOfTags; ++i) { SSchema* pSchema = pCfg->pSchemas + pCfg->numOfColumns + i; char type[32]; @@ -558,7 +564,7 @@ void appendTagFields(char* buf, int32_t* len, STableCfg* pCfg) { } } -void appendTagNameFields(char* buf, int32_t* len, STableCfg* pCfg) { +static void appendTagNameFields(char* buf, int32_t* len, STableCfg* pCfg) { for (int32_t i = 0; i < pCfg->numOfTags; ++i) { SSchema* pSchema = pCfg->pSchemas + pCfg->numOfColumns + i; *len += tsnprintf(buf + VARSTR_HEADER_SIZE + *len, SHOW_CREATE_TB_RESULT_FIELD2_LEN - (VARSTR_HEADER_SIZE + *len), @@ -566,7 +572,7 @@ void appendTagNameFields(char* buf, int32_t* len, STableCfg* pCfg) { } } -int32_t appendTagValues(char* buf, int32_t* len, STableCfg* pCfg) { +static int32_t appendTagValues(char* buf, int32_t* len, STableCfg* pCfg) { int32_t code = TSDB_CODE_SUCCESS; SArray* pTagVals = NULL; STag* pTag = (STag*)pCfg->pTags; @@ -643,7 +649,7 @@ _exit: return code; } -void appendTableOptions(char* buf, int32_t* len, SDbCfgInfo* pDbCfg, STableCfg* pCfg) { +static void appendTableOptions(char* buf, int32_t* len, SDbCfgInfo* pDbCfg, STableCfg* pCfg) { if (pCfg->commentLen > 0) { *len += tsnprintf(buf + VARSTR_HEADER_SIZE + *len, SHOW_CREATE_TB_RESULT_FIELD2_LEN - (VARSTR_HEADER_SIZE + *len), " COMMENT '%s'", pCfg->pComment); @@ -997,7 +1003,7 @@ static int32_t createSelectResultDataBlock(SNodeList* pProjects, SSDataBlock** p return code; } -int32_t buildSelectResultDataBlock(SNodeList* pProjects, SSDataBlock* pBlock) { +static int32_t buildSelectResultDataBlock(SNodeList* pProjects, SSDataBlock* pBlock) { QRY_ERR_RET(blockDataEnsureCapacity(pBlock, 1)); int32_t index = 0; From f97e42e957e55c1091a1a9327e7ebe3dfa7c901d Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Thu, 7 Nov 2024 17:23:52 +0800 Subject: [PATCH 22/55] Update test_passwd.py to add debug logs --- tests/army/user/test_passwd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index 08445b4523..c205273973 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -25,6 +25,9 @@ class TDTestCase(TBase): def run(self): apiPath = self.apiPath() tdLog.info(f"api path: {apiPath}") + p = subprocess.Popen(f"ls {apiPath}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + tdLog.info(f"test files: {out}") if apiPath: test_file_cmd = os.sep.join([apiPath, "passwdTest localhost"]) try: From 9adc756975c7cda6d08ea9f97062d66f4738118e Mon Sep 17 00:00:00 2001 From: xsren <285808407@qq.com> Date: Thu, 7 Nov 2024 17:37:33 +0800 Subject: [PATCH 23/55] enh: parameter validation --- source/libs/command/src/explain.c | 32 +++++++++++++++---------------- source/libs/function/src/tudf.c | 4 ++++ source/libs/function/src/udfd.c | 2 +- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/source/libs/command/src/explain.c b/source/libs/command/src/explain.c index 42c214fac7..3ab739334d 100644 --- a/source/libs/command/src/explain.c +++ b/source/libs/command/src/explain.c @@ -30,8 +30,8 @@ char *gJoinTypeStr[JOIN_TYPE_MAX_VALUE][JOIN_STYPE_MAX_VALUE] = { /*FULL*/ {"Full Join", "Full Join", NULL, NULL, NULL, NULL}, }; -int32_t qExplainGenerateResNode(SPhysiNode *pNode, SExplainGroup *group, SExplainResNode **pRes); -int32_t qExplainAppendGroupResRows(void *pCtx, int32_t groupId, int32_t level, bool singleChannel); +static int32_t qExplainGenerateResNode(SPhysiNode *pNode, SExplainGroup *group, SExplainResNode **pRes); +static int32_t qExplainAppendGroupResRows(void *pCtx, int32_t groupId, int32_t level, bool singleChannel); char *qExplainGetDynQryCtrlType(EDynQueryType type) { switch (type) { @@ -118,7 +118,7 @@ void qExplainFreeCtx(SExplainCtx *pCtx) { taosMemoryFree(pCtx); } -int32_t qExplainInitCtx(SExplainCtx **pCtx, SHashObj *groupHash, bool verbose, double ratio, EExplainMode mode) { +static int32_t qExplainInitCtx(SExplainCtx **pCtx, SHashObj *groupHash, bool verbose, double ratio, EExplainMode mode) { int32_t code = 0; SExplainCtx *ctx = taosMemoryCalloc(1, sizeof(SExplainCtx)); if (NULL == ctx) { @@ -158,7 +158,7 @@ _return: QRY_RET(code); } -int32_t qExplainGenerateResChildren(SPhysiNode *pNode, SExplainGroup *group, SNodeList **pChildren) { +static int32_t qExplainGenerateResChildren(SPhysiNode *pNode, SExplainGroup *group, SNodeList **pChildren) { int32_t tlen = 0; SNodeList *pPhysiChildren = pNode->pChildren; @@ -180,7 +180,7 @@ int32_t qExplainGenerateResChildren(SPhysiNode *pNode, SExplainGroup *group, SNo return TSDB_CODE_SUCCESS; } -int32_t qExplainGenerateResNodeExecInfo(SPhysiNode *pNode, SArray **pExecInfo, SExplainGroup *group) { +static int32_t qExplainGenerateResNodeExecInfo(SPhysiNode *pNode, SArray **pExecInfo, SExplainGroup *group) { *pExecInfo = taosArrayInit(group->nodeNum, sizeof(SExplainExecInfo)); if (NULL == (*pExecInfo)) { qError("taosArrayInit %d explainExecInfo failed", group->nodeNum); @@ -217,7 +217,7 @@ int32_t qExplainGenerateResNodeExecInfo(SPhysiNode *pNode, SArray **pExecInfo, S return TSDB_CODE_SUCCESS; } -int32_t qExplainGenerateResNode(SPhysiNode *pNode, SExplainGroup *group, SExplainResNode **pResNode) { +static int32_t qExplainGenerateResNode(SPhysiNode *pNode, SExplainGroup *group, SExplainResNode **pResNode) { if (NULL == pNode) { *pResNode = NULL; qError("physical node is NULL"); @@ -250,7 +250,7 @@ _return: QRY_RET(code); } -int32_t qExplainBufAppendExecInfo(SArray *pExecInfo, char *tbuf, int32_t *len) { +static int32_t qExplainBufAppendExecInfo(SArray *pExecInfo, char *tbuf, int32_t *len) { int32_t tlen = *len; int32_t nodeNum = taosArrayGetSize(pExecInfo); SExplainExecInfo maxExecInfo = {0}; @@ -275,7 +275,7 @@ int32_t qExplainBufAppendExecInfo(SArray *pExecInfo, char *tbuf, int32_t *len) { return TSDB_CODE_SUCCESS; } -int32_t qExplainBufAppendVerboseExecInfo(SArray *pExecInfo, char *tbuf, int32_t *len) { +static int32_t qExplainBufAppendVerboseExecInfo(SArray *pExecInfo, char *tbuf, int32_t *len) { int32_t tlen = 0; bool gotVerbose = false; int32_t nodeNum = taosArrayGetSize(pExecInfo); @@ -297,7 +297,7 @@ int32_t qExplainBufAppendVerboseExecInfo(SArray *pExecInfo, char *tbuf, int32_t return TSDB_CODE_SUCCESS; } -int32_t qExplainResAppendRow(SExplainCtx *ctx, char *tbuf, int32_t len, int32_t level) { +static int32_t qExplainResAppendRow(SExplainCtx *ctx, char *tbuf, int32_t len, int32_t level) { SQueryExplainRowInfo row = {0}; row.buf = taosMemoryMalloc(len); if (NULL == row.buf) { @@ -362,7 +362,7 @@ static char* qExplainGetScanDataLoad(STableScanPhysiNode* pScan) { return "unknown"; } -int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, int32_t level) { +static int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, int32_t level) { int32_t tlen = 0; bool isVerboseLine = false; char *tbuf = ctx->tbuf; @@ -1900,7 +1900,7 @@ int32_t qExplainResNodeToRowsImpl(SExplainResNode *pResNode, SExplainCtx *ctx, i return TSDB_CODE_SUCCESS; } -int32_t qExplainResNodeToRows(SExplainResNode *pResNode, SExplainCtx *ctx, int32_t level) { +static int32_t qExplainResNodeToRows(SExplainResNode *pResNode, SExplainCtx *ctx, int32_t level) { if (NULL == pResNode) { qError("explain res node is NULL"); QRY_ERR_RET(TSDB_CODE_APP_ERROR); @@ -1915,7 +1915,7 @@ int32_t qExplainResNodeToRows(SExplainResNode *pResNode, SExplainCtx *ctx, int32 return TSDB_CODE_SUCCESS; } -int32_t qExplainAppendGroupResRows(void *pCtx, int32_t groupId, int32_t level, bool singleChannel) { +static int32_t qExplainAppendGroupResRows(void *pCtx, int32_t groupId, int32_t level, bool singleChannel) { SExplainResNode *node = NULL; int32_t code = 0; SExplainCtx *ctx = (SExplainCtx *)pCtx; @@ -1940,7 +1940,7 @@ _return: QRY_RET(code); } -int32_t qExplainGetRspFromCtx(void *ctx, SRetrieveTableRsp **pRsp) { +static int32_t qExplainGetRspFromCtx(void *ctx, SRetrieveTableRsp **pRsp) { int32_t code = 0; SSDataBlock *pBlock = NULL; SExplainCtx *pCtx = (SExplainCtx *)ctx; @@ -1997,7 +1997,7 @@ _return: QRY_RET(code); } -int32_t qExplainPrepareCtx(SQueryPlan *pDag, SExplainCtx **pCtx) { +static int32_t qExplainPrepareCtx(SQueryPlan *pDag, SExplainCtx **pCtx) { int32_t code = 0; SNodeListNode *plans = NULL; int32_t taskNum = 0; @@ -2080,7 +2080,7 @@ _return: QRY_RET(code); } -int32_t qExplainAppendPlanRows(SExplainCtx *pCtx) { +static int32_t qExplainAppendPlanRows(SExplainCtx *pCtx) { if (EXPLAIN_MODE_ANALYZE != pCtx->mode) { return TSDB_CODE_SUCCESS; } @@ -2103,7 +2103,7 @@ int32_t qExplainAppendPlanRows(SExplainCtx *pCtx) { return TSDB_CODE_SUCCESS; } -int32_t qExplainGenerateRsp(SExplainCtx *pCtx, SRetrieveTableRsp **pRsp) { +static int32_t qExplainGenerateRsp(SExplainCtx *pCtx, SRetrieveTableRsp **pRsp) { QRY_ERR_RET(qExplainAppendGroupResRows(pCtx, pCtx->rootGroupId, 0, false)); QRY_ERR_RET(qExplainAppendPlanRows(pCtx)); QRY_ERR_RET(qExplainGetRspFromCtx(pCtx, pRsp)); diff --git a/source/libs/function/src/tudf.c b/source/libs/function/src/tudf.c index a8198a804d..0e5b3ddbdb 100644 --- a/source/libs/function/src/tudf.c +++ b/source/libs/function/src/tudf.c @@ -64,6 +64,10 @@ static void udfWatchUdfd(void *args); void udfUdfdExit(uv_process_t *process, int64_t exitStatus, int32_t termSignal) { fnInfo("udfd process exited with status %" PRId64 ", signal %d", exitStatus, termSignal); SUdfdData *pData = process->data; + if(pData == NULL) { + fnError("udfd process data is NULL"); + return; + } if (exitStatus == 0 && termSignal == 0 || atomic_load_32(&pData->stopCalled)) { fnInfo("udfd process exit due to SIGINT or dnode-mgmt called stop"); } else { diff --git a/source/libs/function/src/udfd.c b/source/libs/function/src/udfd.c index 6eef99e1f8..e3d533186d 100644 --- a/source/libs/function/src/udfd.c +++ b/source/libs/function/src/udfd.c @@ -1507,7 +1507,7 @@ static void removeListeningPipe() { int err = uv_fs_unlink(global.loop, &req, global.listenPipeName, NULL); uv_fs_req_cleanup(&req); if(err) { - fnError("remove listening pipe %s failed, reason:%s, lino:%d", global.listenPipeName, uv_strerror(err), __LINE__); + fnInfo("remove listening pipe %s : %s, lino:%d", global.listenPipeName, uv_strerror(err), __LINE__); } } From b929a735262050d545679c1fbc6d6ab50c4c81f7 Mon Sep 17 00:00:00 2001 From: Feng Chao Date: Fri, 8 Nov 2024 08:02:50 +0800 Subject: [PATCH 24/55] Update test_passwd.py --- tests/army/user/test_passwd.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/army/user/test_passwd.py b/tests/army/user/test_passwd.py index c205273973..dfec175824 100644 --- a/tests/army/user/test_passwd.py +++ b/tests/army/user/test_passwd.py @@ -1,4 +1,5 @@ import os +import platform import subprocess from frame.log import * from frame.cases import * @@ -25,6 +26,12 @@ class TDTestCase(TBase): def run(self): apiPath = self.apiPath() tdLog.info(f"api path: {apiPath}") + if platform.system().lower() == 'linux': + p = subprocess.Popen(f"cd {apiPath} && make", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + if 0 != p.returncode: + tdLog.exit("Test script passwdTest.c make failed") + p = subprocess.Popen(f"ls {apiPath}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() tdLog.info(f"test files: {out}") From a5a3d1d8ad86aa0ee43f443e98ce34359bfd0afb Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Fri, 25 Oct 2024 17:42:45 +0800 Subject: [PATCH 25/55] fix same timestamp written into same tsma res ctb --- include/common/tcommon.h | 1 + include/common/tmsgdef.h | 1 + source/dnode/mgmt/mgmt_mnode/src/mmHandle.c | 1 + source/dnode/mgmt/mgmt_vnode/src/vmHandle.c | 1 + source/dnode/mnode/impl/src/mndStb.c | 74 ++++++++++++------ source/dnode/vnode/src/inc/tq.h | 2 +- source/dnode/vnode/src/sma/smaRollup.c | 2 +- source/dnode/vnode/src/tq/tqRead.c | 6 +- source/dnode/vnode/src/tq/tqUtil.c | 4 +- source/dnode/vnode/src/vnd/vnodeSvr.c | 86 +++++++++++++++++++++ source/libs/wal/src/walRead.c | 2 +- 11 files changed, 149 insertions(+), 31 deletions(-) diff --git a/include/common/tcommon.h b/include/common/tcommon.h index ea764e6760..9572bd7aad 100644 --- a/include/common/tcommon.h +++ b/include/common/tcommon.h @@ -155,6 +155,7 @@ typedef enum EStreamType { STREAM_MID_RETRIEVE, STREAM_PARTITION_DELETE_DATA, STREAM_GET_RESULT, + STREAM_DELETE_GROUP_DATA, } EStreamType; #pragma pack(push, 1) diff --git a/include/common/tmsgdef.h b/include/common/tmsgdef.h index c22a3da5ad..c81d649284 100644 --- a/include/common/tmsgdef.h +++ b/include/common/tmsgdef.h @@ -316,6 +316,7 @@ TD_DEF_MSG_TYPE(TDMT_VND_ARB_CHECK_SYNC, "vnode-arb-check-sync", NULL, NULL) TD_DEF_MSG_TYPE(TDMT_VND_FETCH_TTL_EXPIRED_TBS, "vnode-fetch-ttl-expired-tbs", NULL, NULL) TD_DEF_MSG_TYPE(TDMT_VND_TABLE_NAME, "vnode-table-name", NULL, NULL) + TD_DEF_MSG_TYPE(TDMT_VND_DROP_TSMA_CTB, "vnode-drop-tsma-ctb", NULL, NULL) TD_CLOSE_MSG_SEG(TDMT_VND_MSG) TD_NEW_MSG_SEG(TDMT_SCH_MSG) // 3<<8 diff --git a/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c b/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c index 0d804eadf0..4b79ecf43a 100644 --- a/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c +++ b/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c @@ -182,6 +182,7 @@ SArray *mmGetMsgHandles() { if (dmSetMgmtHandle(pArray, TDMT_MND_DROP_TB_WITH_TSMA, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_FETCH_TTL_EXPIRED_TBS_RSP, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_DROP_TABLE_RSP, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; + if (dmSetMgmtHandle(pArray, TDMT_VND_DROP_TSMA_CTB_RSP, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_MND_RETRIEVE_ANAL_ALGO, mmPutMsgToReadQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_MND_RETRIEVE_IP_WHITE, mmPutMsgToReadQueue, 0) == NULL) goto _OVER; diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c index 006f44b349..a0356a6c4d 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c @@ -1014,6 +1014,7 @@ SArray *vmGetMsgHandles() { if (dmSetMgmtHandle(pArray, TDMT_VND_QUERY_COMPACT_PROGRESS, vmPutMsgToFetchQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_KILL_COMPACT, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_TABLE_NAME, vmPutMsgToFetchQueue, 0) == NULL) goto _OVER; + if (dmSetMgmtHandle(pArray, TDMT_VND_DROP_TSMA_CTB, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_STREAM_TASK_DEPLOY, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_STREAM_TASK_DROP, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index 3725d3a3fc..a52f0656dd 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -96,6 +96,7 @@ int32_t mndInitStb(SMnode *pMnode) { mndSetMsgHandle(pMnode, TDMT_MND_DROP_TB_WITH_TSMA, mndProcessDropTbWithTsma); mndSetMsgHandle(pMnode, TDMT_VND_FETCH_TTL_EXPIRED_TBS_RSP, mndProcessFetchTtlExpiredTbs); mndSetMsgHandle(pMnode, TDMT_VND_DROP_TABLE_RSP, mndTransProcessRsp); + mndSetMsgHandle(pMnode, TDMT_VND_DROP_TSMA_CTB_RSP, mndTransProcessRsp); // mndSetMsgHandle(pMnode, TDMT_MND_SYSTABLE_RETRIEVE, mndProcessRetrieveStbReq); // mndSetMsgHandle(pMnode, TDMT_MND_CREATE_INDEX, mndProcessCreateIndexReq); @@ -4088,7 +4089,8 @@ typedef struct SMDropTbTsmaInfos { typedef struct SMndDropTbsWithTsmaCtx { SHashObj *pTsmaMap; // SHashObj *pDbMap; // - SHashObj *pVgMap; // + SHashObj *pVgMap; // , only for non tsma result child table + SHashObj *pTsmaTbVgMap; // , only for tsma result child table SArray *pResTbNames; // SArray } SMndDropTbsWithTsmaCtx; @@ -4129,6 +4131,16 @@ static void mndDestroyDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx *p) { } taosHashCleanup(p->pVgMap); } + + if (p->pTsmaTbVgMap) { + void *pIter = taosHashIterate(p->pTsmaTbVgMap, NULL); + while (pIter) { + SVDropTbVgReqs *pReqs = pIter; + taosArrayDestroy(pReqs->req.pArray); + pIter = taosHashIterate(p->pTsmaTbVgMap, pIter); + } + taosHashCleanup(p->pTsmaTbVgMap); + } taosMemoryFree(p); } @@ -4154,6 +4166,12 @@ static int32_t mndInitDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx **ppCtx) { code = terrno; goto _end; } + + pCtx->pTsmaTbVgMap = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK); + if (!pCtx->pTsmaTbVgMap) { + code = terrno; + goto _end; + } *ppCtx = pCtx; _end: if (code) mndDestroyDropTbsWithTsmaCtx(pCtx); @@ -4192,16 +4210,38 @@ static void *mndBuildVDropTbsReq(SMnode *pMnode, const SVgroupInfo *pVgInfo, con } static int32_t mndSetDropTbsRedoActions(SMnode *pMnode, STrans *pTrans, const SVDropTbVgReqs *pVgReqs, void *pCont, - int32_t contLen) { + int32_t contLen, tmsg_t msgType) { STransAction action = {0}; action.epSet = pVgReqs->info.epSet; action.pCont = pCont; action.contLen = contLen; - action.msgType = TDMT_VND_DROP_TABLE; + action.msgType = msgType; action.acceptableCode = TSDB_CODE_TDB_TABLE_NOT_EXIST; return mndTransAppendRedoAction(pTrans, &action); } +static int32_t mndBuildDropTbRedoActions(SMnode* pMnode, STrans* pTrans, SHashObj* pVgMap, tmsg_t msgType) { + int32_t code = 0; + void* pIter = taosHashIterate(pVgMap, NULL); + while (pIter) { + const SVDropTbVgReqs *pVgReqs = pIter; + int32_t len = 0; + void *p = mndBuildVDropTbsReq(pMnode, &pVgReqs->info, &pVgReqs->req, &len); + if (!p) { + taosHashCancelIterate(pVgMap, pIter); + code = TSDB_CODE_MND_RETURN_VALUE_NULL; + if (terrno != 0) code = terrno; + break; + } + if ((code = mndSetDropTbsRedoActions(pMnode, pTrans, pVgReqs, p, len, msgType)) != 0) { + taosHashCancelIterate(pVgMap, pIter); + break; + } + pIter = taosHashIterate(pVgMap, pIter); + } + return code; +} + static int32_t mndCreateDropTbsTxnPrepare(SRpcMsg *pRsp, SMndDropTbsWithTsmaCtx *pCtx) { int32_t code = 0; SMnode *pMnode = pRsp->info.node; @@ -4216,23 +4256,9 @@ static int32_t mndCreateDropTbsTxnPrepare(SRpcMsg *pRsp, SMndDropTbsWithTsmaCtx TAOS_CHECK_GOTO(mndTransCheckConflict(pMnode, pTrans), NULL, _OVER); - void *pIter = taosHashIterate(pCtx->pVgMap, NULL); - while (pIter) { - const SVDropTbVgReqs *pVgReqs = pIter; - int32_t len = 0; - void *p = mndBuildVDropTbsReq(pMnode, &pVgReqs->info, &pVgReqs->req, &len); - if (!p) { - taosHashCancelIterate(pCtx->pVgMap, pIter); - code = TSDB_CODE_MND_RETURN_VALUE_NULL; - if (terrno != 0) code = terrno; - goto _OVER; - } - if ((code = mndSetDropTbsRedoActions(pMnode, pTrans, pVgReqs, p, len)) != 0) { - taosHashCancelIterate(pCtx->pVgMap, pIter); - goto _OVER; - } - pIter = taosHashIterate(pCtx->pVgMap, pIter); - } + if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; + if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TSMA_CTB)) != 0) goto _OVER; + if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; if ((code = mndTransPrepare(pMnode, pTrans)) != 0) goto _OVER; _OVER: @@ -4260,7 +4286,8 @@ static int32_t mndProcessDropTbWithTsma(SRpcMsg *pReq) { code = mndDropTbAddTsmaResTbsForSingleVg(pMnode, pCtx, pReq->pTbs, pReq->vgInfo.vgId); if (code) goto _OVER; } - if (mndCreateDropTbsTxnPrepare(pReq, pCtx) == 0) { + code = mndCreateDropTbsTxnPrepare(pReq, pCtx); + if (code == 0) { code = TSDB_CODE_ACTION_IN_PROGRESS; } _OVER: @@ -4445,7 +4472,7 @@ static int32_t mndDropTbAddTsmaResTbsForSingleVg(SMnode *pMnode, SMndDropTbsWith code = terrno; goto _end; } - TAOS_CHECK_GOTO(mndDropTbAdd(pMnode, pCtx->pVgMap, pVgInfo, p, pInfo->suid, true), NULL, _end); + TAOS_CHECK_GOTO(mndDropTbAdd(pMnode, pCtx->pTsmaTbVgMap, pVgInfo, p, pInfo->suid, true), NULL, _end); } } _end: @@ -4476,7 +4503,8 @@ static int32_t mndProcessFetchTtlExpiredTbs(SRpcMsg *pRsp) { code = mndDropTbAddTsmaResTbsForSingleVg(pMnode, pCtx, rsp.pExpiredTbs, rsp.vgId); if (code) goto _end; - if (mndCreateDropTbsTxnPrepare(pRsp, pCtx) == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; + code = mndCreateDropTbsTxnPrepare(pRsp, pCtx); + if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; _end: if (pCtx) mndDestroyDropTbsWithTsmaCtx(pCtx); tDecoderClear(&decoder); diff --git a/source/dnode/vnode/src/inc/tq.h b/source/dnode/vnode/src/inc/tq.h index 653b47ff14..bdb94d4a6e 100644 --- a/source/dnode/vnode/src/inc/tq.h +++ b/source/dnode/vnode/src/inc/tq.h @@ -146,7 +146,7 @@ int32_t tqBuildFName(char** data, const char* path, char* name); int32_t tqOffsetRestoreFromFile(STQ* pTq, char* name); // tq util -int32_t tqExtractDelDataBlock(const void* pData, int32_t len, int64_t ver, void** pRefBlock, int32_t type); +int32_t tqExtractDelDataBlock(const void* pData, int32_t len, int64_t ver, void** pRefBlock, int32_t type, EStreamType blockType); int32_t tqExtractDataForMq(STQ* pTq, STqHandle* pHandle, const SMqPollReq* pRequest, SRpcMsg* pMsg); int32_t tqDoSendDataRsp(const SRpcHandleInfo* pRpcHandleInfo, const SMqDataRsp* pRsp, int32_t epoch, int64_t consumerId, int32_t type, int64_t sver, int64_t ever); diff --git a/source/dnode/vnode/src/sma/smaRollup.c b/source/dnode/vnode/src/sma/smaRollup.c index 80c04a3276..bbc58004d9 100644 --- a/source/dnode/vnode/src/sma/smaRollup.c +++ b/source/dnode/vnode/src/sma/smaRollup.c @@ -1551,7 +1551,7 @@ static int32_t tdRSmaBatchExec(SSma *pSma, SRSmaInfo *pInfo, STaosQall *qall, SA _resume_delete: version = RSMA_EXEC_MSG_VER(msg); if ((code = tqExtractDelDataBlock(RSMA_EXEC_MSG_BODY(msg), RSMA_EXEC_MSG_LEN(msg), version, - &packData.pDataBlock, 1))) { + &packData.pDataBlock, 1, STREAM_DELETE_DATA))) { taosFreeQitem(msg); TAOS_CHECK_EXIT(code); } diff --git a/source/dnode/vnode/src/tq/tqRead.c b/source/dnode/vnode/src/tq/tqRead.c index 95955e579f..c8c34b10a4 100644 --- a/source/dnode/vnode/src/tq/tqRead.c +++ b/source/dnode/vnode/src/tq/tqRead.c @@ -363,11 +363,11 @@ int32_t extractMsgFromWal(SWalReader* pReader, void** pItem, int64_t maxVer, con tqError("%s failed to create data submit for stream since out of memory", id); return code; } - } else if (pCont->msgType == TDMT_VND_DELETE) { + } else if (pCont->msgType == TDMT_VND_DELETE || pCont->msgType == TDMT_VND_DROP_TSMA_CTB) { void* pBody = POINTER_SHIFT(pCont->body, sizeof(SMsgHead)); int32_t len = pCont->bodyLen - sizeof(SMsgHead); - - code = tqExtractDelDataBlock(pBody, len, ver, (void**)pItem, 0); + EStreamType blockType = pCont->msgType == TDMT_VND_DELETE ? STREAM_DELETE_DATA : STREAM_DELETE_GROUP_DATA; + code = tqExtractDelDataBlock(pBody, len, ver, (void**)pItem, 0, blockType); if (code == TSDB_CODE_SUCCESS) { if (*pItem == NULL) { tqDebug("s-task:%s empty delete msg, discard it, len:%d, ver:%" PRId64, id, len, ver); diff --git a/source/dnode/vnode/src/tq/tqUtil.c b/source/dnode/vnode/src/tq/tqUtil.c index e066938fc0..54b063d692 100644 --- a/source/dnode/vnode/src/tq/tqUtil.c +++ b/source/dnode/vnode/src/tq/tqUtil.c @@ -572,7 +572,7 @@ int32_t tqDoSendDataRsp(const SRpcHandleInfo* pRpcHandleInfo, const SMqDataRsp* return 0; } -int32_t tqExtractDelDataBlock(const void* pData, int32_t len, int64_t ver, void** pRefBlock, int32_t type) { +int32_t tqExtractDelDataBlock(const void* pData, int32_t len, int64_t ver, void** pRefBlock, int32_t type, EStreamType blockType) { int32_t code = 0; int32_t line = 0; SDecoder* pCoder = &(SDecoder){0}; @@ -593,7 +593,7 @@ int32_t tqExtractDelDataBlock(const void* pData, int32_t len, int64_t ver, void* } SSDataBlock* pDelBlock = NULL; - code = createSpecialDataBlock(STREAM_DELETE_DATA, &pDelBlock); + code = createSpecialDataBlock(blockType, &pDelBlock); TSDB_CHECK_CODE(code, line, END); code = blockDataEnsureCapacity(pDelBlock, numOfTables); diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index dd13c975cf..1ea2aac809 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -50,6 +50,8 @@ static int32_t vnodeProcessDropIndexReq(SVnode *pVnode, int64_t ver, void *pReq, static int32_t vnodeProcessCompactVnodeReq(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, SRpcMsg *pRsp); static int32_t vnodeProcessConfigChangeReq(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, SRpcMsg *pRsp); static int32_t vnodeProcessArbCheckSyncReq(SVnode *pVnode, void *pReq, int32_t len, SRpcMsg *pRsp); +static int32_t vnodeProcessDropTSmaCtbReq(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, SRpcMsg *pRsp, + SRpcMsg *pOriginRpc); static int32_t vnodePreCheckAssignedLogSyncd(SVnode *pVnode, char *member0Token, char *member1Token); static int32_t vnodeCheckAssignedLogSyncd(SVnode *pVnode, char *member0Token, char *member1Token); @@ -481,6 +483,75 @@ static int32_t vnodePreProcessArbCheckSyncMsg(SVnode *pVnode, SRpcMsg *pMsg) { return code; } +static int32_t vnodePreProcessDropTSmaCtbMsg(SVnode *pVnode, SRpcMsg *pMsg) { + SVDropTbBatchReq dropReq = {0}; + int32_t code = 0; + int32_t lino = 0; + SDecoder dc = {0}; + SEncoder ec = {0}; + int32_t nTbs = 0; + SDeleteRes res = {0}; + int32_t size = 0; + uint8_t *pCont = NULL; + tDecoderInit(&dc, (uint8_t *)pMsg->pCont + sizeof(SMsgHead), pMsg->contLen - sizeof(SMsgHead)); + if (tDecodeSVDropTbBatchReq(&dc, &dropReq) < 0) { + code = TSDB_CODE_INVALID_MSG; + TSDB_CHECK_CODE(code, lino, _exit); + } + nTbs = dropReq.nReqs; + res.skey = INT64_MIN; + res.ekey = INT64_MAX; + res.affectedRows = 1; + res.uidList = taosArrayInit(nTbs, sizeof(tb_uid_t)); + if (!res.uidList) { + code = terrno; + TSDB_CHECK_CODE(code, lino, _exit); + } + + vDebug("vnode preprocess drop tsma ctb, vgId:%d tb num: %d", TD_VID(pVnode), nTbs); + for (int32_t i = 0; i < nTbs; ++i) { + SVDeleteRsp rsp = {.affectedRows = 1}; + tb_uid_t uid = metaGetTableEntryUidByName(pVnode->pMeta, dropReq.pReqs[i].name); + if (uid == 0) { + vWarn("vgId:%d, drop tsma ctb:%s not found", TD_VID(pVnode), dropReq.pReqs[i].name); + continue; + } + if (NULL == taosArrayPush(res.uidList, &uid)) { + code = terrno; + TSDB_CHECK_CODE(code, lino, _exit); + } + } + + tEncodeSize(tEncodeDeleteRes, &res, size, code); + pCont = rpcMallocCont(size + sizeof(SMsgHead)); + if (!pCont) { + code = terrno; + TSDB_CHECK_CODE(code, lino, _exit); + } + ((SMsgHead *)pCont)->contLen = size + sizeof(SMsgHead); + ((SMsgHead *)pCont)->vgId = TD_VID(pVnode); + + tEncoderInit(&ec, pCont + sizeof(SMsgHead), size); + code = tEncodeDeleteRes(&ec, &res); + tEncoderClear(&ec); + if (code != 0) { + vError("vgId:%d %s failed to encode delete response", TD_VID(pVnode), __func__); + TSDB_CHECK_CODE(code, lino, _exit); + } + rpcFreeCont(pMsg->pCont); + pMsg->pCont = pCont; + pCont = NULL; + pMsg->contLen = size + sizeof(SMsgHead); + +_exit: + if (res.uidList) { + taosArrayDestroy(res.uidList); + } + tDecoderClear(&dc); + rpcFreeCont(pCont); + return code; +} + int32_t vnodePreProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg) { int32_t code = 0; @@ -507,6 +578,9 @@ int32_t vnodePreProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg) { case TDMT_VND_ARB_CHECK_SYNC: { code = vnodePreProcessArbCheckSyncMsg(pVnode, pMsg); } break; + case TDMT_VND_DROP_TSMA_CTB: { + code = vnodePreProcessDropTSmaCtbMsg(pVnode, pMsg); + } break; default: break; } @@ -711,6 +785,11 @@ int32_t vnodeProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg, int64_t ver, SRpcMsg case TDMT_VND_ARB_CHECK_SYNC: vnodeProcessArbCheckSyncReq(pVnode, pReq, len, pRsp); break; + case TDMT_VND_DROP_TSMA_CTB: + if (vnodeProcessDropTSmaCtbReq(pVnode, ver, pReq, len, pRsp, pMsg) < 0) { + goto _err; + } + break; default: vError("vgId:%d, unprocessed msg, %d", TD_VID(pVnode), pMsg->msgType); return TSDB_CODE_INVALID_MSG; @@ -2512,3 +2591,10 @@ _OVER: int32_t vnodeAsyncCompact(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, SRpcMsg *pRsp) { return 0; } int32_t tsdbAsyncCompact(STsdb *tsdb, const STimeWindow *tw, bool sync) { return 0; } #endif + +static int32_t vnodeProcessDropTSmaCtbReq(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, SRpcMsg *pRsp, + SRpcMsg *pOriginalMsg) { + pRsp->msgType = TDMT_VND_DROP_TSMA_CTB_RSP; + pRsp->code = TSDB_CODE_SUCCESS; + return pRsp->code; +} diff --git a/source/libs/wal/src/walRead.c b/source/libs/wal/src/walRead.c index 610adfb0e1..9a3ea34eff 100644 --- a/source/libs/wal/src/walRead.c +++ b/source/libs/wal/src/walRead.c @@ -87,7 +87,7 @@ int32_t walNextValidMsg(SWalReader *pReader) { int32_t type = pReader->pHead->head.msgType; if (type == TDMT_VND_SUBMIT || ((type == TDMT_VND_DELETE) && (pReader->cond.deleteMsg == 1)) || - (IS_META_MSG(type) && pReader->cond.scanMeta)) { + (type == TDMT_VND_DROP_TSMA_CTB) || (IS_META_MSG(type) && pReader->cond.scanMeta)) { TAOS_RETURN(walFetchBody(pReader)); } else { TAOS_CHECK_RETURN(walSkipFetchBody(pReader)); From f452ca47fe4919e2aaed6b4dc1c2df7569d55ebe Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Sun, 27 Oct 2024 15:42:14 +0800 Subject: [PATCH 26/55] delete window state when drop table --- include/libs/executor/storageapi.h | 1 + include/libs/stream/streamState.h | 1 + source/dnode/snode/src/snodeInitApi.c | 1 + source/dnode/vnode/src/vnd/vnodeInitApi.c | 1 + source/libs/executor/src/scanoperator.c | 26 +++++++++++++++++++ source/libs/stream/inc/streamBackendRocksdb.h | 1 + source/libs/stream/src/streamBackendRocksdb.c | 6 +++++ source/libs/stream/src/streamState.c | 8 ++++++ 8 files changed, 45 insertions(+) diff --git a/include/libs/executor/storageapi.h b/include/libs/executor/storageapi.h index db0d6339c8..feb7bcc25e 100644 --- a/include/libs/executor/storageapi.h +++ b/include/libs/executor/storageapi.h @@ -336,6 +336,7 @@ typedef struct SStateStore { int32_t (*streamStatePutParName)(SStreamState* pState, int64_t groupId, const char* tbname); int32_t (*streamStateGetParName)(SStreamState* pState, int64_t groupId, void** pVal, bool onlyCache, int32_t* pWinCode); + int32_t (*streamStateDeleteParName)(SStreamState* pState, int64_t groupId); int32_t (*streamStateAddIfNotExist)(SStreamState* pState, const SWinKey* key, void** pVal, int32_t* pVLen, int32_t* pWinCode); diff --git a/include/libs/stream/streamState.h b/include/libs/stream/streamState.h index a50451c3eb..2179547352 100644 --- a/include/libs/stream/streamState.h +++ b/include/libs/stream/streamState.h @@ -116,6 +116,7 @@ void streamStateCurPrev(SStreamState* pState, SStreamStateCur* pCur); int32_t streamStatePutParName(SStreamState* pState, int64_t groupId, const char* tbname); int32_t streamStateGetParName(SStreamState* pState, int64_t groupId, void** pVal, bool onlyCache, int32_t* pWinCode); +int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId); // group id int32_t streamStateGroupPut(SStreamState* pState, int64_t groupId, void* value, int32_t vLen); diff --git a/source/dnode/snode/src/snodeInitApi.c b/source/dnode/snode/src/snodeInitApi.c index 680a2fd83c..4fe4333534 100644 --- a/source/dnode/snode/src/snodeInitApi.c +++ b/source/dnode/snode/src/snodeInitApi.c @@ -31,6 +31,7 @@ void initStateStoreAPI(SStateStore* pStore) { pStore->streamStatePutParName = streamStatePutParName; pStore->streamStateGetParName = streamStateGetParName; + pStore->streamStateDeleteParName = streamStateDeleteParName; pStore->streamStateAddIfNotExist = streamStateAddIfNotExist; pStore->streamStateReleaseBuf = streamStateReleaseBuf; diff --git a/source/dnode/vnode/src/vnd/vnodeInitApi.c b/source/dnode/vnode/src/vnd/vnodeInitApi.c index d688d1323d..0ac0ee1b8f 100644 --- a/source/dnode/vnode/src/vnd/vnodeInitApi.c +++ b/source/dnode/vnode/src/vnd/vnodeInitApi.c @@ -147,6 +147,7 @@ void initStateStoreAPI(SStateStore* pStore) { pStore->streamStatePutParName = streamStatePutParName; pStore->streamStateGetParName = streamStateGetParName; + pStore->streamStateDeleteParName = streamStateDeleteParName; pStore->streamStateAddIfNotExist = streamStateAddIfNotExist; pStore->streamStateReleaseBuf = streamStateReleaseBuf; diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 5b5d5c5d11..8c04492162 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3535,6 +3535,23 @@ static int32_t copyGetResultBlock(SSDataBlock* dest, TSKEY start, TSKEY end) { return appendDataToSpecialBlock(dest, &start, &end, NULL, NULL, NULL); } +static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock) { + int32_t code = TSDB_CODE_SUCCESS; + int32_t lino = 0; + for (int32_t i = 0; i < pBlock->info.rows; i++) { + SColumnInfoData* pGpIdCol = taosArrayGet(pBlock->pDataBlock, GROUPID_COLUMN_INDEX); + int64_t* gpIdCol = (int64_t*)pGpIdCol->pData; + code = pInfo->stateStore.streamStateDeleteParName(pInfo->pStreamScanOp->pTaskInfo->streamInfo.pState, gpIdCol[i]); + QUERY_CHECK_CODE(code, lino, _end); + } + +_end: + if (code != TSDB_CODE_SUCCESS) { + qError("%s failed at line %d since %s", __func__, lino, tstrerror(code)); + } + return code; +} + static int32_t doStreamScanNext(SOperatorInfo* pOperator, SSDataBlock** ppRes) { // NOTE: this operator does never check if current status is done or not int32_t code = TSDB_CODE_SUCCESS; @@ -3774,6 +3791,15 @@ FETCH_NEXT_BLOCK: prepareRangeScan(pInfo, pInfo->pUpdateRes, &pInfo->updateResIndex, NULL); pInfo->scanMode = STREAM_SCAN_FROM_DATAREADER_RANGE; } break; + case STREAM_DELETE_GROUP_DATA: { + printSpecDataBlock(pBlock, getStreamOpName(pOperator->operatorType), "delete group recv", + GET_TASKID(pTaskInfo)); + code = setBlockGroupIdByUid(pInfo, pBlock); + QUERY_CHECK_CODE(code, lino, _end); + + deletePartName(pInfo, pBlock); + pBlock->info.type = STREAM_DELETE_DATA; + } break; case STREAM_CHECKPOINT: { qError("stream check point error. msg type: STREAM_INPUT__DATA_BLOCK"); } break; diff --git a/source/libs/stream/inc/streamBackendRocksdb.h b/source/libs/stream/inc/streamBackendRocksdb.h index d313acc61d..6a10b21c53 100644 --- a/source/libs/stream/inc/streamBackendRocksdb.h +++ b/source/libs/stream/inc/streamBackendRocksdb.h @@ -223,6 +223,7 @@ int32_t streamStateParTagGetKVByCur_rocksdb(SStreamStateCur* pCur, int64_t* pGro // parname cf int32_t streamStatePutParName_rocksdb(SStreamState* pState, int64_t groupId, const char tbname[TSDB_TABLE_NAME_LEN]); int32_t streamStateGetParName_rocksdb(SStreamState* pState, int64_t groupId, void** pVal); +int32_t streamStateDeleteParName_rocksdb(SStreamState* pState, int64_t groupId); void streamStateDestroy_rocksdb(SStreamState* pState, bool remove); diff --git a/source/libs/stream/src/streamBackendRocksdb.c b/source/libs/stream/src/streamBackendRocksdb.c index 09f4e95376..65746b3100 100644 --- a/source/libs/stream/src/streamBackendRocksdb.c +++ b/source/libs/stream/src/streamBackendRocksdb.c @@ -4432,6 +4432,12 @@ int32_t streamStateGetParName_rocksdb(SStreamState* pState, int64_t groupId, voi return code; } +int32_t streamStateDeleteParName_rocksdb(SStreamState* pState, int64_t groupId) { + int code = 0; + STREAM_STATE_DEL_ROCKSDB(pState, "parname", &groupId); + return code; +} + int32_t streamDefaultPut_rocksdb(SStreamState* pState, const void* key, void* pVal, int32_t pVLen) { int code = 0; STREAM_STATE_PUT_ROCKSDB(pState, "default", key, pVal, pVLen); diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 794fc346bf..ff52e1cb54 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -525,6 +525,14 @@ _end: return code; } +int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId) { + int32_t code = tSimpleHashRemove(pState->parNameMap, &groupId, sizeof(int64_t)); + qTrace("%s at line %d res %d", __func__, __LINE__, code); + code = streamStateDeleteParName_rocksdb(pState, groupId); + qTrace("%s at line %d res %d", __func__, __LINE__, code); + return TSDB_CODE_SUCCESS; +} + void streamStateDestroy(SStreamState* pState, bool remove) { streamFileStateDestroy(pState->pFileState); // streamStateDestroy_rocksdb(pState, remove); From 7f501130a26fafc121de70315ed34d27668211e3 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Sun, 27 Oct 2024 18:27:40 +0800 Subject: [PATCH 27/55] client add query fisrt/last/count for drop table --- source/libs/parser/inc/parInt.h | 2 + source/libs/parser/src/parTranslater.c | 72 +++++++++++++++++++++++++- source/libs/parser/src/parser.c | 4 ++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/source/libs/parser/inc/parInt.h b/source/libs/parser/inc/parInt.h index 5999ada70f..f69896dce9 100644 --- a/source/libs/parser/inc/parInt.h +++ b/source/libs/parser/inc/parInt.h @@ -45,6 +45,8 @@ int32_t buildQueryAfterParse(SQuery** pQuery, SNode* pRootNode, int16_t placehol int32_t translateTable(STranslateContext* pCxt, SNode** pTable, SNode* pJoinParent); int32_t getMetaDataFromHash(const char* pKey, int32_t len, SHashObj* pHash, void** pOutput); void tfreeSParseQueryRes(void* p); +int32_t translatePostDropCTbWithTsma(SParseContext* pCxt, SQuery* pQuery, SSDataBlock* pBlock); + #ifdef TD_ENTERPRISE int32_t translateView(STranslateContext* pCxt, SNode** pTable, SName* pName); diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 99c03c412c..01ed634110 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -9238,10 +9238,76 @@ static int32_t doTranslateDropSuperTable(STranslateContext* pCxt, const SName* p return code; } +static int32_t createFunctionForDropCtbWithTSMA(const char* pFuncName, SFunctionNode** ppFunc) { + int32_t code = TSDB_CODE_SUCCESS; + SFunctionNode* pFunc = NULL; + code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc); + SColumnNode* pCol = NULL; + if (TSDB_CODE_SUCCESS == code) { + strcpy(pFunc->functionName, pFuncName); + code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol); + } + if (TSDB_CODE_SUCCESS == code) { + strcpy(((SColumnNode*)pCol)->colName, ROWTS_PSEUDO_COLUMN_NAME); + pCol->colId = PRIMARYKEY_TIMESTAMP_COL_ID; + pCol->isPrimTs = true; + code = nodesListMakeStrictAppend(&pFunc->pParameterList, (SNode*)pCol); + } + if (TSDB_CODE_SUCCESS == code) { + *ppFunc = pFunc; + } else { + nodesDestroyNode((SNode*)pFunc); + } + return code; +} + +static int32_t translateDropCtbWithTsma(STranslateContext* pCxt, SDropTableStmt* pStmt) { + if (!pStmt->withTsma || LIST_LENGTH(pStmt->pTables) == 0) return TSDB_CODE_SUCCESS; + int32_t code = TSDB_CODE_SUCCESS; + SNode* pPrevQuery = NULL; + SNodeList* pProjectionList = NULL; + if (LIST_LENGTH(pStmt->pTables) > 1) return TSDB_CODE_FAILED; + SDropTableClause* pClause = (SDropTableClause*)nodesListGetNode(pStmt->pTables, 0); + + // create select query stmt + code = nodesMakeList(&pProjectionList); + SFunctionNode* pFunc = NULL; + if (TSDB_CODE_SUCCESS == code) { + code = createFunctionForDropCtbWithTSMA("count", &pFunc); + if (TSDB_CODE_SUCCESS == code) { + code = nodesListMakeStrictAppend(&pProjectionList, (SNode*)pFunc); + } + } + if (TSDB_CODE_SUCCESS == code) { + code = createFunctionForDropCtbWithTSMA("first", (SFunctionNode**)&pFunc); + if (TSDB_CODE_SUCCESS == code) { + code = nodesListMakeStrictAppend(&pProjectionList, (SNode*)pFunc); + } + } + + if (TSDB_CODE_SUCCESS == code) { + code = createFunctionForDropCtbWithTSMA("last", (SFunctionNode**)&pFunc); + if (TSDB_CODE_SUCCESS == code) { + code = nodesListMakeStrictAppend(&pProjectionList, (SNode*)pFunc); + } + } + if (TSDB_CODE_SUCCESS == code) { + code = createSimpleSelectStmtFromProjList(pClause->dbName, pClause->tableName, pProjectionList, + (SSelectStmt**)&pPrevQuery); + } + if (TSDB_CODE_SUCCESS != code) { + nodesDestroyList(pProjectionList); + } else { + TSWAP(pCxt->pPrevRoot, pPrevQuery); + } + return code; +} + static int32_t translateDropTable(STranslateContext* pCxt, SDropTableStmt* pStmt) { + if (pStmt->withTsma) return translateDropCtbWithTsma(pCxt, pStmt); + SDropTableClause* pClause = (SDropTableClause*)nodesListGetNode(pStmt->pTables, 0); SName tableName = {0}; - if (pStmt->withTsma) return TSDB_CODE_SUCCESS; toName(pCxt->pParseCxt->acctId, pClause->dbName, pClause->tableName, &tableName); return doTranslateDropSuperTable(pCxt, &tableName, pClause->ignoreNotExists); } @@ -12747,6 +12813,10 @@ int32_t translatePostCreateTSMA(SParseContext* pParseCxt, SQuery* pQuery, SSData return code; } +int32_t translatePostDropCTbWithTsma(SParseContext* pCxt, SQuery* pQuery, SSDataBlock* pBlock) { + +} + static int32_t translateDropTSMA(STranslateContext* pCxt, SDropTSMAStmt* pStmt) { int32_t code = TSDB_CODE_SUCCESS; SMDropSmaReq dropReq = {0}; diff --git a/source/libs/parser/src/parser.c b/source/libs/parser/src/parser.c index 8ac1acb1a2..0c27e5a603 100644 --- a/source/libs/parser/src/parser.c +++ b/source/libs/parser/src/parser.c @@ -324,6 +324,10 @@ int32_t qContinueParsePostQuery(SParseContext* pCxt, SQuery* pQuery, SSDataBlock code = translatePostCreateTSMA(pCxt, pQuery, pBlock); break; } + case QUERY_NODE_DROP_TABLE_STMT: { + translatePostDropCTbWithTsma(pCxt, pQuery, pBlock); + break; + } default: break; } From 2b924c68fd95adf583aec76d8acf78b0a37968c7 Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Mon, 28 Oct 2024 18:08:18 +0800 Subject: [PATCH 28/55] ignore delete result --- source/libs/executor/src/scanoperator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 8c04492162..12ec7d4201 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3798,7 +3798,7 @@ FETCH_NEXT_BLOCK: QUERY_CHECK_CODE(code, lino, _end); deletePartName(pInfo, pBlock); - pBlock->info.type = STREAM_DELETE_DATA; + goto FETCH_NEXT_BLOCK; } break; case STREAM_CHECKPOINT: { qError("stream check point error. msg type: STREAM_INPUT__DATA_BLOCK"); From 5955d121b390bb28c9c458955575ea683f2ccf04 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Mon, 28 Oct 2024 18:07:46 +0800 Subject: [PATCH 29/55] revert first/last fetch --- source/libs/parser/inc/parInt.h | 2 - source/libs/parser/src/parTranslater.c | 72 +------------------------- source/libs/parser/src/parser.c | 4 -- 3 files changed, 1 insertion(+), 77 deletions(-) diff --git a/source/libs/parser/inc/parInt.h b/source/libs/parser/inc/parInt.h index f69896dce9..5999ada70f 100644 --- a/source/libs/parser/inc/parInt.h +++ b/source/libs/parser/inc/parInt.h @@ -45,8 +45,6 @@ int32_t buildQueryAfterParse(SQuery** pQuery, SNode* pRootNode, int16_t placehol int32_t translateTable(STranslateContext* pCxt, SNode** pTable, SNode* pJoinParent); int32_t getMetaDataFromHash(const char* pKey, int32_t len, SHashObj* pHash, void** pOutput); void tfreeSParseQueryRes(void* p); -int32_t translatePostDropCTbWithTsma(SParseContext* pCxt, SQuery* pQuery, SSDataBlock* pBlock); - #ifdef TD_ENTERPRISE int32_t translateView(STranslateContext* pCxt, SNode** pTable, SName* pName); diff --git a/source/libs/parser/src/parTranslater.c b/source/libs/parser/src/parTranslater.c index 01ed634110..99c03c412c 100755 --- a/source/libs/parser/src/parTranslater.c +++ b/source/libs/parser/src/parTranslater.c @@ -9238,76 +9238,10 @@ static int32_t doTranslateDropSuperTable(STranslateContext* pCxt, const SName* p return code; } -static int32_t createFunctionForDropCtbWithTSMA(const char* pFuncName, SFunctionNode** ppFunc) { - int32_t code = TSDB_CODE_SUCCESS; - SFunctionNode* pFunc = NULL; - code = nodesMakeNode(QUERY_NODE_FUNCTION, (SNode**)&pFunc); - SColumnNode* pCol = NULL; - if (TSDB_CODE_SUCCESS == code) { - strcpy(pFunc->functionName, pFuncName); - code = nodesMakeNode(QUERY_NODE_COLUMN, (SNode**)&pCol); - } - if (TSDB_CODE_SUCCESS == code) { - strcpy(((SColumnNode*)pCol)->colName, ROWTS_PSEUDO_COLUMN_NAME); - pCol->colId = PRIMARYKEY_TIMESTAMP_COL_ID; - pCol->isPrimTs = true; - code = nodesListMakeStrictAppend(&pFunc->pParameterList, (SNode*)pCol); - } - if (TSDB_CODE_SUCCESS == code) { - *ppFunc = pFunc; - } else { - nodesDestroyNode((SNode*)pFunc); - } - return code; -} - -static int32_t translateDropCtbWithTsma(STranslateContext* pCxt, SDropTableStmt* pStmt) { - if (!pStmt->withTsma || LIST_LENGTH(pStmt->pTables) == 0) return TSDB_CODE_SUCCESS; - int32_t code = TSDB_CODE_SUCCESS; - SNode* pPrevQuery = NULL; - SNodeList* pProjectionList = NULL; - if (LIST_LENGTH(pStmt->pTables) > 1) return TSDB_CODE_FAILED; - SDropTableClause* pClause = (SDropTableClause*)nodesListGetNode(pStmt->pTables, 0); - - // create select query stmt - code = nodesMakeList(&pProjectionList); - SFunctionNode* pFunc = NULL; - if (TSDB_CODE_SUCCESS == code) { - code = createFunctionForDropCtbWithTSMA("count", &pFunc); - if (TSDB_CODE_SUCCESS == code) { - code = nodesListMakeStrictAppend(&pProjectionList, (SNode*)pFunc); - } - } - if (TSDB_CODE_SUCCESS == code) { - code = createFunctionForDropCtbWithTSMA("first", (SFunctionNode**)&pFunc); - if (TSDB_CODE_SUCCESS == code) { - code = nodesListMakeStrictAppend(&pProjectionList, (SNode*)pFunc); - } - } - - if (TSDB_CODE_SUCCESS == code) { - code = createFunctionForDropCtbWithTSMA("last", (SFunctionNode**)&pFunc); - if (TSDB_CODE_SUCCESS == code) { - code = nodesListMakeStrictAppend(&pProjectionList, (SNode*)pFunc); - } - } - if (TSDB_CODE_SUCCESS == code) { - code = createSimpleSelectStmtFromProjList(pClause->dbName, pClause->tableName, pProjectionList, - (SSelectStmt**)&pPrevQuery); - } - if (TSDB_CODE_SUCCESS != code) { - nodesDestroyList(pProjectionList); - } else { - TSWAP(pCxt->pPrevRoot, pPrevQuery); - } - return code; -} - static int32_t translateDropTable(STranslateContext* pCxt, SDropTableStmt* pStmt) { - if (pStmt->withTsma) return translateDropCtbWithTsma(pCxt, pStmt); - SDropTableClause* pClause = (SDropTableClause*)nodesListGetNode(pStmt->pTables, 0); SName tableName = {0}; + if (pStmt->withTsma) return TSDB_CODE_SUCCESS; toName(pCxt->pParseCxt->acctId, pClause->dbName, pClause->tableName, &tableName); return doTranslateDropSuperTable(pCxt, &tableName, pClause->ignoreNotExists); } @@ -12813,10 +12747,6 @@ int32_t translatePostCreateTSMA(SParseContext* pParseCxt, SQuery* pQuery, SSData return code; } -int32_t translatePostDropCTbWithTsma(SParseContext* pCxt, SQuery* pQuery, SSDataBlock* pBlock) { - -} - static int32_t translateDropTSMA(STranslateContext* pCxt, SDropTSMAStmt* pStmt) { int32_t code = TSDB_CODE_SUCCESS; SMDropSmaReq dropReq = {0}; diff --git a/source/libs/parser/src/parser.c b/source/libs/parser/src/parser.c index 0c27e5a603..8ac1acb1a2 100644 --- a/source/libs/parser/src/parser.c +++ b/source/libs/parser/src/parser.c @@ -324,10 +324,6 @@ int32_t qContinueParsePostQuery(SParseContext* pCxt, SQuery* pQuery, SSDataBlock code = translatePostCreateTSMA(pCxt, pQuery, pBlock); break; } - case QUERY_NODE_DROP_TABLE_STMT: { - translatePostDropCTbWithTsma(pCxt, pQuery, pBlock); - break; - } default: break; } From 9449c8bdc5a4bf30890255781fd79942be71bc25 Mon Sep 17 00:00:00 2001 From: 54liuyao <54liuyao@163.com> Date: Mon, 28 Oct 2024 18:10:32 +0800 Subject: [PATCH 30/55] adj log --- source/libs/stream/src/streamState.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index ff52e1cb54..26c97cbb6e 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -527,9 +527,9 @@ _end: int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId) { int32_t code = tSimpleHashRemove(pState->parNameMap, &groupId, sizeof(int64_t)); - qTrace("%s at line %d res %d", __func__, __LINE__, code); + qTrace("catche %s at line %d res %d", __func__, __LINE__, code); code = streamStateDeleteParName_rocksdb(pState, groupId); - qTrace("%s at line %d res %d", __func__, __LINE__, code); + qTrace("disk %s at line %d res %d", __func__, __LINE__, code); return TSDB_CODE_SUCCESS; } From 413cf3e504c6e1d22d3bc3982df47056ea11c4f5 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Tue, 29 Oct 2024 11:30:04 +0800 Subject: [PATCH 31/55] sink submit data one-by-one --- source/dnode/mnode/impl/src/mndStb.c | 2 +- source/dnode/vnode/src/tq/tqSink.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index a52f0656dd..6efc58119b 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -4256,8 +4256,8 @@ static int32_t mndCreateDropTbsTxnPrepare(SRpcMsg *pRsp, SMndDropTbsWithTsmaCtx TAOS_CHECK_GOTO(mndTransCheckConflict(pMnode, pTrans), NULL, _OVER); + if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TSMA_CTB)) != 0) goto _OVER; if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; - if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TSMA_CTB)) != 0) goto _OVER; if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; if ((code = mndTransPrepare(pMnode, pTrans)) != 0) goto _OVER; diff --git a/source/dnode/vnode/src/tq/tqSink.c b/source/dnode/vnode/src/tq/tqSink.c index be41f7e99e..e972c928f0 100644 --- a/source/dnode/vnode/src/tq/tqSink.c +++ b/source/dnode/vnode/src/tq/tqSink.c @@ -1032,7 +1032,7 @@ void tqSinkDataIntoDstTable(SStreamTask* pTask, void* vnode, void* data) { } bool onlySubmitData = hasOnlySubmitData(pBlocks, numOfBlocks); - if (!onlySubmitData) { + if (!onlySubmitData || pTask->subtableWithoutMd5 == 1) { tqDebug("vgId:%d, s-task:%s write %d stream resBlock(s) into table, has delete block, submit one-by-one", vgId, id, numOfBlocks); From fc39ea70ac8a94bec32f13cc6a2ec09939c02b6c Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Tue, 29 Oct 2024 17:43:43 +0800 Subject: [PATCH 32/55] fix tsma stream same tbname with same group id --- source/libs/executor/src/executorInt.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/source/libs/executor/src/executorInt.c b/source/libs/executor/src/executorInt.c index 1b823bf69d..af8e01be5e 100644 --- a/source/libs/executor/src/executorInt.c +++ b/source/libs/executor/src/executorInt.c @@ -1083,18 +1083,13 @@ void cleanupBasicInfo(SOptrBasicInfo* pInfo) { bool groupbyTbname(SNodeList* pGroupList) { bool bytbname = false; - if (LIST_LENGTH(pGroupList) == 1) { - SNode* p = nodesListGetNode(pGroupList, 0); - if (!p) { - qError("%s failed at line %d since %s", __func__, __LINE__, tstrerror(terrno)); - return false; - } - if (p->type == QUERY_NODE_FUNCTION) { - // partition by tbname/group by tbname - bytbname = (strcmp(((struct SFunctionNode*)p)->functionName, "tbname") == 0); + SNode*pNode = NULL; + FOREACH(pNode, pGroupList) { + if (pNode->type == QUERY_NODE_FUNCTION) { + bytbname = (strcmp(((struct SFunctionNode*)pNode)->functionName, "tbname") == 0); + break; } } - return bytbname; } From f2860b766a4c42677c0b28e620cf1b63936cc0d9 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Wed, 30 Oct 2024 11:14:27 +0800 Subject: [PATCH 33/55] add ret check for deletePartName --- source/libs/executor/src/scanoperator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 12ec7d4201..0cdb0e7954 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3797,7 +3797,8 @@ FETCH_NEXT_BLOCK: code = setBlockGroupIdByUid(pInfo, pBlock); QUERY_CHECK_CODE(code, lino, _end); - deletePartName(pInfo, pBlock); + code = deletePartName(pInfo, pBlock); + QUERY_CHECK_CODE(code, lino, _end); goto FETCH_NEXT_BLOCK; } break; case STREAM_CHECKPOINT: { From 65dffbda0c54ff46b5022148c3e65ce60f50c00b Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Fri, 1 Nov 2024 18:47:41 +0800 Subject: [PATCH 34/55] fix tsma drop ctb --- include/common/tcommon.h | 2 + include/common/tmsg.h | 1 + include/libs/wal/wal.h | 1 + source/common/src/tmsg.c | 2 + source/dnode/mnode/impl/src/mndStb.c | 6 +- source/dnode/vnode/src/inc/tq.h | 1 + source/dnode/vnode/src/meta/metaTable.c | 1 + source/dnode/vnode/src/tq/tq.c | 3 +- source/dnode/vnode/src/tq/tqRead.c | 14 ++ source/dnode/vnode/src/tq/tqSink.c | 134 +++++++++++++++++- source/dnode/vnode/src/tq/tqUtil.c | 51 +++++++ source/dnode/vnode/src/vnd/vnodeSvr.c | 62 +++++++- source/libs/executor/src/scanoperator.c | 34 ++++- .../executor/src/streamtimewindowoperator.c | 2 +- source/libs/stream/src/streamState.c | 3 + source/libs/wal/src/walRead.c | 2 + tests/system-test/2-query/tsma.py | 26 +++- 17 files changed, 320 insertions(+), 25 deletions(-) diff --git a/include/common/tcommon.h b/include/common/tcommon.h index 9572bd7aad..8a8948fb17 100644 --- a/include/common/tcommon.h +++ b/include/common/tcommon.h @@ -156,6 +156,7 @@ typedef enum EStreamType { STREAM_PARTITION_DELETE_DATA, STREAM_GET_RESULT, STREAM_DELETE_GROUP_DATA, + STREAM_DROP_CHILD_TABLE, } EStreamType; #pragma pack(push, 1) @@ -402,6 +403,7 @@ int32_t dumpConfToDataBlock(SSDataBlock* pBlock, int32_t startCol); #define TSMA_RES_STB_EXTRA_COLUMN_NUM 4 // 3 columns: _wstart, _wend, _wduration, 1 tag: tbname static inline bool isTsmaResSTb(const char* stbName) { + return false; const char* pos = strstr(stbName, TSMA_RES_STB_POSTFIX); if (pos && strlen(stbName) == (pos - stbName) + strlen(TSMA_RES_STB_POSTFIX)) { return true; diff --git a/include/common/tmsg.h b/include/common/tmsg.h index 7ff70b243a..3c7d715aa8 100644 --- a/include/common/tmsg.h +++ b/include/common/tmsg.h @@ -3220,6 +3220,7 @@ int tDecodeSVCreateTbBatchRsp(SDecoder* pCoder, SVCreateTbBatchRsp* pRsp); typedef struct { char* name; uint64_t suid; // for tmq in wal format + int64_t uid; int8_t igNotExists; } SVDropTbReq; diff --git a/include/libs/wal/wal.h b/include/libs/wal/wal.h index f95b3f20ca..999adc2eff 100644 --- a/include/libs/wal/wal.h +++ b/include/libs/wal/wal.h @@ -138,6 +138,7 @@ typedef struct { int8_t scanMeta; int8_t deleteMsg; int8_t enableRef; + int8_t scanDropCtb; } SWalFilterCond; // todo hide this struct diff --git a/source/common/src/tmsg.c b/source/common/src/tmsg.c index 6d1699b911..7e79ef48b8 100644 --- a/source/common/src/tmsg.c +++ b/source/common/src/tmsg.c @@ -10277,6 +10277,7 @@ static int32_t tEncodeSVDropTbReq(SEncoder *pCoder, const SVDropTbReq *pReq) { TAOS_CHECK_RETURN(tStartEncode(pCoder)); TAOS_CHECK_RETURN(tEncodeCStr(pCoder, pReq->name)); TAOS_CHECK_RETURN(tEncodeU64(pCoder, pReq->suid)); + TAOS_CHECK_RETURN(tEncodeI64(pCoder, pReq->uid)); TAOS_CHECK_RETURN(tEncodeI8(pCoder, pReq->igNotExists)); tEndEncode(pCoder); @@ -10287,6 +10288,7 @@ static int32_t tDecodeSVDropTbReq(SDecoder *pCoder, SVDropTbReq *pReq) { TAOS_CHECK_RETURN(tStartDecode(pCoder)); TAOS_CHECK_RETURN(tDecodeCStr(pCoder, &pReq->name)); TAOS_CHECK_RETURN(tDecodeU64(pCoder, &pReq->suid)); + TAOS_CHECK_RETURN(tDecodeI64(pCoder, &pReq->uid)); TAOS_CHECK_RETURN(tDecodeI8(pCoder, &pReq->igNotExists)); tEndDecode(pCoder); diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index 6efc58119b..a1044a9f86 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -4256,9 +4256,9 @@ static int32_t mndCreateDropTbsTxnPrepare(SRpcMsg *pRsp, SMndDropTbsWithTsmaCtx TAOS_CHECK_GOTO(mndTransCheckConflict(pMnode, pTrans), NULL, _OVER); - if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TSMA_CTB)) != 0) goto _OVER; + //if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TSMA_CTB)) != 0) goto _OVER; if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; - if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; + //if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; if ((code = mndTransPrepare(pMnode, pTrans)) != 0) goto _OVER; _OVER: @@ -4298,7 +4298,7 @@ _OVER: static int32_t mndDropTbAdd(SMnode *pMnode, SHashObj *pVgHashMap, const SVgroupInfo *pVgInfo, char *name, tb_uid_t suid, bool ignoreNotExists) { - SVDropTbReq req = {.name = name, .suid = suid, .igNotExists = ignoreNotExists}; + SVDropTbReq req = {.name = name, .suid = suid, .igNotExists = ignoreNotExists, .uid = 0}; SVDropTbVgReqs *pReqs = taosHashGet(pVgHashMap, &pVgInfo->vgId, sizeof(pVgInfo->vgId)); SVDropTbVgReqs reqs = {0}; diff --git a/source/dnode/vnode/src/inc/tq.h b/source/dnode/vnode/src/inc/tq.h index bdb94d4a6e..3c40100f9d 100644 --- a/source/dnode/vnode/src/inc/tq.h +++ b/source/dnode/vnode/src/inc/tq.h @@ -158,6 +158,7 @@ int32_t doMergeExistedRows(SSubmitTbData* pExisted, const SSubmitTbData* pNew, c int32_t buildAutoCreateTableReq(const char* stbFullName, int64_t suid, int32_t numOfCols, SSDataBlock* pDataBlock, SArray* pTagArray, bool newSubTableRule, SVCreateTbReq** pReq); +int32_t tqExtractDropCtbDataBlock(const void* data, int32_t len, int64_t ver, void** pRefBlock, int32_t type); #define TQ_ERR_GO_TO_END(c) \ do { \ diff --git a/source/dnode/vnode/src/meta/metaTable.c b/source/dnode/vnode/src/meta/metaTable.c index 5c3516a962..93324d6eb4 100644 --- a/source/dnode/vnode/src/meta/metaTable.c +++ b/source/dnode/vnode/src/meta/metaTable.c @@ -1233,6 +1233,7 @@ int metaDropTable(SMeta *pMeta, int64_t version, SVDropTbReq *pReq, SArray *tbUi metaWLock(pMeta); rc = metaDropTableByUid(pMeta, uid, &type, &suid, &sysTbl); + metaInfo("wjm meta drop table by uid: %"PRId64, uid); metaULock(pMeta); if (rc < 0) goto _exit; diff --git a/source/dnode/vnode/src/tq/tq.c b/source/dnode/vnode/src/tq/tq.c index bd78f62cae..6195899566 100644 --- a/source/dnode/vnode/src/tq/tq.c +++ b/source/dnode/vnode/src/tq/tq.c @@ -758,7 +758,8 @@ int32_t tqBuildStreamTask(void* pTqObj, SStreamTask* pTask, int64_t nextProcessV } if (pTask->info.taskLevel == TASK_LEVEL__SOURCE) { - SWalFilterCond cond = {.deleteMsg = 1}; // delete msg also extract from wal files + bool scanDropCtb = pTask->subtableWithoutMd5 ? true : false; + SWalFilterCond cond = {.deleteMsg = 1, .scanDropCtb = scanDropCtb}; // delete msg also extract from wal files pTask->exec.pWalReader = walOpenReader(pTq->pVnode->pWal, &cond, pTask->id.taskId); if (pTask->exec.pWalReader == NULL) { tqError("vgId:%d failed init wal reader, code:%s", vgId, tstrerror(terrno)); diff --git a/source/dnode/vnode/src/tq/tqRead.c b/source/dnode/vnode/src/tq/tqRead.c index c8c34b10a4..391cbe78fb 100644 --- a/source/dnode/vnode/src/tq/tqRead.c +++ b/source/dnode/vnode/src/tq/tqRead.c @@ -382,6 +382,20 @@ int32_t extractMsgFromWal(SWalReader* pReader, void** pItem, int64_t maxVer, con return code; } + } else if (pCont->msgType == TDMT_VND_DROP_TABLE && pReader->cond.scanDropCtb) { + void* pBody = POINTER_SHIFT(pCont->body, sizeof(SMsgHead)); + int32_t len = pCont->bodyLen - sizeof(SMsgHead); + code = tqExtractDropCtbDataBlock(pBody, len, ver, (void**)pItem, 0); + if (TSDB_CODE_SUCCESS == code) { + if (!*pItem) { + continue; + } else { + tqDebug("s-task:%s drop ctb msg extract from WAL, len:%d, ver:%"PRId64, id, len, ver); + } + } else { + terrno = code; + return code; + } } else { tqError("s-task:%s invalid msg type:%d, ver:%" PRId64, id, pCont->msgType, ver); return TSDB_CODE_STREAM_INTERNAL_ERROR; diff --git a/source/dnode/vnode/src/tq/tqSink.c b/source/dnode/vnode/src/tq/tqSink.c index e972c928f0..3c14870d92 100644 --- a/source/dnode/vnode/src/tq/tqSink.c +++ b/source/dnode/vnode/src/tq/tqSink.c @@ -53,6 +53,7 @@ static int32_t checkTagSchema(SStreamTask* pTask, SVnode* pVnode); static void reubuildAndSendMultiResBlock(SStreamTask* pTask, const SArray* pBlocks, SVnode* pVnode, int64_t earlyTs); static int32_t handleResultBlockMsg(SStreamTask* pTask, SSDataBlock* pDataBlock, int32_t index, SVnode* pVnode, int64_t earlyTs); +static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, const char* dstTableName, int64_t uid); int32_t tqBuildDeleteReq(STQ* pTq, const char* stbFullName, const SSDataBlock* pDataBlock, SBatchDeleteReq* deleteReq, const char* pIdStr, bool newSubTableRule) { @@ -138,7 +139,7 @@ int32_t tqBuildDeleteReq(STQ* pTq, const char* stbFullName, const SSDataBlock* p return 0; } -static int32_t encodeCreateChildTableForRPC(SVCreateTbBatchReq* pReqs, int32_t vgId, void** pBuf, int32_t* contLen) { +static int32_t encodeCreateChildTableForRPC(void* pReqs, int32_t vgId, void** pBuf, int32_t* contLen) { int32_t ret = 0; tEncodeSize(tEncodeSVCreateTbBatchReq, pReqs, *contLen, ret); @@ -170,17 +171,50 @@ end: return ret; } -static int32_t tqPutReqToQueue(SVnode* pVnode, SVCreateTbBatchReq* pReqs) { +static int32_t encodeDropChildTableForRPC(void* pReqs, int32_t vgId, void** ppBuf, int32_t *contLen) { + int32_t code = 0; + SEncoder ec = {0}; + tEncodeSize(tEncodeSVDropTbBatchReq, pReqs, *contLen, code); + if (code < 0) { + code = TSDB_CODE_INVALID_MSG; + goto end; + } + *contLen += sizeof(SMsgHead); + *ppBuf = rpcMallocCont(*contLen); + + if (!*ppBuf) { + code = terrno; + goto end; + } + + ((SMsgHead*)(*ppBuf))->vgId = vgId; + ((SMsgHead*)(*ppBuf))->contLen = htonl(*contLen); + + tEncoderInit(&ec, POINTER_SHIFT(*ppBuf, sizeof(SMsgHead)), (*contLen) - sizeof(SMsgHead)); + code = tEncodeSVDropTbBatchReq(&ec, pReqs); + tEncoderClear(&ec); + if (code < 0) { + rpcFreeCont(*ppBuf); + *ppBuf = NULL; + *contLen = 0; + code = TSDB_CODE_INVALID_MSG; + goto end; + } +end: + return code; +} + +static int32_t tqPutReqToQueue(SVnode* pVnode, void* pReqs, int32_t(*encoder)(void* pReqs, int32_t vgId, void** ppBuf, int32_t *contLen), tmsg_t msgType) { void* buf = NULL; int32_t tlen = 0; - int32_t code = encodeCreateChildTableForRPC(pReqs, TD_VID(pVnode), &buf, &tlen); + int32_t code = encoder(pReqs, TD_VID(pVnode), &buf, &tlen); if (code) { tqError("vgId:%d failed to encode create table msg, create table failed, code:%s", TD_VID(pVnode), tstrerror(code)); return code; } - SRpcMsg msg = {.msgType = TDMT_VND_CREATE_TABLE, .pCont = buf, .contLen = tlen}; + SRpcMsg msg = {.msgType = msgType, .pCont = buf, .contLen = tlen}; code = tmsgPutToQueue(&pVnode->msgCb, WRITE_QUEUE, &msg); if (code) { tqError("failed to put into write-queue since %s", terrstr()); @@ -388,7 +422,7 @@ static int32_t doBuildAndSendCreateTableMsg(SVnode* pVnode, char* stbFullName, S } reqs.nReqs = taosArrayGetSize(reqs.pArray); - code = tqPutReqToQueue(pVnode, &reqs); + code = tqPutReqToQueue(pVnode, &reqs, encodeCreateChildTableForRPC, TDMT_VND_CREATE_TABLE); if (code != TSDB_CODE_SUCCESS) { tqError("s-task:%s failed to send create table msg", id); } @@ -399,6 +433,58 @@ _end: return code; } +static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SSDataBlock* pDataBlock, + SStreamTask* pTask, int64_t suid) { + int32_t lino = 0; + int32_t code = 0; + int32_t rows = pDataBlock->info.rows; + const char* id = pTask->id.idStr; + SVDropTbBatchReq batchReq = {0}; + SVDropTbReq req = {0}; + + if (rows <= 0 || pTask->subtableWithoutMd5 == 0) return TSDB_CODE_SUCCESS; + + batchReq.pArray = taosArrayInit(rows, sizeof(SVDropTbReq)); + if (!batchReq.pArray) return terrno; + batchReq.nReqs = rows; + req.suid = suid; + req.igNotExists = true; + + SColumnInfoData* pTbNameCol = taosArrayGet(pDataBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); + SColumnInfoData* pUidCol = taosArrayGet(pDataBlock->pDataBlock, UID_COLUMN_INDEX); + char tbName[TSDB_TABLE_NAME_LEN + 1] = {0}; + for (int32_t i = 0; i < rows; ++i) { + void* pData = colDataGetVarData(pTbNameCol, i); + memcpy(tbName, varDataVal(pData), varDataLen(pData)); + tbName[varDataLen(pData) + 1] = 0; + req.name = tbName; + // TODO wjm remove uid, it's not my uid + req.uid = *(int64_t*)colDataGetData(pUidCol, i); + if (taosArrayPush(batchReq.pArray, &req) == NULL) { + TSDB_CHECK_CODE(terrno, lino, _exit); + } + } + tqDebug("s-task:%s build drop %d table(s) msg", id, rows); + code = tqPutReqToQueue(pVnode, &batchReq, encodeDropChildTableForRPC, TDMT_VND_DROP_TABLE); + TSDB_CHECK_CODE(code, lino, _exit); + + for (int32_t i = 0; i < rows; ++i) { + void* pData = colDataGetVarData(pTbNameCol, i); + memcpy(tbName, varDataVal(pData), varDataLen(pData)); + tbName[varDataLen(pData) + 1] = 0; + int64_t uid = *(int64_t*)colDataGetData(pUidCol, i); + code = doWaitForDstTableDropped(pVnode, pTask, tbName, uid); + TSDB_CHECK_CODE(code, lino, _exit); + } + return code; + +_exit: + if (batchReq.pArray) { + taosArrayDestroy(batchReq.pArray); + } + return code; +} + int32_t doBuildAndSendSubmitMsg(SVnode* pVnode, SStreamTask* pTask, SSubmitReq2* pReq, int32_t numOfBlocks) { const char* id = pTask->id.idStr; int32_t vgId = TD_VID(pVnode); @@ -807,6 +893,42 @@ int32_t doWaitForDstTableCreated(SVnode* pVnode, SStreamTask* pTask, STableSinkI return TSDB_CODE_SUCCESS; } +static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, const char* dstTableName, int64_t uid) { + int32_t vgId = TD_VID(pVnode); + int64_t suid = pTask->outputInfo.tbSink.stbUid; + const char* id = pTask->id.idStr; + + while (1) { + if (streamTaskShouldStop(pTask)) { + tqDebug("s-task:%s task will stop, quit from waiting for table:%s drop", id, dstTableName); + return TSDB_CODE_STREAM_EXEC_CANCELLED; + } + SMetaReader mr = {0}; + metaReaderDoInit(&mr, pVnode->pMeta, META_READER_LOCK); + int32_t code = metaGetTableEntryByName(&mr, dstTableName); + if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST) { + tqDebug("wjm s-task:%s table:%s has been dropped", id, dstTableName); + metaReaderClear(&mr); + break; + } else if (TSDB_CODE_SUCCESS == code) { + if (isValidDstChildTable(&mr, vgId, dstTableName, suid)) { + metaReaderClear(&mr); + taosMsleep(100); + tqDebug("s-task:%s wait 100ms for table:%s drop", id, dstTableName); + } else { + tqDebug("wjm s-task:%s table:%s exist, but not mine", id, dstTableName); + metaReaderClear(&mr); + break; + } + } else { + tqError("s-task:%s failed to wait for table:%s drop", id, dstTableName); + metaReaderClear(&mr); + return terrno; + } + } + return TSDB_CODE_SUCCESS; +} + int32_t doCreateSinkTableInfo(const char* pDstTableName, STableSinkInfo** pInfo) { int32_t nameLen = strlen(pDstTableName); (*pInfo) = taosMemoryCalloc(1, sizeof(STableSinkInfo) + nameLen + 1); @@ -1052,6 +1174,8 @@ void tqSinkDataIntoDstTable(SStreamTask* pTask, void* vnode, void* data) { code = doBuildAndSendCreateTableMsg(pVnode, stbFullName, pDataBlock, pTask, suid); } else if (pDataBlock->info.type == STREAM_CHECKPOINT) { continue; + } else if (pDataBlock->info.type == STREAM_DROP_CHILD_TABLE && pTask->subtableWithoutMd5) { + code = doBuildAndSendDropTableMsg(pVnode, stbFullName, pDataBlock, pTask, suid); } else { code = handleResultBlockMsg(pTask, pDataBlock, i, pVnode, earlyTs); } diff --git a/source/dnode/vnode/src/tq/tqUtil.c b/source/dnode/vnode/src/tq/tqUtil.c index 54b063d692..0b3d7b180a 100644 --- a/source/dnode/vnode/src/tq/tqUtil.c +++ b/source/dnode/vnode/src/tq/tqUtil.c @@ -751,3 +751,54 @@ int32_t tqGetStreamExecInfo(SVnode* pVnode, int64_t streamId, int64_t* pDelay, b return TSDB_CODE_SUCCESS; } + +int32_t tqExtractDropCtbDataBlock(const void* data, int32_t len, int64_t ver, void** pRefBlock, int32_t type) { + int32_t code = 0; + int32_t lino = 0; + SDecoder dc = {0}; + SVDropTbBatchReq batchReq = {0}; + tDecoderInit(&dc, (uint8_t*)data, len); + code = tDecodeSVDropTbBatchReq(&dc, &batchReq); + TSDB_CHECK_CODE(code, lino, _exit); + if (batchReq.nReqs <= 0) goto _exit; + + SSDataBlock* pBlock = NULL; + code = createSpecialDataBlock(STREAM_DROP_CHILD_TABLE, &pBlock); + TSDB_CHECK_CODE(code, lino, _exit); + + code = blockDataEnsureCapacity(pBlock, batchReq.nReqs); + TSDB_CHECK_CODE(code, lino, _exit); + + pBlock->info.rows = batchReq.nReqs; + pBlock->info.version = ver; + for (int32_t i = 0; i < batchReq.nReqs; ++i) { + SVDropTbReq* pReq = batchReq.pReqs + i; + SColumnInfoData* pCol = taosArrayGet(pBlock->pDataBlock, UID_COLUMN_INDEX); + TSDB_CHECK_NULL(pCol, code, lino, _exit, terrno); + code = colDataSetVal(pCol, i, (const char* )&pReq->uid, false); + TSDB_CHECK_CODE(code, lino, _exit); + + /* + pCol = taosArrayGet(pBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); + TSDB_CHECK_NULL(pCol, code, lino, _exit, terrno); + char varTbName[TSDB_TABLE_NAME_LEN + VARSTR_HEADER_SIZE + 1] = {0}; + varDataSetLen(varTbName, strlen(pReq->name)); + tsnprintf(varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1, "%s", pReq->name); + code = colDataSetVal(pCol, i, varTbName, false); + */ + } + + code = taosAllocateQitem(sizeof(SStreamRefDataBlock), DEF_QITEM, 0, pRefBlock); + TSDB_CHECK_CODE(code, lino, _exit); + ((SStreamRefDataBlock*)(*pRefBlock))->type = STREAM_INPUT__REF_DATA_BLOCK; + ((SStreamRefDataBlock*)(*pRefBlock))->pBlock = pBlock; + +_exit: + tDecoderClear(&dc); + if (TSDB_CODE_SUCCESS != code) { + tqError("faled to extract drop ctb data block, line:%d code:%s", lino, tstrerror(code)); + blockDataCleanup(pBlock); + taosMemoryFree(pBlock); + } + return code; +} diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index 1ea2aac809..a2b5f49b5c 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -552,6 +552,61 @@ _exit: return code; } +int32_t vnodePreProcessDropTbMsg(SVnode* pVnode, SRpcMsg* pMsg) { + int32_t code = TSDB_CODE_SUCCESS; + int32_t lino = 0; + int32_t size = 0; + SDecoder dc = {0}; + SEncoder ec = {0}; + SVDropTbBatchReq receivedBatchReqs = {0}; + SVDropTbBatchReq sentBatchReqs = {0}; + + tDecoderInit(&dc, pMsg->pCont + sizeof(SMsgHead), pMsg->contLen - sizeof(SMsgHead)); + + code = tDecodeSVDropTbBatchReq(&dc, &receivedBatchReqs); + if (code < 0) { + terrno = code; + TSDB_CHECK_CODE(code, lino, _exit); + } + sentBatchReqs.pArray = taosArrayInit(receivedBatchReqs.nReqs, sizeof(SVDropTbReq)); + if (!sentBatchReqs.pArray) { + code = terrno; + goto _exit; + } + + for (int32_t i = 0; i < receivedBatchReqs.nReqs; ++i) { + SVDropTbReq* pReq = receivedBatchReqs.pReqs + i; + tb_uid_t uid = metaGetTableEntryUidByName(pVnode->pMeta, pReq->name); + if (uid == 0) { + vWarn("vgId:%d, preprocess drop ctb: %s not found", TD_VID(pVnode), pReq->name); + continue; + } + pReq->uid = uid; + vDebug("vgId:%d %s for: %s, uid: %"PRId64, TD_VID(pVnode), __func__, pReq->name, pReq->uid); + if (taosArrayPush(sentBatchReqs.pArray, pReq) == NULL) { + code = terrno; + goto _exit; + } + } + sentBatchReqs.nReqs = sentBatchReqs.pArray->size; + + tEncodeSize(tEncodeSVDropTbBatchReq, &sentBatchReqs, size, code); + tEncoderInit(&ec, pMsg->pCont + sizeof(SMsgHead), size); + code = tEncodeSVDropTbBatchReq(&ec, &sentBatchReqs); + tEncoderClear(&ec); + if (code != TSDB_CODE_SUCCESS) { + vError("vgId:%d %s failed to encode drop tb batch req: %s", TD_VID(pVnode), __func__, tstrerror(code)); + TSDB_CHECK_CODE(code, lino, _exit); + } + +_exit: + tDecoderClear(&dc); + if (sentBatchReqs.pArray) { + taosArrayDestroy(sentBatchReqs.pArray); + } + return code; +} + int32_t vnodePreProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg) { int32_t code = 0; @@ -581,6 +636,9 @@ int32_t vnodePreProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg) { case TDMT_VND_DROP_TSMA_CTB: { code = vnodePreProcessDropTSmaCtbMsg(pVnode, pMsg); } break; + case TDMT_VND_DROP_TABLE: { + code = vnodePreProcessDropTbMsg(pVnode, pMsg); + } break; default: break; } @@ -1189,7 +1247,6 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq, STbUidStore *pStore = NULL; SArray *tbUids = NULL; SArray *tbNames = NULL; - pRsp->msgType = TDMT_VND_CREATE_TABLE_RSP; pRsp->code = TSDB_CODE_SUCCESS; pRsp->pCont = NULL; @@ -1245,9 +1302,11 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq, continue; } + vInfo("wjm process create tb req:%s, uid: %"PRId64, pCreateReq->name, pCreateReq->uid); // do create table if (metaCreateTable(pVnode->pMeta, ver, pCreateReq, &cRsp.pMeta) < 0) { if (pCreateReq->flags & TD_CREATE_IF_NOT_EXISTS && terrno == TSDB_CODE_TDB_TABLE_ALREADY_EXIST) { + vInfo("wjm already exists-----------------"); cRsp.code = TSDB_CODE_SUCCESS; } else { cRsp.code = terrno; @@ -1324,6 +1383,7 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq, } _exit: + vInfo("wjm process create table request exit"); tDeleteSVCreateTbBatchReq(&req); taosArrayDestroyEx(rsp.pArray, tFreeSVCreateTbRsp); taosArrayDestroy(tbUids); diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 0cdb0e7954..b5cb22cf12 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3309,7 +3309,9 @@ static int32_t setBlockGroupIdByUid(SStreamScanInfo* pInfo, SSDataBlock* pBlock) int32_t rows = pBlock->info.rows; if (!pInfo->partitionSup.needCalc) { for (int32_t i = 0; i < rows; i++) { + qInfo("wjm, get uid: %"PRIu64, uidCol[i]); uint64_t groupId = getGroupIdByUid(pInfo, uidCol[i]); + qInfo("wjm, get groupid: %"PRIu64, groupId); code = colDataSetVal(pGpCol, i, (const char*)&groupId, false); QUERY_CHECK_CODE(code, lino, _end); } @@ -3535,12 +3537,32 @@ static int32_t copyGetResultBlock(SSDataBlock* dest, TSKEY start, TSKEY end) { return appendDataToSpecialBlock(dest, &start, &end, NULL, NULL, NULL); } -static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock) { +static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock, int32_t *deleteNum) { int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; for (int32_t i = 0; i < pBlock->info.rows; i++) { SColumnInfoData* pGpIdCol = taosArrayGet(pBlock->pDataBlock, GROUPID_COLUMN_INDEX); + SColumnInfoData* pTbnameCol = taosArrayGet(pBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); int64_t* gpIdCol = (int64_t*)pGpIdCol->pData; + void* pParName = NULL; + int32_t winCode = 0; + // TODO wjm test remove non stream child tables + code = pInfo->stateStore.streamStateGetParName(pInfo->pStreamScanOp->pTaskInfo->streamInfo.pState, gpIdCol[i], + &pParName, false, &winCode); + if (TSDB_CODE_SUCCESS == code && winCode != 0) { + qInfo("delete stream part Name for:%"PRId64 " not found", gpIdCol[i]); + colDataSetNULL(pTbnameCol, i); + continue; + } + (*deleteNum)++; + QUERY_CHECK_CODE(code, lino, _end); + char varTbName[TSDB_TABLE_NAME_LEN + VARSTR_HEADER_SIZE + 1] = {0}; + varDataSetLen(varTbName, strlen(pParName)); + tsnprintf(varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1, "%s", pParName); + code = colDataSetVal(pTbnameCol, i, varTbName, false); + qDebug("delete stream part for:%"PRId64 " res tb: %s", gpIdCol[i], (char*)pParName); + pInfo->stateStore.streamStateFreeVal(pParName); + QUERY_CHECK_CODE(code, lino, _end); code = pInfo->stateStore.streamStateDeleteParName(pInfo->pStreamScanOp->pTaskInfo->streamInfo.pState, gpIdCol[i]); QUERY_CHECK_CODE(code, lino, _end); } @@ -3791,15 +3813,13 @@ FETCH_NEXT_BLOCK: prepareRangeScan(pInfo, pInfo->pUpdateRes, &pInfo->updateResIndex, NULL); pInfo->scanMode = STREAM_SCAN_FROM_DATAREADER_RANGE; } break; - case STREAM_DELETE_GROUP_DATA: { - printSpecDataBlock(pBlock, getStreamOpName(pOperator->operatorType), "delete group recv", - GET_TASKID(pTaskInfo)); + case STREAM_DROP_CHILD_TABLE: { + int32_t deleteNum = 0; code = setBlockGroupIdByUid(pInfo, pBlock); QUERY_CHECK_CODE(code, lino, _end); - - code = deletePartName(pInfo, pBlock); + code = deletePartName(pInfo, pBlock, &deleteNum); QUERY_CHECK_CODE(code, lino, _end); - goto FETCH_NEXT_BLOCK; + if (deleteNum == 0) goto FETCH_NEXT_BLOCK; } break; case STREAM_CHECKPOINT: { qError("stream check point error. msg type: STREAM_INPUT__DATA_BLOCK"); diff --git a/source/libs/executor/src/streamtimewindowoperator.c b/source/libs/executor/src/streamtimewindowoperator.c index 8fd00e9313..2e906d2ba6 100644 --- a/source/libs/executor/src/streamtimewindowoperator.c +++ b/source/libs/executor/src/streamtimewindowoperator.c @@ -5215,7 +5215,7 @@ static int32_t doStreamIntervalAggNext(SOperatorInfo* pOperator, SSDataBlock** p code = getAllIntervalWindow(pInfo->aggSup.pResultRowHashTable, pInfo->pUpdatedMap); QUERY_CHECK_CODE(code, lino, _end); continue; - } else if (pBlock->info.type == STREAM_CREATE_CHILD_TABLE) { + } else if (pBlock->info.type == STREAM_CREATE_CHILD_TABLE || pBlock->info.type == STREAM_DROP_CHILD_TABLE) { printDataBlock(pBlock, getStreamOpName(pOperator->operatorType), GET_TASKID(pTaskInfo)); (*ppRes) = pBlock; return code; diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 26c97cbb6e..91ed919446 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -480,6 +480,7 @@ int32_t streamStatePutParName(SStreamState* pState, int64_t groupId, const char if (tSimpleHashGet(pState->parNameMap, &groupId, sizeof(int64_t)) == NULL) { if (tSimpleHashGetSize(pState->parNameMap) < MAX_TABLE_NAME_NUM) { code = tSimpleHashPut(pState->parNameMap, &groupId, sizeof(int64_t), tbname, TSDB_TABLE_NAME_LEN); + qInfo("wjm put group id into parnamemap: %"PRId64 " cur mapsize: %d", groupId, tSimpleHashGetSize(pState->parNameMap)); QUERY_CHECK_CODE(code, lino, _end); } code = streamStatePutParName_rocksdb(pState, groupId, tbname); @@ -505,6 +506,7 @@ int32_t streamStateGetParName(SStreamState* pState, int64_t groupId, void** pVal (*pWinCode) = streamStateGetParName_rocksdb(pState, groupId, pVal); if ((*pWinCode) == TSDB_CODE_SUCCESS && tSimpleHashGetSize(pState->parNameMap) < MAX_TABLE_NAME_NUM) { code = tSimpleHashPut(pState->parNameMap, &groupId, sizeof(int64_t), *pVal, TSDB_TABLE_NAME_LEN); + qInfo("wjm put group id into parnamemap: %"PRId64 " cur mapsize: %d", groupId, tSimpleHashGetSize(pState->parNameMap)); QUERY_CHECK_CODE(code, lino, _end); } goto _end; @@ -526,6 +528,7 @@ _end: } int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId) { + qTrace("wjm delete par for group:%"PRId64 " parnameMapsize: %d", groupId, tSimpleHashGetSize(pState->parNameMap)); int32_t code = tSimpleHashRemove(pState->parNameMap, &groupId, sizeof(int64_t)); qTrace("catche %s at line %d res %d", __func__, __LINE__, code); code = streamStateDeleteParName_rocksdb(pState, groupId); diff --git a/source/libs/wal/src/walRead.c b/source/libs/wal/src/walRead.c index 9a3ea34eff..94dff3f71c 100644 --- a/source/libs/wal/src/walRead.c +++ b/source/libs/wal/src/walRead.c @@ -89,6 +89,8 @@ int32_t walNextValidMsg(SWalReader *pReader) { if (type == TDMT_VND_SUBMIT || ((type == TDMT_VND_DELETE) && (pReader->cond.deleteMsg == 1)) || (type == TDMT_VND_DROP_TSMA_CTB) || (IS_META_MSG(type) && pReader->cond.scanMeta)) { TAOS_RETURN(walFetchBody(pReader)); + } else if (type == TDMT_VND_DROP_TABLE && pReader->cond.scanDropCtb) { + TAOS_RETURN(walFetchBody(pReader)); } else { TAOS_CHECK_RETURN(walSkipFetchBody(pReader)); diff --git a/tests/system-test/2-query/tsma.py b/tests/system-test/2-query/tsma.py index a1638ae4cb..78a3c1406e 100644 --- a/tests/system-test/2-query/tsma.py +++ b/tests/system-test/2-query/tsma.py @@ -604,7 +604,7 @@ class TSMATestSQLGenerator: class TDTestCase: - updatecfgDict = {'asynclog': 0, 'ttlUnit': 1, 'ttlPushInterval': 5, 'ratioOfVnodeStreamThrea': 4, 'maxTsmaNum': 3} + updatecfgDict = {'asynclog': 0, 'ttlUnit': 1, 'ttlPushInterval': 5, 'ratioOfVnodeStreamThrea': 4, 'maxTsmaNum': 3, 'debugFlag': 143} def __init__(self): self.vgroups = 4 @@ -804,9 +804,10 @@ class TDTestCase: self.tsma_tester.check_sql(ctx.sql, ctx) def test_query_with_tsma(self): - self.create_tsma('tsma1', 'test', 'meters', ['avg(c1)', 'avg(c2)'], '5m') - self.create_tsma('tsma2', 'test', 'meters', ['avg(c1)', 'avg(c2)'], '30m') - self.create_tsma('tsma5', 'test', 'norm_tb', ['avg(c1)', 'avg(c2)'], '10m') + self.create_tsma('tsma1', 'test', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)'], '5m') + #self.create_tsma('tsma2', 'test', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)'], '30m') + #self.create_tsma('tsma5', 'test', 'norm_tb', ['avg(c1)', 'avg(c2)'], '10m') + return self.test_query_with_tsma_interval() self.test_query_with_tsma_agg() @@ -1227,17 +1228,28 @@ class TDTestCase: def run(self): self.init_data() - self.test_ddl() + #self.test_ddl() self.test_query_with_tsma() # bug to fix - self.test_flush_query() + #self.test_flush_query() #cluster test cluster_dnode_list = tdSql.get_cluseter_dnodes() clust_dnode_nums = len(cluster_dnode_list) if clust_dnode_nums > 1: self.test_redistribute_vgroups() - + self.test_td_32519() + + def test_td_32519(self): + tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 11:45:00", 1,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 11:55:00", 2,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('DROP TABLE t1', queryTimes=1) + tdSql.execute('CREATE TABLE t1 USING meters TAGS(1, "a", "b", 1,1,1)') + tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 11:59:00", 3,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 12:10:00", 4,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 12:20:00", 5,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('FLUSH DATABASE test', queryTimes=1) + def test_create_tsma(self): function_name = sys._getframe().f_code.co_name tdLog.debug(f'-----{function_name}------') From 9a92c136ce2553965171dd39a819a7edfb688a69 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Tue, 5 Nov 2024 15:10:25 +0800 Subject: [PATCH 35/55] fix drop child table with tsma --- include/common/tcommon.h | 4 +- include/common/tmsgdef.h | 1 - include/libs/stream/tstream.h | 2 +- source/dnode/mgmt/mgmt_mnode/src/mmHandle.c | 1 - source/dnode/mgmt/mgmt_vnode/src/vmHandle.c | 1 - source/dnode/mnode/impl/src/mndStb.c | 1 - source/dnode/vnode/src/tq/tqRead.c | 4 +- source/dnode/vnode/src/tq/tqSink.c | 15 ++++++- source/dnode/vnode/src/tq/tqUtil.c | 9 ----- source/dnode/vnode/src/vnd/vnodeSvr.c | 14 ------- source/libs/executor/src/groupoperator.c | 8 +++- source/libs/executor/src/scanoperator.c | 21 ++++++---- source/libs/parser/src/parser.c | 3 -- source/libs/planner/src/planLogicCreater.c | 27 ++++++------- source/libs/planner/src/planSpliter.c | 6 +-- source/libs/stream/src/streamDispatch.c | 1 + source/libs/stream/src/streamQueue.c | 6 ++- source/libs/wal/src/walRead.c | 2 +- tests/system-test/2-query/tsma.py | 44 +++++++++++++++------ 19 files changed, 92 insertions(+), 78 deletions(-) diff --git a/include/common/tcommon.h b/include/common/tcommon.h index 8a8948fb17..1d9a9bcc61 100644 --- a/include/common/tcommon.h +++ b/include/common/tcommon.h @@ -155,7 +155,6 @@ typedef enum EStreamType { STREAM_MID_RETRIEVE, STREAM_PARTITION_DELETE_DATA, STREAM_GET_RESULT, - STREAM_DELETE_GROUP_DATA, STREAM_DROP_CHILD_TABLE, } EStreamType; @@ -403,7 +402,8 @@ int32_t dumpConfToDataBlock(SSDataBlock* pBlock, int32_t startCol); #define TSMA_RES_STB_EXTRA_COLUMN_NUM 4 // 3 columns: _wstart, _wend, _wduration, 1 tag: tbname static inline bool isTsmaResSTb(const char* stbName) { - return false; + static bool showTsmaTables = false; + if (showTsmaTables) return false; const char* pos = strstr(stbName, TSMA_RES_STB_POSTFIX); if (pos && strlen(stbName) == (pos - stbName) + strlen(TSMA_RES_STB_POSTFIX)) { return true; diff --git a/include/common/tmsgdef.h b/include/common/tmsgdef.h index c81d649284..c22a3da5ad 100644 --- a/include/common/tmsgdef.h +++ b/include/common/tmsgdef.h @@ -316,7 +316,6 @@ TD_DEF_MSG_TYPE(TDMT_VND_ARB_CHECK_SYNC, "vnode-arb-check-sync", NULL, NULL) TD_DEF_MSG_TYPE(TDMT_VND_FETCH_TTL_EXPIRED_TBS, "vnode-fetch-ttl-expired-tbs", NULL, NULL) TD_DEF_MSG_TYPE(TDMT_VND_TABLE_NAME, "vnode-table-name", NULL, NULL) - TD_DEF_MSG_TYPE(TDMT_VND_DROP_TSMA_CTB, "vnode-drop-tsma-ctb", NULL, NULL) TD_CLOSE_MSG_SEG(TDMT_VND_MSG) TD_NEW_MSG_SEG(TDMT_SCH_MSG) // 3<<8 diff --git a/include/libs/stream/tstream.h b/include/libs/stream/tstream.h index de10d6844e..2cf791c8da 100644 --- a/include/libs/stream/tstream.h +++ b/include/libs/stream/tstream.h @@ -462,7 +462,7 @@ struct SStreamTask { struct SStreamMeta* pMeta; SSHashObj* pNameMap; void* pBackend; - int8_t subtableWithoutMd5; + int8_t subtableWithoutMd5; // only for tsma stream tasks char reserve[256]; char* backendPath; }; diff --git a/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c b/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c index 4b79ecf43a..0d804eadf0 100644 --- a/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c +++ b/source/dnode/mgmt/mgmt_mnode/src/mmHandle.c @@ -182,7 +182,6 @@ SArray *mmGetMsgHandles() { if (dmSetMgmtHandle(pArray, TDMT_MND_DROP_TB_WITH_TSMA, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_FETCH_TTL_EXPIRED_TBS_RSP, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_DROP_TABLE_RSP, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; - if (dmSetMgmtHandle(pArray, TDMT_VND_DROP_TSMA_CTB_RSP, mmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_MND_RETRIEVE_ANAL_ALGO, mmPutMsgToReadQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_MND_RETRIEVE_IP_WHITE, mmPutMsgToReadQueue, 0) == NULL) goto _OVER; diff --git a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c index a0356a6c4d..006f44b349 100644 --- a/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c +++ b/source/dnode/mgmt/mgmt_vnode/src/vmHandle.c @@ -1014,7 +1014,6 @@ SArray *vmGetMsgHandles() { if (dmSetMgmtHandle(pArray, TDMT_VND_QUERY_COMPACT_PROGRESS, vmPutMsgToFetchQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_KILL_COMPACT, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_VND_TABLE_NAME, vmPutMsgToFetchQueue, 0) == NULL) goto _OVER; - if (dmSetMgmtHandle(pArray, TDMT_VND_DROP_TSMA_CTB, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_STREAM_TASK_DEPLOY, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; if (dmSetMgmtHandle(pArray, TDMT_STREAM_TASK_DROP, vmPutMsgToWriteQueue, 0) == NULL) goto _OVER; diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index a1044a9f86..e782d505a9 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -96,7 +96,6 @@ int32_t mndInitStb(SMnode *pMnode) { mndSetMsgHandle(pMnode, TDMT_MND_DROP_TB_WITH_TSMA, mndProcessDropTbWithTsma); mndSetMsgHandle(pMnode, TDMT_VND_FETCH_TTL_EXPIRED_TBS_RSP, mndProcessFetchTtlExpiredTbs); mndSetMsgHandle(pMnode, TDMT_VND_DROP_TABLE_RSP, mndTransProcessRsp); - mndSetMsgHandle(pMnode, TDMT_VND_DROP_TSMA_CTB_RSP, mndTransProcessRsp); // mndSetMsgHandle(pMnode, TDMT_MND_SYSTABLE_RETRIEVE, mndProcessRetrieveStbReq); // mndSetMsgHandle(pMnode, TDMT_MND_CREATE_INDEX, mndProcessCreateIndexReq); diff --git a/source/dnode/vnode/src/tq/tqRead.c b/source/dnode/vnode/src/tq/tqRead.c index 391cbe78fb..ea2f3f91be 100644 --- a/source/dnode/vnode/src/tq/tqRead.c +++ b/source/dnode/vnode/src/tq/tqRead.c @@ -363,10 +363,10 @@ int32_t extractMsgFromWal(SWalReader* pReader, void** pItem, int64_t maxVer, con tqError("%s failed to create data submit for stream since out of memory", id); return code; } - } else if (pCont->msgType == TDMT_VND_DELETE || pCont->msgType == TDMT_VND_DROP_TSMA_CTB) { + } else if (pCont->msgType == TDMT_VND_DELETE) { void* pBody = POINTER_SHIFT(pCont->body, sizeof(SMsgHead)); int32_t len = pCont->bodyLen - sizeof(SMsgHead); - EStreamType blockType = pCont->msgType == TDMT_VND_DELETE ? STREAM_DELETE_DATA : STREAM_DELETE_GROUP_DATA; + EStreamType blockType = pCont->msgType == STREAM_DELETE_DATA; code = tqExtractDelDataBlock(pBody, len, ver, (void**)pItem, 0, blockType); if (code == TSDB_CODE_SUCCESS) { if (*pItem == NULL) { diff --git a/source/dnode/vnode/src/tq/tqSink.c b/source/dnode/vnode/src/tq/tqSink.c index 3c14870d92..3c525e7e9c 100644 --- a/source/dnode/vnode/src/tq/tqSink.c +++ b/source/dnode/vnode/src/tq/tqSink.c @@ -464,6 +464,19 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS TSDB_CHECK_CODE(terrno, lino, _exit); } } + + SMetaReader mr = {0}; + metaReaderDoInit(&mr, pVnode->pMeta, META_READER_LOCK); + // TODO wjm handle only one table + code = metaGetTableEntryByName(&mr, tbName); + if (isValidDstChildTable(&mr, TD_VID(pVnode), tbName, pTask->outputInfo.tbSink.stbUid)) { + STableSinkInfo* pTableSinkInfo = NULL; + bool alreadyCached = doGetSinkTableInfoFromCache(pTask->outputInfo.tbSink.pTbInfo, pDataBlock->info.id.groupId, &pTableSinkInfo); + if (alreadyCached) { + pTableSinkInfo->uid = mr.me.uid; + } + } + metaReaderClear(&mr); tqDebug("s-task:%s build drop %d table(s) msg", id, rows); code = tqPutReqToQueue(pVnode, &batchReq, encodeDropChildTableForRPC, TDMT_VND_DROP_TABLE); TSDB_CHECK_CODE(code, lino, _exit); @@ -473,10 +486,10 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS memcpy(tbName, varDataVal(pData), varDataLen(pData)); tbName[varDataLen(pData) + 1] = 0; int64_t uid = *(int64_t*)colDataGetData(pUidCol, i); + // TODO wjm remove uid it's not my uid code = doWaitForDstTableDropped(pVnode, pTask, tbName, uid); TSDB_CHECK_CODE(code, lino, _exit); } - return code; _exit: if (batchReq.pArray) { diff --git a/source/dnode/vnode/src/tq/tqUtil.c b/source/dnode/vnode/src/tq/tqUtil.c index 0b3d7b180a..a92049e5f3 100644 --- a/source/dnode/vnode/src/tq/tqUtil.c +++ b/source/dnode/vnode/src/tq/tqUtil.c @@ -777,15 +777,6 @@ int32_t tqExtractDropCtbDataBlock(const void* data, int32_t len, int64_t ver, vo TSDB_CHECK_NULL(pCol, code, lino, _exit, terrno); code = colDataSetVal(pCol, i, (const char* )&pReq->uid, false); TSDB_CHECK_CODE(code, lino, _exit); - - /* - pCol = taosArrayGet(pBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); - TSDB_CHECK_NULL(pCol, code, lino, _exit, terrno); - char varTbName[TSDB_TABLE_NAME_LEN + VARSTR_HEADER_SIZE + 1] = {0}; - varDataSetLen(varTbName, strlen(pReq->name)); - tsnprintf(varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1, "%s", pReq->name); - code = colDataSetVal(pCol, i, varTbName, false); - */ } code = taosAllocateQitem(sizeof(SStreamRefDataBlock), DEF_QITEM, 0, pRefBlock); diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index a2b5f49b5c..723ebb333d 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -633,9 +633,6 @@ int32_t vnodePreProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg) { case TDMT_VND_ARB_CHECK_SYNC: { code = vnodePreProcessArbCheckSyncMsg(pVnode, pMsg); } break; - case TDMT_VND_DROP_TSMA_CTB: { - code = vnodePreProcessDropTSmaCtbMsg(pVnode, pMsg); - } break; case TDMT_VND_DROP_TABLE: { code = vnodePreProcessDropTbMsg(pVnode, pMsg); } break; @@ -843,11 +840,6 @@ int32_t vnodeProcessWriteMsg(SVnode *pVnode, SRpcMsg *pMsg, int64_t ver, SRpcMsg case TDMT_VND_ARB_CHECK_SYNC: vnodeProcessArbCheckSyncReq(pVnode, pReq, len, pRsp); break; - case TDMT_VND_DROP_TSMA_CTB: - if (vnodeProcessDropTSmaCtbReq(pVnode, ver, pReq, len, pRsp, pMsg) < 0) { - goto _err; - } - break; default: vError("vgId:%d, unprocessed msg, %d", TD_VID(pVnode), pMsg->msgType); return TSDB_CODE_INVALID_MSG; @@ -2652,9 +2644,3 @@ int32_t vnodeAsyncCompact(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, int32_t tsdbAsyncCompact(STsdb *tsdb, const STimeWindow *tw, bool sync) { return 0; } #endif -static int32_t vnodeProcessDropTSmaCtbReq(SVnode *pVnode, int64_t ver, void *pReq, int32_t len, SRpcMsg *pRsp, - SRpcMsg *pOriginalMsg) { - pRsp->msgType = TDMT_VND_DROP_TSMA_CTB_RSP; - pRsp->code = TSDB_CODE_SUCCESS; - return pRsp->code; -} diff --git a/source/libs/executor/src/groupoperator.c b/source/libs/executor/src/groupoperator.c index fec35c3371..d5b4a52be7 100644 --- a/source/libs/executor/src/groupoperator.c +++ b/source/libs/executor/src/groupoperator.c @@ -1326,7 +1326,7 @@ int32_t appendCreateTableRow(void* pState, SExprSupp* pTableSup, SExprSupp* pTag int32_t winCode = TSDB_CODE_SUCCESS; code = pAPI->streamStateGetParName(pState, groupId, &pValue, true, &winCode); QUERY_CHECK_CODE(code, lino, _end); - + qInfo("wjm group id: %"PRId64 " winCode: %d, block type: %d", groupId, winCode, pSrcBlock->info.type); if (winCode != TSDB_CODE_SUCCESS) { SSDataBlock* pTmpBlock = NULL; code = blockCopyOneRow(pSrcBlock, rowId, &pTmpBlock); @@ -1335,6 +1335,8 @@ int32_t appendCreateTableRow(void* pState, SExprSupp* pTableSup, SExprSupp* pTag memset(pTmpBlock->info.parTbName, 0, TSDB_TABLE_NAME_LEN); pTmpBlock->info.id.groupId = groupId; char* tbName = pSrcBlock->info.parTbName; + printSpecDataBlock(pSrcBlock, "wjm", "recv", "wjm"); + printSpecDataBlock(pTmpBlock, "wjm", "recv", "wjm"); if (pTableSup->numOfExprs > 0) { code = projectApplyFunctions(pTableSup->pExprInfo, pDestBlock, pTmpBlock, pTableSup->pCtx, pTableSup->numOfExprs, NULL); @@ -1342,15 +1344,19 @@ int32_t appendCreateTableRow(void* pState, SExprSupp* pTableSup, SExprSupp* pTag SColumnInfoData* pTbCol = taosArrayGet(pDestBlock->pDataBlock, UD_TABLE_NAME_COLUMN_INDEX); QUERY_CHECK_NULL(pTbCol, code, lino, _end, terrno); + printSpecDataBlock(pSrcBlock, "wjm", "recv", "wjm"); + printSpecDataBlock(pTmpBlock, "wjm", "recv", "wjm"); memset(tbName, 0, TSDB_TABLE_NAME_LEN); int32_t len = 0; if (colDataIsNull_s(pTbCol, pDestBlock->info.rows - 1)) { + qInfo("wjm calculated tbnameis null"); len = 1; tbName[0] = 0; } else { void* pData = colDataGetData(pTbCol, pDestBlock->info.rows - 1); len = TMIN(varDataLen(pData), TSDB_TABLE_NAME_LEN - 1); memcpy(tbName, varDataVal(pData), len); + qInfo("wjm calculated tbname: %s", tbName); code = pAPI->streamStatePutParName(pState, groupId, tbName); QUERY_CHECK_CODE(code, lino, _end); } diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index b5cb22cf12..4cd32589d8 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -289,6 +289,7 @@ static int32_t doSetTagColumnData(STableScanBase* pTableScanInfo, SSDataBlock* p pTaskInfo, &pTableScanInfo->metaCache); // ignore the table not exists error, since this table may have been dropped during the scan procedure. if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST) { + if (pTaskInfo->streamInfo.pState) blockDataCleanup(pBlock); code = 0; } } @@ -3038,10 +3039,6 @@ static int32_t setBlockIntoRes(SStreamScanInfo* pInfo, const SSDataBlock* pBlock code = addTagPseudoColumnData(&pInfo->readHandle, pInfo->pPseudoExpr, pInfo->numOfPseudoExpr, pInfo->pRes, pBlockInfo->rows, pTaskInfo, &pTableScanInfo->base.metaCache); // ignore the table not exists error, since this table may have been dropped during the scan procedure. - if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST) { - code = 0; - } - if (code) { blockDataFreeRes((SSDataBlock*)pBlock); QUERY_CHECK_CODE(code, lino, _end); @@ -3312,7 +3309,7 @@ static int32_t setBlockGroupIdByUid(SStreamScanInfo* pInfo, SSDataBlock* pBlock) qInfo("wjm, get uid: %"PRIu64, uidCol[i]); uint64_t groupId = getGroupIdByUid(pInfo, uidCol[i]); qInfo("wjm, get groupid: %"PRIu64, groupId); - code = colDataSetVal(pGpCol, i, (const char*)&groupId, false); + code = colDataSetVal(pGpCol, i, (const char*)(uidCol + i), false); QUERY_CHECK_CODE(code, lino, _end); } } @@ -3541,7 +3538,7 @@ static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock, int32 int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; for (int32_t i = 0; i < pBlock->info.rows; i++) { - SColumnInfoData* pGpIdCol = taosArrayGet(pBlock->pDataBlock, GROUPID_COLUMN_INDEX); + SColumnInfoData* pGpIdCol = taosArrayGet(pBlock->pDataBlock, UID_COLUMN_INDEX); SColumnInfoData* pTbnameCol = taosArrayGet(pBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); int64_t* gpIdCol = (int64_t*)pGpIdCol->pData; void* pParName = NULL; @@ -3558,13 +3555,15 @@ static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock, int32 QUERY_CHECK_CODE(code, lino, _end); char varTbName[TSDB_TABLE_NAME_LEN + VARSTR_HEADER_SIZE + 1] = {0}; varDataSetLen(varTbName, strlen(pParName)); - tsnprintf(varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1, "%s", pParName); + int64_t len = tsnprintf(varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1, "%s", pParName); code = colDataSetVal(pTbnameCol, i, varTbName, false); qDebug("delete stream part for:%"PRId64 " res tb: %s", gpIdCol[i], (char*)pParName); pInfo->stateStore.streamStateFreeVal(pParName); QUERY_CHECK_CODE(code, lino, _end); code = pInfo->stateStore.streamStateDeleteParName(pInfo->pStreamScanOp->pTaskInfo->streamInfo.pState, gpIdCol[i]); QUERY_CHECK_CODE(code, lino, _end); + pBlock->info.id.groupId = gpIdCol[i]; + memcpy(pBlock->info.parTbName, varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1); } _end: @@ -3962,7 +3961,13 @@ FETCH_NEXT_BLOCK: } code = setBlockIntoRes(pInfo, pRes, &pStreamInfo->fillHistoryWindow, false); - QUERY_CHECK_CODE(code, lino, _end); + if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST) { + pInfo->pRes->info.rows = 0; + code = TSDB_CODE_SUCCESS; + } else { + QUERY_CHECK_CODE(code, lino, _end); + } + if (pInfo->pRes->info.rows == 0) { continue; } diff --git a/source/libs/parser/src/parser.c b/source/libs/parser/src/parser.c index 8ac1acb1a2..c2714659ec 100644 --- a/source/libs/parser/src/parser.c +++ b/source/libs/parser/src/parser.c @@ -433,9 +433,6 @@ int32_t qStmtBindParams(SQuery* pQuery, TAOS_MULTI_BIND* pParams, int32_t colIdx nodesDestroyNode(pQuery->pRoot); pQuery->pRoot = NULL; code = nodesCloneNode(pQuery->pPrepareRoot, &pQuery->pRoot); - if (NULL == pQuery->pRoot) { - code = code; - } } if (TSDB_CODE_SUCCESS == code) { rewriteExprAlias(pQuery->pRoot); diff --git a/source/libs/planner/src/planLogicCreater.c b/source/libs/planner/src/planLogicCreater.c index 34c83acee8..09a4b9c593 100644 --- a/source/libs/planner/src/planLogicCreater.c +++ b/source/libs/planner/src/planLogicCreater.c @@ -1534,21 +1534,20 @@ static int32_t createSortLogicNode(SLogicPlanContext* pCxt, SSelectStmt* pSelect if (TSDB_CODE_SUCCESS == code) { pSort->pSortKeys = NULL; code = nodesCloneList(pSelect->pOrderByList, &pSort->pSortKeys); - if (NULL == pSort->pSortKeys) { - code = code; - } - SNode* pNode = NULL; - SOrderByExprNode* firstSortKey = (SOrderByExprNode*)nodesListGetNode(pSort->pSortKeys, 0); - if (isPrimaryKeySort(pSelect->pOrderByList)) pSort->node.outputTsOrder = firstSortKey->order; - if (firstSortKey->pExpr->type == QUERY_NODE_COLUMN) { - SColumnNode* pCol = (SColumnNode*)firstSortKey->pExpr; - int16_t projIdx = 1; - FOREACH(pNode, pSelect->pProjectionList) { - SExprNode* pExpr = (SExprNode*)pNode; - if (0 == strcmp(pCol->node.aliasName, pExpr->aliasName)) { - pCol->projIdx = projIdx; break; + if (NULL != pSort->pSortKeys) { + SNode* pNode = NULL; + SOrderByExprNode* firstSortKey = (SOrderByExprNode*)nodesListGetNode(pSort->pSortKeys, 0); + if (isPrimaryKeySort(pSelect->pOrderByList)) pSort->node.outputTsOrder = firstSortKey->order; + if (firstSortKey->pExpr->type == QUERY_NODE_COLUMN) { + SColumnNode* pCol = (SColumnNode*)firstSortKey->pExpr; + int16_t projIdx = 1; + FOREACH(pNode, pSelect->pProjectionList) { + SExprNode* pExpr = (SExprNode*)pNode; + if (0 == strcmp(pCol->node.aliasName, pExpr->aliasName)) { + pCol->projIdx = projIdx; break; + } + projIdx++; } - projIdx++; } } } diff --git a/source/libs/planner/src/planSpliter.c b/source/libs/planner/src/planSpliter.c index e0e42087f3..e960c0ff5d 100644 --- a/source/libs/planner/src/planSpliter.c +++ b/source/libs/planner/src/planSpliter.c @@ -836,11 +836,9 @@ static int32_t stbSplSplitSessionForStream(SSplitContext* pCxt, SStableSplitInfo nodesDestroyNode(pMergeWin->pTsEnd); pMergeWin->pTsEnd = NULL; code = nodesCloneNode(nodesListGetNode(pPartWin->node.pTargets, index), &pMergeWin->pTsEnd); - if (NULL == pMergeWin->pTsEnd) { - code = code; - } } - code = stbSplCreateExchangeNode(pCxt, pInfo->pSplitNode, pPartWindow); + if (TSDB_CODE_SUCCESS == code) + code = stbSplCreateExchangeNode(pCxt, pInfo->pSplitNode, pPartWindow); } if (TSDB_CODE_SUCCESS == code) { code = nodesListMakeStrictAppend(&pInfo->pSubplan->pChildren, diff --git a/source/libs/stream/src/streamDispatch.c b/source/libs/stream/src/streamDispatch.c index e0fa199199..6e27cd651e 100644 --- a/source/libs/stream/src/streamDispatch.c +++ b/source/libs/stream/src/streamDispatch.c @@ -758,6 +758,7 @@ int32_t streamSearchAndAddBlock(SStreamTask* pTask, SStreamDispatchReq* pReqs, S SUseDbRsp* pDbInfo = &pTask->outputInfo.shuffleDispatcher.dbInfo; hashValue = taosGetTbHashVal(ctbName, strlen(ctbName), pDbInfo->hashMethod, pDbInfo->hashPrefix, pDbInfo->hashSuffix); + stInfo("wjm ctbname for dispatch: %s, pDataBlock.info.parTbName: %s", ctbName, pDataBlock->info.parTbName); SBlockName bln = {0}; bln.hashValue = hashValue; memcpy(bln.parTbName, pDataBlock->info.parTbName, strlen(pDataBlock->info.parTbName)); diff --git a/source/libs/stream/src/streamQueue.c b/source/libs/stream/src/streamQueue.c index 20c3e5a6b9..401aa7530d 100644 --- a/source/libs/stream/src/streamQueue.c +++ b/source/libs/stream/src/streamQueue.c @@ -166,6 +166,8 @@ const char* streamQueueItemGetTypeStr(int32_t type) { return "checkpoint-trigger"; case STREAM_INPUT__TRANS_STATE: return "trans-state"; + case STREAM_INPUT__REF_DATA_BLOCK: + return "ref-block"; default: return "datablock"; } @@ -211,7 +213,7 @@ EExtractDataCode streamTaskGetDataFromInputQ(SStreamTask* pTask, SStreamQueueIte // do not merge blocks for sink node and check point data block int8_t type = qItem->type; if (type == STREAM_INPUT__CHECKPOINT || type == STREAM_INPUT__CHECKPOINT_TRIGGER || - type == STREAM_INPUT__TRANS_STATE) { + type == STREAM_INPUT__TRANS_STATE || type == STREAM_INPUT__REF_DATA_BLOCK) { const char* p = streamQueueItemGetTypeStr(type); if (*pInput == NULL) { @@ -504,4 +506,4 @@ void streamTaskPutbackToken(STokenBucket* pBucket) { // size in KB void streamTaskConsumeQuota(STokenBucket* pBucket, int32_t bytes) { pBucket->quotaRemain -= SIZE_IN_MiB(bytes); } -void streamTaskInputFail(SStreamTask* pTask) { atomic_store_8(&pTask->inputq.status, TASK_INPUT_STATUS__FAILED); } \ No newline at end of file +void streamTaskInputFail(SStreamTask* pTask) { atomic_store_8(&pTask->inputq.status, TASK_INPUT_STATUS__FAILED); } diff --git a/source/libs/wal/src/walRead.c b/source/libs/wal/src/walRead.c index 94dff3f71c..da5e1f47e9 100644 --- a/source/libs/wal/src/walRead.c +++ b/source/libs/wal/src/walRead.c @@ -87,7 +87,7 @@ int32_t walNextValidMsg(SWalReader *pReader) { int32_t type = pReader->pHead->head.msgType; if (type == TDMT_VND_SUBMIT || ((type == TDMT_VND_DELETE) && (pReader->cond.deleteMsg == 1)) || - (type == TDMT_VND_DROP_TSMA_CTB) || (IS_META_MSG(type) && pReader->cond.scanMeta)) { + (IS_META_MSG(type) && pReader->cond.scanMeta)) { TAOS_RETURN(walFetchBody(pReader)); } else if (type == TDMT_VND_DROP_TABLE && pReader->cond.scanDropCtb) { TAOS_RETURN(walFetchBody(pReader)); diff --git a/tests/system-test/2-query/tsma.py b/tests/system-test/2-query/tsma.py index 78a3c1406e..77e57bd36d 100644 --- a/tests/system-test/2-query/tsma.py +++ b/tests/system-test/2-query/tsma.py @@ -805,9 +805,8 @@ class TDTestCase: def test_query_with_tsma(self): self.create_tsma('tsma1', 'test', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)'], '5m') - #self.create_tsma('tsma2', 'test', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)'], '30m') - #self.create_tsma('tsma5', 'test', 'norm_tb', ['avg(c1)', 'avg(c2)'], '10m') - return + self.create_tsma('tsma2', 'test', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)'], '30m') + self.create_tsma('tsma5', 'test', 'norm_tb', ['avg(c1)', 'avg(c2)'], '10m') self.test_query_with_tsma_interval() self.test_query_with_tsma_agg() @@ -1228,10 +1227,10 @@ class TDTestCase: def run(self): self.init_data() - #self.test_ddl() + self.test_ddl() self.test_query_with_tsma() # bug to fix - #self.test_flush_query() + self.test_flush_query() #cluster test cluster_dnode_list = tdSql.get_cluseter_dnodes() @@ -1241,14 +1240,35 @@ class TDTestCase: self.test_td_32519() def test_td_32519(self): - tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 11:45:00", 1,1,1,1,1,1,1, "a", "a")', queryTimes=1) - tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 11:55:00", 2,1,1,1,1,1,1, "a", "a")', queryTimes=1) - tdSql.execute('DROP TABLE t1', queryTimes=1) - tdSql.execute('CREATE TABLE t1 USING meters TAGS(1, "a", "b", 1,1,1)') - tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 11:59:00", 3,1,1,1,1,1,1, "a", "a")', queryTimes=1) - tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 12:10:00", 4,1,1,1,1,1,1, "a", "a")', queryTimes=1) - tdSql.execute('INSERT INTO t1 VALUES("2024-10-24 12:20:00", 5,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute("drop tsma test.tsma5") + self.create_recursive_tsma('tsma1', 'tsma_r', 'test', '1h', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)']) + tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 11:45:00", 1,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 11:55:00", 2,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('DROP TABLE test.t1', queryTimes=1) + self.wait_query_err('desc test.`404e15422d96c8b5de9603c2296681b1`', 10, -2147473917) + self.wait_query_err('desc test.`82b56f091c4346369da0af777c3e580d`', 10, -2147473917) + self.wait_query_err('desc test.`163b7c69922cf6d83a98bfa44e52dade`', 10, -2147473917) + tdSql.execute('CREATE TABLE test.t1 USING test.meters TAGS(1, "a", "b", 1,1,1)') + tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 11:59:00", 3,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 12:10:00", 4,1,1,1,1,1,1, "a", "a")', queryTimes=1) + tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 12:20:00", 5,1,1,1,1,1,1, "a", "a")', queryTimes=1) tdSql.execute('FLUSH DATABASE test', queryTimes=1) + tdSql.query('SELECT * FROM test.t1', queryTimes=1) + tdSql.checkRows(3) + sql = 'SELECT * FROM test.`404e15422d96c8b5de9603c2296681b1`' + self.wait_query(sql, 3, 20) ## tsma1 output ctb for t1 + tdSql.query(sql, queryTimes=1) + tdSql.checkData(0,1, 1) + tdSql.checkData(1,1, 1) + tdSql.checkData(2,1, 1) + #sql = 'select * from test.`82b56f091c4346369da0af777c3e580d`' + #self.wait_query(sql, 2, 10) ## tsma2 output ctb for t1 + #tdSql.query(sql, queryTimes=1) + #tdSql.checkData(0, 1, 1) + #tdSql.checkData(1, 1, 2) + sql = 'select * from test.`163b7c69922cf6d83a98bfa44e52dade`' + self.wait_query(sql, 2, 20) ## tsma_r output ctb for t1 + tdSql.checkData(0, 1, 1) def test_create_tsma(self): function_name = sys._getframe().f_code.co_name From de8576a6a18165be6e71e4a287185158b1224323 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Tue, 5 Nov 2024 16:20:30 +0800 Subject: [PATCH 36/55] refine comments and functions --- source/dnode/mnode/impl/src/mndStb.c | 91 ++++++++++++++++--------- source/libs/executor/src/scanoperator.c | 8 +-- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index e782d505a9..3712196f38 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -4063,8 +4063,8 @@ static int32_t mndProcessDropStbReqFromMNode(SRpcMsg *pReq) { } typedef struct SVDropTbVgReqs { - SVDropTbBatchReq req; - SVgroupInfo info; + SArray *pBatchReqs; + SVgroupInfo info; } SVDropTbVgReqs; typedef struct SMDropTbDbInfo { @@ -4086,16 +4086,17 @@ typedef struct SMDropTbTsmaInfos { } SMDropTbTsmaInfos; typedef struct SMndDropTbsWithTsmaCtx { - SHashObj *pTsmaMap; // - SHashObj *pDbMap; // - SHashObj *pVgMap; // , only for non tsma result child table - SHashObj *pTsmaTbVgMap; // , only for tsma result child table - SArray *pResTbNames; // SArray + SHashObj *pTsmaMap; // + SHashObj *pDbMap; // + SHashObj *pVgMap; // , only for non tsma result child table + SHashObj *pTsmaTbVgMap; // , only for tsma result child table + SArray *pResTbNames; // SArray } SMndDropTbsWithTsmaCtx; static int32_t mndDropTbAddTsmaResTbsForSingleVg(SMnode *pMnode, SMndDropTbsWithTsmaCtx *pCtx, SArray *pTbs, int32_t vgId); +static void destroySVDropTbBatchReqs(void *p); static void mndDestroyDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx *p) { if (!p) return; @@ -4125,7 +4126,7 @@ static void mndDestroyDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx *p) { void *pIter = taosHashIterate(p->pVgMap, NULL); while (pIter) { SVDropTbVgReqs *pReqs = pIter; - taosArrayDestroy(pReqs->req.pArray); + taosArrayDestroyEx(pReqs->pBatchReqs, destroySVDropTbBatchReqs); pIter = taosHashIterate(p->pVgMap, pIter); } taosHashCleanup(p->pVgMap); @@ -4135,7 +4136,7 @@ static void mndDestroyDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx *p) { void *pIter = taosHashIterate(p->pTsmaTbVgMap, NULL); while (pIter) { SVDropTbVgReqs *pReqs = pIter; - taosArrayDestroy(pReqs->req.pArray); + taosArrayDestroyEx(pReqs->pBatchReqs, destroySVDropTbBatchReqs); pIter = taosHashIterate(p->pTsmaTbVgMap, pIter); } taosHashCleanup(p->pTsmaTbVgMap); @@ -4219,20 +4220,25 @@ static int32_t mndSetDropTbsRedoActions(SMnode *pMnode, STrans *pTrans, const SV return mndTransAppendRedoAction(pTrans, &action); } -static int32_t mndBuildDropTbRedoActions(SMnode* pMnode, STrans* pTrans, SHashObj* pVgMap, tmsg_t msgType) { +static int32_t mndBuildDropTbRedoActions(SMnode *pMnode, STrans *pTrans, SHashObj *pVgMap, tmsg_t msgType) { int32_t code = 0; - void* pIter = taosHashIterate(pVgMap, NULL); + void *pIter = taosHashIterate(pVgMap, NULL); while (pIter) { const SVDropTbVgReqs *pVgReqs = pIter; int32_t len = 0; - void *p = mndBuildVDropTbsReq(pMnode, &pVgReqs->info, &pVgReqs->req, &len); - if (!p) { - taosHashCancelIterate(pVgMap, pIter); - code = TSDB_CODE_MND_RETURN_VALUE_NULL; - if (terrno != 0) code = terrno; - break; + for (int32_t i = 0; i < taosArrayGetSize(pVgReqs->pBatchReqs) && code == TSDB_CODE_SUCCESS; ++i) { + SVDropTbBatchReq *pBatchReq = taosArrayGet(pVgReqs->pBatchReqs, i); + void *p = mndBuildVDropTbsReq(pMnode, &pVgReqs->info, pBatchReq, &len); + if (!p) { + code = TSDB_CODE_MND_RETURN_VALUE_NULL; + if (terrno != 0) code = terrno; + break; + } + if ((code = mndSetDropTbsRedoActions(pMnode, pTrans, pVgReqs, p, len, msgType)) != 0) { + break; + } } - if ((code = mndSetDropTbsRedoActions(pMnode, pTrans, pVgReqs, p, len, msgType)) != 0) { + if (TSDB_CODE_SUCCESS != code) { taosHashCancelIterate(pVgMap, pIter); break; } @@ -4255,9 +4261,7 @@ static int32_t mndCreateDropTbsTxnPrepare(SRpcMsg *pRsp, SMndDropTbsWithTsmaCtx TAOS_CHECK_GOTO(mndTransCheckConflict(pMnode, pTrans), NULL, _OVER); - //if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TSMA_CTB)) != 0) goto _OVER; if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; - //if ((code = mndBuildDropTbRedoActions(pMnode, pTrans, pCtx->pTsmaTbVgMap, TDMT_VND_DROP_TABLE)) != 0) goto _OVER; if ((code = mndTransPrepare(pMnode, pTrans)) != 0) goto _OVER; _OVER: @@ -4295,26 +4299,51 @@ _OVER: TAOS_RETURN(code); } +static int32_t createDropTbBatchReq(const SVDropTbReq *pReq, SVDropTbBatchReq *pBatchReq) { + pBatchReq->nReqs = 1; + pBatchReq->pArray = taosArrayInit(TARRAY_MIN_SIZE, sizeof(SVDropTbReq)); + if (!pBatchReq->pArray) return terrno; + if (taosArrayPush(pBatchReq->pArray, pReq) == NULL) { + taosArrayDestroy(pBatchReq->pArray); + pBatchReq->pArray = NULL; + return terrno; + } + return TSDB_CODE_SUCCESS; +} + +static void destroySVDropTbBatchReqs(void *p) { + SVDropTbBatchReq *pReq = p; + taosArrayDestroy(pReq->pArray); + pReq->pArray = NULL; +} + static int32_t mndDropTbAdd(SMnode *pMnode, SHashObj *pVgHashMap, const SVgroupInfo *pVgInfo, char *name, tb_uid_t suid, bool ignoreNotExists) { SVDropTbReq req = {.name = name, .suid = suid, .igNotExists = ignoreNotExists, .uid = 0}; - SVDropTbVgReqs *pReqs = taosHashGet(pVgHashMap, &pVgInfo->vgId, sizeof(pVgInfo->vgId)); - SVDropTbVgReqs reqs = {0}; - if (pReqs == NULL) { - reqs.info = *pVgInfo; - reqs.req.pArray = taosArrayInit(TARRAY_MIN_SIZE, sizeof(SVDropTbReq)); - if (reqs.req.pArray == NULL) { + SVDropTbVgReqs *pVgReqs = taosHashGet(pVgHashMap, &pVgInfo->vgId, sizeof(pVgInfo->vgId)); + SVDropTbVgReqs vgReqs = {0}; + if (pVgReqs == NULL) { + vgReqs.info = *pVgInfo; + vgReqs.pBatchReqs = taosArrayInit(TARRAY_MIN_SIZE, sizeof(SVDropTbBatchReq)); + if (!vgReqs.pBatchReqs) return terrno; + SVDropTbBatchReq batchReq = {0}; + int32_t code = createDropTbBatchReq(&req, &batchReq); + if (TSDB_CODE_SUCCESS != code) return code; + if (taosArrayPush(vgReqs.pBatchReqs, &batchReq) == NULL) { + taosArrayDestroy(batchReq.pArray); return terrno; } - if (taosArrayPush(reqs.req.pArray, &req) == NULL) { - return terrno; - } - if (taosHashPut(pVgHashMap, &pVgInfo->vgId, sizeof(pVgInfo->vgId), &reqs, sizeof(reqs)) != 0) { + if (taosHashPut(pVgHashMap, &pVgInfo->vgId, sizeof(pVgInfo->vgId), &vgReqs, sizeof(vgReqs)) != 0) { + taosArrayDestroyEx(vgReqs.pBatchReqs, destroySVDropTbBatchReqs); return terrno; } } else { - if (taosArrayPush(pReqs->req.pArray, &req) == NULL) { + SVDropTbBatchReq batchReq = {0}; + int32_t code = createDropTbBatchReq(&req, &batchReq); + if (TSDB_CODE_SUCCESS != code) return code; + if (taosArrayPush(pVgReqs->pBatchReqs, &batchReq) == NULL) { + taosArrayDestroy(batchReq.pArray); return terrno; } } diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index 4cd32589d8..b13572d5b2 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3306,10 +3306,8 @@ static int32_t setBlockGroupIdByUid(SStreamScanInfo* pInfo, SSDataBlock* pBlock) int32_t rows = pBlock->info.rows; if (!pInfo->partitionSup.needCalc) { for (int32_t i = 0; i < rows; i++) { - qInfo("wjm, get uid: %"PRIu64, uidCol[i]); uint64_t groupId = getGroupIdByUid(pInfo, uidCol[i]); - qInfo("wjm, get groupid: %"PRIu64, groupId); - code = colDataSetVal(pGpCol, i, (const char*)(uidCol + i), false); + code = colDataSetVal(pGpCol, i, (const char*)&groupId, false); QUERY_CHECK_CODE(code, lino, _end); } } @@ -3538,6 +3536,7 @@ static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock, int32 int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; for (int32_t i = 0; i < pBlock->info.rows; i++) { + // uid is the same as gid SColumnInfoData* pGpIdCol = taosArrayGet(pBlock->pDataBlock, UID_COLUMN_INDEX); SColumnInfoData* pTbnameCol = taosArrayGet(pBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); int64_t* gpIdCol = (int64_t*)pGpIdCol->pData; @@ -3563,6 +3562,7 @@ static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock, int32 code = pInfo->stateStore.streamStateDeleteParName(pInfo->pStreamScanOp->pTaskInfo->streamInfo.pState, gpIdCol[i]); QUERY_CHECK_CODE(code, lino, _end); pBlock->info.id.groupId = gpIdCol[i]; + // currently, only one valid row in pBlock memcpy(pBlock->info.parTbName, varTbName + VARSTR_HEADER_SIZE, TSDB_TABLE_NAME_LEN + 1); } @@ -3814,8 +3814,6 @@ FETCH_NEXT_BLOCK: } break; case STREAM_DROP_CHILD_TABLE: { int32_t deleteNum = 0; - code = setBlockGroupIdByUid(pInfo, pBlock); - QUERY_CHECK_CODE(code, lino, _end); code = deletePartName(pInfo, pBlock, &deleteNum); QUERY_CHECK_CODE(code, lino, _end); if (deleteNum == 0) goto FETCH_NEXT_BLOCK; From a70b4e28c869efe1f6901cc698f50b1e3cd995be Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Tue, 5 Nov 2024 17:43:34 +0800 Subject: [PATCH 37/55] fix tests --- source/dnode/vnode/src/tq/tqRead.c | 2 +- source/dnode/vnode/src/vnd/vnodeSvr.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/dnode/vnode/src/tq/tqRead.c b/source/dnode/vnode/src/tq/tqRead.c index ea2f3f91be..d924e97ae3 100644 --- a/source/dnode/vnode/src/tq/tqRead.c +++ b/source/dnode/vnode/src/tq/tqRead.c @@ -366,7 +366,7 @@ int32_t extractMsgFromWal(SWalReader* pReader, void** pItem, int64_t maxVer, con } else if (pCont->msgType == TDMT_VND_DELETE) { void* pBody = POINTER_SHIFT(pCont->body, sizeof(SMsgHead)); int32_t len = pCont->bodyLen - sizeof(SMsgHead); - EStreamType blockType = pCont->msgType == STREAM_DELETE_DATA; + EStreamType blockType = STREAM_DELETE_DATA; code = tqExtractDelDataBlock(pBody, len, ver, (void**)pItem, 0, blockType); if (code == TSDB_CODE_SUCCESS) { if (*pItem == NULL) { diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index 723ebb333d..64bfa5a04d 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -561,7 +561,7 @@ int32_t vnodePreProcessDropTbMsg(SVnode* pVnode, SRpcMsg* pMsg) { SVDropTbBatchReq receivedBatchReqs = {0}; SVDropTbBatchReq sentBatchReqs = {0}; - tDecoderInit(&dc, pMsg->pCont + sizeof(SMsgHead), pMsg->contLen - sizeof(SMsgHead)); + tDecoderInit(&dc, POINTER_SHIFT(pMsg->pCont, sizeof(SMsgHead)), pMsg->contLen - sizeof(SMsgHead)); code = tDecodeSVDropTbBatchReq(&dc, &receivedBatchReqs); if (code < 0) { @@ -591,7 +591,7 @@ int32_t vnodePreProcessDropTbMsg(SVnode* pVnode, SRpcMsg* pMsg) { sentBatchReqs.nReqs = sentBatchReqs.pArray->size; tEncodeSize(tEncodeSVDropTbBatchReq, &sentBatchReqs, size, code); - tEncoderInit(&ec, pMsg->pCont + sizeof(SMsgHead), size); + tEncoderInit(&ec, POINTER_SHIFT(pMsg->pCont, sizeof(SMsgHead)), size); code = tEncodeSVDropTbBatchReq(&ec, &sentBatchReqs); tEncoderClear(&ec); if (code != TSDB_CODE_SUCCESS) { From 2ddd07142a23f5d4aca1cfc30a0ec547c62015ee Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Tue, 5 Nov 2024 19:20:12 +0800 Subject: [PATCH 38/55] remove logs --- source/dnode/vnode/src/meta/metaTable.c | 1 - source/dnode/vnode/src/tq/tqSink.c | 19 ++----- source/dnode/vnode/src/vnd/vnodeSvr.c | 72 ------------------------ source/libs/executor/src/groupoperator.c | 7 --- source/libs/executor/src/scanoperator.c | 3 +- source/libs/stream/src/streamDispatch.c | 1 - source/libs/stream/src/streamState.c | 3 - tests/system-test/2-query/tsma.py | 6 +- 8 files changed, 11 insertions(+), 101 deletions(-) diff --git a/source/dnode/vnode/src/meta/metaTable.c b/source/dnode/vnode/src/meta/metaTable.c index 93324d6eb4..5c3516a962 100644 --- a/source/dnode/vnode/src/meta/metaTable.c +++ b/source/dnode/vnode/src/meta/metaTable.c @@ -1233,7 +1233,6 @@ int metaDropTable(SMeta *pMeta, int64_t version, SVDropTbReq *pReq, SArray *tbUi metaWLock(pMeta); rc = metaDropTableByUid(pMeta, uid, &type, &suid, &sysTbl); - metaInfo("wjm meta drop table by uid: %"PRId64, uid); metaULock(pMeta); if (rc < 0) goto _exit; diff --git a/source/dnode/vnode/src/tq/tqSink.c b/source/dnode/vnode/src/tq/tqSink.c index 3c525e7e9c..6f8a9022dd 100644 --- a/source/dnode/vnode/src/tq/tqSink.c +++ b/source/dnode/vnode/src/tq/tqSink.c @@ -53,7 +53,7 @@ static int32_t checkTagSchema(SStreamTask* pTask, SVnode* pVnode); static void reubuildAndSendMultiResBlock(SStreamTask* pTask, const SArray* pBlocks, SVnode* pVnode, int64_t earlyTs); static int32_t handleResultBlockMsg(SStreamTask* pTask, SSDataBlock* pDataBlock, int32_t index, SVnode* pVnode, int64_t earlyTs); -static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, const char* dstTableName, int64_t uid); +static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, const char* dstTableName); int32_t tqBuildDeleteReq(STQ* pTq, const char* stbFullName, const SSDataBlock* pDataBlock, SBatchDeleteReq* deleteReq, const char* pIdStr, bool newSubTableRule) { @@ -442,7 +442,7 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS SVDropTbBatchReq batchReq = {0}; SVDropTbReq req = {0}; - if (rows <= 0 || pTask->subtableWithoutMd5 == 0) return TSDB_CODE_SUCCESS; + if (rows <= 0 || rows > 1 || pTask->subtableWithoutMd5 == 0) return TSDB_CODE_SUCCESS; batchReq.pArray = taosArrayInit(rows, sizeof(SVDropTbReq)); if (!batchReq.pArray) return terrno; @@ -451,15 +451,12 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS req.igNotExists = true; SColumnInfoData* pTbNameCol = taosArrayGet(pDataBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); - SColumnInfoData* pUidCol = taosArrayGet(pDataBlock->pDataBlock, UID_COLUMN_INDEX); char tbName[TSDB_TABLE_NAME_LEN + 1] = {0}; for (int32_t i = 0; i < rows; ++i) { void* pData = colDataGetVarData(pTbNameCol, i); memcpy(tbName, varDataVal(pData), varDataLen(pData)); tbName[varDataLen(pData) + 1] = 0; req.name = tbName; - // TODO wjm remove uid, it's not my uid - req.uid = *(int64_t*)colDataGetData(pUidCol, i); if (taosArrayPush(batchReq.pArray, &req) == NULL) { TSDB_CHECK_CODE(terrno, lino, _exit); } @@ -467,9 +464,9 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS SMetaReader mr = {0}; metaReaderDoInit(&mr, pVnode->pMeta, META_READER_LOCK); - // TODO wjm handle only one table + // only one row code = metaGetTableEntryByName(&mr, tbName); - if (isValidDstChildTable(&mr, TD_VID(pVnode), tbName, pTask->outputInfo.tbSink.stbUid)) { + if (TSDB_CODE_SUCCESS == code && isValidDstChildTable(&mr, TD_VID(pVnode), tbName, pTask->outputInfo.tbSink.stbUid)) { STableSinkInfo* pTableSinkInfo = NULL; bool alreadyCached = doGetSinkTableInfoFromCache(pTask->outputInfo.tbSink.pTbInfo, pDataBlock->info.id.groupId, &pTableSinkInfo); if (alreadyCached) { @@ -485,9 +482,7 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS void* pData = colDataGetVarData(pTbNameCol, i); memcpy(tbName, varDataVal(pData), varDataLen(pData)); tbName[varDataLen(pData) + 1] = 0; - int64_t uid = *(int64_t*)colDataGetData(pUidCol, i); - // TODO wjm remove uid it's not my uid - code = doWaitForDstTableDropped(pVnode, pTask, tbName, uid); + code = doWaitForDstTableDropped(pVnode, pTask, tbName); TSDB_CHECK_CODE(code, lino, _exit); } @@ -906,7 +901,7 @@ int32_t doWaitForDstTableCreated(SVnode* pVnode, SStreamTask* pTask, STableSinkI return TSDB_CODE_SUCCESS; } -static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, const char* dstTableName, int64_t uid) { +static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, const char* dstTableName) { int32_t vgId = TD_VID(pVnode); int64_t suid = pTask->outputInfo.tbSink.stbUid; const char* id = pTask->id.idStr; @@ -920,7 +915,6 @@ static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, cons metaReaderDoInit(&mr, pVnode->pMeta, META_READER_LOCK); int32_t code = metaGetTableEntryByName(&mr, dstTableName); if (code == TSDB_CODE_PAR_TABLE_NOT_EXIST) { - tqDebug("wjm s-task:%s table:%s has been dropped", id, dstTableName); metaReaderClear(&mr); break; } else if (TSDB_CODE_SUCCESS == code) { @@ -929,7 +923,6 @@ static int32_t doWaitForDstTableDropped(SVnode* pVnode, SStreamTask* pTask, cons taosMsleep(100); tqDebug("s-task:%s wait 100ms for table:%s drop", id, dstTableName); } else { - tqDebug("wjm s-task:%s table:%s exist, but not mine", id, dstTableName); metaReaderClear(&mr); break; } diff --git a/source/dnode/vnode/src/vnd/vnodeSvr.c b/source/dnode/vnode/src/vnd/vnodeSvr.c index 64bfa5a04d..6702b8b588 100644 --- a/source/dnode/vnode/src/vnd/vnodeSvr.c +++ b/source/dnode/vnode/src/vnd/vnodeSvr.c @@ -483,75 +483,6 @@ static int32_t vnodePreProcessArbCheckSyncMsg(SVnode *pVnode, SRpcMsg *pMsg) { return code; } -static int32_t vnodePreProcessDropTSmaCtbMsg(SVnode *pVnode, SRpcMsg *pMsg) { - SVDropTbBatchReq dropReq = {0}; - int32_t code = 0; - int32_t lino = 0; - SDecoder dc = {0}; - SEncoder ec = {0}; - int32_t nTbs = 0; - SDeleteRes res = {0}; - int32_t size = 0; - uint8_t *pCont = NULL; - tDecoderInit(&dc, (uint8_t *)pMsg->pCont + sizeof(SMsgHead), pMsg->contLen - sizeof(SMsgHead)); - if (tDecodeSVDropTbBatchReq(&dc, &dropReq) < 0) { - code = TSDB_CODE_INVALID_MSG; - TSDB_CHECK_CODE(code, lino, _exit); - } - nTbs = dropReq.nReqs; - res.skey = INT64_MIN; - res.ekey = INT64_MAX; - res.affectedRows = 1; - res.uidList = taosArrayInit(nTbs, sizeof(tb_uid_t)); - if (!res.uidList) { - code = terrno; - TSDB_CHECK_CODE(code, lino, _exit); - } - - vDebug("vnode preprocess drop tsma ctb, vgId:%d tb num: %d", TD_VID(pVnode), nTbs); - for (int32_t i = 0; i < nTbs; ++i) { - SVDeleteRsp rsp = {.affectedRows = 1}; - tb_uid_t uid = metaGetTableEntryUidByName(pVnode->pMeta, dropReq.pReqs[i].name); - if (uid == 0) { - vWarn("vgId:%d, drop tsma ctb:%s not found", TD_VID(pVnode), dropReq.pReqs[i].name); - continue; - } - if (NULL == taosArrayPush(res.uidList, &uid)) { - code = terrno; - TSDB_CHECK_CODE(code, lino, _exit); - } - } - - tEncodeSize(tEncodeDeleteRes, &res, size, code); - pCont = rpcMallocCont(size + sizeof(SMsgHead)); - if (!pCont) { - code = terrno; - TSDB_CHECK_CODE(code, lino, _exit); - } - ((SMsgHead *)pCont)->contLen = size + sizeof(SMsgHead); - ((SMsgHead *)pCont)->vgId = TD_VID(pVnode); - - tEncoderInit(&ec, pCont + sizeof(SMsgHead), size); - code = tEncodeDeleteRes(&ec, &res); - tEncoderClear(&ec); - if (code != 0) { - vError("vgId:%d %s failed to encode delete response", TD_VID(pVnode), __func__); - TSDB_CHECK_CODE(code, lino, _exit); - } - rpcFreeCont(pMsg->pCont); - pMsg->pCont = pCont; - pCont = NULL; - pMsg->contLen = size + sizeof(SMsgHead); - -_exit: - if (res.uidList) { - taosArrayDestroy(res.uidList); - } - tDecoderClear(&dc); - rpcFreeCont(pCont); - return code; -} - int32_t vnodePreProcessDropTbMsg(SVnode* pVnode, SRpcMsg* pMsg) { int32_t code = TSDB_CODE_SUCCESS; int32_t lino = 0; @@ -1294,11 +1225,9 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq, continue; } - vInfo("wjm process create tb req:%s, uid: %"PRId64, pCreateReq->name, pCreateReq->uid); // do create table if (metaCreateTable(pVnode->pMeta, ver, pCreateReq, &cRsp.pMeta) < 0) { if (pCreateReq->flags & TD_CREATE_IF_NOT_EXISTS && terrno == TSDB_CODE_TDB_TABLE_ALREADY_EXIST) { - vInfo("wjm already exists-----------------"); cRsp.code = TSDB_CODE_SUCCESS; } else { cRsp.code = terrno; @@ -1375,7 +1304,6 @@ static int32_t vnodeProcessCreateTbReq(SVnode *pVnode, int64_t ver, void *pReq, } _exit: - vInfo("wjm process create table request exit"); tDeleteSVCreateTbBatchReq(&req); taosArrayDestroyEx(rsp.pArray, tFreeSVCreateTbRsp); taosArrayDestroy(tbUids); diff --git a/source/libs/executor/src/groupoperator.c b/source/libs/executor/src/groupoperator.c index d5b4a52be7..d6e3d26267 100644 --- a/source/libs/executor/src/groupoperator.c +++ b/source/libs/executor/src/groupoperator.c @@ -1326,7 +1326,6 @@ int32_t appendCreateTableRow(void* pState, SExprSupp* pTableSup, SExprSupp* pTag int32_t winCode = TSDB_CODE_SUCCESS; code = pAPI->streamStateGetParName(pState, groupId, &pValue, true, &winCode); QUERY_CHECK_CODE(code, lino, _end); - qInfo("wjm group id: %"PRId64 " winCode: %d, block type: %d", groupId, winCode, pSrcBlock->info.type); if (winCode != TSDB_CODE_SUCCESS) { SSDataBlock* pTmpBlock = NULL; code = blockCopyOneRow(pSrcBlock, rowId, &pTmpBlock); @@ -1335,8 +1334,6 @@ int32_t appendCreateTableRow(void* pState, SExprSupp* pTableSup, SExprSupp* pTag memset(pTmpBlock->info.parTbName, 0, TSDB_TABLE_NAME_LEN); pTmpBlock->info.id.groupId = groupId; char* tbName = pSrcBlock->info.parTbName; - printSpecDataBlock(pSrcBlock, "wjm", "recv", "wjm"); - printSpecDataBlock(pTmpBlock, "wjm", "recv", "wjm"); if (pTableSup->numOfExprs > 0) { code = projectApplyFunctions(pTableSup->pExprInfo, pDestBlock, pTmpBlock, pTableSup->pCtx, pTableSup->numOfExprs, NULL); @@ -1344,19 +1341,15 @@ int32_t appendCreateTableRow(void* pState, SExprSupp* pTableSup, SExprSupp* pTag SColumnInfoData* pTbCol = taosArrayGet(pDestBlock->pDataBlock, UD_TABLE_NAME_COLUMN_INDEX); QUERY_CHECK_NULL(pTbCol, code, lino, _end, terrno); - printSpecDataBlock(pSrcBlock, "wjm", "recv", "wjm"); - printSpecDataBlock(pTmpBlock, "wjm", "recv", "wjm"); memset(tbName, 0, TSDB_TABLE_NAME_LEN); int32_t len = 0; if (colDataIsNull_s(pTbCol, pDestBlock->info.rows - 1)) { - qInfo("wjm calculated tbnameis null"); len = 1; tbName[0] = 0; } else { void* pData = colDataGetData(pTbCol, pDestBlock->info.rows - 1); len = TMIN(varDataLen(pData), TSDB_TABLE_NAME_LEN - 1); memcpy(tbName, varDataVal(pData), len); - qInfo("wjm calculated tbname: %s", tbName); code = pAPI->streamStatePutParName(pState, groupId, tbName); QUERY_CHECK_CODE(code, lino, _end); } diff --git a/source/libs/executor/src/scanoperator.c b/source/libs/executor/src/scanoperator.c index b13572d5b2..84dde6a579 100644 --- a/source/libs/executor/src/scanoperator.c +++ b/source/libs/executor/src/scanoperator.c @@ -3542,11 +3542,10 @@ static int32_t deletePartName(SStreamScanInfo* pInfo, SSDataBlock* pBlock, int32 int64_t* gpIdCol = (int64_t*)pGpIdCol->pData; void* pParName = NULL; int32_t winCode = 0; - // TODO wjm test remove non stream child tables code = pInfo->stateStore.streamStateGetParName(pInfo->pStreamScanOp->pTaskInfo->streamInfo.pState, gpIdCol[i], &pParName, false, &winCode); if (TSDB_CODE_SUCCESS == code && winCode != 0) { - qInfo("delete stream part Name for:%"PRId64 " not found", gpIdCol[i]); + qDebug("delete stream part Name for:%"PRId64 " not found", gpIdCol[i]); colDataSetNULL(pTbnameCol, i); continue; } diff --git a/source/libs/stream/src/streamDispatch.c b/source/libs/stream/src/streamDispatch.c index 6e27cd651e..e0fa199199 100644 --- a/source/libs/stream/src/streamDispatch.c +++ b/source/libs/stream/src/streamDispatch.c @@ -758,7 +758,6 @@ int32_t streamSearchAndAddBlock(SStreamTask* pTask, SStreamDispatchReq* pReqs, S SUseDbRsp* pDbInfo = &pTask->outputInfo.shuffleDispatcher.dbInfo; hashValue = taosGetTbHashVal(ctbName, strlen(ctbName), pDbInfo->hashMethod, pDbInfo->hashPrefix, pDbInfo->hashSuffix); - stInfo("wjm ctbname for dispatch: %s, pDataBlock.info.parTbName: %s", ctbName, pDataBlock->info.parTbName); SBlockName bln = {0}; bln.hashValue = hashValue; memcpy(bln.parTbName, pDataBlock->info.parTbName, strlen(pDataBlock->info.parTbName)); diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 91ed919446..26c97cbb6e 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -480,7 +480,6 @@ int32_t streamStatePutParName(SStreamState* pState, int64_t groupId, const char if (tSimpleHashGet(pState->parNameMap, &groupId, sizeof(int64_t)) == NULL) { if (tSimpleHashGetSize(pState->parNameMap) < MAX_TABLE_NAME_NUM) { code = tSimpleHashPut(pState->parNameMap, &groupId, sizeof(int64_t), tbname, TSDB_TABLE_NAME_LEN); - qInfo("wjm put group id into parnamemap: %"PRId64 " cur mapsize: %d", groupId, tSimpleHashGetSize(pState->parNameMap)); QUERY_CHECK_CODE(code, lino, _end); } code = streamStatePutParName_rocksdb(pState, groupId, tbname); @@ -506,7 +505,6 @@ int32_t streamStateGetParName(SStreamState* pState, int64_t groupId, void** pVal (*pWinCode) = streamStateGetParName_rocksdb(pState, groupId, pVal); if ((*pWinCode) == TSDB_CODE_SUCCESS && tSimpleHashGetSize(pState->parNameMap) < MAX_TABLE_NAME_NUM) { code = tSimpleHashPut(pState->parNameMap, &groupId, sizeof(int64_t), *pVal, TSDB_TABLE_NAME_LEN); - qInfo("wjm put group id into parnamemap: %"PRId64 " cur mapsize: %d", groupId, tSimpleHashGetSize(pState->parNameMap)); QUERY_CHECK_CODE(code, lino, _end); } goto _end; @@ -528,7 +526,6 @@ _end: } int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId) { - qTrace("wjm delete par for group:%"PRId64 " parnameMapsize: %d", groupId, tSimpleHashGetSize(pState->parNameMap)); int32_t code = tSimpleHashRemove(pState->parNameMap, &groupId, sizeof(int64_t)); qTrace("catche %s at line %d res %d", __func__, __LINE__, code); code = streamStateDeleteParName_rocksdb(pState, groupId); diff --git a/tests/system-test/2-query/tsma.py b/tests/system-test/2-query/tsma.py index 77e57bd36d..1c688d568c 100644 --- a/tests/system-test/2-query/tsma.py +++ b/tests/system-test/2-query/tsma.py @@ -1237,10 +1237,11 @@ class TDTestCase: clust_dnode_nums = len(cluster_dnode_list) if clust_dnode_nums > 1: self.test_redistribute_vgroups() - self.test_td_32519() + tdSql.execute("drop tsma test.tsma5") + for _ in range(4): + self.test_td_32519() def test_td_32519(self): - tdSql.execute("drop tsma test.tsma5") self.create_recursive_tsma('tsma1', 'tsma_r', 'test', '1h', 'meters', ['avg(c1)', 'avg(c2)', 'count(ts)']) tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 11:45:00", 1,1,1,1,1,1,1, "a", "a")', queryTimes=1) tdSql.execute('INSERT INTO test.t1 VALUES("2024-10-24 11:55:00", 2,1,1,1,1,1,1, "a", "a")', queryTimes=1) @@ -1269,6 +1270,7 @@ class TDTestCase: sql = 'select * from test.`163b7c69922cf6d83a98bfa44e52dade`' self.wait_query(sql, 2, 20) ## tsma_r output ctb for t1 tdSql.checkData(0, 1, 1) + self.drop_tsma('tsma_r', 'test') def test_create_tsma(self): function_name = sys._getframe().f_code.co_name From 274a7fd876b6c5f841be16f7e1fecb753d5e73a1 Mon Sep 17 00:00:00 2001 From: wangjiaming0909 <604227650@qq.com> Date: Wed, 6 Nov 2024 09:55:18 +0800 Subject: [PATCH 39/55] refine unused codes --- source/dnode/mnode/impl/src/mndStb.c | 196 +-------------------------- source/dnode/vnode/src/tq/tqSink.c | 29 ++-- source/libs/stream/src/streamState.c | 8 +- 3 files changed, 23 insertions(+), 210 deletions(-) diff --git a/source/dnode/mnode/impl/src/mndStb.c b/source/dnode/mnode/impl/src/mndStb.c index 3712196f38..eb6c326d1e 100644 --- a/source/dnode/mnode/impl/src/mndStb.c +++ b/source/dnode/mnode/impl/src/mndStb.c @@ -4086,42 +4086,16 @@ typedef struct SMDropTbTsmaInfos { } SMDropTbTsmaInfos; typedef struct SMndDropTbsWithTsmaCtx { - SHashObj *pTsmaMap; // - SHashObj *pDbMap; // - SHashObj *pVgMap; // , only for non tsma result child table - SHashObj *pTsmaTbVgMap; // , only for tsma result child table - SArray *pResTbNames; // SArray + SHashObj *pVgMap; // } SMndDropTbsWithTsmaCtx; -static int32_t mndDropTbAddTsmaResTbsForSingleVg(SMnode *pMnode, SMndDropTbsWithTsmaCtx *pCtx, SArray *pTbs, +static int32_t mndDropTbForSingleVg(SMnode *pMnode, SMndDropTbsWithTsmaCtx *pCtx, SArray *pTbs, int32_t vgId); static void destroySVDropTbBatchReqs(void *p); static void mndDestroyDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx *p) { if (!p) return; - if (p->pDbMap) { - void *pIter = taosHashIterate(p->pDbMap, NULL); - while (pIter) { - SMDropTbDbInfo *pInfo = pIter; - taosArrayDestroy(pInfo->dbVgInfos); - pIter = taosHashIterate(p->pDbMap, pIter); - } - taosHashCleanup(p->pDbMap); - } - if (p->pResTbNames) { - taosArrayDestroyP(p->pResTbNames, taosMemoryFree); - } - if (p->pTsmaMap) { - void *pIter = taosHashIterate(p->pTsmaMap, NULL); - while (pIter) { - SMDropTbTsmaInfos *pInfos = pIter; - taosArrayDestroy(pInfos->pTsmaInfos); - pIter = taosHashIterate(p->pTsmaMap, pIter); - } - taosHashCleanup(p->pTsmaMap); - } - if (p->pVgMap) { void *pIter = taosHashIterate(p->pVgMap, NULL); while (pIter) { @@ -4131,16 +4105,6 @@ static void mndDestroyDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx *p) { } taosHashCleanup(p->pVgMap); } - - if (p->pTsmaTbVgMap) { - void *pIter = taosHashIterate(p->pTsmaTbVgMap, NULL); - while (pIter) { - SVDropTbVgReqs *pReqs = pIter; - taosArrayDestroyEx(pReqs->pBatchReqs, destroySVDropTbBatchReqs); - pIter = taosHashIterate(p->pTsmaTbVgMap, pIter); - } - taosHashCleanup(p->pTsmaTbVgMap); - } taosMemoryFree(p); } @@ -4148,18 +4112,6 @@ static int32_t mndInitDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx **ppCtx) { int32_t code = 0; SMndDropTbsWithTsmaCtx *pCtx = taosMemoryCalloc(1, sizeof(SMndDropTbsWithTsmaCtx)); if (!pCtx) return terrno; - pCtx->pTsmaMap = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BIGINT), true, HASH_NO_LOCK); - if (!pCtx->pTsmaMap) { - code = terrno; - goto _end; - } - - pCtx->pDbMap = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_BINARY), true, HASH_NO_LOCK); - if (!pCtx->pDbMap) { - code = terrno; - goto _end; - } - pCtx->pResTbNames = taosArrayInit(TARRAY_MIN_SIZE, POINTER_BYTES); pCtx->pVgMap = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK); if (!pCtx->pVgMap) { @@ -4167,11 +4119,6 @@ static int32_t mndInitDropTbsWithTsmaCtx(SMndDropTbsWithTsmaCtx **ppCtx) { goto _end; } - pCtx->pTsmaTbVgMap = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK); - if (!pCtx->pTsmaTbVgMap) { - code = terrno; - goto _end; - } *ppCtx = pCtx; _end: if (code) mndDestroyDropTbsWithTsmaCtx(pCtx); @@ -4286,7 +4233,7 @@ static int32_t mndProcessDropTbWithTsma(SRpcMsg *pReq) { if (code) goto _OVER; for (int32_t i = 0; i < dropReq.pVgReqs->size; ++i) { SMDropTbReqsOnSingleVg *pReq = taosArrayGet(dropReq.pVgReqs, i); - code = mndDropTbAddTsmaResTbsForSingleVg(pMnode, pCtx, pReq->pTbs, pReq->vgInfo.vgId); + code = mndDropTbForSingleVg(pMnode, pCtx, pReq->pTbs, pReq->vgInfo.vgId); if (code) goto _OVER; } code = mndCreateDropTbsTxnPrepare(pReq, pCtx); @@ -4350,61 +4297,7 @@ static int32_t mndDropTbAdd(SMnode *pMnode, SHashObj *pVgHashMap, const SVgroupI return 0; } -int vgInfoCmp(const void *lp, const void *rp) { - SVgroupInfo *pLeft = (SVgroupInfo *)lp; - SVgroupInfo *pRight = (SVgroupInfo *)rp; - if (pLeft->hashBegin < pRight->hashBegin) { - return -1; - } else if (pLeft->hashBegin > pRight->hashBegin) { - return 1; - } - - return 0; -} - -static int32_t mndGetDbVgInfoForTsma(SMnode *pMnode, const char *dbname, SMDropTbTsmaInfo *pInfo) { - int32_t code = 0; - SDbObj *pDb = mndAcquireDb(pMnode, dbname); - if (!pDb) { - code = TSDB_CODE_MND_DB_NOT_EXIST; - goto _end; - } - - pInfo->dbInfo.dbVgInfos = taosArrayInit(pDb->cfg.numOfVgroups, sizeof(SVgroupInfo)); - if (!pInfo->dbInfo.dbVgInfos) { - code = terrno; - goto _end; - } - mndBuildDBVgroupInfo(pDb, pMnode, pInfo->dbInfo.dbVgInfos); - taosArraySort(pInfo->dbInfo.dbVgInfos, vgInfoCmp); - - pInfo->dbInfo.hashPrefix = pDb->cfg.hashPrefix; - pInfo->dbInfo.hashSuffix = pDb->cfg.hashSuffix; - pInfo->dbInfo.hashMethod = pDb->cfg.hashMethod; - -_end: - if (pDb) mndReleaseDb(pMnode, pDb); - if (code && pInfo->dbInfo.dbVgInfos) { - taosArrayDestroy(pInfo->dbInfo.dbVgInfos); - pInfo->dbInfo.dbVgInfos = NULL; - } - TAOS_RETURN(code); -} - -int32_t vgHashValCmp(const void *lp, const void *rp) { - uint32_t *key = (uint32_t *)lp; - SVgroupInfo *pVg = (SVgroupInfo *)rp; - - if (*key < pVg->hashBegin) { - return -1; - } else if (*key > pVg->hashEnd) { - return 1; - } - - return 0; -} - -static int32_t mndDropTbAddTsmaResTbsForSingleVg(SMnode *pMnode, SMndDropTbsWithTsmaCtx *pCtx, SArray *pTbs, +static int32_t mndDropTbForSingleVg(SMnode *pMnode, SMndDropTbsWithTsmaCtx *pCtx, SArray *pTbs, int32_t vgId) { int32_t code = 0; @@ -4420,88 +4313,9 @@ static int32_t mndDropTbAddTsmaResTbsForSingleVg(SMnode *pMnode, SMndDropTbsWith vgInfo.epSet = mndGetVgroupEpset(pMnode, pVgObj); mndReleaseVgroup(pMnode, pVgObj); - // get all stb uids - for (int32_t i = 0; i < pTbs->size; ++i) { - const SVDropTbReq *pTb = taosArrayGet(pTbs, i); - if (taosHashGet(pCtx->pTsmaMap, &pTb->suid, sizeof(pTb->suid))) { - } else { - SMDropTbTsmaInfos infos = {0}; - infos.pTsmaInfos = taosArrayInit(2, sizeof(SMDropTbTsmaInfo)); - if (!infos.pTsmaInfos) { - code = terrno; - goto _end; - } - if (taosHashPut(pCtx->pTsmaMap, &pTb->suid, sizeof(pTb->suid), &infos, sizeof(infos)) != 0) { - code = terrno; - goto _end; - } - } - } - - void *pIter = NULL; - SSmaObj *pSma = NULL; - char buf[TSDB_TABLE_FNAME_LEN] = {0}; - // get used tsmas and it's dbs - while (1) { - pIter = sdbFetch(pMnode->pSdb, SDB_SMA, pIter, (void **)&pSma); - if (!pIter) break; - SMDropTbTsmaInfos *pInfos = taosHashGet(pCtx->pTsmaMap, &pSma->stbUid, sizeof(pSma->stbUid)); - if (pInfos) { - SMDropTbTsmaInfo info = {0}; - int32_t len = sprintf(buf, "%s", pSma->name); - sprintf(info.tsmaResTbDbFName, "%s", pSma->db); - snprintf(info.tsmaResTbNamePrefix, TSDB_TABLE_FNAME_LEN, "%s", buf); - SMDropTbDbInfo *pDbInfo = taosHashGet(pCtx->pDbMap, pSma->db, TSDB_DB_FNAME_LEN); - info.suid = pSma->dstTbUid; - if (!pDbInfo) { - code = mndGetDbVgInfoForTsma(pMnode, pSma->db, &info); - if (code != TSDB_CODE_SUCCESS) { - sdbCancelFetch(pMnode->pSdb, pIter); - sdbRelease(pMnode->pSdb, pSma); - goto _end; - } - if (taosHashPut(pCtx->pDbMap, pSma->db, TSDB_DB_FNAME_LEN, &info.dbInfo, sizeof(SMDropTbDbInfo)) != 0) { - sdbCancelFetch(pMnode->pSdb, pIter); - sdbRelease(pMnode->pSdb, pSma); - goto _end; - } - } else { - info.dbInfo = *pDbInfo; - } - if (taosArrayPush(pInfos->pTsmaInfos, &info) == NULL) { - code = terrno; - sdbCancelFetch(pMnode->pSdb, pIter); - sdbRelease(pMnode->pSdb, pSma); - goto _end; - } - } - sdbRelease(pMnode->pSdb, pSma); - } - - // generate vg req map for (int32_t i = 0; i < pTbs->size; ++i) { SVDropTbReq *pTb = taosArrayGet(pTbs, i); TAOS_CHECK_GOTO(mndDropTbAdd(pMnode, pCtx->pVgMap, &vgInfo, pTb->name, pTb->suid, pTb->igNotExists), NULL, _end); - - SMDropTbTsmaInfos *pInfos = taosHashGet(pCtx->pTsmaMap, &pTb->suid, sizeof(pTb->suid)); - SArray *pVgInfos = NULL; - char buf[TSDB_TABLE_FNAME_LEN + TSDB_TABLE_NAME_LEN + 1]; - char resTbFullName[TSDB_TABLE_FNAME_LEN + 1] = {0}; - for (int32_t j = 0; j < pInfos->pTsmaInfos->size; ++j) { - SMDropTbTsmaInfo *pInfo = taosArrayGet(pInfos->pTsmaInfos, j); - int32_t len = sprintf(buf, "%s_%s", pInfo->tsmaResTbNamePrefix, pTb->name); - len = taosCreateMD5Hash(buf, len); - len = snprintf(resTbFullName, TSDB_TABLE_FNAME_LEN + 1, "%s.%s", pInfo->tsmaResTbDbFName, buf); - uint32_t hashVal = taosGetTbHashVal(resTbFullName, len, pInfo->dbInfo.hashMethod, pInfo->dbInfo.hashPrefix, - pInfo->dbInfo.hashSuffix); - const SVgroupInfo *pVgInfo = taosArraySearch(pInfo->dbInfo.dbVgInfos, &hashVal, vgHashValCmp, TD_EQ); - void *p = taosStrdup(resTbFullName + strlen(pInfo->tsmaResTbDbFName) + TSDB_NAME_DELIMITER_LEN); - if (taosArrayPush(pCtx->pResTbNames, &p) == NULL) { - code = terrno; - goto _end; - } - TAOS_CHECK_GOTO(mndDropTbAdd(pMnode, pCtx->pTsmaTbVgMap, pVgInfo, p, pInfo->suid, true), NULL, _end); - } } _end: return code; @@ -4529,7 +4343,7 @@ static int32_t mndProcessFetchTtlExpiredTbs(SRpcMsg *pRsp) { code = mndInitDropTbsWithTsmaCtx(&pCtx); if (code) goto _end; - code = mndDropTbAddTsmaResTbsForSingleVg(pMnode, pCtx, rsp.pExpiredTbs, rsp.vgId); + code = mndDropTbForSingleVg(pMnode, pCtx, rsp.pExpiredTbs, rsp.vgId); if (code) goto _end; code = mndCreateDropTbsTxnPrepare(pRsp, pCtx); if (code == 0) code = TSDB_CODE_ACTION_IN_PROGRESS; diff --git a/source/dnode/vnode/src/tq/tqSink.c b/source/dnode/vnode/src/tq/tqSink.c index 6f8a9022dd..3f4ff7f3d9 100644 --- a/source/dnode/vnode/src/tq/tqSink.c +++ b/source/dnode/vnode/src/tq/tqSink.c @@ -451,20 +451,19 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS req.igNotExists = true; SColumnInfoData* pTbNameCol = taosArrayGet(pDataBlock->pDataBlock, TABLE_NAME_COLUMN_INDEX); - char tbName[TSDB_TABLE_NAME_LEN + 1] = {0}; - for (int32_t i = 0; i < rows; ++i) { - void* pData = colDataGetVarData(pTbNameCol, i); - memcpy(tbName, varDataVal(pData), varDataLen(pData)); - tbName[varDataLen(pData) + 1] = 0; - req.name = tbName; - if (taosArrayPush(batchReq.pArray, &req) == NULL) { - TSDB_CHECK_CODE(terrno, lino, _exit); - } + char tbName[TSDB_TABLE_NAME_LEN + 1] = {0}; + int32_t i = 0; + void* pData = colDataGetVarData(pTbNameCol, i); + memcpy(tbName, varDataVal(pData), varDataLen(pData)); + tbName[varDataLen(pData) + 1] = 0; + req.name = tbName; + if (taosArrayPush(batchReq.pArray, &req) == NULL) { + TSDB_CHECK_CODE(terrno, lino, _exit); } SMetaReader mr = {0}; metaReaderDoInit(&mr, pVnode->pMeta, META_READER_LOCK); - // only one row + code = metaGetTableEntryByName(&mr, tbName); if (TSDB_CODE_SUCCESS == code && isValidDstChildTable(&mr, TD_VID(pVnode), tbName, pTask->outputInfo.tbSink.stbUid)) { STableSinkInfo* pTableSinkInfo = NULL; @@ -478,13 +477,9 @@ static int32_t doBuildAndSendDropTableMsg(SVnode* pVnode, char* pStbFullname, SS code = tqPutReqToQueue(pVnode, &batchReq, encodeDropChildTableForRPC, TDMT_VND_DROP_TABLE); TSDB_CHECK_CODE(code, lino, _exit); - for (int32_t i = 0; i < rows; ++i) { - void* pData = colDataGetVarData(pTbNameCol, i); - memcpy(tbName, varDataVal(pData), varDataLen(pData)); - tbName[varDataLen(pData) + 1] = 0; - code = doWaitForDstTableDropped(pVnode, pTask, tbName); - TSDB_CHECK_CODE(code, lino, _exit); - } + + code = doWaitForDstTableDropped(pVnode, pTask, tbName); + TSDB_CHECK_CODE(code, lino, _exit); _exit: if (batchReq.pArray) { diff --git a/source/libs/stream/src/streamState.c b/source/libs/stream/src/streamState.c index 26c97cbb6e..5461b5899b 100644 --- a/source/libs/stream/src/streamState.c +++ b/source/libs/stream/src/streamState.c @@ -527,9 +527,13 @@ _end: int32_t streamStateDeleteParName(SStreamState* pState, int64_t groupId) { int32_t code = tSimpleHashRemove(pState->parNameMap, &groupId, sizeof(int64_t)); - qTrace("catche %s at line %d res %d", __func__, __LINE__, code); + if (TSDB_CODE_SUCCESS != code) { + qWarn("failed to remove parname from cache, code:%d", code); + } code = streamStateDeleteParName_rocksdb(pState, groupId); - qTrace("disk %s at line %d res %d", __func__, __LINE__, code); + if (TSDB_CODE_SUCCESS != code) { + qWarn("failed to remove parname from rocksdb, code:%d", code); + } return TSDB_CODE_SUCCESS; } From 0d7491434bd6cf483374868cd6501e5aa752531a Mon Sep 17 00:00:00 2001 From: kailixu Date: Fri, 8 Nov 2024 15:06:52 +0800 Subject: [PATCH 40/55] enh: add test case for grant --- tests/parallel_test/cases.task | 1 + tests/system-test/0-others/information_schema.py | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/parallel_test/cases.task b/tests/parallel_test/cases.task index 151358aec3..11ec619d93 100644 --- a/tests/parallel_test/cases.task +++ b/tests/parallel_test/cases.task @@ -436,6 +436,7 @@ ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/show.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/show_tag_index.py ,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/information_schema.py +,,y,system-test,./pytest.sh python3 ./test.py -f 0-others/grant.py ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/abs.py ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/abs.py -R ,,y,system-test,./pytest.sh python3 ./test.py -f 2-query/and_or_for_byte.py diff --git a/tests/system-test/0-others/information_schema.py b/tests/system-test/0-others/information_schema.py index aa548d4e59..c1a3942db6 100644 --- a/tests/system-test/0-others/information_schema.py +++ b/tests/system-test/0-others/information_schema.py @@ -299,6 +299,7 @@ class TDTestCase: 'oracle':'Oracle', 'mssql':'SqlServer', 'mongodb':'MongoDB', + 'csv':'CSV', } tdSql.execute('drop database if exists db2') From 2add1d260ae5d619b0ad1f252c9373fa0c23896c Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 16:07:33 +0800 Subject: [PATCH 41/55] Update index.md --- docs/zh/06-advanced/06-data-analysis/index.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/zh/06-advanced/06-data-analysis/index.md b/docs/zh/06-advanced/06-data-analysis/index.md index 2cbea1caba..02fcdfd148 100644 --- a/docs/zh/06-advanced/06-data-analysis/index.md +++ b/docs/zh/06-advanced/06-data-analysis/index.md @@ -1,15 +1,16 @@ --- -sidebar_label: 数据分析 -title: 数据分析功能 +sidebar_label: TDgpt +title: TDgpt --- ## 概述 -ANode(Analysis Node)是 TDengine 提供数据分析功能的扩展组件,通过 Restful 接口提供分析服务,拓展 TDengine 的功能,支持时间序列高级分析。 -ANode 是无状态的数据分析节点,集群中可以存在多个 ANode 节点,相互之间没有关联。将 ANode 注册到 TDengine 集群以后,通过 SQL 语句即可调用并完成时序分析任务。 -下图是数据分析的技术架构示意图。 +TDgpt 是 TDengine Enterprise 中针对时序数据提供高级分析功能的企业级组件,能够独立于 TDengine 主进程部署和运行,不消耗和占用 TDengine 主进程的资源,通过内置接口向 TDengine 提供运行时动态扩展的高级时序数据分析功能。TDgpt 具有服务无状态、功能易扩展、快速弹性部署、应用轻量化、高安全性等特点。 +TDgpt 运行在部署于 TDengine 集群中的 Analysis Node (ANode)中。每个 TDengine 集群中可以部署一个或若干个 ANode 节点,不同的 ANode 节点之间不相关,无同步或协同的要求。ANode 注册到 TDengine 集群以后,就可以通过内部接口提供服务。TDgpt 提供的高级时序数据分析服务可分为时序数据异常检测和时序数据预测分析两个类别。 -![数据分析功能架构图](./pic/data-analysis.png) +如下是数据分析的技术架构示意图。 + +myImage ## 安装部署 ### 环境准备 From 62f2dfced6162db05ebeafa9f58fa04959812a5d Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 16:21:23 +0800 Subject: [PATCH 42/55] Update index.md --- docs/zh/06-advanced/06-data-analysis/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/06-advanced/06-data-analysis/index.md b/docs/zh/06-advanced/06-data-analysis/index.md index 02fcdfd148..870a8636c6 100644 --- a/docs/zh/06-advanced/06-data-analysis/index.md +++ b/docs/zh/06-advanced/06-data-analysis/index.md @@ -10,7 +10,7 @@ TDgpt 运行在部署于 TDengine 集群中的 Analysis Node (ANode)中。每个 如下是数据分析的技术架构示意图。 -myImage +myImage ## 安装部署 ### 环境准备 From eec7ef7f69965ef2c1fc5a5658b4954862177058 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 16:22:45 +0800 Subject: [PATCH 43/55] Update addins.md --- docs/zh/06-advanced/06-data-analysis/addins.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/zh/06-advanced/06-data-analysis/addins.md b/docs/zh/06-advanced/06-data-analysis/addins.md index c0b8921718..11d47cf3a1 100644 --- a/docs/zh/06-advanced/06-data-analysis/addins.md +++ b/docs/zh/06-advanced/06-data-analysis/addins.md @@ -7,7 +7,21 @@ sidebar_label: "addins" ## 目录结构 -![数据分析功能架构图](./pic/dir.png) +```bash +. +├── cfg +├── model +│   └── ac_detection +├── release +├── script +└── taosanalytics + ├── algo + │   ├── ad + │   └── fc + ├── misc + └── test + +``` |目录|说明| |---|---| From e3bb384f07d74a0b3ac5a32ec9587ec4048f1d06 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 16:23:52 +0800 Subject: [PATCH 44/55] doc: update a figure. --- .../06-data-analysis/pic/data-analysis.png | Bin 50231 -> 50620 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png diff --git a/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png b/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png old mode 100644 new mode 100755 index 44fd82832f3e26d546ad15756decba130e5fc4a8..6bb4277831d5fa66150316282414c857d3eeb457 GIT binary patch literal 50620 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 literal 50231 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

|1yF&^SgV9Uvu*tSEEClMA`Rag{L~7 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 dP2#WrIJ45LmAISpld_dyuIEnH{S Date: Fri, 8 Nov 2024 17:17:47 +0800 Subject: [PATCH 45/55] add ip white list read --- docs/zh/08-operation/16-security.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/zh/08-operation/16-security.md b/docs/zh/08-operation/16-security.md index 4f47a644f7..1c8ad2200f 100644 --- a/docs/zh/08-operation/16-security.md +++ b/docs/zh/08-operation/16-security.md @@ -26,6 +26,22 @@ SHOW USERS; ```sql ALTER USER TEST DROP HOST HOST_NAME1 ``` +说明 +1. 开源版和企业版本都能添加成功,且可以查询到,但是开源版本不会对IP做任何限制。 +2. create user u_write pass 'taosdata1' host 'iprange1','iprange2', 可以一次添加多个iprange, 服务端会做去重,去重的逻辑是需要iprange 完全一样 +3. 默认会把127.0.0.1 添加到白名单列表,且在白名单列表可以查询 +4. 集群的节点IP集合会自动添加到白名单列表,但是查询不到。 +5. taosadaper 和 taosd 不在一个机器的时候,需要把taosadaper IP手动添加到taosd 白名单列表中 +6. 集群情况下,各个节点 enableWhiteList 成一样,或者全为false,或者全为true, 要不然集群无法启动 +7. 白名单变更生效时间1s,不超过2s, 每次变更对收发性能有些微影响(多一次判断,可以忽略),变更完之后、影响忽略不计, 变更过程中对集群没有影响,对正在访问客户端也没有影响(假设这些客户端的IP包含在white list内) +8. 如果添加两个ip range, 192.168.1.1/16(假设为A), 192.168.1.1/24(假设为B), 严格来说,A包含了B,但是考虑情况太复杂,并不会对A和B做合并 +9. 要删除的时候,必须严格匹配。 也就是如果添加的是192.168.1.1/24, 要删除也是192.168.1.1/24 +10. 只有root 才有权限对其他用户增删ip white list +11. 兼容之前的版本,但是不支持从当前版本回退到之前版本 +12. x.x.x.x/32 和x.x.x.x 属于同一个iprange, 显示为x.x.x.x +13. 如果客户端拿到的 0.0.0.0/0, 说明没有开启白名单。 +14. 如果白名单发生了改变, 客户端会在heartbeat里检测到。 +15. 针对一个user, 添加的IP个数上限是2048 ## 审计日志 From 2464fd06b35e31011444f9226d94694adbc3f9f1 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 18:07:32 +0800 Subject: [PATCH 46/55] doc: update figures. --- .../06-data-analysis/pic/data-analysis.png | Bin 50620 -> 51818 bytes .../06-advanced/06-data-analysis/pic/dir.png | Bin 7286 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/zh/06-advanced/06-data-analysis/pic/dir.png diff --git a/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png b/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png index 6bb4277831d5fa66150316282414c857d3eeb457..8914eb6730367f3736ff6173355b686be1b0c938 100755 GIT binary patch literal 51818 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 diff --git a/docs/zh/06-advanced/06-data-analysis/pic/dir.png b/docs/zh/06-advanced/06-data-analysis/pic/dir.png deleted file mode 100644 index d5aafb44274b375795df8e63eb10b813eb56047f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7286 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 Date: Fri, 8 Nov 2024 18:15:39 +0800 Subject: [PATCH 47/55] doc: update figures. --- .../06-data-analysis/pic/data-analysis.png | Bin 51818 -> 61418 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png b/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png index 8914eb6730367f3736ff6173355b686be1b0c938..f844c373684fede89453b9a8693e009107b9bd7f 100755 GIT binary patch literal 61418 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

eQ^{-{s8IPHsq&cgfcWzgY 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 literal 51818 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 Date: Fri, 8 Nov 2024 18:36:45 +0800 Subject: [PATCH 48/55] doc: update figures. --- .../06-data-analysis/pic/data-analysis.png | Bin 61418 -> 59017 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png b/docs/zh/06-advanced/06-data-analysis/pic/data-analysis.png index f844c373684fede89453b9a8693e009107b9bd7f..8950cbd58fbff7aa068c760a256bdb5a53be8a9b 100755 GIT binary patch literal 59017 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 L)1!I%j@SPm32I`5 literal 61418 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

eQ^{-{s8IPHsq&cgfcWzgY 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 From 5b959169ad76d7f452d081bcba951946e6b5dc37 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 18:41:18 +0800 Subject: [PATCH 49/55] Update index.md --- docs/zh/06-advanced/06-data-analysis/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/zh/06-advanced/06-data-analysis/index.md b/docs/zh/06-advanced/06-data-analysis/index.md index 870a8636c6..2aed7f908d 100644 --- a/docs/zh/06-advanced/06-data-analysis/index.md +++ b/docs/zh/06-advanced/06-data-analysis/index.md @@ -10,7 +10,10 @@ TDgpt 运行在部署于 TDengine 集群中的 Analysis Node (ANode)中。每个 如下是数据分析的技术架构示意图。 -myImage +TDgpt架构图 + +通过注册指令语句,将 ANode 注册到 MNode 中就加入到 TDengine 集群,查询会按需向其请求数据分析服务。请求服务通过 VNode 直接向 ANode 发起,用户则可以通过 SQL 语句直接调用 ANode 提供的服务。 + ## 安装部署 ### 环境准备 From e8a0b8a3ef3f1a9c21d0195cc4ac84a1e3505dfb Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 18:52:39 +0800 Subject: [PATCH 50/55] doc: add docs. --- .../06-data-analysis/03-anomaly-detection.md | 4 +- .../zh/06-advanced/06-data-analysis/addins.md | 4 +- .../06-data-analysis/analysis-preprocess.md | 23 +++ docs/zh/06-advanced/06-data-analysis/index.md | 173 ------------------ .../06-data-analysis/management.md | 162 ++++++++++++++++ 5 files changed, 189 insertions(+), 177 deletions(-) create mode 100644 docs/zh/06-advanced/06-data-analysis/analysis-preprocess.md create mode 100644 docs/zh/06-advanced/06-data-analysis/management.md diff --git a/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md b/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md index bdfa455ae3..1eae6088ce 100644 --- a/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md +++ b/docs/zh/06-advanced/06-data-analysis/03-anomaly-detection.md @@ -1,6 +1,6 @@ --- -title: "Anomaly-detection" -sidebar_label: "Anomaly-detection" +title: "异常检测算法" +sidebar_label: "异常检测算法" --- 本节讲述异常检测算法模型的使用方法。 diff --git a/docs/zh/06-advanced/06-data-analysis/addins.md b/docs/zh/06-advanced/06-data-analysis/addins.md index 11d47cf3a1..de24eb15a6 100644 --- a/docs/zh/06-advanced/06-data-analysis/addins.md +++ b/docs/zh/06-advanced/06-data-analysis/addins.md @@ -1,6 +1,6 @@ --- -title: "addins" -sidebar_label: "addins" +title: "算法开发者指南" +sidebar_label: "算法开发者指南" --- 本节说明如何将自己开发的预测算法和异常检测算法整合到 TDengine 分析平台,并能够通过 SQL 语句进行调用。 diff --git a/docs/zh/06-advanced/06-data-analysis/analysis-preprocess.md b/docs/zh/06-advanced/06-data-analysis/analysis-preprocess.md new file mode 100644 index 0000000000..4d65b247c2 --- /dev/null +++ b/docs/zh/06-advanced/06-data-analysis/analysis-preprocess.md @@ -0,0 +1,23 @@ +--- +title: "数据分析预处理" +sidebar_label: "数据分析预处理" +--- + +## 时序数据分析功能 + +### 白噪声检查 + +分析平台提供的 Restful 服务要求输入的时间序列不能是白噪声时间序列(White Noise Data, WND)和随机数序列 , 因此针对所有数据均默认进行白噪声检查。当前白噪声检查采用通行的 `Ljung-Box` 检验,`Ljung-Box` 统计量检查过程需要遍历整个输入序列并进行计算。 +如果用户能够明确输入序列一定不是白噪声序列,那么可以通过输入参数,指定预测之前忽略该检查,从而节省分析过程的 CPU 计算资源。 +同时支持独立地针对输入序列进行白噪声检测(该检测功能暂不独立对外开放)。 + + +### 数据重采样和时间戳对齐 + +分析平台支持将输入数据进行重采样预处理,从而确保输出结果按照用户指定的等间隔进行处理。处理过程分为两种类别: + +- 数据时间戳对齐。由于真实数据可能并非严格按照查询指定的时间戳输入。此时分析平台会自动将数据的时间间隔按照指定的时间间隔进行对齐。例如输入时间序列 [11, 22, 29, 41],用户指定时间间隔为 10,该序列将被对齐重整为以下序列 [10, 20, 30, 40]。 +- 数据时间重采样。用户输入时间序列的采样频率超过了输出结果的频率,例如输入时间序列的采样频率是 5,输出结果的频率是 10,输入时间序列 [0, 5, 10, 15, 20, 25, 30] 将被重采用为间隔 为 10 的序列 [0, 10, 20,30],[5, 15, 25] 处的数据将被丢弃。 + +需要注意的是,数据输入平台不支持缺失数据补齐后进行的预测分析,如果输入时间序列数据 [11, 22, 29, 49],并且用户要求的时间间隔为 10,重整对齐后的序列是 [10, 20, 30, 50] 那么该序列进行预测分析将返回错误。 + diff --git a/docs/zh/06-advanced/06-data-analysis/index.md b/docs/zh/06-advanced/06-data-analysis/index.md index 2aed7f908d..5af2e2194b 100644 --- a/docs/zh/06-advanced/06-data-analysis/index.md +++ b/docs/zh/06-advanced/06-data-analysis/index.md @@ -15,180 +15,7 @@ TDgpt 运行在部署于 TDengine 集群中的 Analysis Node (ANode)中。每个 通过注册指令语句,将 ANode 注册到 MNode 中就加入到 TDengine 集群,查询会按需向其请求数据分析服务。请求服务通过 VNode 直接向 ANode 发起,用户则可以通过 SQL 语句直接调用 ANode 提供的服务。 -## 安装部署 -### 环境准备 -ANode 要求节点上准备有 Python 3.10 及以上版本,以及相应的 Python 包自动安装组件 Pip,同时请确保能够正常连接互联网。 -### 安装及卸载 -使用专门的 ANode 安装包 TDengine-enterprise-anode-1.x.x.tar.gz 进行 ANode 的安装部署工作,安装过程与 TDengine 的安装流程一致。 - -```bash -tar -xzvf TDengine-enterprise-anode-1.0.0.tar.gz -cd TDengine-enterprise-anode-1.0.0 -sudo ./install.sh -``` - -卸载 ANode,执行命令 `rmtaosanode` 即可。 - -### 其他 -为了避免 ANode 安装后影响目标节点现有的 Python 库。 ANode 使用 Python 虚拟环境运行,安装后的默认 Python 目录处于 `/var/lib/taos/taosanode/venv/`。为了避免反复安装虚拟环境带来的开销,卸载 ANode 并不会自动删除该虚拟环境,如果您确认不需要 Python 的虚拟环境,可以手动删除。 - -## 启动及停止服务 -安装 ANode 以后,可以使用 `systemctl` 来管理 ANode 的服务。使用如下命令可以启动/停止/检查状态。 - -```bash -systemctl start taosanoded -systemctl stop taosanoded -systemctl status taosanoded -``` - -## 目录及配置说明 -|目录/文件|说明| -|---------------|------| -|/usr/local/taos/taosanode/bin|可执行文件目录| -|/usr/local/taos/taosanode/resource|资源文件目录,链接到文件夹 /var/lib/taos/taosanode/resource/| -|/usr/local/taos/taosanode/lib|库文件目录| -|/var/lib/taos/taosanode/model/|模型文件目录,链接到文件夹 /var/lib/taos/taosanode/model| -|/var/log/taos/taosanode/|日志文件目录| -|/etc/taos/taosanode.ini|配置文件| - -### 配置说明 - -Anode 提供的 RestFul 服务使用 uWSGI 驱动,因此 ANode 和 uWSGI 的配置信息存放在同一个配置文件中,具体如下: - -```ini -[uwsgi] -# charset -env = LC_ALL = en_US.UTF-8 - -# ip:port -http = 127.0.0.1:6050 - -# the local unix socket file than communicate to Nginx -#socket = 127.0.0.1:8001 -#socket-timeout = 10 - -# base directory -chdir = /usr/local/taos/taosanode/lib - -# initialize python file -wsgi-file = /usr/local/taos/taosanode/lib/taos/app.py - -# call module of uWSGI -callable = app - -# auto remove unix Socket and pid file when stopping -vacuum = true - -# socket exec model -#chmod-socket = 664 - -# uWSGI pid -uid = root - -# uWSGI gid -gid = root - -# main process -master = true - -# the number of worker processes -processes = 2 - -# pid file -pidfile = /usr/local/taos/taosanode/taosanode.pid - -# enable threads -enable-threads = true - -# the number of threads for each process -threads = 4 - -# memory useage report -memory-report = true - -# smooth restart -reload-mercy = 10 - -# conflict with systemctl, so do NOT uncomment this -# daemonize = /var/log/taos/taosanode/taosanode.log - -# log directory -logto = /var/log/taos/taosanode/taosanode.log - -# wWSGI monitor port -stats = 127.0.0.1:8387 - -# python virtual environment directory -virtualenv = /usr/local/taos/taosanode/venv/ - -[taosanode] -# default app log file -app-log = /var/log/taos/taosanode/taosanode.app.log - -# model storage directory -model-dir = /usr/local/taos/taosanode/model/ - -# default log level -log-level = DEBUG - -# draw the query results -draw-result = 0 -``` - -**提示** -请勿设置 `daemonize` 参数,该参数会导致 uWSGI 与 systemctl 冲突,从而无法正常启动。 - - -## ANode 基本操作 -### 管理 ANode -#### 创建 ANode -```sql -CREATE ANODE {node_url} -``` -node_url 是提供服务的 ANode 的 IP 和 PORT, 例如:`create anode 'http://localhost:6050'`。启动 ANode 以后如果不注册到 TDengine 集群中,则无法提供正常的服务。不建议 ANode 注册到两个或多个集群中。 - -#### 查看 ANode -列出集群中所有的数据分析节点,包括其 `FQDN`, `PORT`, `STATUS`。 -```sql -SHOW ANODES; -``` - -#### 查看提供的时序数据分析服务 - -```SQL -SHOW ANODES FULL; -``` - -#### 强制刷新集群中的分析算法缓存 -```SQL -UPDATE ANODE {node_id} -UPDATE ALL ANODES -``` - -#### 删除 ANode -```sql -DROP ANODE {anode_id} -``` -删除 ANode 只是将 ANode 从 TDengine 集群中删除,管理 ANode 的启停仍然需要使用`systemctl`命令。 - -### 时序数据分析功能 - -#### 白噪声检查 - -分析平台提供的 Restful 服务要求输入的时间序列不能是白噪声时间序列(White Noise Data, WND)和随机数序列 , 因此针对所有数据均默认进行白噪声检查。当前白噪声检查采用通行的 `Ljung-Box` 检验,`Ljung-Box` 统计量检查过程需要遍历整个输入序列并进行计算。 -如果用户能够明确输入序列一定不是白噪声序列,那么可以通过输入参数,指定预测之前忽略该检查,从而节省分析过程的 CPU 计算资源。 -同时支持独立地针对输入序列进行白噪声检测(该检测功能暂不独立对外开放)。 - - -#### 数据重采样和时间戳对齐 - -分析平台支持将输入数据进行重采样预处理,从而确保输出结果按照用户指定的等间隔进行处理。处理过程分为两种类别: - -- 数据时间戳对齐。由于真实数据可能并非严格按照查询指定的时间戳输入。此时分析平台会自动将数据的时间间隔按照指定的时间间隔进行对齐。例如输入时间序列 [11, 22, 29, 41],用户指定时间间隔为 10,该序列将被对齐重整为以下序列 [10, 20, 30, 40]。 -- 数据时间重采样。用户输入时间序列的采样频率超过了输出结果的频率,例如输入时间序列的采样频率是 5,输出结果的频率是 10,输入时间序列 [0, 5, 10, 15, 20, 25, 30] 将被重采用为间隔 为 10 的序列 [0, 10, 20,30],[5, 15, 25] 处的数据将被丢弃。 - -需要注意的是,数据输入平台不支持缺失数据补齐后进行的预测分析,如果输入时间序列数据 [11, 22, 29, 49],并且用户要求的时间间隔为 10,重整对齐后的序列是 [10, 20, 30, 50] 那么该序列进行预测分析将返回错误。 #### 时序数据异常检测 diff --git a/docs/zh/06-advanced/06-data-analysis/management.md b/docs/zh/06-advanced/06-data-analysis/management.md new file mode 100644 index 0000000000..abfa791239 --- /dev/null +++ b/docs/zh/06-advanced/06-data-analysis/management.md @@ -0,0 +1,162 @@ +--- +title: "安装部署" +sidebar_label: "安装部署" +--- + +## 安装部署 +### 环境准备 +ANode 要求节点上准备有 Python 3.10 及以上版本,以及相应的 Python 包自动安装组件 Pip,同时请确保能够正常连接互联网。 + +### 安装及卸载 +使用专门的 ANode 安装包 TDengine-enterprise-anode-1.x.x.tar.gz 进行 ANode 的安装部署工作,安装过程与 TDengine 的安装流程一致。 + +```bash +tar -xzvf TDengine-enterprise-anode-1.0.0.tar.gz +cd TDengine-enterprise-anode-1.0.0 +sudo ./install.sh +``` + +卸载 ANode,执行命令 `rmtaosanode` 即可。 + +### 其他 +为了避免 ANode 安装后影响目标节点现有的 Python 库。 ANode 使用 Python 虚拟环境运行,安装后的默认 Python 目录处于 `/var/lib/taos/taosanode/venv/`。为了避免反复安装虚拟环境带来的开销,卸载 ANode 并不会自动删除该虚拟环境,如果您确认不需要 Python 的虚拟环境,可以手动删除。 + +## 启动及停止服务 +安装 ANode 以后,可以使用 `systemctl` 来管理 ANode 的服务。使用如下命令可以启动/停止/检查状态。 + +```bash +systemctl start taosanoded +systemctl stop taosanoded +systemctl status taosanoded +``` + +## 目录及配置说明 +|目录/文件|说明| +|---------------|------| +|/usr/local/taos/taosanode/bin|可执行文件目录| +|/usr/local/taos/taosanode/resource|资源文件目录,链接到文件夹 /var/lib/taos/taosanode/resource/| +|/usr/local/taos/taosanode/lib|库文件目录| +|/var/lib/taos/taosanode/model/|模型文件目录,链接到文件夹 /var/lib/taos/taosanode/model| +|/var/log/taos/taosanode/|日志文件目录| +|/etc/taos/taosanode.ini|配置文件| + +### 配置说明 + +Anode 提供的 RestFul 服务使用 uWSGI 驱动,因此 ANode 和 uWSGI 的配置信息存放在同一个配置文件中,具体如下: + +```ini +[uwsgi] +# charset +env = LC_ALL = en_US.UTF-8 + +# ip:port +http = 127.0.0.1:6050 + +# the local unix socket file than communicate to Nginx +#socket = 127.0.0.1:8001 +#socket-timeout = 10 + +# base directory +chdir = /usr/local/taos/taosanode/lib + +# initialize python file +wsgi-file = /usr/local/taos/taosanode/lib/taos/app.py + +# call module of uWSGI +callable = app + +# auto remove unix Socket and pid file when stopping +vacuum = true + +# socket exec model +#chmod-socket = 664 + +# uWSGI pid +uid = root + +# uWSGI gid +gid = root + +# main process +master = true + +# the number of worker processes +processes = 2 + +# pid file +pidfile = /usr/local/taos/taosanode/taosanode.pid + +# enable threads +enable-threads = true + +# the number of threads for each process +threads = 4 + +# memory useage report +memory-report = true + +# smooth restart +reload-mercy = 10 + +# conflict with systemctl, so do NOT uncomment this +# daemonize = /var/log/taos/taosanode/taosanode.log + +# log directory +logto = /var/log/taos/taosanode/taosanode.log + +# wWSGI monitor port +stats = 127.0.0.1:8387 + +# python virtual environment directory +virtualenv = /usr/local/taos/taosanode/venv/ + +[taosanode] +# default app log file +app-log = /var/log/taos/taosanode/taosanode.app.log + +# model storage directory +model-dir = /usr/local/taos/taosanode/model/ + +# default log level +log-level = DEBUG + +# draw the query results +draw-result = 0 +``` + +**提示** +请勿设置 `daemonize` 参数,该参数会导致 uWSGI 与 systemctl 冲突,从而无法正常启动。 + + + +## ANode 基本操作 +### 管理 ANode +#### 创建 ANode +```sql +CREATE ANODE {node_url} +``` +node_url 是提供服务的 ANode 的 IP 和 PORT, 例如:`create anode 'http://localhost:6050'`。启动 ANode 以后如果不注册到 TDengine 集群中,则无法提供正常的服务。不建议 ANode 注册到两个或多个集群中。 + +#### 查看 ANode +列出集群中所有的数据分析节点,包括其 `FQDN`, `PORT`, `STATUS`。 +```sql +SHOW ANODES; +``` + +#### 查看提供的时序数据分析服务 + +```SQL +SHOW ANODES FULL; +``` + +#### 强制刷新集群中的分析算法缓存 +```SQL +UPDATE ANODE {node_id} +UPDATE ALL ANODES +``` + +#### 删除 ANode +```sql +DROP ANODE {anode_id} +``` +删除 ANode 只是将 ANode 从 TDengine 集群中删除,管理 ANode 的启停仍然需要使用`systemctl`命令。 From 480bd818fe0e1b67fe9ab2c639549d947d8f5f08 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Fri, 8 Nov 2024 18:54:06 +0800 Subject: [PATCH 51/55] Update management.md --- docs/zh/06-advanced/06-data-analysis/management.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/zh/06-advanced/06-data-analysis/management.md b/docs/zh/06-advanced/06-data-analysis/management.md index abfa791239..3f814f68f0 100644 --- a/docs/zh/06-advanced/06-data-analysis/management.md +++ b/docs/zh/06-advanced/06-data-analysis/management.md @@ -130,7 +130,6 @@ draw-result = 0 ## ANode 基本操作 -### 管理 ANode #### 创建 ANode ```sql CREATE ANODE {node_url} From 4ea40116d21174cccab4eaea593b048cdb4d1e1b Mon Sep 17 00:00:00 2001 From: sheyanjie-qq <249478495@qq.com> Date: Fri, 8 Nov 2024 19:19:32 +0800 Subject: [PATCH 52/55] update spring version, improve demo code --- docs/examples/JDBC/SpringJdbcTemplate/pom.xml | 8 ++++---- docs/examples/JDBC/springbootdemo/pom.xml | 4 +++- .../SpringbootdemoApplication.java | 3 ++- .../src/main/resources/application.properties | 2 ++ docs/examples/JDBC/taosdemo/pom.xml | 3 ++- .../taosdemo/service/QueryService.java | 2 +- .../taosdemo/service/DatabaseServiceTest.java | 18 +++++++++++++++++- .../taosdemo/service/QueryServiceTest.java | 6 +++--- .../service/SuperTableServiceTest.java | 16 +++++++++++++++- .../main/java/com/taos/example/DruidDemo.java | 6 +++--- .../java/com/taos/example/GeometryDemo.java | 5 +++-- .../main/java/com/taos/example/HikariDemo.java | 4 ++-- .../example/TelnetLineProtocolExample.java | 1 + 13 files changed, 58 insertions(+), 20 deletions(-) diff --git a/docs/examples/JDBC/SpringJdbcTemplate/pom.xml b/docs/examples/JDBC/SpringJdbcTemplate/pom.xml index 6e4941b4f1..34719dc135 100644 --- a/docs/examples/JDBC/SpringJdbcTemplate/pom.xml +++ b/docs/examples/JDBC/SpringJdbcTemplate/pom.xml @@ -22,19 +22,19 @@ org.springframework spring-context - 5.2.8.RELEASE + 5.3.39 org.springframework spring-jdbc - 5.1.9.RELEASE + 5.3.39 org.springframework spring-test - 5.1.9.RELEASE + 5.3.39 @@ -47,7 +47,7 @@ com.taosdata.jdbc taos-jdbcdriver - 3.0.0 + 3.4.0 diff --git a/docs/examples/JDBC/springbootdemo/pom.xml b/docs/examples/JDBC/springbootdemo/pom.xml index ee15f6013e..ba75cdcec3 100644 --- a/docs/examples/JDBC/springbootdemo/pom.xml +++ b/docs/examples/JDBC/springbootdemo/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.2.1.RELEASE + 2.6.15 com.taosdata.example @@ -65,6 +65,8 @@ spring-boot-starter-aop + + com.taosdata.jdbc taos-jdbcdriver diff --git a/docs/examples/JDBC/springbootdemo/src/main/java/com/taosdata/example/springbootdemo/SpringbootdemoApplication.java b/docs/examples/JDBC/springbootdemo/src/main/java/com/taosdata/example/springbootdemo/SpringbootdemoApplication.java index 53edaa5796..df7aa32158 100644 --- a/docs/examples/JDBC/springbootdemo/src/main/java/com/taosdata/example/springbootdemo/SpringbootdemoApplication.java +++ b/docs/examples/JDBC/springbootdemo/src/main/java/com/taosdata/example/springbootdemo/SpringbootdemoApplication.java @@ -3,9 +3,10 @@ package com.taosdata.example.springbootdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration; @MapperScan(basePackages = {"com.taosdata.example.springbootdemo"}) -@SpringBootApplication +@SpringBootApplication(exclude = {JdbcRepositoriesAutoConfiguration.class}) public class SpringbootdemoApplication { public static void main(String[] args) { diff --git a/docs/examples/JDBC/springbootdemo/src/main/resources/application.properties b/docs/examples/JDBC/springbootdemo/src/main/resources/application.properties index 00a06a5098..2b231f403b 100644 --- a/docs/examples/JDBC/springbootdemo/src/main/resources/application.properties +++ b/docs/examples/JDBC/springbootdemo/src/main/resources/application.properties @@ -15,6 +15,8 @@ spring.datasource.druid.max-wait=30000 spring.datasource.druid.validation-query=select SERVER_VERSION(); spring.aop.auto=true spring.aop.proxy-target-class=true + +spring.jooq.sql-dialect= #mybatis mybatis.mapper-locations=classpath:mapper/*.xml logging.level.com.taosdata.jdbc.springbootdemo.dao=debug diff --git a/docs/examples/JDBC/taosdemo/pom.xml b/docs/examples/JDBC/taosdemo/pom.xml index ab5912aa9e..c36973947b 100644 --- a/docs/examples/JDBC/taosdemo/pom.xml +++ b/docs/examples/JDBC/taosdemo/pom.xml @@ -10,7 +10,7 @@ Demo project for TDengine - 5.3.27 + 5.3.39 @@ -130,6 +130,7 @@ org.apache.maven.plugins maven-compiler-plugin + 3.13.0 8 8 diff --git a/docs/examples/JDBC/taosdemo/src/main/java/com/taosdata/taosdemo/service/QueryService.java b/docs/examples/JDBC/taosdemo/src/main/java/com/taosdata/taosdemo/service/QueryService.java index ab0a1125d2..33e8845d12 100644 --- a/docs/examples/JDBC/taosdemo/src/main/java/com/taosdata/taosdemo/service/QueryService.java +++ b/docs/examples/JDBC/taosdemo/src/main/java/com/taosdata/taosdemo/service/QueryService.java @@ -37,7 +37,7 @@ public class QueryService { stmt.execute("use " + dbName); ResultSet rs = stmt.executeQuery("show stables"); while (rs.next()) { - String name = rs.getString("name"); + String name = rs.getString("stable_name"); sqls.add("select count(*) from " + dbName + "." + name); sqls.add("select first(*) from " + dbName + "." + name); sqls.add("select last(*) from " + dbName + "." + name); diff --git a/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/DatabaseServiceTest.java b/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/DatabaseServiceTest.java index 621ba7df5d..e8c6432e38 100644 --- a/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/DatabaseServiceTest.java +++ b/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/DatabaseServiceTest.java @@ -1,10 +1,14 @@ package com.taosdata.taosdemo.service; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.BeforeClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; public class DatabaseServiceTest { - private DatabaseService service; + + private static DatabaseService service; @Test public void testCreateDatabase1() { @@ -20,4 +24,16 @@ public class DatabaseServiceTest { public void useDatabase() { service.useDatabase("test"); } + + @BeforeClass + public static void beforeClass() throws ClassNotFoundException { + Class.forName("com.taosdata.jdbc.TSDBDriver"); + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8"); + config.setUsername("root"); + config.setPassword("taosdata"); + HikariDataSource dataSource = new HikariDataSource(config); + service = new DatabaseService(dataSource); + } + } \ No newline at end of file diff --git a/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/QueryServiceTest.java b/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/QueryServiceTest.java index f2ad25710c..989aa094f3 100644 --- a/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/QueryServiceTest.java +++ b/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/QueryServiceTest.java @@ -15,7 +15,7 @@ public class QueryServiceTest { @Test public void generateSuperTableQueries() { - String[] sqls = queryService.generateSuperTableQueries("restful_test"); + String[] sqls = queryService.generateSuperTableQueries("test"); for (String sql : sqls) { System.out.println(sql); } @@ -23,8 +23,8 @@ public class QueryServiceTest { @Test public void querySuperTable() { - String[] sqls = queryService.generateSuperTableQueries("restful_test"); - queryService.querySuperTable(sqls, 1000, 10, 10); + String[] sqls = queryService.generateSuperTableQueries("test"); + queryService.querySuperTable(sqls, 100, 3, 3); } @BeforeClass diff --git a/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/SuperTableServiceTest.java b/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/SuperTableServiceTest.java index 33e52af1ea..4edba8c518 100644 --- a/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/SuperTableServiceTest.java +++ b/docs/examples/JDBC/taosdemo/src/test/java/com/taosdata/taosdemo/service/SuperTableServiceTest.java @@ -3,6 +3,9 @@ package com.taosdata.taosdemo.service; import com.taosdata.taosdemo.domain.FieldMeta; import com.taosdata.taosdemo.domain.SuperTableMeta; import com.taosdata.taosdemo.domain.TagMeta; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; @@ -10,7 +13,7 @@ import java.util.List; public class SuperTableServiceTest { - private SuperTableService service; + private static SuperTableService service; @Test public void testCreate() { @@ -29,4 +32,15 @@ public class SuperTableServiceTest { service.create(superTableMeta); } + @BeforeClass + public static void beforeClass() throws ClassNotFoundException { + Class.forName("com.taosdata.jdbc.TSDBDriver"); + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/?charset=UTF-8&locale=en_US.UTF-8&timezone=UTC-8"); + config.setUsername("root"); + config.setPassword("taosdata"); + HikariDataSource dataSource = new HikariDataSource(config); + service = new SuperTableService(dataSource); + } + } \ No newline at end of file diff --git a/docs/examples/java/src/main/java/com/taos/example/DruidDemo.java b/docs/examples/java/src/main/java/com/taos/example/DruidDemo.java index a366efd419..8fbf33ef6d 100644 --- a/docs/examples/java/src/main/java/com/taos/example/DruidDemo.java +++ b/docs/examples/java/src/main/java/com/taos/example/DruidDemo.java @@ -1,4 +1,4 @@ -package com.taosdata.example; +package com.taos.example; import com.alibaba.druid.pool.DruidDataSource; @@ -8,11 +8,11 @@ import java.sql.Statement; public class DruidDemo { // ANCHOR: connection_pool public static void main(String[] args) throws Exception { - String url = "jdbc:TAOS://127.0.0.1:6030/log"; + String url = "jdbc:TAOS-WS://127.0.0.1:6041/log"; DruidDataSource dataSource = new DruidDataSource(); // jdbc properties - dataSource.setDriverClassName("com.taosdata.jdbc.TSDBDriver"); + dataSource.setDriverClassName("com.taosdata.jdbc.ws.WebSocketDriver"); dataSource.setUrl(url); dataSource.setUsername("root"); dataSource.setPassword("taosdata"); diff --git a/docs/examples/java/src/main/java/com/taos/example/GeometryDemo.java b/docs/examples/java/src/main/java/com/taos/example/GeometryDemo.java index 036125e7ea..4045a96642 100644 --- a/docs/examples/java/src/main/java/com/taos/example/GeometryDemo.java +++ b/docs/examples/java/src/main/java/com/taos/example/GeometryDemo.java @@ -144,8 +144,9 @@ public class GeometryDemo { private void executeQuery(String sql) { long start = System.currentTimeMillis(); - try (Statement statement = connection.createStatement()) { - ResultSet resultSet = statement.executeQuery(sql); + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + long end = System.currentTimeMillis(); printSql(sql, true, (end - start)); diff --git a/docs/examples/java/src/main/java/com/taos/example/HikariDemo.java b/docs/examples/java/src/main/java/com/taos/example/HikariDemo.java index 50b20fdb0c..e7a90276d7 100644 --- a/docs/examples/java/src/main/java/com/taos/example/HikariDemo.java +++ b/docs/examples/java/src/main/java/com/taos/example/HikariDemo.java @@ -1,4 +1,4 @@ -package com.taosdata.example; +package com.taos.example; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -11,7 +11,7 @@ public class HikariDemo { public static void main(String[] args) throws Exception { HikariConfig config = new HikariConfig(); // jdbc properties - config.setJdbcUrl("jdbc:TAOS://127.0.0.1:6030/log"); + config.setJdbcUrl("jdbc:TAOS-WS://127.0.0.1:6041/log"); config.setUsername("root"); config.setPassword("taosdata"); // connection pool configurations diff --git a/docs/examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java b/docs/examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java index 4c9368288d..11b234c4e0 100644 --- a/docs/examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java +++ b/docs/examples/java/src/main/java/com/taos/example/TelnetLineProtocolExample.java @@ -39,6 +39,7 @@ public class TelnetLineProtocolExample { createDatabase(conn); SchemalessWriter writer = new SchemalessWriter(conn); writer.write(lines, SchemalessProtocolType.TELNET, SchemalessTimestampType.NOT_CONFIGURED); + writer.close(); } } From e5fdd98075ad46a09e2432d4bb41cf4338c54610 Mon Sep 17 00:00:00 2001 From: kailixu Date: Sat, 9 Nov 2024 09:11:21 +0800 Subject: [PATCH 53/55] chore: adjust test case of grant --- tests/system-test/0-others/grant.py | 222 ++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 tests/system-test/0-others/grant.py diff --git a/tests/system-test/0-others/grant.py b/tests/system-test/0-others/grant.py new file mode 100644 index 0000000000..9e54d9ca37 --- /dev/null +++ b/tests/system-test/0-others/grant.py @@ -0,0 +1,222 @@ +from ssl import ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE +import taos +import sys +import time +import os + +from util.log import * +from util.sql import * +from util.cases import * +from util.dnodes import * +from util.dnodes import TDDnodes +from util.dnodes import TDDnode +import time +import socket +import subprocess + +class MyDnodes(TDDnodes): + def __init__(self ,dnodes_lists): + super(MyDnodes,self).__init__() + self.dnodes = dnodes_lists # dnode must be TDDnode instance + if platform.system().lower() == 'windows': + self.simDeployed = True + else: + self.simDeployed = False + +class TDTestCase: + noConn = True + def getTDinternalPath(): + path_parts = os.getcwd().split(os.sep) + try: + tdinternal_index = path_parts.index("TDinternal") + except ValueError: + raise ValueError("The specified directory 'TDinternal' was not found in the path.") + return os.sep.join(path_parts[:tdinternal_index + 1]) + + def init(self, conn, logSql, replicaVar=1): + tdLog.debug(f"start to excute {__file__}") + self.TDDnodes = None + self.depoly_cluster(5) + self.master_dnode = self.TDDnodes.dnodes[0] + self.host=self.master_dnode.cfgDict["fqdn"] + conn1 = taos.connect(self.master_dnode.cfgDict["fqdn"] , config=self.master_dnode.cfgDir) + tdSql.init(conn1.cursor(), True) + self.TDinternal = TDTestCase.getTDinternalPath() + self.workPath = os.path.join(self.TDinternal, "debug", "build", "bin") + tdLog.info(self.workPath) + + def getBuildPath(self): + selfPath = os.path.dirname(os.path.realpath(__file__)) + + if ("community" in selfPath): + projPath = selfPath[:selfPath.find("community")] + else: + projPath = selfPath[:selfPath.find("tests")] + + for root, dirs, files in os.walk(projPath): + if ("taosd" in files or "taosd.exe" in files): + rootRealPath = os.path.dirname(os.path.realpath(root)) + if ("packaging" not in rootRealPath): + buildPath = root[:len(root) - len("/build/bin")] + break + return buildPath + + def depoly_cluster(self ,dnodes_nums): + + testCluster = False + valgrind = 0 + hostname = socket.gethostname() + dnodes = [] + start_port = 6030 + for num in range(1, dnodes_nums+1): + dnode = TDDnode(num) + dnode.addExtraCfg("firstEp", f"{hostname}:{start_port}") + dnode.addExtraCfg("fqdn", f"{hostname}") + dnode.addExtraCfg("serverPort", f"{start_port + (num-1)*100}") + dnode.addExtraCfg("monitorFqdn", hostname) + dnode.addExtraCfg("monitorPort", 7043) + dnodes.append(dnode) + + self.TDDnodes = MyDnodes(dnodes) + self.TDDnodes.init("") + self.TDDnodes.setTestCluster(testCluster) + self.TDDnodes.setValgrind(valgrind) + + self.TDDnodes.setAsan(tdDnodes.getAsan()) + self.TDDnodes.stopAll() + for dnode in self.TDDnodes.dnodes: + self.TDDnodes.deploy(dnode.index,{}) + + for dnode in self.TDDnodes.dnodes: + self.TDDnodes.starttaosd(dnode.index) + + # create cluster + for dnode in self.TDDnodes.dnodes[1:]: + # print(dnode.cfgDict) + dnode_id = dnode.cfgDict["fqdn"] + ":" +dnode.cfgDict["serverPort"] + dnode_first_host = dnode.cfgDict["firstEp"].split(":")[0] + dnode_first_port = dnode.cfgDict["firstEp"].split(":")[-1] + cmd = f"{self.getBuildPath()}/build/bin/taos -h {dnode_first_host} -P {dnode_first_port} -s \"create dnode \\\"{dnode_id}\\\"\"" + print(cmd) + os.system(cmd) + + time.sleep(2) + tdLog.info(" create cluster done! ") + + def s0_five_dnode_one_mnode(self): + tdSql.query("select * from information_schema.ins_dnodes;") + tdSql.checkData(0,1,'%s:6030'%self.host) + tdSql.checkData(4,1,'%s:6430'%self.host) + tdSql.checkData(0,4,'ready') + tdSql.checkData(4,4,'ready') + tdSql.query("select * from information_schema.ins_mnodes;") + tdSql.checkData(0,1,'%s:6030'%self.host) + tdSql.checkData(0,2,'leader') + tdSql.checkData(0,3,'ready') + tdSql.error("create mnode on dnode 1;") + tdSql.error("drop mnode on dnode 1;") + tdSql.execute("create database if not exists audit"); + tdSql.execute("use audit"); + tdSql.execute("create table operations(ts timestamp, c0 int primary key,c1 bigint,c2 int,c3 float,c4 double) tags(t0 bigint unsigned)"); + tdSql.execute("create table t_operations_abc using operations tags(1)"); + tdSql.execute("drop database if exists db") + tdSql.execute("create database if not exists db replica 1") + tdSql.execute("use db") + tdSql.execute("create table stb0(ts timestamp, c0 int primary key,c1 bigint,c2 int,c3 float,c4 double) tags(t0 bigint unsigned)"); + tdSql.execute("create table ctb0 using stb0 tags(0)"); + tdSql.execute("create stream streams1 trigger at_once IGNORE EXPIRED 0 IGNORE UPDATE 0 into streamt as select _wstart, count(*) c1, count(c2) c2 , sum(c3) c3 , max(c4) c4 from stb0 interval(10s)"); + tdSql.execute("create topic topic_stb_column as select ts, c3 from stb0"); + tdSql.execute("create topic topic_stb_all as select ts, c1, c2, c3 from stb0"); + tdSql.execute("create topic topic_stb_function as select ts, abs(c1), sin(c2) from stb0"); + tdSql.execute("create view view1 as select * from stb0"); + + def getConnection(self, dnode): + host = dnode.cfgDict["fqdn"] + port = dnode.cfgDict["serverPort"] + config_dir = dnode.cfgDir + return taos.connect(host=host, port=int(port), config=config_dir) + + def s1_check_alive(self): + # check cluster alive + tdLog.printNoPrefix("======== test cluster alive: ") + tdSql.checkDataLoop(0, 0, 1, "show cluster alive;", 20, 0.5) + + tdSql.query("show db.alive;") + tdSql.checkData(0, 0, 1) + + def s2_check_show_grants_ungranted(self): + tdLog.printNoPrefix("======== test show grants ungranted: ") + self.infoPath = os.path.join(self.workPath, ".clusterInfo") + infoFile = open(self.infoPath, "w") + try: + tdSql.query(f'select create_time,expire_time,version from information_schema.ins_cluster;') + tdSql.checkEqual(len(tdSql.queryResult), 1) + infoFile.write(";".join(map(str, tdSql.queryResult[0])) + "\n") + tdSql.query(f'show cluster machines;') + tdSql.checkEqual(len(tdSql.queryResult), 1) + infoFile.write(";".join(map(str,tdSql.queryResult[0])) + "\n") + tdSql.query(f'show grants;') + tdSql.checkEqual(len(tdSql.queryResult), 1) + infoFile.write(";".join(map(str,tdSql.queryResult[0])) + "\n") + tdSql.query(f'show grants full;') + tdSql.checkEqual(len(tdSql.queryResult), 31) + + if infoFile: + infoFile.flush() + + files_and_dirs = os.listdir(f'{self.workPath}') + print(f"files_and_dirs: {files_and_dirs}") + + process = subprocess.Popen(f'{self.workPath}{os.sep}grantTest', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = process.communicate() + output = output.decode(encoding="utf-8") + error = error.decode(encoding="utf-8") + print(f"code: {process.returncode}") + print(f"error:\n{error}") + tdSql.checkEqual(process.returncode, 0) + tdSql.checkEqual(error, "") + lines = output.splitlines() + for line in lines: + if line.startswith("code:"): + fields = line.split(":") + tdSql.error(f"{fields[2]}", int(fields[1]), fields[3]) + except Exception as e: + if os.path.exists(self.infoPath): + os.remove(self.infoPath) + raise Exception(repr(e)) + finally: + if infoFile: + infoFile.close() + + def s3_check_show_grants_granted(self): + tdLog.printNoPrefix("======== test show grants granted: ") + try: + process = subprocess.Popen(f'{self.workPath}{os.sep}grantTest 1', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, error = process.communicate() + output = output.decode(encoding="utf-8") + error = error.decode(encoding="utf-8") + print(f"code: {process.returncode}") + print(f"error:\n{error}") + print(f"output:\n{output}") + tdSql.checkEqual(process.returncode, 0) + except Exception as e: + raise Exception(repr(e)) + finally: + if os.path.exists(self.infoPath): + os.remove(self.infoPath) + + def run(self): + # print(self.master_dnode.cfgDict) + # keep the order of following steps + self.s0_five_dnode_one_mnode() + self.s1_check_alive() + self.s2_check_show_grants_ungranted() + self.s3_check_show_grants_granted() + + + def stop(self): + tdSql.close() + tdLog.success(f"{__file__} successfully executed") + +tdCases.addLinux(__file__, TDTestCase()) +tdCases.addWindows(__file__, TDTestCase()) From 5d0f3206d5af86e62571310c17a1e3ed446c5f13 Mon Sep 17 00:00:00 2001 From: Shengliang Guan Date: Sat, 9 Nov 2024 11:27:04 +0800 Subject: [PATCH 54/55] doc: minor changes --- docs/zh/08-operation/16-security.md | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/zh/08-operation/16-security.md b/docs/zh/08-operation/16-security.md index 1c8ad2200f..e3cd72d9dc 100644 --- a/docs/zh/08-operation/16-security.md +++ b/docs/zh/08-operation/16-security.md @@ -27,21 +27,21 @@ SHOW USERS; ALTER USER TEST DROP HOST HOST_NAME1 ``` 说明 -1. 开源版和企业版本都能添加成功,且可以查询到,但是开源版本不会对IP做任何限制。 -2. create user u_write pass 'taosdata1' host 'iprange1','iprange2', 可以一次添加多个iprange, 服务端会做去重,去重的逻辑是需要iprange 完全一样 -3. 默认会把127.0.0.1 添加到白名单列表,且在白名单列表可以查询 -4. 集群的节点IP集合会自动添加到白名单列表,但是查询不到。 -5. taosadaper 和 taosd 不在一个机器的时候,需要把taosadaper IP手动添加到taosd 白名单列表中 -6. 集群情况下,各个节点 enableWhiteList 成一样,或者全为false,或者全为true, 要不然集群无法启动 -7. 白名单变更生效时间1s,不超过2s, 每次变更对收发性能有些微影响(多一次判断,可以忽略),变更完之后、影响忽略不计, 变更过程中对集群没有影响,对正在访问客户端也没有影响(假设这些客户端的IP包含在white list内) -8. 如果添加两个ip range, 192.168.1.1/16(假设为A), 192.168.1.1/24(假设为B), 严格来说,A包含了B,但是考虑情况太复杂,并不会对A和B做合并 -9. 要删除的时候,必须严格匹配。 也就是如果添加的是192.168.1.1/24, 要删除也是192.168.1.1/24 -10. 只有root 才有权限对其他用户增删ip white list -11. 兼容之前的版本,但是不支持从当前版本回退到之前版本 -12. x.x.x.x/32 和x.x.x.x 属于同一个iprange, 显示为x.x.x.x -13. 如果客户端拿到的 0.0.0.0/0, 说明没有开启白名单。 -14. 如果白名单发生了改变, 客户端会在heartbeat里检测到。 -15. 针对一个user, 添加的IP个数上限是2048 +- 开源版和企业版本都能添加成功,且可以查询到,但是开源版本不会对 IP 做任何限制。 +- create user u_write pass 'taosdata1' host 'iprange1','iprange2', 可以一次添加多个 iprange, 服务端会做去重,去重的逻辑是需要 iprange 完全一样 +- 默认会把 127.0.0.1 添加到白名单列表,且在白名单列表可以查询 +- 集群的节点 IP 集合会自动添加到白名单列表,但是查询不到。 +- taosadaper 和 taosd 不在一个机器的时候,需要把 taosadaper IP 手动添加到 taosd 白名单列表中 +- 集群情况下,各个节点 enableWhiteList 成一样,或者全为 false,或者全为 true, 要不然集群无法启动 +- 白名单变更生效时间 1s,不超过 2s, 每次变更对收发性能有些微影响(多一次判断,可以忽略),变更完之后、影响忽略不计, 变更过程中对集群没有影响,对正在访问客户端也没有影响(假设这些客户端的 IP 包含在 white list 内) +- 如果添加两个 ip range, 192.168.1.1/16(假设为 A), 192.168.1.1/24(假设为 B), 严格来说,A 包含了 B,但是考虑情况太复杂,并不会对 A 和 B 做合并 +- 要删除的时候,必须严格匹配。 也就是如果添加的是 192.168.1.1/24, 要删除也是 192.168.1.1/24 +- 只有 root 才有权限对其他用户增删 ip white list +- 兼容之前的版本,但是不支持从当前版本回退到之前版本 +- x.x.x.x/32 和 x.x.x.x 属于同一个 iprange, 显示为 x.x.x.x +- 如果客户端拿到的 0.0.0.0/0, 说明没有开启白名单 +- 如果白名单发生了改变, 客户端会在 heartbeat 里检测到。 +- 针对一个 user, 添加的 IP 个数上限是 2048 ## 审计日志 From cc91f7e73315555329ff415c69c1367f8f168357 Mon Sep 17 00:00:00 2001 From: Haojun Liao Date: Sun, 10 Nov 2024 09:58:02 +0800 Subject: [PATCH 55/55] doc: fix error. --- docs/zh/06-advanced/06-data-analysis/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/06-advanced/06-data-analysis/index.md b/docs/zh/06-advanced/06-data-analysis/index.md index 5af2e2194b..c04b0b47bd 100644 --- a/docs/zh/06-advanced/06-data-analysis/index.md +++ b/docs/zh/06-advanced/06-data-analysis/index.md @@ -10,7 +10,7 @@ TDgpt 运行在部署于 TDengine 集群中的 Analysis Node (ANode)中。每个 如下是数据分析的技术架构示意图。 -TDgpt架构图 +TDgpt架构图 通过注册指令语句,将 ANode 注册到 MNode 中就加入到 TDengine 集群,查询会按需向其请求数据分析服务。请求服务通过 VNode 直接向 ANode 发起,用户则可以通过 SQL 语句直接调用 ANode 提供的服务。