[to #53431523] forbid collection functions with potential correctness problem

This commit is contained in:
0xacc 2023-12-22 11:43:15 +00:00 committed by ob-robot
parent 043cf555ed
commit 2939bcb2af
6 changed files with 172 additions and 141 deletions

View File

@ -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);
}
}
}
}

View File

@ -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;
}

View File

@ -487,61 +487,63 @@ int ObExprInOrNotIn::calc_result_typeN(ObExprResType &type,
}
/* 比较规则:
* 1null之后相等c1或c2包含nullnull
* 2c2未初始化null
* 3c1未初始化null
* 4c1 in (c2,c3,c4), nullnull
* 5false
* 6true
* 7nt1 in nt1, true, nulltrue
* 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;
}

View File

@ -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 {

View File

@ -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())) {

View File

@ -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)) {