Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions source/client/src/clientStmt2.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include "clientInt.h"
#include "clientLog.h"
#include "tdef.h"
#include "tglobal.h"

#include "clientStmt.h"
#include "clientStmt2.h"
/*
char* gStmtStatusStr[] = {"unknown", "init", "prepare", "settbname", "settags",
"fetchFields", "bind", "bindCol", "addBatch", "exec"};
*/

static FORCE_INLINE int32_t stmtAllocQNodeFromBuf(STableBufInfo* pTblBuf, void** pBuf) {
if (pTblBuf->buffOffset < pTblBuf->buffSize) {
*pBuf = (char*)pTblBuf->pCurBuff + pTblBuf->buffOffset;
Expand Down Expand Up @@ -2489,6 +2491,12 @@ TAOS_RES* stmtUseResult2(TAOS_STMT2* stmt) {
return NULL;
}

if (tsUseAdapter) {
TAOS_RES* res = (TAOS_RES*)pStmt->exec.pRequest;
pStmt->exec.pRequest = NULL;
return res;
}

return pStmt->exec.pRequest;
}

Expand Down
156 changes: 146 additions & 10 deletions source/client/test/stmt2Test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1878,11 +1878,26 @@ TEST(stmt2Case, query) {
TAOS_STMT2_BIND params = {TSDB_DATA_TYPE_TIMESTAMP, &ts, &t64_len, NULL, 1};
TAOS_STMT2_BIND* paramv = &params;
TAOS_STMT2_BINDV bindv = {1, NULL, NULL, &paramv};
code = taos_stmt2_bind_param(stmt, &bindv, -1);
checkError(stmt, code);
for (int i = 0; i < 3; i++) {
code = taos_stmt2_bind_param(stmt, &bindv, -1);
checkError(stmt, code);

taos_stmt2_exec(stmt, NULL);
checkError(stmt, code);
taos_stmt2_exec(stmt, NULL);
checkError(stmt, code);

TAOS_RES* pRes = taos_stmt2_result(stmt);
ASSERT_NE(pRes, nullptr);
TAOS_ROW row = taos_fetch_row(pRes);
ASSERT_NE(row, nullptr);
ASSERT_EQ(strncmp((char*)row[0], "tb1", 3), 0);
ASSERT_EQ(strncmp((char*)row[1], "abc", 3), 0);
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
row = taos_fetch_row(pRes);
ASSERT_NE(row, nullptr);
ASSERT_EQ(strncmp((char*)row[0], "tb2", 3), 0);
ASSERT_EQ(strncmp((char*)row[1], "xyz", 3), 0);
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result from taos_stmt2_result should be freed using taos_free_result to prevent a memory leak. In the loop, each iteration calls taos_stmt2_result which returns a new result object, but only the last one gets implicitly freed when the statement is closed. The previous iterations' result objects are leaked.

Suggested change
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
taos_free_result(pRes);

Copilot uses AI. Check for mistakes.
}

taos_stmt2_close(stmt);
}
Expand Down Expand Up @@ -1913,16 +1928,19 @@ TEST(stmt2Case, query) {
TAOS_STMT2_BIND params = {TSDB_DATA_TYPE_TIMESTAMP, &ts, &t64_len, NULL, 1};
TAOS_STMT2_BIND* paramv = &params;
TAOS_STMT2_BINDV bindv = {1, NULL, NULL, &paramv};
code = taos_stmt2_bind_param(stmt, &bindv, -1);
checkError(stmt, code);

taos_stmt2_exec(stmt, NULL);
checkError(stmt, code);
for (int i = 0; i < 3; i++) {
code = taos_stmt2_bind_param(stmt, &bindv, -1);
checkError(stmt, code);

taos_stmt2_exec(stmt, NULL);
checkError(stmt, code);

tsem_wait(&aa->sem);
}

tsem_wait(&aa->sem);
tsem_destroy(&aa->sem);
taosMemoryFree(aa);

taos_stmt2_close(stmt);
}

Expand Down Expand Up @@ -2018,6 +2036,124 @@ TEST(stmt2Case, query) {
taos_close(taos);
}

TEST(stmt2Case, query_use_adapter) {
TAOS* taos = taos_connect("localhost", "root", "taosdata", "", 0);
ASSERT_NE(taos, nullptr);
int32_t code = taos_options(TSDB_OPTION_USE_ADAPTER, "true");
ASSERT_EQ(code, TSDB_CODE_SUCCESS);
do_query(taos, "drop database if exists stmt2_testdb_37");
do_query(taos, "create database IF NOT EXISTS stmt2_testdb_37");
do_query(taos, "create stable stmt2_testdb_37.stb (ts timestamp, b binary(10)) tags(t1 int, t2 binary(10))");
do_query(taos,
"insert into stmt2_testdb_37.tb1 using stmt2_testdb_37.stb tags(1,'abc') values(1591060628000, "
"'abc'),(1591060628001,'def'),(1591060628002, 'hij')");
do_query(taos,
"insert into stmt2_testdb_37.tb2 using stmt2_testdb_37.stb tags(2,'xyz') values(1591060628000, "
"'abc'),(1591060628001,'def'),(1591060628004, 'hij')");
do_query(taos, "use stmt2_testdb_37");

// sync query
{
TAOS_STMT2_OPTION option = {0, true, true, NULL, NULL};

TAOS_STMT2* stmt = taos_stmt2_init(taos, &option);
ASSERT_NE(stmt, nullptr);

const char* sql = "select tbname,t2,b from stmt2_testdb_37.stb where ts = ? order by tbname";
int code = taos_stmt2_prepare(stmt, sql, 0);
checkError(stmt, code);

for (int i = 0; i < 3; i++) {
int fieldNum = 0;
TAOS_FIELD_ALL* pFields = NULL;
code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields);
checkError(stmt, code);
ASSERT_EQ(fieldNum, 1);

Comment on lines +2066 to +2072
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The taos_stmt2_get_fields call should be moved outside the loop. The field metadata doesn't change between iterations of the same prepared statement, so calling it on every iteration is inefficient and inconsistent with the pattern used in the non-adapter test (lines 1870-1874) where it's called once before the loop.

Suggested change
for (int i = 0; i < 3; i++) {
int fieldNum = 0;
TAOS_FIELD_ALL* pFields = NULL;
code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields);
checkError(stmt, code);
ASSERT_EQ(fieldNum, 1);
int fieldNum = 0;
TAOS_FIELD_ALL* pFields = NULL;
code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields);
checkError(stmt, code);
ASSERT_EQ(fieldNum, 1);
for (int i = 0; i < 3; i++) {

Copilot uses AI. Check for mistakes.
int t64_len = sizeof(int64_t);
int64_t ts = 1591060628000;
TAOS_STMT2_BIND params = {TSDB_DATA_TYPE_TIMESTAMP, &ts, &t64_len, NULL, 1};
TAOS_STMT2_BIND* paramv = &params;
TAOS_STMT2_BINDV bindv = {1, NULL, NULL, &paramv};
code = taos_stmt2_bind_param(stmt, &bindv, -1);
checkError(stmt, code);

taos_stmt2_exec(stmt, NULL);
checkError(stmt, code);

TAOS_RES* pRes = taos_stmt2_result(stmt);
ASSERT_NE(pRes, nullptr);
TAOS_ROW row = taos_fetch_row(pRes);
ASSERT_NE(row, nullptr);
ASSERT_EQ(strncmp((char*)row[0], "tb1", 3), 0);
ASSERT_EQ(strncmp((char*)row[1], "abc", 3), 0);
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
row = taos_fetch_row(pRes);
ASSERT_NE(row, nullptr);
ASSERT_EQ(strncmp((char*)row[0], "tb2", 3), 0);
ASSERT_EQ(strncmp((char*)row[1], "xyz", 3), 0);
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
taos_free_result(pRes);
}

taos_stmt2_close(stmt);
}

// async query with async fetch
{
AsyncArgs* aa = (AsyncArgs*)taosMemoryMalloc(sizeof(AsyncArgs));
aa->async_affected_rows = 0;
ASSERT_EQ(tsem_init(&aa->sem, 0, 0), TSDB_CODE_SUCCESS);

TAOS_STMT2_OPTION option = {0, true, true, stmtAsyncQueryCb, (void*)aa};

TAOS_STMT2* stmt = taos_stmt2_init(taos, &option);
ASSERT_NE(stmt, nullptr);

const char* sql = "select tbname,t2,b from stmt2_testdb_37.stb where ts = ? order by tbname";
int code = taos_stmt2_prepare(stmt, sql, 0);
checkError(stmt, code);

for (int i = 0; i < 3; i++) {
int fieldNum = 0;
TAOS_FIELD_ALL* pFields = NULL;
code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields);
checkError(stmt, code);
ASSERT_EQ(fieldNum, 1);

Comment on lines +2117 to +2123
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The taos_stmt2_get_fields call should be moved outside the loop. The field metadata doesn't change between iterations of the same prepared statement, so calling it on every iteration is inefficient and inconsistent with the pattern used in the non-adapter test (lines 1920-1924) where it's called once before the loop.

Suggested change
for (int i = 0; i < 3; i++) {
int fieldNum = 0;
TAOS_FIELD_ALL* pFields = NULL;
code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields);
checkError(stmt, code);
ASSERT_EQ(fieldNum, 1);
int fieldNum = 0;
TAOS_FIELD_ALL* pFields = NULL;
code = taos_stmt2_get_fields(stmt, &fieldNum, &pFields);
checkError(stmt, code);
ASSERT_EQ(fieldNum, 1);
for (int i = 0; i < 3; i++) {

Copilot uses AI. Check for mistakes.
int t64_len = sizeof(int64_t);
int64_t ts = 1591060628000;
TAOS_STMT2_BIND params = {TSDB_DATA_TYPE_TIMESTAMP, &ts, &t64_len, NULL, 1};
TAOS_STMT2_BIND* paramv = &params;
TAOS_STMT2_BINDV bindv = {1, NULL, NULL, &paramv};
code = taos_stmt2_bind_param(stmt, &bindv, -1);
checkError(stmt, code);

taos_stmt2_exec(stmt, NULL);
checkError(stmt, code);

tsem_wait(&aa->sem);

TAOS_RES* pRes = taos_stmt2_result(stmt);
ASSERT_NE(pRes, nullptr);
TAOS_ROW row = taos_fetch_row(pRes);
ASSERT_NE(row, nullptr);
ASSERT_EQ(strncmp((char*)row[0], "tb1", 3), 0);
ASSERT_EQ(strncmp((char*)row[1], "abc", 3), 0);
ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);
row = taos_fetch_row(pRes);
ASSERT_NE(row, nullptr);
Comment on lines +2144 to +2145
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The assertions for the content of the second fetched row are missing here. The corresponding synchronous test case includes these assertions. To ensure the async query logic is fully tested, please add assertions for the second row's data, similar to the sync test case.

      row = taos_fetch_row(pRes);
      ASSERT_NE(row, nullptr);
      ASSERT_EQ(strncmp((char*)row[0], "tb2", 3), 0);
      ASSERT_EQ(strncmp((char*)row[1], "xyz", 3), 0);
      ASSERT_EQ(strncmp((char*)row[2], "abc", 3), 0);

taos_free_result(pRes);
Comment on lines +2137 to +2146
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result from taos_stmt2_result should be freed using taos_free_result to prevent a memory leak. Each iteration returns a result object that needs to be explicitly freed. Only the last iteration frees its result, leaving the first two iterations' results leaked.

Copilot uses AI. Check for mistakes.
Comment on lines +2144 to +2146
Copy link

Copilot AI Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete result validation: only one row is fetched and validated, but the sync test validates two rows (tb1 and tb2). The second row should also be validated with assertions to ensure consistent test coverage between sync and async modes.

Copilot uses AI. Check for mistakes.
}

tsem_destroy(&aa->sem);
taosMemoryFree(aa);
taos_stmt2_close(stmt);
}
do_query(taos, "drop database if exists stmt2_testdb_37");
taos_close(taos);
}

void asyncSelectError(void* param, TAOS_RES* pRes, int code) {
taosMsleep(500);
// wrong usage 3 : sync fetch in callback
Expand Down
Loading