Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
39 changes: 39 additions & 0 deletions include/libs/parser/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@ extern "C" {
#include "query.h"
#include "querynodes.h"

#include <syslog.h>
Comment thread
freemine marked this conversation as resolved.
Outdated

#if 0 /* { */
#if 0 /* { */
#define D(fmt, ...) \
fprintf(stderr, "@[%p]%s[%d]:%s():" fmt "\n", \
(void*)(uintptr_t)taosThreadSelf(), __FILE__, __LINE__, __func__, \
##__VA_ARGS__)

#define A(expr, fmt, ...) \
if (!(expr)) { \
D("assert `%s` failure:" fmt "", #expr, ##__VA_ARGS__); \
abort(); \
}
#else /* }{ */
#define D(fmt, ...) \
syslog(LOG_DEBUG, "@[%p]%s[%d]:%s():" fmt "\n", \
(void*)(uintptr_t)taosThreadSelf(), __FILE__, __LINE__, __func__, \
##__VA_ARGS__)

#define A(expr, fmt, ...) \
if (!(expr)) { \
D("assert `%s` failure:" fmt "", #expr, ##__VA_ARGS__); \
abort(); \
}
#endif /* } */
#endif /* } */
Comment thread
freemine marked this conversation as resolved.
Outdated

#define PAR_ERR_RET(c) \
do { \
int32_t _code = c; \
Expand Down Expand Up @@ -165,7 +193,18 @@ typedef struct SParseContext {
void* charsetCxt;
} SParseContext;

typedef struct SPureInsertParserCtx {
int nr_params;

char buf[512];
} SPureInsertParserCtx;

int32_t qParseSql(SParseContext* pCxt, SQuery** pQuery);
bool qIsLiteralSql(const char* pStr);

// NOTE: only for insert into [db.]? (...) values (...)
int32_t qPureParseInsert(SPureInsertParserCtx *pCtx, const char *pStr);

bool qIsInsertValuesSql(const char* pStr, size_t length);
bool qIsUpdateSetSql(const char* pStr, size_t length, SName* pTableName, int32_t acctId, const char* dbName,
char* msgBuf, int32_t msgBufLen, int* pCode);
Expand Down
6 changes: 5 additions & 1 deletion source/client/inc/clientInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,8 @@ typedef struct SRequestObj {
int32_t execPhase; // EQueryExecPhase
int64_t phaseStartTime; // when current phase started, ms
int8_t secureDelete;

TAOS_STMT2 *literal_by_stmt2; // reference only
Comment thread
freemine marked this conversation as resolved.
} SRequestObj;

typedef struct SSyncQueryParam {
Expand All @@ -387,9 +389,11 @@ void syncCatalogFn(SMetaData* pResult, void* param, int32_t code);
TAOS_RES* taosQueryImpl(TAOS* taos, const char* sql, bool validateOnly, int8_t source);
TAOS_RES* taosQueryImplWithReqid(TAOS* taos, const char* sql, bool validateOnly, int64_t reqid);

void taosAsyncExecLiteral(TAOS_STMT2 *stmt);

void taosAsyncQueryImpl(uint64_t connId, const char* sql, __taos_async_fn_t fp, void* param, bool validateOnly,
int8_t source);
void taosAsyncQueryImplWithReqid(uint64_t connId, const char* sql, __taos_async_fn_t fp, void* param, bool validateOnly,
void taosAsyncQueryImplWithReqid(TAOS_STMT2 *stmt, uint64_t connId, const char* sql, __taos_async_fn_t fp, void* param, bool validateOnly,
int64_t reqid);
void taosAsyncFetchImpl(SRequestObj* pRequest, __taos_async_fn_t fp, void* param);
int32_t clientParseSql(void* param, const char* dbName, const char* sql, bool parseOnly, const char* effectiveUser,
Expand Down
18 changes: 18 additions & 0 deletions source/client/inc/clientStmt2.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ STableDataCxt *pCurrBlock;
SSubmitTbData *pCurrTbData;
} SStmtExecInfo;
*/
typedef struct SStmt2LiteralCtx {
tsem_t sem;

int32_t code;

uint8_t sem_valid:1;
uint8_t prepared:1;
uint8_t executed:1;
} SStmt2LiteralCtx;

typedef struct {
bool stbInterlaceMode;
STMT_TYPE type;
Expand Down Expand Up @@ -176,6 +186,10 @@ typedef struct {
bool asyncResultAvailable;
SStmtStatInfo stat;
SArray* pVgDataBlocksForRetry; // SArray<SVgDataBlocks*> saved serialized data for NEED_CLIENT_HANDLE_ERROR retry

char msgBuf[128];
SStmt2LiteralCtx ctx;
uint8_t literal:1;
} STscStmt2;
/*
extern char *gStmtStatusStr[];
Expand Down Expand Up @@ -256,6 +270,7 @@ TAOS_STMT2 *stmtInit2(STscObj *taos, TAOS_STMT2_OPTION *pOptions);
int stmtClose2(TAOS_STMT2 *stmt);
int stmtExec2(TAOS_STMT2 *stmt, int *affected_rows);
int stmtPrepare2(TAOS_STMT2 *stmt, const char *sql, unsigned long length);
int stmtBindLiteral2(TAOS_STMT2 *stmt);
int stmtSetTbName2(TAOS_STMT2 *stmt, const char *tbName);
int stmtSetTbTags2(TAOS_STMT2 *stmt, TAOS_STMT2_BIND *tags, SVCreateTbReq **pCreateTbReq);
int stmtCheckTags2(TAOS_STMT2 *stmt, SVCreateTbReq **pCreateTbReq);
Expand All @@ -271,6 +286,9 @@ int stmtAsyncBindThreadFunc(void *args);
void stmtBuildErrorMsg(STscStmt2 *pStmt, const char *msg);
int32_t stmtBuildErrorMsgWithCode(STscStmt2 *pStmt, const char *msg, int32_t errorCode);


int stmtIsLiteral(TAOS_STMT2 *stmt);

#ifdef __cplusplus
}
#endif
Expand Down
43 changes: 26 additions & 17 deletions source/client/src/clientImpl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3247,7 +3247,7 @@ void taosAsyncQueryImpl(uint64_t connId, const char* sql, __taos_async_fn_t fp,
doAsyncQuery(pRequest, false);
}

void taosAsyncQueryImplWithReqid(uint64_t connId, const char* sql, __taos_async_fn_t fp, void* param, bool validateOnly,
void taosAsyncQueryImplWithReqid(TAOS_STMT2 *stmt, uint64_t connId, const char* sql, __taos_async_fn_t fp, void* param, bool validateOnly,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

stmt的函数单独封装,不要和query耦合在一起

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

just don't get a clear solutions, otherwise i have to hard-copy the whole logic in the block.
or, what suggestions might be here?

int64_t reqid) {
if (sql == NULL || NULL == fp) {
terrno = TSDB_CODE_INVALID_PARA;
Expand Down Expand Up @@ -3276,6 +3276,8 @@ void taosAsyncQueryImplWithReqid(uint64_t connId, const char* sql, __taos_async_
return;
}

pRequest->literal_by_stmt2 = stmt;

code = connCheckAndUpateMetric(connId);

if (code != TSDB_CODE_SUCCESS) {
Expand Down Expand Up @@ -3347,7 +3349,7 @@ TAOS_RES* taosQueryImplWithReqid(TAOS* taos, const char* sql, bool validateOnly,
return NULL;
}

taosAsyncQueryImplWithReqid(*(int64_t*)taos, sql, syncQueryFn, param, validateOnly, reqid);
taosAsyncQueryImplWithReqid(NULL, *(int64_t*)taos, sql, syncQueryFn, param, validateOnly, reqid);
code = tsem_wait(&param->sem);
if (TSDB_CODE_SUCCESS != code) {
taosMemoryFree(param);
Expand Down Expand Up @@ -3460,29 +3462,36 @@ void doRequestCallback(SRequestObj* pRequest, int32_t code) {
pRequest->inCallback = true;

int64_t this = pRequest->self;
if (tsQueryTbNotExistAsEmpty && TD_RES_QUERY(&pRequest->resType) && pRequest->isQuery &&
(code == TSDB_CODE_PAR_TABLE_NOT_EXIST || code == TSDB_CODE_TDB_TABLE_NOT_EXIST)) {
code = TSDB_CODE_SUCCESS;
pRequest->type = TSDB_SQL_RETRIEVE_EMPTY_RESULT;
if (pRequest->code == TSDB_CODE_PAR_TABLE_NOT_EXIST || pRequest->code == TSDB_CODE_TDB_TABLE_NOT_EXIST) {
pRequest->code = TSDB_CODE_SUCCESS;
if (pRequest->msgBuf != NULL && pRequest->msgBufLen > 0) {
pRequest->msgBuf[0] = '\0';

if (!pRequest->literal_by_stmt2) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[medium/high] queryTableNotExistAsEmpty is silently bypassed for literal stmt2

The tsQueryTbNotExistAsEmpty option (converting TABLE_NOT_EXIST into an empty result set) is skipped when pRequest->literal_by_stmt2 is non-NULL. The same SELECT query through taos_query returns an empty result set; through literal stmt2 it returns an error. This behavioral difference can silently break JDBC/ODBC applications that rely on this option.

Fix direction: Preserve queryTableNotExistAsEmpty handling for literal stmt2 inside the if (!pRequest->literal_by_stmt2) block, unless the divergence is intentional and documented.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

同上,stmt函数单独封装,不要和query耦合

if (tsQueryTbNotExistAsEmpty && TD_RES_QUERY(&pRequest->resType) && pRequest->isQuery &&
(code == TSDB_CODE_PAR_TABLE_NOT_EXIST || code == TSDB_CODE_TDB_TABLE_NOT_EXIST)) {
code = TSDB_CODE_SUCCESS;
pRequest->type = TSDB_SQL_RETRIEVE_EMPTY_RESULT;
if (pRequest->code == TSDB_CODE_PAR_TABLE_NOT_EXIST || pRequest->code == TSDB_CODE_TDB_TABLE_NOT_EXIST) {
pRequest->code = TSDB_CODE_SUCCESS;
if (pRequest->msgBuf != NULL && pRequest->msgBufLen > 0) {
pRequest->msgBuf[0] = '\0';
}
}
}
}

tscDebug("QID:0x%" PRIx64 ", taos_query end, req:0x%" PRIx64 ", res:%p", pRequest->requestId, pRequest->self,
pRequest);
tscDebug("QID:0x%" PRIx64 ", taos_query end, req:0x%" PRIx64 ", res:%p", pRequest->requestId, pRequest->self,
pRequest);
}

if (pRequest->body.queryFp != NULL) {
pRequest->body.queryFp(((SSyncQueryParam*)pRequest->body.interParam)->userParam, pRequest, code);
}

SRequestObj* pReq = acquireRequest(this);
if (pReq != NULL) {
pReq->inCallback = false;
(void)releaseRequest(this);
if (!pRequest->literal_by_stmt2) {
SRequestObj* pReq = acquireRequest(this);
if (pReq != NULL) {
pReq->inCallback = false;
(void)releaseRequest(this);
}
} else {
pRequest->inCallback = false;
}
}

Expand Down
69 changes: 50 additions & 19 deletions source/client/src/clientMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,7 @@ void taos_query_a(TAOS *taos, const char *sql, __taos_async_fn_t fp, void *param

void taos_query_a_with_reqid(TAOS *taos, const char *sql, __taos_async_fn_t fp, void *param, int64_t reqid) {
int64_t connId = *(int64_t *)taos;
taosAsyncQueryImplWithReqid(connId, sql, fp, param, false, reqid);
taosAsyncQueryImplWithReqid(NULL, connId, sql, fp, param, false, reqid);
}

int32_t createParseContext(const SRequestObj *pRequest, SParseContext **pCxt, SSqlCallbackWrapper *pWrapper) {
Expand Down Expand Up @@ -2089,24 +2089,8 @@ int32_t prepareAndParseSqlSyntax(SSqlCallbackWrapper **ppWrapper, SRequestObj *p
return code;
}

void doAsyncQuery(SRequestObj *pRequest, bool updateMetaForce) {
SSqlCallbackWrapper *pWrapper = NULL;
int32_t code = TSDB_CODE_SUCCESS;

CLIENT_UPDATE_REQUEST_PHASE_IF_CHANGED(pRequest, QUERY_PHASE_PARSE);

if (pRequest->retry++ > REQUEST_TOTAL_EXEC_TIMES) {
code = pRequest->prevCode;
terrno = code;
pRequest->code = code;
tscDebug("req:0x%" PRIx64 ", call sync query cb with code:%s", pRequest->self, tstrerror(code));
doRequestCallback(pRequest, code);
return;
}

if (TSDB_CODE_SUCCESS == code) {
code = prepareAndParseSqlSyntax(&pWrapper, pRequest, updateMetaForce);
}
static void doAsyncExec(SRequestObj *pRequest, int32_t code) {
SSqlCallbackWrapper *pWrapper = pRequest->pWrapper;

if (TSDB_CODE_SUCCESS == code) {
pRequest->stmtType = pRequest->pQuery->pRoot->type;
Expand Down Expand Up @@ -2146,6 +2130,39 @@ void doAsyncQuery(SRequestObj *pRequest, bool updateMetaForce) {
}
}

void doAsyncQuery(SRequestObj *pRequest, bool updateMetaForce) {
SSqlCallbackWrapper *pWrapper = NULL;
int32_t code = TSDB_CODE_SUCCESS;

CLIENT_UPDATE_REQUEST_PHASE_IF_CHANGED(pRequest, QUERY_PHASE_PARSE);

if (pRequest->retry++ > REQUEST_TOTAL_EXEC_TIMES) {
code = pRequest->prevCode;
terrno = code;
pRequest->code = code;
tscDebug("req:0x%" PRIx64 ", call sync query cb with code:%s", pRequest->self, tstrerror(code));
doRequestCallback(pRequest, code);
return;
}

if (TSDB_CODE_SUCCESS == code) {
code = prepareAndParseSqlSyntax(&pWrapper, pRequest, updateMetaForce);
}

if (pRequest->literal_by_stmt2) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

问题SRequestObj.literal_by_stmt2 是指向 TAOS_STMT2* 的裸指针(注释为 // reference only),无所有权/生命周期协议。在异步 exec 路径中,doAsyncQuery 会解引用该指针检查 ctx.prepared。若 stmt 在请求飞行期间被 close,该指针即变为悬空指针。

证据

  • clientInt.hTAOS_STMT2 *literal_by_stmt2; // reference only
  • clientMain.c:doAsyncQueryTAOS_STMT2* stmt = pRequest->literal_by_stmt2; STscStmt2* pStmt = (STscStmt2*)stmt;

与本 MR 的关联:本 MR 引入该请求→stmt 反向引用以实现 literal exec-direct 功能。

修复建议:在赋值 literal_by_stmt2 时对 stmt 加引用计数,请求完成后释放;或在 stmt reset/close 时显式将 pRequest->literal_by_stmt2 置 NULL(需要加锁)。

doRequestCallback(pRequest, code);
return;
}

assert(pWrapper == pRequest->pWrapper);
doAsyncExec(pRequest, code);
}

void taosAsyncExecLiteral(TAOS_STMT2 *stmt) {
STscStmt2* pStmt = (STscStmt2*)stmt;
doAsyncExec(pStmt->exec.pRequest, pStmt->ctx.code);
}

void restartAsyncQuery(SRequestObj *pRequest, int32_t code) {
tscInfo("restart request:%s p:%p", pRequest->sqlstr, pRequest);
SRequestObj *pUserReq = pRequest;
Expand Down Expand Up @@ -2815,6 +2832,10 @@ int taos_stmt2_bind_param(TAOS_STMT2 *stmt, TAOS_STMT2_BINDV *bindv, int32_t col
int32_t code = TSDB_CODE_SUCCESS;
STMT2_DLOG_E("start to bind param");

if (stmtIsLiteral(pStmt)) {
return stmtBindLiteral2(stmt);
}

// check query bind number
bool isQuery = (STMT_TYPE_QUERY == pStmt->sql.type || (pStmt->sql.type == 0 && stmt2IsSelect(stmt)));
if (isQuery) {
Expand Down Expand Up @@ -2908,6 +2929,10 @@ int taos_stmt2_bind_param_a(TAOS_STMT2 *stmt, TAOS_STMT2_BINDV *bindv, int32_t c

STscStmt2 *pStmt = (STscStmt2 *)stmt;

if (stmtIsLiteral(pStmt)) {
return stmtBindLiteral2(stmt);
}

ThreadArgs *args = (ThreadArgs *)taosMemoryMalloc(sizeof(ThreadArgs));
args->stmt = stmt;
args->bindv = bindv;
Expand Down Expand Up @@ -2978,6 +3003,12 @@ int taos_stmt2_get_fields(TAOS_STMT2 *stmt, int *count, TAOS_FIELD_ALL **fields)
STscStmt2 *pStmt = (STscStmt2 *)stmt;
STMT2_DLOG_E("start to get fields");

if (stmtIsLiteral(pStmt)) {
if (count) *count = 0;
if (fields) *fields = NULL;
return TSDB_CODE_SUCCESS;
}

if (STMT_TYPE_INSERT == pStmt->sql.type || STMT_TYPE_MULTI_INSERT == pStmt->sql.type ||
(pStmt->sql.type == 0 && stmt2IsInsert(stmt))) {
return stmtGetStbColFields2(stmt, count, fields);
Expand Down
Loading
Loading