Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
taosdata
TDengine
提交
c67f5f10
T
TDengine
项目概览
taosdata
/
TDengine
大约 2 年 前同步成功
通知
1192
Star
22018
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看板
未验证
提交
c67f5f10
编写于
7月 30, 2022
作者:
L
Liu Jicong
提交者:
GitHub
7月 30, 2022
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #15595 from taosdata/feature/stream
enh(wal): add lock to guarantee read behaviour
上级
71e41495
6d67b171
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
26 addition
and
22 deletion
+26
-22
examples/c/tmq.c
examples/c/tmq.c
+2
-2
source/dnode/vnode/src/tq/tq.c
source/dnode/vnode/src/tq/tq.c
+3
-0
source/libs/executor/src/executor.c
source/libs/executor/src/executor.c
+0
-1
source/libs/executor/src/scanoperator.c
source/libs/executor/src/scanoperator.c
+0
-10
source/libs/function/src/builtins.c
source/libs/function/src/builtins.c
+7
-7
source/libs/stream/src/streamDispatch.c
source/libs/stream/src/streamDispatch.c
+2
-1
source/libs/wal/src/walRead.c
source/libs/wal/src/walRead.c
+11
-0
source/libs/wal/src/walWrite.c
source/libs/wal/src/walWrite.c
+1
-1
未找到文件。
examples/c/tmq.c
浏览文件 @
c67f5f10
...
@@ -29,7 +29,7 @@ static void msg_process(TAOS_RES* msg) {
...
@@ -29,7 +29,7 @@ static void msg_process(TAOS_RES* msg) {
printf
(
"vg: %d
\n
"
,
tmq_get_vgroup_id
(
msg
));
printf
(
"vg: %d
\n
"
,
tmq_get_vgroup_id
(
msg
));
if
(
tmq_get_res_type
(
msg
)
==
TMQ_RES_TABLE_META
)
{
if
(
tmq_get_res_type
(
msg
)
==
TMQ_RES_TABLE_META
)
{
tmq_raw_data
raw
=
{
0
};
tmq_raw_data
raw
=
{
0
};
int32_t
code
=
tmq_get_raw
(
msg
,
&
raw
);
int32_t
code
=
tmq_get_raw
(
msg
,
&
raw
);
if
(
code
==
0
)
{
if
(
code
==
0
)
{
TAOS
*
pConn
=
taos_connect
(
"192.168.1.86"
,
"root"
,
"taosdata"
,
NULL
,
0
);
TAOS
*
pConn
=
taos_connect
(
"192.168.1.86"
,
"root"
,
"taosdata"
,
NULL
,
0
);
if
(
pConn
==
NULL
)
{
if
(
pConn
==
NULL
)
{
...
@@ -302,7 +302,7 @@ int32_t create_topic() {
...
@@ -302,7 +302,7 @@ int32_t create_topic() {
}
}
taos_free_result
(
pRes
);
taos_free_result
(
pRes
);
// pRes = taos_query(pConn, "create topic topic_ctb_column with meta as database abc1");
// pRes = taos_query(pConn, "create topic topic_ctb_column with meta as database abc1");
pRes
=
taos_query
(
pConn
,
"create topic topic_ctb_column as select ts, c1, c2, c3 from st1"
);
pRes
=
taos_query
(
pConn
,
"create topic topic_ctb_column as select ts, c1, c2, c3 from st1"
);
if
(
taos_errno
(
pRes
)
!=
0
)
{
if
(
taos_errno
(
pRes
)
!=
0
)
{
printf
(
"failed to create topic topic_ctb_column, reason:%s
\n
"
,
taos_errstr
(
pRes
));
printf
(
"failed to create topic topic_ctb_column, reason:%s
\n
"
,
taos_errstr
(
pRes
));
...
...
source/dnode/vnode/src/tq/tq.c
浏览文件 @
c67f5f10
...
@@ -684,6 +684,9 @@ int32_t tqProcessTaskDeployReq(STQ* pTq, char* msg, int32_t msgLen) {
...
@@ -684,6 +684,9 @@ int32_t tqProcessTaskDeployReq(STQ* pTq, char* msg, int32_t msgLen) {
taosHashPut
(
pTq
->
pStreamTasks
,
&
pTask
->
taskId
,
sizeof
(
int32_t
),
&
pTask
,
sizeof
(
void
*
));
taosHashPut
(
pTq
->
pStreamTasks
,
&
pTask
->
taskId
,
sizeof
(
int32_t
),
&
pTask
,
sizeof
(
void
*
));
/*SMeta* pMeta = pTq->pVnode->pMeta;*/
/*tdbTbUpsert(pMeta->pTaskIdx, &pTask->taskId, sizeof(int32_t), msg, msgLen, &pMeta->txn);*/
return
0
;
return
0
;
FAIL:
FAIL:
if
(
pTask
->
inputQueue
)
streamQueueClose
(
pTask
->
inputQueue
);
if
(
pTask
->
inputQueue
)
streamQueueClose
(
pTask
->
inputQueue
);
...
...
source/libs/executor/src/executor.c
浏览文件 @
c67f5f10
...
@@ -48,7 +48,6 @@ static int32_t doSetStreamBlock(SOperatorInfo* pOperator, void* input, size_t nu
...
@@ -48,7 +48,6 @@ static int32_t doSetStreamBlock(SOperatorInfo* pOperator, void* input, size_t nu
pOperator
->
status
=
OP_NOT_OPENED
;
pOperator
->
status
=
OP_NOT_OPENED
;
SStreamScanInfo
*
pInfo
=
pOperator
->
info
;
SStreamScanInfo
*
pInfo
=
pOperator
->
info
;
/*pInfo->assignBlockUid = assignUid;*/
// TODO: if a block was set but not consumed,
// TODO: if a block was set but not consumed,
// prevent setting a different type of block
// prevent setting a different type of block
...
...
source/libs/executor/src/scanoperator.c
浏览文件 @
c67f5f10
...
@@ -481,10 +481,6 @@ static SSDataBlock* doTableScanImpl(SOperatorInfo* pOperator) {
...
@@ -481,10 +481,6 @@ static SSDataBlock* doTableScanImpl(SOperatorInfo* pOperator) {
pBlock
->
info
.
groupId
=
*
groupId
;
pBlock
->
info
.
groupId
=
*
groupId
;
}
}
if
(
pTableScanInfo
->
assignBlockUid
)
{
pBlock
->
info
.
groupId
=
pBlock
->
info
.
uid
;
}
pOperator
->
resultInfo
.
totalRows
=
pTableScanInfo
->
readRecorder
.
totalRows
;
pOperator
->
resultInfo
.
totalRows
=
pTableScanInfo
->
readRecorder
.
totalRows
;
pTableScanInfo
->
readRecorder
.
elapsedTime
+=
(
taosGetTimestampUs
()
-
st
)
/
1000
.
0
;
pTableScanInfo
->
readRecorder
.
elapsedTime
+=
(
taosGetTimestampUs
()
-
st
)
/
1000
.
0
;
...
@@ -1174,12 +1170,6 @@ static int32_t setBlockIntoRes(SStreamScanInfo* pInfo, const SSDataBlock* pBlock
...
@@ -1174,12 +1170,6 @@ static int32_t setBlockIntoRes(SStreamScanInfo* pInfo, const SSDataBlock* pBlock
pInfo
->
pRes
->
info
.
groupId
=
0
;
pInfo
->
pRes
->
info
.
groupId
=
0
;
}
}
// for generating rollup SMA result, each time is an independent time serie.
// TODO temporarily used, when the statement of "partition by tbname" is ready, remove this
if
(
pInfo
->
assignBlockUid
)
{
pInfo
->
pRes
->
info
.
groupId
=
pBlock
->
info
.
uid
;
}
// todo extract method
// todo extract method
for
(
int32_t
i
=
0
;
i
<
taosArrayGetSize
(
pInfo
->
pColMatchInfo
);
++
i
)
{
for
(
int32_t
i
=
0
;
i
<
taosArrayGetSize
(
pInfo
->
pColMatchInfo
);
++
i
)
{
SColMatchInfo
*
pColMatchInfo
=
taosArrayGet
(
pInfo
->
pColMatchInfo
,
i
);
SColMatchInfo
*
pColMatchInfo
=
taosArrayGet
(
pInfo
->
pColMatchInfo
,
i
);
...
...
source/libs/function/src/builtins.c
浏览文件 @
c67f5f10
...
@@ -2236,7 +2236,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
...
@@ -2236,7 +2236,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
{
{
.
name
=
"interp"
,
.
name
=
"interp"
,
.
type
=
FUNCTION_TYPE_INTERP
,
.
type
=
FUNCTION_TYPE_INTERP
,
.
classification
=
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_INTERVAL_INTERPO_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
,
.
classification
=
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_INTERVAL_INTERPO_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
|
FUNC_MGT_FORBID_STREAM_FUNC
,
.
translateFunc
=
translateFirstLast
,
.
translateFunc
=
translateFirstLast
,
.
getEnvFunc
=
getSelectivityFuncEnv
,
.
getEnvFunc
=
getSelectivityFuncEnv
,
.
initFunc
=
functionSetup
,
.
initFunc
=
functionSetup
,
...
@@ -2246,7 +2246,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
...
@@ -2246,7 +2246,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
{
{
.
name
=
"derivative"
,
.
name
=
"derivative"
,
.
type
=
FUNCTION_TYPE_DERIVATIVE
,
.
type
=
FUNCTION_TYPE_DERIVATIVE
,
.
classification
=
FUNC_MGT_INDEFINITE_ROWS_FUNC
|
FUNC_MGT_SELECT_FUNC
|
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
|
FUNC_MGT_CUMULATIVE_FUNC
,
.
classification
=
FUNC_MGT_INDEFINITE_ROWS_FUNC
|
FUNC_MGT_SELECT_FUNC
|
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
|
FUNC_MGT_CUMULATIVE_FUNC
|
FUNC_MGT_FORBID_STREAM_FUNC
,
.
translateFunc
=
translateDerivative
,
.
translateFunc
=
translateDerivative
,
.
getEnvFunc
=
getDerivativeFuncEnv
,
.
getEnvFunc
=
getDerivativeFuncEnv
,
.
initFunc
=
derivativeFuncSetup
,
.
initFunc
=
derivativeFuncSetup
,
...
@@ -2280,7 +2280,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
...
@@ -2280,7 +2280,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
{
{
.
name
=
"_cache_last_row"
,
.
name
=
"_cache_last_row"
,
.
type
=
FUNCTION_TYPE_CACHE_LAST_ROW
,
.
type
=
FUNCTION_TYPE_CACHE_LAST_ROW
,
.
classification
=
FUNC_MGT_AGG_FUNC
|
FUNC_MGT_MULTI_RES_FUNC
|
FUNC_MGT_SELECT_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
,
.
classification
=
FUNC_MGT_AGG_FUNC
|
FUNC_MGT_MULTI_RES_FUNC
|
FUNC_MGT_SELECT_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
|
FUNC_MGT_FORBID_STREAM_FUNC
,
.
translateFunc
=
translateFirstLast
,
.
translateFunc
=
translateFirstLast
,
.
getEnvFunc
=
getFirstLastFuncEnv
,
.
getEnvFunc
=
getFirstLastFuncEnv
,
.
initFunc
=
functionSetup
,
.
initFunc
=
functionSetup
,
...
@@ -2375,7 +2375,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
...
@@ -2375,7 +2375,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
{
{
.
name
=
"histogram"
,
.
name
=
"histogram"
,
.
type
=
FUNCTION_TYPE_HISTOGRAM
,
.
type
=
FUNCTION_TYPE_HISTOGRAM
,
.
classification
=
FUNC_MGT_AGG_FUNC
|
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_MULTI_ROWS_FUNC
|
FUNC_MGT_FORBID_FILL_FUNC
,
.
classification
=
FUNC_MGT_AGG_FUNC
|
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_MULTI_ROWS_FUNC
|
FUNC_MGT_FORBID_FILL_FUNC
|
FUNC_MGT_FORBID_STREAM_FUNC
,
.
translateFunc
=
translateHistogram
,
.
translateFunc
=
translateHistogram
,
.
getEnvFunc
=
getHistogramFuncEnv
,
.
getEnvFunc
=
getHistogramFuncEnv
,
.
initFunc
=
histogramFunctionSetup
,
.
initFunc
=
histogramFunctionSetup
,
...
@@ -2460,7 +2460,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
...
@@ -2460,7 +2460,7 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
.
processFunc
=
diffFunction
,
.
processFunc
=
diffFunction
,
.
sprocessFunc
=
diffScalarFunction
,
.
sprocessFunc
=
diffScalarFunction
,
.
finalizeFunc
=
functionFinalize
,
.
finalizeFunc
=
functionFinalize
,
.
estimateReturnRowsFunc
=
diffEstReturnRows
.
estimateReturnRowsFunc
=
diffEstReturnRows
,
},
},
{
{
.
name
=
"statecount"
,
.
name
=
"statecount"
,
...
@@ -2521,8 +2521,8 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
...
@@ -2521,8 +2521,8 @@ const SBuiltinFuncDefinition funcMgtBuiltins[] = {
{
{
.
name
=
"tail"
,
.
name
=
"tail"
,
.
type
=
FUNCTION_TYPE_TAIL
,
.
type
=
FUNCTION_TYPE_TAIL
,
.
classification
=
FUNC_MGT_SELECT_FUNC
|
FUNC_MGT_INDEFINITE_ROWS_FUNC
|
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_FORBID_STREAM_FUNC
|
.
classification
=
FUNC_MGT_SELECT_FUNC
|
FUNC_MGT_INDEFINITE_ROWS_FUNC
|
FUNC_MGT_TIMELINE_FUNC
|
FUNC_MGT_IMPLICIT_TS_FUNC
,
FUNC_MGT_
FORBID_STREAM_FUNC
|
FUNC_MGT_
IMPLICIT_TS_FUNC
,
.
translateFunc
=
translateTail
,
.
translateFunc
=
translateTail
,
.
getEnvFunc
=
getTailFuncEnv
,
.
getEnvFunc
=
getTailFuncEnv
,
.
initFunc
=
tailFunctionSetup
,
.
initFunc
=
tailFunctionSetup
,
...
...
source/libs/stream/src/streamDispatch.c
浏览文件 @
c67f5f10
...
@@ -462,7 +462,8 @@ int32_t streamDispatch(SStreamTask* pTask) {
...
@@ -462,7 +462,8 @@ int32_t streamDispatch(SStreamTask* pTask) {
if
(
streamDispatchAllBlocks
(
pTask
,
pBlock
)
<
0
)
{
if
(
streamDispatchAllBlocks
(
pTask
,
pBlock
)
<
0
)
{
ASSERT
(
0
);
ASSERT
(
0
);
code
=
-
1
;
code
=
-
1
;
// TODO set status fail
streamQueueProcessFail
(
pTask
->
outputQueue
);
atomic_store_8
(
&
pTask
->
outputStatus
,
TASK_OUTPUT_STATUS__NORMAL
);
goto
FREE
;
goto
FREE
;
}
}
/*atomic_store_8(&pTask->outputStatus, TASK_OUTPUT_STATUS__NORMAL);*/
/*atomic_store_8(&pTask->outputStatus, TASK_OUTPUT_STATUS__NORMAL);*/
...
...
source/libs/wal/src/walRead.c
浏览文件 @
c67f5f10
...
@@ -441,9 +441,12 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -441,9 +441,12 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
return
-
1
;
return
-
1
;
}
}
taosThreadMutexLock
(
&
pReader
->
mutex
);
if
(
pReader
->
curInvalid
||
pReader
->
curVersion
!=
ver
)
{
if
(
pReader
->
curInvalid
||
pReader
->
curVersion
!=
ver
)
{
if
(
walReadSeekVer
(
pReader
,
ver
)
<
0
)
{
if
(
walReadSeekVer
(
pReader
,
ver
)
<
0
)
{
wError
(
"vgId:%d, unexpected wal log, index:%"
PRId64
", since %s"
,
pReader
->
pWal
->
cfg
.
vgId
,
ver
,
terrstr
());
wError
(
"vgId:%d, unexpected wal log, index:%"
PRId64
", since %s"
,
pReader
->
pWal
->
cfg
.
vgId
,
ver
,
terrstr
());
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
seeked
=
true
;
seeked
=
true
;
...
@@ -464,6 +467,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -464,6 +467,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
}
}
ASSERT
(
0
);
ASSERT
(
0
);
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
}
}
...
@@ -473,6 +477,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -473,6 +477,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
wError
(
"vgId:%d, unexpected wal log, index:%"
PRId64
", since head checksum not passed"
,
pReader
->
pWal
->
cfg
.
vgId
,
wError
(
"vgId:%d, unexpected wal log, index:%"
PRId64
", since head checksum not passed"
,
pReader
->
pWal
->
cfg
.
vgId
,
ver
);
ver
);
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
...
@@ -480,6 +485,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -480,6 +485,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
void
*
ptr
=
taosMemoryRealloc
(
pReader
->
pHead
,
sizeof
(
SWalCkHead
)
+
pReader
->
pHead
->
head
.
bodyLen
);
void
*
ptr
=
taosMemoryRealloc
(
pReader
->
pHead
,
sizeof
(
SWalCkHead
)
+
pReader
->
pHead
->
head
.
bodyLen
);
if
(
ptr
==
NULL
)
{
if
(
ptr
==
NULL
)
{
terrno
=
TSDB_CODE_WAL_OUT_OF_MEMORY
;
terrno
=
TSDB_CODE_WAL_OUT_OF_MEMORY
;
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
pReader
->
pHead
=
ptr
;
pReader
->
pHead
=
ptr
;
...
@@ -494,6 +500,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -494,6 +500,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
ASSERT
(
0
);
ASSERT
(
0
);
}
}
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
...
@@ -503,6 +510,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -503,6 +510,7 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
pReader
->
curInvalid
=
1
;
pReader
->
curInvalid
=
1
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
ASSERT
(
0
);
ASSERT
(
0
);
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
...
@@ -516,9 +524,12 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
...
@@ -516,9 +524,12 @@ int32_t walReadVer(SWalReader *pReader, int64_t ver) {
pReader
->
curInvalid
=
1
;
pReader
->
curInvalid
=
1
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
terrno
=
TSDB_CODE_WAL_FILE_CORRUPTED
;
ASSERT
(
0
);
ASSERT
(
0
);
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
-
1
;
return
-
1
;
}
}
pReader
->
curVersion
++
;
pReader
->
curVersion
++
;
taosThreadMutexUnlock
(
&
pReader
->
mutex
);
return
0
;
return
0
;
}
}
source/libs/wal/src/walWrite.c
浏览文件 @
c67f5f10
...
@@ -408,7 +408,7 @@ static FORCE_INLINE int32_t walWriteImpl(SWal *pWal, int64_t index, tmsg_t msgTy
...
@@ -408,7 +408,7 @@ static FORCE_INLINE int32_t walWriteImpl(SWal *pWal, int64_t index, tmsg_t msgTy
pWal
->
writeHead
.
head
.
version
=
index
;
pWal
->
writeHead
.
head
.
version
=
index
;
pWal
->
writeHead
.
head
.
bodyLen
=
bodyLen
;
pWal
->
writeHead
.
head
.
bodyLen
=
bodyLen
;
pWal
->
writeHead
.
head
.
msgType
=
msgType
;
pWal
->
writeHead
.
head
.
msgType
=
msgType
;
pWal
->
writeHead
.
head
.
ingestTs
=
taosGetTimestampMs
()
;
pWal
->
writeHead
.
head
.
ingestTs
=
0
;
// sync info for sync module
// sync info for sync module
pWal
->
writeHead
.
head
.
syncMeta
=
syncMeta
;
pWal
->
writeHead
.
head
.
syncMeta
=
syncMeta
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录