/** * Copyright (c) 2021 OceanBase * OceanBase CE is licensed under Mulan PubL v2. * You can use this software according to the terms and conditions of the Mulan PubL v2. * You may obtain a copy of Mulan PubL v2 at: * http://license.coscl.org.cn/MulanPubL-2.0 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PubL v2 for more details. */ #define USING_LOG_PREFIX SQL_OPT #include "sql/optimizer/ob_log_expr_values.h" #include "sql/optimizer/ob_optimizer_util.h" #include "sql/optimizer/ob_opt_est_cost.h" #include "sql/optimizer/ob_log_plan.h" #include "sql/engine/expr/ob_expr_column_conv.h" #include "sql/optimizer/ob_del_upd_log_plan.h" #include "sql/optimizer/ob_join_order.h" #include "sql/rewrite/ob_transform_utils.h" using namespace oceanbase::common; namespace oceanbase { namespace sql { /** * Print log info with expressions */ #define EXPLAIN_PRINT_INSERT_VALUES(values, column_count, type) \ { \ if (OB_ISNULL(values)) { \ ret = OB_ERR_UNEXPECTED; \ } else if (OB_FAIL(BUF_PRINTF(#values"("))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } else { \ int64_t N = values->count(); \ int64_t M = column_count; \ if (N == 0 || M == 0) { \ if (OB_FAIL(BUF_PRINTF("nil"))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } \ } else if (OB_UNLIKELY(0 != N % M)) { \ ret = OB_ERR_UNEXPECTED; \ LOG_WARN("invalid value count", K(ret), "value_count", N, "row_count", M); \ } else { \ for (int64_t i = 0; OB_SUCC(ret) && i < N / M; i++) { \ if (OB_FAIL(BUF_PRINTF("{"))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } \ for (int64_t j = 0; OB_SUCC(ret) && j < M; j++) { \ int64_t expr_idx = i * M + j; \ if (OB_UNLIKELY(expr_idx >= values->count()) || OB_UNLIKELY(expr_idx < 0)) { \ ret = OB_ERR_UNEXPECTED; \ } else if (OB_ISNULL(values->at(expr_idx))) { \ ret = OB_ERR_UNEXPECTED; \ } else { \ if (OB_FAIL(values->at(expr_idx)->get_name(buf, buf_len, pos, type))) { \ } else { \ if (j < M - 1) { \ if (OB_FAIL(BUF_PRINTF(", "))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } \ } \ } \ } \ } \ if (OB_FAIL(BUF_PRINTF("}"))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } else if (i < N / M - 1) { \ if (OB_FAIL(BUF_PRINTF(", "))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } \ } \ } \ } \ if (OB_SUCC(ret)) { \ if (OB_FAIL(BUF_PRINTF(")"))) { \ LOG_WARN("fail to print to buf", K(ret)); \ } \ } \ } \ } int ObLogExprValues::add_values_expr(const common::ObIArray &value_exprs) { int ret = OB_SUCCESS; if (OB_FAIL(append(value_exprs_, value_exprs))) { LOG_WARN("failed to append expr", K(ret)); } else if (get_stmt()->is_insert_stmt() && is_ins_values_batch_opt()) { const ObInsertStmt *insert_stmt = static_cast(get_stmt()); ObRawExpr *stmt_id_expr = NULL; if (OB_ISNULL(stmt_id_expr = insert_stmt->get_ab_stmt_id_expr())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt_id_expr is null", K(ret)); } else if (OB_FAIL(value_exprs_.push_back(stmt_id_expr))) { LOG_WARN("fail to push stmt_id_expr", K(ret)); } } return ret; } int ObLogExprValues::add_values_desc(const common::ObIArray &value_desc) { int ret = OB_SUCCESS; if (OB_FAIL(append(value_desc_, value_desc))) { LOG_WARN("failed to append desc", K(ret)); } return ret; } int ObLogExprValues::compute_fd_item_set() { int ret = OB_SUCCESS; if (OB_ISNULL(my_plan_) || OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpect parameter", K(my_plan_), K(get_stmt())); } else if (!get_stmt()->is_select_stmt()) { set_fd_item_set(&empty_fd_item_set_); } else { ObFdItemSet *fd_item_set = NULL; ObSEArray select_exprs; if (OB_FAIL(static_cast(get_stmt())->get_select_exprs(select_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (OB_FAIL(my_plan_->get_fd_item_factory().create_fd_item_set(fd_item_set))) { LOG_WARN("failed to create fd item set", K(ret)); } else if (!ObTransformUtils::need_compute_fd_item_set(select_exprs)) { //do nothing } else { for (int64_t i = 0; OB_SUCC(ret) && i < select_exprs.count(); ++i) { ObSEArray value_exprs; ObExprFdItem *fd_item = NULL; if (OB_FAIL(value_exprs.push_back(select_exprs.at(i)))) { LOG_WARN("failed to push back expr", K(ret)); } else if (OB_FAIL(my_plan_->get_fd_item_factory().create_expr_fd_item(fd_item, true, value_exprs, select_exprs))) { LOG_WARN("failed to create fd item", K(ret)); } else if (OB_FAIL(fd_item_set->push_back(fd_item))) { LOG_WARN("failed to push back fd item", K(ret)); } } if (OB_FAIL(ret)) { /*do nothing*/ } else if (OB_FAIL(deduce_const_exprs_and_ft_item_set(*fd_item_set))) { LOG_WARN("failed to deduce fd item set", K(ret)); } else { set_fd_item_set(fd_item_set); } } } return ret; } int ObLogExprValues::compute_equal_set() { int ret = OB_SUCCESS; set_output_equal_sets(&empty_expr_sets_); return ret; } int ObLogExprValues::compute_table_set() { int ret = OB_SUCCESS; set_table_set(&empty_table_set_); return ret; } int ObLogExprValues::compute_op_ordering() { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (!get_stmt()->is_select_stmt()) { /*do nothing*/ } else { ObSEArray select_exprs; if (OB_FAIL(static_cast(get_stmt())->get_select_exprs(select_exprs))) { LOG_WARN("failed to get select exprs", K(ret)); } else if (OB_FAIL(ObOptimizerUtil::make_sort_keys(select_exprs, op_ordering_))) { LOG_WARN("failed to copy sort keys", K(ret)); } else { /*do nothing*/ } } return ret; } int ObLogExprValues::est_cost() { int ret = OB_SUCCESS; double card = 0.0; double op_cost = 0.0; double cost = 0.0; EstimateCostInfo param; param.need_parallel_ = get_parallel(); if (OB_FAIL(do_re_est_cost(param, card, op_cost, cost))) { LOG_WARN("failed to get re est cost infos", K(ret)); } else { set_card(card); set_op_cost(op_cost); set_cost(cost); } return ret; } int ObLogExprValues::do_re_est_cost(EstimateCostInfo ¶m, double &card, double &op_cost, double &cost) { int ret = OB_SUCCESS; if (OB_ISNULL(get_plan()) || OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (get_stmt()->is_insert_stmt() || is_values_table_) { ObOptimizerContext &opt_ctx = get_plan()->get_optimizer_context(); card = get_stmt()->is_insert_stmt() ? static_cast(get_stmt())->get_insert_row_count() : get_values_row_count(); op_cost = ObOptEstCost::cost_get_rows(get_card(), opt_ctx); cost = op_cost; } else { ObOptimizerContext &opt_ctx = get_plan()->get_optimizer_context(); card = 1.0; op_cost = ObOptEstCost::cost_filter_rows(get_card(), filter_exprs_, opt_ctx); cost = op_cost; } return ret; } int ObLogExprValues::compute_sharding_info() { int ret = OB_SUCCESS; if (OB_ISNULL(get_plan())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { strong_sharding_ = get_plan()->get_optimizer_context().get_match_all_sharding(); } return ret; } int ObLogExprValues::compute_one_row_info() { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (!get_stmt()->is_insert_stmt()) { is_at_most_one_row_ = get_values_row_count() <= 1; } else { /*do nothing*/ } return ret; } int ObLogExprValues::get_op_exprs(ObIArray &all_exprs) { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(ObLogicalOperator::get_op_exprs(all_exprs))) { LOG_WARN("failed to get op exprs", K(ret)); } else if (OB_FAIL(append(all_exprs, value_exprs_))) { LOG_WARN("failed to append exprs", K(ret)); } else if (get_stmt()->is_insert_stmt()) { const ObInsertStmt *insert_stmt = static_cast(get_stmt()); if (OB_FAIL(append(all_exprs, insert_stmt->get_values_desc()))) { LOG_WARN("failed to append exprs", K(ret)); } else { /*do nothing*/ } } else if (is_values_table_) { if (OB_FAIL(append(all_exprs, value_desc_))) { LOG_WARN("failed to append exprs", K(ret)); } else { /*do nothing*/ } } else { /*do nothing*/ } return ret; } int ObLogExprValues::allocate_expr_post(ObAllocExprContext &ctx) { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(get_stmt()), K(ret)); } else if (get_stmt()->is_insert_stmt() || is_values_table_) { const ObIArray &values_desc = get_stmt()->is_insert_stmt() ? static_cast(get_stmt())->get_values_desc() : value_desc_; for (int64_t i = 0; OB_SUCC(ret) && i < values_desc.count(); ++i) { ObColumnRefRawExpr *value_col = values_desc.at(i); if (OB_FAIL(mark_expr_produced(value_col, branch_id_, id_, ctx))) { LOG_WARN("makr expr produced failed", K(ret)); } else if (!is_plan_root() && OB_FAIL(output_exprs_.push_back(value_col))) { LOG_WARN("failed to push back exprs", K(ret)); } else { /*do nothing*/ } } } if (OB_FAIL(ret)) { /*do nothing*/ } else if (OB_FAIL(ObLogicalOperator::allocate_expr_post(ctx))) { LOG_WARN("failed to allocate expr post", K(ret)); } else if (contain_array_binding_param() && OB_FAIL(construct_array_binding_values())) { LOG_WARN("construct array binding values failed", K(ret)); } else if (value_exprs_.empty() && OB_FAIL(append(value_exprs_, get_output_exprs()))) { LOG_WARN("failed to append exprs", K(ret)); } else if (value_exprs_.empty() && OB_FAIL(allocate_dummy_output())) { LOG_WARN("failed to allocate dummy output", K(ret)); } else if (OB_FAIL(construct_sequence_values())) { LOG_WARN("failed to construct sequence values", K(ret)); } else if (OB_FAIL(mark_probably_local_exprs())) { LOG_WARN("failed to mark local exprs", K(ret)); } else if (is_values_table_) { /* defence code */ if (OB_UNLIKELY(value_desc_.count() != get_output_exprs().count())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("defence code, EXPRESSION request output_exprs equals to value_desc", K(ret)); } } return ret; } int ObLogExprValues::construct_sequence_values() { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt is null", K(ret)); } else if (get_stmt()->is_insert_stmt() && !value_exprs_.empty() && output_exprs_.count() >= 2) { int64_t seq_expr_idx = -1; for (int64_t i = 0; OB_SUCC(ret) && i < output_exprs_.count(); ++i) { if (OB_ISNULL(output_exprs_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("output expr is null", K(ret)); } else if (T_FUN_SYS_SEQ_NEXTVAL == output_exprs_.at(i)->get_expr_type()) { seq_expr_idx = i; break; } } if (OB_SUCC(ret) && -1 != seq_expr_idx) { ObRawExpr *seq_expr = output_exprs_.at(seq_expr_idx); const int64_t value_count = value_exprs_.count(); const int64_t output_count = output_exprs_.count(); if (OB_UNLIKELY((0 != value_count % (output_count - 1)) || (0 == seq_expr_idx))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpected value count", K(value_count), K(output_count), K(seq_expr_idx)); } else { const int64_t group_num = value_count / (output_count - 1); ObSEArray new_value_exprs; if (OB_FAIL(new_value_exprs.reserve(group_num * output_count))) { LOG_WARN("failed to reserve array", K(ret)); } else { for (int64_t i = 0; OB_SUCC(ret) && i < value_exprs_.count(); ++i) { if (OB_FAIL(new_value_exprs.push_back(value_exprs_.at(i)))) { LOG_WARN("failed to push back expr", K(ret)); } else if (((i + 1) % seq_expr_idx == 0) && OB_FAIL(new_value_exprs.push_back(seq_expr))) { LOG_WARN("failed to push back sequence expr", K(ret)); } } if (OB_SUCC(ret) && OB_FAIL(value_exprs_.assign(new_value_exprs))) { LOG_WARN("failed to assign new value exprs", K(ret)); } } } } } return ret; } int ObLogExprValues::construct_array_binding_values() { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt()) || OB_ISNULL(get_plan()) || OB_UNLIKELY(!get_stmt()->is_select_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("array binding param only in select stmt", K(ret), KPC(get_stmt())); } else { const ObSelectStmt *select_stmt = static_cast(get_stmt()); if (OB_FAIL(value_exprs_.assign(select_stmt->get_query_ctx()->ab_param_exprs_))) { LOG_WARN("assign ab param exprs to value exprs failed", K(ret)); } else if (OB_FAIL(get_plan()->get_optimizer_context().get_all_exprs().append(value_exprs_))) { LOG_WARN("fail to append ab param exprs", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < output_exprs_.count(); ++i) { ObRawExpr *raw_expr = NULL; if (OB_ISNULL(raw_expr = output_exprs_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (raw_expr->get_expr_type() == T_PSEUDO_STMT_ID) { if (OB_FAIL(value_exprs_.push_back(raw_expr))) { LOG_WARN("failed to add expr", K(ret)); } } } } return ret; } int ObLogExprValues::extract_err_log_info() { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (!get_stmt()->is_insert_stmt()) { // do nothing } else if (OB_FAIL(ObLogDelUpd::generate_errlog_info( static_cast(*get_stmt()), get_err_log_define()))) { LOG_WARN("failed to generate errlog info", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < get_err_log_define().err_log_value_exprs_.count(); ++i) { ObRawExpr *&expr = get_err_log_define().err_log_value_exprs_.at(i); if (OB_ISNULL(expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("expr is null", K(ret)); } else if (expr->get_expr_type() == T_FUN_COLUMN_CONV) { expr = expr->get_param_expr(ObExprColumnConv::VALUE_EXPR); } } return ret; } int ObLogExprValues::get_plan_item_info(PlanText &plan_text, ObSqlPlanItem &plan_item) { int ret = OB_SUCCESS; if (OB_FAIL(ObLogicalOperator::get_plan_item_info(plan_text, plan_item))) { LOG_WARN("failed to get plan item info", K(ret)); } else { const ObIArray *values = &get_value_exprs(); BEGIN_BUF_PRINT; EXPLAIN_PRINT_INSERT_VALUES(values, output_exprs_.count(), type); END_BUF_PRINT(plan_item.special_predicates_, plan_item.special_predicates_len_); if (OB_SUCC(ret) && is_values_table_) { const ObString &name = get_table_name(); BUF_PRINT_OB_STR(name.ptr(), name.length(), plan_item.object_alias_, plan_item.object_alias_len_); } } return ret; } int ObLogExprValues::mark_probably_local_exprs() { int ret = OB_SUCCESS; FOREACH_CNT_X(e, value_exprs_, OB_SUCC(ret)) { CK(NULL != *e); OZ((*e)->add_flag(IS_PROBABLY_LOCAL)); } return ret; } int ObLogExprValues::allocate_dummy_output() { int ret = OB_SUCCESS; ObConstRawExpr *dummy_expr = NULL; if (OB_ISNULL(get_plan())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (is_oracle_mode() && ObRawExprUtils::build_const_number_expr( get_plan()->get_optimizer_context().get_expr_factory(), ObNumberType, number::ObNumber::get_positive_one(), dummy_expr)) { LOG_WARN("failed to build const expr", K(ret)); } else if (!is_oracle_mode() && ObRawExprUtils::build_const_int_expr( get_plan()->get_optimizer_context().get_expr_factory(), ObIntType, 1, dummy_expr)) { LOG_WARN("failed to build const expr", K(ret)); } else if (OB_ISNULL(dummy_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(dummy_expr->extract_info())) { LOG_WARN("failed to extract info for dummy expr", K(ret)); } else if (OB_FAIL(value_exprs_.push_back(dummy_expr))) { LOG_WARN("failed to push back expr", K(ret)); } else if (OB_FAIL(output_exprs_.push_back(dummy_expr))) { LOG_WARN("failed to push back expr", K(ret)); } else if (OB_FAIL(get_plan()->get_optimizer_context().get_all_exprs().append(dummy_expr))) { LOG_WARN("failed to append exprs", K(ret)); } else { /*do nothing*/ } return ret; } bool ObLogExprValues::is_ins_values_batch_opt() const { bool bret = false; if (get_stmt() != nullptr && get_stmt()->is_insert_stmt()) { bret = get_stmt()->get_query_ctx()->ins_values_batch_opt_; } return bret; } int ObLogExprValues::get_array_param_group_id(int64_t &group_id, bool &find) { int ret = OB_SUCCESS; const ObExecContext *exec_ctx = NULL; find = false; group_id = -1; if (OB_ISNULL(my_plan_) || OB_ISNULL(exec_ctx = my_plan_->get_optimizer_context().get_exec_ctx())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid argument", K(ret), KP(my_plan_), KP(exec_ctx)); } else if (exec_ctx->has_dynamic_values_table()) { for (int64_t i = 0; OB_SUCC(ret) && !find && i < value_exprs_.count(); i++) { if (OB_ISNULL(value_exprs_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("raw_expr is null", K(ret)); } else if (value_exprs_.at(i)->is_const_raw_expr()) { const ObConstRawExpr *const_expr = static_cast(value_exprs_.at(i)); if (const_expr->get_array_param_group_id() >= 0) { find = true; group_id = const_expr->get_array_param_group_id(); } } } } return ret; } bool ObLogExprValues::contain_array_binding_param() const { bool bret = false; if (get_stmt() != nullptr && get_stmt()->is_select_stmt()) { const ObSelectStmt *select_stmt = static_cast(get_stmt()); bret = select_stmt->contain_ab_param(); } return bret; } int ObLogExprValues::inner_replace_op_exprs(ObRawExprReplacer &replacer) { int ret = OB_SUCCESS; if (OB_FAIL(replace_exprs_action(replacer, value_exprs_))) { LOG_WARN("failed to replace exprs", K(ret)); } return ret; } int ObLogExprValues::compute_op_parallel_and_server_info() { int ret = common::OB_SUCCESS; if (get_num_of_child() == 0) { ret = set_parallel_and_server_info_for_match_all(); } else { ret = ObLogicalOperator::compute_op_parallel_and_server_info(); } return ret; } int ObLogExprValues::is_my_fixed_expr(const ObRawExpr *expr, bool &is_fixed) { is_fixed = ObOptimizerUtil::find_item(value_desc_, expr); return OB_SUCCESS; } } // namespace sql }// namespace oceanbase