[td-10564] refactor query statement parse procedure.
This commit is contained in:
parent
0fcbd2f52c
commit
c3881e1a53
|
@ -76,6 +76,336 @@ static int32_t evaluateImpl(tSqlExpr* pExpr, int32_t tsPrecision) {
|
||||||
return TSDB_CODE_SUCCESS;
|
return TSDB_CODE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t validateSqlNode(SSqlNode* pSqlNode, SQueryStmtInfo* pQueryInfo, char* msg, int32_t msgBufLen) {
|
||||||
|
assert(pSqlNode != NULL && (pSqlNode->from == NULL || taosArrayGetSize(pSqlNode->from->list) > 0));
|
||||||
|
|
||||||
|
const char* msg1 = "point interpolation query needs timestamp";
|
||||||
|
const char* msg2 = "too many tables in from clause";
|
||||||
|
const char* msg3 = "start(end) time of query range required or time range too large";
|
||||||
|
const char* msg4 = "interval query not supported, since the result of sub query not include valid timestamp column";
|
||||||
|
const char* msg5 = "only tag query not compatible with normal column filter";
|
||||||
|
const char* msg6 = "not support stddev/percentile/interp in the outer query yet";
|
||||||
|
const char* msg7 = "derivative/twa/irate requires timestamp column exists in subquery";
|
||||||
|
const char* msg8 = "condition missing for join query";
|
||||||
|
const char* msg9 = "not support 3 level select";
|
||||||
|
|
||||||
|
int32_t code = TSDB_CODE_SUCCESS;
|
||||||
|
STableMetaInfo *pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||||
|
if (pTableMetaInfo == NULL) {
|
||||||
|
pTableMetaInfo = tscAddEmptyMetaInfo(pQueryInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* handle the sql expression without from subclause
|
||||||
|
* select server_status();
|
||||||
|
* select server_version();
|
||||||
|
* select client_version();
|
||||||
|
* select database();
|
||||||
|
*/
|
||||||
|
if (pSqlNode->from == NULL) {
|
||||||
|
assert(pSqlNode->fillType == NULL && pSqlNode->pGroupby == NULL && pSqlNode->pWhere == NULL &&
|
||||||
|
pSqlNode->pSortOrder == NULL);
|
||||||
|
return doLocalQueryProcess(pCmd, pQueryInfo, pSqlNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pSqlNode->from->type == SQL_NODE_FROM_SUBQUERY) {
|
||||||
|
clearAllTableMetaInfo(pQueryInfo, false, pSql->self);
|
||||||
|
pQueryInfo->numOfTables = 0;
|
||||||
|
|
||||||
|
// parse the subquery in the first place
|
||||||
|
int32_t numOfSub = (int32_t)taosArrayGetSize(pSqlNode->from->list);
|
||||||
|
for (int32_t i = 0; i < numOfSub; ++i) {
|
||||||
|
// check if there is 3 level select
|
||||||
|
SRelElementPair* subInfo = taosArrayGet(pSqlNode->from->list, i);
|
||||||
|
SSqlNode* p = taosArrayGetP(subInfo->pSubquery, 0);
|
||||||
|
if (p->from->type == SQL_NODE_FROM_SUBQUERY) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg9);
|
||||||
|
}
|
||||||
|
|
||||||
|
code = doValidateSubquery(pSqlNode, i, pSql, pQueryInfo, tscGetErrorMsgPayload(pCmd));
|
||||||
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t timeWindowQuery =
|
||||||
|
(TPARSER_HAS_TOKEN(pSqlNode->interval.interval) || TPARSER_HAS_TOKEN(pSqlNode->sessionVal.gap));
|
||||||
|
TSDB_QUERY_SET_TYPE(pQueryInfo->type, TSDB_QUERY_TYPE_TABLE_QUERY);
|
||||||
|
|
||||||
|
// parse the group by clause in the first place
|
||||||
|
if (validateGroupbyNode(pQueryInfo, pSqlNode->pGroupby, pCmd) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (validateSelectNodeList(pCmd, pQueryInfo, pSqlNode->pSelNodeList, false, timeWindowQuery, true) !=
|
||||||
|
TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo NOT support yet
|
||||||
|
for (int32_t i = 0; i < tscNumOfExprs(pQueryInfo); ++i) {
|
||||||
|
SExprInfo* pExpr = tscExprGet(pQueryInfo, i);
|
||||||
|
int32_t f = pExpr->base.functionId;
|
||||||
|
if (f == TSDB_FUNC_STDDEV || f == TSDB_FUNC_PERCT || f == TSDB_FUNC_INTERP) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((timeWindowQuery || pQueryInfo->stateWindow) && f == TSDB_FUNC_LAST) {
|
||||||
|
pExpr->base.numOfParams = 1;
|
||||||
|
pExpr->base.param[0].i64 = TSDB_ORDER_ASC;
|
||||||
|
pExpr->base.param[0].nType = TSDB_DATA_TYPE_INT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STableMeta* pTableMeta = tscGetMetaInfo(pQueryInfo, 0)->pTableMeta;
|
||||||
|
SSchema* pSchema = tscGetTableColumnSchema(pTableMeta, 0);
|
||||||
|
|
||||||
|
if (pSchema->type != TSDB_DATA_TYPE_TIMESTAMP) {
|
||||||
|
int32_t numOfExprs = (int32_t)tscNumOfExprs(pQueryInfo);
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < numOfExprs; ++i) {
|
||||||
|
SExprInfo* pExpr = tscExprGet(pQueryInfo, i);
|
||||||
|
|
||||||
|
int32_t f = pExpr->base.functionId;
|
||||||
|
if (f == TSDB_FUNC_DERIVATIVE || f == TSDB_FUNC_TWA || f == TSDB_FUNC_IRATE) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the query filter condition info
|
||||||
|
if (pSqlNode->pWhere != NULL) {
|
||||||
|
if (validateWhereNode(pQueryInfo, &pSqlNode->pWhere, pSql) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pQueryInfo->numOfTables > 1) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the interval info
|
||||||
|
if (validateIntervalNode(pSql, pQueryInfo, pSqlNode) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
} else {
|
||||||
|
if (validateSessionNode(pCmd, pQueryInfo, pSqlNode) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the window_state
|
||||||
|
if (validateStateWindowNode(pCmd, pQueryInfo, pSqlNode, false) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTimeWindowQuery(pQueryInfo)) {
|
||||||
|
// check if the first column of the nest query result is timestamp column
|
||||||
|
SColumn* pCol = taosArrayGetP(pQueryInfo->colList, 0);
|
||||||
|
if (pCol->info.type != TSDB_DATA_TYPE_TIMESTAMP) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validateFunctionsInIntervalOrGroupbyQuery(pCmd, pQueryInfo) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable group result mixed up if interval/session window query exists.
|
||||||
|
if (isTimeWindowQuery(pQueryInfo)) {
|
||||||
|
size_t num = taosArrayGetSize(pQueryInfo->pUpstream);
|
||||||
|
for(int32_t i = 0; i < num; ++i) {
|
||||||
|
SQueryInfo* pUp = taosArrayGetP(pQueryInfo->pUpstream, i);
|
||||||
|
pUp->multigroupResult = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the having clause in the first place
|
||||||
|
int32_t joinQuery = (pSqlNode->from != NULL && taosArrayGetSize(pSqlNode->from->list) > 1);
|
||||||
|
if (validateHavingClause(pQueryInfo, pSqlNode->pHaving, pCmd, pSqlNode->pSelNodeList, joinQuery, timeWindowQuery) !=
|
||||||
|
TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((code = validateLimitNode(pCmd, pQueryInfo, pSqlNode, pSql)) != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set order by info
|
||||||
|
if (validateOrderbyNode(pCmd, pQueryInfo, pSqlNode, tscGetTableSchema(pTableMeta)) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((code = doFunctionsCompatibleCheck(pCmd, pQueryInfo, tscGetErrorMsgPayload(pCmd))) != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateFunctionInterBuf(pQueryInfo, false);
|
||||||
|
updateLastScanOrderIfNeeded(pQueryInfo);
|
||||||
|
|
||||||
|
if ((code = validateFillNode(pCmd, pQueryInfo, pSqlNode)) != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pQueryInfo->command = TSDB_SQL_SELECT;
|
||||||
|
|
||||||
|
size_t numOfTables = taosArrayGetSize(pSqlNode->from->list);
|
||||||
|
if (numOfTables > TSDB_MAX_JOIN_TABLE_NUM) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set all query tables, which are maybe more than one.
|
||||||
|
code = doLoadAllTableMeta(pSql, pQueryInfo, pSqlNode, (int32_t) numOfTables);
|
||||||
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSTable = UTIL_TABLE_IS_SUPER_TABLE(pTableMetaInfo);
|
||||||
|
|
||||||
|
int32_t type = isSTable? TSDB_QUERY_TYPE_STABLE_QUERY:TSDB_QUERY_TYPE_TABLE_QUERY;
|
||||||
|
TSDB_QUERY_SET_TYPE(pQueryInfo->type, type);
|
||||||
|
|
||||||
|
// parse the group by clause in the first place
|
||||||
|
if (validateGroupbyNode(pQueryInfo, pSqlNode->pGroupby, pCmd) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
pQueryInfo->onlyHasTagCond = true;
|
||||||
|
// set where info
|
||||||
|
if (pSqlNode->pWhere != NULL) {
|
||||||
|
if (validateWhereNode(pQueryInfo, &pSqlNode->pWhere, pSql) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
pSqlNode->pWhere = NULL;
|
||||||
|
} else {
|
||||||
|
if (taosArrayGetSize(pSqlNode->from->list) > 1) { // Cross join not allowed yet
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, "cross join not supported yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t joinQuery = (pSqlNode->from != NULL && taosArrayGetSize(pSqlNode->from->list) > 1);
|
||||||
|
int32_t timeWindowQuery =
|
||||||
|
(TPARSER_HAS_TOKEN(pSqlNode->interval.interval) || TPARSER_HAS_TOKEN(pSqlNode->sessionVal.gap));
|
||||||
|
|
||||||
|
if (validateSelectNodeList(pCmd, pQueryInfo, pSqlNode->pSelNodeList, joinQuery, timeWindowQuery, false) !=
|
||||||
|
TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSTable && tscQueryTags(pQueryInfo) && pQueryInfo->distinct && !pQueryInfo->onlyHasTagCond) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the window_state
|
||||||
|
if (validateStateWindowNode(pCmd, pQueryInfo, pSqlNode, isSTable) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set order by info
|
||||||
|
if (validateOrderbyNode(pCmd, pQueryInfo, pSqlNode, tscGetTableSchema(pTableMetaInfo->pTableMeta)) !=
|
||||||
|
TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set interval value
|
||||||
|
if (validateIntervalNode(pSql, pQueryInfo, pSqlNode) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tscQueryTags(pQueryInfo)) {
|
||||||
|
SExprInfo* pExpr1 = tscExprGet(pQueryInfo, 0);
|
||||||
|
|
||||||
|
if (pExpr1->base.functionId != TSDB_FUNC_TID_TAG) {
|
||||||
|
if ((pQueryInfo->colCond && taosArrayGetSize(pQueryInfo->colCond) > 0) || IS_TSWINDOW_SPECIFIED(pQueryInfo->window)) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the having clause in the first place
|
||||||
|
if (validateHavingClause(pQueryInfo, pSqlNode->pHaving, pCmd, pSqlNode->pSelNodeList, joinQuery, timeWindowQuery) !=
|
||||||
|
TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* transfer sql functions that need secondary merge into another format
|
||||||
|
* in dealing with super table queries such as: count/first/last
|
||||||
|
*/
|
||||||
|
if (validateSessionNode(pCmd, pQueryInfo, pSqlNode) != TSDB_CODE_SUCCESS) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTimeWindowQuery(pQueryInfo) && (validateFunctionsInIntervalOrGroupbyQuery(pCmd, pQueryInfo) != TSDB_CODE_SUCCESS)) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSTable) {
|
||||||
|
tscTansformFuncForSTableQuery(pQueryInfo);
|
||||||
|
if (hasUnsupportFunctionsForSTableQuery(pCmd, pQueryInfo)) {
|
||||||
|
return TSDB_CODE_TSC_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no result due to invalid query time range
|
||||||
|
if (pQueryInfo->window.skey > pQueryInfo->window.ekey) {
|
||||||
|
pQueryInfo->command = TSDB_SQL_RETRIEVE_EMPTY_RESULT;
|
||||||
|
return TSDB_CODE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTimestampForPointInterpQuery(pQueryInfo)) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of join query, time range is required.
|
||||||
|
if (QUERY_IS_JOIN_QUERY(pQueryInfo->type)) {
|
||||||
|
uint64_t timeRange = (uint64_t)pQueryInfo->window.ekey - pQueryInfo->window.skey;
|
||||||
|
if (timeRange == 0 && pQueryInfo->window.skey == 0) {
|
||||||
|
return parserSetInvalidOperatorMsg(msg, msgBufLen, msg3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((code = validateLimitNode(pCmd, pQueryInfo, pSqlNode, pSql)) != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((code = doFunctionsCompatibleCheck(pCmd, pQueryInfo,tscGetErrorMsgPayload(pCmd))) != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLastScanOrderIfNeeded(pQueryInfo);
|
||||||
|
tscFieldInfoUpdateOffset(pQueryInfo);
|
||||||
|
// updateFunctionInterBuf(pQueryInfo, isSTable);
|
||||||
|
|
||||||
|
if ((code = validateFillNode(pCmd, pQueryInfo, pSqlNode)) != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // set the query info
|
||||||
|
pQueryInfo->projectionQuery = tscIsProjectionQuery(pQueryInfo);
|
||||||
|
pQueryInfo->hasFilter = tscHasColumnFilter(pQueryInfo);
|
||||||
|
pQueryInfo->simpleAgg = isSimpleAggregateRv(pQueryInfo);
|
||||||
|
pQueryInfo->onlyTagQuery = onlyTagPrjFunction(pQueryInfo);
|
||||||
|
pQueryInfo->groupbyColumn = tscGroupbyColumn(pQueryInfo);
|
||||||
|
pQueryInfo->arithmeticOnAgg = tsIsArithmeticQueryOnAggResult(pQueryInfo);
|
||||||
|
pQueryInfo->orderProjectQuery = tscOrderedProjectionQueryOnSTable(pQueryInfo, 0);
|
||||||
|
|
||||||
|
SExprInfo** p = NULL;
|
||||||
|
int32_t numOfExpr = 0;
|
||||||
|
pTableMetaInfo = tscGetMetaInfo(pQueryInfo, 0);
|
||||||
|
code = createProjectionExpr(pQueryInfo, pTableMetaInfo, &p, &numOfExpr);
|
||||||
|
if (pQueryInfo->exprList1 == NULL) {
|
||||||
|
pQueryInfo->exprList1 = taosArrayInit(4, POINTER_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
taosArrayAddBatch(pQueryInfo->exprList1, (void*) p, numOfExpr);
|
||||||
|
tfree(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TSDB_CODE_SUCCESS; // Does not build query message here
|
||||||
|
}
|
||||||
|
|
||||||
int32_t evaluateSqlNode(SSqlNode* pNode, int32_t tsPrecision, char* msg, int32_t msgBufLen) {
|
int32_t evaluateSqlNode(SSqlNode* pNode, int32_t tsPrecision, char* msg, int32_t msgBufLen) {
|
||||||
assert(pNode != NULL && msg != NULL && msgBufLen > 0);
|
assert(pNode != NULL && msg != NULL && msgBufLen > 0);
|
||||||
if (pNode->pWhere == NULL) {
|
if (pNode->pWhere == NULL) {
|
||||||
|
@ -534,15 +864,33 @@ int32_t qParserValidateSqlNode(struct SCatalog* pCatalog, SSqlInfo* pInfo, SQuer
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SMetaReq req = {0};
|
SMetaReq req = {0};
|
||||||
|
SMetaData data = {0};
|
||||||
|
|
||||||
// TODO: check if the qnode info has been cached already
|
// TODO: check if the qnode info has been cached already
|
||||||
req.qNodeEpset = true;
|
req.qNodeEpset = true;
|
||||||
code = qParserExtractRequestedMetaInfo(pInfo, &req, msgBuf, msgBufLen);
|
code = qParserExtractRequestedMetaInfo(pInfo, &req, msgBuf, msgBufLen);
|
||||||
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
// load the meta data from catalog
|
// load the meta data from catalog
|
||||||
|
code = catalogGetMetaData(pCatalog, &req, &data);
|
||||||
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
// evaluate the sqlnode
|
// evaluate the sqlnode
|
||||||
|
STableMeta* pTableMeta = (STableMeta*) taosArrayGetP(data.pTableMeta, 0);
|
||||||
|
assert(pTableMeta != NULL);
|
||||||
|
|
||||||
|
size_t len = taosArrayGetSize(pInfo->list);
|
||||||
|
for(int32_t i = 0; i < len; ++i) {
|
||||||
|
SSqlNode* p = taosArrayGetP(pInfo->list, i);
|
||||||
|
code = evaluateSqlNode(p, pTableMeta->tableInfo.precision, msgBuf, msgBufLen);
|
||||||
|
if (code != TSDB_CODE_SUCCESS) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert the sqlnode into queryinfo
|
// convert the sqlnode into queryinfo
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue