Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
clmforever
oceanbase
提交
25510c83
O
oceanbase
项目概览
clmforever
/
oceanbase
与 Fork 源项目一致
Fork自
oceanbase / oceanbase
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
O
oceanbase
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
25510c83
编写于
3月 10, 2022
作者:
Z
zs0
提交者:
LINGuanRen
3月 10, 2022
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix bug: create fd use invalid unique index
上级
33f67070
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
113 addition
and
88 deletion
+113
-88
src/sql/optimizer/ob_logical_operator.h
src/sql/optimizer/ob_logical_operator.h
+6
-3
src/sql/resolver/expr/ob_raw_expr_canonicalizer_impl.cpp
src/sql/resolver/expr/ob_raw_expr_canonicalizer_impl.cpp
+2
-0
src/sql/rewrite/ob_transform_utils.cpp
src/sql/rewrite/ob_transform_utils.cpp
+71
-71
src/sql/rewrite/ob_transform_utils.h
src/sql/rewrite/ob_transform_utils.h
+4
-3
src/sql/rewrite/ob_transform_where_subquery_pullup.cpp
src/sql/rewrite/ob_transform_where_subquery_pullup.cpp
+11
-2
unittest/sql/resolver/expr/test_raw_expr_canonicalizer.result
...test/sql/resolver/expr/test_raw_expr_canonicalizer.result
+19
-9
未找到文件。
src/sql/optimizer/ob_logical_operator.h
浏览文件 @
25510c83
...
...
@@ -261,9 +261,12 @@ static const char OPCOST[] = "OP_COST";
* these operator never generate expr
*/
#define IS_EXPR_PASSBY_OPER(type) \
(log_op_def::LOG_GRANULE_ITERATOR == (type) || log_op_def::LOG_LINK == (type) || \
log_op_def::LOG_EXCHANGE == (type) || log_op_def::LOG_MONITORING_DUMP == (type))
#define IS_EXPR_PASSBY_OPER(type) (log_op_def::LOG_GRANULE_ITERATOR == (type) \
|| log_op_def::LOG_LINK == (type) \
|| log_op_def::LOG_EXCHANGE == (type) \
|| log_op_def::LOG_MONITORING_DUMP == (type) \
|| log_op_def::LOG_JOIN_FILTER == (type))
struct
FilterCompare
{
FilterCompare
(
common
::
ObIArray
<
ObExprSelPair
>&
predicate_selectivities
)
...
...
src/sql/resolver/expr/ob_raw_expr_canonicalizer_impl.cpp
浏览文件 @
25510c83
...
...
@@ -33,6 +33,8 @@ int ObRawExprCanonicalizerImpl::canonicalize(ObRawExpr*& expr)
LOG_WARN
(
"cluster and or failed"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
pull_similar_expr
(
expr
)))
{
LOG_WARN
(
"pull similar expr failed"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
expr
->
extract_info
()))
{
LOG_WARN
(
"failed to extract info"
,
K
(
ret
),
K
(
*
expr
));
}
return
ret
;
}
...
...
src/sql/rewrite/ob_transform_utils.cpp
浏览文件 @
25510c83
...
...
@@ -2533,7 +2533,44 @@ int ObTransformUtils::is_match_index(const ObDMLStmt* stmt, const ObIArray<uint6
return
ret
;
}
int
ObTransformUtils
::
extract_query_ref_expr
(
ObIArray
<
ObRawExpr
*>&
exprs
,
ObIArray
<
ObQueryRefRawExpr
*>&
subqueries
)
int
ObTransformUtils
::
extract_inseparable_query_ref_expr
(
ObIArray
<
ObRawExpr
*>
&
exprs
,
ObIArray
<
ObRawExpr
*>
&
target_exprs
)
{
int
ret
=
OB_SUCCESS
;
for
(
int64_t
i
=
0
;
OB_SUCC
(
ret
)
&&
i
<
exprs
.
count
();
i
++
)
{
if
(
OB_FAIL
(
extract_inseparable_query_ref_expr
(
exprs
.
at
(
i
),
target_exprs
)))
{
LOG_WARN
(
"failed to extract query ref exprs"
,
K
(
ret
));
}
}
return
ret
;
}
int
ObTransformUtils
::
extract_inseparable_query_ref_expr
(
ObRawExpr
*
expr
,
ObIArray
<
ObRawExpr
*>
&
target_exprs
)
{
int
ret
=
OB_SUCCESS
;
if
(
OB_ISNULL
(
expr
))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"expr is null"
,
K
(
ret
));
}
else
if
(
expr
->
is_query_ref_expr
()
||
T_OP_EXISTS
==
expr
->
get_expr_type
()
||
T_OP_NOT_EXISTS
==
expr
->
get_expr_type
()
||
expr
->
has_flag
(
IS_WITH_ALL
)
||
expr
->
has_flag
(
IS_WITH_ANY
))
{
if
(
OB_FAIL
(
add_var_to_array_no_dup
(
target_exprs
,
expr
)))
{
LOG_WARN
(
"failed to add var to array no dup"
,
K
(
ret
));
}
}
else
if
(
expr
->
has_flag
(
CNT_SUB_QUERY
))
{
for
(
int64_t
i
=
0
;
OB_SUCC
(
ret
)
&&
i
<
expr
->
get_param_count
();
++
i
)
{
if
(
OB_FAIL
(
SMART_CALL
(
extract_inseparable_query_ref_expr
(
expr
->
get_param_expr
(
i
),
target_exprs
))))
{
LOG_WARN
(
"failed to extract query ref expr"
,
K
(
ret
));
}
}
}
return
ret
;
}
int
ObTransformUtils
::
extract_query_ref_expr
(
ObIArray
<
ObRawExpr
*>
&
exprs
,
ObIArray
<
ObQueryRefRawExpr
*>
&
subqueries
)
{
int
ret
=
OB_SUCCESS
;
for
(
int64_t
i
=
0
;
OB_SUCC
(
ret
)
&&
i
<
exprs
.
count
();
i
++
)
{
...
...
@@ -3465,13 +3502,14 @@ int ObTransformUtils::compute_basic_table_property(ObDMLStmt* stmt, UniqueCheckH
ObIArray
<
ObRawExpr
*>&
cond_exprs
,
UniqueCheckInfo
&
res_info
)
{
int
ret
=
OB_SUCCESS
;
ObSEArray
<
ObRawExpr
*
,
8
>
cur_cond_exprs
;
const
ObTableSchema
*
table_schema
=
NULL
;
ObSEArray
<
ObAuxTableMetaInfo
,
16
>
index_infos
;
ObSchemaChecker
*
schema_checker
=
NULL
;
ObSEArray
<
ObRawExpr
*
,
8
>
cur_cond_exprs
;
ObSqlBitSet
<>
table_set
;
ObSqlSchemaGuard
*
schema_guard
=
NULL
;
uint64_t
index_tids
[
OB_MAX_INDEX_PER_TABLE
];
int64_t
index_count
=
OB_MAX_INDEX_PER_TABLE
;
if
(
OB_ISNULL
(
stmt
)
||
OB_ISNULL
(
table
)
||
OB_ISNULL
(
check_helper
.
alloc_
)
||
OB_ISNULL
(
check_helper
.
fd_factory_
)
||
OB_ISNULL
(
schema_checker
=
check_helper
.
schema_checker_
))
{
OB_ISNULL
(
check_helper
.
schema_checker_
)
||
OB_ISNULL
(
schema_guard
=
check_helper
.
schema_checker_
->
get_sql_schema_guard
()))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"unexpected null"
,
K
(
ret
));
}
else
if
(
!
table
->
is_basic_table
())
{
...
...
@@ -3488,43 +3526,31 @@ int ObTransformUtils::compute_basic_table_property(ObDMLStmt* stmt, UniqueCheckH
LOG_WARN
(
"failed to compute const exprs"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
ObEqualAnalysis
::
compute_equal_set
(
check_helper
.
alloc_
,
cur_cond_exprs
,
res_info
.
equal_sets_
)))
{
LOG_WARN
(
"failed to compute compute equal set"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
schema_checker
->
get_table_schema
(
table
->
ref_id_
,
table_schema
)))
{
LOG_WARN
(
"failed to get table schema"
,
K
(
ret
),
K
(
table
->
ref_id_
));
}
else
if
(
OB_ISNULL
(
table_schema
))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"table schema should not be null"
,
K
(
ret
),
K
(
table
));
}
else
if
(
OB_FAIL
(
ObOptimizerUtil
::
try_add_fd_item
(
stmt
,
*
check_helper
.
fd_factory_
,
table
->
table_id_
,
res_info
.
table_set_
,
table_schema
,
cur_cond_exprs
,
res_info
.
not_null_
,
res_info
.
fd_sets_
,
res_info
.
candi_fd_sets_
)))
{
LOG_WARN
(
"failed to try add fd item"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
table_schema
->
get_simple_index_infos_without_delay_deleted_tid
(
index_infos
,
false
)))
{
LOG_WARN
(
"get simple_index_infos without delay_deleted_tid failed"
,
K
(
ret
));
}
for
(
int64_t
i
=
0
;
OB_SUCC
(
ret
)
&&
i
<
index_infos
.
count
();
++
i
)
{
const
ObTableSchema
*
index_schema
=
NULL
;
if
(
OB_FAIL
(
schema_checker
->
get_table_schema
(
index_infos
.
at
(
i
).
table_id_
,
index_schema
)))
{
LOG_WARN
(
"failed to get table schema"
,
K
(
ret
));
}
else
if
(
OB_ISNULL
(
index_schema
))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"get null index schema"
,
K
(
ret
));
}
else
if
(
!
index_schema
->
is_unique_index
())
{
/*do nothing*/
}
else
if
(
OB_FAIL
(
ObOptimizerUtil
::
try_add_fd_item
(
stmt
,
*
check_helper
.
fd_factory_
,
table
->
table_id_
,
res_info
.
table_set_
,
index_schema
,
cur_cond_exprs
,
res_info
.
not_null_
,
res_info
.
fd_sets_
,
res_info
.
candi_fd_sets_
)))
{
LOG_WARN
(
"failed to try add fd item"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
schema_guard
->
get_can_read_index_array
(
table
->
ref_id_
,
index_tids
,
index_count
,
false
,
true
/*global index*/
,
false
/*domain index*/
)))
{
LOG_WARN
(
"failed to get can read index"
,
K
(
ret
),
K
(
table
->
ref_id_
));
}
else
{
for
(
int64_t
i
=
-
1
;
OB_SUCC
(
ret
)
&&
i
<
index_count
;
++
i
)
{
const
ObTableSchema
*
index_schema
=
NULL
;
uint64_t
index_id
=
(
i
==
-
1
?
table
->
ref_id_
:
index_tids
[
i
]);
if
(
OB_FAIL
(
schema_guard
->
get_table_schema
(
index_id
,
index_schema
)))
{
LOG_WARN
(
"failed to get table schema"
,
K
(
ret
));
}
else
if
(
OB_ISNULL
(
index_schema
))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"get null index schema"
,
K
(
ret
));
}
else
if
(
-
1
!=
i
&&
!
index_schema
->
is_unique_index
())
{
// do nothing
}
else
if
(
OB_FAIL
(
ObOptimizerUtil
::
try_add_fd_item
(
stmt
,
*
check_helper
.
fd_factory_
,
table
->
table_id_
,
res_info
.
table_set_
,
index_schema
,
cur_cond_exprs
,
res_info
.
not_null_
,
res_info
.
fd_sets_
,
res_info
.
candi_fd_sets_
)))
{
LOG_WARN
(
"failed to try add fd item"
,
K
(
ret
));
}
}
}
return
ret
;
...
...
@@ -5359,7 +5385,6 @@ int ObTransformUtils::create_simple_view(
if
(
OB_SUCC
(
ret
))
{
// create select list
ObSEArray
<
ObRawExpr
*
,
4
>
columns
;
ObSEArray
<
ObQueryRefRawExpr
*
,
4
>
query_refs
;
ObSqlBitSet
<>
from_tables
;
ObSEArray
<
ObRawExpr
*
,
16
>
shared_exprs
;
if
(
OB_FAIL
(
view_stmt
->
get_from_tables
(
from_tables
)))
{
...
...
@@ -5368,10 +5393,8 @@ int ObTransformUtils::create_simple_view(
LOG_WARN
(
"failed to get column exprs"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
extract_table_exprs
(
*
view_stmt
,
columns
,
from_tables
,
select_list
)))
{
LOG_WARN
(
"failed to extract table exprs"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
extract_
query_ref_expr
(
post_join_exprs
,
query_refs
)))
{
}
else
if
(
OB_FAIL
(
extract_
inseparable_query_ref_expr
(
post_join_exprs
,
select_list
)))
{
LOG_WARN
(
"failed to extract query refs"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
append
(
select_list
,
query_refs
)))
{
LOG_WARN
(
"failed to append query ref exprs"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
extract_shared_expr
(
stmt
,
view_stmt
,
shared_exprs
)))
{
LOG_WARN
(
"failed to extract shared expr"
,
K
(
ret
));
}
else
if
(
OB_FAIL
(
append
(
select_list
,
shared_exprs
)))
{
...
...
@@ -6156,30 +6179,7 @@ int ObTransformUtils::rebuild_select_items(ObSelectStmt& stmt, ObRelIds& output_
return
ret
;
}
int
ObTransformUtils
::
create_select_item_for_subquery
(
ObSelectStmt
&
stmt
,
ObSelectStmt
*&
child_stmt
,
ObIAllocator
&
alloc
,
ObIArray
<
ObRawExpr
*>&
query_ref_exprs
)
{
int
ret
=
OB_SUCCESS
;
ObSEArray
<
ObQueryRefRawExpr
*
,
4
>
tmp_query_ref_exprs
;
for
(
int64_t
i
=
0
;
OB_SUCC
(
ret
)
&&
i
<
stmt
.
get_select_item_size
();
++
i
)
{
ObRawExpr
*
cur_expr
=
stmt
.
get_select_item
(
i
).
expr_
;
if
(
OB_FAIL
(
ObTransformUtils
::
extract_query_ref_expr
(
cur_expr
,
tmp_query_ref_exprs
)))
{
LOG_WARN
(
"failed to extract query ref expr"
,
K
(
ret
));
}
}
if
(
OB_FAIL
(
ret
))
{
}
else
if
(
OB_FAIL
(
append
(
query_ref_exprs
,
tmp_query_ref_exprs
)))
{
LOG_WARN
(
"failed to append exprs"
,
K
(
ret
));
}
for
(
int64_t
i
=
0
;
OB_SUCC
(
ret
)
&&
i
<
query_ref_exprs
.
count
();
++
i
)
{
if
(
OB_FAIL
(
create_select_item
(
alloc
,
query_ref_exprs
.
at
(
i
),
child_stmt
)))
{
LOG_WARN
(
"failed to create select item"
,
K
(
ret
));
}
}
return
ret
;
}
int
ObTransformUtils
::
right_join_to_left
(
ObDMLStmt
*
stmt
)
int
ObTransformUtils
::
right_join_to_left
(
ObDMLStmt
*
stmt
)
{
int
ret
=
OB_SUCCESS
;
if
(
OB_ISNULL
(
stmt
))
{
...
...
src/sql/rewrite/ob_transform_utils.h
浏览文件 @
25510c83
...
...
@@ -313,6 +313,10 @@ public:
const
ObColumnRefRawExpr
*
col_expr
,
bool
&
is_match
,
EqualSets
*
equal_sets
=
NULL
,
ObIArray
<
ObColumnRefRawExpr
*>*
col_exprs
=
NULL
);
static
int
extract_inseparable_query_ref_expr
(
ObIArray
<
ObRawExpr
*>
&
exprs
,
ObIArray
<
ObRawExpr
*>
&
target_exprs
);
static
int
extract_inseparable_query_ref_expr
(
ObRawExpr
*
expr
,
ObIArray
<
ObRawExpr
*>
&
target_exprs
);
static
int
extract_query_ref_expr
(
ObIArray
<
ObRawExpr
*>&
exprs
,
ObIArray
<
ObQueryRefRawExpr
*>&
subqueries
);
static
int
extract_query_ref_expr
(
ObRawExpr
*
expr
,
ObIArray
<
ObQueryRefRawExpr
*>&
subqueries
);
...
...
@@ -753,9 +757,6 @@ public:
static
int
replace_with_groupby_exprs
(
ObSelectStmt
*
select_stmt
,
ObRawExpr
*&
expr
);
private:
static
int
create_select_item_for_subquery
(
ObSelectStmt
&
stmt
,
ObSelectStmt
*&
child_stmt
,
ObIAllocator
&
alloc
,
ObIArray
<
ObRawExpr
*>&
query_ref_exprs
);
static
int
add_non_duplicated_select_expr
(
ObIArray
<
ObRawExpr
*>&
add_select_exprs
,
ObIArray
<
ObRawExpr
*>&
org_select_exprs
);
};
...
...
src/sql/rewrite/ob_transform_where_subquery_pullup.cpp
浏览文件 @
25510c83
...
...
@@ -2166,6 +2166,8 @@ int ObWhereSubQueryPullup::transform_single_set_query(ObDMLStmt* stmt, bool& tra
OB_ISNULL
(
queries
.
at
(
j
)
->
get_ref_stmt
()))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"invalid subquery"
,
K
(
ret
));
}
else
if
(
queries
.
at
(
j
)
->
get_ref_stmt
()
->
is_eliminated
())
{
// do nothing
}
else
if
(
is_vector_query
(
queries
.
at
(
j
)))
{
// not necessary limitation
}
else
if
(
OB_FAIL
(
tmp
.
push_back
(
queries
.
at
(
j
))))
{
...
...
@@ -2196,7 +2198,13 @@ int ObWhereSubQueryPullup::transform_single_set_query(ObDMLStmt* stmt, bool& tra
}
}
for
(
int64_t
j
=
0
;
OB_SUCC
(
ret
)
&&
j
<
queries
.
count
();
++
j
)
{
if
(
OB_FAIL
(
unnest_single_set_subquery
(
stmt
,
queries
.
at
(
j
),
true
,
is_vector_assign
,
is_select_expr
)))
{
ObSelectStmt
*
subquery
=
NULL
;
if
(
OB_ISNULL
(
queries
.
at
(
j
))
||
OB_ISNULL
(
subquery
=
queries
.
at
(
j
)
->
get_ref_stmt
()))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"get unexpected null"
,
K
(
ret
));
}
else
if
(
subquery
->
is_eliminated
())
{
//do nothing
}
else
if
(
OB_FAIL
(
unnest_single_set_subquery
(
stmt
,
queries
.
at
(
j
),
true
,
is_vector_assign
,
is_select_expr
)))
{
LOG_WARN
(
"failed to unnest single set subquery"
,
K
(
ret
));
}
else
{
trans_happened
=
true
;
...
...
@@ -2260,7 +2268,8 @@ int ObWhereSubQueryPullup::check_subquery_validity(ObSelectStmt* subquery, bool&
if
(
OB_ISNULL
(
subquery
))
{
ret
=
OB_ERR_UNEXPECTED
;
LOG_WARN
(
"subquery is null"
,
K
(
ret
),
K
(
subquery
));
}
else
if
(
!
subquery
->
is_spj
()
||
subquery
->
has_subquery
()
||
subquery
->
get_stmt_hint
().
enable_no_unnest
())
{
}
else
if
(
!
subquery
->
is_spj
()
||
subquery
->
has_subquery
()
||
subquery
->
get_stmt_hint
().
enable_no_unnest
()
||
subquery
->
is_eliminated
())
{
is_valid
=
false
;
}
else
if
(
OB_FAIL
(
subquery
->
get_column_exprs
(
columns
)))
{
LOG_WARN
(
"failed to get column exprs"
,
K
(
ret
));
...
...
unittest/sql/resolver/expr/test_raw_expr_canonicalizer.result
浏览文件 @
25510c83
...
...
@@ -20,9 +20,11 @@ not (10 > 5 and 100 < 9)
}
},
"expr_info": [
"IS_OR",
"IS_CALCULABLE_EXPR",
"IS_CONST_EXPR",
"CNT_CONST",
"CNT_OR",
"CNT_CALCULABLE_EXPR"
],
"rel_id": [
...
...
@@ -474,8 +476,7 @@ not a = b
}
},
"expr_info": [
"CNT_COLUMN",
"IS_JOIN_COND"
"CNT_COLUMN"
],
"rel_id": [
],
...
...
@@ -709,10 +710,8 @@ not a is true
}
},
"expr_info": [
"IS_IS_EXPR",
"CNT_CONST",
"CNT_COLUMN",
"CNT_IS_EXPR"
"CNT_COLUMN"
],
"rel_id": [
],
...
...
@@ -847,8 +846,7 @@ not a between 1 and 100
},
"expr_info": [
"CNT_CONST",
"CNT_COLUMN",
"IS_RANGE_COND"
"CNT_COLUMN"
],
"rel_id": [
],
...
...
@@ -1166,11 +1164,9 @@ not a between 1 and 100
}
},
"expr_info": [
"IS_OR",
"IS_CALCULABLE_EXPR",
"IS_CONST_EXPR",
"CNT_CONST",
"CNT_OR",
"CNT_CALCULABLE_EXPR"
],
"rel_id": [
...
...
@@ -1352,6 +1348,11 @@ not a between 1 and 100
}
},
"expr_info": [
"IS_CALCULABLE_EXPR",
"IS_CONST_EXPR",
"CNT_CONST",
"CNT_OR",
"CNT_CALCULABLE_EXPR"
],
"rel_id": [
],
...
...
@@ -1615,6 +1616,10 @@ A or (A And B) or (A And C)
}
},
"expr_info": [
"IS_OR",
"CNT_CONST",
"CNT_COLUMN",
"CNT_OR"
],
"rel_id": [
],
...
...
@@ -1642,6 +1647,8 @@ A or (A And B) or (A And C)
}
},
"expr_info": [
"IS_CONST",
"CNT_CONST"
],
"rel_id": [
],
...
...
@@ -1716,6 +1723,7 @@ A or (A And B) or (A And C)
}
},
"expr_info": [
"CNT_COLUMN"
],
"rel_id": [
],
...
...
@@ -1832,6 +1840,7 @@ A or (A And B) or (A And C)
}
},
"expr_info": [
"CNT_COLUMN"
],
"rel_id": [
],
...
...
@@ -1948,6 +1957,7 @@ A or (A And B) or (A And C)
}
},
"expr_info": [
"CNT_COLUMN"
],
"rel_id": [
],
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录