Files
oceanbase/src/sql/resolver/ddl/ob_create_routine_resolver.cpp
obdev 57f1c6e7ee [FEAT MERGE]:develop pl feature of 4.2 version
Co-authored-by: LiuYoung00 <liuyanglo_ol@163.com>
Co-authored-by: 0xacc <heyongyi1998@gmail.com>
Co-authored-by: seuwebber <webber_code@163.com>
2023-04-27 16:08:11 +08:00

1001 lines
46 KiB
C++

/**
* 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 "ob_create_routine_resolver.h"
#include "ob_create_routine_stmt.h"
#include "sql/resolver/ob_resolver_utils.h"
#include "pl/parser/parse_stmt_item_type.h"
#include "pl/ob_pl_router.h"
#include "pl/ob_pl_package.h"
#include "pl/ob_pl_resolver.h"
#include "share/schema/ob_trigger_info.h"
namespace oceanbase
{
using namespace common;
using namespace share::schema;
using namespace pl;
namespace sql
{
int ObCreateRoutineResolver::check_dup_routine_param(const ObIArray<ObRoutineParam*> &params, const ObString &param_name)
{
int ret = OB_SUCCESS;
for (int i = 0; OB_SUCC(ret) && i < params.count(); ++i) {
ObRoutineParam* param = params.at(i);
CK(OB_NOT_NULL(param));
if (OB_SUCC(ret) && 0 == param_name.case_compare(param->get_param_name())) {
ret = OB_ERR_SP_DUP_PARAM;
LOG_USER_ERROR(OB_ERR_SP_DUP_PARAM, param_name.length(), param_name.ptr());
LOG_WARN("Duplicate parameter", K(param_name), K(ret));
break;
}
}
return ret;
}
int ObCreateRoutineResolver::create_routine_arg(obrpc::ObCreateRoutineArg *&crt_routine_arg)
{
int ret = OB_SUCCESS;
ObCreateRoutineStmt *crt_routine_stmt = NULL;
crt_routine_arg = NULL;
if (OB_ISNULL(crt_routine_stmt = create_stmt<ObCreateRoutineStmt>())) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("allocate memory for create routine stmt failed", K(ret));
} else {
crt_routine_arg = &(crt_routine_stmt->get_routine_arg());
}
return ret;
}
int ObCreateRoutineResolver::set_routine_info(const ObRoutineType &type,
ObRoutineInfo &routine_info,
bool is_udt_udf)
{
int ret = OB_SUCCESS;
CK(OB_NOT_NULL(session_info_));
if (OB_SUCC(ret)) {
if (is_udt_udf) {
routine_info.set_is_udt_udf();
}
routine_info.set_routine_type(type);
routine_info.set_tenant_id(session_info_->get_effective_tenant_id());
routine_info.set_owner_id(session_info_->get_user_id());
routine_info.set_overload(ROUTINE_STANDALONE_OVERLOAD);
routine_info.set_subprogram_id(ROUTINE_STANDALONE_SUBPROGRAM_ID);
routine_info.set_tg_timing_event(static_cast<TgTimingEvent>(params_.tg_timing_event_));
char buf[OB_MAX_PROC_ENV_LENGTH];
int64_t pos = 0;
if (OB_FAIL(ObExecEnv::gen_exec_env(*session_info_, buf, OB_MAX_PROC_ENV_LENGTH, pos))) {
LOG_WARN("failed to generate exec env", K(ret));
} else {
routine_info.set_exec_env(ObString(pos, buf));
}
}
return ret;
}
int ObCreateRoutineResolver::analyze_router_sql(obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
ObRoutineInfo &routine_info = crt_routine_arg->routine_info_;
CK(OB_NOT_NULL(schema_checker_), OB_NOT_NULL(params_.sql_proxy_), OB_NOT_NULL(session_info_));
if (OB_SUCC(ret)) {
pl::ObPLRouter router(routine_info, *session_info_, *schema_checker_->get_schema_guard(), *params_.sql_proxy_);
ObString route_sql;
if (OB_FAIL(router.analyze(route_sql, crt_routine_arg->dependency_infos_, routine_info))) {
LOG_WARN("failed to analyze route sql", K(route_sql), K(ret));
} else if (OB_FAIL(ObSQLUtils::convert_sql_text_to_schema_for_storing(
*allocator_, session_info_->get_dtc_params(), route_sql))) {
LOG_WARN("fail to convert charset", K(ret));
} else if (OB_FAIL(routine_info.set_route_sql(route_sql))) {
LOG_WARN("set routine body failed", K(route_sql), K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObCreateRoutineResolver::resolve_sp_definer(const ParseNode *parse_node,
ObRoutineInfo &routine_info)
{
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 {
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 (user_name != cur_user_name && !session_info_->has_user_super_privilege()) {
ret = OB_ERR_NO_PRIVILEGE;
LOG_WARN("no privilege", K(ret));
} else if (OB_ISNULL(host_node)) {
// 需要检查当前用户是否有超级权限或者set user id的权限,如果权限ok,那么host为%
if (session_info_->has_user_super_privilege()) {
host_name.assign_ptr("%", 1);
} else {
ret = OB_ERR_NO_PRIVILEGE;
LOG_WARN("no privilege", K(ret));
}
} else {
host_name.assign_ptr(host_node->str_value_, static_cast<int32_t>(host_node->str_len_));
// 显式指定host为%,需要当前用户有超级权限或者set user id的权限
if (0 == host_name.case_compare("%") && !session_info_->has_user_super_privilege()) {
ret = OB_ERR_NO_PRIVILEGE;
LOG_WARN("no privilege", K(ret));
}
}
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);
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(routine_info.set_priv_user(priv_user))) {
LOG_WARN("failed to set priv user", K(ret));
}
}
return ret;
}
int ObCreateRoutineResolver::resolve_sp_name(const ParseNode *parse_node,
obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
CK(OB_NOT_NULL(parse_node), OB_NOT_NULL(session_info_), OB_NOT_NULL(crt_routine_arg));
if (OB_SUCC(ret)) {
ObRoutineInfo &proc_info = crt_routine_arg->routine_info_;
ObString db_name, sp_name;
if (OB_FAIL(ObResolverUtils::resolve_sp_name(*session_info_, *parse_node, db_name, sp_name))) {
LOG_WARN("failed to resolve sp name", K(ret));
} else if (OB_FAIL(proc_info.set_routine_name(sp_name))) {
LOG_WARN("failed to set routine name", K(sp_name), K(ret));
} else {
crt_routine_arg->db_name_ = db_name;
}
}
return ret;
}
int ObCreateRoutineResolver::resolve_sp_body(const ParseNode *parse_node,
ObRoutineInfo &routine_info)
{
int ret = OB_SUCCESS;
CK(OB_NOT_NULL(parse_node));
if (OB_SUCC(ret)) {
ObString routine_body;
routine_body.assign_ptr(parse_node->str_value_, static_cast<int32_t>(parse_node->str_len_));
if (OB_FAIL(ObSQLUtils::convert_sql_text_to_schema_for_storing(
*allocator_, session_info_->get_dtc_params(), routine_body))) {
LOG_WARN("fail to convert charset", K(ret));
} else if (OB_FAIL(routine_info.set_routine_body(routine_body))) {
LOG_WARN("failed to set routine body", K(ret));
}
}
return ret;
}
int ObCreateRoutineResolver::set_routine_param(const ObIArray<ObObjAccessIdx> &access_idxs,
ObRoutineParam &routine_param)
{
int ret = OB_SUCCESS;
CK (ObObjAccessIdx::is_table_column(access_idxs)
|| ObObjAccessIdx::is_package_variable(access_idxs)
|| ObObjAccessIdx::is_table(access_idxs)
|| ObObjAccessIdx::is_pkg_type(access_idxs)
|| ObObjAccessIdx::is_udt_type(access_idxs));
CK (OB_NOT_NULL(params_.session_info_));
if (ObObjAccessIdx::is_table_column(access_idxs)) {
CK (2 == access_idxs.count() || 3 == access_idxs.count()); // table.col or db.table.col
OX (routine_param.set_param_type(ObExtendType));
OX (routine_param.set_table_col_type());
OX (routine_param.set_type_name(access_idxs.at(access_idxs.count() - 1).var_name_)); // table column name
OX (routine_param.set_type_subname(access_idxs.at(access_idxs.count() - 2).var_name_));
if (OB_FAIL(ret)) {
} else if (3 == access_idxs.count()) {
routine_param.set_type_owner(access_idxs.at(0).var_index_);
} else {
const ObTableSchema *table = NULL;
CK (OB_NOT_NULL(params_.schema_checker_));
OZ (params_.schema_checker_->get_table_schema(params_.session_info_->get_effective_tenant_id(), access_idxs.at(0).var_index_, table));
CK (OB_NOT_NULL(table));
OX (routine_param.set_type_owner(table->get_database_id()));
}
} else if (ObObjAccessIdx::is_package_variable(access_idxs)) {
CK (2 == access_idxs.count() || 3 == access_idxs.count()); // pkg.var or db.pkg.var
OX (routine_param.set_param_type(ObExtendType));
OX (routine_param.set_pkg_var_type());
OX (routine_param.set_type_name(access_idxs.at(access_idxs.count() - 1).var_name_));
OX (routine_param.set_type_subname(access_idxs.at(access_idxs.count() - 2).var_name_));
if (OB_FAIL(ret)) {
} else if (3 == access_idxs.count()) {
if (OB_SYS_TENANT_ID == get_tenant_id_by_object_id(access_idxs.at(1).var_index_)) {
OX (routine_param.set_type_owner(OB_SYS_DATABASE_ID));
} else {
OX (routine_param.set_type_owner(access_idxs.at(0).var_index_));
}
} else if (OB_SYS_TENANT_ID == get_tenant_id_by_object_id(access_idxs.at(0).var_index_)) { // 系统包中的var
OX (routine_param.set_type_owner(OB_SYS_DATABASE_ID));
}
} else if (ObObjAccessIdx::is_table(access_idxs)) {
CK (1 == access_idxs.count() || 2 == access_idxs.count());
OX (routine_param.set_param_type(ObExtendType));
OX (routine_param.set_table_row_type());
OX (routine_param.set_type_name(access_idxs.at(access_idxs.count() - 1).var_name_)); // table name
if (OB_FAIL(ret)) {
} else if (2 == access_idxs.count()) {
routine_param.set_type_owner(access_idxs.at(0).var_index_);
} else {
const ObTableSchema *table = NULL;
CK (OB_NOT_NULL(params_.schema_checker_));
OZ (params_.schema_checker_->get_table_schema(params_.session_info_->get_effective_tenant_id(), access_idxs.at(0).var_index_, table));
CK (OB_NOT_NULL(table));
OX (routine_param.set_type_owner(table->get_database_id()));
}
} else if (ObObjAccessIdx::is_pkg_type(access_idxs)) {
CK (access_idxs.count() >= 1 && access_idxs.count() <= 3);
if (OB_FAIL(ret)) {
} else {
OX (routine_param.set_param_type(ObExtendType));
OX (routine_param.set_pkg_type());
OX (routine_param.set_type_name(access_idxs.at(access_idxs.count()-1).var_name_));
if (2 == access_idxs.count()) { // pkg.type
OX (routine_param.set_type_subname(access_idxs.at(0).var_name_));
if (OB_SYS_TENANT_ID == get_tenant_id_by_object_id(access_idxs.at(0).var_index_)) { // 系统包中的type
OX (routine_param.set_type_owner(OB_SYS_DATABASE_ID));
}
} else if (3 == access_idxs.count()) { // db.pkg.type
OX (routine_param.set_type_subname(access_idxs.at(1).var_name_));
if (OB_SYS_TENANT_ID == get_tenant_id_by_object_id(access_idxs.at(1).var_index_)) {
OX (routine_param.set_type_owner(OB_SYS_DATABASE_ID));
} else {
OX (routine_param.set_type_owner(access_idxs.at(0).var_index_));
}
}
}
} else if (ObObjAccessIdx::is_udt_type(access_idxs)) {
CK (access_idxs.count() >= 1 && access_idxs.count() <= 2);
OX (routine_param.set_param_type(ObExtendType));
OX (routine_param.set_udt_type());
OX (routine_param.set_type_name(access_idxs.at(access_idxs.count()-1).var_name_));
if (OB_FAIL(ret)) {
} else if (2 == access_idxs.count()) {
if (OB_SYS_TENANT_ID == get_tenant_id_by_object_id(access_idxs.at(1).var_index_)) {
// system type, set owner is oceanbase
routine_param.set_type_owner(OB_SYS_DATABASE_ID);
} else {
routine_param.set_type_owner(access_idxs.at(0).var_index_);
}
} else if (OB_SYS_TENANT_ID == get_tenant_id_by_object_id(access_idxs.at(0).var_index_)) {
// system type, set owner is oceanbase
routine_param.set_type_owner(OB_SYS_DATABASE_ID);
}
}
return ret;
}
int ObCreateRoutineResolver::resolve_param_type(const ParseNode *type_node,
const ObString &param_name,
ObSQLSessionInfo &session_info,
ObRoutineParam &routine_param)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(type_node));
CK (OB_NOT_NULL(allocator_));
if (OB_SUCC(ret)) {
if (T_SP_ROWTYPE == type_node->type_
|| T_SP_TYPE == type_node->type_) { // %Type %RowType
ObArray<ObObjAccessIdx> access_idxs;
ObArray<ObObjAccessIdent> obj_access_idents;
CK (OB_LIKELY(1 == type_node->num_child_),
OB_NOT_NULL(type_node->children_[0]),
OB_LIKELY(T_SP_OBJ_ACCESS_REF == type_node->children_[0]->type_),
OB_NOT_NULL(schema_checker_),
OB_NOT_NULL(schema_checker_->get_schema_guard()),
OB_NOT_NULL(params_.expr_factory_),
OB_NOT_NULL(params_.sql_proxy_));
if (OB_FAIL(ret)) {
} else if (OB_FAIL(ObPLResolver::resolve_obj_access_node(*(type_node->children_[0]),
session_info,
*(params_.expr_factory_),
*(schema_checker_->get_schema_guard()),
*(params_.sql_proxy_),
obj_access_idents,
access_idxs))) {
// maybe dependent object not exist yet!
LOG_WARN("failed to transform from iparam", K(ret));
if (ObPLResolver::is_object_not_exist_error(ret)) {
ret = OB_SUCCESS;
ObArray<ObObjAccessIdent> obj_access_idents;
ObPLExternTypeInfo extern_type_info;
CK (OB_NOT_NULL(params_.expr_factory_));
OZ (ObPLResolver::resolve_obj_access_idents(*(type_node->children_[0]),
*(params_.expr_factory_),
obj_access_idents,
session_info));
OZ (ObPLResolver::resolve_extern_type_info(T_SP_ROWTYPE == type_node->type_,
*(schema_checker_->get_schema_guard()),
session_info,
obj_access_idents,
&extern_type_info));
OX (routine_param.set_param_type(ObExtendType));
OX (routine_param.set_type_owner(extern_type_info.type_owner_));
OX (routine_param.set_type_name(extern_type_info.type_name_));
OX (routine_param.set_type_subname(extern_type_info.type_subname_));
OX (routine_param.set_extern_type_flag(
static_cast<ObParamExternType>(extern_type_info.flag_)));
if (OB_SUCC(ret)) {
LOG_USER_ERROR(OB_ERR_SP_UNDECLARED_TYPE,
extern_type_info.type_name_.length(), extern_type_info.type_name_.ptr());
}
}
} else {
uint64_t owner_id = OB_INVALID_ID;
CK (!session_info.get_database_name().empty());
OZ (schema_checker_->get_database_id(session_info.get_effective_tenant_id(),
session_info.get_database_name(),
owner_id));
OX (routine_param.set_type_owner(owner_id));
CK (OB_LIKELY(access_idxs.count() > 0));
if (OB_SUCC(ret)) {
if (T_SP_TYPE == type_node->type_) {
if (ObObjAccessIdx::is_table_column(access_idxs)
|| ObObjAccessIdx::is_package_variable(access_idxs)) {
OZ (set_routine_param(access_idxs, routine_param));
} else {
ret = OB_ERR_TYPE_DECL_ILLEGAL;
LOG_WARN("PLS-00206: %TYPE must be applied to a variable, column, field or attribute",
K(ret), K(access_idxs));
}
} else if (ObObjAccessIdx::is_table(access_idxs) ||
ObObjAccessIdx::is_package_cursor_variable(access_idxs)) {
OZ (set_routine_param(access_idxs, routine_param));
} else {
ret = OB_ERR_WRONG_ROWTYPE;
LOG_WARN("PLS-00310: with %ROWTYPE attribute, ident must name a table, cursor or cursor-variable",
K(ret), K(access_idxs));
}
}
}
} else if (T_SP_OBJ_ACCESS_REF == type_node->type_) { // User Define Type
ObArray<ObObjAccessIdx> access_idxs;
ObArray<ObObjAccessIdent> obj_access_idents;
uint64_t current_db_id = OB_INVALID_ID;
CK (OB_NOT_NULL(schema_checker_),
OB_NOT_NULL(schema_checker_->get_schema_guard()));
CK (OB_NOT_NULL(params_.expr_factory_));
OZ (schema_checker_->get_database_id(session_info.get_effective_tenant_id(),
session_info.get_database_name(),
current_db_id));
if (OB_FAIL(ret)) {
} else if (OB_FAIL(ObPLResolver::resolve_obj_access_node(
*(type_node),
session_info,
*(params_.expr_factory_),
*(schema_checker_->get_schema_guard()),
*(params_.sql_proxy_),
obj_access_idents,
access_idxs))) {
// maybe dependent object not exist yet!
LOG_WARN("failed to transform from iparam", K(ret));
if (ObPLResolver::is_object_not_exist_error(ret)) {
ret = OB_SUCCESS;
ObArray<ObObjAccessIdent> obj_access_idents;
ObPLExternTypeInfo extern_type_info;
CK (OB_NOT_NULL(params_.expr_factory_));
OZ (ObPLResolver::resolve_obj_access_idents(*(type_node),
*(params_.expr_factory_),
obj_access_idents,
session_info));
if (OB_FAIL(ret)) {
} else if (1 == obj_access_idents.count()
&& 0 == obj_access_idents.at(0).access_name_.case_compare("SYS_REFCURSOR")) {
routine_param.set_type_name(obj_access_idents.at(0).access_name_);
routine_param.set_sys_refcursor_type();
} else if (1 == obj_access_idents.count()) { //udt
routine_param.set_type_name(obj_access_idents.at(0).access_name_);
routine_param.set_type_owner(current_db_id);
routine_param.set_udt_type();
} else if (3 == obj_access_idents.count()) { //db.pkg.type
uint64_t owner_id = OB_INVALID_ID;
OZ (schema_checker_->get_database_id(session_info.get_effective_tenant_id(),
obj_access_idents.at(0).access_name_,
owner_id));
OX (routine_param.set_type_name(obj_access_idents.at(2).access_name_));
OX (routine_param.set_type_owner(owner_id));
OX (routine_param.set_type_subname(obj_access_idents.at(1).access_name_));
OX (routine_param.set_pkg_type());
} else if (2 == obj_access_idents.count()) {//db.type or pkg.type
bool exist = false;
uint64_t owner_id = OB_INVALID_ID;
OZ (schema_checker_->get_schema_guard()->check_database_exist(
session_info.get_effective_tenant_id(),
obj_access_idents.at(0).access_name_,
exist,
&owner_id));
if (OB_FAIL(ret)) {
} else if (exist) {
routine_param.set_type_name(obj_access_idents.at(1).access_name_);
routine_param.set_type_owner(owner_id);
routine_param.set_udt_type();
} else {
routine_param.set_type_name(obj_access_idents.at(1).access_name_);
routine_param.set_type_owner(current_db_id);
routine_param.set_type_subname(obj_access_idents.at(0).access_name_);
routine_param.set_pkg_type();
}
}
OX (routine_param.set_param_type(ObExtendType));
}
} else {
CK (ObObjAccessIdx::is_pkg_type(access_idxs) || ObObjAccessIdx::is_udt_type(access_idxs));
OZ (set_routine_param(access_idxs, routine_param));
if (OB_SUCC(ret)
&& routine_param.is_extern_type()
&& OB_INVALID_ID == routine_param.get_type_owner()) {
OX (routine_param.set_type_owner(current_db_id));
}
}
} else { // Basic Type
ObPLDataType data_type;
data_type.reset();
OZ (ObPLResolver::resolve_sp_scalar_type(*allocator_,
type_node,
param_name,
*session_info_,
data_type,
param_name.empty() ? false : true));
if (OB_SUCC(ret) && data_type.is_pl_integer_type()) {
routine_param.set_pl_integer_type(data_type.get_pl_integer_type());
}
if (OB_SUCC(ret) && OB_NOT_NULL(data_type.get_data_type())
&& data_type.get_data_type()->get_charset_type() == CHARSET_ANY) {
ret = OB_NOT_SUPPORTED;
LOG_USER_ERROR(OB_NOT_SUPPORTED,
"character set ANY_CS not supported in standalone function/procedure");
LOG_WARN("character set ANY_CS not supported in standalone function/procedure", K(ret));
}
CK (OB_NOT_NULL(data_type.get_data_type()));
OZ (routine_param.set_extended_type_info(data_type.get_type_info()));
OX (routine_param.set_param_type(*(data_type.get_data_type())));
}
}
return ret;
}
int ObCreateRoutineResolver::analyze_expr_type(ObRawExpr *&expr,
ObRoutineInfo &routine_info)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(expr));
if (OB_FAIL(ret)) {
} else if (T_OP_GET_PACKAGE_VAR == expr->get_expr_type()) {
OX (routine_info.set_rps());
} else if (T_FUN_UDF == expr->get_expr_type()) {
OX (routine_info.set_external_state());
} else {
for (int64_t i = 0;
OB_SUCC(ret) &&
(!routine_info.is_rps() || !routine_info.is_external_state()) &&
i < expr->get_param_count();
++i) {
OZ (analyze_expr_type(expr->get_param_expr(i), routine_info));
}
}
return ret;
}
int ObCreateRoutineResolver::resolve_param_list(const ParseNode *param_list, ObRoutineInfo &routine_info)
{
int ret = OB_SUCCESS;
ObString param_name;
ObRoutineParam routine_param;
ObPLDataType data_type;
CK(OB_NOT_NULL(session_info_));
if (OB_SUCC(ret) && param_list != NULL) {
CK(OB_LIKELY(T_SP_PARAM_LIST == param_list->type_));
CK(OB_NOT_NULL(param_list->children_));
if (param_list->num_child_ > OB_MAX_PROC_PARAM_COUNT) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("too many formal parameters, max number of formal parameters"
"in an explicit cursor, function, or procedure is 65536!",
K(ret), K(OB_MAX_PROC_PARAM_COUNT));
LOG_USER_ERROR(OB_NOT_SUPPORTED, "number of formal parameters large than 65536");
}
for (int64_t i = 0; OB_SUCC(ret) && i < param_list->num_child_; ++i) {
const ParseNode *param_node = param_list->children_[i];
const ParseNode *name_node = NULL;
const ParseNode *type_node = NULL;
CK(OB_NOT_NULL(param_node));
CK(OB_LIKELY(T_SP_PARAM == param_node->type_));
CK(OB_NOT_NULL(param_node->children_));
CK(OB_NOT_NULL(name_node = param_node->children_[0]));
CK(OB_NOT_NULL(type_node = param_node->children_[1]));
routine_param.reset();
data_type.reset();
if (OB_SUCC(ret)) {
routine_param.set_tenant_id(session_info_->get_effective_tenant_id());
routine_param.set_sequence(routine_info.is_procedure()?i+1:i+2);
routine_param.set_subprogram_id(routine_info.get_subprogram_id());
routine_param.set_param_position(i+1);
routine_param.set_param_level(0); //todo user defined type guangang.gg
param_name.assign_ptr(name_node->str_value_, static_cast<int32_t>(name_node->str_len_));
if (OB_FAIL(check_dup_routine_param(routine_info.get_routine_params(), param_name))) {
LOG_WARN("fail to check dup routine param", K(param_name), K(ret));
} else if (OB_FAIL(routine_param.set_param_name(param_name))) {
LOG_WARN("set param name failed", K(ret), K(param_name));
} else if (OB_FAIL(resolve_param_type(type_node,
param_name,
*session_info_,
routine_param))) {
LOG_WARN("failed to resolve param type", K(ret), K(param_name));
}
}
if (OB_SUCC(ret)) {
switch (param_node->int32_values_[0]) {
case MODE_IN:
routine_param.set_in_sp_param_flag();
break;
case MODE_OUT:
routine_param.set_out_sp_param_flag();
routine_info.set_has_out_param();
break;
case MODE_INOUT:
routine_param.set_inout_sp_param_flag();
routine_info.set_has_out_param();
break;
default:
ret = OB_ERR_UNEXPECTED;
LOG_WARN("sp inout flag is invalid", K(param_node->value_));
break;
}
}
if (OB_SUCC(ret) && 1 == param_node->int32_values_[1]) {
routine_param.set_nocopy_param();
}
// 设置default value expr str
if (OB_SUCC(ret)
&& 3 == param_node->num_child_ // oracle mode has default node
&& OB_NOT_NULL(param_node->children_[2])) {
if (lib::is_mysql_mode()) {
ret = OB_NOT_SUPPORTED;
LOG_WARN("stored procedure's paramlist not supported default value in mysql mode", K(ret), K(lbt()));
LOG_USER_ERROR(OB_NOT_SUPPORTED, "stored procedure's paramlist use default value in mysql mode");
} else {
const ParseNode *default_node = param_node->children_[2];
if (OB_UNLIKELY(default_node->type_ != T_SP_DECL_DEFAULT)
|| OB_ISNULL(default_node->children_[0])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("wrong default value node", K(ret));
} else if (!routine_param.is_in_sp_param()) {
ret = OB_ERR_OUT_PARAM_HAS_DEFAULT;
LOG_WARN("PLS-00230: out or in out parameter can not has default value", K(ret));
} else {
ObString default_value(static_cast<int32_t>(default_node->str_len_),
default_node->str_value_);
ObRawExpr *default_expr = NULL;
ObPLDataType pl_type;
ObObjType src_type = ObMaxType;
uint64_t src_type_id = OB_INVALID_ID;
ObRoutineMatchInfo::MatchInfo match_info;
OZ (pl::ObPLDataType::transform_from_iparam(&(routine_param),
*(schema_checker_->get_schema_guard()),
*(session_info_),
*(allocator_),
*(params_.sql_proxy_),
pl_type));
// 默认值在CreateRoutine阶段不需要计算,执行时计算,但是这里要resolve下,避免用户使用非法变量
OZ (pl::ObPLResolver::resolve_raw_expr(*(default_node->children_[0]),
params_,
default_expr,
false /*for_writer*/));
CK (OB_NOT_NULL(default_expr));
OZ (ObResolverUtils::get_type_and_type_id(default_expr, src_type, src_type_id));
OZ (ObResolverUtils::check_type_match(
params_, match_info, default_expr, src_type, src_type_id, pl_type));
OZ (ObSQLUtils::convert_sql_text_to_schema_for_storing(
*allocator_, session_info_->get_dtc_params(), default_value));
OZ (routine_param.set_default_value(default_value));
OX (match_info.need_cast_ ? routine_param.set_default_cast() : void(NULL));
OZ (analyze_expr_type(default_expr, routine_info));
}
}
}
if (OB_SUCC(ret)) {
if (OB_FAIL(routine_info.add_routine_param(routine_param))) {
LOG_WARN("add proc param failed", K(ret));
}
}
}
}
return ret;
}
int ObCreateRoutineResolver::resolve_clause_list(
const ParseNode *clause_list, share::schema::ObRoutineInfo &func_info)
{
int ret = OB_SUCCESS;
if (OB_ISNULL(clause_list)) { // do nothing ...
} else {
ObProcType proc_type =
ObRoutineType::ROUTINE_FUNCTION_TYPE == func_info.get_routine_type() ?
ObProcType::STANDALONE_FUNCTION
: ObRoutineType::ROUTINE_PROCEDURE_TYPE == func_info.get_routine_type() ?
ObProcType::STANDALONE_PROCEDURE
: ObProcType::INVALID_PROC_TYPE;
ObPLDataType ret_type;
if (func_info.is_function()) {
const ObRoutineParam *routine_param = NULL;
CK (OB_NOT_NULL(func_info.get_ret_info()));
OX (routine_param = static_cast<const ObRoutineParam*>(func_info.get_ret_info()));
CK (OB_NOT_NULL(routine_param));
OZ (pl::ObPLDataType::transform_from_iparam(routine_param,
*schema_checker_->get_schema_guard(),
*session_info_,
*allocator_,
*params_.sql_proxy_,
ret_type));
}
CK (proc_type != ObProcType::INVALID_PROC_TYPE);
OZ (ObPLResolver::resolve_sf_clause(clause_list, &func_info, proc_type, ret_type));
}
return ret;
}
int ObCreateRoutineResolver::resolve_ret_type(const ParseNode *ret_type_node,
share::schema::ObRoutineInfo &func_info)
{
int ret = OB_SUCCESS;
ObRoutineParam ret_type_param;
CK (OB_NOT_NULL(session_info_));
CK (OB_NOT_NULL(ret_type_node));
CHECK_COMPATIBILITY_MODE(session_info_);
OX (ret_type_param.set_tenant_id(session_info_->get_effective_tenant_id()));
OX (ret_type_param.set_sequence(ROUTINE_RET_TYPE_SEQUENCE));
OX (ret_type_param.set_param_position(ROUTINE_RET_TYPE_POSITION));
OX (ret_type_param.set_subprogram_id(func_info.get_subprogram_id()));
OX (ret_type_param.set_param_level(0));
OZ (resolve_param_type(ret_type_node, ObString(), *session_info_, ret_type_param));
OZ (func_info.add_routine_param(ret_type_param));
return ret;
}
int ObCreateRoutineResolver::resolve_replace(const ParseNode *parse_node, obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
CK(OB_NOT_NULL(crt_routine_arg), OB_NOT_NULL(parse_node));
if (OB_SUCC(ret)) {
crt_routine_arg->is_or_replace_ = parse_node->int32_values_[0];
}
return ret;
}
int ObCreateRoutineResolver::resolve_editionable(
const ParseNode *parse_node, obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(crt_routine_arg), OB_NOT_NULL(parse_node));
if (OB_SUCC(ret) && 1 == parse_node->int32_values_[1]) {
crt_routine_arg->routine_info_.set_noneditionable();
}
return ret;
}
int ObCreateRoutineResolver::resolve_aggregate_body(
const ParseNode *parse_node, ObRoutineInfo &routine_info)
{
int ret = OB_SUCCESS;
ObString db_name, type_name;
const share::schema::ObUDTTypeInfo *udt_info = NULL;
CK (OB_NOT_NULL(parse_node), OB_NOT_NULL(session_info_));
CK (OB_NOT_NULL(schema_checker_));
CK (T_SF_AGGREGATE_BODY == parse_node->type_);
CK (1 == parse_node->num_child_);
CK (OB_NOT_NULL(parse_node->children_[0]));
OZ (ObResolverUtils::resolve_sp_name(
*session_info_, *(parse_node->children_[0]), db_name, type_name));
OZ (schema_checker_->get_udt_info(
session_info_->get_effective_tenant_id(), db_name, type_name, udt_info));
if (OB_ISNULL(udt_info) // try sys udt
&& (db_name.case_compare(OB_ORA_SYS_SCHEMA_NAME)
|| db_name.case_compare(OB_SYS_DATABASE_NAME))) {
OZ (schema_checker_->get_udt_info(
OB_SYS_TENANT_ID, OB_SYS_DATABASE_NAME, type_name, udt_info));
}
if (OB_SUCC(ret) && OB_ISNULL(udt_info)) {
ret = OB_ERR_SP_UNDECLARED_VAR;
LOG_WARN("PLS-00201: identifier type_name must be declared",
K(ret), K(type_name), K(db_name));
LOG_USER_ERROR(OB_ERR_SP_UNDECLARED_VAR, type_name.length(), type_name.ptr());
}
OX (routine_info.set_type_id(udt_info->get_type_id()));
return ret;
}
int ObCreateRoutineResolver::resolve_impl(ObRoutineType routine_type,
const ParseNode *sp_definer_node,
const ParseNode *name_node,
const ParseNode *body_node,
const ParseNode *ret_node,
const ParseNode *param_node,
const ParseNode *clause_list,
obrpc::ObCreateRoutineArg *crt_routine_arg,
bool is_udt_udf)
{
int ret = OB_SUCCESS;
bool need_reset_default_database = false;
uint64_t old_database_id = OB_INVALID_ID;
ObSqlString old_database_name;
CK(OB_NOT_NULL(session_info_), OB_NOT_NULL(allocator_), OB_NOT_NULL(schema_checker_));
CHECK_COMPATIBILITY_MODE(session_info_);
CK (INVALID_ROUTINE_TYPE != routine_type);
OZ(resolve_sp_definer(sp_definer_node, crt_routine_arg->routine_info_));
OZ (resolve_sp_name(name_node, crt_routine_arg));
OZ (set_routine_info(routine_type, crt_routine_arg->routine_info_, is_udt_udf));
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(),
crt_routine_arg->db_name_,
stmt::T_CREATE_ROUTINE,
session_info_->get_enable_role_array()));
}
if (OB_SUCC(ret)) {
uint64_t database_id = OB_INVALID_ID;
const share::schema::ObDatabaseSchema *database_schema = NULL;
OZ (schema_checker_->get_schema_guard()->get_database_id(session_info_->get_effective_tenant_id(),
crt_routine_arg->db_name_,
database_id));
OZ (schema_checker_->get_schema_guard()->get_database_schema(session_info_->get_effective_tenant_id(),
database_id, database_schema));
if (OB_FAIL(ret) || OB_ISNULL(database_schema)) {
ret = OB_ERR_BAD_DATABASE;
LOG_WARN("fail to get database schema", K(ret));
LOG_USER_ERROR(OB_ERR_BAD_DATABASE, crt_routine_arg->db_name_.length(), crt_routine_arg->db_name_.ptr());
}
if (OB_SUCC(ret)
&& database_schema->get_database_name_str() != session_info_->get_database_name()) {
OZ (old_database_name.append(session_info_->get_database_name()));
OX (old_database_id = session_info_->get_database_id());
OZ (session_info_->set_default_database(database_schema->get_database_name_str()));
OX (session_info_->set_database_id(database_id));
OX (crt_routine_arg->routine_info_.set_database_id(database_id));
OX (need_reset_default_database = true);
}
}
OZ (resolve_sp_body(body_node, crt_routine_arg->routine_info_));
if (OB_SUCC(ret) && ROUTINE_FUNCTION_TYPE == routine_type) {
OZ (resolve_ret_type(ret_node, crt_routine_arg->routine_info_));
}
OZ (resolve_param_list(param_node, crt_routine_arg->routine_info_));
OZ (resolve_clause_list(clause_list, crt_routine_arg->routine_info_));
CK (OB_NOT_NULL(body_node));
if (OB_FAIL(ret)) {
} else if (T_SF_AGGREGATE_BODY == body_node->type_) {
if (crt_routine_arg->routine_info_.get_param_count() != 1) {
ret = OB_ERR_INCORRECT_ARGUMENTS;
LOG_WARN("PLS-00652: aggregate functions should have exactly one argument",
K(ret), K(crt_routine_arg->routine_info_.get_param_count()));
}
OZ (resolve_aggregate_body(body_node, crt_routine_arg->routine_info_));
OX (crt_routine_arg->routine_info_.set_is_aggregate());
} else {
OZ (analyze_router_sql(crt_routine_arg));
}
if (need_reset_default_database) {
int tmp_ret = OB_SUCCESS;
if (OB_SUCCESS != (tmp_ret = session_info_->set_default_database(old_database_name.string()))) {
ret = OB_SUCCESS == ret ? tmp_ret : ret; // 不覆盖错误码
LOG_ERROR("failed to reset default database", K(ret), K(tmp_ret), K(old_database_name));
} else {
session_info_->set_database_id(old_database_id);
}
}
return ret;
}
int ObCreateRoutineResolver::resolve(const ParseNode &parse_tree,
const ParseNode *sp_definer_node,
const ParseNode *name_node,
const ParseNode *body_node,
const ParseNode *ret_node,
const ParseNode *param_node,
const ParseNode *clause_list,
obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(crt_routine_arg));
CK (OB_NOT_NULL(session_info_));
CK (OB_NOT_NULL(allocator_));
CK (OB_NOT_NULL(schema_checker_));
CHECK_COMPATIBILITY_MODE(session_info_);
if (OB_SUCC(ret)) {
ObRoutineType type = T_SF_CREATE == parse_tree.type_ ? ROUTINE_FUNCTION_TYPE :
T_SP_CREATE == parse_tree.type_ ? ROUTINE_PROCEDURE_TYPE : INVALID_ROUTINE_TYPE;
if (lib::is_oracle_mode() && OB_FAIL(resolve_replace(&parse_tree, crt_routine_arg))) {
LOG_WARN("failed to resolve replace", K(ret));
} else if (lib::is_mysql_mode()
&& session_info_->is_inner()
&& FALSE_IT(crt_routine_arg->is_or_replace_ = true)) {
// MySQL模式下该字段被复用未是否是InnerSQL发送的请求。用于恢复MySQL下被放入回收站的Routine。
// Oracle模式下不做处理, 因为Oracle模式下PL对象不进入回收站。
} else if (lib::is_oracle_mode() && OB_FAIL(resolve_editionable(&parse_tree,
crt_routine_arg))) {
LOG_WARN("failed to resolve editionable", K(ret));
} else if (OB_FAIL(resolve_impl(type,
sp_definer_node,
name_node,
body_node,
ret_node,
param_node,
clause_list,
crt_routine_arg))) {
LOG_WARN("failed to resolve routine info", K(ret));
} else { /*do nothing*/ }
}
return ret;
}
int ObCreateRoutineResolver::resolve(const ParseNode &parse_tree)
{
int ret = OB_SUCCESS;
obrpc::ObCreateRoutineArg *crt_routine_arg = NULL;
OZ (create_routine_arg(crt_routine_arg));
if (OB_SUCC(ret) && OB_ISNULL(crt_routine_arg)) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_WARN("allocate memory for create routine stmt failed", K(ret));
}
OZ (resolve_impl(parse_tree, crt_routine_arg));
if (OB_SUCC(ret)) {
ObErrorInfo &error_info = crt_routine_arg->error_info_;
ObRoutineInfo &routine_info = crt_routine_arg->routine_info_;
error_info.collect_error_info(&routine_info);
}
return ret;
}
int ObCreateProcedureResolver::resolve_impl(
const ParseNode &parse_tree, obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(crt_routine_arg));
CK(OB_NOT_NULL(session_info_),
OB_LIKELY(T_SP_CREATE == parse_tree.type_), OB_NOT_NULL(parse_tree.children_),
OB_LIKELY((5 == parse_tree.num_child_ && !lib::is_oracle_mode())
|| (1 == parse_tree.num_child_
&& lib::is_oracle_mode()
&& NULL != parse_tree.children_[0]
&& T_SP_SOURCE == parse_tree.children_[0]->type_
&& 4 == parse_tree.children_[0]->num_child_)));
if (OB_SUCC(ret)) {
bool is_oracle_compatible = lib::is_oracle_mode();
const ParseNode &source_tree = is_oracle_compatible ? *parse_tree.children_[0] : parse_tree;
const ParseNode *sp_definer_node = is_oracle_compatible ? nullptr : source_tree.children_[0];
const ParseNode *sp_name_node = source_tree.children_[is_oracle_compatible ? 0 : 1];
const ParseNode *body_node = source_tree.children_[is_oracle_compatible ? 3 : 4];
const ParseNode *param_node = source_tree.children_[is_oracle_compatible ? 1 : 2];
const ParseNode *clause_node = source_tree.children_[is_oracle_compatible ? 2 : 3];
if (OB_FAIL(ObCreateRoutineResolver::resolve(parse_tree, sp_definer_node, sp_name_node,
body_node, NULL, param_node, clause_node,
crt_routine_arg))) {
LOG_WARN("failed to resolve sp body", K(ret));
}
}
return ret;
}
int ObCreateFunctionResolver::resolve_impl(
const ParseNode &parse_tree, obrpc::ObCreateRoutineArg *crt_routine_arg)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(crt_routine_arg));
CK (OB_NOT_NULL(session_info_));
CK (OB_NOT_NULL(allocator_));
CK (OB_NOT_NULL(schema_checker_));
CK (OB_LIKELY(T_SF_CREATE == parse_tree.type_));
CK (OB_NOT_NULL(parse_tree.children_));
CK (OB_LIKELY((6 == parse_tree.num_child_ && !lib::is_oracle_mode())
|| (1 == parse_tree.num_child_
&& lib::is_oracle_mode()
&& NULL != parse_tree.children_[0])));
if (OB_SUCC(ret)
&& lib::is_oracle_mode()) {
CK (T_SF_SOURCE == parse_tree.children_[0]->type_
|| T_SF_AGGREGATE_SOURCE == parse_tree.children_[0]->type_);
CK (6 == parse_tree.children_[0]->num_child_);
}
if (OB_SUCC(ret)) {
bool is_oracle_compatible = lib::is_oracle_mode();
const ParseNode &source_tree = is_oracle_compatible ? *parse_tree.children_[0] : parse_tree;
const ParseNode *sp_definer_node = is_oracle_compatible ? nullptr : source_tree.children_[0];
ParseNode *sf_name_node = source_tree.children_[is_oracle_compatible ? 0 : 1];
ParseNode *body_node = source_tree.children_[5];
ParseNode *param_node = source_tree.children_[is_oracle_compatible ? 1 : 2];
ParseNode *ret_node = source_tree.children_[is_oracle_compatible ? 2 : 3];
ParseNode *sf_clause_list = source_tree.children_[is_oracle_compatible ? 3 : 4];
ParseNode *pipelined_clase = is_oracle_compatible ? source_tree.children_[4] : NULL;
if (OB_FAIL(ObCreateRoutineResolver::resolve(parse_tree,
sp_definer_node,
sf_name_node,
body_node,
ret_node,
param_node,
sf_clause_list,
crt_routine_arg))) {
LOG_WARN("failed to resolve sp body", K(ret));
}
}
return ret;
}
}
}