Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
taosdata
TDengine
提交
4eebc5c4
TDengine
项目概览
taosdata
/
TDengine
1 年多 前同步成功
通知
1187
Star
22018
Fork
4786
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
TDengine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
4eebc5c4
编写于
11月 08, 2021
作者:
H
Haojun Liao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[td-10564] disable the function id during sql parse.
上级
b4bba7ee
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
189 addition
and
205 deletion
+189
-205
include/common/taosmsg.h
include/common/taosmsg.h
+1
-0
include/libs/parser/parser.h
include/libs/parser/parser.h
+0
-2
source/libs/parser/inc/queryInfoUtil.h
source/libs/parser/inc/queryInfoUtil.h
+1
-1
source/libs/parser/src/astValidate.c
source/libs/parser/src/astValidate.c
+86
-118
source/libs/parser/src/queryInfoUtil.c
source/libs/parser/src/queryInfoUtil.c
+15
-4
source/libs/parser/test/parserTests.cpp
source/libs/parser/test/parserTests.cpp
+86
-80
未找到文件。
include/common/taosmsg.h
浏览文件 @
4eebc5c4
...
...
@@ -203,6 +203,7 @@ enum _mgmt_table {
#define TSDB_COL_NORMAL 0x0u // the normal column of the table
#define TSDB_COL_TAG 0x1u // the tag column type
#define TSDB_COL_UDC 0x2u // the user specified normal string column, it is a dummy column
#define TSDB_COL_TMP 0x3u // internal column generated by the previous operators
#define TSDB_COL_NULL 0x4u // the column filter NULL or not
#define TSDB_COL_IS_TAG(f) (((f&(~(TSDB_COL_NULL)))&TSDB_COL_TAG) != 0)
...
...
include/libs/parser/parser.h
浏览文件 @
4eebc5c4
...
...
@@ -168,9 +168,7 @@ void columnListDestroy(SArray* pColumnList);
void
dropAllExprInfo
(
SArray
**
pExprInfo
,
int32_t
numOfLevel
);
typedef
struct
SSourceParam
{
// struct tExprNode **pExprNode;
SArray
*
pExprNodeList
;
//Array<struct tExprNode*>
// SColumn *pCols;
SArray
*
pColumnList
;
//Array<struct SColumn>
int32_t
num
;
}
SSourceParam
;
...
...
source/libs/parser/inc/queryInfoUtil.h
浏览文件 @
4eebc5c4
...
...
@@ -30,7 +30,7 @@ SSchema *getTableTagSchema(const STableMeta* pTableMeta);
SArray
*
getCurrentExprList
(
SQueryStmtInfo
*
pQueryInfo
);
size_t
getNumOfExprs
(
SQueryStmtInfo
*
pQueryInfo
);
SExprInfo
*
createBinaryExprInfo
(
struct
tExprNode
*
pNode
,
SSchema
*
pResSchema
);
SExprInfo
*
createBinaryExprInfo
(
struct
tExprNode
*
pNode
,
SSchema
*
pResSchema
,
const
char
*
funcName
);
void
addExprInfo
(
SArray
*
pExprList
,
int32_t
index
,
SExprInfo
*
pExprInfo
,
int32_t
level
);
void
updateExprInfo
(
SExprInfo
*
pExprInfo
,
int16_t
functionId
,
int32_t
colId
,
int16_t
srcColumnIndex
,
int16_t
resType
,
int16_t
resSize
);
...
...
source/libs/parser/src/astValidate.c
浏览文件 @
4eebc5c4
...
...
@@ -1708,7 +1708,6 @@ void setResultColName(char* name, tSqlExprItem* pItem, SToken* pToken, SToken* f
}
}
SExprInfo
*
doAddOneExprInfo
(
SQueryStmtInfo
*
pQueryInfo
,
const
char
*
funcName
,
SSourceParam
*
pSourceParam
,
int32_t
outputIndex
,
STableMetaInfo
*
pTableMetaInfo
,
SSchema
*
pResultSchema
,
int32_t
interSize
,
const
char
*
token
,
bool
finalResult
)
{
SExprInfo
*
pExpr
=
createExprInfo
(
pTableMetaInfo
,
funcName
,
pSourceParam
,
pResultSchema
,
interSize
);
...
...
@@ -1722,7 +1721,7 @@ SExprInfo* doAddOneExprInfo(SQueryStmtInfo* pQueryInfo, const char* funcName, SS
if
(
pSourceParam
->
pColumnList
!=
NULL
)
{
SColumn
*
pCol
=
taosArrayGetP
(
pSourceParam
->
pColumnList
,
0
);
if
(
pCol
->
flag
!=
COLUMN_INDEX_INITIAL_VAL
)
{
if
(
TSDB_COL_IS_TAG
(
pCol
->
flag
)
||
TSDB_COL_IS_NORMAL_COL
(
pCol
->
flag
)
)
{
SArray
*
p
=
TSDB_COL_IS_TAG
(
pCol
->
flag
)
?
pTableMetaInfo
->
tagColList
:
pQueryInfo
->
colList
;
for
(
int32_t
i
=
0
;
i
<
pSourceParam
->
num
;
++
i
)
{
...
...
@@ -1744,6 +1743,12 @@ SExprInfo* doAddOneExprInfo(SQueryStmtInfo* pQueryInfo, const char* funcName, SS
return
pExpr
;
}
static
void
extractFunctionName
(
char
*
name
,
const
tSqlExprItem
*
pItem
)
{
assert
(
pItem
!=
NULL
);
SToken
*
funcToken
=
&
pItem
->
pNode
->
Expr
.
operand
;
memcpy
(
name
,
funcToken
->
z
,
funcToken
->
n
);
}
static
int32_t
addOneExprInfo
(
SQueryStmtInfo
*
pQueryInfo
,
tSqlExprItem
*
pItem
,
int32_t
functionId
,
int32_t
outputIndex
,
SSchema
*
pSchema
,
SColumnIndex
*
pColIndex
,
tExprNode
*
pNode
,
bool
finalResult
,
SMsgBuf
*
pMsgBuf
)
{
const
char
*
msg1
=
"not support column types"
;
if
(
functionId
==
FUNCTION_SPREAD
)
{
...
...
@@ -1762,13 +1767,15 @@ static int32_t addOneExprInfo(SQueryStmtInfo* pQueryInfo, tSqlExprItem* pItem, i
SSchema
resultSchema
=
createSchema
(
resInfo
.
type
,
resInfo
.
bytes
,
getNewResColId
(),
name
);
STableMetaInfo
*
pTableMetaInfo
=
getMetaInfo
(
pQueryInfo
,
pColIndex
->
tableIndex
);
SColumn
c
=
createColumn
(
pTableMetaInfo
->
pTableMeta
->
uid
,
pSchema
->
name
,
pColIndex
->
type
,
pSchema
);
SColumn
c
=
createColumn
(
pTableMetaInfo
->
pTableMeta
->
uid
,
pTableMetaInfo
->
aliasName
,
pColIndex
->
type
,
pSchema
);
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
pNode
,
&
c
);
doAddOneExprInfo
(
pQueryInfo
,
functionId
,
&
param
,
outputIndex
,
pTableMetaInfo
,
&
resultSchema
,
resInfo
.
intermediateBytes
,
name
,
finalResult
);
char
fname
[
FUNCTIONS_NAME_MAX_LENGTH
]
=
{
0
};
extractFunctionName
(
fname
,
pItem
);
doAddOneExprInfo
(
pQueryInfo
,
fname
,
&
param
,
outputIndex
,
pTableMetaInfo
,
&
resultSchema
,
resInfo
.
intermediateBytes
,
name
,
finalResult
);
return
TSDB_CODE_SUCCESS
;
}
...
...
@@ -1814,7 +1821,7 @@ static void setTsOutputExprInfo(SQueryStmtInfo* pQueryInfo, STableMetaInfo* pTab
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
NULL
,
&
col
);
SExprInfo
*
pExpr
=
createExprInfo
(
pTableMetaInfo
,
FUNCTION_TS_DUMMY
,
&
param
,
&
s
,
TSDB_KEYSIZE
);
SExprInfo
*
pExpr
=
createExprInfo
(
pTableMetaInfo
,
"ts_dummy"
,
&
param
,
&
s
,
TSDB_KEYSIZE
);
strncpy
(
pExpr
->
base
.
token
,
"ts"
,
tListLen
(
pExpr
->
base
.
token
));
SArray
*
pExprList
=
getCurrentExprList
(
pQueryInfo
);
...
...
@@ -1908,6 +1915,7 @@ static int32_t doHandleOneParam(SQueryStmtInfo *pQueryInfo, tSqlExprItem* pItem,
STableMetaInfo
*
pTableMetaInfo
=
{
0
};
int32_t
code
=
extractFunctionParameterInfo
(
pQueryInfo
,
tokenId
,
&
pTableMetaInfo
,
&
columnSchema
,
&
pNode
,
&
index
,
pParamElem
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
buildInvalidOperationMsg
(
pMsgBuf
,
msg3
);
}
...
...
@@ -1937,6 +1945,8 @@ int32_t extractFunctionParameterInfo(SQueryStmtInfo* pQueryInfo, int32_t tokenId
const
char
*
msg6
=
"aggregate function can not be nested in aggregate function"
;
const
char
*
msg7
=
"invalid function name"
;
pQueryInfo
->
exprListLevelIndex
+=
1
;
if
(
tokenId
==
TK_ALL
||
tokenId
==
TK_ID
)
{
// simple parameter
// simple parameter or nested function
// It is a parameter of a aggregate function, so it can not be still a aggregate function.
...
...
@@ -1983,6 +1993,7 @@ int32_t extractFunctionParameterInfo(SQueryStmtInfo* pQueryInfo, int32_t tokenId
}
}
else
if
(
tokenId
==
TK_PLUS
||
tokenId
==
TK_MINUS
||
tokenId
==
TK_STAR
||
tokenId
==
TK_REM
||
tokenId
==
TK_DIVIDE
||
tokenId
==
TK_CONCAT
)
{
pIndex
->
tableIndex
=
0
;
// todo set the correct table index
pIndex
->
type
=
TSDB_COL_TMP
;
// It is a temporary column generated by arithmetic expression.
SArray
*
pExprList
=
getCurrentExprList
(
pQueryInfo
);
size_t
n
=
taosArrayGetSize
(
pExprList
);
...
...
@@ -1998,7 +2009,8 @@ int32_t extractFunctionParameterInfo(SQueryStmtInfo* pQueryInfo, int32_t tokenId
}
else
{
assert
(
0
);
}
pQueryInfo
->
exprListLevelIndex
-=
1
;
return
TSDB_CODE_SUCCESS
;
}
...
...
@@ -2063,13 +2075,17 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
char
token
[
TSDB_COL_NAME_LEN
]
=
{
0
};
setTokenAndResColumnName
(
pItem
,
s
.
name
,
token
,
sizeof
(
s
.
name
)
-
1
);
pTableMetaInfo
=
getMetaInfo
(
pQueryInfo
,
index
.
tableIndex
);
SColumn
c
=
createColumn
(
pTableMetaInfo
->
pTableMeta
->
uid
,
pTableMetaInfo
->
aliasName
,
index
.
type
,
&
columnSchema
);
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
pNode
,
&
c
);
int32_t
outputIndex
=
getNumOfFields
(
&
pQueryInfo
->
fieldsInfo
);
doAddOneExprInfo
(
pQueryInfo
,
functionId
,
&
param
,
outputIndex
,
pTableMetaInfo
,
&
s
,
size
,
token
,
finalResult
);
char
fname
[
FUNCTIONS_NAME_MAX_LENGTH
]
=
{
0
};
extractFunctionName
(
fname
,
pItem
);
doAddOneExprInfo
(
pQueryInfo
,
fname
,
&
param
,
outputIndex
,
pTableMetaInfo
,
&
s
,
size
,
token
,
finalResult
);
return
TSDB_CODE_SUCCESS
;
}
...
...
@@ -2100,10 +2116,7 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
int32_t
tokenId
=
pParamElem
->
pNode
->
tokenId
;
SColumnIndex
index
=
COLUMN_INDEX_INITIALIZER
;
SSchema
columnSchema
=
{
0
};
pQueryInfo
->
exprListLevelIndex
+=
1
;
code
=
extractFunctionParameterInfo
(
pQueryInfo
,
tokenId
,
&
pTableMetaInfo
,
&
columnSchema
,
&
pNode
,
&
index
,
pParamElem
,
pMsgBuf
);
pQueryInfo
->
exprListLevelIndex
-=
1
;
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
...
...
@@ -2141,11 +2154,10 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
pNode
,
&
c
);
SExprInfo
*
pExpr
=
doAddOneExprInfo
(
pQueryInfo
,
functionId
,
&
param
,
numOfOutput
,
pTableMetaInfo
,
&
s
,
resInfo
.
intermediateBytes
,
token
,
finalResult
);
if
(
finalResult
)
{
addResColumnInfo
(
pQueryInfo
,
numOfOutput
,
&
s
,
pExpr
);
}
char
funcName
[
FUNCTIONS_NAME_MAX_LENGTH
]
=
{
0
};
extractFunctionName
(
funcName
,
pItem
);
SExprInfo
*
pExpr
=
doAddOneExprInfo
(
pQueryInfo
,
funcName
,
&
param
,
numOfOutput
,
pTableMetaInfo
,
&
s
,
resInfo
.
intermediateBytes
,
token
,
finalResult
);
if
(
functionId
==
FUNCTION_LEASTSQR
)
{
// set the leastsquares parameters
char
val
[
8
]
=
{
0
};
if
(
taosVariantDump
(
&
pParamElem
[
1
].
pNode
->
value
,
val
,
TSDB_DATA_TYPE_DOUBLE
,
true
)
<
0
)
{
...
...
@@ -2285,7 +2297,9 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
pNode
,
&
c
);
SExprInfo
*
pExpr
=
doAddOneExprInfo
(
pQueryInfo
,
functionId
,
&
param
,
colIndex
,
pTableMetaInfo
,
&
s
,
resInfo
.
intermediateBytes
,
token
,
finalResult
);
char
funcName
[
FUNCTIONS_NAME_MAX_LENGTH
]
=
{
0
};
extractFunctionName
(
funcName
,
pItem
);
SExprInfo
*
pExpr
=
doAddOneExprInfo
(
pQueryInfo
,
funcName
,
&
param
,
colIndex
,
pTableMetaInfo
,
&
s
,
resInfo
.
intermediateBytes
,
token
,
finalResult
);
SToken
*
pParamToken
=
&
pParamElem
[
1
].
pNode
->
exprToken
;
pExpr
->
base
.
numOfParams
+=
1
;
...
...
@@ -2426,8 +2440,6 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
SColumnIndex
index1
=
COLUMN_INDEX_INITIALIZER
;
SSchema
columnSchema1
=
{
0
};
pQueryInfo
->
exprListLevelIndex
+=
1
;
code
=
extractFunctionParameterInfo
(
pQueryInfo
,
tokenId1
,
&
pTableMetaInfo
,
&
columnSchema1
,
&
pNode1
,
&
index1
,
p1
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
return
code
;
...
...
@@ -2443,8 +2455,6 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
return
code
;
}
pQueryInfo
->
exprListLevelIndex
-=
1
;
int32_t
srcType1
=
columnSchema1
.
type
,
srcType2
=
columnSchema2
.
type
;
if
(
IS_VAR_DATA_TYPE
(
srcType1
)
||
IS_VAR_DATA_TYPE
(
columnSchema2
.
type
)
||
srcType1
==
TSDB_DATA_TYPE_TIMESTAMP
||
srcType1
==
TSDB_DATA_TYPE_BOOL
||
srcType2
==
TSDB_DATA_TYPE_TIMESTAMP
||
srcType2
==
TSDB_DATA_TYPE_BOOL
)
{
...
...
@@ -2464,7 +2474,10 @@ int32_t addAggExprAndResColumn(SQueryStmtInfo* pQueryInfo, int32_t colIndex, tSq
addIntoSourceParam
(
&
param
,
pNode1
,
&
c1
);
addIntoSourceParam
(
&
param
,
pNode2
,
&
c2
);
doAddOneExprInfo
(
pQueryInfo
,
functionId
,
&
param
,
colIndex
,
pTableMetaInfo
,
&
s
,
resInfo
.
intermediateBytes
,
token
,
finalResult
);
char
funcName
[
FUNCTIONS_NAME_MAX_LENGTH
]
=
{
0
};
extractFunctionName
(
funcName
,
pItem
);
doAddOneExprInfo
(
pQueryInfo
,
funcName
,
&
param
,
colIndex
,
pTableMetaInfo
,
&
s
,
resInfo
.
intermediateBytes
,
token
,
finalResult
);
return
TSDB_CODE_SUCCESS
;
}
...
...
@@ -2634,14 +2647,14 @@ SExprInfo* doAddProjectCol(SQueryStmtInfo* pQueryInfo, int32_t outputColIndex, S
SSchema
*
pSchema
=
getOneColumnSchema
(
pTableMeta
,
pColIndex
->
columnIndex
);
SColumnIndex
index
=
*
pColIndex
;
int16_t
functionId
=
0
;
char
*
funcName
=
NULL
;
if
(
TSDB_COL_IS_TAG
(
index
.
type
))
{
int32_t
numOfCols
=
getNumOfColumns
(
pTableMeta
);
index
.
columnIndex
=
pColIndex
->
columnIndex
-
numOfCols
;
func
tionId
=
FUNCTION_TAGPRJ
;
func
Name
=
"project_tag"
;
}
else
{
index
.
columnIndex
=
pColIndex
->
columnIndex
;
func
tionId
=
FUNCTION_PRJ
;
func
Name
=
"project_col"
;
}
const
char
*
name
=
(
aliasName
==
NULL
)
?
pSchema
->
name
:
aliasName
;
...
...
@@ -2653,7 +2666,7 @@ SExprInfo* doAddProjectCol(SQueryStmtInfo* pQueryInfo, int32_t outputColIndex, S
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
NULL
,
&
c
);
return
doAddOneExprInfo
(
pQueryInfo
,
func
tionId
,
&
param
,
outputColIndex
,
pTableMetaInfo
,
&
s
,
0
,
s
.
name
,
true
);
return
doAddOneExprInfo
(
pQueryInfo
,
func
Name
,
&
param
,
outputColIndex
,
pTableMetaInfo
,
&
s
,
0
,
s
.
name
,
true
);
}
static
int32_t
doAddProjectionExprAndResColumn
(
SQueryStmtInfo
*
pQueryInfo
,
SColumnIndex
*
pIndex
,
int32_t
startPos
)
{
...
...
@@ -2803,7 +2816,7 @@ int32_t addProjectionExprAndResColumn(SQueryStmtInfo* pQueryInfo, tSqlExprItem*
SSourceParam
param
=
{
0
};
addIntoSourceParam
(
&
param
,
NULL
,
&
c
);
SExprInfo
*
pExpr
=
doAddOneExprInfo
(
pQueryInfo
,
FUNCTION_PRJ
,
&
param
,
startPos
,
pTableMetaInfo
,
&
colSchema
,
0
,
token
,
true
);
SExprInfo
*
pExpr
=
doAddOneExprInfo
(
pQueryInfo
,
"project"
,
&
param
,
startPos
,
pTableMetaInfo
,
&
colSchema
,
0
,
token
,
true
);
// NOTE: the first parameter is reserved for the tag column id during join query process.
pExpr
->
base
.
numOfParams
=
2
;
taosVariantAssign
(
&
pExpr
->
base
.
param
[
1
],
&
pItem
->
pNode
->
value
);
...
...
@@ -3043,7 +3056,7 @@ int32_t sqlExprToExprNode(tExprNode **pExpr, const tSqlExpr* pSqlExpr, SQuerySt
assert
(
found
);
if
(
pCols
!=
NULL
)
{
// record the involved columns
SColumn
c
=
createColumn
(
uid
,
NULL
,
TSDB_COL_
NORMAL
,
(
*
pExpr
)
->
pSchema
);
SColumn
c
=
createColumn
(
uid
,
NULL
,
TSDB_COL_
TMP
,
(
*
pExpr
)
->
pSchema
);
taosArrayPush
(
pCols
,
&
c
);
}
...
...
@@ -3054,7 +3067,8 @@ int32_t sqlExprToExprNode(tExprNode **pExpr, const tSqlExpr* pSqlExpr, SQuerySt
}
pQueryInfo
->
curTableIdx
=
index
.
tableIndex
;
STableMeta
*
pTableMeta
=
getMetaInfo
(
pQueryInfo
,
index
.
tableIndex
)
->
pTableMeta
;
STableMetaInfo
*
pTableMetaInfo
=
getMetaInfo
(
pQueryInfo
,
index
.
tableIndex
);
STableMeta
*
pTableMeta
=
pTableMetaInfo
->
pTableMeta
;
*
pExpr
=
calloc
(
1
,
sizeof
(
tExprNode
));
(
*
pExpr
)
->
nodeType
=
TEXPR_COL_NODE
;
...
...
@@ -3062,6 +3076,12 @@ int32_t sqlExprToExprNode(tExprNode **pExpr, const tSqlExpr* pSqlExpr, SQuerySt
SSchema
*
pSchema
=
getOneColumnSchema
(
pTableMeta
,
index
.
columnIndex
);
*
(
*
pExpr
)
->
pSchema
=
*
pSchema
;
if
(
pCols
!=
NULL
)
{
// record the involved columns
SColumn
c
=
createColumn
(
pTableMeta
->
uid
,
pTableMetaInfo
->
aliasName
,
TSDB_COL_NORMAL
,
(
*
pExpr
)
->
pSchema
);
taosArrayPush
(
pCols
,
&
c
);
}
return
TSDB_CODE_SUCCESS
;
}
else
if
(
pSqlExpr
->
tokenId
==
TK_SET
)
{
int32_t
colType
=
-
1
;
...
...
@@ -3144,109 +3164,57 @@ static int32_t multiColumnListInsert(SQueryStmtInfo* pQueryInfo, SArray* pColumn
}
static
int32_t
addScalarExprAndResColumn
(
SQueryStmtInfo
*
pQueryInfo
,
int32_t
exprIndex
,
tSqlExprItem
*
pItem
,
SMsgBuf
*
pMsgBuf
)
{
const
char
*
msg1
=
"invalid column name, illegal column type, or columns in expression from two tables"
;
const
char
*
msg2
=
"invalid arithmetic expression in select clause"
;
int32_t
arithmeticType
=
NON_ARITHMEIC_EXPR
;
SArray
*
pColumnList
=
taosArrayInit
(
4
,
sizeof
(
SColumn
));
if
(
arithmeticType
==
NORMAL_ARITHMETIC
)
{
// expr string is set as the parameter of function
SSchema
s
=
createSchema
(
TSDB_DATA_TYPE_DOUBLE
,
sizeof
(
double
),
getNewResColId
(),
""
);
tExprNode
*
pNode
=
NULL
;
SArray
*
colList
=
taosArrayInit
(
10
,
sizeof
(
SColumn
));
int32_t
ret
=
sqlExprToExprNode
(
&
pNode
,
pItem
->
pNode
,
pQueryInfo
,
colList
,
pMsgBuf
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
taosArrayDestroy
(
colList
);
tExprTreeDestroy
(
pNode
,
NULL
);
return
buildInvalidOperationMsg
(
pMsgBuf
,
msg2
);
}
SExprInfo
*
pExpr
=
createBinaryExprInfo
(
pNode
,
&
s
);
SArray
*
pExprList
=
getCurrentExprList
(
pQueryInfo
);
addExprInfo
(
pExprList
,
exprIndex
,
pExpr
,
pQueryInfo
->
exprListLevelIndex
);
setTokenAndResColumnName
(
pItem
,
pExpr
->
base
.
resSchema
.
name
,
pExpr
->
base
.
token
,
TSDB_COL_NAME_LEN
);
// check for if there is a tag in the arithmetic express
int32_t
code
=
multiColumnListInsert
(
pQueryInfo
,
pColumnList
,
pMsgBuf
);
if
(
code
!=
TSDB_CODE_SUCCESS
)
{
taosArrayDestroy
(
colList
);
tExprTreeDestroy
(
pNode
,
NULL
);
return
code
;
}
SBufferWriter
bw
=
tbufInitWriter
(
NULL
,
false
);
// TRY(0) {
exprTreeToBinary
(
&
bw
,
pNode
);
// } CATCH(code) {
// tbufCloseWriter(&bw);
// UNUSED(code);
// TODO: other error handling
// } END_TRY
SSchema
s
=
createSchema
(
TSDB_DATA_TYPE_DOUBLE
,
sizeof
(
double
),
getNewResColId
(),
""
);
assert
(
taosArrayGetSize
(
pColumnList
)
==
0
);
int32_t
len
=
tbufTell
(
&
bw
);
char
*
c
=
tbufGetData
(
&
bw
,
false
);
// set the serialized binary string as the parameter of arithmetic expression
addExprInfoParam
(
&
pExpr
->
base
,
c
,
TSDB_DATA_TYPE_BINARY
,
(
int32_t
)
len
);
// Need to be added to the final result list.
if
(
pQueryInfo
->
exprListLevelIndex
==
0
)
{
addResColumnInfo
(
pQueryInfo
,
exprIndex
,
&
pExpr
->
base
.
resSchema
,
pExpr
);
}
tbufCloseWriter
(
&
bw
);
taosArrayDestroy
(
colList
);
}
else
{
SSchema
s
=
createSchema
(
TSDB_DATA_TYPE_DOUBLE
,
sizeof
(
double
),
getNewResColId
(),
""
);
addResColumnInfo
(
pQueryInfo
,
exprIndex
,
&
s
,
NULL
);
assert
(
taosArrayGetSize
(
pColumnList
)
==
0
);
tExprNode
*
pNode
=
NULL
;
int32_t
ret
=
sqlExprToExprNode
(
&
pNode
,
pItem
->
pNode
,
pQueryInfo
,
pColumnList
,
pMsgBuf
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
tExprTreeDestroy
(
pNode
,
NULL
);
return
buildInvalidOperationMsg
(
pMsgBuf
,
"invalid expression in select clause"
);
}
tExprNode
*
pNode
=
NULL
;
int32_t
ret
=
sqlExprToExprNode
(
&
pNode
,
pItem
->
pNode
,
pQueryInfo
,
pColumnList
,
pMsgBuf
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
tExprTreeDestroy
(
pNode
,
NULL
);
return
buildInvalidOperationMsg
(
pMsgBuf
,
"invalid expression in select clause"
);
}
SExprInfo
*
pExpr
=
createBinaryExprInfo
(
pNode
,
&
s
,
"project"
);
SArray
*
pExprList
=
getCurrentExprList
(
pQueryInfo
);
addExprInfo
(
pExprList
,
exprIndex
,
pExpr
,
pQueryInfo
->
exprListLevelIndex
);
SExprInfo
*
pExpr
=
createBinaryExprInfo
(
pNode
,
&
s
);
SArray
*
pExprList
=
getCurrentExprList
(
pQueryInfo
);
addExprInfo
(
pExprList
,
exprIndex
,
pExpr
,
pQueryInfo
->
exprListLevelIndex
);
setTokenAndResColumnName
(
pItem
,
pExpr
->
base
.
resSchema
.
name
,
pExpr
->
base
.
token
,
TSDB_COL_NAME_LEN
);
setTokenAndResColumnName
(
pItem
,
pExpr
->
base
.
resSchema
.
name
,
pExpr
->
base
.
token
,
TSDB_COL_NAME_LEN
);
// extract columns according to the tExprNode tree
size_t
num
=
taosArrayGetSize
(
pColumnList
);
pExpr
->
base
.
pColumns
=
calloc
(
num
,
sizeof
(
SColumn
));
for
(
int32_t
i
=
0
;
i
<
num
;
++
i
)
{
SColumn
*
pCol
=
taosArrayGet
(
pColumnList
,
i
);
pExpr
->
base
.
pColumns
[
i
]
=
*
pCol
;
// extract columns according to the tExprNode tree
size_t
num
=
taosArrayGetSize
(
pColumnList
);
pExpr
->
base
.
pColumns
=
calloc
(
num
,
sizeof
(
SColumn
));
for
(
int32_t
i
=
0
;
i
<
num
;
++
i
)
{
pExpr
->
base
.
pColumns
[
i
]
=
*
(
SColumn
*
)
taosArrayGet
(
pColumnList
,
i
);
if
(
pCol
->
flag
==
TSDB_COL_NORMAL
)
{
SSchema
sch
=
createSchema
(
pCol
->
info
.
type
,
pCol
->
info
.
bytes
,
pCol
->
info
.
colId
,
pCol
->
name
);
columnListInsert
(
pQueryInfo
->
colList
,
pCol
->
uid
,
&
sch
,
pCol
->
flag
);
}
}
pExpr
->
base
.
numOfCols
=
num
;
pExpr
->
base
.
numOfCols
=
num
;
pExpr
->
base
.
numOfParams
=
1
;
SBufferWriter
bw
=
tbufInitWriter
(
NULL
,
false
);
// TRY(0) {
exprTreeToBinary
(
&
bw
,
pExpr
->
pExpr
);
// } CATCH(code) {
// tbufCloseWriter(&bw);
// UNUSED(code);
// TODO: other error handling
// } END_TRY
pExpr
->
base
.
numOfParams
=
1
;
SBufferWriter
bw
=
tbufInitWriter
(
NULL
,
false
);
// TRY(0) {
exprTreeToBinary
(
&
bw
,
pExpr
->
pExpr
);
// } CATCH(code) {
// tbufCloseWriter(&bw);
// UNUSED(code);
// TODO: other error handling
// } END_TRY
SSqlExpr
*
pSqlExpr
=
&
pExpr
->
base
;
pSqlExpr
->
param
[
0
].
nLen
=
(
int16_t
)
tbufTell
(
&
bw
);
pSqlExpr
->
param
[
0
].
pz
=
tbufGetData
(
&
bw
,
true
);
pSqlExpr
->
param
[
0
].
nType
=
TSDB_DATA_TYPE_BINARY
;
SSqlExpr
*
pSqlExpr
=
&
pExpr
->
base
;
pSqlExpr
->
param
[
0
].
nLen
=
(
int16_t
)
tbufTell
(
&
bw
);
pSqlExpr
->
param
[
0
].
pz
=
tbufGetData
(
&
bw
,
true
);
pSqlExpr
->
param
[
0
].
nType
=
TSDB_DATA_TYPE_BINARY
;
tbufCloseWriter
(
&
bw
);
tbufCloseWriter
(
&
bw
);
// tbufCloseWriter(&bw); // TODO there is a memory leak
}
// tbufCloseWriter(&bw); // TODO there is a memory leak
taosArrayDestroy
(
pColumnList
);
return
TSDB_CODE_SUCCESS
;
...
...
source/libs/parser/src/queryInfoUtil.c
浏览文件 @
4eebc5c4
...
...
@@ -68,8 +68,15 @@ static tExprNode* createFunctionExprNode(const char* funcName, struct SSourcePar
for
(
int32_t
i
=
0
;
i
<
pParam
->
num
;
++
i
)
{
p
[
i
]
=
calloc
(
1
,
sizeof
(
tExprNode
));
p
[
i
]
->
nodeType
=
TEXPR_COL_NODE
;
p
[
i
]
->
pSchema
=
calloc
(
1
,
sizeof
(
SSchema
));
memcpy
(
p
[
i
]
->
pSchema
,
taosArrayGetP
(
pParam
->
pColumnList
,
i
),
sizeof
(
SSchema
));
SColumn
*
pSrc
=
taosArrayGetP
(
pParam
->
pColumnList
,
i
);
SSchema
*
pSchema
=
calloc
(
1
,
sizeof
(
SSchema
));
tstrncpy
(
pSchema
->
name
,
pSrc
->
name
,
tListLen
(
pSchema
->
name
));
pSchema
->
type
=
pSrc
->
info
.
type
;
pSchema
->
bytes
=
pSrc
->
info
.
bytes
;
pSchema
->
colId
=
pSrc
->
info
.
colId
;
p
[
i
]
->
pSchema
=
pSchema
;
}
}
else
{
assert
(
pParam
->
pColumnList
==
NULL
);
...
...
@@ -88,7 +95,7 @@ static tExprNode* createFunctionExprNode(const char* funcName, struct SSourcePar
return
pNode
;
}
SExprInfo
*
createBinaryExprInfo
(
tExprNode
*
pNode
,
SSchema
*
pResSchema
)
{
SExprInfo
*
createBinaryExprInfo
(
tExprNode
*
pNode
,
SSchema
*
pResSchema
,
const
char
*
funcName
)
{
assert
(
pNode
!=
NULL
&&
pResSchema
!=
NULL
);
SExprInfo
*
pExpr
=
calloc
(
1
,
sizeof
(
SExprInfo
));
...
...
@@ -97,6 +104,10 @@ SExprInfo* createBinaryExprInfo(tExprNode* pNode, SSchema* pResSchema) {
}
pExpr
->
pExpr
=
pNode
;
char
*
fName
=
pExpr
->
pExpr
->
_function
.
functionName
;
tstrncpy
(
fName
,
funcName
,
tListLen
(
pExpr
->
pExpr
->
_function
.
functionName
));
memcpy
(
&
pExpr
->
base
.
resSchema
,
pResSchema
,
sizeof
(
SSchema
));
return
pExpr
;
}
...
...
@@ -156,7 +167,7 @@ void addExprInfo(SArray* pExprList, int32_t index, SExprInfo* pExprInfo, int32_t
taosArrayInsert
(
pExprList
,
index
,
&
pExprInfo
);
}
printf
(
"add function
, id:
%s, level:%d, total:%ld
\n
"
,
pExprInfo
->
pExpr
->
_function
.
functionName
,
level
,
taosArrayGetSize
(
pExprList
));
printf
(
"add function
:
%s, level:%d, total:%ld
\n
"
,
pExprInfo
->
pExpr
->
_function
.
functionName
,
level
,
taosArrayGetSize
(
pExprList
));
}
void
updateExprInfo
(
SExprInfo
*
pExprInfo
,
int16_t
functionId
,
int32_t
colId
,
int16_t
srcColumnIndex
,
int16_t
resType
,
int16_t
resSize
)
{
...
...
source/libs/parser/test/parserTests.cpp
浏览文件 @
4eebc5c4
...
...
@@ -13,6 +13,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <function.h>
#include <gtest/gtest.h>
#include <iostream>
#pragma GCC diagnostic ignored "-Wwrite-strings"
...
...
@@ -393,86 +394,91 @@ void sqlCheck(const char* sql, bool valid) {
// destroySqlInfo(&info1);
//}
TEST
(
testCase
,
function_Test10
)
{
sqlCheck
(
"select c from `t.1abc`"
,
true
);
sqlCheck
(
"select length(c) from `t.1abc`"
,
true
);
sqlCheck
(
"select sum(length(a+b)) from `t.1abc`"
,
true
);
sqlCheck
(
"select sum(sum(a+b)) from `t.1abc`"
,
false
);
sqlCheck
(
"select sum(length(a) + length(b)) from `t.1abc`"
,
true
);
sqlCheck
(
"select length(sum(a) + sum(b)) + length(sum(a) + sum(b)) from `t.1abc`"
,
true
);
sqlCheck
(
"select sum(length(sum(a))) from `t.1abc`"
,
true
);
sqlCheck
(
"select cov(a, b) from `t.1abc`"
,
true
);
// sqlCheck("select concat(concat(a,b), concat(a,b)) from `t.1abc`", true);
// sqlCheck("select length(length(length(a))) from `t.1abc`", true);
//TEST(testCase, function_Test10) {
// sqlCheck("select c from `t.1abc`", true);
// sqlCheck("select length(c) from `t.1abc`", true);
// sqlCheck("select sum(length(a+b)) from `t.1abc`", true);
// sqlCheck("select sum(sum(a+b)) from `t.1abc`", false);
// sqlCheck("select sum(length(a) + length(b)) from `t.1abc`", true);
// sqlCheck("select length(sum(a) + sum(b)) + length(sum(a) + sum(b)) from `t.1abc`", true);
// sqlCheck("select sum(length(sum(a))) from `t.1abc`", true);
// sqlCheck("select cov(a, b) from `t.1abc`", true);
//// sqlCheck("select concat(concat(a,b), concat(a,b)) from `t.1abc`", true);
//// sqlCheck("select length(length(length(a))) from `t.1abc`", true);
//}
TEST
(
testCase
,
function_Test6
)
{
SSqlInfo
info1
=
doGenerateAST
(
"select sum(a+b) as a1, first(b*a), count(b+b), count(1), count(42.1) from `t.1abc` interval(10s, 1s)"
);
ASSERT_EQ
(
info1
.
valid
,
true
);
char
msg
[
128
]
=
{
0
};
SMsgBuf
buf
;
buf
.
len
=
128
;
buf
.
buf
=
msg
;
SSqlNode
*
pNode
=
(
SSqlNode
*
)
taosArrayGetP
(((
SArray
*
)
info1
.
list
),
0
);
int32_t
code
=
evaluateSqlNode
(
pNode
,
TSDB_TIME_PRECISION_NANO
,
&
buf
);
ASSERT_EQ
(
code
,
0
);
SMetaReq
req
=
{
0
};
int32_t
ret
=
qParserExtractRequestedMetaInfo
(
&
info1
,
&
req
,
msg
,
128
);
ASSERT_EQ
(
ret
,
0
);
ASSERT_EQ
(
taosArrayGetSize
(
req
.
pTableName
),
1
);
SQueryStmtInfo
*
pQueryInfo
=
createQueryInfo
();
setTableMetaInfo
(
pQueryInfo
,
&
req
);
SSqlNode
*
pSqlNode
=
(
SSqlNode
*
)
taosArrayGetP
(
info1
.
list
,
0
);
ret
=
validateSqlNode
(
pSqlNode
,
pQueryInfo
,
&
buf
);
ASSERT_EQ
(
ret
,
0
);
SArray
*
pExprList
=
pQueryInfo
->
exprList
[
0
];
ASSERT_EQ
(
taosArrayGetSize
(
pExprList
),
5
);
SExprInfo
*
p1
=
(
SExprInfo
*
)
taosArrayGetP
(
pExprList
,
0
);
ASSERT_EQ
(
p1
->
base
.
pColumns
->
uid
,
110
);
ASSERT_EQ
(
p1
->
base
.
numOfParams
,
0
);
ASSERT_EQ
(
p1
->
base
.
resSchema
.
type
,
TSDB_DATA_TYPE_DOUBLE
);
ASSERT_STRCASEEQ
(
p1
->
base
.
resSchema
.
name
,
"a1"
);
ASSERT_EQ
(
p1
->
base
.
pColumns
->
flag
,
TSDB_COL_TMP
);
ASSERT_STRCASEEQ
(
p1
->
base
.
token
,
"sum(a+b)"
);
ASSERT_EQ
(
p1
->
base
.
interBytes
,
16
);
ASSERT_EQ
(
p1
->
pExpr
->
nodeType
,
TEXPR_FUNCTION_NODE
);
ASSERT_STRCASEEQ
(
p1
->
pExpr
->
_function
.
functionName
,
"sum"
);
ASSERT_EQ
(
p1
->
pExpr
->
_function
.
num
,
1
);
tExprNode
*
pParam
=
p1
->
pExpr
->
_function
.
pChild
[
0
];
ASSERT_EQ
(
pParam
->
nodeType
,
TEXPR_COL_NODE
);
ASSERT_STREQ
(
pParam
->
pSchema
->
name
,
"t.1abc.a+b"
);
ASSERT_EQ
(
taosArrayGetSize
(
pQueryInfo
->
colList
),
3
);
ASSERT_EQ
(
pQueryInfo
->
fieldsInfo
.
numOfOutput
,
5
);
SExprInfo
*
p2
=
(
SExprInfo
*
)
taosArrayGetP
(
pExprList
,
1
);
ASSERT_EQ
(
p2
->
base
.
pColumns
->
uid
,
110
);
ASSERT_EQ
(
p2
->
base
.
numOfParams
,
0
);
ASSERT_EQ
(
p2
->
base
.
resSchema
.
type
,
TSDB_DATA_TYPE_DOUBLE
);
ASSERT_STRCASEEQ
(
p2
->
base
.
resSchema
.
name
,
"first(b*a)"
);
ASSERT_EQ
(
p2
->
base
.
pColumns
->
flag
,
TSDB_COL_TMP
);
ASSERT_STREQ
(
p2
->
base
.
pColumns
->
name
,
"t.1abc.b*a"
);
ASSERT_STRCASEEQ
(
p2
->
base
.
token
,
"first(b*a)"
);
ASSERT_EQ
(
p2
->
base
.
interBytes
,
24
);
ASSERT_EQ
(
p2
->
pExpr
->
nodeType
,
TEXPR_FUNCTION_NODE
);
ASSERT_STRCASEEQ
(
p2
->
pExpr
->
_function
.
functionName
,
"first"
);
ASSERT_EQ
(
p2
->
pExpr
->
_function
.
num
,
1
);
ASSERT_EQ
(
p2
->
pExpr
->
_function
.
pChild
[
0
]
->
nodeType
,
TEXPR_COL_NODE
);
ASSERT_STREQ
(
p2
->
pExpr
->
_function
.
pChild
[
0
]
->
pSchema
->
name
,
"t.1abc.b*a"
);
destroyQueryInfo
(
pQueryInfo
);
qParserClearupMetaRequestInfo
(
&
req
);
destroySqlInfo
(
&
info1
);
}
//TEST(testCase, function_Test6) {
// SSqlInfo info1 = doGenerateAST("select sum(a+b) as a1, first(b*a), count(b+b), count(1), count(42.1) from `t.1abc` interval(10s, 1s)");
// ASSERT_EQ(info1.valid, true);
//
// char msg[128] = {0};
// SMsgBuf buf;
// buf.len = 128;
// buf.buf = msg;
//
// SSqlNode* pNode = (SSqlNode*) taosArrayGetP(((SArray*)info1.list), 0);
// int32_t code = evaluateSqlNode(pNode, TSDB_TIME_PRECISION_NANO, &buf);
// ASSERT_EQ(code, 0);
//
// SMetaReq req = {0};
// int32_t ret = qParserExtractRequestedMetaInfo(&info1, &req, msg, 128);
// ASSERT_EQ(ret, 0);
// ASSERT_EQ(taosArrayGetSize(req.pTableName), 1);
//
// SQueryStmtInfo* pQueryInfo = createQueryInfo();
// setTableMetaInfo(pQueryInfo, &req);
//
// SSqlNode* pSqlNode = (SSqlNode*)taosArrayGetP(info1.list, 0);
// ret = validateSqlNode(pSqlNode, pQueryInfo, &buf);
// ASSERT_EQ(ret, 0);
//
// SArray* pExprList = pQueryInfo->exprList[0];
// ASSERT_EQ(taosArrayGetSize(pExprList), 5);
//
// SExprInfo* p1 = (SExprInfo*) taosArrayGetP(pExprList, 0);
// ASSERT_EQ(p1->base.pColumns->uid, 110);
// ASSERT_EQ(p1->base.numOfParams, 0);
// ASSERT_EQ(p1->base.resSchema.type, TSDB_DATA_TYPE_DOUBLE);
// ASSERT_STRCASEEQ(p1->base.resSchema.name, "a1");
// ASSERT_EQ(p1->base.pColumns->flag, TSDB_COL_NORMAL);
// ASSERT_STRCASEEQ(p1->base.token, "sum(a+b)");
// ASSERT_EQ(p1->base.interBytes, 16);
// ASSERT_EQ(p1->pExpr->nodeType, TEXPR_UNARYEXPR_NODE);
// ASSERT_EQ(p1->pExpr->_function.functionId, FUNCTION_SUM);
// ASSERT_TRUE(p1->pExpr->_node.pRight == NULL);
//
// tExprNode* pParam = p1->pExpr->_node.pLeft;
//
// ASSERT_EQ(pParam->nodeType, TEXPR_BINARYEXPR_NODE);
// ASSERT_EQ(pParam->_node.optr, TSDB_BINARY_OP_ADD);
// ASSERT_EQ(pParam->_node.pLeft->nodeType, TEXPR_COL_NODE);
// ASSERT_EQ(pParam->_node.pRight->nodeType, TEXPR_COL_NODE);
//
// ASSERT_EQ(taosArrayGetSize(pQueryInfo->colList), 3);
// ASSERT_EQ(pQueryInfo->fieldsInfo.numOfOutput, 5);
//
// SExprInfo* p2 = (SExprInfo*) taosArrayGetP(pExprList, 1);
// ASSERT_EQ(p2->base.pColumns->uid, 110);
// ASSERT_EQ(p2->base.numOfParams, 0);
// ASSERT_EQ(p2->base.resSchema.type, TSDB_DATA_TYPE_DOUBLE);
// ASSERT_STRCASEEQ(p2->base.resSchema.name, "first(b*a)");
// ASSERT_EQ(p2->base.pColumns->flag, TSDB_COL_NORMAL);
// ASSERT_STRCASEEQ(p2->base.token, "first(b*a)");
// ASSERT_EQ(p2->base.interBytes, 24);
// ASSERT_EQ(p2->pExpr->nodeType, TEXPR_UNARYEXPR_NODE);
// ASSERT_EQ(p2->pExpr->_function.functionId, FUNCTION_FIRST);
// ASSERT_TRUE(p2->pExpr->_node.pRight == NULL);
//
// destroyQueryInfo(pQueryInfo);
// qParserClearupMetaRequestInfo(&req);
// destroySqlInfo(&info1);
//}
//
//TEST(testCase, function_Test7) {
// SSqlInfo info1 = doGenerateAST("select count(a+b),count(1) from `t.1abc` interval(10s, 1s)");
// ASSERT_EQ(info1.valid, true);
...
...
@@ -510,7 +516,7 @@ TEST(testCase, function_Test10) {
// ASSERT_STRCASEEQ(p1->base.token, "count(a+b)");
// ASSERT_EQ(p1->base.interBytes, 8);
// ASSERT_EQ(p1->pExpr->nodeType, TEXPR_UNARYEXPR_NODE);
// ASSERT_
EQ(p1->pExpr->_function.functionId, FUNCTION_COUNT
);
// ASSERT_
STREQ(p1->pExpr->_function.functionName, "count"
);
// ASSERT_TRUE(p1->pExpr->_node.pRight == NULL);
//
// tExprNode* pParam = p1->pExpr->_node.pLeft;
...
...
@@ -566,7 +572,7 @@ TEST(testCase, function_Test10) {
// ASSERT_EQ(p1->base.interBytes, 16);
//
// ASSERT_EQ(p1->pExpr->nodeType, TEXPR_UNARYEXPR_NODE);
// ASSERT_
EQ(p1->pExpr->_function.functionId, FUNCTION_TOP
);
// ASSERT_
STRCASEEQ(p1->pExpr->_function.functionName, "top"
);
// ASSERT_TRUE(p1->pExpr->_node.pRight == NULL);
//
// tExprNode* pParam = p1->pExpr->_node.pLeft;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录