[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, const ObExprCalcType calc_type,
CollectionPredRes &result) CollectionPredRes &result)
{ {
int ret = OB_SUCCESS; int ret = OB_NOT_SUPPORTED;
result = COLL_PRED_FALSE; LOG_USER_ERROR(OB_NOT_SUPPORTED, "SUBMULTISET expr is");
if (obj1.get_meta().is_ext() && obj2.get_meta().is_ext()) { LOG_WARN("SUBMULTISET expr is not supported");
pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext()); // result = COLL_PRED_FALSE;
pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext()); // if (obj1.get_meta().is_ext() && obj2.get_meta().is_ext()) {
int64_t c1_cnt = c1->get_actual_count(); // pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext());
int64_t c2_cnt = c2->get_actual_count(); // pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext());
// 1) return true when: // int64_t c1_cnt = c1->get_actual_count();
// 1. obj1 is not null and contains no row, return true whatever obj2 is. // int64_t c2_cnt = c2->get_actual_count();
// for example obj nest_type := nest_type(); // // 1) return true when:
// 2. obj1 and obj2 not null, obj1 does not contain any null element, obj1 elem one-to-one // // 1. obj1 is not null and contains no row, return true whatever obj2 is.
// mapping to obj2 element. // // 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: // // 2) return null when:
// 1. obj1 is null // // 1. obj1 is null
// 2. obj2 is null, and obj1 is not null and not empty. // // 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 // // 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 // // 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 // // nt3 submultiset nt1 => false
// 3) return false when: // // 3) return false when:
// if none of above conditions occur, return false; // // if none of above conditions occur, return false;
if (OB_ISNULL(c1) || OB_ISNULL(c2)) { // if (OB_ISNULL(c1) || OB_ISNULL(c2)) {
ret = OB_ERR_UNEXPECTED; // ret = OB_ERR_UNEXPECTED;
LOG_WARN("union udt failed due to null udt", K(ret), K(obj1), K(obj2)); // 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() // } else if (pl::PL_NESTED_TABLE_TYPE != c1->get_type()
|| pl::PL_NESTED_TABLE_TYPE != c2->get_type()) { // || pl::PL_NESTED_TABLE_TYPE != c2->get_type()) {
ret = OB_NOT_SUPPORTED; // ret = OB_NOT_SUPPORTED;
LOG_WARN("not support udt union except nested table", K(ret), K(c1), K(c2)); // 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"); // 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())) { // } else if ((c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type())) {
ret = OB_ERR_UNEXPECTED; // ret = OB_ERR_UNEXPECTED;
LOG_WARN("udt union failed due to uninited", K(ret)); // LOG_WARN("udt union failed due to uninited", K(ret));
} else if (0 == c1_cnt && (c1->is_inited())) { // } else if (0 == c1_cnt && (c1->is_inited())) {
result = COLL_PRED_TRUE; // result = COLL_PRED_TRUE;
} else if (!c1->is_inited()) { // } else if (!c1->is_inited()) {
result = COLL_PRED_NULL; // null // result = COLL_PRED_NULL; // null
} else if (!c2->is_inited()) { // } else if (!c2->is_inited()) {
result = COLL_PRED_NULL; // result = COLL_PRED_NULL;
} else if (c1_cnt > c2_cnt) { // } else if (c1_cnt > c2_cnt) {
result = COLL_PRED_FALSE; // result = COLL_PRED_FALSE;
} else if (calc_collection_is_contained_without_null(c1, c2, tz_offset, calc_type, result)) { // } 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)); // LOG_WARN("faile to calc multiset without null and delete val", K(ret));
} else { // } else {
// 去除了null和delete数据之后比较,如果是包含关系,但是c1含有null,置结果为null. // // 去除了null和delete数据之后比较,如果是包含关系,但是c1含有null,置结果为null.
if (COLL_PRED_TRUE == result && c1->is_contain_null_val()) { // if (COLL_PRED_TRUE == result && c1->is_contain_null_val()) {
result = COLL_PRED_NULL; // result = COLL_PRED_NULL;
} // }
} // }
} // }
return ret; return ret;
} }
@ -405,36 +407,42 @@ int ObExprCollPred::eval_coll_pred(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &
if (obj2.get_meta().is_ext()) { if (obj2.get_meta().is_ext()) {
pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext()); pl::ObPLCollection *c2 = reinterpret_cast<pl::ObPLCollection *>(obj2.get_ext());
if (OB_NOT_NULL(c2) && 0 < c2->get_actual_count()) { if (OB_NOT_NULL(c2) && 0 < c2->get_actual_count()) {
ObObj *elem = static_cast<ObObj*>(c2->get_data()); if (c2->is_of_composite()) {
ObObj mem_cast; ret = OB_NOT_SUPPORTED;
const ObObj * res_obj1 = &obj1; LOG_USER_ERROR(OB_NOT_SUPPORTED, "MEMBER OF for collections of composite types is");
ObCollationType cast_coll_type = elem->get_collation_type(); LOG_WARN("MEMBER OF for collections of composite types is not supported", K(c2->get_element_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 { } else {
LocalNTSHashMap dmap_c; ObObj *elem = static_cast<ObObj*>(c2->get_data());
int64_t res_cnt = 0; ObObj mem_cast;
FILL_HASH_MAP(dmap_c, c2, res_cnt); const ObObj * res_obj1 = &obj1;
if (OB_SUCC(ret)) { ObCollationType cast_coll_type = elem->get_collation_type();
if (OB_NOT_NULL(dmap_c.get(*res_obj1)) ^ (MULTISET_MODIFIER_NOT == info->ms_modifier_)) { ObCastMode cp_cast_mode;
result.set_tinyint(1); if (OB_FAIL(ObSQLUtils::get_default_cast_mode(ctx.exec_ctx_.get_my_session(),
} else { cp_cast_mode))) {
result.set_tinyint(0); 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()) { if (obj1.get_meta().is_ext()) {
pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext()); pl::ObPLCollection *c1 = reinterpret_cast<pl::ObPLCollection *>(obj1.get_ext());
if (OB_NOT_NULL(c1)) { if (OB_NOT_NULL(c1)) {
LocalNTSHashMap dmap_c; if (c1->is_of_composite()) {
int64_t count = c1->get_actual_count(); ret = OB_NOT_SUPPORTED;
int res_cnt = 0; LOG_USER_ERROR(OB_NOT_SUPPORTED, "IS A SET for collections of composite types is");
if (0 == count) { LOG_WARN("IS A SET for collections of composite types is not supported", K(c1->get_element_type()));
// empty nest table is a set
result.set_tinyint(1);
} else if (!c1->is_inited()) {
result.set_null();
} else { } else {
FILL_HASH_MAP(dmap_c, c1, res_cnt); LocalNTSHashMap dmap_c;
if (OB_SUCC(ret)) { int64_t count = c1->get_actual_count();
if ((count == res_cnt) ^ (MULTISET_MODIFIER_NOT == info->ms_modifier_)) { int res_cnt = 0;
result.set_tinyint(1); if (0 == count) {
} else { // empty nest table is a set
result.set_tinyint(0); 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_type(ObExtendType));
OX (type.set_extend_type(type_));
OX (type.set_udt_id(udt_id_)); OX (type.set_udt_id(udt_id_));
return ret; return ret;
} }

View File

@ -487,61 +487,63 @@ int ObExprInOrNotIn::calc_result_typeN(ObExprResType &type,
} }
/* 比较规则: /* 比较规则:
* 1null之后相等c1或c2包含nullnull * Oracle document:
* 2c2未初始化null * Two nested table variables are equal if and only if they have the same set of elements (in any order).
* 3c1未初始化null
* 4c1 in (c2,c3,c4), nullnull * the problem is how to define "the same set of elements", which is not documented by Oracle.
* 5false * the rules we follow here are:
* 6true * 1. if the elements are of an uncomparable type, such as Record, return an error
* 7nt1 in nt1, true, nulltrue * 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, int ObExprInOrNotIn::eval_pl_udt_in(const ObExpr &expr,
ObEvalCtx &ctx, ObEvalCtx &ctx,
ObDatum &expr_datum) ObDatum &expr_datum)
{ {
int ret = OB_SUCCESS; int ret = OB_NOT_SUPPORTED;
CollectionPredRes res = CollectionPredRes::COLL_PRED_INVALID; LOG_USER_ERROR(OB_NOT_SUPPORTED, "IN expr for composite types is");
ObDatum *left = NULL; LOG_WARN("IN expr for composite types is not supported", K(expr));
ObDatum *right = NULL; // CollectionPredRes res = CollectionPredRes::COLL_PRED_INVALID;
pl::ObPLCollection *coll = NULL; // ObDatum *left = NULL;
OZ(expr.args_[0]->eval(ctx, left)); // ObDatum *right = NULL;
if (OB_SUCC(ret)) { // pl::ObPLCollection *coll = NULL;
coll = reinterpret_cast<pl::ObPLCollection *>(left->get_ext()); // bool is_any_result_null = false;
if (OB_NOT_NULL(coll) && coll->is_contain_null_val()) { // OZ(expr.args_[0]->eval(ctx, left));
set_datum_result(T_OP_IN == expr.type_, false, true, expr_datum); // if (OB_SUCC(ret)) {
} else { // coll = reinterpret_cast<pl::ObPLCollection *>(left->get_ext());
const ObExpr *row = expr.args_[1]; // const ObExpr *row = expr.args_[1];
for (int64_t i = 0; OB_SUCC(ret) && i < row->arg_cnt_; ++i) { // for (int64_t i = 0; OB_SUCC(ret) && i < row->arg_cnt_; ++i) {
OZ(row->args_[i]->eval(ctx, right)); // OZ(row->args_[i]->eval(ctx, right));
CollectionPredRes eq_cmp_res = COLL_PRED_INVALID; // CollectionPredRes eq_cmp_res = COLL_PRED_INVALID;
if (OB_SUCC(ret)) { // if (OB_SUCC(ret)) {
if (OB_FAIL(ObRelationalExprOperator::pl_udt_compare2( // if (OB_FAIL(ObRelationalExprOperator::pl_udt_compare2(
eq_cmp_res, *left->extend_obj_, *right->extend_obj_, // eq_cmp_res, *left->extend_obj_, *right->extend_obj_,
ctx.exec_ctx_, CO_EQ))) { // ctx.exec_ctx_, CO_EQ))) {
LOG_WARN("failed to compare to nest table", K(ret)); // LOG_WARN("failed to compare to nest table", K(ret));
} else { // } else {
res = static_cast<CollectionPredRes>(eq_cmp_res); // res = static_cast<CollectionPredRes>(eq_cmp_res);
if (COLL_PRED_TRUE == res) { // if (COLL_PRED_TRUE == res) {
break; // break;
} else if (COLL_PRED_NULL == res) { // } else if (COLL_PRED_NULL == res) {
break; // is_any_result_null = true;
} // }
} // }
} // }
} // if (OB_SUCC(ret)) {
if (OB_SUCC(ret)) { // if (COLL_PRED_TRUE == res) {
if (COLL_PRED_NULL == res) { // set_datum_result(T_OP_IN == expr.type_, true, false, expr_datum);
set_datum_result(T_OP_IN == expr.type_, false, true, expr_datum); // } else if (is_any_result_null) {
} else if (COLL_PRED_TRUE == res) { // set_datum_result(T_OP_IN == expr.type_, false, true, expr_datum);
set_datum_result(T_OP_IN == expr.type_, true, false, expr_datum); // } else {
} else if (COLL_PRED_FALSE == res) { // set_datum_result(T_OP_IN == expr.type_, false, false, expr_datum);
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); // }
} // }
}
}
}
return ret; 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()) { } else if (c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type()) {
ret = OB_ERR_UNEXPECTED; ret = OB_ERR_UNEXPECTED;
LOG_WARN("udt union failed due to uninited", K(ret), KPC(c1), KPC(c2)); 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()) { } else if (!c1->is_inited() || !c2->is_inited()) {
// if has uninit collection, result is uninit, so do nothing ... // if has uninit collection, result is uninit, so do nothing ...
} else { } 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()) { } else if (c1->get_element_type().get_obj_type() != c2->get_element_type().get_obj_type()) {
ret = OB_ERR_UNEXPECTED; ret = OB_ERR_UNEXPECTED;
LOG_WARN("not support udt compare with different elem type", K(ret), K(c1), K(c2)); 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()) { } else if (!c1->is_inited() || !c2->is_inited()) {
cmp_result = CollectionPredRes::COLL_PRED_NULL; cmp_result = CollectionPredRes::COLL_PRED_NULL;
} else if ((c1->get_actual_count() != c2->get_actual_count())) { } 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())) { } else if (0 > c1->get_count() || !(c1->is_inited())) {
ret = OB_ERR_UNEXPECTED; ret = OB_ERR_UNEXPECTED;
LOG_WARN("union udt failed due to null udt", K(ret), K(c1->get_count()), K(c1->is_inited())); 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 { } else {
coll = static_cast<pl::ObPLNestedTable*>(allocator.alloc(sizeof(pl::ObPLNestedTable))); coll = static_cast<pl::ObPLNestedTable*>(allocator.alloc(sizeof(pl::ObPLNestedTable)));
if (OB_ISNULL(coll)) { if (OB_ISNULL(coll)) {