patch 4.0

This commit is contained in:
wangzelin.wzl
2022-10-24 10:34:53 +08:00
parent 4ad6e00ec3
commit 93a1074b0c
10533 changed files with 2588271 additions and 2299373 deletions

View 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