Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
taosdata
TDengine
提交
14cfbd7e
T
TDengine
项目概览
taosdata
/
TDengine
1 年多 前同步成功
通知
1185
Star
22016
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看板
提交
14cfbd7e
编写于
7月 06, 2022
作者:
dengyihao
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix tag/json tag error
上级
b6fce4b8
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
72 addition
and
65 deletion
+72
-65
source/dnode/vnode/src/meta/metaTable.c
source/dnode/vnode/src/meta/metaTable.c
+4
-4
source/libs/index/src/index.c
source/libs/index/src/index.c
+11
-3
source/libs/index/src/indexComm.c
source/libs/index/src/indexComm.c
+5
-1
source/libs/index/src/indexFilter.c
source/libs/index/src/indexFilter.c
+11
-10
source/os/src/osString.c
source/os/src/osString.c
+41
-47
未找到文件。
source/dnode/vnode/src/meta/metaTable.c
浏览文件 @
14cfbd7e
...
...
@@ -74,7 +74,7 @@ static int metaSaveJsonVarToIdx(SMeta *pMeta, const SMetaEntry *pCtbEntry, const
SIndexTerm
*
term
=
NULL
;
if
(
type
==
TSDB_DATA_TYPE_NULL
)
{
// handle null value
term
=
indexTermCreate
(
suid
,
ADD_VALUE
,
TSDB_DATA_TYPE_VARCHAR
,
key
,
nKey
,
NULL
,
0
);
}
else
if
(
type
==
TSDB_DATA_TYPE_NCHAR
)
{
if
(
pTagVal
->
nData
>
0
)
{
char
*
val
=
taosMemoryCalloc
(
1
,
pTagVal
->
nData
+
VARSTR_HEADER_SIZE
);
...
...
@@ -83,9 +83,10 @@ static int metaSaveJsonVarToIdx(SMeta *pMeta, const SMetaEntry *pCtbEntry, const
type
=
TSDB_DATA_TYPE_VARCHAR
;
term
=
indexTermCreate
(
suid
,
ADD_VALUE
,
type
,
key
,
nKey
,
val
,
len
);
}
else
if
(
pTagVal
->
nData
==
0
)
{
// TODO
char
*
val
=
NULL
;
int32_t
len
=
0
;
// handle NULL key
term
=
indexTermCreate
(
suid
,
ADD_VALUE
,
TSDB_DATA_TYPE_VARCHAR
,
key
,
nKey
,
pTagVal
->
pData
,
0
);
}
}
else
if
(
type
==
TSDB_DATA_TYPE_DOUBLE
)
{
double
val
=
*
(
double
*
)(
&
pTagVal
->
i64
);
...
...
@@ -441,7 +442,6 @@ static int metaDropTableByUid(SMeta *pMeta, tb_uid_t uid, int *type) {
tdbTbDelete
(
pMeta
->
pUidIdx
,
&
uid
,
sizeof
(
uid
),
&
pMeta
->
txn
);
if
(
e
.
type
!=
TSDB_SUPER_TABLE
)
metaDeleteTtlIdx
(
pMeta
,
&
e
);
if
(
e
.
type
==
TSDB_CHILD_TABLE
)
{
tdbTbDelete
(
pMeta
->
pCtbIdx
,
&
(
SCtbIdxKey
){.
suid
=
e
.
ctbEntry
.
suid
,
.
uid
=
uid
},
sizeof
(
SCtbIdxKey
),
&
pMeta
->
txn
);
}
else
if
(
e
.
type
==
TSDB_NORMAL_TABLE
)
{
...
...
@@ -1095,7 +1095,7 @@ static int metaHandleEntry(SMeta *pMeta, const SMetaEntry *pME) {
if
(
pME
->
type
==
TSDB_SUPER_TABLE
)
{
if
(
metaUpdateSuidIdx
(
pMeta
,
pME
)
<
0
)
goto
_err
;
}
}
}
if
(
pME
->
type
!=
TSDB_SUPER_TABLE
)
{
...
...
source/libs/index/src/index.c
浏览文件 @
14cfbd7e
...
...
@@ -278,9 +278,17 @@ SIndexTerm* indexTermCreate(int64_t suid, SIndexOperOnColumn oper, uint8_t colTy
tm
->
nColName
=
nColName
;
char
*
buf
=
NULL
;
int32_t
len
=
idxConvertDataToStr
((
void
*
)
colVal
,
IDX_TYPE_GET_TYPE
(
colType
),
(
void
**
)
&
buf
);
assert
(
len
!=
-
1
);
int32_t
len
=
0
;
if
(
colVal
!=
NULL
&&
nColVal
!=
0
)
{
len
=
idxConvertDataToStr
((
void
*
)
colVal
,
IDX_TYPE_GET_TYPE
(
colType
),
(
void
**
)
&
buf
);
}
else
if
(
colVal
==
NULL
)
{
buf
=
strndup
(
INDEX_DATA_NULL_STR
,
(
int32_t
)
strlen
(
INDEX_DATA_NULL_STR
));
len
=
(
int32_t
)
strlen
(
INDEX_DATA_NULL_STR
);
}
else
{
const
char
*
emptyStr
=
" "
;
buf
=
strndup
(
emptyStr
,
(
int32_t
)
strlen
(
emptyStr
));
len
=
(
int32_t
)
strlen
(
emptyStr
);
}
tm
->
colVal
=
buf
;
tm
->
nColVal
=
len
;
...
...
source/libs/index/src/indexComm.c
浏览文件 @
14cfbd7e
...
...
@@ -29,7 +29,7 @@
#define INDEX_DATA_BIGINT_NULL 0x8000000000000000LL
#define INDEX_DATA_TIMESTAMP_NULL TSDB_DATA_BIGINT_NULL
#define INDEX_DATA_FLOAT_NULL 0x7FF00000 // it is an NAN
#define INDEX_DATA_FLOAT_NULL 0x7FF00000
// it is an NAN
#define INDEX_DATA_DOUBLE_NULL 0x7FFFFF0000000000LL // an NAN
#define INDEX_DATA_NCHAR_NULL 0xFFFFFFFF
#define INDEX_DATA_BINARY_NULL 0xFF
...
...
@@ -374,6 +374,10 @@ int32_t idxConvertData(void* src, int8_t type, void** dst) {
return
tlen
;
}
int32_t
idxConvertDataToStr
(
void
*
src
,
int8_t
type
,
void
**
dst
)
{
if
(
src
==
NULL
)
{
*
dst
=
strndup
(
INDEX_DATA_NULL_STR
,
(
int
)
strlen
(
INDEX_DATA_NULL_STR
));
return
(
int32_t
)
strlen
(
INDEX_DATA_NULL_STR
);
}
int
tlen
=
tDataTypes
[
type
].
bytes
;
int32_t
bufSize
=
64
;
switch
(
type
)
{
...
...
source/libs/index/src/indexFilter.c
浏览文件 @
14cfbd7e
...
...
@@ -181,18 +181,16 @@ static int32_t sifInitJsonParam(SNode *node, SIFParam *param, SIFCtx *ctx) {
param
->
colValType
=
l
->
node
.
resType
.
type
;
memcpy
(
param
->
dbName
,
l
->
dbName
,
sizeof
(
l
->
dbName
));
memcpy
(
param
->
colName
,
r
->
literal
,
strlen
(
r
->
literal
));
// sprintf(param->colName, "%s_%s", l->colName, r->literal);
param
->
colValType
=
r
->
typeData
;
param
->
status
=
SFLT_COARSE_INDEX
;
return
0
;
// memcpy(param->colName, l->colName, sizeof(l->colName));
}
static
int32_t
sifInitParam
(
SNode
*
node
,
SIFParam
*
param
,
SIFCtx
*
ctx
)
{
param
->
status
=
SFLT_COARSE_INDEX
;
switch
(
nodeType
(
node
))
{
case
QUERY_NODE_VALUE
:
{
SValueNode
*
vn
=
(
SValueNode
*
)
node
;
if
(
vn
->
typeData
==
TSDB_DATA_TYPE_NULL
||
(
vn
->
literal
==
NULL
||
strlen
(
vn
->
literal
)
==
0
))
{
if
(
vn
->
typeData
==
TSDB_DATA_TYPE_NULL
&&
(
vn
->
literal
==
NULL
||
strlen
(
vn
->
literal
)
==
0
))
{
param
->
status
=
SFLT_NOT_INDEX
;
return
0
;
}
...
...
@@ -511,7 +509,6 @@ static int32_t sifGetOperFn(int32_t funcId, sif_func_t *func, SIdxFltStatus *sta
}
return
0
;
}
// typedef struct filterFuncDict {
static
int32_t
sifExecOper
(
SOperatorNode
*
node
,
SIFCtx
*
ctx
,
SIFParam
*
output
)
{
int32_t
code
=
0
;
...
...
@@ -532,9 +529,11 @@ static int32_t sifExecOper(SOperatorNode *node, SIFCtx *ctx, SIFParam *output) {
SIFParam
*
params
=
NULL
;
SIF_ERR_RET
(
sifInitOperParams
(
&
params
,
node
,
ctx
));
if
(
params
[
0
].
status
==
SFLT_NOT_INDEX
||
(
nParam
>
1
&&
params
[
1
].
status
==
SFLT_NOT_INDEX
))
{
output
->
status
=
SFLT_NOT_INDEX
;
return
code
;
if
(
node
->
opType
!=
OP_TYPE_JSON_CONTAINS
)
{
if
(
params
[
0
].
status
==
SFLT_NOT_INDEX
||
(
nParam
>
1
&&
params
[
1
].
status
==
SFLT_NOT_INDEX
))
{
output
->
status
=
SFLT_NOT_INDEX
;
return
code
;
}
}
// ugly code, refactor later
...
...
@@ -546,9 +545,11 @@ static int32_t sifExecOper(SOperatorNode *node, SIFCtx *ctx, SIFParam *output) {
SIF_ERR_RET
(
operFn
(
&
params
[
0
],
nParam
>
1
?
&
params
[
1
]
:
NULL
,
output
));
}
else
{
// ugly code, refactor later
if
(
nParam
>
1
&&
params
[
1
].
status
==
SFLT_NOT_INDEX
)
{
output
->
status
=
SFLT_NOT_INDEX
;
return
code
;
if
(
node
->
opType
!=
OP_TYPE_JSON_CONTAINS
)
{
if
(
nParam
>
1
&&
params
[
1
].
status
==
SFLT_NOT_INDEX
)
{
output
->
status
=
SFLT_NOT_INDEX
;
return
code
;
}
}
SIF_ERR_RET
(
sifGetOperFn
(
node
->
opType
,
&
operFn
,
&
output
->
status
));
}
...
...
source/os/src/osString.c
浏览文件 @
14cfbd7e
...
...
@@ -29,9 +29,8 @@ char *strsep(char **stringp, const char *delim) {
char
*
s
;
const
char
*
spanp
;
int32_t
c
,
sc
;
char
*
tok
;
if
((
s
=
*
stringp
)
==
NULL
)
return
(
NULL
);
char
*
tok
;
if
((
s
=
*
stringp
)
==
NULL
)
return
(
NULL
);
for
(
tok
=
s
;;)
{
c
=
*
s
++
;
spanp
=
delim
;
...
...
@@ -51,10 +50,10 @@ char *strsep(char **stringp, const char *delim) {
/* Duplicate a string, up to at most size characters */
char
*
strndup
(
const
char
*
s
,
size_t
size
)
{
size_t
l
;
char
*
s2
;
char
*
s2
;
l
=
strlen
(
s
);
if
(
l
>
size
)
l
=
size
;
s2
=
malloc
(
l
+
1
);
if
(
l
>
size
)
l
=
size
;
s2
=
malloc
(
l
+
1
);
if
(
s2
)
{
strncpy
(
s2
,
s
,
l
);
s2
[
l
]
=
'\0'
;
...
...
@@ -63,13 +62,12 @@ char *strndup(const char *s, size_t size) {
}
/* Copy no more than N characters of SRC to DEST, returning the address of
the terminating '\0' in DEST, if any, or else DEST + N. */
char
*
stpncpy
(
char
*
dest
,
const
char
*
src
,
size_t
n
)
{
size_t
size
=
strnlen
(
src
,
n
);
memcpy
(
dest
,
src
,
size
);
char
*
stpncpy
(
char
*
dest
,
const
char
*
src
,
size_t
n
)
{
size_t
size
=
strnlen
(
src
,
n
);
memcpy
(
dest
,
src
,
size
);
dest
+=
size
;
if
(
size
==
n
)
return
dest
;
return
memset
(
dest
,
'\0'
,
n
-
size
);
if
(
size
==
n
)
return
dest
;
return
memset
(
dest
,
'\0'
,
n
-
size
);
}
#endif
...
...
@@ -113,10 +111,9 @@ int32_t tasoUcs4Compare(TdUcs4 *f1_ucs4, TdUcs4 *f2_ucs4, int32_t bytes) {
#endif
}
TdUcs4
*
tasoUcs4Copy
(
TdUcs4
*
target_ucs4
,
TdUcs4
*
source_ucs4
,
int32_t
len_ucs4
)
{
assert
(
taosMemorySize
(
target_ucs4
)
>=
len_ucs4
*
sizeof
(
TdUcs4
));
return
memcpy
(
target_ucs4
,
source_ucs4
,
len_ucs4
*
sizeof
(
TdUcs4
));
TdUcs4
*
tasoUcs4Copy
(
TdUcs4
*
target_ucs4
,
TdUcs4
*
source_ucs4
,
int32_t
len_ucs4
)
{
assert
(
taosMemorySize
(
target_ucs4
)
>=
len_ucs4
*
sizeof
(
TdUcs4
));
return
memcpy
(
target_ucs4
,
source_ucs4
,
len_ucs4
*
sizeof
(
TdUcs4
));
}
int32_t
taosUcs4ToMbs
(
TdUcs4
*
ucs4
,
int32_t
ucs4_max_len
,
char
*
mbs
)
{
...
...
@@ -137,7 +134,7 @@ int32_t taosUcs4ToMbs(TdUcs4 *ucs4, int32_t ucs4_max_len, char *mbs) {
#endif
}
bool
taosMbsToUcs4
(
const
char
*
mbs
,
size_t
mbsLength
,
TdUcs4
*
ucs4
,
int32_t
ucs4_max_len
,
int32_t
*
len
)
{
bool
taosMbsToUcs4
(
const
char
*
mbs
,
size_t
mbsLength
,
TdUcs4
*
ucs4
,
int32_t
ucs4_max_len
,
int32_t
*
len
)
{
#ifdef DISALLOW_NCHAR_WITHOUT_ICONV
printf
(
"Nchar cannot be read and written without iconv, please install iconv library and recompile TDengine.
\n
"
);
return
-
1
;
...
...
@@ -146,7 +143,7 @@ bool taosMbsToUcs4(const char *mbs, size_t mbsLength, TdUcs4 *ucs4, int32_t ucs4
iconv_t
cd
=
iconv_open
(
DEFAULT_UNICODE_ENCODEC
,
tsCharset
);
size_t
ucs4_input_len
=
mbsLength
;
size_t
outLeft
=
ucs4_max_len
;
if
(
iconv
(
cd
,
(
char
**
)
&
mbs
,
&
ucs4_input_len
,
(
char
**
)
&
ucs4
,
&
outLeft
)
==
-
1
)
{
if
(
iconv
(
cd
,
(
char
**
)
&
mbs
,
&
ucs4_input_len
,
(
char
**
)
&
ucs4
,
&
outLeft
)
==
-
1
)
{
iconv_close
(
cd
);
return
false
;
}
...
...
@@ -195,7 +192,7 @@ int32_t taosUcs4len(TdUcs4 *ucs4) {
return
n
;
}
//dst buffer size should be at least 2*len + 1
//
dst buffer size should be at least 2*len + 1
int32_t
taosHexEncode
(
const
unsigned
char
*
src
,
char
*
dst
,
int32_t
len
)
{
if
(
!
dst
)
{
return
-
1
;
...
...
@@ -214,7 +211,7 @@ int32_t taosHexDecode(const char *src, char *dst, int32_t len) {
}
uint8_t
hn
,
ln
,
out
;
for
(
int
i
=
0
,
j
=
0
;
i
<
len
*
2
;
i
+=
2
,
++
j
)
{
for
(
int
i
=
0
,
j
=
0
;
i
<
len
*
2
;
i
+=
2
,
++
j
)
{
hn
=
src
[
i
]
>
'9'
?
src
[
i
]
-
'a'
+
10
:
src
[
i
]
-
'0'
;
ln
=
src
[
i
+
1
]
>
'9'
?
src
[
i
+
1
]
-
'a'
+
10
:
src
[
i
+
1
]
-
'0'
;
...
...
@@ -238,25 +235,22 @@ int32_t taosWcharToMb(char *pStr, TdWchar wchar) { return wctomb(pStr, wchar); }
int32_t
taosWcharsToMbs
(
char
*
pStrs
,
TdWchar
*
pWchars
,
int32_t
size
)
{
return
wcstombs
(
pStrs
,
pWchars
,
size
);
}
char
*
taosStrCaseStr
(
const
char
*
str
,
const
char
*
pattern
)
{
size_t
i
;
if
(
!*
pattern
)
return
(
char
*
)
str
;
for
(;
*
str
;
str
++
)
{
if
(
toupper
(
*
str
)
==
toupper
(
*
pattern
))
{
for
(
i
=
1
;;
i
++
)
{
if
(
!
pattern
[
i
])
return
(
char
*
)
str
;
if
(
toupper
(
str
[
i
])
!=
toupper
(
pattern
[
i
]))
break
;
}
}
}
return
NULL
;
size_t
i
;
if
(
!*
pattern
)
return
(
char
*
)
str
;
for
(;
*
str
;
str
++
)
{
if
(
toupper
(
*
str
)
==
toupper
(
*
pattern
))
{
for
(
i
=
1
;;
i
++
)
{
if
(
!
pattern
[
i
])
return
(
char
*
)
str
;
if
(
toupper
(
str
[
i
])
!=
toupper
(
pattern
[
i
]))
break
;
}
}
}
return
NULL
;
}
int64_t
taosStr2Int64
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int64_t
taosStr2Int64
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int64_t
tmp
=
strtoll
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -265,7 +259,7 @@ int64_t taosStr2Int64(const char *str, char** pEnd, int32_t radix) {
return
tmp
;
}
uint64_t
taosStr2UInt64
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint64_t
taosStr2UInt64
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint64_t
tmp
=
strtoull
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -274,7 +268,7 @@ uint64_t taosStr2UInt64(const char *str, char** pEnd, int32_t radix) {
return
tmp
;
}
int32_t
taosStr2Int32
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int32_t
taosStr2Int32
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int32_t
tmp
=
strtol
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -283,7 +277,7 @@ int32_t taosStr2Int32(const char *str, char** pEnd, int32_t radix) {
return
tmp
;
}
uint32_t
taosStr2UInt32
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint32_t
taosStr2UInt32
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint32_t
tmp
=
strtol
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -292,7 +286,7 @@ uint32_t taosStr2UInt32(const char *str, char** pEnd, int32_t radix) {
return
tmp
;
}
int16_t
taosStr2Int16
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int16_t
taosStr2Int16
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int32_t
tmp
=
strtol
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -303,7 +297,7 @@ int16_t taosStr2Int16(const char *str, char** pEnd, int32_t radix) {
return
(
int16_t
)
tmp
;
}
uint16_t
taosStr2UInt16
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint16_t
taosStr2UInt16
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint32_t
tmp
=
strtoul
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -313,7 +307,7 @@ uint16_t taosStr2UInt16(const char *str, char** pEnd, int32_t radix) {
return
(
uint16_t
)
tmp
;
}
int8_t
taosStr2Int8
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int8_t
taosStr2Int8
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
int32_t
tmp
=
strtol
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -324,7 +318,7 @@ int8_t taosStr2Int8(const char *str, char** pEnd, int32_t radix) {
return
tmp
;
}
uint8_t
taosStr2UInt8
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint8_t
taosStr2UInt8
(
const
char
*
str
,
char
**
pEnd
,
int32_t
radix
)
{
uint32_t
tmp
=
strtoul
(
str
,
pEnd
,
radix
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -334,7 +328,7 @@ uint8_t taosStr2UInt8(const char *str, char** pEnd, int32_t radix) {
return
tmp
;
}
double
taosStr2Double
(
const
char
*
str
,
char
**
pEnd
)
{
double
taosStr2Double
(
const
char
*
str
,
char
**
pEnd
)
{
double
tmp
=
strtod
(
str
,
pEnd
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -344,7 +338,7 @@ double taosStr2Double(const char *str, char** pEnd) {
return
tmp
;
}
float
taosStr2Float
(
const
char
*
str
,
char
**
pEnd
)
{
float
taosStr2Float
(
const
char
*
str
,
char
**
pEnd
)
{
float
tmp
=
strtof
(
str
,
pEnd
);
#ifdef TD_CHECK_STR_TO_INT_ERROR
assert
(
errno
!=
ERANGE
);
...
...
@@ -353,4 +347,4 @@ float taosStr2Float(const char *str, char** pEnd) {
assert
(
tmp
!=
NAN
);
#endif
return
tmp
;
}
\ No newline at end of file
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录