/** * 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_for_update.h" #include "sql/optimizer/ob_log_plan.h" #include "sql/optimizer/ob_optimizer_util.h" #include "sql/optimizer/ob_opt_est_cost.h" #include "sql/optimizer/ob_log_table_scan.h" using namespace oceanbase; using namespace sql; using namespace oceanbase::common; ObLogForUpdate::ObLogForUpdate(ObLogPlan &plan) : ObLogicalOperator(plan), skip_locked_(false), is_multi_part_dml_(false), gi_charged_(false), wait_ts_(-1), lock_rownum_(NULL), index_dml_info_() {} const char* ObLogForUpdate::get_name() const { const char *ret = "NOT SET"; if (is_multi_part_dml()) { ret = "DISTRIBUTED FOR UPDATE"; } else { ret = "FOR UPDATE"; } return ret; } int ObLogForUpdate::get_plan_item_info(PlanText &plan_text, ObSqlPlanItem &plan_item) { int ret = OB_SUCCESS; const ObDMLStmt *stmt = NULL; if (OB_ISNULL(get_plan()) || OB_ISNULL(stmt = get_plan()->get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt is null", K(ret)); } else if (OB_FAIL(ObLogicalOperator::get_plan_item_info(plan_text, plan_item))) { LOG_WARN("failed to get plan item info", K(ret)); } else { BEGIN_BUF_PRINT; if (OB_FAIL(BUF_PRINTF("lock tables"))) { LOG_WARN("BUF_PRINTF fails", K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < index_dml_info_.count(); ++i) { TableItem *table = NULL; if (OB_ISNULL(index_dml_info_.at(i)) || OB_ISNULL(table = stmt->get_table_item_by_id(index_dml_info_.at(i)->table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("index dml info is null", K(ret), K(index_dml_info_.at(i)), K(table)); } else if (OB_FAIL(BUF_PRINTF("%c%.*s%c", i == 0 ? '(' : ' ', table->get_table_name().length(), table->get_table_name().ptr(), i == index_dml_info_.count() - 1 ? ')' : ','))) { LOG_WARN("failed to print lock table name", K(ret)); } } END_BUF_PRINT(plan_item.special_predicates_, plan_item.special_predicates_len_); } return ret; } int ObLogForUpdate::compute_sharding_info() { int ret = OB_SUCCESS; ObLogicalOperator *child = NULL; if (OB_ISNULL(child = get_child(ObLogicalOperator::first_child))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(child), K(ret)); } else if (OB_FAIL(ObLogicalOperator::compute_sharding_info())) { LOG_WARN("failed to compute sharding info", K(ret)); } else if (OB_ISNULL(get_sharding())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { is_partition_wise_ = !is_multi_part_dml_ && !child->is_exchange_allocated() && get_sharding()->is_distributed() && NULL != get_sharding()->get_phy_table_location_info(); } return ret; } int ObLogForUpdate::allocate_granule_pre(AllocGIContext &ctx) { return pw_allocate_granule_pre(ctx); } int ObLogForUpdate::allocate_granule_post(AllocGIContext &ctx) { int ret = OB_SUCCESS; bool is_partition_wise_state = ctx.is_in_partition_wise_state(); if (OB_ISNULL(get_plan())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else if (OB_FAIL(pw_allocate_granule_post(ctx))) { // 分配完GI以后,会对ctx的状态进行清理 LOG_WARN("failed to allocate pw gi post", K(ret)); } else { if (is_partition_wise_state && ctx.is_op_set_pw(this)) { ObSEArray tsc_ops; if (OB_FAIL(find_all_tsc(tsc_ops, this))) { LOG_WARN("failed to find all tsc", K(ret)); } else if (tsc_ops.count() < 1){ // do nothing set_gi_above(true); } else { // tsc op与当前dml算子都需要set gi above ARRAY_FOREACH(tsc_ops, idx) { ObLogTableScan *tsc_op = static_cast(tsc_ops.at(idx)); tsc_op->set_gi_above(true); } set_gi_above(true); } } else { LOG_TRACE("not allocate dml gi for px", K(ret), K(ctx), K(ctx.is_op_set_pw(this))); } } return ret; } int ObLogForUpdate::get_op_exprs(ObIArray &all_exprs) { int ret = OB_SUCCESS; if (is_multi_part_dml() && OB_FAIL(generate_multi_part_partition_id_expr())) { LOG_WARN("failed to generate update expr", K(ret)); } else if (OB_FAIL(get_for_update_dependant_exprs(all_exprs))) { LOG_WARN("failed to get for update dependant exprs", K(ret)); } else if (NULL != lock_rownum_ && OB_FAIL(all_exprs.push_back(lock_rownum_))) { LOG_WARN("failed to push back exprs", K(ret)); } else if (OB_FAIL(ObLogicalOperator::get_op_exprs(all_exprs))) { LOG_WARN("failed to get op exprs", K(ret)); } else { /*do nothing*/ } return ret; } int ObLogForUpdate::generate_multi_part_partition_id_expr() { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt()) || OB_ISNULL(get_plan())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(get_stmt()), K(ret)); } for (int64_t i = 0; OB_SUCC(ret) && i < index_dml_info_.count(); ++i) { const TableItem *table_item = NULL; ObRawExpr *part_expr = NULL; if (OB_ISNULL(index_dml_info_.at(i)) || OB_ISNULL(table_item = get_stmt()->get_table_item_by_id( index_dml_info_.at(i)->table_id_))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret), K(index_dml_info_.at(i)), K(table_item)); } else if (OB_FAIL(get_plan()->gen_calc_part_id_expr(table_item->table_id_, table_item->ref_id_, CALC_PARTITION_TABLET_ID, part_expr))) { LOG_WARN("failed to gen calc part id expr", K(ret)); } else if (OB_ISNULL(part_expr)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { index_dml_info_.at(i)->old_part_id_expr_ = part_expr; } } return ret; } int ObLogForUpdate::get_for_update_dependant_exprs(ObIArray &dep_exprs) { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { const TableItem *table_item = NULL; for (int64_t i = 0; OB_SUCC(ret) && i < index_dml_info_.count(); i++) { if (OB_ISNULL(index_dml_info_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("index dml info is null", K(ret)); } else if (OB_FAIL(append(dep_exprs, index_dml_info_.at(i)->column_exprs_))) { LOG_WARN("failed to append rowkey expr", K(ret)); } else if (!is_multi_part_dml()) { /*do nothing*/ } else if (NULL != index_dml_info_.at(i)->old_part_id_expr_ && OB_FAIL(dep_exprs.push_back(index_dml_info_.at(i)->old_part_id_expr_))) { LOG_WARN("failed to push back old partition id expr", K(ret)); } else { /*do nothing*/ } } // mark expr reference for (int64_t i = 0; OB_SUCC(ret) && i < dep_exprs.count(); i++) { if (OB_ISNULL(dep_exprs.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(ret)); } else { dep_exprs.at(i)->set_explicited_reference(); } } } return ret; } int ObLogForUpdate::est_cost() { int ret = OB_SUCCESS; ObLogicalOperator *first_child = NULL; if (OB_ISNULL(get_plan()) || OB_ISNULL(first_child = get_child(ObLogicalOperator::first_child))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("first child is null", K(ret)); } else { // todo: refine for update cost ObOptimizerContext &opt_ctx = get_plan()->get_optimizer_context(); set_op_cost(ObOptEstCost::cost_get_rows(first_child->get_card(), opt_ctx)); set_cost(first_child->get_cost() + op_cost_); set_card(first_child->get_card()); } return ret; } int ObLogForUpdate::compute_op_ordering() { int ret = OB_SUCCESS; ObLogicalOperator *child = NULL; if (OB_ISNULL(child = get_child(first_child))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("Child is null", K(ret)); } else if (OB_FAIL(set_op_ordering(child->get_op_ordering()))) { LOG_WARN("Failed to set op ordering", K(ret)); } return ret; } int ObLogForUpdate::get_table_columns(const uint64_t table_id, ObIArray &table_cols) const { int ret = OB_SUCCESS; if (OB_ISNULL(get_stmt())) { ret = OB_ERR_UNEXPECTED; LOG_WARN("stmt is null", K(ret)); } else if (OB_FAIL(get_stmt()->get_column_exprs(table_id, table_cols))) { LOG_WARN("failed to get column exprs", K(ret)); } for (int64_t i = table_cols.count() - 1; OB_SUCC(ret) && i >= 0; --i) { if (OB_ISNULL(table_cols.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("table column is null", K(ret), K(table_cols.at(i))); } else if (!IS_SHADOW_COLUMN(table_cols.at(i)->get_column_id())) { // do nothing } else if (OB_FAIL(table_cols.remove(i))) { LOG_WARN("failed to remove column expr", K(ret)); } } return ret; } int ObLogForUpdate::get_rowkey_exprs(const uint64_t table_id, ObIArray &rowkey) const { int ret = OB_SUCCESS; for (int64_t i = 0; OB_SUCC(ret) && i < index_dml_info_.count(); ++i) { if (OB_ISNULL(index_dml_info_.at(i))) { ret = OB_ERR_UNEXPECTED; LOG_WARN("index dml info is null", K(ret)); } else if (index_dml_info_.at(i)->table_id_ != table_id) { // do nothing } else if (OB_FAIL(append(rowkey, index_dml_info_.at(i)->column_exprs_))) { LOG_WARN("failed to append rowkey expr", K(ret)); } else { break; } } return ret; } int ObLogForUpdate::is_rowkey_nullable(const uint64_t table_id, bool &is_nullable) const { int ret = OB_SUCCESS; is_nullable = false; if (OB_FAIL(ObOptimizerUtil::is_table_on_null_side(get_stmt(), table_id, is_nullable))) { LOG_WARN("failed to check is table on null side", K(ret)); } return ret; } bool ObLogForUpdate::is_multi_table_skip_locked() { bool is_multi_table_skip_locked = false; if (index_dml_info_.count() > 1 && is_skip_locked()) { is_multi_table_skip_locked = true; } return is_multi_table_skip_locked; } int ObLogForUpdate::inner_replace_op_exprs(ObRawExprReplacer &replacer) { int ret = OB_SUCCESS; for (int64_t i = 0; OB_SUCC(ret) && i < index_dml_info_.count(); ++i) { IndexDMLInfo *dml_info = index_dml_info_.at(i); if (OB_ISNULL(dml_info)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("dml info is null", K(ret)); } else if (OB_FAIL(replace_exprs_action(replacer, dml_info->ck_cst_exprs_))) { LOG_WARN("failed to replace exprs", K(ret)); } else if (NULL != dml_info->new_part_id_expr_ && OB_FAIL(replace_expr_action(replacer, dml_info->new_part_id_expr_))) { LOG_WARN("failed to replace new parititon id expr", K(ret)); } else if (NULL != dml_info->old_part_id_expr_ && OB_FAIL(replace_expr_action(replacer, dml_info->old_part_id_expr_))) { LOG_WARN("failed to replace old parititon id expr", K(ret)); } else if (NULL != dml_info->old_rowid_expr_ && OB_FAIL(replace_expr_action(replacer, dml_info->old_rowid_expr_))) { LOG_WARN("failed to replace old rowid expr", K(ret)); } else if (NULL != dml_info->new_rowid_expr_ && OB_FAIL(replace_expr_action(replacer, dml_info->new_rowid_expr_))) { LOG_WARN("failed to replace new rowid expr", K(ret)); } else if (OB_FAIL(replace_exprs_action(replacer, dml_info->column_convert_exprs_))) { LOG_WARN("failed to replace exprs", K(ret)); } else if (OB_FAIL(replace_exprs_action(replacer, dml_info->column_old_values_exprs_))) { LOG_WARN("failed to replace column old values exprs ", K(ret)); } for (int64_t j = 0; OB_SUCC(ret) && j < dml_info->assignments_.count(); ++j) { if (OB_FAIL(replace_expr_action(replacer, dml_info->assignments_.at(j).expr_))) { LOG_WARN("failed to replace expr", K(ret)); } } } return ret; }