diff --git a/src/sql/resolver/dml/ob_inlist_resolver.cpp b/src/sql/resolver/dml/ob_inlist_resolver.cpp index e50b59968..f7a1fe23a 100644 --- a/src/sql/resolver/dml/ob_inlist_resolver.cpp +++ b/src/sql/resolver/dml/ob_inlist_resolver.cpp @@ -36,9 +36,14 @@ int ObInListResolver::resolve_inlist(ObInListInfo &inlist_info) LOG_WARN("got NULL ptr", K(ret), KP(list_node), KP(cur_resolver_)); } else { ObResolverParams ¶ms = cur_resolver_->params_; - if (OB_FAIL(resolve_values_table_from_inlist(list_node, column_cnt, row_cnt, - inlist_info.is_question_mark_, params.param_list_, - params.session_info_, params.allocator_, + if (OB_FAIL(resolve_values_table_from_inlist(list_node, + column_cnt, + row_cnt, + inlist_info.is_question_mark_, + params.is_prepare_stage_, + params.param_list_, + params.session_info_, + params.allocator_, table_def))) { LOG_WARN("failed to resolve values table from inlist", K(ret)); } else if (OB_FAIL(resolve_subquery_from_values_table(params.stmt_factory_, @@ -60,6 +65,7 @@ int ObInListResolver::resolve_values_table_from_inlist(const ParseNode *in_list, const int64_t column_cnt, const int64_t row_cnt, const bool is_question_mark, + const bool is_prepare_stage, const ParamStore *param_store, ObSQLSessionInfo *session_info, ObIAllocator *allocator, @@ -67,8 +73,10 @@ int ObInListResolver::resolve_values_table_from_inlist(const ParseNode *in_list, { int ret = OB_SUCCESS; char *table_buf = NULL; - ObValuesTableDef::TableAccessType access_type = is_question_mark ? - ObValuesTableDef::ACCESS_PARAM : ObValuesTableDef::ACCESS_OBJ; + // we treat question marks in prepare stmt as objs instead of params + ObValuesTableDef::TableAccessType access_type = is_question_mark && !is_prepare_stage + ? ObValuesTableDef::ACCESS_PARAM + : ObValuesTableDef::ACCESS_OBJ; if (OB_ISNULL(allocator) || OB_ISNULL(session_info) || OB_ISNULL(in_list)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("got unexpected NULL ptr", K(ret)); @@ -92,7 +100,7 @@ int ObInListResolver::resolve_values_table_from_inlist(const ParseNode *in_list, LOG_WARN("failed to resolve access param values table", K(ret)); } else if (ObValuesTableDef::ACCESS_OBJ == access_type && OB_FAIL(resolve_access_obj_values_table(*in_list, column_cnt, row_cnt, session_info, - allocator, *table_def))) { + allocator, is_prepare_stage, *table_def))) { LOG_WARN("failed to resolve access obj values table", K(ret)); } else if (OB_FAIL(cur_resolver_->estimate_values_table_stats(*table_def))) { LOG_WARN("failed to estimate values table stats", K(ret)); @@ -168,6 +176,7 @@ int ObInListResolver::check_inlist_rewrite_enable(const ParseNode &in_list, is_enable = false; int64_t threshold = INT64_MAX; uint64_t optimizer_features_enable_version = 0; + bool is_prepare_stmt = false; // 1. check basic requests if (OB_ISNULL(session_info)) { ret = OB_ERR_UNEXPECTED; @@ -177,12 +186,11 @@ int ObInListResolver::check_inlist_rewrite_enable(const ParseNode &in_list, || (T_OP_IN != op_type && T_OP_NOT_IN != op_type) || T_EXPR_LIST != in_list.type_ || is_need_print - || session_info->is_varparams_sql_prepare() || (NULL != stmt && stmt->is_select_stmt() && static_cast(stmt)->is_hierarchical_query())) { - LOG_TRACE("no need rewrite inlist", K(is_root_condition), K(scope), K(in_list.type_), - K(op_type), K(is_need_print), K(session_info->is_varparams_sql_prepare())); + LOG_TRACE("no need rewrite inlist", + K(is_root_condition), K(scope), K(in_list.type_), K(op_type), K(is_need_print)); } else { if (NULL == stmt) { if (OB_FAIL(session_info->get_optimizer_features_enable_version(optimizer_features_enable_version))) { @@ -195,6 +203,7 @@ int ObInListResolver::check_inlist_rewrite_enable(const ParseNode &in_list, ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { + is_prepare_stmt = stmt->get_query_ctx()->is_prepare_stmt(); threshold = session_info->get_inlist_rewrite_threshold(); const ObGlobalHint &global_hint = stmt->get_query_ctx()->get_global_hint(); if (OB_FAIL(global_hint.opt_params_.get_integer_opt_param( @@ -240,54 +249,45 @@ int ObInListResolver::check_inlist_rewrite_enable(const ParseNode &in_list, share::SYS_VAR_COLLATION_SERVER, server_collation))) { LOG_WARN("get sys variables failed", K(ret)); } else { - for (int64_t j = 0; OB_SUCC(ret) && is_enable && j < column_cnt; j++) { - const ParseNode *node = column_cnt == 1 ? in_list.children_[0] : - in_list.children_[0]->children_[j]; - DistinctObjMeta param_type; - // know this inlist is question mark or const - if (j == 0) { - if (OB_ISNULL(node)) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("get unexpected param", K(ret)); - } else { - is_question_mark = T_QUESTIONMARK == node->type_; - } - } - if (OB_FAIL(ret)) { - } else if (OB_FAIL(get_const_node_types(node, param_store, is_question_mark, - connect_collation, nchar_collation, static_cast(server_collation), enable_decimal_int, alloc, - param_type, is_enable))) { - LOG_WARN("failed to got const node types", K(ret)); - } else if (is_enable) { - if (lib::is_oracle_mode() && ObCharType == param_type.obj_type_) { - // in oracle mode, inlist to values table rewrite may cast char types to varchar2 - // but comparison behaviors for chars and varchar2s are different for trailing spaces - // which will lead to unexpect comparison result - is_enable = false; - } else - if (ob_is_enum_or_set_type(param_type.obj_type_) || - is_lob_locator(param_type.obj_type_)) { - is_enable = false; - } else if (OB_FAIL(param_types.push_back(param_type))) { - LOG_WARN("failed to push back", K(ret)); - } - } - } - - for (int64_t i = 1; OB_SUCC(ret) && is_enable && i < row_cnt; i++) { - for (int64_t j = 0; OB_SUCC(ret) && is_enable && j < column_cnt; j++) { + for (int64_t j = 0; OB_SUCC(ret) && is_enable && j < column_cnt; ++j) { + DistinctObjMeta param_type_prev; + for (int64_t i = 0; OB_SUCC(ret) && is_enable && i < row_cnt; ++i) { if (OB_UNLIKELY(column_cnt > 1 && in_list.children_[i]->num_child_ != column_cnt)) { is_enable = false; /* delay return error code */ } else { - const ParseNode *node = column_cnt == 1 ? in_list.children_[i] : - in_list.children_[i]->children_[j]; - DistinctObjMeta param_type; - if (OB_FAIL(get_const_node_types(node, param_store, is_question_mark, - connect_collation, nchar_collation, static_cast(server_collation), enable_decimal_int, alloc, - param_type, is_enable))) { + DistinctObjMeta param_type_cur; + const ParseNode *node = column_cnt == 1 ? in_list.children_[i] + : in_list.children_[i]->children_[j]; + if (OB_ISNULL(node)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get unexpected param", K(ret)); + } else if (FALSE_IT(is_question_mark = T_QUESTIONMARK == node->type_)) { + } else if (is_prepare_stmt && is_question_mark) { + // skip type matching for question marks in prepare stmt, because they have no type + } else if (OB_FAIL(get_const_node_types(node, + param_store, + is_question_mark, + connect_collation, + nchar_collation, + static_cast(server_collation), + enable_decimal_int, + alloc, + param_type_cur, + is_enable))) { LOG_WARN("failed to got const node types", K(ret)); - } else if (is_enable && param_type == param_types.at(j)) { - /*is same type*/ + } else if (!is_enable) { + } else if (ObMaxType == param_type_prev.obj_type_) { + param_type_prev = param_type_cur; + if (lib::is_oracle_mode() && ObCharType == param_type_cur.obj_type_) { + // in oracle mode, inlist to values table rewrite may cast char types to varchar2 + // but comparison behaviors for chars and varchar2s are different for trailing spaces + // which will lead to unexpect comparison result + is_enable = false; + } else if (ob_is_enum_or_set_type(param_type_cur.obj_type_) + || is_lob_locator(param_type_cur.obj_type_)) { + is_enable = false; + } + } else if (param_type_prev == param_type_cur) { } else { is_enable = false; } @@ -401,6 +401,7 @@ int ObInListResolver::resolve_access_obj_values_table(const ParseNode &in_list, const int64_t row_cnt, ObSQLSessionInfo *session_info, ObIAllocator *allocator, + const bool is_prepare_stage, ObValuesTableDef &table_def) { int ret = OB_SUCCESS; @@ -449,8 +450,18 @@ int ObInListResolver::resolve_access_obj_values_table(const ParseNode &in_list, } for (int64_t j = 0; OB_SUCC(ret) && j < column_cnt; j++) { const ParseNode *element = column_cnt == 1 ? row_node : row_node->children_[j]; + ParseNode pseudo_char_node = {.type_ = T_CHAR, .str_value_ = NULL, .str_len_ = 0}; ObObjParam obj_param; ObExprResType res_type; + if (is_prepare_stage && T_QUESTIONMARK == element->type_) { + // In prepare stage, we resolve question marks as an empty char node. + // This is just for result type aggregation in prepare stage only. + // In execution time of ps protocol, the question marks will be resolved with real values. + pseudo_char_node.type_ = T_CHAR; + pseudo_char_node.str_len_ = 0; + pseudo_char_node.str_value_ = NULL; + element = &pseudo_char_node; + } if (OB_FAIL(ObResolverUtils::resolve_const(element, stmt_type, *allocator, coll_type, nchar_collation, timezone_info, obj_param, is_paramlize, literal_prefix, length_semantics, diff --git a/src/sql/resolver/dml/ob_inlist_resolver.h b/src/sql/resolver/dml/ob_inlist_resolver.h index f07e7b92e..c517ace31 100644 --- a/src/sql/resolver/dml/ob_inlist_resolver.h +++ b/src/sql/resolver/dml/ob_inlist_resolver.h @@ -59,6 +59,7 @@ private: const int64_t column_cnt, const int64_t row_cnt, const bool is_question_mark, + const bool is_prepare_stmt, const ParamStore *param_store, ObSQLSessionInfo *session_info, ObIAllocator *allocator, @@ -94,6 +95,7 @@ private: const int64_t row_cnt, ObSQLSessionInfo *session_info, ObIAllocator *allocator, + const bool is_prepare_stage, ObValuesTableDef &table_def); private: ObDMLResolver *cur_resolver_;