[to #53431523] forbid collection functions with potential correctness problem
This commit is contained in:
parent
043cf555ed
commit
2939bcb2af
@ -218,56 +218,58 @@ int ObExprCollPred::calc_is_submultiset(const ObObj &obj1,
|
||||
const ObExprCalcType calc_type,
|
||||
CollectionPredRes &result)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
result = COLL_PRED_FALSE;
|
||||
if (obj1.get_meta().is_ext() && obj2.get_meta().is_ext()) {
|
||||
pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext());
|
||||
pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext());
|
||||
int64_t c1_cnt = c1->get_actual_count();
|
||||
int64_t c2_cnt = c2->get_actual_count();
|
||||
// 1) return true when:
|
||||
// 1. obj1 is not null and contains no row, return true whatever obj2 is.
|
||||
// for example obj nest_type := nest_type();
|
||||
// 2. obj1 and obj2 not null, obj1 does not contain any null element, obj1 elem one-to-one
|
||||
// mapping to obj2 element.
|
||||
int ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "SUBMULTISET expr is");
|
||||
LOG_WARN("SUBMULTISET expr is not supported");
|
||||
// result = COLL_PRED_FALSE;
|
||||
// if (obj1.get_meta().is_ext() && obj2.get_meta().is_ext()) {
|
||||
// pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext());
|
||||
// pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext());
|
||||
// int64_t c1_cnt = c1->get_actual_count();
|
||||
// int64_t c2_cnt = c2->get_actual_count();
|
||||
// // 1) return true when:
|
||||
// // 1. obj1 is not null and contains no row, return true whatever obj2 is.
|
||||
// // for example obj nest_type := nest_type();
|
||||
// // 2. obj1 and obj2 not null, obj1 does not contain any null element, obj1 elem one-to-one
|
||||
// // mapping to obj2 element.
|
||||
|
||||
// 2) return null when:
|
||||
// 1. obj1 is null
|
||||
// 2. obj2 is null, and obj1 is not null and not empty.
|
||||
// 3. obj1 contain null element, and any non null is element is one-to-one mapping to obj2 elem
|
||||
// for example: nt1(1,2,3,4,5), nt2(1,2,NULL), nt3(1,7,null); nt2 submultiset of nt1 => null
|
||||
// nt3 submultiset nt1 => false
|
||||
// // 2) return null when:
|
||||
// // 1. obj1 is null
|
||||
// // 2. obj2 is null, and obj1 is not null and not empty.
|
||||
// // 3. obj1 contain null element, and any non null is element is one-to-one mapping to obj2 elem
|
||||
// // for example: nt1(1,2,3,4,5), nt2(1,2,NULL), nt3(1,7,null); nt2 submultiset of nt1 => null
|
||||
// // nt3 submultiset nt1 => false
|
||||
|
||||
// 3) return false when:
|
||||
// if none of above conditions occur, return false;
|
||||
if (OB_ISNULL(c1) || OB_ISNULL(c2)) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("union udt failed due to null udt", K(ret), K(obj1), K(obj2));
|
||||
} else if (pl::PL_NESTED_TABLE_TYPE != c1->get_type()
|
||||
|| pl::PL_NESTED_TABLE_TYPE != c2->get_type()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_WARN("not support udt union except nested table", K(ret), K(c1), K(c2));
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "udt union except nested table");
|
||||
} else if ((c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type())) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("udt union failed due to uninited", K(ret));
|
||||
} else if (0 == c1_cnt && (c1->is_inited())) {
|
||||
result = COLL_PRED_TRUE;
|
||||
} else if (!c1->is_inited()) {
|
||||
result = COLL_PRED_NULL; // null
|
||||
} else if (!c2->is_inited()) {
|
||||
result = COLL_PRED_NULL;
|
||||
} else if (c1_cnt > c2_cnt) {
|
||||
result = COLL_PRED_FALSE;
|
||||
} else if (calc_collection_is_contained_without_null(c1, c2, tz_offset, calc_type, result)) {
|
||||
LOG_WARN("faile to calc multiset without null and delete val", K(ret));
|
||||
} else {
|
||||
// 去除了null和delete数据之后比较,如果是包含关系,但是c1含有null,置结果为null.
|
||||
if (COLL_PRED_TRUE == result && c1->is_contain_null_val()) {
|
||||
result = COLL_PRED_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
// // 3) return false when:
|
||||
// // if none of above conditions occur, return false;
|
||||
// if (OB_ISNULL(c1) || OB_ISNULL(c2)) {
|
||||
// ret = OB_ERR_UNEXPECTED;
|
||||
// LOG_WARN("union udt failed due to null udt", K(ret), K(obj1), K(obj2));
|
||||
// } else if (pl::PL_NESTED_TABLE_TYPE != c1->get_type()
|
||||
// || pl::PL_NESTED_TABLE_TYPE != c2->get_type()) {
|
||||
// ret = OB_NOT_SUPPORTED;
|
||||
// LOG_WARN("not support udt union except nested table", K(ret), K(c1), K(c2));
|
||||
// LOG_USER_ERROR(OB_NOT_SUPPORTED, "udt union except nested table");
|
||||
// } else if ((c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type())) {
|
||||
// ret = OB_ERR_UNEXPECTED;
|
||||
// LOG_WARN("udt union failed due to uninited", K(ret));
|
||||
// } else if (0 == c1_cnt && (c1->is_inited())) {
|
||||
// result = COLL_PRED_TRUE;
|
||||
// } else if (!c1->is_inited()) {
|
||||
// result = COLL_PRED_NULL; // null
|
||||
// } else if (!c2->is_inited()) {
|
||||
// result = COLL_PRED_NULL;
|
||||
// } else if (c1_cnt > c2_cnt) {
|
||||
// result = COLL_PRED_FALSE;
|
||||
// } else if (calc_collection_is_contained_without_null(c1, c2, tz_offset, calc_type, result)) {
|
||||
// LOG_WARN("faile to calc multiset without null and delete val", K(ret));
|
||||
// } else {
|
||||
// // 去除了null和delete数据之后比较,如果是包含关系,但是c1含有null,置结果为null.
|
||||
// if (COLL_PRED_TRUE == result && c1->is_contain_null_val()) {
|
||||
// result = COLL_PRED_NULL;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -405,36 +407,42 @@ int ObExprCollPred::eval_coll_pred(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &
|
||||
if (obj2.get_meta().is_ext()) {
|
||||
pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext());
|
||||
if (OB_NOT_NULL(c2) && 0 < c2->get_actual_count()) {
|
||||
ObObj *elem = static_cast<ObObj*>(c2->get_data());
|
||||
ObObj mem_cast;
|
||||
const ObObj * res_obj1 = &obj1;
|
||||
ObCollationType cast_coll_type = elem->get_collation_type();
|
||||
ObCastMode cp_cast_mode;
|
||||
if (OB_FAIL(ObSQLUtils::get_default_cast_mode(ctx.exec_ctx_.get_my_session(),
|
||||
cp_cast_mode))) {
|
||||
LOG_WARN("failed to get default cast mode", K(ret));
|
||||
}
|
||||
const ObDataTypeCastParams dtc_params
|
||||
= ObBasicSessionInfo::create_dtc_params(ctx.exec_ctx_.get_my_session());
|
||||
ObCastCtx cast_ctx(&ctx.exec_ctx_.get_allocator(),
|
||||
&dtc_params,
|
||||
get_cur_time(ctx.exec_ctx_.get_physical_plan_ctx()),
|
||||
cp_cast_mode,
|
||||
cast_coll_type,
|
||||
nullptr);
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (OB_FAIL(ObObjCaster::to_type(elem->get_type(), cast_ctx,
|
||||
obj1, mem_cast, res_obj1))) {
|
||||
LOG_WARN("failed to cast member to collection elem type", K(ret));
|
||||
if (c2->is_of_composite()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "MEMBER OF for collections of composite types is");
|
||||
LOG_WARN("MEMBER OF for collections of composite types is not supported", K(c2->get_element_type()));
|
||||
} else {
|
||||
LocalNTSHashMap dmap_c;
|
||||
int64_t res_cnt = 0;
|
||||
FILL_HASH_MAP(dmap_c, c2, res_cnt);
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_NOT_NULL(dmap_c.get(*res_obj1)) ^ (MULTISET_MODIFIER_NOT == info->ms_modifier_)) {
|
||||
result.set_tinyint(1);
|
||||
} else {
|
||||
result.set_tinyint(0);
|
||||
ObObj *elem = static_cast<ObObj*>(c2->get_data());
|
||||
ObObj mem_cast;
|
||||
const ObObj * res_obj1 = &obj1;
|
||||
ObCollationType cast_coll_type = elem->get_collation_type();
|
||||
ObCastMode cp_cast_mode;
|
||||
if (OB_FAIL(ObSQLUtils::get_default_cast_mode(ctx.exec_ctx_.get_my_session(),
|
||||
cp_cast_mode))) {
|
||||
LOG_WARN("failed to get default cast mode", K(ret));
|
||||
}
|
||||
const ObDataTypeCastParams dtc_params
|
||||
= ObBasicSessionInfo::create_dtc_params(ctx.exec_ctx_.get_my_session());
|
||||
ObCastCtx cast_ctx(&ctx.exec_ctx_.get_allocator(),
|
||||
&dtc_params,
|
||||
get_cur_time(ctx.exec_ctx_.get_physical_plan_ctx()),
|
||||
cp_cast_mode,
|
||||
cast_coll_type,
|
||||
nullptr);
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (OB_FAIL(ObObjCaster::to_type(elem->get_type(), cast_ctx,
|
||||
obj1, mem_cast, res_obj1))) {
|
||||
LOG_WARN("failed to cast member to collection elem type", K(ret));
|
||||
} else {
|
||||
LocalNTSHashMap dmap_c;
|
||||
int64_t res_cnt = 0;
|
||||
FILL_HASH_MAP(dmap_c, c2, res_cnt);
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_NOT_NULL(dmap_c.get(*res_obj1)) ^ (MULTISET_MODIFIER_NOT == info->ms_modifier_)) {
|
||||
result.set_tinyint(1);
|
||||
} else {
|
||||
result.set_tinyint(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -446,21 +454,27 @@ int ObExprCollPred::eval_coll_pred(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &
|
||||
if (obj1.get_meta().is_ext()) {
|
||||
pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext());
|
||||
if (OB_NOT_NULL(c1)) {
|
||||
LocalNTSHashMap dmap_c;
|
||||
int64_t count = c1->get_actual_count();
|
||||
int res_cnt = 0;
|
||||
if (0 == count) {
|
||||
// empty nest table is a set
|
||||
result.set_tinyint(1);
|
||||
} else if (!c1->is_inited()) {
|
||||
result.set_null();
|
||||
if (c1->is_of_composite()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "IS A SET for collections of composite types is");
|
||||
LOG_WARN("IS A SET for collections of composite types is not supported", K(c1->get_element_type()));
|
||||
} else {
|
||||
FILL_HASH_MAP(dmap_c, c1, res_cnt);
|
||||
if (OB_SUCC(ret)) {
|
||||
if ((count == res_cnt) ^ (MULTISET_MODIFIER_NOT == info->ms_modifier_)) {
|
||||
result.set_tinyint(1);
|
||||
} else {
|
||||
result.set_tinyint(0);
|
||||
LocalNTSHashMap dmap_c;
|
||||
int64_t count = c1->get_actual_count();
|
||||
int res_cnt = 0;
|
||||
if (0 == count) {
|
||||
// empty nest table is a set
|
||||
result.set_tinyint(1);
|
||||
} else if (!c1->is_inited()) {
|
||||
result.set_null();
|
||||
} else {
|
||||
FILL_HASH_MAP(dmap_c, c1, res_cnt);
|
||||
if (OB_SUCC(ret)) {
|
||||
if ((count == res_cnt) ^ (MULTISET_MODIFIER_NOT == info->ms_modifier_)) {
|
||||
result.set_tinyint(1);
|
||||
} else {
|
||||
result.set_tinyint(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ int ObExprCollectionConstruct::calc_result_typeN(ObExprResType &type,
|
||||
}
|
||||
}
|
||||
OX (type.set_type(ObExtendType));
|
||||
OX (type.set_extend_type(type_));
|
||||
OX (type.set_udt_id(udt_id_));
|
||||
return ret;
|
||||
}
|
||||
|
@ -487,61 +487,63 @@ int ObExprInOrNotIn::calc_result_typeN(ObExprResType &type,
|
||||
}
|
||||
|
||||
/* 比较规则:
|
||||
* 1、去除null之后相等,但是c1或c2包含null,结果是null
|
||||
* 2、c2未初始化,结果是null
|
||||
* 3、c1未初始化,结果是null
|
||||
* 4、c1 in (c2,c3,c4), 如果其中某个结果是null,则整体结果是null
|
||||
* 5、除上面情况,不相等,false
|
||||
* 6、其它情况,true
|
||||
* 7、nt1 in nt1, true, 但是,两个未初始化集合比较,结果为null,空集和空集比较,true
|
||||
* Oracle document:
|
||||
* Two nested table variables are equal if and only if they have the same set of elements (in any order).
|
||||
|
||||
* the problem is how to define "the same set of elements", which is not documented by Oracle.
|
||||
* the rules we follow here are:
|
||||
* 1. if the elements are of an uncomparable type, such as Record, return an error
|
||||
* 2. when NULL (NULL can be a nested table itself or its element) is compared with any other element, return NULL
|
||||
* 3. nt in (nt1, nt2, ...) returns:
|
||||
* a. TRUE if any of nt=nt1, nt=nt2, ... is TRUE
|
||||
* b. NULL if none of them is TRUE, and at least one of them is NULL
|
||||
* c. FALSE if all of them are FALSE
|
||||
*/
|
||||
// forbid composite types in IN expr before composite types comparison is refactored
|
||||
int ObExprInOrNotIn::eval_pl_udt_in(const ObExpr &expr,
|
||||
ObEvalCtx &ctx,
|
||||
ObDatum &expr_datum)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
CollectionPredRes res = CollectionPredRes::COLL_PRED_INVALID;
|
||||
ObDatum *left = NULL;
|
||||
ObDatum *right = NULL;
|
||||
pl::ObPLCollection *coll = NULL;
|
||||
OZ(expr.args_[0]->eval(ctx, left));
|
||||
if (OB_SUCC(ret)) {
|
||||
coll = reinterpret_cast<pl::ObPLCollection *>(left->get_ext());
|
||||
if (OB_NOT_NULL(coll) && coll->is_contain_null_val()) {
|
||||
set_datum_result(T_OP_IN == expr.type_, false, true, expr_datum);
|
||||
} else {
|
||||
const ObExpr *row = expr.args_[1];
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < row->arg_cnt_; ++i) {
|
||||
OZ(row->args_[i]->eval(ctx, right));
|
||||
CollectionPredRes eq_cmp_res = COLL_PRED_INVALID;
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_FAIL(ObRelationalExprOperator::pl_udt_compare2(
|
||||
eq_cmp_res, *left->extend_obj_, *right->extend_obj_,
|
||||
ctx.exec_ctx_, CO_EQ))) {
|
||||
LOG_WARN("failed to compare to nest table", K(ret));
|
||||
} else {
|
||||
res = static_cast<CollectionPredRes>(eq_cmp_res);
|
||||
if (COLL_PRED_TRUE == res) {
|
||||
break;
|
||||
} else if (COLL_PRED_NULL == res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
if (COLL_PRED_NULL == res) {
|
||||
set_datum_result(T_OP_IN == expr.type_, false, true, expr_datum);
|
||||
} else if (COLL_PRED_TRUE == res) {
|
||||
set_datum_result(T_OP_IN == expr.type_, true, false, expr_datum);
|
||||
} else if (COLL_PRED_FALSE == res) {
|
||||
set_datum_result(T_OP_IN == expr.type_, false, false, expr_datum);
|
||||
} else {
|
||||
set_datum_result(T_OP_IN == expr.type_, false, false, expr_datum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "IN expr for composite types is");
|
||||
LOG_WARN("IN expr for composite types is not supported", K(expr));
|
||||
// CollectionPredRes res = CollectionPredRes::COLL_PRED_INVALID;
|
||||
// ObDatum *left = NULL;
|
||||
// ObDatum *right = NULL;
|
||||
// pl::ObPLCollection *coll = NULL;
|
||||
// bool is_any_result_null = false;
|
||||
// OZ(expr.args_[0]->eval(ctx, left));
|
||||
// if (OB_SUCC(ret)) {
|
||||
// coll = reinterpret_cast<pl::ObPLCollection *>(left->get_ext());
|
||||
// const ObExpr *row = expr.args_[1];
|
||||
// for (int64_t i = 0; OB_SUCC(ret) && i < row->arg_cnt_; ++i) {
|
||||
// OZ(row->args_[i]->eval(ctx, right));
|
||||
// CollectionPredRes eq_cmp_res = COLL_PRED_INVALID;
|
||||
// if (OB_SUCC(ret)) {
|
||||
// if (OB_FAIL(ObRelationalExprOperator::pl_udt_compare2(
|
||||
// eq_cmp_res, *left->extend_obj_, *right->extend_obj_,
|
||||
// ctx.exec_ctx_, CO_EQ))) {
|
||||
// LOG_WARN("failed to compare to nest table", K(ret));
|
||||
// } else {
|
||||
// res = static_cast<CollectionPredRes>(eq_cmp_res);
|
||||
// if (COLL_PRED_TRUE == res) {
|
||||
// break;
|
||||
// } else if (COLL_PRED_NULL == res) {
|
||||
// is_any_result_null = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (OB_SUCC(ret)) {
|
||||
// if (COLL_PRED_TRUE == res) {
|
||||
// set_datum_result(T_OP_IN == expr.type_, true, false, expr_datum);
|
||||
// } else if (is_any_result_null) {
|
||||
// set_datum_result(T_OP_IN == expr.type_, false, true, expr_datum);
|
||||
// } else {
|
||||
// set_datum_result(T_OP_IN == expr.type_, false, false, expr_datum);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -529,6 +529,10 @@ int ObExprMultiSet::eval_multiset(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &r
|
||||
} else if (c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type()) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("udt union failed due to uninited", K(ret), KPC(c1), KPC(c2));
|
||||
} else if (c1->is_of_composite()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "MULTISET expr for collections of composite types is");
|
||||
LOG_WARN("MULTISET expr for collections of composite types is not supported", K(c1->get_element_type()), K(c2->get_element_type()));
|
||||
} else if (!c1->is_inited() || !c2->is_inited()) {
|
||||
// if has uninit collection, result is uninit, so do nothing ...
|
||||
} else {
|
||||
|
@ -2889,6 +2889,12 @@ int ObRelationalExprOperator::pl_udt_compare2(CollectionPredRes &cmp_result,
|
||||
} else if (c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type()) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("not support udt compare with different elem type", K(ret), K(c1), K(c2));
|
||||
} else if (c1->is_of_composite()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "comparison of composite types is");
|
||||
LOG_WARN("comparison of composite types is not supported",
|
||||
K(c1->get_element_type()),
|
||||
K(c2->get_element_type()));
|
||||
} else if (!c1->is_inited() || !c2->is_inited()) {
|
||||
cmp_result = CollectionPredRes::COLL_PRED_NULL;
|
||||
} else if ((c1->get_actual_count() != c2->get_actual_count())) {
|
||||
|
@ -99,6 +99,10 @@ int ObExprSet::eval_coll(const ObObj &obj, ObExecContext &ctx, pl::ObPLNestedTab
|
||||
} else if (0 > c1->get_count() || !(c1->is_inited())) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("union udt failed due to null udt", K(ret), K(c1->get_count()), K(c1->is_inited()));
|
||||
} else if (c1->is_of_composite()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "SET for collections of composite types is");
|
||||
LOG_WARN("SET for collections of composite types is not supported", K(c1->get_element_type()));
|
||||
} else {
|
||||
coll = static_cast<pl::ObPLNestedTable*>(allocator.alloc(sizeof(pl::ObPLNestedTable)));
|
||||
if (OB_ISNULL(coll)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user