Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
慢慢CG
TDengine
提交
4d7a69d1
T
TDengine
项目概览
慢慢CG
/
TDengine
与 Fork 源项目一致
Fork自
taosdata / TDengine
通知
1
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看板
提交
4d7a69d1
编写于
10月 18, 2020
作者:
F
freemine
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1. support fraction in timestamp
2. trying to use tsdb_xxx_to_yyy
上级
b0ff31e1
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
74 addition
and
57 deletion
+74
-57
src/connector/odbc/src/todbc.c
src/connector/odbc/src/todbc.c
+72
-52
src/connector/odbc/tests/odbc.py
src/connector/odbc/tests/odbc.py
+2
-5
未找到文件。
src/connector/odbc/src/todbc.c
浏览文件 @
4d7a69d1
...
...
@@ -143,6 +143,19 @@ do { \
D("%s: elapsed: [%.6f]s", #statement, delta); \
} while (0)
#define CHK_CONV(statement) \
do { \
const char *sqlstate = statement; \
if (sqlstate) { \
SET_ERROR(sql, sqlstate, TSDB_CODE_ODBC_OUT_OF_RANGE, \
"no convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d]", \
sql_c_type(valueType), valueType, valueType, \
taos_data_type(type), type, type, idx+1); \
return SQL_ERROR; \
} \
} while (0)
typedef
struct
env_s
env_t
;
typedef
struct
conn_s
conn_t
;
typedef
struct
sql_s
sql_t
;
...
...
@@ -541,7 +554,6 @@ static SQLRETURN doSQLFreeStmt(SQLHSTMT StatementHandle,
}
sql
->
n_params
=
0
;
DASSERT
(
DEC_REF
(
sql
->
conn
)
>
0
);
DASSERT
(
DEC_REF
(
sql
)
==
0
);
...
...
@@ -796,10 +808,10 @@ static SQLRETURN conv_tsdb_f4_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F
static
SQLRETURN
conv_tsdb_f8_to_c_double
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
double
f8
);
static
SQLRETURN
conv_tsdb_f8_to_c_char
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
double
f8
);
static
SQLRETURN
conv_tsdb_f8_to_c_binary
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
double
f8
);
static
SQLRETURN
conv_tsdb_ts_to_c_v8
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_str
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_bin
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_ts
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_v8
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_str
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_bin
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_ts_to_c_ts
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
);
static
SQLRETURN
conv_tsdb_bin_to_c_str
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
const
unsigned
char
*
bin
);
static
SQLRETURN
conv_tsdb_bin_to_c_bin
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
const
unsigned
char
*
bin
);
static
SQLRETURN
conv_tsdb_str_to_c_bit
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
const
char
*
str
);
...
...
@@ -987,7 +999,7 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
}
}
break
;
case
TSDB_DATA_TYPE_TIMESTAMP
:
{
TIMESTAMP_STRUCT
ts
=
{
0
};
SQL_
TIMESTAMP_STRUCT
ts
=
{
0
};
int64_t
v
=
*
(
int64_t
*
)
row
;
time_t
t
=
v
/
1000
;
struct
tm
tm
=
{
0
};
...
...
@@ -998,7 +1010,7 @@ static SQLRETURN doSQLGetData(SQLHSTMT StatementHandle,
ts
.
hour
=
tm
.
tm_hour
;
ts
.
minute
=
tm
.
tm_min
;
ts
.
second
=
tm
.
tm_sec
;
ts
.
fraction
=
v
%
1000
;
ts
.
fraction
=
v
%
1000
*
1000000
;
switch
(
target
.
ct
)
{
case
SQL_C_SBIGINT
:
return
conv_tsdb_ts_to_c_v8
(
sql
,
&
target
,
field
,
&
ts
);
case
SQL_C_CHAR
:
return
conv_tsdb_ts_to_c_str
(
sql
,
&
target
,
field
,
&
ts
);
...
...
@@ -1269,10 +1281,10 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
bind
->
length
=
&
bind
->
buffer_length
;
switch
(
valueType
)
{
case
SQL_C_LONG
:
{
bind
->
u
.
b
=
*
(
int32_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_bit
(
*
(
int32_t
*
)
paramValue
,
&
bind
->
u
.
b
))
;
}
break
;
case
SQL_C_BIT
:
{
bind
->
u
.
b
=
*
(
int8_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_bit
(
*
(
int8_t
*
)
paramValue
,
&
bind
->
u
.
b
))
;
}
break
;
case
SQL_C_CHAR
:
case
SQL_C_WCHAR
:
...
...
@@ -1313,16 +1325,16 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
bind
->
length
=
&
bind
->
buffer_length
;
switch
(
valueType
)
{
case
SQL_C_TINYINT
:
{
bind
->
u
.
v1
=
*
(
int8_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_tinyint
(
*
(
int8_t
*
)
paramValue
,
&
bind
->
u
.
v1
))
;
}
break
;
case
SQL_C_SHORT
:
{
bind
->
u
.
v1
=
*
(
int16_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_tinyint
(
*
(
int16_t
*
)
paramValue
,
&
bind
->
u
.
v1
))
;
}
break
;
case
SQL_C_LONG
:
{
bind
->
u
.
v1
=
*
(
int32_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_tinyint
(
*
(
int32_t
*
)
paramValue
,
&
bind
->
u
.
v1
))
;
}
break
;
case
SQL_C_SBIGINT
:
{
bind
->
u
.
v1
=
*
(
int64_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_tinyint
(
*
(
int64_t
*
)
paramValue
,
&
bind
->
u
.
v1
))
;
}
break
;
case
SQL_C_CHAR
:
case
SQL_C_WCHAR
:
...
...
@@ -1361,10 +1373,10 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
bind
->
length
=
&
bind
->
buffer_length
;
switch
(
valueType
)
{
case
SQL_C_LONG
:
{
bind
->
u
.
v2
=
*
(
int32_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_smallint
(
*
(
int32_t
*
)
paramValue
,
&
bind
->
u
.
v2
))
;
}
break
;
case
SQL_C_SHORT
:
{
bind
->
u
.
v2
=
*
(
int16_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_smallint
(
*
(
int16_t
*
)
paramValue
,
&
bind
->
u
.
v2
))
;
}
break
;
case
SQL_C_CHAR
:
case
SQL_C_WCHAR
:
...
...
@@ -1405,7 +1417,7 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
bind
->
length
=
&
bind
->
buffer_length
;
switch
(
valueType
)
{
case
SQL_C_LONG
:
{
bind
->
u
.
v4
=
*
(
int32_t
*
)
paramValue
;
CHK_CONV
(
tsdb_int64_to_int
(
*
(
int32_t
*
)
paramValue
,
&
bind
->
u
.
v4
))
;
}
break
;
case
SQL_C_CHAR
:
case
SQL_C_WCHAR
:
...
...
@@ -1635,13 +1647,20 @@ static SQLRETURN do_bind_param_value(sql_t *sql, int idx_row, int idx, param_bin
DASSERT
(
soi
);
DASSERT
(
*
soi
!=
SQL_NTS
);
size_t
bytes
=
0
;
int
r
=
0
;
int64_t
t
=
0
;
SQLCHAR
*
utf8
=
wchars_to_chars
(
paramValue
,
*
soi
/
2
,
&
bytes
);
struct
tm
tm
=
{
0
};
strptime
((
const
char
*
)
utf8
,
"%Y-%m-%d %H:%M:%S"
,
&
tm
);
int64_t
t
=
(
int64_t
)
mktime
(
&
tm
);
t
*=
1000
;
// why cast utf8 to 'char*' ?
r
=
taosParseTime
((
char
*
)
utf8
,
&
t
,
strlen
((
const
char
*
)
utf8
),
TSDB_TIME_PRECISION_MILLI
,
0
);
bind
->
u
.
v8
=
t
;
free
(
utf8
);
if
(
r
)
{
SET_ERROR
(
sql
,
"22007"
,
TSDB_CODE_ODBC_OUT_OF_RANGE
,
"convertion from [%s[%d/0x%x]] to [%s[%d/0x%x]] for parameter [%d] failed"
,
sql_c_type
(
valueType
),
valueType
,
valueType
,
taos_data_type
(
type
),
type
,
type
,
idx
+
1
);
return
SQL_ERROR
;
}
}
break
;
case
SQL_C_SBIGINT
:
{
int64_t
t
=
*
(
int64_t
*
)
paramValue
;
...
...
@@ -2152,23 +2171,24 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
if
(
ColumnSize
)
{
*
ColumnSize
=
field
->
bytes
;
}
if
(
DecimalDigits
)
*
DecimalDigits
=
0
;
if
(
DataType
)
{
switch
(
field
->
type
)
{
case
TSDB_DATA_TYPE_BOOL
:
{
*
DataType
=
SQL_
C_
TINYINT
;
*
DataType
=
SQL_TINYINT
;
}
break
;
case
TSDB_DATA_TYPE_TINYINT
:
{
*
DataType
=
SQL_
C_
TINYINT
;
*
DataType
=
SQL_TINYINT
;
}
break
;
case
TSDB_DATA_TYPE_SMALLINT
:
{
*
DataType
=
SQL_
C_SHOR
T
;
*
DataType
=
SQL_
SMALLIN
T
;
}
break
;
case
TSDB_DATA_TYPE_INT
:
{
*
DataType
=
SQL_
C_LONG
;
*
DataType
=
SQL_
INTEGER
;
}
break
;
case
TSDB_DATA_TYPE_BIGINT
:
{
...
...
@@ -2176,24 +2196,29 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
}
break
;
case
TSDB_DATA_TYPE_FLOAT
:
{
*
DataType
=
SQL_
C_
FLOAT
;
*
DataType
=
SQL_FLOAT
;
}
break
;
case
TSDB_DATA_TYPE_DOUBLE
:
{
*
DataType
=
SQL_
C_
DOUBLE
;
*
DataType
=
SQL_DOUBLE
;
}
break
;
case
TSDB_DATA_TYPE_TIMESTAMP
:
{
*
DataType
=
SQL_C_TIMESTAMP
;
// *DataType = SQL_TIMESTAMP;
// *ColumnSize = 30;
// *DecimalDigits = 3;
*
DataType
=
SQL_TIMESTAMP
;
*
ColumnSize
=
sizeof
(
SQL_TIMESTAMP_STRUCT
);
*
DecimalDigits
=
0
;
}
break
;
case
TSDB_DATA_TYPE_NCHAR
:
{
*
DataType
=
SQL_C
_C
HAR
;
// unicode ?
*
DataType
=
SQL_CHAR
;
// unicode ?
if
(
ColumnSize
)
*
ColumnSize
-=
VARSTR_HEADER_SIZE
;
}
break
;
case
TSDB_DATA_TYPE_BINARY
:
{
*
DataType
=
SQL_
C_
BINARY
;
*
DataType
=
SQL_BINARY
;
if
(
ColumnSize
)
*
ColumnSize
-=
VARSTR_HEADER_SIZE
;
}
break
;
...
...
@@ -2204,13 +2229,6 @@ static SQLRETURN doSQLDescribeCol(SQLHSTMT StatementHandle,
break
;
}
}
if
(
DecimalDigits
)
{
if
(
field
->
type
==
TSDB_DATA_TYPE_TIMESTAMP
)
{
*
DecimalDigits
=
3
;
}
else
{
*
DecimalDigits
=
0
;
}
}
if
(
Nullable
)
{
*
Nullable
=
SQL_NULLABLE_UNKNOWN
;
}
...
...
@@ -2340,10 +2358,6 @@ SQLRETURN SQL_API SQLSetStmtAttr(SQLHSTMT StatementHandle,
static
void
init_routine
(
void
)
{
if
(
0
)
{
string_conv
(
NULL
,
NULL
,
NULL
,
0
,
NULL
,
0
,
NULL
,
NULL
);
...
...
@@ -2763,7 +2777,7 @@ static SQLRETURN conv_tsdb_f8_to_c_binary(sql_t *sql, c_target_t *target, TAOS_F
}
static
SQLRETURN
conv_tsdb_ts_to_c_v8
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
)
static
SQLRETURN
conv_tsdb_ts_to_c_v8
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
)
{
struct
tm
tm
=
{
0
};
tm
.
tm_sec
=
ts
->
second
;
...
...
@@ -2776,12 +2790,12 @@ static SQLRETURN conv_tsdb_ts_to_c_v8(sql_t *sql, c_target_t *target, TAOS_FIELD
DASSERT
(
sizeof
(
t
)
==
sizeof
(
int64_t
));
int64_t
v
=
(
int64_t
)
t
;
v
*=
1000
;
v
+=
ts
->
fraction
%
1
000
;
v
+=
ts
->
fraction
/
1000
000
;
memcpy
(
target
->
ptr
,
&
v
,
sizeof
(
v
));
return
SQL_SUCCESS
;
}
static
SQLRETURN
conv_tsdb_ts_to_c_str
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
)
static
SQLRETURN
conv_tsdb_ts_to_c_str
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
)
{
struct
tm
tm
=
{
0
};
tm
.
tm_sec
=
ts
->
second
;
...
...
@@ -2797,7 +2811,10 @@ static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIEL
*
target
->
soi
=
n
;
snprintf
(
target
->
ptr
,
target
->
len
,
"%s.%03d"
,
buf
,
ts
->
fraction
);
unsigned
int
fraction
=
ts
->
fraction
;
fraction
/=
1000000
;
snprintf
(
target
->
ptr
,
target
->
len
,
"%s.%03d"
,
buf
,
fraction
);
if
(
target
->
soi
)
*
target
->
soi
=
strlen
((
const
char
*
)
target
->
ptr
);
if
(
n
<=
target
->
len
)
{
return
SQL_SUCCESS
;
...
...
@@ -2807,7 +2824,7 @@ static SQLRETURN conv_tsdb_ts_to_c_str(sql_t *sql, c_target_t *target, TAOS_FIEL
return
SQL_SUCCESS_WITH_INFO
;
}
static
SQLRETURN
conv_tsdb_ts_to_c_bin
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
)
static
SQLRETURN
conv_tsdb_ts_to_c_bin
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
)
{
struct
tm
tm
=
{
0
};
tm
.
tm_sec
=
ts
->
second
;
...
...
@@ -2821,9 +2838,10 @@ static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIEL
int
n
=
strftime
(
buf
,
sizeof
(
buf
),
"%Y-%m-%d %H:%M:%S"
,
&
tm
);
DASSERT
(
n
<
sizeof
(
buf
));
*
target
->
soi
=
n
;
memcpy
(
target
->
ptr
,
buf
,
(
n
>
target
->
len
?
target
->
len
:
n
));
unsigned
int
fraction
=
ts
->
fraction
;
fraction
/=
1000000
;
snprintf
(
target
->
ptr
,
target
->
len
,
"%s.%03d"
,
buf
,
fraction
);
if
(
target
->
soi
)
*
target
->
soi
=
strlen
((
const
char
*
)
target
->
ptr
);
if
(
n
<=
target
->
len
)
{
return
SQL_SUCCESS
;
...
...
@@ -2833,9 +2851,11 @@ static SQLRETURN conv_tsdb_ts_to_c_bin(sql_t *sql, c_target_t *target, TAOS_FIEL
return
SQL_SUCCESS_WITH_INFO
;
}
static
SQLRETURN
conv_tsdb_ts_to_c_ts
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
TIMESTAMP_STRUCT
*
ts
)
static
SQLRETURN
conv_tsdb_ts_to_c_ts
(
sql_t
*
sql
,
c_target_t
*
target
,
TAOS_FIELD
*
field
,
SQL_
TIMESTAMP_STRUCT
*
ts
)
{
DASSERT
(
target
->
len
==
sizeof
(
*
ts
));
memcpy
(
target
->
ptr
,
ts
,
sizeof
(
*
ts
));
*
target
->
soi
=
target
->
len
;
return
SQL_SUCCESS
;
}
...
...
@@ -3107,21 +3127,21 @@ const char* tsdb_int64_to_bit(int64_t src, int8_t *dst)
const
char
*
tsdb_int64_to_tinyint
(
int64_t
src
,
int8_t
*
dst
)
{
*
dst
=
src
;
if
(
src
>=
SCHAR_MIN
||
src
<=
SCHAR_MAX
)
return
SQL_SUCCESS
;
if
(
src
>=
SCHAR_MIN
&&
src
<=
SCHAR_MAX
)
return
NULL
;
return
"22003"
;
}
const
char
*
tsdb_int64_to_smallint
(
int64_t
src
,
int16_t
*
dst
)
{
*
dst
=
src
;
if
(
src
>=
SHRT_MIN
||
src
<=
SHRT_MAX
)
return
SQL_SUCCESS
;
if
(
src
>=
SHRT_MIN
&&
src
<=
SHRT_MAX
)
return
NULL
;
return
"22003"
;
}
const
char
*
tsdb_int64_to_int
(
int64_t
src
,
int32_t
*
dst
)
{
*
dst
=
src
;
if
(
src
>=
LONG_MIN
||
src
<=
LONG_MAX
)
return
SQL_SUCCESS
;
if
(
src
>=
LONG_MIN
&&
src
<=
LONG_MAX
)
return
NULL
;
return
"22003"
;
}
...
...
src/connector/odbc/tests/odbc.py
浏览文件 @
4d7a69d1
...
...
@@ -43,13 +43,10 @@ cursor = cnxn.cursor()
cursor
.
execute
(
"insert into db.t values('2020-10-13 06:44:00', 1, 127, 32767, 32768, 32769, 123.456, 789.987, 'hello', 'world')"
)
cursor
.
close
()
print
(
"hhhhhhhhhhhhhhhhhhhhhh"
)
cursor
=
cnxn
.
cursor
()
cursor
.
execute
(
"insert into db.t values(?,?,?,?,?,?,?,?,?,?)"
,
"2020-10-13 07:06:00"
,
0
,
229
,
32767
,
32768
,
32769
,
123.456
,
789.987
,
"hel后lo"
,
"wo哈rld"
);
cursor
.
execute
(
"insert into db.t values(?,?,?,?,?,?,?,?,?,?)"
,
"2020-10-13 07:06:00"
,
0
,
127
,
32767
,
32768
,
32769
,
123.456
,
789.987
,
"hel后lo"
,
"wo哈rld"
);
cursor
.
close
()
print
(
"hhhhhhhhhhhhhhhhhhhhhh"
)
cursor
=
cnxn
.
cursor
()
cursor
.
execute
(
"SELECT * from db.t"
)
row
=
cursor
.
fetchone
()
...
...
@@ -91,7 +88,7 @@ params = [ ('A', 1), ('B', 2), ('C', 3) ]
params
=
[
(
'2020-10-16 00:00:00'
,
1
),
(
'2020-10-16 00:00:01'
,
4
),
(
'2020-10-16 00:00:02'
,
5
),
(
'2020-10-16 00:00:03'
,
6
)
]
(
'2020-10-16 00:00:03
.009
'
,
6
)
]
cursor
=
cnxn
.
cursor
()
cursor
.
fast_executemany
=
True
cursor
.
executemany
(
"insert into db.v values (?, ?)"
,
params
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录