patch 4.0
This commit is contained in:
931
src/sql/resolver/ddl/ob_trigger_resolver.cpp
Normal file
931
src/sql/resolver/ddl/ob_trigger_resolver.cpp
Normal file
@ -0,0 +1,931 @@
|
||||
/**
|
||||
* 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/ob_resolver_utils.h"
|
||||
#include "sql/resolver/ddl/ob_trigger_resolver.h"
|
||||
#include "sql/resolver/ddl/ob_trigger_stmt.h"
|
||||
#include "pl/parser/ob_pl_parser.h"
|
||||
#include "pl/ob_pl_resolver.h"
|
||||
#include "sql/resolver/ob_stmt_resolver.h"
|
||||
#include "sql/resolver/ddl/ob_create_routine_resolver.h"
|
||||
|
||||
namespace oceanbase
|
||||
{
|
||||
namespace sql
|
||||
{
|
||||
using namespace common;
|
||||
using namespace obrpc;
|
||||
using namespace share::schema;
|
||||
|
||||
int ObTriggerResolver::resolve(const ParseNode &parse_tree)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObItemType stmt_type = parse_tree.type_;
|
||||
switch (stmt_type) {
|
||||
case T_TG_CREATE: {
|
||||
ObCreateTriggerStmt *stmt = create_stmt<ObCreateTriggerStmt>();
|
||||
OV (OB_NOT_NULL(stmt), OB_ALLOCATE_MEMORY_FAILED);
|
||||
OZ (resolve_create_trigger_stmt(parse_tree, stmt->get_trigger_arg()));
|
||||
break;
|
||||
}
|
||||
case T_TG_DROP: {
|
||||
ObDropTriggerStmt *stmt = create_stmt<ObDropTriggerStmt>();
|
||||
OV (OB_NOT_NULL(stmt), OB_ALLOCATE_MEMORY_FAILED);
|
||||
OZ (resolve_drop_trigger_stmt(parse_tree, stmt->get_trigger_arg()));
|
||||
break;
|
||||
}
|
||||
case T_TG_ALTER: {
|
||||
ObAlterTriggerStmt *stmt = create_stmt<ObAlterTriggerStmt>();
|
||||
OV (OB_NOT_NULL(stmt), OB_ALLOCATE_MEMORY_FAILED);
|
||||
OZ (resolve_alter_trigger_stmt(parse_tree, stmt->get_trigger_arg()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("invalid stmt type", K(ret), K(stmt_type));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_sp_definer(const ParseNode *parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
CK(OB_NOT_NULL(schema_checker_));
|
||||
CK(OB_NOT_NULL(schema_checker_->get_schema_guard()));
|
||||
CK(OB_NOT_NULL(session_info_));
|
||||
CK(OB_NOT_NULL(allocator_));
|
||||
ObString user_name, host_name;
|
||||
ObString cur_user_name, cur_host_name;
|
||||
cur_user_name = session_info_->get_user_name();
|
||||
cur_host_name = session_info_->get_host_name();
|
||||
if (OB_NOT_NULL(parse_node)) {
|
||||
CK(T_USER_WITH_HOST_NAME == parse_node->type_);
|
||||
if (OB_SUCC(ret)) {
|
||||
const ParseNode *user_node = parse_node->children_[0];
|
||||
const ParseNode *host_node = parse_node->children_[1];
|
||||
|
||||
if (OB_ISNULL(user_node)) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("user must be specified", K(ret));
|
||||
} else {
|
||||
// 需要检查当前用户是否有超级权限或者set user id的权限
|
||||
if (!session_info_->has_user_super_privilege()) {
|
||||
ret = OB_ERR_NO_PRIVILEGE;
|
||||
LOG_WARN("no privilege", K(ret));
|
||||
} else {
|
||||
user_name.assign_ptr(user_node->str_value_, static_cast<int32_t>(user_node->str_len_));
|
||||
// 得区分current_user和“current_user”, 前者需要获取当前用户和host,后者是作为用户名存在
|
||||
if (0 == user_name.case_compare("current_user") && T_IDENT == user_node->type_) {
|
||||
user_name = cur_user_name;
|
||||
host_name = cur_host_name;
|
||||
} else if (OB_ISNULL(host_node)) {
|
||||
host_name.assign_ptr("%", 1);
|
||||
} else {
|
||||
host_name.assign_ptr(host_node->str_value_, static_cast<int32_t>(host_node->str_len_));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
// 检查user@host是否在mysql.user表中
|
||||
const ObUserInfo* user_info = nullptr;
|
||||
if (OB_FAIL(schema_checker_->get_schema_guard()->get_user_info(session_info_->get_effective_tenant_id(),
|
||||
user_name,
|
||||
host_name,
|
||||
user_info))) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("fail to get_user_info", K(ret));
|
||||
} else if (OB_ISNULL(user_info)) {
|
||||
LOG_USER_WARN(OB_ERR_USER_NOT_EXIST);
|
||||
pl::ObPL::insert_error_msg(OB_ERR_USER_NOT_EXIST);
|
||||
ret = OB_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (lib::is_mysql_mode()) {
|
||||
// 不指定definer时,默认为当前用户和host
|
||||
user_name = cur_user_name;
|
||||
host_name = cur_host_name;
|
||||
}
|
||||
if (OB_SUCC(ret) && lib::is_mysql_mode()) {
|
||||
//user@host作为一个整体存储到priv_user字段
|
||||
char tmp_buf[common::OB_MAX_USER_NAME_LENGTH + common::OB_MAX_HOST_NAME_LENGTH + 2] = {};
|
||||
snprintf(tmp_buf, sizeof(tmp_buf), "%.*s@%.*s", user_name.length(), user_name.ptr(),
|
||||
host_name.length(), host_name.ptr());
|
||||
|
||||
ObString priv_user(tmp_buf);
|
||||
if (OB_FAIL(ObSQLUtils::convert_sql_text_to_schema_for_storing(
|
||||
*allocator_, session_info_->get_dtc_params(), priv_user))) {
|
||||
LOG_WARN("fail to convert charset", K(ret));
|
||||
} else if (OB_FAIL(trigger_arg.trigger_info_.set_trigger_priv_user(priv_user))) {
|
||||
LOG_WARN("failed to set priv user", K(ret));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_create_trigger_stmt(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
bool is_ora = lib::is_oracle_mode();
|
||||
OV (parse_node.type_ == T_TG_CREATE, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ == (is_ora ? 1 : 2), OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[is_ora ? 0 : 1])); // trigger source.
|
||||
OV (OB_NOT_NULL(session_info_));
|
||||
if (OB_SUCC(ret) && parse_node.int32_values_[1] == 1) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "editionable in create trigger");
|
||||
}
|
||||
OX (trigger_arg.with_replace_ = (parse_node.int32_values_[0] != 0));
|
||||
OX (trigger_arg.trigger_info_.set_tenant_id(session_info_->get_effective_tenant_id()));
|
||||
OX (trigger_arg.trigger_info_.set_owner_id(session_info_->get_user_id()));
|
||||
OZ (resolve_sp_definer(is_ora ? nullptr : parse_node.children_[0], trigger_arg));
|
||||
OZ (resolve_trigger_source(*parse_node.children_[is_ora ? 0 : 1], trigger_arg));
|
||||
if (OB_SUCC(ret)) {
|
||||
ObErrorInfo &error_info = trigger_arg.error_info_;
|
||||
error_info.collect_error_info(&(trigger_arg.trigger_info_));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_drop_trigger_stmt(const ParseNode &parse_node,
|
||||
ObDropTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
OV (parse_node.type_ == T_TG_DROP, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ == 1, OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[0])); // trigger name.
|
||||
OV (OB_NOT_NULL(session_info_));
|
||||
OX (trigger_arg.tenant_id_ = session_info_->get_effective_tenant_id());
|
||||
OZ (resolve_schema_name(*parse_node.children_[0], trigger_arg.trigger_database_, trigger_arg.trigger_name_));
|
||||
OV (OB_NOT_NULL(schema_checker_));
|
||||
if (OB_SUCC(ret) && ObSchemaChecker::is_ora_priv_check()) {
|
||||
OZ (schema_checker_->check_ora_ddl_priv(
|
||||
session_info_->get_effective_tenant_id(),
|
||||
session_info_->get_priv_user_id(),
|
||||
trigger_arg.trigger_database_,
|
||||
stmt::T_DROP_TRIGGER,
|
||||
session_info_->get_enable_role_array()));
|
||||
}
|
||||
OX (trigger_arg.if_exist_ = parse_node.value_);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_alter_trigger_stmt(const ParseNode &parse_node,
|
||||
ObAlterTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObString trigger_db_name;
|
||||
ObString trigger_name;
|
||||
const ObTriggerInfo *old_tg_info = NULL;
|
||||
ObTriggerInfo new_tg_info;
|
||||
OV (parse_node.type_ == T_TG_ALTER, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ == 1, OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[0])); //trigger name.
|
||||
OV (OB_NOT_NULL(session_info_) && OB_NOT_NULL(schema_checker_));
|
||||
OZ (resolve_schema_name(*parse_node.children_[0], trigger_db_name, trigger_name));
|
||||
OZ (new_tg_info.set_trigger_name(trigger_name), trigger_name);
|
||||
OZ (schema_checker_->get_trigger_info(session_info_->get_effective_tenant_id(), trigger_db_name,
|
||||
trigger_name, old_tg_info));
|
||||
if (OB_SUCC(ret) && OB_ISNULL(old_tg_info)) {
|
||||
ret = OB_ERR_TRIGGER_NOT_EXIST;
|
||||
LOG_ORACLE_USER_ERROR(OB_ERR_TRIGGER_NOT_EXIST, trigger_name.length(), trigger_name.ptr());
|
||||
}
|
||||
OX (new_tg_info.set_trigger_id(old_tg_info->get_trigger_id()));
|
||||
OZ (resolve_alter_clause(parse_node, new_tg_info));
|
||||
OZ (trigger_arg.trigger_infos_.push_back(new_tg_info));
|
||||
if (OB_SUCC(ret) && ObSchemaChecker::is_ora_priv_check()) {
|
||||
OZ(schema_checker_->check_ora_ddl_priv(session_info_->get_effective_tenant_id(),
|
||||
session_info_->get_priv_user_id(),
|
||||
trigger_db_name, stmt::T_ALTER_TRIGGER,
|
||||
session_info_->get_enable_role_array()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_trigger_source(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObString trigger_name;
|
||||
ObString trigger_body;
|
||||
OV (parse_node.type_ == T_TG_SOURCE, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ == 2, OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[0])); // trigger name.
|
||||
OV (OB_NOT_NULL(parse_node.children_[1])); // trigger definition.
|
||||
OZ (resolve_schema_name(*parse_node.children_[0], trigger_arg.trigger_database_, trigger_name));
|
||||
OV (OB_NOT_NULL(session_info_));
|
||||
OV (OB_NOT_NULL(schema_checker_));
|
||||
if (OB_SUCC(ret) && ObSchemaChecker::is_ora_priv_check()) {
|
||||
OZ (schema_checker_->check_ora_ddl_priv(
|
||||
session_info_->get_effective_tenant_id(),
|
||||
session_info_->get_priv_user_id(),
|
||||
trigger_arg.trigger_database_,
|
||||
stmt::T_CREATE_TRIGGER,
|
||||
session_info_->get_enable_role_array()));
|
||||
}
|
||||
OZ (trigger_arg.trigger_info_.set_trigger_name(trigger_name), trigger_name);
|
||||
trigger_body = ObString(parse_node.children_[1]->str_len_, parse_node.children_[1]->str_value_);
|
||||
OZ (ObSQLUtils::convert_sql_text_to_schema_for_storing(
|
||||
*allocator_, session_info_->get_dtc_params(), trigger_body));
|
||||
OZ (trigger_arg.trigger_info_.set_trigger_body(trigger_body));
|
||||
if (OB_FAIL(ret)) {
|
||||
// do nothing
|
||||
} else if (T_TG_SIMPLE_DML == parse_node.children_[1]->type_) {
|
||||
OX (trigger_arg.trigger_info_.set_simple_dml_type());
|
||||
OZ (resolve_simple_dml_trigger(*parse_node.children_[1], trigger_arg));
|
||||
} else if (T_TG_INSTEAD_DML == parse_node.children_[1]->type_) {
|
||||
OX (trigger_arg.trigger_info_.set_instead_dml_type());
|
||||
OZ (resolve_instead_dml_trigger(*parse_node.children_[1], trigger_arg));
|
||||
} else if (T_TG_COMPOUND_DML == parse_node.children_[1]->type_) {
|
||||
OX (trigger_arg.trigger_info_.set_compound_dml_type());
|
||||
OZ (resolve_compound_dml_trigger(*parse_node.children_[1], trigger_arg));
|
||||
}
|
||||
if (OB_SUCC(ret) && parse_node.value_ != 0 && is_oracle_mode()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "default collation in create trigger");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_instead_dml_trigger(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
// An INSTEAD OF trigger is always a row-level trigger.
|
||||
int ret = OB_SUCCESS;
|
||||
LOG_DEBUG("resolve instead of trigger");
|
||||
OV (T_TG_INSTEAD_DML == parse_node.type_, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ == 4, OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[0])); // dml event.
|
||||
// when-clause not supported in oracle
|
||||
OV (OB_ISNULL(parse_node.children_[2]), OB_ERR_WHEN_CLAUSE_IN_TRI);
|
||||
OV (OB_NOT_NULL(parse_node.children_[3])); // trigger body.
|
||||
OX (trigger_arg.trigger_info_.add_before_row()); // instead of trigger is always before row.
|
||||
OZ (resolve_dml_event_option(*parse_node.children_[0], trigger_arg));
|
||||
OZ (resolve_reference_names(parse_node.children_[1], trigger_arg));
|
||||
OZ (resolve_trigger_status(parse_node.int16_values_[1], trigger_arg));
|
||||
OZ (resolve_trigger_body(*parse_node.children_[3], trigger_arg));
|
||||
OZ (fill_package_info(trigger_arg.trigger_info_));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_simple_dml_trigger(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
bool is_ora = lib::is_oracle_mode();
|
||||
OV (parse_node.type_ == T_TG_SIMPLE_DML, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ == (is_ora ? 4 : 3), OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[0])); // dml event.
|
||||
OV (OB_NOT_NULL(parse_node.children_[is_ora ? 3 : 2])); // simple trigger body.
|
||||
if (OB_FAIL(ret)) {
|
||||
// do nothing
|
||||
} else if (is_ora) {
|
||||
OX (LOG_DEBUG("TRIGGER", K(parse_node.int16_values_[0]), K(parse_node.int16_values_[1]),
|
||||
K(parse_node.int16_values_[2])));
|
||||
OZ (resolve_timing_point(parse_node.int16_values_[0], parse_node.int16_values_[1], trigger_arg));
|
||||
OZ (resolve_dml_event_option(*parse_node.children_[0], trigger_arg));
|
||||
OZ (resolve_reference_names(parse_node.children_[1], trigger_arg));
|
||||
OZ (resolve_trigger_status(parse_node.int16_values_[2], trigger_arg));
|
||||
OZ (resolve_when_condition(parse_node.children_[2], trigger_arg));
|
||||
} else {
|
||||
OX (LOG_DEBUG("TRIGGER", K(parse_node.int16_values_[0])));
|
||||
OV (OB_NOT_NULL(parse_node.children_[1])); // msyql mode, trigger_name
|
||||
if (OB_SUCC(ret)) {
|
||||
if (T_BEFORE == parse_node.int16_values_[0]) {
|
||||
trigger_arg.trigger_info_.add_before_row();
|
||||
} else if (T_AFTER == parse_node.int16_values_[0]) {
|
||||
trigger_arg.trigger_info_.add_after_row();
|
||||
} else {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("parse_node timing points is invalid", K(parse_node.int16_values_[0]), K(ret));
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
switch (parse_node.children_[0]->type_)
|
||||
{
|
||||
case T_INSERT:
|
||||
trigger_arg.trigger_info_.add_insert_event();
|
||||
break;
|
||||
case T_UPDATE:
|
||||
trigger_arg.trigger_info_.add_update_event();
|
||||
break;
|
||||
case T_DELETE:
|
||||
trigger_arg.trigger_info_.add_delete_event();
|
||||
break;
|
||||
default:
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("parse_node type is invalid", K(ret), K(parse_node.children_[0]->type_));
|
||||
break;
|
||||
}
|
||||
}
|
||||
OX (trigger_arg.trigger_info_.set_enable());
|
||||
OZ (trigger_arg.trigger_info_.set_ref_old_name(REF_OLD));
|
||||
OZ (trigger_arg.trigger_info_.set_ref_new_name(REF_NEW));
|
||||
OZ (trigger_arg.trigger_info_.set_ref_parent_name(REF_PARENT));
|
||||
}
|
||||
OZ (resolve_schema_name(*parse_node.children_[1],
|
||||
trigger_arg.base_object_database_, trigger_arg.base_object_name_));
|
||||
OZ (resolve_base_object(trigger_arg, false));
|
||||
}
|
||||
OZ (resolve_trigger_body(*parse_node.children_[is_ora ? 3 : 2], trigger_arg));
|
||||
OZ (fill_package_info(trigger_arg.trigger_info_));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_compound_dml_trigger(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
OV (T_TG_COMPOUND_DML == parse_node.type_, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
CK (OB_NOT_NULL(parse_node.children_[3]));
|
||||
OZ (resolve_dml_event_option(*parse_node.children_[0], trigger_arg));
|
||||
OZ (resolve_reference_names(parse_node.children_[1], trigger_arg));
|
||||
OZ (resolve_when_condition(parse_node.children_[2], trigger_arg));
|
||||
OZ (resolve_trigger_status(static_cast<int16_t>(parse_node.value_), trigger_arg));
|
||||
OZ (resolve_compound_timing_point(*parse_node.children_[3]->children_[1], trigger_arg));
|
||||
OZ (resolve_compound_trigger_body(*parse_node.children_[3], trigger_arg));
|
||||
OZ (fill_package_info(trigger_arg.trigger_info_));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_compound_timing_point(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
const ObTableSchema *table_schema = NULL;
|
||||
ObSchemaGetterGuard *schema_guard = schema_checker_->get_schema_guard();
|
||||
CK (OB_NOT_NULL(schema_guard));
|
||||
OZ (schema_guard->get_table_schema(trigger_arg.trigger_info_.get_tenant_id(),
|
||||
trigger_arg.trigger_info_.get_base_object_id(),
|
||||
table_schema));
|
||||
CK (OB_NOT_NULL(table_schema));
|
||||
#define LABEL_NOT_MATCH(ident1, ident2) \
|
||||
ret = OB_ERR_END_LABEL_NOT_MATCH; \
|
||||
LOG_USER_ERROR(OB_ERR_END_LABEL_NOT_MATCH, ident1.length(), ident1.ptr(), ident2.length(), ident2.ptr()); \
|
||||
LOG_WARN("END identifier must match START identifier", K(ident1), K(ident2), K(ret));
|
||||
|
||||
#define CHECK_DUPLICATE_OR_SET_POINT(timing) \
|
||||
if (trigger_arg.trigger_info_.has_##timing##_point()) { \
|
||||
ret = OB_ERR_DUPLICATE_TRIGGER_SECTION; \
|
||||
LOG_WARN("duplicate Compound Triggers section after row", K(ret)); \
|
||||
} else { \
|
||||
trigger_arg.trigger_info_.add_##timing(); \
|
||||
}
|
||||
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < parse_node.num_child_; i++) {
|
||||
CK (OB_NOT_NULL(parse_node.children_[i]));
|
||||
if (OB_SUCC(ret)) {
|
||||
const int16_t header_timing = parse_node.children_[i]->int16_values_[0];
|
||||
const int16_t header_level = parse_node.children_[i]->int16_values_[1];
|
||||
const int16_t tail_timing = parse_node.children_[i]->int16_values_[2];
|
||||
const int16_t tail_level = parse_node.children_[i]->int16_values_[3];
|
||||
if (T_INSTEAD == header_timing) {
|
||||
if (T_INSTEAD != tail_timing) {
|
||||
ret = OB_ERR_PARSE_PLSQL;
|
||||
LOG_WARN("unexpected symbol", K(ret));
|
||||
if (T_AFTER == tail_timing) {
|
||||
LOG_USER_ERROR(OB_ERR_PARSE_PLSQL, "\"AFTER\"", "instead");
|
||||
} else {
|
||||
LOG_USER_ERROR(OB_ERR_PARSE_PLSQL, "\"BEFORE\"", "instead");
|
||||
}
|
||||
} else if (!table_schema->is_user_view()) {
|
||||
ret = OB_ERR_INVALID_SECTION;
|
||||
LOG_WARN("invalid section for this type of Compound Trigger", K(ret));
|
||||
} else {
|
||||
trigger_arg.trigger_info_.add_instead_row();
|
||||
trigger_arg.trigger_info_.add_before_row(); // instead of trigger 在before row时机执行
|
||||
}
|
||||
} else {
|
||||
if (T_BEFORE == header_timing) {
|
||||
if (T_BEFORE != tail_timing) {
|
||||
LABEL_NOT_MATCH(ObString("BEFORE"), ObString("AFTER"));
|
||||
} else if (T_TP_STATEMENT == header_level) {
|
||||
if (T_TP_STATEMENT != tail_level) {
|
||||
LABEL_NOT_MATCH(ObString("STATEMENT"), ObString("ROW"));
|
||||
} else {
|
||||
CHECK_DUPLICATE_OR_SET_POINT(before_stmt);
|
||||
}
|
||||
} else {
|
||||
if (T_TP_EACH_ROW != tail_level) {
|
||||
LABEL_NOT_MATCH(ObString("ROW"), ObString("STATEMENT"));
|
||||
} else {
|
||||
CHECK_DUPLICATE_OR_SET_POINT(before_row);
|
||||
}
|
||||
}
|
||||
} else if (T_AFTER == header_timing) {
|
||||
if (T_AFTER != tail_timing) {
|
||||
LABEL_NOT_MATCH(ObString("AFTER"), ObString("BEFORE"));
|
||||
} else if (T_TP_STATEMENT == header_level) {
|
||||
if (T_TP_STATEMENT != tail_level) {
|
||||
LABEL_NOT_MATCH(ObString("STATEMENT"), ObString("ROW"));
|
||||
} else {
|
||||
CHECK_DUPLICATE_OR_SET_POINT(after_stmt);
|
||||
}
|
||||
} else {
|
||||
if (T_TP_EACH_ROW != tail_level) {
|
||||
LABEL_NOT_MATCH(ObString("ROW"), ObString("STATEMENT"));
|
||||
} else {
|
||||
CHECK_DUPLICATE_OR_SET_POINT(after_row);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret) && !table_schema->is_user_table()) {
|
||||
ret = OB_ERR_INVALID_SECTION;
|
||||
LOG_WARN("invalid section for this type of Compound Trigger", K(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef CHECK_DUPLICATE_OR_SET_POINT
|
||||
#undef LABEL_NOT_MATCH
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_timing_point(int16_t before_or_after, int16_t stmt_or_row,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
pl::ObPLParser pl_parser(*allocator_, session_info_->get_local_collation_connection());
|
||||
ParseResult parse_result;
|
||||
const ObStmtNodeTree *parse_tree = NULL;
|
||||
bool is_include_old_new_in_trigger = false;
|
||||
const ObString &trigger_body = trigger_arg.trigger_info_.get_trigger_body();
|
||||
parse_result.is_for_trigger_ = 1;
|
||||
parse_result.mysql_compatible_comment_ = 0;
|
||||
parse_result.is_dynamic_sql_ = 0;
|
||||
OZ (pl_parser.parse(trigger_body, trigger_body, parse_result));
|
||||
CK (OB_NOT_NULL(parse_tree = parse_result.result_tree_));
|
||||
CK (T_STMT_LIST == parse_tree->type_);
|
||||
CK (1 == parse_tree->num_child_);
|
||||
CK (OB_NOT_NULL(parse_tree = parse_tree->children_[0]));
|
||||
OX (is_include_old_new_in_trigger
|
||||
= (1 == parse_result.is_include_old_new_in_trigger_) ? true : false);
|
||||
if (OB_SUCC(ret) && T_SP_PRE_STMTS == parse_tree->type_) {
|
||||
CK (OB_NOT_NULL(params_.allocator_));
|
||||
CK (OB_NOT_NULL(params_.schema_checker_));
|
||||
CK (OB_NOT_NULL(params_.session_info_));
|
||||
OZ (pl::ObPLResolver::resolve_condition_compile(
|
||||
*(params_.allocator_),
|
||||
params_.session_info_,
|
||||
params_.schema_checker_->get_schema_guard(),
|
||||
NULL, /*package_guard*/
|
||||
params_.sql_proxy_, /*sql_proxy*/
|
||||
&(trigger_arg.trigger_info_.get_package_exec_env()),
|
||||
parse_tree,
|
||||
parse_tree,
|
||||
true /*inner_parse*/,
|
||||
true /*for trigger*/,
|
||||
false /*for dynamic*/,
|
||||
&(is_include_old_new_in_trigger)
|
||||
));
|
||||
}
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (is_include_old_new_in_trigger && T_TP_EACH_ROW != stmt_or_row) {
|
||||
ret = OB_ERR_NEW_OLD_REFERENCES;
|
||||
LOG_WARN("NEW or OLD references not allowed in table level triggers", K(ret),
|
||||
K(trigger_arg.trigger_info_.get_trigger_body()));
|
||||
} else if (T_BEFORE == before_or_after && T_TP_STATEMENT == stmt_or_row) {
|
||||
trigger_arg.trigger_info_.add_before_stmt();
|
||||
} else if (T_AFTER == before_or_after && T_TP_STATEMENT == stmt_or_row) {
|
||||
trigger_arg.trigger_info_.add_after_stmt();
|
||||
} else if (T_BEFORE == before_or_after && T_TP_EACH_ROW == stmt_or_row) {
|
||||
trigger_arg.trigger_info_.add_before_row();
|
||||
} else if (T_AFTER == before_or_after && T_TP_EACH_ROW == stmt_or_row) {
|
||||
trigger_arg.trigger_info_.add_after_row();
|
||||
} else {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("parse_node timing points is invalid", K(ret),
|
||||
K(before_or_after), K(stmt_or_row));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_dml_event_option(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
OV (parse_node.type_ == T_TG_DML_EVENT_OPTION, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (3 == parse_node.num_child_, OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(parse_node.children_[0])); // dml event list.
|
||||
OV (OB_NOT_NULL(parse_node.children_[2])); // base object name.
|
||||
if (OB_SUCC(ret) && OB_NOT_NULL(parse_node.children_[1])) {
|
||||
if (trigger_arg.trigger_info_.is_simple_dml_type()) {
|
||||
ret = OB_ERR_NESTED_TABLE_IN_TRI;
|
||||
LOG_WARN("nested table not allowed here", K(ret));
|
||||
} else if (trigger_arg.trigger_info_.is_instead_dml_type()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_WARN("nested table cluase not supported now", K(ret));
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "nested table cluase");
|
||||
}
|
||||
}
|
||||
OZ (resolve_dml_event_list(*parse_node.children_[0], trigger_arg));
|
||||
OZ (resolve_schema_name(*parse_node.children_[2],
|
||||
trigger_arg.base_object_database_, trigger_arg.base_object_name_));
|
||||
OZ (resolve_base_object(trigger_arg, NULL == parse_node.children_[2]->children_[0]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_reference_names(const ParseNode *parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObString ref_old_name = REF_OLD;
|
||||
ObString ref_new_name = REF_NEW;
|
||||
ObString ref_parent_name = REF_PARENT;
|
||||
if (parse_node != NULL) {
|
||||
/*
|
||||
* CREATE OR REPLACE TRIGGER insert_trigger1
|
||||
* BEFORE INSERT OR UPDATE
|
||||
* ON employees
|
||||
* REFERENCING old AS old new AS new old AS old111 new AS new222
|
||||
* FOR EACH ROW
|
||||
* WHEN (NEW222.dep_id = 101)
|
||||
* BEGIN
|
||||
* if (INSERTING and :NEW222.pk is NULL) then
|
||||
* :NEW222.pk := 1;
|
||||
* end if;
|
||||
* END;
|
||||
*
|
||||
* later ref name will overwrite previous name.
|
||||
*/
|
||||
OV (parse_node->type_ == T_TG_REF_LIST, OB_ERR_UNEXPECTED, parse_node->type_);
|
||||
for (int32_t i = 0; OB_SUCC(ret) && i < parse_node->num_child_; i++) {
|
||||
const ParseNode *ref_node = parse_node->children_[i];
|
||||
OV (OB_NOT_NULL(ref_node), OB_ERR_UNEXPECTED, i);
|
||||
OV (ref_node->type_ == T_IDENT, OB_ERR_UNEXPECTED, ref_node->type_);
|
||||
OV (OB_NOT_NULL(ref_node->str_value_), OB_ERR_UNEXPECTED, i)
|
||||
OX (switch (ref_node->value_) {
|
||||
case T_TG_REF_OLD:
|
||||
OX (ref_old_name.assign_ptr(ref_node->str_value_, ref_node->str_len_));
|
||||
break;
|
||||
case T_TG_REF_NEW:
|
||||
OX (ref_new_name.assign_ptr(ref_node->str_value_, ref_node->str_len_));
|
||||
break;
|
||||
case T_TG_REF_PARENT:
|
||||
ret = OB_ERR_TRIGGER_INVALID_REF_NAME;
|
||||
LOG_WARN("invalid REFERENCING name", K(ret));
|
||||
break;
|
||||
default:
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("referencing type is invalid", K(ref_node->value_));
|
||||
break;
|
||||
})
|
||||
}
|
||||
}
|
||||
OZ (trigger_arg.trigger_info_.set_ref_old_name(ref_old_name));
|
||||
OZ (trigger_arg.trigger_info_.set_ref_new_name(ref_new_name));
|
||||
OZ (trigger_arg.trigger_info_.set_ref_parent_name(ref_parent_name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_dml_event_list(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObIAllocator *allocator = trigger_arg.trigger_info_.get_allocator();
|
||||
const ParseNode *event_node = NULL;
|
||||
OV (parse_node.type_ == T_TG_DML_EVENT_LIST, OB_ERR_UNEXPECTED, parse_node.type_);
|
||||
OV (parse_node.num_child_ > 0, OB_ERR_UNEXPECTED, parse_node.num_child_);
|
||||
OV (OB_NOT_NULL(parse_node.children_));
|
||||
OV (OB_NOT_NULL(allocator));
|
||||
// update columns.
|
||||
#define RESOLVE_UPDATE_COLUMN_LIST(event, tg_arg, col_arr) \
|
||||
{ \
|
||||
if (NULL != event->str_value_) { \
|
||||
if (tg_arg.trigger_info_.is_instead_dml_type() || !tg_arg.trigger_info_.has_update_event()) { \
|
||||
ret = OB_ERR_COL_LIST_IN_TRI; \
|
||||
LOG_WARN("column list not valid for instead of trigger type", K(ret)); \
|
||||
} else { \
|
||||
const ObString new_columns(event->str_len_, event->str_value_); \
|
||||
static const char *UPDATE_OF_STR = "UPDATE OF "; \
|
||||
char *buf = NULL; \
|
||||
int64_t buf_len = new_columns.length() + STRLEN(UPDATE_OF_STR); \
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < event->num_child_; ++i) { \
|
||||
ObString col(event->children_[i]->str_value_); \
|
||||
for (int64_t j = 0; OB_SUCC(ret) && j < col_arr.count(); ++j) { \
|
||||
if (0 == col_arr.at(j).case_compare(col)) { \
|
||||
ret = OB_ERR_FIELD_SPECIFIED_TWICE; \
|
||||
LOG_WARN("duplicate column name", K(col), K(ret)); \
|
||||
} \
|
||||
} \
|
||||
OZ (col_arr.push_back(col)); \
|
||||
} \
|
||||
OV (OB_NOT_NULL(buf = static_cast<char *>(allocator->alloc(buf_len))), \
|
||||
OB_ALLOCATE_MEMORY_FAILED, buf_len); \
|
||||
OX (MEMCPY(buf, UPDATE_OF_STR, STRLEN(UPDATE_OF_STR))); \
|
||||
OX (MEMCPY(buf + buf_len - new_columns.length(), new_columns.ptr(), \
|
||||
new_columns.length())); \
|
||||
OX (tg_arg.trigger_info_.assign_update_columns(buf, buf_len)); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
/*
|
||||
* CREATE OR REPLACE TRIGGER simple_trigger
|
||||
* BEFORE INSERT OR
|
||||
* INSERT OR
|
||||
* UPDATE OF department_id, salary OR
|
||||
* UPDATE OF employee_name
|
||||
* ON employees
|
||||
* FOR EACH ROW
|
||||
* BEGIN
|
||||
* NULL;
|
||||
* END;
|
||||
* /
|
||||
* duplicate event is OK, just combine them.
|
||||
*/
|
||||
ObArray<ObString> col_array;
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < parse_node.num_child_; i++) {
|
||||
OV (OB_NOT_NULL(event_node = parse_node.children_[i]));
|
||||
switch (event_node->type_) {
|
||||
case T_INSERT: {
|
||||
OX (trigger_arg.trigger_info_.add_insert_event());
|
||||
RESOLVE_UPDATE_COLUMN_LIST(event_node, trigger_arg, col_array);
|
||||
break;
|
||||
}
|
||||
case T_UPDATE: {
|
||||
OX (trigger_arg.trigger_info_.add_update_event());
|
||||
RESOLVE_UPDATE_COLUMN_LIST(event_node, trigger_arg, col_array);
|
||||
break;
|
||||
}
|
||||
case T_DELETE: {
|
||||
OX (trigger_arg.trigger_info_.add_delete_event());
|
||||
RESOLVE_UPDATE_COLUMN_LIST(event_node, trigger_arg, col_array);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("parse_node type is invalid", K(ret), K(event_node->type_));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_trigger_status(int16_t enable_or_disable,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (enable_or_disable == T_ENABLE) {
|
||||
trigger_arg.trigger_info_.set_enable();
|
||||
} else if (enable_or_disable == T_DISABLE) {
|
||||
trigger_arg.trigger_info_.set_disable();
|
||||
} else {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("parse_node timing points is invalid", K(ret), K(enable_or_disable));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_when_condition(const ParseNode *parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (NULL != parse_node) {
|
||||
ObString when_condition;
|
||||
OV (trigger_arg.trigger_info_.has_before_row_point()
|
||||
|| trigger_arg.trigger_info_.has_after_row_point()
|
||||
|| trigger_arg.trigger_info_.is_compound_dml_type(), OB_ERR_WHEN_CLAUSE);
|
||||
OV (parse_node->type_ == T_TG_WHEN_CONDITION, OB_ERR_UNEXPECTED, parse_node->type_);
|
||||
OV (OB_NOT_NULL(parse_node->str_value_) && parse_node->str_len_ > 0);
|
||||
OX (when_condition.assign_ptr(parse_node->str_value_, static_cast<int32_t>(parse_node->str_len_)));
|
||||
OZ (ObSQLUtils::convert_sql_text_to_schema_for_storing(
|
||||
*allocator_, session_info_->get_dtc_params(), when_condition));
|
||||
OZ (trigger_arg.trigger_info_.set_when_condition(when_condition), when_condition);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_trigger_body(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObTriggerInfo &trigger_info = trigger_arg.trigger_info_;
|
||||
ObString tg_body;
|
||||
if (lib::is_oracle_mode()) {
|
||||
OV (parse_node.type_ == T_SP_BLOCK_CONTENT || parse_node.type_ == T_SP_LABELED_BLOCK);
|
||||
}
|
||||
CK (OB_NOT_NULL(session_info_));
|
||||
OV (OB_NOT_NULL(parse_node.str_value_) && parse_node.str_len_ > 0);
|
||||
OX (tg_body.assign_ptr(parse_node.str_value_,
|
||||
static_cast<int32_t>(parse_node.str_len_)));
|
||||
OX (LOG_DEBUG("TRIGGER", K(tg_body)));
|
||||
OZ (trigger_info.gen_package_source(trigger_arg.base_object_database_,
|
||||
trigger_arg.base_object_name_, parse_node,
|
||||
session_info_->get_dtc_params()));
|
||||
if (OB_SUCC(ret) && lib::is_mysql_mode()) {
|
||||
ObString procedure_source;
|
||||
pl::ObPLParser parser(*allocator_, session_info_->get_local_collation_connection());
|
||||
ObStmtNodeTree *parse_tree = NULL;
|
||||
CHECK_COMPATIBILITY_MODE(session_info_);
|
||||
OZ (trigger_info.gen_procedure_source(trigger_arg.base_object_database_,
|
||||
trigger_arg.base_object_name_,
|
||||
parse_node,
|
||||
session_info_->get_dtc_params(),
|
||||
procedure_source));
|
||||
OZ (parser.parse_package(procedure_source, parse_tree, session_info_->get_dtc_params(), NULL, true));
|
||||
if (OB_SUCC(ret)) {
|
||||
params_.tg_timing_event_ = static_cast<int64_t>(trigger_info.get_timing_event());
|
||||
HEAP_VAR(ObCreateProcedureResolver, resolver, params_) {
|
||||
bool saved_trigger_flag = session_info_->is_for_trigger_package();
|
||||
session_info_->set_for_trigger_package(true);
|
||||
if (OB_FAIL(resolver.resolve(*parse_tree->children_[0]))) {
|
||||
LOG_WARN("resolve trigger procedure failed", K(parse_tree->children_[0]->type_), K(ret));
|
||||
}
|
||||
//无论是否执行成功都要恢复该变量原值
|
||||
session_info_->set_for_trigger_package(saved_trigger_flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_compound_trigger_body(const ParseNode &parse_node,
|
||||
ObCreateTriggerArg &trigger_arg)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObTriggerInfo &trigger_info = trigger_arg.trigger_info_;
|
||||
CK (OB_NOT_NULL(session_info_));
|
||||
OZ (trigger_info.gen_package_source(trigger_arg.base_object_database_,
|
||||
trigger_arg.base_object_name_,
|
||||
parse_node,
|
||||
session_info_->get_dtc_params()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_schema_name(const ParseNode &parse_node,
|
||||
ObString &database_name,
|
||||
ObString &schema_name)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
OV (OB_NOT_NULL(session_info_));
|
||||
OZ (ObResolverUtils::resolve_sp_name(*session_info_, parse_node, database_name, schema_name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_alter_clause(const ParseNode &alter_clause,
|
||||
ObTriggerInfo &tg_info)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (T_ENABLE == static_cast<ObItemType>(alter_clause.value_)) {
|
||||
tg_info.set_enable();
|
||||
} else if (T_DISABLE == static_cast<ObItemType>(alter_clause.value_)) {
|
||||
tg_info.set_disable();
|
||||
} else {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("alter clause must be ENABLE/DISABLE", K(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::fill_package_info(ObTriggerInfo &trigger_info)
|
||||
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
char buf[OB_MAX_PROC_ENV_LENGTH];
|
||||
int64_t pos = 0;
|
||||
ObString pl_exec_env;
|
||||
OV (OB_NOT_NULL(session_info_));
|
||||
OZ (ObExecEnv::gen_exec_env(*session_info_, buf, OB_MAX_PROC_ENV_LENGTH, pos));
|
||||
OX (pl_exec_env.assign_ptr(buf, static_cast<int32_t>(pos)));
|
||||
OX (trigger_info.set_package_flag(0));
|
||||
OX (trigger_info.set_package_comp_flag(0));
|
||||
OZ (trigger_info.set_package_exec_env(pl_exec_env));
|
||||
OX (trigger_info.set_sql_mode(session_info_->get_sql_mode()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObTriggerResolver::resolve_base_object(ObCreateTriggerArg &tg_arg,
|
||||
bool search_public_schema) {
|
||||
int ret = OB_SUCCESS;
|
||||
uint64_t tg_db_id = OB_INVALID_ID;
|
||||
ObTriggerInfo &tg_info = tg_arg.trigger_info_;
|
||||
uint64_t tenant_id = tg_info.get_tenant_id();
|
||||
ObSchemaGetterGuard *schema_guard = schema_checker_->get_schema_guard();
|
||||
const ObTableSchema *table_schema = NULL;
|
||||
OV (OB_NOT_NULL(schema_guard));
|
||||
OZ (schema_checker_->get_database_id(tenant_id, tg_arg.trigger_database_, tg_db_id));
|
||||
OZ (schema_guard->get_table_schema(tenant_id, tg_arg.base_object_database_,
|
||||
tg_arg.base_object_name_,
|
||||
false/*is_index*/, table_schema));
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (OB_ISNULL(table_schema)) {
|
||||
if (lib::is_oracle_mode()) {
|
||||
uint64_t base_db_id = OB_INVALID_ID;
|
||||
uint64_t object_db_id = OB_INVALID_ID;
|
||||
ObSynonymChecker synonym_checker;
|
||||
ObString object_db_name;
|
||||
ObString object_name;
|
||||
const ObDatabaseSchema *object_db_schema = NULL;
|
||||
bool exist = false;
|
||||
OZ (schema_checker_->get_database_id(tenant_id, tg_arg.base_object_database_, base_db_id));
|
||||
OZ (ObResolverUtils::resolve_synonym_object_recursively(*schema_checker_, synonym_checker,
|
||||
tenant_id, base_db_id,
|
||||
tg_arg.base_object_name_,
|
||||
object_db_id, object_name, exist,
|
||||
search_public_schema));
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (!exist) {
|
||||
ret = OB_TABLE_NOT_EXIST;
|
||||
LOG_WARN("synonym not exist", K(tenant_id), K(base_db_id),
|
||||
K(tg_arg.base_object_name_), K(ret));
|
||||
} else if (OB_FAIL(schema_guard->get_table_schema(tenant_id, object_db_id, object_name,
|
||||
false/*is_index*/, table_schema))) {
|
||||
LOG_WARN("get table schema failed", K(object_name), K(ret));
|
||||
} else if (OB_ISNULL(table_schema)) {
|
||||
ret = OB_TABLE_NOT_EXIST;
|
||||
LOG_WARN("table schema is null", KP(table_schema), K(ret));
|
||||
} else {
|
||||
// oracle mode, 前面设置的base_object_database_可能不正确,此处兜底
|
||||
tg_arg.base_object_name_ = object_name;
|
||||
object_db_id = table_schema->get_database_id();
|
||||
OZ (schema_checker_->get_database_schema(tenant_id, object_db_id, object_db_schema));
|
||||
CK (OB_NOT_NULL(object_db_schema));
|
||||
OX (tg_arg.base_object_database_ = object_db_schema->get_database_name());
|
||||
}
|
||||
} else {
|
||||
ret = OB_TABLE_NOT_EXIST;
|
||||
LOG_WARN("table or view does not exist", K(tenant_id), K(tg_db_id),
|
||||
K(tg_arg.base_object_name_), K(ret));
|
||||
LOG_MYSQL_USER_ERROR(OB_TABLE_NOT_EXIST, tg_arg.base_object_database_.ptr(),
|
||||
tg_arg.base_object_name_.ptr());
|
||||
}
|
||||
}
|
||||
if (OB_FAIL(ret)) {
|
||||
} else if (OB_ISNULL(table_schema)) {
|
||||
ret = OB_ERR_BAD_TABLE;
|
||||
LOG_WARN("table schema is invalid", K(ret));
|
||||
} else if (table_schema->is_in_recyclebin()) {
|
||||
ret = OB_ERR_OPERATION_ON_RECYCLE_OBJECT;
|
||||
LOG_WARN("table is in recyclebin", K(ret));
|
||||
} else if (tg_info.is_instead_dml_type()) {
|
||||
if (!table_schema->is_user_view()) {
|
||||
ret = OB_ERR_INSTEAD_TRI_ON_TABLE;
|
||||
LOG_WARN("instead of trigger only support on user view", K(ret));
|
||||
} else if (table_schema->is_read_only()) {
|
||||
ret = OB_ERR_TRIGGER_CANT_CRT_ON_RO_VIEW;
|
||||
LOG_WARN("cannot CREATE INSTEAD OF trigger on a read-only view",
|
||||
K(table_schema->get_table_name_str()), K(ret));
|
||||
}
|
||||
} else if (tg_info.is_simple_dml_type()) {
|
||||
if (!table_schema->is_user_table()) {
|
||||
ret = OB_NOT_SUPPORTED;
|
||||
LOG_WARN("simple dml trigger only support on user table", K(ret));
|
||||
LOG_USER_ERROR(OB_NOT_SUPPORTED, "simple dml trigger isn't used on user table");
|
||||
} else if (lib::is_mysql_mode()) {
|
||||
uint64_t trigger_id = OB_INVALID_ID;
|
||||
const ObTriggerInfo *trigger_info = NULL;
|
||||
const uint64_t tenant_id = table_schema->get_tenant_id();
|
||||
const ObIArray<uint64_t> &trigger_list = table_schema->get_trigger_list();
|
||||
if (tg_db_id != table_schema->get_database_id()) {
|
||||
ret = OB_ERR_TRIGGER_IN_WRONG_SCHEMA;
|
||||
LOG_WARN("trigger database must same as table database", K(tg_db_id),
|
||||
K(table_schema->get_database_id()), K(ret));
|
||||
}
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < trigger_list.count(); i++) {
|
||||
OX (trigger_id = trigger_list.at(i));
|
||||
OZ (schema_guard->get_trigger_info(tenant_id, trigger_id, trigger_info), trigger_id);
|
||||
OV (OB_NOT_NULL(trigger_info), OB_ERR_UNEXPECTED, trigger_id);
|
||||
if (OB_SUCC(ret)
|
||||
&& trigger_info->get_timing_points() == tg_arg.trigger_info_.get_timing_points()
|
||||
&& trigger_info->get_trigger_events() == tg_arg.trigger_info_.get_trigger_events()) {
|
||||
ret = OB_ERR_TRIGGER_NOT_SUPPORT;
|
||||
LOG_WARN("not support multiple triggers with the same timing points in mysql mode",
|
||||
K(trigger_id), K(trigger_info->get_trigger_name()), K(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
tg_info.set_database_id(tg_db_id);
|
||||
tg_info.set_base_object_type(table_schema->is_user_table() ? TABLE_SCHEMA : VIEW_SCHEMA);
|
||||
tg_info.set_base_object_id(table_schema->get_table_id());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const ObString ObTriggerResolver::REF_OLD = "OLD";
|
||||
const ObString ObTriggerResolver::REF_NEW = "NEW";
|
||||
const ObString ObTriggerResolver::REF_PARENT = "PARENT";
|
||||
|
||||
} // namespace sql
|
||||
} // namespace oceanbase
|
||||
|
||||
Reference in New Issue
Block a user