init push

This commit is contained in:
oceanbase-admin
2021-05-31 22:56:52 +08:00
commit cea7de1475
7020 changed files with 5689869 additions and 0 deletions

View File

@ -0,0 +1,786 @@
/**
* 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_RESV
#include "sql/resolver/dml/ob_update_resolver.h"
#include "sql/session/ob_sql_session_info.h"
#include "sql/resolver/dml/ob_select_stmt.h"
#include "sql/resolver/dml/ob_select_resolver.h"
#include "sql/resolver/ob_resolver_utils.h"
namespace oceanbase {
using namespace common;
using namespace share;
using namespace share::schema;
namespace sql {
ObUpdateResolver::ObUpdateResolver(ObResolverParams& param)
: ObDMLResolver(param), has_add_all_rowkey_(false), has_add_all_columns_(false), update_column_ids_()
{
param.contain_dml_ = true;
}
ObUpdateResolver::~ObUpdateResolver()
{}
int ObUpdateResolver::resolve(const ParseNode& parse_tree)
{
int ret = OB_SUCCESS;
ObUpdateStmt* update_stmt = NULL;
if (T_UPDATE != parse_tree.type_ || 3 > parse_tree.num_child_ || OB_ISNULL(parse_tree.children_) ||
OB_ISNULL(session_info_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid parse tree for update",
K(parse_tree.type_),
K(parse_tree.num_child_),
K(parse_tree.children_),
K((session_info_)));
} else if (OB_ISNULL(update_stmt = create_stmt<ObUpdateStmt>())) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("create update stmt failed");
} else {
stmt_ = update_stmt;
update_stmt->set_ignore(false);
if (NULL != parse_tree.children_[IGNORE]) {
update_stmt->set_ignore(true);
session_info_->set_ignore_stmt(true);
}
}
// 1. resolve table items
if (OB_SUCC(ret)) {
ParseNode* table_node = parse_tree.children_[TABLE];
if (OB_FAIL(resolve_table_list(*table_node))) {
LOG_WARN("resolve table failed", K(ret));
}
}
if (OB_SUCC(ret)) {
current_scope_ = T_UPDATE_SCOPE;
// resolve assignments
ParseNode* assign_list = parse_tree.children_[UPDATE_LIST];
if (OB_ISNULL(assign_list) || OB_UNLIKELY(T_ASSIGN_LIST != assign_list->type_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid assign list node", K(assign_list));
} else if (OB_FAIL(resolve_assignments(*assign_list, update_stmt->get_tables_assignments(), current_scope_))) {
LOG_WARN("fail to resolve assignment", K(ret));
}
}
if (OB_SUCC(ret)) {
FOREACH_CNT_X(it, update_stmt->get_table_items(), OB_SUCC(ret))
{
if (NULL != *it && (*it)->is_generated_table()) {
if (NULL != (*it)->view_base_item_ && OB_FAIL(add_all_column_to_updatable_view(*update_stmt, *(*it)))) {
LOG_WARN("add all column for updatable view failed", K(ret));
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(view_pullup_generated_column_exprs())) {
LOG_WARN("view pullup generated column exprs failed", K(ret));
}
}
}
if (OB_SUCC(ret)) {
// Resolve cascaded updated columns
if (OB_FAIL(resolve_additional_assignments(update_stmt->get_tables_assignments(), T_UPDATE_SCOPE))) {
LOG_WARN("fail to resolve_additional_assignments", K(ret));
}
}
if (OB_SUCC(ret) && update_stmt->get_table_size() > 1) {
// check multi table update conflict
if (OB_FAIL(check_multi_update_key_conflict())) {
LOG_WARN("check multi update key conflict failed", K(ret));
}
}
// add column for table scan, keep rowkey column in the head
if (OB_SUCC(ret)) {
if (OB_FAIL(add_related_columns_to_stmt())) {
LOG_WARN("fail to add column to stmt", K(ret));
}
}
// 3. resolve other clauses
if (OB_SUCC(ret)) {
if (share::is_oracle_mode()) {
ObTablesAssignments& tas = update_stmt->get_tables_assignments();
if (1 != tas.count()) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("oracle mode don't support multi table update grammar", K(ret));
} else {
ObTableAssignment& ta = tas.at(0);
if (OB_INVALID_ID == ta.table_id_) {
ret = OB_ERR_UNEXPECTED;
LOG_ERROR("invalid table assignment", K(ta.table_id_));
} else if (OB_FAIL(resolve_check_constraints(update_stmt->get_table_item(0)))) {
LOG_WARN("resolve check constraint failed", K(ret));
} else if (session_info_->use_static_typing_engine()) {
//
// TODO : support trigger in new engine.
// Replace constraint with assigned values is removed in 1d840f573269 commit to
// support trigger. Because the trigger may alter the assigned value, constraint
// check must base on the altered values. A magic hack is done in code generator
// to make the constraint find the new values.
//
// We can not do this magic in new engine because we can not evaluate expression
// with different input rows. We must make sure the constraint base on the assigned
// values here. Trigger is not supported in new engine right now, because PL is not
// supported. We believe we can add a trigger generated expr here and make
// constraint base on that expressions to solve the problem in future.
for (uint64_t i = 0; OB_SUCC(ret) && i < update_stmt->get_check_constraint_exprs_size(); ++i) {
if (OB_FAIL(ObTableAssignment::expand_expr(
tas.at(0).assignments_, update_stmt->get_check_constraint_exprs().at(i)))) {
LOG_WARN("expand generated column expr failed", K(ret));
}
}
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(resolve_where_clause(parse_tree.children_[WHERE]))) {
LOG_WARN("resolve where clause failed", K(ret));
} else if (OB_FAIL(resolve_order_clause(parse_tree.children_[ORDER_BY]))) {
LOG_WARN("resolve order clause failed", K(ret));
} else if (OB_FAIL(resolve_limit_clause(parse_tree.children_[LIMIT]))) {
LOG_WARN("resolve limit clause failed", K(ret));
} else if (OB_FAIL(resolve_hints(parse_tree.children_[HINT]))) {
LOG_WARN("resolve hints failed", K(ret));
} else if (OB_FAIL(resolve_returning(parse_tree.children_[RETURNING]))) {
LOG_WARN("resolve returning failed", K(ret));
} else {
if (session_info_->use_static_typing_engine() && !update_stmt->get_returning_exprs().empty()) {
// The old engine pass the updated row to the returning expression to
// get the updated value. We can not do this in static engine, we need to
// replace the column with the assigned value.
ObTableAssignment& ta = update_stmt->get_tables_assignments().at(0);
FOREACH_CNT_X(e, update_stmt->get_returning_exprs(), OB_SUCC(ret))
{
OZ(ObTableAssignment::expand_expr(ta.assignments_, *e));
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(update_stmt->formalize_stmt(session_info_))) {
LOG_WARN("pull update stmt all expr relation ids failed", K(ret));
}
}
}
if (OB_SUCC(ret) && is_mysql_mode() && !update_stmt->is_ignore()) {
bool is_multi_update = false;
if (OB_FAIL(is_multi_table_update(update_stmt, is_multi_update))) {
LOG_WARN("failed to check is multi table udpate", K(ret));
} else if (is_multi_update && update_stmt->has_order_by()) {
// Incorrect usage of UPDATE and ORDER BY
ret = OB_ERR_UPDATE_ORDER_BY;
} else if (is_multi_update && update_stmt->has_limit()) {
// Incorrect usage of UPDATE and LIMIT
ret = OB_ERR_UPDATE_LIMIT;
}
}
for (int i = 0; OB_SUCC(ret) && i < update_stmt->get_tables_assignments().count(); i++) {
const ObTableAssignment& tas = update_stmt->get_tables_assignments().at(i);
if (OB_FAIL(try_add_rowid_column_to_stmt(tas))) {
LOG_WARN("failed to try adding rowid column", K(ret));
}
} // for end
}
if (OB_SUCC(ret)) {
// Resolve which global indexes need to be updated in cascade
int64_t N = update_stmt->get_tables_assignments().count();
ObSEArray<uint64_t, 4> global_indexs;
for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) {
global_indexs.reset();
const ObTableAssignment& ta = update_stmt->get_tables_assignments().at(i);
if (OB_FAIL(resolve_cascade_updated_global_index(ta, global_indexs))) {
LOG_WARN("resolve cascade updated global index failed", K(ret));
} else if (OB_FAIL(resolve_multi_table_dml_info(ta, global_indexs))) {
LOG_WARN("resolve global update index info failed", K(ret));
}
}
}
if (OB_SUCC(ret)) {
TableItem* update_table = NULL;
if (OB_FAIL(view_pullup_part_exprs())) {
LOG_WARN("view pull up part exprs failed", K(ret));
} else if (OB_FAIL(check_view_updatable())) {
LOG_TRACE("view not updatable", K(ret));
} else if (update_stmt->get_from_item_size() == 1 &&
NULL != (update_table = update_stmt->get_table_item(update_stmt->get_from_item(0))) &&
update_table->is_basic_table()) {
// do nothing
} else {
update_stmt->set_dml_source_from_join(true);
}
}
if (OB_SUCC(ret)) {
// Distribute the centralized assignment information to each index, and do assignment updates for each index
if (OB_FAIL(update_stmt->refill_index_assignment_info())) {
LOG_WARN("init index assignment info failed", K(ret));
}
}
if (OB_SUCC(ret)) {
ObIArray<TableColumns>& all_table_columns = update_stmt->get_all_table_columns();
for (int64_t i = 0; OB_SUCC(ret) && i < all_table_columns.count(); ++i) {
ObIArray<IndexDMLInfo>& index_infos = all_table_columns.at(i).index_dml_infos_;
for (int64_t j = 0; OB_SUCC(ret) && j < index_infos.count(); ++j) {
IndexDMLInfo& index_info = index_infos.at(j);
if (OB_FAIL(try_add_remove_const_expr(index_info))) {
LOG_WARN("add value expr failed", K(ret));
} else if (OB_FAIL(add_rowkey_ids(index_info.index_tid_, index_info.primary_key_ids_))) {
LOG_WARN("fail init index key ids", K(ret));
} else if (OB_FAIL(get_part_key_ids(index_info.index_tid_, index_info.part_key_ids_))) {
LOG_WARN("fail init part key ids", K(ret));
}
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(update_stmt->check_dml_need_filter_null())) {
LOG_WARN("failed to check dml need filter null", K(ret));
} else if (share::is_mysql_mode() && OB_FAIL(check_safe_update_mode(update_stmt))) {
LOG_WARN("failed to check fullfill safe update mode", K(ret));
}
}
if (OB_SUCC(ret)) {
if (update_stmt->is_ignore() && update_stmt->has_global_index()) {
ret = OB_NOT_SUPPORTED;
LOG_USER_ERROR(OB_NOT_SUPPORTED, "ignore with global index");
} else { /*do nothing*/
}
}
return ret;
}
int ObUpdateResolver::try_add_remove_const_expr(IndexDMLInfo& index_info)
{
int ret = OB_SUCCESS;
CK(OB_NOT_NULL(session_info_) && OB_NOT_NULL(schema_checker_));
if (OB_SUCC(ret) && session_info_->use_static_typing_engine()) {
const ObTableSchema* table_schema = NULL;
OZ(schema_checker_->get_table_schema(index_info.index_tid_, table_schema));
CK(OB_NOT_NULL(table_schema));
if (OB_SUCC(ret) && table_schema->is_user_table()) {
const ObIArray<ObForeignKeyInfo>& fk_infos = table_schema->get_foreign_key_infos();
for (int64_t i = 0; OB_SUCC(ret) && i < index_info.assignments_.count(); ++i) {
ObAssignment& assign = index_info.assignments_.at(i);
CK(OB_NOT_NULL(assign.expr_) && OB_NOT_NULL(assign.column_expr_));
if (OB_FAIL(ret)) {
} else if (assign.expr_->has_const_or_const_expr_flag() &&
is_parent_col_self_ref_fk(assign.column_expr_->get_column_id(), fk_infos)) {
ObRawExpr* new_expr = NULL;
CK(OB_NOT_NULL(params_.expr_factory_));
OZ(ObRawExprUtils::build_remove_const_expr(*params_.expr_factory_, *session_info_, assign.expr_, new_expr));
CK(OB_NOT_NULL(new_expr));
OX(assign.expr_ = new_expr);
}
}
}
}
return ret;
}
// check if %parent_col_id is parent column of a self reference foreign key
bool ObUpdateResolver::is_parent_col_self_ref_fk(uint64_t parent_col_id, const ObIArray<ObForeignKeyInfo>& fk_infos)
{
bool bret = false;
for (int64_t i = 0; i < fk_infos.count(); i++) {
const ObForeignKeyInfo& fk_info = fk_infos.at(i);
if (fk_info.parent_table_id_ == fk_info.child_table_id_) {
if (has_exist_in_array(fk_info.parent_column_ids_, parent_col_id, NULL)) {
bret = true;
break;
}
}
}
return bret;
}
int ObUpdateResolver::check_safe_update_mode(ObUpdateStmt* update_stmt)
{
int ret = OB_SUCCESS;
bool is_sql_safe_updates = false;
if (OB_ISNULL(params_.session_info_) || OB_ISNULL(update_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected", K(ret), K(params_.session_info_), K(update_stmt));
} else if (OB_FAIL(params_.session_info_->get_sql_safe_updates(is_sql_safe_updates))) {
LOG_WARN("failed to get is safe update mode", K(ret));
} else if (is_sql_safe_updates) {
if (!update_stmt->has_limit() && update_stmt->get_condition_exprs().empty()) {
ret = OB_ERR_SAFE_UPDATE_MODE_NEED_WHERE_OR_LIMIT;
LOG_WARN("using safe update mode need WHERE or LIMIT", K(ret));
}
} else { /*do nothing*/
}
return ret;
}
int ObUpdateResolver::check_multi_update_key_conflict()
{
int ret = OB_SUCCESS;
const ObUpdateStmt* update_stmt = get_update_stmt();
if (OB_ISNULL(update_stmt)) {
ret = OB_NOT_INIT;
LOG_WARN("update stmt is null");
} else {
const ObTablesAssignments& tas = update_stmt->get_tables_assignments();
for (int64_t i = 0; OB_SUCC(ret) && i < tas.count() - 1; ++i) {
for (int64_t j = i + 1; OB_SUCC(ret) && j < tas.count(); ++j) {
const ObTableAssignment& ta1 = tas.at(i);
const ObTableAssignment& ta2 = tas.at(j);
const TableItem* table1 = update_stmt->get_table_item_by_id(ta1.table_id_);
const TableItem* table2 = update_stmt->get_table_item_by_id(ta2.table_id_);
if (OB_ISNULL(table1) || OB_ISNULL(table2)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("tables are null", K(table1), K(table2));
} else if (ta1.table_id_ != ta2.table_id_ && table1->ref_id_ == table2->ref_id_) {
uint64_t base_table_id =
table1->is_generated_table() ? table1->get_base_table_item().ref_id_ : table1->ref_id_;
for (int64_t k = 0; OB_SUCC(ret) && k < ta1.assignments_.count(); ++k) {
bool is_key = false;
uint64_t column_id = ta1.assignments_.at(k).column_expr_->get_column_id();
if (OB_FAIL(schema_checker_->column_is_key(base_table_id, column_id, is_key))) {
LOG_WARN("check column is key failed", K(ret), K(base_table_id), K(column_id));
} else if (is_key) {
// set error code
ret = OB_ERR_MULTI_UPDATE_KEY_CONFLICT;
const ObString concat_name1 = concat_table_name(table1->database_name_, table1->get_table_name());
const ObString concat_name2 = concat_table_name(table2->database_name_, table2->get_table_name());
LOG_USER_ERROR(OB_ERR_MULTI_UPDATE_KEY_CONFLICT,
concat_name1.length(),
concat_name1.ptr(),
concat_name2.length(),
concat_name2.ptr());
}
}
for (int64_t k = 0; OB_SUCC(ret) && k < ta2.assignments_.count(); ++k) {
bool is_key = false;
uint64_t column_id = ta2.assignments_.at(k).column_expr_->get_column_id();
if (OB_FAIL(schema_checker_->column_is_key(base_table_id, column_id, is_key))) {
LOG_WARN("check column is key failed", K(ret), K(base_table_id), K(column_id));
} else if (is_key) {
// set error code
ret = OB_ERR_MULTI_UPDATE_KEY_CONFLICT;
const ObString concat_name1 = concat_table_name(table1->database_name_, table1->get_table_name());
const ObString concat_name2 = concat_table_name(table2->database_name_, table2->get_table_name());
LOG_USER_ERROR(OB_ERR_MULTI_UPDATE_KEY_CONFLICT,
concat_name1.length(),
concat_name1.ptr(),
concat_name2.length(),
concat_name2.ptr());
}
}
}
}
}
}
return ret;
}
int ObUpdateResolver::resolve_table_list(const ParseNode& parse_tree)
{
int ret = OB_SUCCESS;
ObUpdateStmt* update_stmt = get_update_stmt();
TableItem* table_item = NULL;
ObSelectStmt* ref_stmt = NULL;
if (OB_UNLIKELY(T_TABLE_REFERENCES != parse_tree.type_) || OB_UNLIKELY(parse_tree.num_child_ < 1)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(parse_tree.type_), K(parse_tree.num_child_));
} else if (OB_ISNULL(update_stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid update stmt", K(update_stmt));
}
for (int64_t i = 0; OB_SUCC(ret) && i < parse_tree.num_child_; ++i) {
const ParseNode* table_node = parse_tree.children_[i];
if (OB_ISNULL(table_node)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("table node is null");
} else if (OB_FAIL(ObDMLResolver::resolve_table(*table_node, table_item))) {
LOG_WARN("failed to resolve table", K(ret));
// This is to be compatible with oracle
} else if (is_oracle_mode() && table_node->num_child_ == 2) {
if (OB_ISNULL(table_item) || !table_item->is_generated_table() || OB_ISNULL(ref_stmt = table_item->ref_query_)) {
int ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected error", K(table_item), K(ref_stmt), K(ret));
} else if (OB_UNLIKELY(ref_stmt->get_from_items().count() != 1)) {
ret = OB_ERR_ILLEGAL_VIEW_UPDATE;
LOG_WARN("not updatable", K(ret));
} else { /*do nothing*/
}
} else { /*do nothing*/
}
if (OB_SUCC(ret)) {
if (OB_FAIL(column_namespace_checker_.add_reference_table(table_item))) {
LOG_WARN("add reference table to namespace checker failed", K(ret));
} else if (OB_FAIL(update_stmt->add_from_item(table_item->table_id_, table_item->is_joined_table()))) {
LOG_WARN("failed to add from item", K(ret));
} else {
/*
In order to share the same logic with 'select' to generate access path costly, we
add the table in the udpate stmt in the from_item list as well.
*/
LOG_DEBUG("succ to add from item", KPC(table_item));
}
}
}
return ret;
}
int ObUpdateResolver::add_related_columns_to_stmt()
{
int ret = OB_SUCCESS;
ObUpdateStmt* update_stmt = get_update_stmt();
if (OB_ISNULL(update_stmt) || OB_ISNULL(params_.session_info_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid update stmt", K(update_stmt), K(params_.session_info_));
} else {
ObTablesAssignments& tas = update_stmt->get_tables_assignments();
int64_t N = tas.count();
for (int64_t i = 0; OB_SUCC(ret) && i < N; ++i) {
ObTableAssignment& ta = tas.at(i);
const TableItem* table = update_stmt->get_table_item_by_id(ta.table_id_);
if (OB_ISNULL(table)) {
ret = OB_ERR_UNEXPECTED;
LOG_ERROR("invalid table assignment", K(table));
} else {
uint64_t table_id = ta.table_id_;
IndexDMLInfo* primary_dml_info = NULL;
const ObTableSchema* table_schema = NULL;
const uint64_t base_table_id = table->get_base_table_item().ref_id_;
if (OB_ISNULL(primary_dml_info = update_stmt->get_or_add_table_columns(table_id))) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("failed to get table columns", K(table_id));
} else if (OB_FAIL(add_all_rowkey_columns_to_stmt(*table, primary_dml_info->column_exprs_))) {
LOG_WARN("add all rowkey columns to stmt failed", K(ret));
} else if (OB_FAIL(schema_checker_->get_table_schema(base_table_id, table_schema))) {
LOG_WARN("fail to get table schema", K(ret), K(base_table_id));
} else if (OB_ISNULL(table_schema)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid table schema", K(table_schema));
} else {
primary_dml_info->table_id_ = table->table_id_;
primary_dml_info->loc_table_id_ = table->get_base_table_item().table_id_;
primary_dml_info->index_tid_ = base_table_id;
primary_dml_info->rowkey_cnt_ = table_schema->get_rowkey_column_num();
primary_dml_info->part_cnt_ = table_schema->get_partition_cnt();
primary_dml_info->all_part_num_ = table_schema->get_all_part_num();
primary_dml_info->index_name_ = table_schema->get_table_name_str();
int64_t binlog_row_image = ObBinlogRowImage::FULL;
if (OB_FAIL(params_.session_info_->get_binlog_row_image(binlog_row_image))) {
LOG_WARN("fail to get binlog row image", K(ret));
} else if (need_all_columns(*table_schema, binlog_row_image)) {
if (OB_FAIL(add_all_columns_to_stmt(*table, primary_dml_info->column_exprs_))) {
LOG_WARN("fail to add all column to stmt", K(ret), K(table_id));
}
} else {
int64_t M = ta.assignments_.count();
for (int64_t j = 0; OB_SUCC(ret) && j < M; ++j) {
ObAssignment& assign = ta.assignments_.at(j);
if (OB_FAIL(add_var_to_array_no_dup(primary_dml_info->column_exprs_, assign.column_expr_))) {
LOG_WARN("failed to add all table columns", K(ret));
} else if (OB_FAIL(add_index_related_columns_to_stmt(
*table, assign.column_expr_->get_column_id(), primary_dml_info->column_exprs_))) {
LOG_WARN("failed to add index columns", K(ret));
}
}
}
}
}
} // end for
}
return ret;
}
int ObUpdateResolver::resolve_cascade_updated_global_index(
const ObTableAssignment& ta, ObIArray<uint64_t>& cascade_global_index)
{
int ret = OB_SUCCESS;
const TableItem* table = NULL;
if (OB_ISNULL(get_stmt()) || OB_ISNULL(schema_checker_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("params are invalid", K(ret), K(get_stmt()), K_(schema_checker));
} else if (OB_ISNULL(table = get_stmt()->get_table_item_by_id(ta.table_id_))) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("table item is invalid", K(ret), K(ta.table_id_));
} else {
uint64_t ref_table_id = table->get_base_table_item().ref_id_;
uint64_t index_tid[OB_MAX_INDEX_PER_TABLE];
int64_t index_cnt = OB_MAX_INDEX_PER_TABLE;
if (OB_FAIL(schema_checker_->get_can_write_index_array(
ref_table_id, index_tid, index_cnt, true /*only fetch global index*/))) {
LOG_WARN("get can write index array failed", K(ret));
}
for (int64_t i = 0; OB_SUCC(ret) && i < index_cnt; ++i) {
bool is_exist = false;
for (int64_t j = 0; OB_SUCC(ret) && !is_exist && j < ta.assignments_.count(); ++j) {
uint64_t column_id = OB_INVALID_ID;
ColumnItem* col_item = NULL;
const ObAssignment& assign = ta.assignments_.at(j);
if (OB_ISNULL(assign.column_expr_)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("assign column expr is null");
} else if (OB_ISNULL(col_item = get_update_stmt()->get_column_item_by_id(
table->table_id_, assign.column_expr_->get_column_id()))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("column item not found", K(ret), K(table->table_id_), K(assign.column_expr_->get_column_id()));
} else {
column_id = table->is_generated_table() ? col_item->base_cid_ : col_item->column_id_;
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(schema_checker_->check_column_exists(index_tid[i], column_id, is_exist))) {
LOG_WARN("check column exists failed", K(ret));
} else if (is_exist) {
if (OB_FAIL(cascade_global_index.push_back(index_tid[i]))) {
LOG_WARN("add index tid to cascade global index failed", K(ret));
}
}
}
}
}
return ret;
}
int ObUpdateResolver::resolve_multi_table_dml_info(const ObTableAssignment& ta, ObIArray<uint64_t>& global_indexs)
{
int ret = OB_SUCCESS;
int64_t binlog_row_image = 0;
ObUpdateStmt* update_stmt = get_update_stmt();
if (OB_ISNULL(update_stmt) || OB_ISNULL(params_.session_info_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update stmt is null", K(params_.session_info_), K(update_stmt));
} else if (OB_FAIL(params_.session_info_->get_binlog_row_image(binlog_row_image))) {
LOG_WARN("fail to get binlog row image", K(ret));
} else {
update_stmt->set_has_global_index(!global_indexs.empty());
IndexDMLInfo index_dml_info;
for (int64_t i = 0; OB_SUCC(ret) && i < global_indexs.count(); ++i) {
index_dml_info.reset();
const ObTableSchema* index_schema = NULL;
const ObAssignments& assignments = ta.assignments_;
const TableItem* table = NULL;
uint64_t index_tid = global_indexs.at(i);
if (OB_FAIL(schema_checker_->get_table_schema(index_tid, index_schema))) {
LOG_WARN("get index table schema failed", K(ret), K(index_tid));
} else if (OB_ISNULL(index_schema)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("index schema is null");
} else if (OB_FAIL(resolve_index_related_column_exprs(
ta.table_id_, *index_schema, assignments, index_dml_info.column_exprs_))) {
LOG_WARN("resolve index related column exprs failed", K(ret));
} else if (OB_ISNULL(table = update_stmt->get_table_item_by_id(ta.table_id_))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("failed to get table item", K(ret), K(ta.table_id_));
} else {
index_dml_info.table_id_ = ta.table_id_;
index_dml_info.loc_table_id_ = table->get_base_table_item().table_id_;
index_dml_info.index_tid_ = index_tid;
index_dml_info.part_cnt_ = index_schema->get_partition_cnt();
index_dml_info.all_part_num_ = index_schema->get_all_part_num();
index_dml_info.rowkey_cnt_ = index_schema->get_rowkey_column_num();
if (OB_FAIL(index_schema->get_index_name(index_dml_info.index_name_))) {
LOG_WARN("get index name from index schema failed", K(ret));
} else if (OB_FAIL(update_stmt->add_multi_table_dml_info(index_dml_info))) {
LOG_WARN("add index dml info to update stmt failed", K(ret));
}
}
if (OB_SUCC(ret) && !with_clause_without_record_) {
ObSchemaObjVersion table_version;
table_version.object_id_ = index_schema->get_table_id();
table_version.object_type_ = DEPENDENCY_TABLE;
table_version.version_ = index_schema->get_schema_version();
if (OB_FAIL(update_stmt->add_global_dependency_table(table_version))) {
LOG_WARN("add global dependency table failed", K(ret));
}
}
}
}
return ret;
}
int ObUpdateResolver::check_view_updatable()
{
int ret = OB_SUCCESS;
ObUpdateStmt* update_stmt = get_update_stmt();
if (NULL == update_stmt) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("update stmt is NULL", K(ret));
}
// uv_check_basic already checked
if (OB_SUCC(ret) && is_mysql_mode()) {
FOREACH_CNT_X(assign, update_stmt->get_tables_assignments(), OB_SUCC(ret))
{
const TableItem* table = update_stmt->get_table_item_by_id(assign->table_id_);
if (OB_ISNULL(table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("table item is null", K(ret), K(assign->table_id_));
} else if (!table->is_generated_table()) {
continue;
}
// check select item subquery
if (OB_SUCC(ret)) {
bool has_subquery = false;
bool has_dependent_subquery = false;
bool ref_update_table = false;
if (OB_FAIL(ObResolverUtils::uv_check_select_item_subquery(
*table, has_subquery, has_dependent_subquery, ref_update_table))) {
LOG_WARN("updatable view check select item failed", K(ret));
} else {
LOG_DEBUG("update view check", K(has_subquery), K(has_dependent_subquery), K(ref_update_table));
ret = has_dependent_subquery ? OB_ERR_NON_UPDATABLE_TABLE : OB_SUCCESS;
}
}
if (OB_SUCC(ret)) {
bool ref_update_table = false;
if (OB_FAIL(ObResolverUtils::uv_check_where_subquery(*table, ref_update_table))) {
LOG_WARN("update view check where condition failed", K(ret));
} else {
LOG_DEBUG("update view check", K(ref_update_table));
ret = ref_update_table ? OB_ERR_NON_UPDATABLE_TABLE : OB_SUCCESS;
}
}
if (OB_SUCC(ret)) {
bool has_non_inner_join = false;
if (OB_FAIL(ObResolverUtils::uv_check_has_non_inner_join(*table, has_non_inner_join))) {
LOG_WARN("check has non inner join failed", K(ret));
} else {
LOG_DEBUG("update view check", K(has_non_inner_join));
ret = has_non_inner_join ? OB_ERR_NON_UPDATABLE_TABLE : OB_SUCCESS;
}
}
if (OB_ERR_NON_UPDATABLE_TABLE == ret) {
ObString upd_str = "UPDATE";
LOG_USER_ERROR(OB_ERR_NON_UPDATABLE_TABLE,
table->get_table_name().length(),
table->get_table_name().ptr(),
upd_str.length(),
upd_str.ptr());
}
}
}
if (OB_SUCC(ret) && lib::is_oracle_mode()) {
FOREACH_CNT_X(assign, update_stmt->get_tables_assignments(), OB_SUCC(ret))
{
const TableItem* table = update_stmt->get_table_item_by_id(assign->table_id_);
if (OB_ISNULL(table)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("table item is null", K(ret), K(assign->table_id_));
} else if (!table->is_generated_table()) {
continue;
}
if (OB_SUCC(ret)) {
bool has_distinct = false;
if (OB_FAIL(
ObResolverUtils::uv_check_oracle_distinct(*table, *session_info_, *schema_checker_, has_distinct))) {
LOG_WARN("check updatable view distinct failed", K(ret));
} else {
LOG_DEBUG("check has distinct", K(ret), K(has_distinct));
ret = has_distinct ? OB_ERR_ILLEGAL_VIEW_UPDATE : ret;
}
}
// check key preserved table
if (OB_SUCC(ret)) {
bool key_preserved = 0;
if (OB_FAIL(uv_check_key_preserved(*table, key_preserved))) {
LOG_WARN("check key preserved failed", K(ret));
} else {
LOG_DEBUG("check key preserved", K(key_preserved));
ret = !key_preserved ? OB_ERR_O_UPDATE_VIEW_NON_KEY_PRESERVED : ret;
}
}
}
}
return ret;
}
int ObUpdateResolver::try_add_rowid_column_to_stmt(const ObTableAssignment& tas)
{
int ret = OB_SUCCESS;
ObUpdateStmt* update_stmt = get_update_stmt();
IndexDMLInfo* primary_dml_info = NULL;
ObArray<ObColumnRefRawExpr*> column_exprs;
if (OB_ISNULL(update_stmt)) {
ret = OB_ERR_UNEXPECTED;
SQL_RESV_LOG(WARN, "delete stmt is null", K(ret));
} else if (OB_ISNULL(primary_dml_info = update_stmt->get_or_add_table_columns(tas.table_id_))) {
ret = OB_ERR_UNEXPECTED;
SQL_RESV_LOG(WARN, "unexpecteed null primary dml info", K(ret));
} else if (OB_FAIL(update_stmt->get_column_exprs(column_exprs))) {
LOG_WARN("failed to get column exprs", K(ret));
} else {
int rowid_col_idx = -1;
for (int i = 0; - 1 == rowid_col_idx && OB_SUCC(ret) && i < column_exprs.count(); i++) {
if (OB_ISNULL(column_exprs.at(i))) {
ret = OB_ERR_UNEXPECTED;
SQL_RESV_LOG(WARN, "unexpected null column expr", K(ret));
} else if (OB_HIDDEN_LOGICAL_ROWID_COLUMN_ID == column_exprs.at(i)->get_column_id() &&
tas.table_id_ == column_exprs.at(i)->get_table_id()) {
rowid_col_idx = i;
}
} // for end
if (OB_FAIL(ret) || -1 == rowid_col_idx) {
// do nothing
} else if (OB_FAIL(add_var_to_array_no_dup(primary_dml_info->column_exprs_, column_exprs.at(rowid_col_idx)))) {
SQL_RESV_LOG(WARN, "failed to add element to array", K(ret));
}
}
return ret;
}
int ObUpdateResolver::is_multi_table_update(const ObDMLStmt* stmt, bool& is_multi_table)
{
int ret = OB_SUCCESS;
is_multi_table = false;
if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get null stmt", K(ret));
} else if (stmt->get_from_item_size() > 1) {
is_multi_table = true;
} else {
const TableItem* table_item = stmt->get_table_item(stmt->get_from_item(0));
if (OB_ISNULL(table_item)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get null table item", K(ret));
} else if (table_item->is_joined_table()) {
is_multi_table = true;
} else if (table_item->is_generated_table() &&
OB_FAIL(is_multi_table_update(table_item->ref_query_, is_multi_table))) {
LOG_WARN("failed to check is multi table update", K(ret));
}
}
return ret;
}
} // namespace sql
} // namespace oceanbase