Files
oceanbase/src/sql/resolver/cmd/ob_call_procedure_resolver.cpp

466 lines
20 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_call_procedure_resolver.h"
#include "ob_call_procedure_stmt.h"
#include "sql/resolver/ob_resolver_utils.h"
#include "sql/resolver/expr/ob_raw_expr_util.h"
#include "sql/resolver/dml/ob_select_resolver.h"
#include "pl/ob_pl_stmt.h"
#include "pl/ob_pl_package.h"
#include "observer/ob_req_time_service.h"
#include "pl/ob_pl_compile.h"
namespace oceanbase
{
using namespace common;
using namespace share::schema;
namespace sql
{
int ObCallProcedureResolver::check_param_expr_legal(ObRawExpr *param)
{
int ret = OB_SUCCESS;
if (OB_NOT_NULL(param)) {
if (T_REF_QUERY == param->get_expr_type()) {
ret = OB_NOT_SUPPORTED;
LOG_USER_ERROR(OB_NOT_SUPPORTED, "subqueries or stored function calls here");
} else if (T_FUN_SYS_PL_SEQ_NEXT_VALUE == param->get_expr_type()) {
ret = OB_NOT_SUPPORTED;
LOG_USER_ERROR(OB_NOT_SUPPORTED, "ORA-06576 : not a valid function or procedure name");
} /* else if (T_OP_GET_PACKAGE_VAR == param->get_expr_type()) {
ret = OB_NOT_SUPPORTED;
LOG_USER_ERROR(OB_NOT_SUPPORTED, "PLS-221: not procedure or not defined!");
} */
for (int64_t i = 0; OB_SUCC(ret) && i < param->get_param_count(); ++i) {
OZ (check_param_expr_legal(param->get_param_expr(i)));
}
}
return ret;
}
int ObCallProcedureResolver::resolve_cparams(const ParseNode *params_node,
const ObRoutineInfo *routine_info,
ObCallProcedureStmt *stmt)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(routine_info));
CK (OB_NOT_NULL(stmt));
ObArray<ObRawExpr*> params;
// Step 1: 初始化参数列表
for (int64_t i = 0; OB_SUCC(ret) && i < routine_info->get_param_count(); ++i) {
OZ (params.push_back(NULL));
}
// Step 2: 从ParamsNode中解析参数
if (OB_SUCC(ret) && OB_NOT_NULL(params_node)) {
bool has_assign_param = false;
if (T_SP_CPARAM_LIST != params_node->type_) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid params list node", K(ret), K(params_node->type_));
}
for (int64_t i = 0; OB_SUCC(ret) && i < params_node->num_child_; ++i) {
if (OB_ISNULL(params_node->children_[i])) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("param node is NULL", K(i), K(ret));
} else if (T_SP_CPARAM == params_node->children_[i]->type_) {
has_assign_param = true;
if (OB_FAIL(resolve_cparam_with_assign(params_node->children_[i], routine_info, params))) {
LOG_WARN("failed to resolve cparam with assign", K(ret));
}
} else if (has_assign_param) {
ret = OB_ERR_SP_WRONG_ARG_NUM;
LOG_WARN("can not set param without assign after param with assign", K(ret));
} else if (OB_FAIL(resolve_cparam_without_assign(params_node->children_[i], i, params))) {
LOG_WARN("failed to resolve cparam without assign", K(ret), K(i));
}
}
}
// Step 3: 处理空缺参数, 如果有默认值填充默认值, 否则报错
for (int64_t i = 0; OB_SUCC(ret) && i < params.count(); ++i) {
ObConstRawExpr *default_expr = NULL;
if (OB_ISNULL(params.at(i))) { // 空缺参数
params_.is_default_param_ = true;
ObRoutineParam *routine_param = routine_info->get_routine_params().at(i);
CK (OB_NOT_NULL(routine_param));
if (OB_SUCC(ret) && routine_param->get_default_value().empty()) {
ret = OB_ERR_SP_WRONG_ARG_NUM;
LOG_WARN("routine param dese not has default value", K(ret));
}
CK (OB_NOT_NULL(params_.expr_factory_));
OZ (ObRawExprUtils::build_const_int_expr(
*(params_.expr_factory_), ObIntType, 0, default_expr));
CK (OB_NOT_NULL(default_expr));
OZ (default_expr->add_flag(IS_PL_MOCK_DEFAULT_EXPR));
OX (params.at(i) = default_expr);
}
}
// Step 4: 将参数数组设置到stmt中
OZ (stmt->add_params(params));
if (OB_SUCC(ret)) { // 判断所有参数没有复杂表达式参数
bool v = true;
for (int64_t i = 0; v && OB_SUCC(ret) && i < params.count(); i ++) {
if (OB_ISNULL(params.at(i))) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("invalid argument", K(ret));
} else if (params.at(i)->is_const_raw_expr()) {
const ObConstRawExpr *const_expr = static_cast<const ObConstRawExpr *>(params.at(i));
if (T_QUESTIONMARK != const_expr->get_expr_type()) {
v = false;
}
} else {
v = false;
}
} // for end
stmt->set_can_direct_use_param(v);
}
return ret;
}
int ObCallProcedureResolver::resolve_cparam_without_assign(const ParseNode *param_node,
const int64_t position,
ObIArray<ObRawExpr*> &params)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(param_node));
ObRawExpr *param = NULL;
if (OB_FAIL(ret)) {
} else if (position < 0 || position >= params.count()) {
ret = OB_ERR_SP_WRONG_ARG_NUM;
LOG_WARN("wrong argument number", K(ret), K(position), K(params.count()));
} else if (OB_NOT_NULL(params.at(position))) {
ret = OB_ERR_SP_DUP_PARAM;
LOG_WARN("dup params", K(ret), K(position));
} else if (OB_FAIL(pl::ObPLResolver::resolve_raw_expr(*param_node, params_, param))) {
LOG_WARN("failed to resolve const expr", K(ret));
} else if (OB_ISNULL(param)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("param expr is null", K(ret), K(param));
} else if (OB_FAIL(check_param_expr_legal(param))) {
LOG_WARN("failed to check param expr legal", K(ret), KPC(param));
} else if (T_OP_ROW == param->get_expr_type() && 1 != param->get_param_count()) {
ret = OB_ERR_INVALID_COLUMN_NUM;
LOG_USER_ERROR(OB_ERR_INVALID_COLUMN_NUM, static_cast<int64_t>(1));
LOG_WARN("op_row input param count is not 1", K(param->get_param_count()), K(ret));
} else {
params.at(position) = param;
}
return ret;
}
int ObCallProcedureResolver::resolve_cparam_with_assign(const ParseNode *param_node,
const ObRoutineInfo* routine_info,
ObIArray<ObRawExpr*> &params)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(param_node));
CK (OB_NOT_NULL(routine_info));
CK (OB_LIKELY(2 == param_node->num_child_));
CK (OB_NOT_NULL(param_node->children_[0]));
CK (OB_NOT_NULL(param_node->children_[1]));
if (OB_SUCC(ret)) {
const ParseNode *name_node = NULL;
if (T_OBJ_ACCESS_REF == param_node->children_[0]->type_) {
CK (OB_LIKELY(2 == param_node->children_[0]->num_child_));
CK (OB_NOT_NULL(param_node->children_[0]->children_[0]));
CK (OB_ISNULL(param_node->children_[0]->children_[1]));
CK (OB_LIKELY(T_IDENT == param_node->children_[0]->children_[0]->type_));
name_node = param_node->children_[0]->children_[0];
} else if (T_IDENT == param_node->children_[0]->type_) {
name_node = param_node->children_[0];
} else if (T_COLUMN_REF == param_node->children_[0]->type_ &&
3 == param_node->children_[0]->num_child_ &&
NULL == param_node->children_[0]->children_[0] &&
NULL == param_node->children_[0]->children_[1] &&
T_IDENT == param_node->children_[0]->children_[2]->type_) {
name_node = param_node->children_[0]->children_[2];
} else {
ret = OB_ERR_CALL_WRONG_ARG;
LOG_WARN("PLS-00306: wrong number or types of arguments in call", K(ret));
}
if (OB_SUCC(ret)) {
ObString name = ObString(static_cast<int32_t>(name_node->str_len_), name_node->str_value_);
int64_t position = -1;
if (OB_FAIL(routine_info->find_param_by_name(name, position))) {
LOG_WARN("failed to find param name in proc info", K(ret), K(name), K(*routine_info));
} else if (OB_UNLIKELY(-1 == position)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("invalid postition value", K(ret), K(position));
} else if (OB_FAIL(resolve_cparam_without_assign(param_node->children_[1], position, params))) {
LOG_WARN("failed to resolve cparam without assign", K(ret));
}
}
}
return ret;
}
int ObCallProcedureResolver::resolve_param_exprs(const ParseNode *params_node,
ObIArray<ObRawExpr*> &expr_params)
{
int ret = OB_SUCCESS;
CK (OB_NOT_NULL(params_node));
CK (T_SP_CPARAM_LIST == params_node->type_);
CK (OB_NOT_NULL(params_.session_info_));
for (int64_t i = 0; OB_SUCC(ret) && i < params_node->num_child_; ++i) {
ObRawExpr* raw_expr = NULL;
CK (OB_NOT_NULL(params_node->children_[i]));
if (OB_SUCC(ret) && params_.is_execute_call_stmt_) {
ObArray<ObQualifiedName> columns;
ObArray<ObVarInfo> sys_vars;
ObArray<ObAggFunRawExpr*> aggr_exprs;
ObArray<ObWinFunRawExpr*> win_exprs;
ObArray<ObSubQueryInfo> sub_query_info;
ObArray<ObUDFInfo> udf_info;
ObArray<ObOpRawExpr*> op_exprs;
if (OB_FAIL(ObRawExprUtils::build_raw_expr(*params_.expr_factory_,
*params_.session_info_,
params_.schema_checker_,
params_.secondary_namespace_,
T_PL_SCOPE,
NULL/*ObStmt*/,
params_.param_list_,
NULL/*external_param_info*/,
*params_node->children_[i],
raw_expr,
columns,
sys_vars,
aggr_exprs,
win_exprs,
sub_query_info,
udf_info,
op_exprs,
true,
static_cast<TgTimingEvent>(params_.tg_timing_event_)))) {
LOG_WARN("failed to build raw expr", K(ret));
}
} else {
OZ (pl::ObPLResolver::resolve_raw_expr(*params_node->children_[i], params_, raw_expr));
}
CK (OB_NOT_NULL(raw_expr));
OZ (check_param_expr_legal(raw_expr));
OZ (expr_params.push_back(raw_expr));
}
return ret;
}
int ObCallProcedureResolver::resolve(const ParseNode &parse_tree)
{
int ret = OB_SUCCESS;
ObCallProcedureStmt *stmt = NULL;
ParseNode *name_node = parse_tree.children_[0];
ParseNode *params_node = parse_tree.children_[1];
ObString db_name;
ObString package_name;
ObString sp_name;
const ObRoutineInfo *proc_info = NULL;
if (OB_ISNULL(schema_checker_) || OB_ISNULL(session_info_)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("argument is NULL", K(schema_checker_), K(session_info_), K(ret));
} else if (OB_UNLIKELY(T_SP_CALL_STMT != parse_tree.type_ || OB_ISNULL(name_node))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("the children of parse tree is NULL", K(parse_tree.type_), K(name_node), K(ret));
} else if (OB_ISNULL(stmt = create_stmt<ObCallProcedureStmt>())) {
ret = OB_ALLOCATE_MEMORY_FAILED;
LOG_ERROR("create call stmt failed", K(ret));
} else {
stmt_ = stmt;
}
// 解析过程名称
if (OB_SUCC(ret)) {
if (T_SP_ACCESS_NAME != name_node->type_) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Invalid procedure name node", K(name_node->type_), K(ret));
} else {
if (OB_FAIL(ObResolverUtils::resolve_sp_access_name(*schema_checker_,
session_info_->get_effective_tenant_id(),
session_info_->get_database_name(),
*name_node,
db_name, package_name, sp_name))) {
LOG_WARN("resolve sp name failed", K(ret));
} else if (db_name.empty() && session_info_->get_database_name().empty()) {
ret = OB_ERR_NO_DB_SELECTED;
LOG_WARN("no database selected", K(ret), K(db_name));
} else {
if (!db_name.empty()) {
OX (stmt->set_db_name(db_name));
} else {
OX (stmt->set_db_name(session_info_->get_database_name()));
}
}
}
}
ObSEArray<ObRawExpr*, 16> expr_params;
// 获取routine schem info
if (OB_SUCC(ret)) {
if (OB_NOT_NULL(params_node)
&& OB_FAIL(resolve_param_exprs(params_node, expr_params))) {
LOG_WARN("failed to resolve param exprs", K(ret));
} else if (OB_FAIL(ObResolverUtils::get_routine(params_,
(*session_info_).get_effective_tenant_id(),
(*session_info_).get_database_name(),
db_name,
package_name,
sp_name,
ROUTINE_PROCEDURE_TYPE,
expr_params,
proc_info))) {
LOG_WARN("failed to get routine info", K(ret), K(db_name), K(package_name), K(sp_name));
} else if (OB_ISNULL(proc_info)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("proc info is null", K(ret), K(db_name), K(package_name), K(sp_name), K(proc_info));
} else if (proc_info->has_accessible_by_clause()) {
ret = OB_ERR_MISMATCH_SUBPROGRAM;
LOG_WARN("PLS-00263: mismatch between string on a subprogram specification and body",
K(ret), KPC(proc_info));
}
if (OB_SUCC(ret) && proc_info->is_udt_routine() && !proc_info->is_udt_static_routine()) {
ret = OB_ERR_CALL_WRONG_ARG;
LOG_USER_ERROR(OB_ERR_CALL_WRONG_ARG, proc_info->get_routine_name().length(),
proc_info->get_routine_name().ptr());
}
if (OB_SUCC(ret) && proc_info->is_udt_routine()) {
stmt->set_is_udt_routine(true);
}
if (OB_SUCC(ret)) {
ObSchemaObjVersion obj_version;
obj_version.object_id_ = proc_info->get_routine_id();
obj_version.object_type_ = DEPENDENCY_PROCEDURE;
obj_version.version_ = proc_info->get_schema_version();
OZ (stmt->add_global_dependency_table(obj_version));
}
}
// 解析参数列表
// if (OB_SUCC(ret) && params_.is_execute_call_stmt_) {
// OZ (stmt->add_params(expr_params));
// OX (stmt->set_can_direct_use_param(true));
// } else {
OZ (resolve_cparams(params_node, proc_info, stmt));
// }
if (OB_SUCC(ret)) {
if (OB_INVALID_ID == proc_info->get_package_id()) {
//standalone procedure
stmt->set_package_id(proc_info->get_package_id());
stmt->set_routine_id(proc_info->get_routine_id());
} else {
//package procedure
stmt->set_package_id(proc_info->get_package_id());
stmt->set_routine_id(proc_info->get_subprogram_id());
}
for (int64_t i = 0; OB_SUCC(ret) && i < proc_info->get_param_count(); ++i) {
const ObRoutineParam *param_info = proc_info->get_routine_params().at(i);
const ObRawExpr *param_expr = stmt->get_params().at(i);
pl::ObPLDataType pl_type;
CK (OB_NOT_NULL(param_info));
CK (OB_NOT_NULL(param_expr));
if (OB_SUCC(ret)) {
CK (OB_NOT_NULL(schema_checker_->get_schema_mgr()));
CK (OB_NOT_NULL(params_.sql_proxy_));
CK (OB_NOT_NULL(params_.allocator_));
CK (OB_NOT_NULL(session_info_));
OZ (pl::ObPLDataType::transform_from_iparam(param_info,
*(schema_checker_->get_schema_mgr()),
*(session_info_),
*(params_.allocator_),
*(params_.sql_proxy_),
pl_type));
if (OB_FAIL(ret)) {
} else if (params_.is_prepare_protocol_
&& params_.is_prepare_stage_
&& param_expr->get_expr_type() != T_QUESTIONMARK) {
// do nothing ...
} else if (!param_info->is_extern_type()) {
} else {
if (OB_SUCC(ret)) {
//not support complex param not in prepare, except default value
if (pl_type.is_user_type()) {
if (!params_.is_prepare_protocol_
&& !param_expr->has_flag(IS_PL_MOCK_DEFAULT_EXPR)) {
ret = OB_ERR_CALL_WRONG_ARG;
LOG_WARN("PLS-00306: wrong number or types of arguments in call stmt", K(ret));
}
}
}
}
}
if (OB_SUCC(ret)) {
if (param_info->is_out_sp_param() || param_info->is_inout_sp_param()) {
const ObRawExpr* param = stmt->get_params().at(i);
if (lib::is_mysql_mode()
&& param->get_expr_type() != T_OP_GET_USER_VAR
&& param->get_expr_type() != T_OP_GET_SYS_VAR) {
ret = OB_ER_SP_NOT_VAR_ARG;
LOG_USER_ERROR(OB_ER_SP_NOT_VAR_ARG, static_cast<int32_t>(i), static_cast<int32_t>(sp_name.length()), sp_name.ptr());
LOG_WARN("OUT or INOUT argument for routine is not a variable", K(param->get_expr_type()), K(ret));
} else if (param_info->is_sys_refcursor_type()
|| (param_info->is_pkg_type() && pl_type.is_cursor_type())) {
OZ (stmt->add_out_param(i,
param_info->get_mode(),
param_info->get_param_name(),
pl_type,
ObString("SYS_REFCURSOR"),
ObString("")));
} else if (param_info->is_complex_type()) { // UDT
if (param_info->get_type_owner() == session_info_->get_database_id()) {
CK (!session_info_->get_database_name().empty());
OZ (stmt->add_out_param(i,
param_info->get_mode(),
param_info->get_param_name(),
pl_type,
param_info->get_type_name(),
session_info_->get_database_name()));
} else {
const ObDatabaseSchema *db_schema = NULL;
CK (OB_NOT_NULL(schema_checker_));
CK (OB_NOT_NULL(schema_checker_->get_schema_mgr()));
OZ (schema_checker_->get_schema_mgr()->get_database_schema(param_info->get_tenant_id(),
param_info->get_type_owner(), db_schema), param_info->get_type_owner());
if (OB_SUCC(ret) && OB_ISNULL(db_schema)) {
ret = OB_ERR_BAD_DATABASE;
LOG_WARN("failed to get type owner", K(param_info->get_type_owner()));
}
OZ (stmt->add_out_param(i,
param_info->get_mode(),
param_info->get_param_name(),
pl_type,
param_info->get_type_name(),
OB_SYS_TENANT_ID == db_schema->get_tenant_id()
? ObString("SYS") : db_schema->get_database_name_str()), i);
}
} else if (pl_type.is_user_type()) {
// 通过Call语句执行PL且参数是复杂类型的情况, 仅在PS模式支持, 通过客户端无法构造复杂数据类型;
// PS模式仅支持UDT作为出参, 这里将其他模式的复杂类型出参禁掉;
ret = OB_NOT_SUPPORTED;
LOG_WARN("not supported other type as out parameter except udt", K(ret), K(pl_type.is_user_type()));
LOG_USER_ERROR(OB_NOT_SUPPORTED, "other complex type as out parameter except user define type");
} else {
OZ (stmt->add_out_param(i,
param_info->get_mode(),
param_info->get_param_name(),
pl_type,
param_info->get_type_name(),
ObString("")));
}
}
}
}
}
return ret;
}
}
}