Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
taosdata
TDengine
提交
4c6dad1f
T
TDengine
项目概览
taosdata
/
TDengine
11 个月 前同步成功
通知
1179
Star
22014
Fork
4786
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
TDengine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
4c6dad1f
编写于
11月 11, 2021
作者:
H
Haojun Liao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[td-10564]Fix bug in parser.
上级
86687338
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
74 addition
and
150 deletion
+74
-150
include/libs/parser/parser.h
include/libs/parser/parser.h
+3
-2
source/libs/parser/src/astValidate.c
source/libs/parser/src/astValidate.c
+45
-107
source/libs/parser/src/queryInfoUtil.c
source/libs/parser/src/queryInfoUtil.c
+14
-9
source/libs/parser/test/parserTests.cpp
source/libs/parser/test/parserTests.cpp
+10
-0
source/libs/planner/src/planner.c
source/libs/planner/src/planner.c
+2
-32
未找到文件。
include/libs/parser/parser.h
浏览文件 @
4c6dad1f
...
...
@@ -108,10 +108,10 @@ typedef struct SQueryStmtInfo {
SArray
*
pUdfInfo
;
struct
SQueryStmtInfo
*
sibling
;
// sibling
SArray
*
pUpstream
;
// SArray<struct SQueryStmtInfo>
struct
SQueryStmtInfo
*
pDownstream
;
SMultiFunctionsDesc
info
;
SArray
*
pUpstream
;
// SArray<struct SQueryStmtInfo>
int32_t
havingFieldNum
;
SMultiFunctionsDesc
info
;
int32_t
exprListLevelIndex
;
}
SQueryStmtInfo
;
...
...
@@ -176,6 +176,7 @@ typedef struct SSourceParam {
SExprInfo
*
createExprInfo
(
STableMetaInfo
*
pTableMetaInfo
,
const
char
*
funcName
,
SSourceParam
*
pSource
,
SSchema
*
pResSchema
,
int16_t
interSize
);
int32_t
copyExprInfoList
(
SArray
*
dst
,
const
SArray
*
src
,
uint64_t
uid
,
bool
deepcopy
);
int32_t
getExprFunctionLevel
(
SQueryStmtInfo
*
pQueryInfo
);
STableMetaInfo
*
getMetaInfo
(
SQueryStmtInfo
*
pQueryInfo
,
int32_t
tableIndex
);
SSchema
*
getOneColumnSchema
(
const
STableMeta
*
pTableMeta
,
int32_t
colIndex
);
...
...
source/libs/parser/src/astValidate.c
浏览文件 @
4c6dad1f
...
...
@@ -35,6 +35,11 @@
#define COLUMN_INDEX_INITIAL_VAL (-2)
#define COLUMN_INDEX_INITIALIZER { COLUMN_INDEX_INITIAL_VAL, COLUMN_INDEX_INITIAL_VAL }
static
int32_t
resColId
=
5000
;
int32_t
getNewResColId
()
{
return
resColId
++
;
}
static
int32_t
validateSelectNodeList
(
SQueryStmtInfo
*
pQueryInfo
,
SArray
*
pSelNodeList
,
bool
outerQuery
,
SMsgBuf
*
pMsgBuf
);
static
int32_t
extractFunctionParameterInfo
(
SQueryStmtInfo
*
pQueryInfo
,
int32_t
tokenId
,
STableMetaInfo
**
pTableMetaInfo
,
SSchema
*
columnSchema
,
tExprNode
**
pNode
,
SColumnIndex
*
pIndex
,
tSqlExprItem
*
pParamElem
,
SMsgBuf
*
pMsgBuf
);
...
...
@@ -1562,6 +1567,15 @@ int32_t validateSqlNode(SSqlNode* pSqlNode, SQueryStmtInfo* pQueryInfo, SMsgBuf*
}
}
for
(
int32_t
i
=
0
;
i
<
getExprFunctionLevel
(
pQueryInfo
);
++
i
)
{
SArray
*
functionList
=
extractFunctionList
(
pQueryInfo
->
exprList
[
i
]);
extractFunctionDesc
(
functionList
,
&
pQueryInfo
->
info
);
if
((
code
=
checkForInvalidExpr
(
pQueryInfo
,
pMsgBuf
))
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
return
TSDB_CODE_SUCCESS
;
// Does not build query message here
}
...
...
@@ -1603,7 +1617,11 @@ int32_t checkForInvalidExpr(SQueryStmtInfo* pQueryInfo, SMsgBuf* pMsgBuf) {
size_t
size
=
getNumOfExprs
(
pQueryInfo
);
for
(
int32_t
i
=
0
;
i
<
size
;
++
i
)
{
SExprInfo
*
pExpr
=
getExprInfo
(
pQueryInfo
,
i
);
int32_t
functionId
=
getExprFunctionId
(
pExpr
);
if
(
pExpr
->
pExpr
->
nodeType
!=
TEXPR_FUNCTION_NODE
)
{
continue
;
}
int32_t
functionId
=
getExprFunctionId
(
pExpr
);
if
(
functionId
==
FUNCTION_COUNT
&&
TSDB_COL_IS_TAG
(
pExpr
->
base
.
pColumns
->
flag
))
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
msg1
);
}
...
...
@@ -1669,11 +1687,6 @@ int32_t checkForInvalidExpr(SQueryStmtInfo* pQueryInfo, SMsgBuf* pMsgBuf) {
}
}
static
int32_t
resColId
=
5000
;
int32_t
getNewResColId
()
{
return
resColId
++
;
}
int32_t
addResColumnInfo
(
SQueryStmtInfo
*
pQueryInfo
,
int32_t
outputIndex
,
SSchema
*
pSchema
,
SExprInfo
*
pSqlExpr
)
{
SInternalField
*
pInfo
=
insertFieldInfo
(
&
pQueryInfo
->
fieldsInfo
,
outputIndex
,
pSchema
);
pInfo
->
pExpr
=
pSqlExpr
;
...
...
@@ -1787,7 +1800,6 @@ static int32_t checkForAliasName(SMsgBuf* pMsgBuf, char* aliasName) {
return
TSDB_CODE_SUCCESS
;
}
static
int32_t
validateComplexExpr
(
tSqlExpr
*
pExpr
,
SQueryStmtInfo
*
pQueryInfo
,
SArray
*
pColList
,
int32_t
*
type
,
SMsgBuf
*
pMsgBuf
);
static
int32_t
sqlExprToExprNode
(
tExprNode
**
pExpr
,
const
tSqlExpr
*
pSqlExpr
,
SQueryStmtInfo
*
pQueryInfo
,
SArray
*
pCols
,
bool
*
keepTableCols
,
SMsgBuf
*
pMsgBuf
);
static
int64_t
getTickPerSecond
(
SVariant
*
pVariant
,
int32_t
precision
,
int64_t
*
tickPerSec
,
SMsgBuf
*
pMsgBuf
)
{
...
...
@@ -2600,15 +2612,15 @@ static int32_t validateExprLeafFunctionNode(SQueryStmtInfo* pQueryInfo, tSqlExpr
return
TSDB_CODE_SUCCESS
;
}
int32_t
validateScalarFunctionParamNum
(
tSqlExprItem
*
pItem
,
SMsgBuf
*
pMsgBuf
)
{
static
int32_t
validateScalarFunctionParamNum
(
tSqlExpr
*
pSqlExpr
,
int32_t
functionId
,
SMsgBuf
*
pMsgBuf
)
{
int32_t
code
=
TSDB_CODE_SUCCESS
;
switch
(
pItem
->
functionId
)
{
switch
(
functionId
)
{
case
FUNCTION_CEIL
:
{
code
=
checkForkParam
(
p
Item
->
pNode
,
1
,
pMsgBuf
);
code
=
checkForkParam
(
p
SqlExpr
,
1
,
pMsgBuf
);
break
;
}
case
FUNCTION_LENGTH
:
{
code
=
checkForkParam
(
p
Item
->
pNode
,
1
,
pMsgBuf
);
code
=
checkForkParam
(
p
SqlExpr
,
1
,
pMsgBuf
);
break
;
}
}
...
...
@@ -2616,38 +2628,6 @@ int32_t validateScalarFunctionParamNum(tSqlExprItem* pItem, SMsgBuf* pMsgBuf) {
return
code
;
}
int32_t
validateScalarFunctionParam
(
SQueryStmtInfo
*
pQueryInfo
,
tSqlExpr
*
pExpr
,
SArray
*
pList
,
int32_t
*
exprType
,
SMsgBuf
*
pMsgBuf
)
{
int32_t
code
=
TSDB_CODE_SUCCESS
;
SArray
*
pParamList
=
pExpr
->
Expr
.
paramList
;
*
exprType
=
NORMAL_ARITHMETIC
;
for
(
int32_t
i
=
0
;
i
<
1
;
++
i
)
{
tSqlExprItem
*
pParamElem
=
taosArrayGet
(
pParamList
,
i
);
tSqlExpr
*
pSqlExpr
=
pParamElem
->
pNode
;
int32_t
type
=
pSqlExpr
->
type
;
if
(
type
==
SQL_NODE_VALUE
)
{
// do nothing for scalar function, or maybe the evaluation can be done here
}
else
if
(
type
==
SQL_NODE_SQLFUNCTION
)
{
code
=
validateExprLeafFunctionNode
(
pQueryInfo
,
pSqlExpr
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
else
if
(
type
==
SQL_NODE_EXPR
)
{
code
=
validateComplexExpr
(
pSqlExpr
,
pQueryInfo
,
pList
,
exprType
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
else
if
(
type
==
SQL_NODE_TABLE_COLUMN
)
{
code
=
validateExprLeafColumnNode
(
pQueryInfo
,
&
pSqlExpr
->
columnName
,
pList
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
}
}
SExprInfo
*
doAddProjectCol
(
SQueryStmtInfo
*
pQueryInfo
,
int32_t
outputColIndex
,
SColumnIndex
*
pColIndex
,
const
char
*
aliasName
,
int32_t
colId
)
{
STableMeta
*
pTableMeta
=
getMetaInfo
(
pQueryInfo
,
pColIndex
->
tableIndex
)
->
pTableMeta
;
...
...
@@ -2888,57 +2868,6 @@ static int32_t validateExprLeafNode(tSqlExpr* pExpr, SQueryStmtInfo* pQueryInfo,
return
TSDB_CODE_SUCCESS
;
}
int32_t
validateComplexExpr
(
tSqlExpr
*
pExpr
,
SQueryStmtInfo
*
pQueryInfo
,
SArray
*
pColList
,
int32_t
*
type
,
SMsgBuf
*
pMsgBuf
)
{
if
(
pExpr
==
NULL
)
{
return
TSDB_CODE_SUCCESS
;
}
int32_t
code
=
TSDB_CODE_SUCCESS
;
if
(
pExpr
->
type
==
SQL_NODE_SQLFUNCTION
)
{
return
validateScalarFunctionParam
(
pQueryInfo
,
pExpr
,
pColList
,
type
,
pMsgBuf
);
}
tSqlExpr
*
pLeft
=
pExpr
->
pLeft
;
if
(
pLeft
->
type
==
SQL_NODE_EXPR
)
{
code
=
validateComplexExpr
(
pLeft
,
pQueryInfo
,
pColList
,
type
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
else
{
code
=
validateExprLeafNode
(
pLeft
,
pQueryInfo
,
pColList
,
type
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
tSqlExpr
*
pRight
=
pExpr
->
pRight
;
if
(
pRight
->
type
==
SQL_NODE_EXPR
)
{
code
=
validateComplexExpr
(
pRight
,
pQueryInfo
,
pColList
,
type
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
else
{
code
=
validateExprLeafNode
(
pRight
,
pQueryInfo
,
pColList
,
type
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
}
// check divide by 0
if
(
pExpr
->
tokenId
==
TK_DIVIDE
&&
pRight
->
type
==
SQL_NODE_VALUE
)
{
int32_t
type1
=
pRight
->
value
.
nType
;
const
char
*
msg1
=
"invalid expr (divide by 0)"
;
if
(
type1
==
TSDB_DATA_TYPE_DOUBLE
&&
pRight
->
value
.
d
<
DBL_EPSILON
)
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
msg1
);
}
else
if
(
type1
==
TSDB_DATA_TYPE_INT
&&
pRight
->
value
.
i
==
0
)
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
msg1
);
}
}
return
TSDB_CODE_SUCCESS
;
}
static
uint64_t
findTmpSourceColumnInNextLevel
(
SQueryStmtInfo
*
pQueryInfo
,
tExprNode
*
pExpr
)
{
// This function must be a aggregate function, so it must be in the next level
pQueryInfo
->
exprListLevelIndex
+=
1
;
...
...
@@ -3080,7 +3009,8 @@ int32_t validateSqlExpr(const tSqlExpr* pSqlExpr, SQueryStmtInfo *pQueryInfo, SM
}
int32_t
tokenId
=
pSqlExpr
->
tokenId
;
if
(
pRight
->
type
==
SQL_NODE_VALUE
&&
pRight
->
value
.
nType
==
TSDB_DATA_TYPE_DOUBLE
&&
pRight
->
value
.
d
==
0
&&
tokenId
==
TK_DIVIDE
)
{
if
(
pRight
->
type
==
SQL_NODE_VALUE
&&
(
pRight
->
value
.
nType
==
TSDB_DATA_TYPE_DOUBLE
||
pRight
->
value
.
nType
==
TSDB_DATA_TYPE_INT
)
&&
pRight
->
value
.
d
==
0
&&
tokenId
==
TK_DIVIDE
)
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
"invalid expression (divided by 0)"
);
}
...
...
@@ -3098,6 +3028,20 @@ int32_t validateSqlExpr(const tSqlExpr* pSqlExpr, SQueryStmtInfo *pQueryInfo, SM
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
return
ret
;
}
}
else
if
(
pSqlExpr
->
type
==
SQL_NODE_SQLFUNCTION
)
{
bool
scalar
=
false
;
int32_t
functionId
=
qIsBuiltinFunction
(
pSqlExpr
->
Expr
.
operand
.
z
,
pSqlExpr
->
Expr
.
operand
.
n
,
&
scalar
);
if
(
functionId
<
0
)
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
"invalid function name"
);
}
// do check the parameter number for scalar function
if
(
scalar
)
{
int32_t
ret
=
validateScalarFunctionParamNum
(
pSqlExpr
,
functionId
,
pMsgBuf
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
"invalid number of function parameters"
);
}
}
}
return
TSDB_CODE_SUCCESS
;
...
...
@@ -3379,14 +3323,14 @@ int32_t validateSelectNodeList(SQueryStmtInfo* pQueryInfo, SArray* pSelNodeList,
if
(
type
==
SQL_NODE_SQLFUNCTION
)
{
bool
scalarFunc
=
false
;
pItem
->
functionId
=
qIsBuiltinFunction
(
pItem
->
pNode
->
Expr
.
operand
.
z
,
pItem
->
pNode
->
Expr
.
operand
.
n
,
&
scalarFunc
);
if
(
pItem
->
functionId
==
FUNCTION_INVALID_ID
)
{
int32_t
functionId
=
FUNCTION_INVALID_ID
;
bool
valid
=
qIsValidUdf
(
pQueryInfo
->
pUdfInfo
,
pItem
->
pNode
->
Expr
.
operand
.
z
,
pItem
->
pNode
->
Expr
.
operand
.
n
,
&
functionId
);
if
(
!
valid
)
{
if
(
pItem
->
functionId
==
FUNCTION_INVALID_ID
)
{
// temporarily disable the udf
//
int32_t functionId = FUNCTION_INVALID_ID;
//
bool valid = qIsValidUdf(pQueryInfo->pUdfInfo, pItem->pNode->Expr.operand.z, pItem->pNode->Expr.operand.n, &functionId);
//
if (!valid) {
return
buildInvalidOperationMsg
(
pMsgBuf
,
msg5
);
}
//
}
pItem
->
functionId
=
functionId
;
//
pItem->functionId = functionId;
}
if
(
scalarFunc
)
{
// scalar function
...
...
@@ -3920,12 +3864,6 @@ int32_t qParserValidateSqlNode(struct SCatalog* pCatalog, SSqlInfo* pInfo, SQuer
validateSqlNode
(
p
,
pQueryInfo
,
&
buf
);
}
SArray
*
functionList
=
extractFunctionList
(
pQueryInfo
->
exprList
[
0
]);
extractFunctionDesc
(
functionList
,
&
pQueryInfo
->
info
);
if
((
code
=
checkForInvalidExpr
(
pQueryInfo
,
&
buf
))
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
}
return
code
;
}
source/libs/parser/src/queryInfoUtil.c
浏览文件 @
4c6dad1f
...
...
@@ -223,7 +223,7 @@ void addExprInfoParam(SSqlExpr* pExpr, char* argument, int32_t type, int32_t byt
}
int32_t
getExprFunctionId
(
SExprInfo
*
pExprInfo
)
{
assert
(
pExprInfo
!=
NULL
&&
pExprInfo
->
pExpr
!=
NULL
&&
pExprInfo
->
pExpr
->
nodeType
==
TEXPR_
UNARYEXPR
_NODE
);
assert
(
pExprInfo
!=
NULL
&&
pExprInfo
->
pExpr
!=
NULL
&&
pExprInfo
->
pExpr
->
nodeType
==
TEXPR_
FUNCTION
_NODE
);
return
0
;
}
...
...
@@ -350,11 +350,16 @@ bool tscHasColumnFilter(SQueryStmtInfo* pQueryInfo) {
return
false
;
}
//void tscClearInterpInfo(SQueryStmtInfo* pQueryInfo) {
// if (!tscIsPointInterpQuery(pQueryInfo)) {
// return;
// }
//
// pQueryInfo->fillType = TSDB_FILL_NONE;
// tfree(pQueryInfo->fillVal);
//}
\ No newline at end of file
int32_t
getExprFunctionLevel
(
SQueryStmtInfo
*
pQueryInfo
)
{
int32_t
n
=
10
;
int32_t
level
=
0
;
for
(
int32_t
i
=
0
;
i
<
n
;
++
i
)
{
SArray
*
pList
=
pQueryInfo
->
exprList
[
i
];
if
(
taosArrayGetSize
(
pList
)
>
0
)
{
level
+=
1
;
}
}
return
level
;
}
\ No newline at end of file
source/libs/parser/test/parserTests.cpp
浏览文件 @
4c6dad1f
...
...
@@ -412,6 +412,16 @@ TEST(testCase, function_Test10) {
sqlCheck
(
"select block_dist() from `t.1abc`"
,
true
);
sqlCheck
(
"select block_dist(a) from `t.1abc`"
,
false
);
sqlCheck
(
"select count(*) from `t.1abc` interval(1s) group by a"
,
false
);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sqlCheck
(
"select length119(a,b) from `t.1abc`"
,
false
);
sqlCheck
(
"select length(a,b) from `t.1abc`"
,
false
);
sqlCheck
(
"select block_dist() + 20 from `t.1abc`"
,
false
);
sqlCheck
(
"select top(a, 20), count(b) from `t.1abc`"
,
false
);
sqlCheck
(
"select count(b), c from `t.1abc`"
,
false
);
sqlCheck
(
"select last_row(*), count(b) from `t.1abc`"
,
false
);
sqlCheck
(
"select last_row(a, b) + 20 from `t.1abc`"
,
false
);
sqlCheck
(
"select last_row(count(*)) from `t.1abc`"
,
false
);
}
TEST
(
testCase
,
function_Test6
)
{
...
...
source/libs/planner/src/planner.c
浏览文件 @
4c6dad1f
...
...
@@ -217,42 +217,12 @@ static SQueryPlanNode* doAddTableColumnNode(SQueryStmtInfo* pQueryInfo, STableMe
return
pNode
;
}
static
int32_t
getFunctionLevel
(
SQueryStmtInfo
*
pQueryInfo
)
{
int32_t
n
=
10
;
int32_t
level
=
0
;
for
(
int32_t
i
=
0
;
i
<
n
;
++
i
)
{
SArray
*
pList
=
pQueryInfo
->
exprList
[
i
];
if
(
taosArrayGetSize
(
pList
)
>
0
)
{
level
+=
1
;
}
}
return
level
;
}
static
SQueryPlanNode
*
createOneQueryPlanNode
(
SArray
*
p
,
SQueryPlanNode
*
pNode
,
SExprInfo
*
pExpr
,
SQueryTableInfo
*
info
)
{
if
(
pExpr
->
pExpr
->
nodeType
==
TEXPR_FUNCTION_NODE
)
{
bool
aggregateFunc
=
qIsAggregateFunction
(
pExpr
->
pExpr
->
_function
.
functionName
);
if
(
aggregateFunc
)
{
int32_t
numOfOutput
=
(
int32_t
)
taosArrayGetSize
(
p
);
return
createQueryNode
(
QNODE_AGGREGATE
,
"Aggregate"
,
&
pNode
,
1
,
p
->
pData
,
numOfOutput
,
info
,
NULL
);
}
else
{
int32_t
numOfOutput
=
(
int32_t
)
taosArrayGetSize
(
p
);
return
createQueryNode
(
QNODE_PROJECT
,
"Projection"
,
&
pNode
,
1
,
p
->
pData
,
numOfOutput
,
info
,
NULL
);
}
}
else
{
int32_t
numOfOutput
=
(
int32_t
)
taosArrayGetSize
(
p
);
return
createQueryNode
(
QNODE_PROJECT
,
"Projection"
,
&
pNode
,
1
,
p
->
pData
,
numOfOutput
,
info
,
NULL
);
}
}
static
SQueryPlanNode
*
doCreateQueryPlanForSingleTableImpl
(
SQueryStmtInfo
*
pQueryInfo
,
SQueryPlanNode
*
pNode
,
SQueryTableInfo
*
info
)
{
// group by column not by tag
size_t
numOfGroupCols
=
taosArrayGetSize
(
pQueryInfo
->
groupbyExpr
.
columnInfo
);
// check for aggregation
int32_t
level
=
getFunctionLevel
(
pQueryInfo
);
int32_t
level
=
get
Expr
FunctionLevel
(
pQueryInfo
);
for
(
int32_t
i
=
level
-
1
;
i
>=
0
;
--
i
)
{
SArray
*
p
=
pQueryInfo
->
exprList
[
i
];
...
...
@@ -344,7 +314,7 @@ static bool isAllAggExpr(SArray* pList) {
static
void
exprInfoPushDown
(
SQueryStmtInfo
*
pQueryInfo
)
{
assert
(
pQueryInfo
!=
NULL
);
size_t
level
=
getFunctionLevel
(
pQueryInfo
);
size_t
level
=
get
Expr
FunctionLevel
(
pQueryInfo
);
for
(
int32_t
i
=
0
;
i
<
level
-
1
;
++
i
)
{
SArray
*
p
=
pQueryInfo
->
exprList
[
i
];
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录