Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
王长生-AnH
TDengine
提交
87363383
T
TDengine
项目概览
王长生-AnH
/
TDengine
与 Fork 源项目一致
Fork自
taosdata / TDengine
通知
5
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,发现更多精彩内容 >>
提交
87363383
编写于
4月 23, 2020
作者:
weixin_48148422
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
TD-97: search in array is done
上级
de50a2d7
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
256 addition
and
140 deletion
+256
-140
src/inc/taosdef.h
src/inc/taosdef.h
+14
-0
src/query/inc/qast.h
src/query/inc/qast.h
+0
-2
src/query/inc/tvariant.h
src/query/inc/tvariant.h
+2
-0
src/query/src/qast.c
src/query/src/qast.c
+143
-100
src/query/src/tvariant.c
src/query/src/tvariant.c
+23
-0
src/tsdb/src/tsdbRead.c
src/tsdb/src/tsdbRead.c
+3
-1
src/util/inc/tarray.h
src/util/inc/tarray.h
+14
-1
src/util/src/tarray.c
src/util/src/tarray.c
+24
-2
src/util/src/tcompare.c
src/util/src/tcompare.c
+33
-34
未找到文件。
src/inc/taosdef.h
浏览文件 @
87363383
...
@@ -32,6 +32,20 @@ extern "C" {
...
@@ -32,6 +32,20 @@ extern "C" {
#define TSKEY int64_t
#define TSKEY int64_t
#endif
#endif
// Data type definition
#define TSDB_DATA_TYPE_NULL 0 // 1 bytes
#define TSDB_DATA_TYPE_BOOL 1 // 1 bytes
#define TSDB_DATA_TYPE_TINYINT 2 // 1 byte
#define TSDB_DATA_TYPE_SMALLINT 3 // 2 bytes
#define TSDB_DATA_TYPE_INT 4 // 4 bytes
#define TSDB_DATA_TYPE_BIGINT 5 // 8 bytes
#define TSDB_DATA_TYPE_FLOAT 6 // 4 bytes
#define TSDB_DATA_TYPE_DOUBLE 7 // 8 bytes
#define TSDB_DATA_TYPE_BINARY 8 // string
#define TSDB_DATA_TYPE_TIMESTAMP 9 // 8 bytes
#define TSDB_DATA_TYPE_NCHAR 10 // unicode string
#define TSDB_DATA_TYPE_ARRAY 11 // only used 'in' query to hold the values
// Bytes for each type.
// Bytes for each type.
extern
const
int32_t
TYPE_BYTES
[
11
];
extern
const
int32_t
TYPE_BYTES
[
11
];
// TODO: replace and remove code below
// TODO: replace and remove code below
...
...
src/query/inc/qast.h
浏览文件 @
87363383
...
@@ -35,7 +35,6 @@ enum {
...
@@ -35,7 +35,6 @@ enum {
TSQL_NODE_EXPR
=
0x1
,
TSQL_NODE_EXPR
=
0x1
,
TSQL_NODE_COL
=
0x2
,
TSQL_NODE_COL
=
0x2
,
TSQL_NODE_VALUE
=
0x4
,
TSQL_NODE_VALUE
=
0x4
,
TSQL_NODE_ARRAY
=
0x8
,
};
};
typedef
bool
(
*
__result_filter_fn_t
)(
const
void
*
,
void
*
);
typedef
bool
(
*
__result_filter_fn_t
)(
const
void
*
,
void
*
);
...
@@ -71,7 +70,6 @@ typedef struct tExprNode {
...
@@ -71,7 +70,6 @@ typedef struct tExprNode {
struct
tExprNode
*
pRight
;
// right child pointer
struct
tExprNode
*
pRight
;
// right child pointer
}
_node
;
}
_node
;
struct
SSchema
*
pSchema
;
struct
SSchema
*
pSchema
;
SArray
*
array
;
tVariant
*
pVal
;
tVariant
*
pVal
;
};
};
}
tExprNode
;
}
tExprNode
;
...
...
src/query/inc/tvariant.h
浏览文件 @
87363383
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
#define TDENGINE_TVARIANT_H
#define TDENGINE_TVARIANT_H
#include "tstoken.h"
#include "tstoken.h"
#include "tarray.h"
#ifdef __cplusplus
#ifdef __cplusplus
extern
"C"
{
extern
"C"
{
...
@@ -31,6 +32,7 @@ typedef struct tVariant {
...
@@ -31,6 +32,7 @@ typedef struct tVariant {
double
dKey
;
double
dKey
;
char
*
pz
;
char
*
pz
;
wchar_t
*
wpz
;
wchar_t
*
wpz
;
SArray
*
arr
;
// only for 'in' query to hold value list, not value for a field
};
};
}
tVariant
;
}
tVariant
;
...
...
src/query/src/qast.c
浏览文件 @
87363383
...
@@ -460,9 +460,6 @@ void tExprTreeDestroy(tExprNode **pExpr, void (*fp)(void *)) {
...
@@ -460,9 +460,6 @@ void tExprTreeDestroy(tExprNode **pExpr, void (*fp)(void *)) {
free
((
*
pExpr
)
->
pVal
);
free
((
*
pExpr
)
->
pVal
);
}
else
if
((
*
pExpr
)
->
nodeType
==
TSQL_NODE_COL
)
{
}
else
if
((
*
pExpr
)
->
nodeType
==
TSQL_NODE_COL
)
{
free
((
*
pExpr
)
->
pSchema
);
free
((
*
pExpr
)
->
pSchema
);
}
else
if
((
*
pExpr
)
->
nodeType
==
TSQL_NODE_ARRAY
)
{
// BUGBUG: memory leak, need free elements in the array
taosArrayDestroy
((
*
pExpr
)
->
array
);
}
}
free
(
*
pExpr
);
free
(
*
pExpr
);
...
@@ -524,38 +521,48 @@ static int32_t setQueryCond(tQueryInfo *queryColInfo, SQueryCond* pCond) {
...
@@ -524,38 +521,48 @@ static int32_t setQueryCond(tQueryInfo *queryColInfo, SQueryCond* pCond) {
if
(
optr
==
TSDB_RELATION_GREATER
||
optr
==
TSDB_RELATION_GREATER_EQUAL
||
if
(
optr
==
TSDB_RELATION_GREATER
||
optr
==
TSDB_RELATION_GREATER_EQUAL
||
optr
==
TSDB_RELATION_EQUAL
||
optr
==
TSDB_RELATION_NOT_EQUAL
)
{
optr
==
TSDB_RELATION_EQUAL
||
optr
==
TSDB_RELATION_NOT_EQUAL
)
{
pCond
->
start
=
calloc
(
1
,
sizeof
(
tVariant
));
pCond
->
start
=
calloc
(
1
,
sizeof
(
tVariant
));
tVariantAssign
(
&
pCond
->
start
->
v
,
&
queryColInfo
->
q
);
tVariantAssign
(
&
pCond
->
start
->
v
,
&
queryColInfo
->
q
);
pCond
->
start
->
optr
=
queryColInfo
->
optr
;
pCond
->
start
->
optr
=
queryColInfo
->
optr
;
}
else
if
(
optr
==
TSDB_RELATION_LESS
||
optr
==
TSDB_RELATION_LESS_EQUAL
)
{
}
else
if
(
optr
==
TSDB_RELATION_LESS
||
optr
==
TSDB_RELATION_LESS_EQUAL
)
{
pCond
->
end
=
calloc
(
1
,
sizeof
(
tVariant
));
pCond
->
end
=
calloc
(
1
,
sizeof
(
tVariant
));
tVariantAssign
(
&
pCond
->
end
->
v
,
&
queryColInfo
->
q
);
tVariantAssign
(
&
pCond
->
end
->
v
,
&
queryColInfo
->
q
);
pCond
->
end
->
optr
=
queryColInfo
->
optr
;
pCond
->
end
->
optr
=
queryColInfo
->
optr
;
}
else
if
(
optr
==
TSDB_RELATION_IN
)
{
printf
(
"relation is in
\n
"
);
}
else
if
(
optr
==
TSDB_RELATION_LIKE
)
{
printf
(
"relation is like
\n
"
);
}
}
return
TSDB_CODE_SUCCESS
;
return
TSDB_CODE_SUCCESS
;
}
}
static
void
tQuery
OnSkipList
(
SSkipList
*
pSkipList
,
SQueryCond
*
pCond
,
int32_t
type
,
SArray
*
result
)
{
static
void
tQuery
IndexColumn
(
SSkipList
*
pSkipList
,
tQueryInfo
*
pQueryInfo
,
SArray
*
result
)
{
SSkipListIterator
*
iter
=
NULL
;
SSkipListIterator
*
iter
=
NULL
;
int32_t
type
=
pQueryInfo
->
q
.
nType
;
if
(
pCond
->
start
!=
NULL
)
{
iter
=
tSkipListCreateIterFromVal
(
pSkipList
,
(
char
*
)
&
pCond
->
start
->
v
.
i64Key
,
type
,
TSDB_ORDER_ASC
);
SQueryCond
cond
=
{
0
};
setQueryCond
(
pQueryInfo
,
&
cond
);
if
(
cond
.
start
!=
NULL
)
{
iter
=
tSkipListCreateIterFromVal
(
pSkipList
,
(
char
*
)
&
cond
.
start
->
v
.
i64Key
,
type
,
TSDB_ORDER_ASC
);
}
else
{
}
else
{
iter
=
tSkipListCreateIterFromVal
(
pSkipList
,
(
char
*
)
&
pCond
->
end
->
v
.
i64Key
,
type
,
TSDB_ORDER_DESC
);
iter
=
tSkipListCreateIterFromVal
(
pSkipList
,
(
char
*
)
&
cond
.
end
->
v
.
i64Key
,
type
,
TSDB_ORDER_DESC
);
}
}
__compar_fn_t
func
=
getComparFunc
(
pSkipList
->
keyInfo
.
type
,
type
,
0
);
__compar_fn_t
func
=
getComparFunc
(
pSkipList
->
keyInfo
.
type
,
type
,
0
);
if
(
pCond
->
start
!=
NULL
)
{
if
(
cond
.
start
!=
NULL
)
{
int32_t
optr
=
pCond
->
start
->
optr
;
int32_t
optr
=
cond
.
start
->
optr
;
if
(
optr
==
TSDB_RELATION_EQUAL
)
{
if
(
optr
==
TSDB_RELATION_EQUAL
)
{
while
(
tSkipListIterNext
(
iter
))
{
while
(
tSkipListIterNext
(
iter
))
{
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
int32_t
ret
=
func
(
SL_GET_NODE_KEY
(
pSkipList
,
pNode
),
&
pCond
->
start
->
v
.
i64Key
);
int32_t
ret
=
func
(
SL_GET_NODE_KEY
(
pSkipList
,
pNode
),
&
cond
.
start
->
v
.
i64Key
);
if
(
ret
==
0
)
{
if
(
ret
==
0
)
{
taosArrayPush
(
result
,
SL_GET_NODE_DATA
(
pNode
));
taosArrayPush
(
result
,
SL_GET_NODE_DATA
(
pNode
));
}
else
{
}
else
{
...
@@ -570,7 +577,7 @@ static void tQueryOnSkipList(SSkipList* pSkipList, SQueryCond* pCond, int32_t ty
...
@@ -570,7 +577,7 @@ static void tQueryOnSkipList(SSkipList* pSkipList, SQueryCond* pCond, int32_t ty
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
if
(
comp
)
{
if
(
comp
)
{
ret
=
func
(
SL_GET_NODE_KEY
(
pSkipList
,
pNode
),
&
pCond
->
start
->
v
.
i64Key
);
ret
=
func
(
SL_GET_NODE_KEY
(
pSkipList
,
pNode
),
&
cond
.
start
->
v
.
i64Key
);
assert
(
ret
>=
0
);
assert
(
ret
>=
0
);
}
}
...
@@ -587,7 +594,7 @@ static void tQueryOnSkipList(SSkipList* pSkipList, SQueryCond* pCond, int32_t ty
...
@@ -587,7 +594,7 @@ static void tQueryOnSkipList(SSkipList* pSkipList, SQueryCond* pCond, int32_t ty
assert
(
0
);
assert
(
0
);
}
}
}
else
{
}
else
{
int32_t
optr
=
pCond
->
end
->
optr
;
int32_t
optr
=
cond
.
end
->
optr
;
if
(
optr
==
TSDB_RELATION_LESS
||
optr
==
TSDB_RELATION_LESS_EQUAL
)
{
if
(
optr
==
TSDB_RELATION_LESS
||
optr
==
TSDB_RELATION_LESS_EQUAL
)
{
bool
comp
=
true
;
bool
comp
=
true
;
...
@@ -597,7 +604,7 @@ static void tQueryOnSkipList(SSkipList* pSkipList, SQueryCond* pCond, int32_t ty
...
@@ -597,7 +604,7 @@ static void tQueryOnSkipList(SSkipList* pSkipList, SQueryCond* pCond, int32_t ty
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
if
(
comp
)
{
if
(
comp
)
{
ret
=
func
(
SL_GET_NODE_KEY
(
pSkipList
,
pNode
),
&
pCond
->
end
->
v
.
i64Key
);
ret
=
func
(
SL_GET_NODE_KEY
(
pSkipList
,
pNode
),
&
cond
.
end
->
v
.
i64Key
);
assert
(
ret
<=
0
);
assert
(
ret
<=
0
);
}
}
...
@@ -788,6 +795,36 @@ static void tSQLBinaryTraverseOnSkipList(tExprNode *pExpr, SArray *pResult, SSki
...
@@ -788,6 +795,36 @@ static void tSQLBinaryTraverseOnSkipList(tExprNode *pExpr, SArray *pResult, SSki
}
}
}
}
#include <tsdbMain.h>
static
void
tQueryIndexlessColumn
(
SSkipList
*
pSkipList
,
tQueryInfo
*
pQueryInfo
,
SArray
*
result
)
{
SSkipListIterator
*
iter
=
tSkipListCreateIter
(
pSkipList
);
while
(
tSkipListIterNext
(
iter
))
{
bool
addToResult
=
false
;
SSkipListNode
*
pNode
=
tSkipListIterGet
(
iter
);
STable
*
table
=
*
(
STable
**
)
SL_GET_NODE_DATA
(
pNode
);
if
(
pQueryInfo
->
colIndex
==
TSDB_TBNAME_COLUMN_INDEX
)
{
if
(
pQueryInfo
->
optr
==
TSDB_RELATION_IN
)
{
addToResult
=
pQueryInfo
->
compare
(
table
->
name
,
pQueryInfo
->
q
.
arr
);
}
else
if
(
pQueryInfo
->
optr
==
TSDB_RELATION_LIKE
)
{
addToResult
=
pQueryInfo
->
compare
(
table
->
name
,
pQueryInfo
->
q
.
pz
);
}
}
else
{
// TODO: other columns
}
if
(
addToResult
)
{
taosArrayPush
(
result
,
table
);
}
}
}
// post-root order traverse syntax tree
// post-root order traverse syntax tree
void
tExprTreeTraverse
(
tExprNode
*
pExpr
,
SSkipList
*
pSkipList
,
SArray
*
result
,
SBinaryFilterSupp
*
param
)
{
void
tExprTreeTraverse
(
tExprNode
*
pExpr
,
SSkipList
*
pSkipList
,
SArray
*
result
,
SBinaryFilterSupp
*
param
)
{
if
(
pExpr
==
NULL
)
{
if
(
pExpr
==
NULL
)
{
...
@@ -797,95 +834,99 @@ void tExprTreeTraverse(tExprNode *pExpr, SSkipList *pSkipList, SArray *result, S
...
@@ -797,95 +834,99 @@ void tExprTreeTraverse(tExprNode *pExpr, SSkipList *pSkipList, SArray *result, S
tExprNode
*
pLeft
=
pExpr
->
_node
.
pLeft
;
tExprNode
*
pLeft
=
pExpr
->
_node
.
pLeft
;
tExprNode
*
pRight
=
pExpr
->
_node
.
pRight
;
tExprNode
*
pRight
=
pExpr
->
_node
.
pRight
;
//
recursive traverse left child branch
//
column project
if
(
pLeft
->
nodeType
==
TSQL_NODE_EXPR
||
pRight
->
nodeType
=
=
TSQL_NODE_EXPR
)
{
if
(
pLeft
->
nodeType
!=
TSQL_NODE_EXPR
&&
pRight
->
nodeType
!
=
TSQL_NODE_EXPR
)
{
uint8_t
weight
=
pLeft
->
_node
.
hasPK
+
pRight
->
_node
.
hasPK
;
assert
(
pLeft
->
nodeType
==
TSQL_NODE_COL
&&
pRight
->
nodeType
==
TSQL_NODE_VALUE
)
;
if
(
weight
==
0
&&
taosArrayGetSize
(
result
)
>
0
&&
pSkipList
==
NULL
)
{
param
->
setupInfoFn
(
pExpr
,
param
->
pExtInfo
);
/**
if
(
pSkipList
==
NULL
)
{
* Perform the filter operation based on the initial filter result, which is obtained from filtering from index.
tSQLListTraverseOnResult
(
pExpr
,
param
->
fp
,
result
);
* Since no index presented, the filter operation is done by scan all elements in the result set.
return
;
*
}
* if the query is a high selectivity filter, only small portion of meters are retrieved.
*/
tQueryInfo
*
pQueryInfo
=
pExpr
->
_node
.
info
;
if
(
pQueryInfo
->
colIndex
==
0
&&
pQueryInfo
->
optr
!=
TSDB_RELATION_LIKE
)
{
tQueryIndexColumn
(
pSkipList
,
pQueryInfo
,
result
);
}
else
{
tQueryIndexlessColumn
(
pSkipList
,
pQueryInfo
,
result
);
}
return
;
}
// recursive traverse left child branch
uint8_t
weight
=
pLeft
->
_node
.
hasPK
+
pRight
->
_node
.
hasPK
;
if
(
weight
==
0
)
{
if
(
taosArrayGetSize
(
result
)
>
0
&&
pSkipList
==
NULL
)
{
/**
* Perform the filter operation based on the initial filter result, which is obtained from filtering from index.
* Since no index presented, the filter operation is done by scan all elements in the result set.
*
* if the query is a high selectivity filter, only small portion of meters are retrieved.
*/
exprTreeTraverseImpl
(
pExpr
,
result
,
param
);
exprTreeTraverseImpl
(
pExpr
,
result
,
param
);
}
else
if
(
weight
==
0
)
{
}
else
{
/**
/**
* apply the hierarchical expression to every node in skiplist for find the qualified nodes
* apply the hierarchical expression to every node in skiplist for find the qualified nodes
*/
*/
assert
(
taosArrayGetSize
(
result
)
==
0
);
assert
(
taosArrayGetSize
(
result
)
==
0
);
tSQLBinaryTraverseOnSkipList
(
pExpr
,
result
,
pSkipList
,
param
);
tSQLBinaryTraverseOnSkipList
(
pExpr
,
result
,
pSkipList
,
param
);
}
else
if
(
weight
==
2
||
(
weight
==
1
&&
pExpr
->
_node
.
optr
==
TSDB_RELATION_OR
))
{
}
SArray
*
rLeft
=
taosArrayInit
(
10
,
POINTER_BYTES
);
return
;
SArray
*
rRight
=
taosArrayInit
(
10
,
POINTER_BYTES
);
}
tExprTreeTraverse
(
pLeft
,
pSkipList
,
rLeft
,
param
);
if
(
weight
==
2
||
(
weight
==
1
&&
pExpr
->
_node
.
optr
==
TSDB_RELATION_OR
))
{
tExprTreeTraverse
(
pRight
,
pSkipList
,
rRight
,
param
);
SArray
*
rLeft
=
taosArrayInit
(
10
,
POINTER_BYTES
);
SArray
*
rRight
=
taosArrayInit
(
10
,
POINTER_BYTES
);
if
(
pExpr
->
_node
.
optr
==
TSDB_RELATION_AND
)
{
// CROSS
intersect
(
rLeft
,
rRight
,
result
);
tExprTreeTraverse
(
pLeft
,
pSkipList
,
rLeft
,
param
);
}
else
if
(
pExpr
->
_node
.
optr
==
TSDB_RELATION_OR
)
{
// or
tExprTreeTraverse
(
pRight
,
pSkipList
,
rRight
,
param
);
merge
(
rLeft
,
rRight
,
result
);
}
else
{
assert
(
false
);
}
taosArrayDestroy
(
rLeft
);
if
(
pExpr
->
_node
.
optr
==
TSDB_RELATION_AND
)
{
// CROSS
taosArrayDestroy
(
rRight
);
intersect
(
rLeft
,
rRight
,
result
);
}
else
if
(
pExpr
->
_node
.
optr
==
TSDB_RELATION_OR
)
{
// or
merge
(
rLeft
,
rRight
,
result
);
}
else
{
}
else
{
/*
assert
(
false
);
* (weight == 1 && pExpr->nSQLBinaryOptr == TSDB_RELATION_AND) is handled here
}
*
* first, we filter results based on the skiplist index, which is the initial filter stage,
* then, we conduct the secondary filter operation based on the result from the initial filter stage.
*/
assert
(
pExpr
->
_node
.
optr
==
TSDB_RELATION_AND
);
tExprNode
*
pFirst
=
NULL
;
tExprNode
*
pSecond
=
NULL
;
if
(
pLeft
->
_node
.
hasPK
==
1
)
{
pFirst
=
pLeft
;
pSecond
=
pRight
;
}
else
{
pFirst
=
pRight
;
pSecond
=
pLeft
;
}
assert
(
pFirst
!=
pSecond
&&
pFirst
!=
NULL
&&
pSecond
!=
NULL
);
taosArrayDestroy
(
rLeft
);
taosArrayDestroy
(
rRight
);
return
;
}
// we filter the result based on the skiplist index in the first place
/*
tExprTreeTraverse
(
pFirst
,
pSkipList
,
result
,
param
);
* (weight == 1 && pExpr->nSQLBinaryOptr == TSDB_RELATION_AND) is handled here
*
* first, we filter results based on the skiplist index, which is the initial filter stage,
* then, we conduct the secondary filter operation based on the result from the initial filter stage.
*/
assert
(
pExpr
->
_node
.
optr
==
TSDB_RELATION_AND
);
tExprNode
*
pFirst
=
NULL
;
tExprNode
*
pSecond
=
NULL
;
if
(
pLeft
->
_node
.
hasPK
==
1
)
{
pFirst
=
pLeft
;
pSecond
=
pRight
;
}
else
{
pFirst
=
pRight
;
pSecond
=
pLeft
;
}
/*
assert
(
pFirst
!=
pSecond
&&
pFirst
!=
NULL
&&
pSecond
!=
NULL
);
* recursively perform the filter operation based on the initial results,
* So, we do not set the skip list index as a parameter
*/
tExprTreeTraverse
(
pSecond
,
NULL
,
result
,
param
);
}
}
else
{
// column project
assert
(
pLeft
->
nodeType
==
TSQL_NODE_COL
&&
(
pRight
->
nodeType
==
TSQL_NODE_VALUE
||
pRight
->
nodeType
==
TSQL_NODE_ARRAY
));
param
->
setupInfoFn
(
pExpr
,
param
->
pExtInfo
);
// we filter the result based on the skiplist index in the first place
if
(
pSkipList
==
NULL
)
{
tExprTreeTraverse
(
pFirst
,
pSkipList
,
result
,
param
);
tSQLListTraverseOnResult
(
pExpr
,
param
->
fp
,
result
);
}
else
{
/*
tQueryInfo
*
pQueryInfo
=
pExpr
->
_node
.
info
;
* recursively perform the filter operation based on the initial results,
* So, we do not set the skip list index as a parameter
if
(
pQueryInfo
->
colIndex
==
0
&&
pQueryInfo
->
optr
!=
TSDB_RELATION_LIKE
)
{
*/
SQueryCond
cond
=
{
0
};
tExprTreeTraverse
(
pSecond
,
NULL
,
result
,
param
);
/*int32_t ret = */
setQueryCond
(
pQueryInfo
,
&
cond
);
tQueryOnSkipList
(
pSkipList
,
&
cond
,
pQueryInfo
->
q
.
nType
,
result
);
}
else
{
/* Brutal force scan the whole skip list to find the appropriate result,
* since the filter is not applied to indexed column.
*/
assert
(
0
);
// result->num = tSkipListIterateList(pSkipList, (tSkipListNode ***)&result->pRes, fp, queryColInfo);
}
}
}
}
}
void
tSQLBinaryExprCalcTraverse
(
tExprNode
*
pExprs
,
int32_t
numOfRows
,
char
*
pOutput
,
void
*
param
,
int32_t
order
,
void
tSQLBinaryExprCalcTraverse
(
tExprNode
*
pExprs
,
int32_t
numOfRows
,
char
*
pOutput
,
void
*
param
,
int32_t
order
,
char
*
(
*
getSourceDataBlock
)(
void
*
,
char
*
,
int32_t
))
{
char
*
(
*
getSourceDataBlock
)(
void
*
,
char
*
,
int32_t
))
{
if
(
pExprs
==
NULL
)
{
if
(
pExprs
==
NULL
)
{
...
@@ -1090,12 +1131,6 @@ tExprNode* exprTreeFromBinary(const void* pBuf, size_t size) {
...
@@ -1090,12 +1131,6 @@ tExprNode* exprTreeFromBinary(const void* pBuf, size_t size) {
return
exprTreeFromBinaryImpl
(
&
rbuf
);
return
exprTreeFromBinaryImpl
(
&
rbuf
);
}
}
static
int
cmpStrInArray
(
const
void
*
a
,
const
void
*
b
)
{
const
char
*
x
=
*
(
const
char
**
)
a
;
const
char
*
y
=
*
(
const
char
**
)
b
;
return
strcmp
(
x
,
y
);
}
tExprNode
*
exprTreeFromTableName
(
const
char
*
tbnameCond
)
{
tExprNode
*
exprTreeFromTableName
(
const
char
*
tbnameCond
)
{
if
(
!
tbnameCond
)
{
if
(
!
tbnameCond
)
{
return
NULL
;
return
NULL
;
...
@@ -1132,6 +1167,7 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
...
@@ -1132,6 +1167,7 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
expr
->
_node
.
pRight
=
right
;
expr
->
_node
.
pRight
=
right
;
if
(
strncmp
(
tbnameCond
,
QUERY_COND_REL_PREFIX_LIKE
,
QUERY_COND_REL_PREFIX_LIKE_LEN
)
==
0
)
{
if
(
strncmp
(
tbnameCond
,
QUERY_COND_REL_PREFIX_LIKE
,
QUERY_COND_REL_PREFIX_LIKE_LEN
)
==
0
)
{
right
->
nodeType
=
TSQL_NODE_VALUE
;
expr
->
_node
.
optr
=
TSDB_RELATION_LIKE
;
expr
->
_node
.
optr
=
TSDB_RELATION_LIKE
;
tVariant
*
pVal
=
calloc
(
1
,
sizeof
(
tVariant
));
tVariant
*
pVal
=
calloc
(
1
,
sizeof
(
tVariant
));
if
(
pVal
==
NULL
)
{
if
(
pVal
==
NULL
)
{
...
@@ -1147,8 +1183,15 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
...
@@ -1147,8 +1183,15 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
memcpy
(
pVal
->
pz
,
tbnameCond
+
QUERY_COND_REL_PREFIX_LIKE_LEN
,
len
);
memcpy
(
pVal
->
pz
,
tbnameCond
+
QUERY_COND_REL_PREFIX_LIKE_LEN
,
len
);
}
else
if
(
strncmp
(
tbnameCond
,
QUERY_COND_REL_PREFIX_IN
,
QUERY_COND_REL_PREFIX_IN_LEN
)
==
0
)
{
}
else
if
(
strncmp
(
tbnameCond
,
QUERY_COND_REL_PREFIX_IN
,
QUERY_COND_REL_PREFIX_IN_LEN
)
==
0
)
{
right
->
nodeType
=
TSQL_NODE_VALUE
;
expr
->
_node
.
optr
=
TSDB_RELATION_IN
;
expr
->
_node
.
optr
=
TSDB_RELATION_IN
;
right
->
array
=
taosArrayInit
(
2
,
sizeof
(
char
*
));
tVariant
*
pVal
=
calloc
(
1
,
sizeof
(
tVariant
));
if
(
pVal
==
NULL
)
{
// TODO:
}
right
->
pVal
=
pVal
;
pVal
->
nType
=
TSDB_DATA_TYPE_ARRAY
;
pVal
->
arr
=
taosArrayInit
(
2
,
sizeof
(
char
*
));
const
char
*
cond
=
tbnameCond
+
QUERY_COND_REL_PREFIX_IN_LEN
;
const
char
*
cond
=
tbnameCond
+
QUERY_COND_REL_PREFIX_IN_LEN
;
for
(
const
char
*
e
=
cond
;
*
e
!=
0
;
e
++
)
{
for
(
const
char
*
e
=
cond
;
*
e
!=
0
;
e
++
)
{
...
@@ -1158,7 +1201,7 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
...
@@ -1158,7 +1201,7 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
memcpy
(
p
,
cond
,
len
);
memcpy
(
p
,
cond
,
len
);
p
[
len
-
1
]
=
0
;
p
[
len
-
1
]
=
0
;
cond
+=
len
;
cond
+=
len
;
taosArrayPush
(
right
->
array
,
&
p
);
taosArrayPush
(
pVal
->
arr
,
&
p
);
}
}
}
}
...
@@ -1167,10 +1210,10 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
...
@@ -1167,10 +1210,10 @@ tExprNode* exprTreeFromTableName(const char* tbnameCond) {
char
*
p
=
malloc
(
len
);
char
*
p
=
malloc
(
len
);
memcpy
(
p
,
cond
,
len
);
memcpy
(
p
,
cond
,
len
);
p
[
len
-
1
]
=
0
;
p
[
len
-
1
]
=
0
;
taosArrayPush
(
right
->
array
,
&
p
);
taosArrayPush
(
pVal
->
arr
,
&
p
);
}
}
taosArraySort
(
right
->
array
,
cmpStrInArray
);
taosArraySort
String
(
pVal
->
arr
);
}
}
return
expr
;
return
expr
;
...
...
src/query/src/tvariant.c
浏览文件 @
87363383
...
@@ -130,6 +130,17 @@ void tVariantDestroy(tVariant *pVar) {
...
@@ -130,6 +130,17 @@ void tVariantDestroy(tVariant *pVar) {
tfree
(
pVar
->
pz
);
tfree
(
pVar
->
pz
);
pVar
->
nLen
=
0
;
pVar
->
nLen
=
0
;
}
}
// NOTE: this is only for string array
if
(
pVar
->
nType
==
TSDB_DATA_TYPE_ARRAY
)
{
size_t
num
=
taosArrayGetSize
(
pVar
->
arr
);
for
(
size_t
i
=
0
;
i
<
num
;
i
++
)
{
void
*
p
=
taosArrayGetP
(
pVar
->
arr
,
i
);
free
(
p
);
}
taosArrayDestroy
(
pVar
->
arr
);
pVar
->
arr
=
NULL
;
}
}
}
void
tVariantAssign
(
tVariant
*
pDst
,
const
tVariant
*
pSrc
)
{
void
tVariantAssign
(
tVariant
*
pDst
,
const
tVariant
*
pSrc
)
{
...
@@ -145,6 +156,18 @@ void tVariantAssign(tVariant *pDst, const tVariant *pSrc) {
...
@@ -145,6 +156,18 @@ void tVariantAssign(tVariant *pDst, const tVariant *pSrc) {
pDst
->
pz
=
calloc
(
1
,
len
);
pDst
->
pz
=
calloc
(
1
,
len
);
memcpy
(
pDst
->
pz
,
pSrc
->
pz
,
len
);
memcpy
(
pDst
->
pz
,
pSrc
->
pz
,
len
);
return
;
}
// this is only for string array
if
(
pSrc
->
nType
==
TSDB_DATA_TYPE_ARRAY
)
{
size_t
num
=
taosArrayGetSize
(
pSrc
->
arr
);
pDst
->
arr
=
taosArrayInit
(
num
,
sizeof
(
char
*
));
for
(
size_t
i
=
0
;
i
<
num
;
i
++
)
{
char
*
p
=
(
char
*
)
taosArrayGetP
(
pSrc
->
arr
,
i
);
char
*
n
=
strdup
(
p
);
taosArrayPush
(
pDst
->
arr
,
&
n
);
}
}
}
}
}
...
...
src/tsdb/src/tsdbRead.c
浏览文件 @
87363383
...
@@ -1213,7 +1213,9 @@ void filterPrepare(void* expr, void* param) {
...
@@ -1213,7 +1213,9 @@ void filterPrepare(void* expr, void* param) {
pInfo
->
compare
=
getComparFunc
(
pSchema
->
type
,
pCond
->
nType
,
pInfo
->
optr
);
pInfo
->
compare
=
getComparFunc
(
pSchema
->
type
,
pCond
->
nType
,
pInfo
->
optr
);
tVariantAssign
(
&
pInfo
->
q
,
pCond
);
tVariantAssign
(
&
pInfo
->
q
,
pCond
);
tVariantTypeSetType
(
&
pInfo
->
q
,
pInfo
->
sch
.
type
);
if
(
pInfo
->
optr
!=
TSDB_RELATION_IN
)
{
tVariantTypeSetType
(
&
pInfo
->
q
,
pInfo
->
sch
.
type
);
}
}
}
int32_t
doCompare
(
const
char
*
f1
,
const
char
*
f2
,
int32_t
type
,
size_t
size
)
{
int32_t
doCompare
(
const
char
*
f1
,
const
char
*
f2
,
int32_t
type
,
size_t
size
)
{
...
...
src/util/inc/tarray.h
浏览文件 @
87363383
...
@@ -119,13 +119,26 @@ void taosArrayDestroy(SArray* pArray);
...
@@ -119,13 +119,26 @@ void taosArrayDestroy(SArray* pArray);
*/
*/
void
taosArraySort
(
SArray
*
pArray
,
int
(
*
compar
)(
const
void
*
,
const
void
*
));
void
taosArraySort
(
SArray
*
pArray
,
int
(
*
compar
)(
const
void
*
,
const
void
*
));
/**
* sort string array
* @param pArray
*/
void
taosArraySortString
(
SArray
*
pArray
);
/**
/**
* search the array
* search the array
* @param pArray
* @param pArray
* @param compar
* @param compar
* @param key
* @param key
*/
*/
void
*
taosArraySearch
(
SArray
*
pArray
,
int
(
*
compar
)(
const
void
*
,
const
void
*
),
const
void
*
key
);
void
*
taosArraySearch
(
const
SArray
*
pArray
,
int
(
*
compar
)(
const
void
*
,
const
void
*
),
const
void
*
key
);
/**
* search the array
* @param pArray
* @param key
*/
char
*
taosArraySearchString
(
const
SArray
*
pArray
,
const
char
*
key
);
#ifdef __cplusplus
#ifdef __cplusplus
}
}
...
...
src/util/src/tarray.c
浏览文件 @
87363383
...
@@ -27,7 +27,7 @@ void* taosArrayInit(size_t size, size_t elemSize) {
...
@@ -27,7 +27,7 @@ void* taosArrayInit(size_t size, size_t elemSize) {
return
NULL
;
return
NULL
;
}
}
pArray
->
pData
=
calloc
(
size
,
elemSize
*
size
);
pArray
->
pData
=
calloc
(
size
,
elemSize
);
if
(
pArray
->
pData
==
NULL
)
{
if
(
pArray
->
pData
==
NULL
)
{
free
(
pArray
);
free
(
pArray
);
return
NULL
;
return
NULL
;
...
@@ -192,10 +192,32 @@ void taosArraySort(SArray* pArray, int (*compar)(const void*, const void*)) {
...
@@ -192,10 +192,32 @@ void taosArraySort(SArray* pArray, int (*compar)(const void*, const void*)) {
qsort
(
pArray
->
pData
,
pArray
->
size
,
pArray
->
elemSize
,
compar
);
qsort
(
pArray
->
pData
,
pArray
->
size
,
pArray
->
elemSize
,
compar
);
}
}
void
*
taosArraySearch
(
SArray
*
pArray
,
int
(
*
compar
)(
const
void
*
,
const
void
*
),
const
void
*
key
)
{
void
*
taosArraySearch
(
const
SArray
*
pArray
,
int
(
*
compar
)(
const
void
*
,
const
void
*
),
const
void
*
key
)
{
assert
(
pArray
!=
NULL
);
assert
(
pArray
!=
NULL
);
assert
(
compar
!=
NULL
);
assert
(
compar
!=
NULL
);
assert
(
key
!=
NULL
);
assert
(
key
!=
NULL
);
return
bsearch
(
key
,
pArray
->
pData
,
pArray
->
size
,
pArray
->
elemSize
,
compar
);
return
bsearch
(
key
,
pArray
->
pData
,
pArray
->
size
,
pArray
->
elemSize
,
compar
);
}
}
static
int
taosArrayCompareString
(
const
void
*
a
,
const
void
*
b
)
{
const
char
*
x
=
*
(
const
char
**
)
a
;
const
char
*
y
=
*
(
const
char
**
)
b
;
return
strcmp
(
x
,
y
);
}
void
taosArraySortString
(
SArray
*
pArray
)
{
assert
(
pArray
!=
NULL
);
qsort
(
pArray
->
pData
,
pArray
->
size
,
pArray
->
elemSize
,
taosArrayCompareString
);
}
char
*
taosArraySearchString
(
const
SArray
*
pArray
,
const
char
*
key
)
{
assert
(
pArray
!=
NULL
);
assert
(
key
!=
NULL
);
void
*
p
=
bsearch
(
&
key
,
pArray
->
pData
,
pArray
->
size
,
pArray
->
elemSize
,
taosArrayCompareString
);
if
(
p
==
NULL
)
{
return
NULL
;
}
return
*
(
char
**
)
p
;
}
\ No newline at end of file
src/util/src/tcompare.c
浏览文件 @
87363383
#include "taosdef.h"
#include "taosdef.h"
#include "tcompare.h"
#include "tcompare.h"
#include <tarray.h>
#include "tutil.h"
#include "tutil.h"
int32_t
compareInt32Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareInt32Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
ret
=
GET_INT32_VAL
(
pLeft
)
-
GET_INT32_VAL
(
pRight
);
int32_t
left
=
GET_INT32_VAL
(
pLeft
),
right
=
GET_INT32_VAL
(
pRight
);
if
(
ret
==
0
)
{
if
(
left
>
right
)
return
1
;
return
0
;
if
(
left
<
right
)
return
-
1
;
}
else
{
return
0
;
return
ret
>
0
?
1
:
-
1
;
}
}
}
int32_t
compareInt64Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareInt64Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int64_t
ret
=
GET_INT64_VAL
(
pLeft
)
-
GET_INT64_VAL
(
pRight
);
int64_t
left
=
GET_INT64_VAL
(
pLeft
),
right
=
GET_INT64_VAL
(
pRight
);
if
(
ret
==
0
)
{
if
(
left
>
right
)
return
1
;
return
0
;
if
(
left
<
right
)
return
-
1
;
}
else
{
return
0
;
return
ret
>
0
?
1
:
-
1
;
}
}
}
int32_t
compareInt16Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareInt16Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
ret
=
GET_INT16_VAL
(
pLeft
)
-
GET_INT16_VAL
(
pRight
);
int16_t
left
=
GET_INT16_VAL
(
pLeft
),
right
=
GET_INT16_VAL
(
pRight
);
if
(
ret
==
0
)
{
if
(
left
>
right
)
return
1
;
return
0
;
if
(
left
<
right
)
return
-
1
;
}
else
{
return
0
;
return
ret
>
0
?
1
:
-
1
;
}
}
}
int32_t
compareInt8Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareInt8Val
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
ret
=
GET_INT8_VAL
(
pLeft
)
-
GET_INT8_VAL
(
pRight
);
int8_t
left
=
GET_INT8_VAL
(
pLeft
),
right
=
GET_INT8_VAL
(
pRight
);
if
(
ret
==
0
)
{
if
(
left
>
right
)
return
1
;
return
0
;
if
(
left
<
right
)
return
-
1
;
}
else
{
return
0
;
return
ret
>
0
?
1
:
-
1
;
}
}
}
int32_t
compareIntDoubleVal
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareIntDoubleVal
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
...
@@ -69,12 +61,7 @@ int32_t compareDoubleVal(const void *pLeft, const void *pRight) {
...
@@ -69,12 +61,7 @@ int32_t compareDoubleVal(const void *pLeft, const void *pRight) {
}
}
int32_t
compareStrVal
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareStrVal
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
ret
=
strcmp
(
pLeft
,
pRight
);
return
(
int32_t
)
strcmp
(
pLeft
,
pRight
);
if
(
ret
==
0
)
{
return
0
;
}
else
{
return
ret
>
0
?
1
:
-
1
;
}
}
}
int32_t
compareWStrVal
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
int32_t
compareWStrVal
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
...
@@ -228,6 +215,11 @@ static UNUSED_FUNC int32_t compareStrPatternComp(const void* pLeft, const void*
...
@@ -228,6 +215,11 @@ static UNUSED_FUNC int32_t compareStrPatternComp(const void* pLeft, const void*
return
(
ret
==
TSDB_PATTERN_MATCH
)
?
0
:
1
;
return
(
ret
==
TSDB_PATTERN_MATCH
)
?
0
:
1
;
}
}
static
int32_t
compareStrInList
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
const
SArray
*
arr
=
(
const
SArray
*
)
pRight
;
return
taosArraySearchString
(
arr
,
&
pLeft
)
==
NULL
?
0
:
1
;
}
static
UNUSED_FUNC
int32_t
compareWStrPatternComp
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
static
UNUSED_FUNC
int32_t
compareWStrPatternComp
(
const
void
*
pLeft
,
const
void
*
pRight
)
{
SPatternCompareInfo
pInfo
=
{
'%'
,
'_'
};
SPatternCompareInfo
pInfo
=
{
'%'
,
'_'
};
...
@@ -250,7 +242,6 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
...
@@ -250,7 +242,6 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
case
TSDB_DATA_TYPE_BIGINT
:
case
TSDB_DATA_TYPE_BIGINT
:
case
TSDB_DATA_TYPE_TIMESTAMP
:
{
case
TSDB_DATA_TYPE_TIMESTAMP
:
{
// assert(type == filterDataType);
// assert(type == filterDataType);
if
(
filterDataType
==
TSDB_DATA_TYPE_BIGINT
||
filterDataType
==
TSDB_DATA_TYPE_TIMESTAMP
)
{
if
(
filterDataType
==
TSDB_DATA_TYPE_BIGINT
||
filterDataType
==
TSDB_DATA_TYPE_TIMESTAMP
)
{
comparFn
=
compareInt64Val
;
comparFn
=
compareInt64Val
;
}
else
if
(
filterDataType
>=
TSDB_DATA_TYPE_FLOAT
&&
filterDataType
<=
TSDB_DATA_TYPE_DOUBLE
)
{
}
else
if
(
filterDataType
>=
TSDB_DATA_TYPE_FLOAT
&&
filterDataType
<=
TSDB_DATA_TYPE_DOUBLE
)
{
...
@@ -259,6 +250,7 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
...
@@ -259,6 +250,7 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
break
;
break
;
}
}
case
TSDB_DATA_TYPE_BOOL
:
{
case
TSDB_DATA_TYPE_BOOL
:
{
if
(
filterDataType
>=
TSDB_DATA_TYPE_BOOL
&&
filterDataType
<=
TSDB_DATA_TYPE_BIGINT
)
{
if
(
filterDataType
>=
TSDB_DATA_TYPE_BOOL
&&
filterDataType
<=
TSDB_DATA_TYPE_BIGINT
)
{
comparFn
=
compareInt32Val
;
comparFn
=
compareInt32Val
;
...
@@ -267,6 +259,7 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
...
@@ -267,6 +259,7 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
}
}
break
;
break
;
}
}
case
TSDB_DATA_TYPE_FLOAT
:
case
TSDB_DATA_TYPE_FLOAT
:
case
TSDB_DATA_TYPE_DOUBLE
:
{
case
TSDB_DATA_TYPE_DOUBLE
:
{
if
(
filterDataType
>=
TSDB_DATA_TYPE_BOOL
&&
filterDataType
<=
TSDB_DATA_TYPE_BIGINT
)
{
if
(
filterDataType
>=
TSDB_DATA_TYPE_BOOL
&&
filterDataType
<=
TSDB_DATA_TYPE_BIGINT
)
{
...
@@ -276,12 +269,18 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
...
@@ -276,12 +269,18 @@ __compar_fn_t getComparFunc(int32_t type, int32_t filterDataType, int32_t optr)
}
}
break
;
break
;
}
}
case
TSDB_DATA_TYPE_BINARY
:
{
case
TSDB_DATA_TYPE_BINARY
:
{
assert
(
filterDataType
==
TSDB_DATA_TYPE_BINARY
);
if
(
optr
==
TSDB_RELATION_LIKE
)
{
/* wildcard query using like operator */
if
(
optr
==
TSDB_RELATION_LIKE
)
{
/* wildcard query using like operator */
assert
(
filterDataType
==
TSDB_DATA_TYPE_BINARY
);
comparFn
=
compareStrPatternComp
;
comparFn
=
compareStrPatternComp
;
}
else
if
(
optr
==
TSDB_RELATION_IN
)
{
assert
(
filterDataType
==
TSDB_DATA_TYPE_ARRAY
);
comparFn
=
compareStrInList
;
}
else
{
/* normal relational comparFn */
}
else
{
/* normal relational comparFn */
assert
(
filterDataType
==
TSDB_DATA_TYPE_BINARY
);
comparFn
=
compareStrVal
;
comparFn
=
compareStrVal
;
}
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录