Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
dgr6878
TDengine
提交
58258bc9
T
TDengine
项目概览
dgr6878
/
TDengine
与 Fork 源项目一致
Fork自
taosdata / TDengine
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
TDengine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
58258bc9
编写于
4月 03, 2020
作者:
H
hjxilinx
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
[td-97] add express tree serialize and deserilize function and the corresponding unit test cases.
上级
ce88438b
变更
10
展开全部
显示空白变更内容
内联
并排
Showing
10 changed file
with
808 addition
and
66 deletion
+808
-66
.gitignore
.gitignore
+1
-0
src/client/src/tscSQLParser.c
src/client/src/tscSQLParser.c
+6
-6
src/client/src/tscUtil.c
src/client/src/tscUtil.c
+1
-1
src/query/inc/qast.h
src/query/inc/qast.h
+9
-4
src/query/src/qast.c
src/query/src/qast.c
+115
-11
src/query/src/queryExecutor.c
src/query/src/queryExecutor.c
+1
-1
src/query/tests/astTest.cpp
src/query/tests/astTest.cpp
+630
-0
src/util/inc/tbuffer.h
src/util/inc/tbuffer.h
+35
-34
src/util/src/tbuffer.c
src/util/src/tbuffer.c
+9
-8
src/vnode/tsdb/src/tsdbRead.c
src/vnode/tsdb/src/tsdbRead.c
+1
-1
未找到文件。
.gitignore
浏览文件 @
58258bc9
...
...
@@ -12,6 +12,7 @@ rpms/
mac/
*.pyc
*.tmp
*.swp
src/connector/nodejs/node_modules/
src/connector/nodejs/out/
tests/test/
...
...
src/client/src/tscSQLParser.c
浏览文件 @
58258bc9
...
...
@@ -117,7 +117,7 @@ static int32_t doCheckForCreateFromStable(SSqlObj* pSql, SSqlInfo* pInfo);
static
int32_t
doCheckForStream
(
SSqlObj
*
pSql
,
SSqlInfo
*
pInfo
);
static
int32_t
doCheckForQuery
(
SSqlObj
*
pSql
,
SQuerySQL
*
pQuerySql
,
int32_t
index
);
static
int32_t
tSQLBinaryExprCreateFromSqlExpr
(
tExprNode
**
pExpr
,
tSQLExpr
*
pAst
,
int32_t
*
num
,
static
int32_t
convertSyntaxTreeToExprTree
(
tExprNode
**
pExpr
,
tSQLExpr
*
pAst
,
int32_t
*
num
,
SColIndexEx
**
pColIndex
,
SSqlExprInfo
*
pExprInfo
);
/*
...
...
@@ -1211,9 +1211,9 @@ int32_t parseSelectClause(SSqlCmd* pCmd, int32_t clauseIndex, tSQLExprList* pSel
tExprNode
*
pNode
=
NULL
;
SColIndexEx
*
pColIndex
=
NULL
;
int32_t
ret
=
tSQLBinaryExprCreateFromSqlExpr
(
&
pNode
,
pItem
->
pNode
,
&
pBinExprInfo
->
numOfCols
,
&
pColIndex
,
&
pQueryInfo
->
exprsInfo
);
int32_t
ret
=
convertSyntaxTreeToExprTree
(
&
pNode
,
pItem
->
pNode
,
&
pBinExprInfo
->
numOfCols
,
&
pColIndex
,
&
pQueryInfo
->
exprsInfo
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
t
SQLBinaryExpr
Destroy
(
&
pNode
,
NULL
);
t
ExprTree
Destroy
(
&
pNode
,
NULL
);
return
invalidSqlErrMsg
(
pQueryInfo
->
msg
,
"invalid expression in select clause"
);
}
...
...
@@ -5807,20 +5807,20 @@ int32_t doCheckForQuery(SSqlObj* pSql, SQuerySQL* pQuerySql, int32_t index) {
return
TSDB_CODE_SUCCESS
;
// Does not build query message here
}
static
int32_t
tSQLBinaryExprCreateFromSqlExpr
(
tExprNode
**
pExpr
,
tSQLExpr
*
pAst
,
int32_t
*
num
,
static
int32_t
convertSyntaxTreeToExprTree
(
tExprNode
**
pExpr
,
tSQLExpr
*
pAst
,
int32_t
*
num
,
SColIndexEx
**
pColIndex
,
SSqlExprInfo
*
pExprInfo
)
{
tExprNode
*
pLeft
=
NULL
;
tExprNode
*
pRight
=
NULL
;
if
(
pAst
->
pLeft
!=
NULL
)
{
int32_t
ret
=
tSQLBinaryExprCreateFromSqlExpr
(
&
pLeft
,
pAst
->
pLeft
,
num
,
pColIndex
,
pExprInfo
);
int32_t
ret
=
convertSyntaxTreeToExprTree
(
&
pLeft
,
pAst
->
pLeft
,
num
,
pColIndex
,
pExprInfo
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
return
ret
;
}
}
if
(
pAst
->
pRight
!=
NULL
)
{
int32_t
ret
=
tSQLBinaryExprCreateFromSqlExpr
(
&
pRight
,
pAst
->
pRight
,
num
,
pColIndex
,
pExprInfo
);
int32_t
ret
=
convertSyntaxTreeToExprTree
(
&
pRight
,
pAst
->
pRight
,
num
,
pColIndex
,
pExprInfo
);
if
(
ret
!=
TSDB_CODE_SUCCESS
)
{
return
ret
;
}
...
...
src/client/src/tscUtil.c
浏览文件 @
58258bc9
...
...
@@ -1105,7 +1105,7 @@ void tscClearFieldInfo(SFieldInfo* pFieldInfo) {
for
(
int32_t
i
=
0
;
i
<
pFieldInfo
->
numOfOutputCols
;
++
i
)
{
if
(
pFieldInfo
->
pExpr
[
i
]
!=
NULL
)
{
t
SQLBinaryExpr
Destroy
(
&
pFieldInfo
->
pExpr
[
i
]
->
binExprInfo
.
pBinExpr
,
NULL
);
t
ExprTree
Destroy
(
&
pFieldInfo
->
pExpr
[
i
]
->
binExprInfo
.
pBinExpr
,
NULL
);
tfree
(
pFieldInfo
->
pExpr
[
i
]
->
binExprInfo
.
pReqColumns
);
tfree
(
pFieldInfo
->
pExpr
[
i
]);
}
...
...
src/query/inc/qast.h
浏览文件 @
58258bc9
...
...
@@ -16,6 +16,7 @@
#ifndef TDENGINE_TAST_H
#define TDENGINE_TAST_H
#include <tbuffer.h>
#ifdef __cplusplus
extern
"C"
{
#endif
...
...
@@ -80,7 +81,7 @@ void tSQLBinaryExprFromString(tExprNode **pExpr, SSchema *pSchema, int32_t numOf
void
tSQLBinaryExprToString
(
tExprNode
*
pExpr
,
char
*
dst
,
int32_t
*
len
);
void
t
SQLBinaryExpr
Destroy
(
tExprNode
**
pExprs
,
void
(
*
fp
)(
void
*
));
void
t
ExprTree
Destroy
(
tExprNode
**
pExprs
,
void
(
*
fp
)(
void
*
));
void
tSQLBinaryExprTraverse
(
tExprNode
*
pExpr
,
SSkipList
*
pSkipList
,
SArray
*
result
,
SBinaryFilterSupp
*
param
);
...
...
@@ -91,6 +92,10 @@ void tSQLBinaryExprTrv(tExprNode *pExprs, int32_t *val, int16_t *ids);
uint8_t
getBinaryExprOptr
(
SSQLToken
*
pToken
);
SBuffer
exprTreeToBinary
(
tExprNode
*
pExprTree
);
tExprNode
*
exprTreeFromBinary
(
const
void
*
pBuf
,
size_t
size
);
#ifdef __cplusplus
}
#endif
...
...
src/query/src/qast.c
浏览文件 @
58258bc9
...
...
@@ -13,10 +13,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os.h"
#include "tutil.h"
#include "tbuffer.h"
#include "qast.h"
#include "qsqlparser.h"
#include "qsyntaxtreefunction.h"
#include "taosdef.h"
...
...
@@ -26,9 +27,7 @@
#include "tstoken.h"
#include "ttokendef.h"
#include "../../client/inc/tschemautil.h"
#include "qast.h"
#include "tarray.h"
#include "tskiplist.h"
...
...
@@ -246,6 +245,7 @@ static tExprNode *createSyntaxTree(SSchema *pSchema, int32_t numOfCols, char *st
if
(
t0
.
type
==
TK_RP
)
{
return
NULL
;
}
pLeft
=
tExprNodeCreate
(
pSchema
,
numOfCols
,
&
t0
);
}
...
...
@@ -433,25 +433,34 @@ static void tExprNodeDestroy(tExprNode *pNode, void (*fp)(void *)) {
}
if
(
pNode
->
nodeType
==
TSQL_NODE_EXPR
)
{
t
SQLBinaryExpr
Destroy
(
&
pNode
,
fp
);
t
ExprTree
Destroy
(
&
pNode
,
fp
);
}
else
if
(
pNode
->
nodeType
==
TSQL_NODE_VALUE
)
{
tVariantDestroy
(
pNode
->
pVal
);
}
else
if
(
pNode
->
nodeType
==
TSQL_NODE_COL
)
{
free
(
pNode
->
pSchema
);
}
free
(
pNode
);
}
void
t
SQLBinaryExpr
Destroy
(
tExprNode
**
pExpr
,
void
(
*
fp
)(
void
*
))
{
void
t
ExprTree
Destroy
(
tExprNode
**
pExpr
,
void
(
*
fp
)(
void
*
))
{
if
(
*
pExpr
==
NULL
)
{
return
;
}
tExprNodeDestroy
((
*
pExpr
)
->
_node
.
pLeft
,
fp
);
tExprNodeDestroy
((
*
pExpr
)
->
_node
.
pRight
,
fp
);
if
((
*
pExpr
)
->
nodeType
==
TSQL_NODE_EXPR
)
{
tExprTreeDestroy
(
&
(
*
pExpr
)
->
_node
.
pLeft
,
fp
);
tExprTreeDestroy
(
&
(
*
pExpr
)
->
_node
.
pRight
,
fp
);
if
(
fp
!=
NULL
)
{
fp
((
*
pExpr
)
->
_node
.
info
);
}
}
else
if
((
*
pExpr
)
->
nodeType
==
TSQL_NODE_VALUE
)
{
tVariantDestroy
((
*
pExpr
)
->
pVal
);
free
((
*
pExpr
)
->
pVal
);
}
else
if
((
*
pExpr
)
->
nodeType
==
TSQL_NODE_COL
)
{
free
((
*
pExpr
)
->
pSchema
);
}
free
(
*
pExpr
);
*
pExpr
=
NULL
;
...
...
@@ -916,3 +925,98 @@ void tSQLBinaryExprTrv(tExprNode *pExprs, int32_t *val, int16_t *ids) {
(
*
val
)
+=
1
;
}
}
static
int32_t
exprTreeToBinaryImpl
(
tExprNode
*
pExprTree
,
SBuffer
*
pBuf
)
{
tbufWrite
(
pBuf
,
&
pExprTree
->
nodeType
,
sizeof
(
pExprTree
->
nodeType
));
if
(
pExprTree
->
nodeType
==
TSQL_NODE_VALUE
)
{
tVariant
*
pVal
=
pExprTree
->
pVal
;
tbufWrite
(
pBuf
,
&
pVal
->
nType
,
sizeof
(
pVal
->
nType
));
if
(
pVal
->
nType
==
TSDB_DATA_TYPE_BINARY
)
{
tbufWrite
(
pBuf
,
&
pVal
->
nLen
,
sizeof
(
pVal
->
nLen
));
tbufWrite
(
pBuf
,
pVal
->
pz
,
pVal
->
nLen
);
}
else
{
tbufWrite
(
pBuf
,
&
pVal
->
pz
,
sizeof
(
pVal
->
i64Key
));
}
}
else
if
(
pExprTree
->
nodeType
==
TSQL_NODE_COL
)
{
SSchema
*
pSchema
=
pExprTree
->
pSchema
;
tbufWrite
(
pBuf
,
&
pSchema
->
colId
,
sizeof
(
pSchema
->
colId
));
tbufWrite
(
pBuf
,
&
pSchema
->
bytes
,
sizeof
(
pSchema
->
bytes
));
tbufWrite
(
pBuf
,
&
pSchema
->
type
,
sizeof
(
pSchema
->
type
));
int32_t
len
=
strlen
(
pSchema
->
name
);
tbufWriteStringLen
(
pBuf
,
pSchema
->
name
,
len
);
}
else
if
(
pExprTree
->
nodeType
==
TSQL_NODE_EXPR
)
{
tbufWrite
(
pBuf
,
&
pExprTree
->
_node
.
optr
,
sizeof
(
pExprTree
->
_node
.
optr
));
tbufWrite
(
pBuf
,
&
pExprTree
->
_node
.
hasPK
,
sizeof
(
pExprTree
->
_node
.
hasPK
));
exprTreeToBinaryImpl
(
pExprTree
->
_node
.
pLeft
,
pBuf
);
exprTreeToBinaryImpl
(
pExprTree
->
_node
.
pRight
,
pBuf
);
}
}
SBuffer
exprTreeToBinary
(
tExprNode
*
pExprTree
)
{
SBuffer
buf
=
{
0
};
if
(
pExprTree
==
NULL
)
{
return
buf
;
}
int32_t
code
=
tbufBeginWrite
(
&
buf
);
if
(
code
!=
0
)
{
return
buf
;
}
exprTreeToBinaryImpl
(
pExprTree
,
&
buf
);
return
buf
;
}
static
tExprNode
*
exprTreeFromBinaryImpl
(
tExprNode
**
pExprTree
,
SBuffer
*
pBuf
)
{
tExprNode
*
pExpr
=
calloc
(
1
,
sizeof
(
tExprNode
));
tbufReadToBuffer
(
pBuf
,
&
pExpr
->
nodeType
,
sizeof
(
pExpr
->
nodeType
));
if
(
pExpr
->
nodeType
==
TSQL_NODE_VALUE
)
{
tVariant
*
pVal
=
calloc
(
1
,
sizeof
(
tVariant
));
tbufReadToBuffer
(
pBuf
,
&
pVal
->
nType
,
sizeof
(
pVal
->
nType
));
if
(
pVal
->
nType
==
TSDB_DATA_TYPE_BINARY
)
{
tbufReadToBuffer
(
pBuf
,
&
pVal
->
nLen
,
sizeof
(
pVal
->
nLen
));
pVal
->
pz
=
calloc
(
1
,
pVal
->
nLen
+
1
);
tbufReadToBuffer
(
pBuf
,
pVal
->
pz
,
pVal
->
nLen
);
}
else
{
tbufReadToBuffer
(
pBuf
,
&
pVal
->
pz
,
sizeof
(
pVal
->
i64Key
));
}
pExpr
->
pVal
=
pVal
;
}
else
if
(
pExpr
->
nodeType
==
TSQL_NODE_COL
)
{
SSchema
*
pSchema
=
calloc
(
1
,
sizeof
(
SSchema
));
tbufReadToBuffer
(
pBuf
,
&
pSchema
->
colId
,
sizeof
(
pSchema
->
colId
));
tbufReadToBuffer
(
pBuf
,
&
pSchema
->
bytes
,
sizeof
(
pSchema
->
bytes
));
tbufReadToBuffer
(
pBuf
,
&
pSchema
->
type
,
sizeof
(
pSchema
->
type
));
tbufReadToString
(
pBuf
,
pSchema
->
name
,
TSDB_COL_NAME_LEN
);
pExpr
->
pSchema
=
pSchema
;
}
else
if
(
pExpr
->
nodeType
==
TSQL_NODE_EXPR
)
{
tbufReadToBuffer
(
pBuf
,
&
pExpr
->
_node
.
optr
,
sizeof
(
pExpr
->
_node
.
optr
));
tbufReadToBuffer
(
pBuf
,
&
pExpr
->
_node
.
hasPK
,
sizeof
(
pExpr
->
_node
.
hasPK
));
exprTreeFromBinaryImpl
(
&
pExpr
->
_node
.
pLeft
,
pBuf
);
exprTreeFromBinaryImpl
(
&
pExpr
->
_node
.
pRight
,
pBuf
);
assert
(
pExpr
->
_node
.
pLeft
!=
NULL
&&
pExpr
->
_node
.
pRight
!=
NULL
);
}
*
pExprTree
=
pExpr
;
}
tExprNode
*
exprTreeFromBinary
(
const
void
*
pBuf
,
size_t
size
)
{
SBuffer
rbuf
=
{
0
};
int32_t
code
=
tbufBeginRead
(
&
rbuf
,
pBuf
,
size
);
tExprNode
*
pExprNode
=
NULL
;
exprTreeFromBinaryImpl
(
&
pExprNode
,
&
rbuf
);
return
pExprNode
;
}
\ No newline at end of file
src/query/src/queryExecutor.c
浏览文件 @
58258bc9
...
...
@@ -5961,7 +5961,7 @@ static void freeQInfo(SQInfo *pQInfo) {
if
(
pBinExprInfo
->
numOfCols
>
0
)
{
tfree
(
pBinExprInfo
->
pReqColumns
);
t
SQLBinaryExpr
Destroy
(
&
pBinExprInfo
->
pBinExpr
,
NULL
);
t
ExprTree
Destroy
(
&
pBinExprInfo
->
pBinExpr
,
NULL
);
}
}
...
...
src/query/tests/astTest.cpp
0 → 100644
浏览文件 @
58258bc9
此差异已折叠。
点击以展开。
src/util/inc/tbuffer.h
浏览文件 @
58258bc9
...
...
@@ -13,14 +13,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <setjmp.h>
#ifndef TDENGINE_TBUFFER_H
#define TDENGINE_TBUFFER_H
#include "setjmp.h"
#include "os.h"
#ifdef __cplusplus
extern
"C"
{
#endif
/*
SBuffer can be used to read or write a buffer, but cannot be used for both
...
...
@@ -85,7 +86,6 @@ typedef struct {
size_t
size
;
}
SBuffer
;
// common functions can be used in both read & write
#define tbufThrowError(buf, code) longjmp((buf)->jb, (code))
size_t
tbufTell
(
SBuffer
*
buf
);
...
...
@@ -93,15 +93,13 @@ size_t tbufSeekTo(SBuffer* buf, size_t pos);
size_t
tbufSkip
(
SBuffer
*
buf
,
size_t
size
);
void
tbufClose
(
SBuffer
*
buf
,
bool
keepData
);
// basic read functions
#define tbufBeginRead(buf,
data, len) (((buf)->data = (char*)data), ((buf)->pos = 0), ((buf)->size = ((
data) == NULL) ? 0 : (len)), setjmp((buf)->jb))
#define tbufBeginRead(buf,
_data, len) ((buf)->data = (char*)(_data), ((buf)->pos = 0), ((buf)->size = ((_
data) == NULL) ? 0 : (len)), setjmp((buf)->jb))
char
*
tbufRead
(
SBuffer
*
buf
,
size_t
size
);
void
tbufReadToBuffer
(
SBuffer
*
buf
,
void
*
dst
,
size_t
size
);
const
char
*
tbufReadString
(
SBuffer
*
buf
,
size_t
*
len
);
size_t
tbufReadToString
(
SBuffer
*
buf
,
char
*
dst
,
size_t
size
);
// basic write functions
#define tbufBeginWrite(buf) ((buf)->data = NULL, ((buf)->pos = 0), ((buf)->size = 0), setjmp((buf)->jb))
void
tbufEnsureCapacity
(
SBuffer
*
buf
,
size_t
size
);
...
...
@@ -111,7 +109,6 @@ void tbufWriteAt(SBuffer* buf, size_t pos, const void* data, size_t size);
void
tbufWriteStringLen
(
SBuffer
*
buf
,
const
char
*
str
,
size_t
len
);
void
tbufWriteString
(
SBuffer
*
buf
,
const
char
*
str
);
// read & write function for primitive types
#ifndef TBUFFER_DEFINE_FUNCTION
#define TBUFFER_DEFINE_FUNCTION(type, name) \
...
...
@@ -120,17 +117,21 @@ void tbufWriteString(SBuffer* buf, const char* str);
void tbufWrite##name##At(SBuffer* buf, size_t pos, type data);
#endif
TBUFFER_DEFINE_FUNCTION
(
bool
,
Bool
)
TBUFFER_DEFINE_FUNCTION
(
char
,
Char
)
TBUFFER_DEFINE_FUNCTION
(
int8_t
,
Int8
)
TBUFFER_DEFINE_FUNCTION
(
uint8_t
,
Unt8
)
TBUFFER_DEFINE_FUNCTION
(
int16_t
,
Int16
)
TBUFFER_DEFINE_FUNCTION
(
uint16_t
,
Uint16
)
TBUFFER_DEFINE_FUNCTION
(
int32_t
,
Int32
)
TBUFFER_DEFINE_FUNCTION
(
uint32_t
,
Uint32
)
TBUFFER_DEFINE_FUNCTION
(
int64_t
,
Int64
)
TBUFFER_DEFINE_FUNCTION
(
uint64_t
,
Uint64
)
TBUFFER_DEFINE_FUNCTION
(
float
,
Float
)
TBUFFER_DEFINE_FUNCTION
(
double
,
Double
)
TBUFFER_DEFINE_FUNCTION
(
bool
,
Bool
)
TBUFFER_DEFINE_FUNCTION
(
char
,
Char
)
TBUFFER_DEFINE_FUNCTION
(
int8_t
,
Int8
)
TBUFFER_DEFINE_FUNCTION
(
uint8_t
,
Unt8
)
TBUFFER_DEFINE_FUNCTION
(
int16_t
,
Int16
)
TBUFFER_DEFINE_FUNCTION
(
uint16_t
,
Uint16
)
TBUFFER_DEFINE_FUNCTION
(
int32_t
,
Int32
)
TBUFFER_DEFINE_FUNCTION
(
uint32_t
,
Uint32
)
TBUFFER_DEFINE_FUNCTION
(
int64_t
,
Int64
)
TBUFFER_DEFINE_FUNCTION
(
uint64_t
,
Uint64
)
TBUFFER_DEFINE_FUNCTION
(
float
,
Float
)
TBUFFER_DEFINE_FUNCTION
(
double
,
Double
)
#ifdef __cplusplus
}
#endif
#endif
\ No newline at end of file
src/util/src/tbuffer.c
浏览文件 @
58258bc9
...
...
@@ -30,7 +30,7 @@
tbufWriteAt(buf, pos, &data, sizeof(data));\
}
#include "
../inc/
tbuffer.h"
#include "tbuffer.h"
////////////////////////////////////////////////////////////////////////////////
...
...
@@ -125,6 +125,7 @@ char* tbufGetData(SBuffer* buf, bool takeOver) {
buf
->
size
=
0
;
buf
->
data
=
NULL
;
}
return
ret
;
}
...
...
src/vnode/tsdb/src/tsdbRead.c
浏览文件 @
58258bc9
...
...
@@ -1310,7 +1310,7 @@ static int32_t doQueryTableList(STable* pSTable, SArray* pRes, const char* pCond
};
tSQLBinaryExprTraverse
(
pExpr
,
pSTable
->
pIndex
,
pRes
,
&
supp
);
t
SQLBinaryExpr
Destroy
(
&
pExpr
,
tSQLListTraverseDestroyInfo
);
t
ExprTree
Destroy
(
&
pExpr
,
tSQLListTraverseDestroyInfo
);
tansformQueryResult
(
pRes
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录