400 lines
13 KiB
C++
400 lines
13 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_ENG
|
|
|
|
#include "ob_expr_plsql_variable.h"
|
|
#include "lib/utility/ob_print_utils.h"
|
|
#include "sql/engine/ob_physical_plan_ctx.h"
|
|
#include "sql/engine/ob_exec_context.h"
|
|
#include "pl/ob_pl.h"
|
|
#include "sql/engine/expr/ob_expr_lob_utils.h"
|
|
|
|
namespace oceanbase
|
|
{
|
|
using namespace common;
|
|
using namespace pl;
|
|
|
|
namespace sql
|
|
{
|
|
|
|
OB_SERIALIZE_MEMBER((ObExprPLSQLVariable, ObFuncExprOperator), plsql_line_, plsql_variable_);
|
|
|
|
ObExprPLSQLVariable::ObExprPLSQLVariable(ObIAllocator &alloc)
|
|
: ObFuncExprOperator(alloc, T_FUN_PLSQL_VARIABLE, N_PLSQL_VARIABLE, 0, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION,
|
|
false, INTERNAL_IN_ORACLE_MODE),
|
|
plsql_line_(OB_INVALID_INDEX),
|
|
plsql_variable_(),
|
|
allocator_(alloc)
|
|
{}
|
|
|
|
int ObExprPLSQLVariable::assign(const ObExprOperator &other)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObExprPLSQLVariable *tmp = dynamic_cast<const ObExprPLSQLVariable *>(&other);
|
|
if (OB_UNLIKELY(OB_ISNULL(tmp))) {
|
|
ret = OB_INVALID_ARGUMENT;
|
|
LOG_WARN("invalid argument. wrong type for other", K(other), K(ret));
|
|
} else if (OB_LIKELY(this != tmp)) {
|
|
if (OB_FAIL(ObExprOperator::assign(other))) {
|
|
LOG_WARN("copy in Base class ObExprOperator failed", K(other), K(ret));
|
|
} else {
|
|
OX (this->plsql_line_ = tmp->plsql_line_);
|
|
OZ (deep_copy_plsql_variable(tmp->plsql_variable_));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::calc_result_type0(
|
|
ObExprResType &type, common::ObExprTypeCtx &type_ctx) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (0 == plsql_variable_.case_compare("PLSQL_LINE")) {
|
|
type.set_int32();
|
|
type.set_precision(4);
|
|
type.set_scale(0);
|
|
} else if (0 == plsql_variable_.case_compare("PLSQL_UNIT")
|
|
|| 0 == plsql_variable_.case_compare("PLSQL_UNIT_OWNER")) {
|
|
type.set_varchar();
|
|
type.set_default_collation_type();
|
|
type.set_length(common::OB_MAX_VARCHAR_LENGTH);
|
|
type.set_length_semantics(LS_BYTE);
|
|
} else {
|
|
const sql::ObSQLSessionInfo *session_info = type_ctx.get_session();
|
|
ObString plsql_ccflags;
|
|
ObObj value;
|
|
CK (OB_NOT_NULL(session_info));
|
|
// may plsql relative system variables, try it.
|
|
if (OB_FAIL(ret)) {
|
|
} else if (0 == plsql_variable_.case_compare("PLSQL_CCFLAGS")
|
|
|| OB_FAIL(session_info->get_sys_variable_by_name(plsql_variable_, value))) {
|
|
// not system variable
|
|
if (OB_ERR_SYS_VARIABLE_UNKNOWN == ret
|
|
// plsql_ccflags may rewrite by itself.
|
|
|| 0 == plsql_variable_.case_compare("PLSQL_CCFLAGS")) {
|
|
// then may plsql_ccflags, try it.
|
|
ret = OB_SUCCESS;
|
|
OX (plsql_ccflags = session_info->get_plsql_ccflags());
|
|
OZ (get_key_value(plsql_ccflags, plsql_variable_, value));
|
|
OX (type.set_meta(value.get_meta()));
|
|
OX (type.set_collation_type(ObCharset::get_system_collation()));
|
|
if (OB_SUCC(ret) && value.is_string_type()) {
|
|
type.set_length(value.get_string_len());
|
|
type.set_length_semantics(LS_BYTE);
|
|
}
|
|
} else {
|
|
LOG_WARN("failed to get system variable by name", K(ret), K(plsql_variable_));
|
|
}
|
|
} else {
|
|
type.set_meta(value.get_meta());
|
|
if (value.is_string_type()) {
|
|
type.set_length(value.get_string_len());
|
|
type.set_length_semantics(LS_BYTE);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::get_plsql_unit(common::ObObj &result,
|
|
common::ObIAllocator &alloc,
|
|
ObSQLSessionInfo &session_info,
|
|
const ObExprResType &result_type,
|
|
const ObString &plsql_variable)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
pl::ObPLContext *pl_context = NULL;
|
|
ObPLExecState *pl_state = NULL;
|
|
ObString plsql_unit;
|
|
CK (OB_NOT_NULL(pl_context = session_info.get_pl_context()));
|
|
CK (pl_context->is_top_stack());
|
|
CK (pl_context->get_exec_stack().count() > 0);
|
|
CK (OB_NOT_NULL(pl_state = pl_context
|
|
->get_exec_stack().at(pl_context->get_exec_stack().count() - 1)));
|
|
if (OB_FAIL(ret)) {
|
|
} else if (0 == plsql_variable.case_compare("PLSQL_UNIT")) {
|
|
if (pl_state->get_function().get_package_name().empty()) {
|
|
OZ (ob_write_string(alloc,
|
|
pl_state->get_function().get_function_name(),
|
|
plsql_unit));
|
|
} else {
|
|
OZ (ob_write_string(alloc,
|
|
pl_state->get_function().get_package_name(),
|
|
plsql_unit));
|
|
}
|
|
} else if (0 == plsql_variable.case_compare("PLSQL_UNIT_OWNER")) {
|
|
if (!pl_state->get_function().get_database_name().empty()) {
|
|
OZ (ob_write_string(alloc,
|
|
pl_state->get_function().get_database_name(),
|
|
plsql_unit));
|
|
}
|
|
}
|
|
OX (result.set_varchar(plsql_unit));
|
|
OX (result.set_collation(result_type));
|
|
return ret;
|
|
}
|
|
|
|
// check valid identifiled
|
|
int ObExprPLSQLVariable::check_key(const common::ObString &v)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObString key = v;
|
|
bool valid = true;
|
|
key = key.trim();
|
|
if (!key.empty()
|
|
&& ((key.ptr()[0] >= 'a' && key.ptr()[0] <= 'z')
|
|
|| (key.ptr()[0] >= 'A' && key.ptr()[0] <= 'Z'))) {
|
|
for (int64_t i = 1; valid && i < key.length(); ++i) {
|
|
if (key.ptr()[i] == '_'
|
|
|| key.ptr()[i] == '$'
|
|
|| key.ptr()[i] == '#'
|
|
|| (key.ptr()[i] >= '0' && key.ptr()[i] <= '9')
|
|
|| ((key.ptr()[i] >= 'a' && key.ptr()[i] <= 'z')
|
|
|| (key.ptr()[i] >= 'A' && key.ptr()[i] <= 'Z'))) {
|
|
// do nothing ...
|
|
} else {
|
|
valid = false;
|
|
}
|
|
}
|
|
} else {
|
|
valid = false;
|
|
}
|
|
if (!valid) {
|
|
ret = OB_ERR_INVALID_PLSQL_CCFLAGS;
|
|
LOG_WARN("ORA-39962: invalid parameter for PLSQL_CCFLAGS", K(ret), K(v));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::check_value(
|
|
const common::ObString &v, ObObj &val_obj)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObString val = v;
|
|
val = val.trim();
|
|
if (0 == val.case_compare("NULL")) {
|
|
val_obj.set_null();
|
|
} else if (0 == val.case_compare("TRUE")) {
|
|
val_obj.set_tinyint(1);
|
|
} else if (0 == val.case_compare("FALSE")) {
|
|
val_obj.set_tinyint(0);
|
|
} else {
|
|
char buf[OB_TMP_BUF_SIZE_256 + 1];
|
|
int64_t int_val = 0;
|
|
STRNCPY(buf, val.ptr(), val.length());
|
|
OX (buf[val.length()] = '\0');
|
|
OZ (ob_atoll(buf, int_val));
|
|
CK (int_val > -2147483647 && int_val <= 2147483647);
|
|
OX (val_obj.set_int32(int_val));
|
|
if (OB_FAIL(ret)) {
|
|
ret = OB_ERR_INVALID_PLSQL_CCFLAGS;
|
|
LOG_WARN("ORA-39962: invalid parameter for PLSQL_CCFLAGS", K(ret), K(v));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::add_to_array(
|
|
const common::ObString &key, ObObj &val, ObIArray<std::pair<ObString, ObObj> > &result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t i = 0;
|
|
for (; i < result.count(); ++i) {
|
|
if (0 == key.case_compare(result.at(i).first)) {
|
|
result.at(i).second = val;
|
|
break;
|
|
}
|
|
}
|
|
if (i == result.count()) {
|
|
OZ (result.push_back(std::make_pair(key, val)));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::check_plsql_ccflags(
|
|
const common::ObString &v, ObIArray<std::pair<ObString, ObObj> > *result)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObString v1 = v;
|
|
if (0 == v1.length()) {
|
|
// do nothing ...
|
|
} else if (v1.length() > OB_TMP_BUF_SIZE_256) {
|
|
ret = OB_ERR_PARAMETER_TOO_LONG;
|
|
LOG_WARN("ORA-32021: parameter value longer than string characters", K(ret), K(v));
|
|
LOG_USER_ERROR(OB_ERR_PARAMETER_TOO_LONG, static_cast<int32_t>(OB_TMP_BUF_SIZE_256));
|
|
} else {
|
|
while (OB_SUCC(ret) && v1.length() > 0) {
|
|
ObString value = v1.split_on(',');
|
|
ObString key;
|
|
ObObj val_obj;
|
|
if (value.empty()) {
|
|
value = v1;
|
|
v1.reset();
|
|
}
|
|
key = value.split_on(':');
|
|
key = key.trim();
|
|
OZ (check_key(key));
|
|
OZ (check_value(value, val_obj));
|
|
if (OB_SUCC(ret) && OB_NOT_NULL(result)) {
|
|
OZ (add_to_array(key, val_obj, *result));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::get_key_value(
|
|
const common::ObString &plsql_ccflags, const common::ObString &key, ObObj &value)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<std::pair<common::ObString, ObObj>, 4> result;
|
|
bool found = false;
|
|
OZ (check_plsql_ccflags(plsql_ccflags, &result));
|
|
if (OB_SUCC(ret)) {
|
|
for (int64_t i = 0; !found && i < result.count(); ++i) {
|
|
if (0 == key.case_compare(result.at(i).first)) {
|
|
value = result.at(i).second;
|
|
found = true;
|
|
}
|
|
}
|
|
if (!found) {
|
|
if (0 == key.case_compare("PLSQL_CCFLAGS")) {
|
|
value.set_varchar(plsql_ccflags);
|
|
} else {
|
|
value.set_null();
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObExprPLSQLVariable::cg_expr(ObExprCGCtx &op_cg_ctx,
|
|
const ObRawExpr &raw_expr,
|
|
ObExpr &rt_expr) const
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObIAllocator &alloc = *op_cg_ctx.allocator_;
|
|
const ObPLSQLVariableRawExpr &fun_sys = static_cast<const ObPLSQLVariableRawExpr &>(raw_expr);
|
|
ObPLSQLVariableInfo *info = OB_NEWx(ObPLSQLVariableInfo, (&alloc), alloc, T_FUN_PLSQL_VARIABLE);
|
|
if (NULL == info) {
|
|
ret = OB_ALLOCATE_MEMORY_FAILED;
|
|
LOG_WARN("allocate memory failed", K(ret));
|
|
} else {
|
|
OZ(info->from_raw_expr(fun_sys, alloc));
|
|
info->result_type_ = result_type_;
|
|
rt_expr.extra_info_ = info;
|
|
}
|
|
rt_expr.eval_func_ = eval_plsql_variable;
|
|
return ret;
|
|
}
|
|
int ObExprPLSQLVariable::eval_plsql_variable(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObPLSQLVariableInfo *info = static_cast<ObPLSQLVariableInfo *>(expr.extra_info_);
|
|
ObObj result;
|
|
if (OB_ISNULL(info) || OB_ISNULL(ctx.exec_ctx_.get_my_session())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("failed to get expr info", K(ret));
|
|
} else if (0 == info->plsql_variable_.case_compare("PLSQL_LINE")) {
|
|
result.set_int32(info->plsql_line_);
|
|
} else if (0 == info->plsql_variable_.case_compare("PLSQL_UNIT")
|
|
|| 0 == info->plsql_variable_.case_compare("PLSQL_UNIT_OWNER")) {
|
|
OZ (get_plsql_unit(result, ctx.exec_ctx_.get_allocator(),
|
|
*ctx.exec_ctx_.get_my_session(), info->result_type_,
|
|
info->plsql_variable_));
|
|
} else {
|
|
ObString plsql_ccflags;
|
|
// may plsql relative system variables, try it.
|
|
if (OB_FAIL(ret)) {
|
|
} else if (0 == info->plsql_variable_.case_compare("PLSQL_CCFLAGS")
|
|
|| OB_FAIL(ctx.exec_ctx_.get_my_session()->get_sys_variable_by_name(info->plsql_variable_,
|
|
result))) {
|
|
// not system variable
|
|
if (OB_ERR_SYS_VARIABLE_UNKNOWN == ret
|
|
// plsql_ccflags may rewrite by itself.
|
|
|| 0 == info->plsql_variable_.case_compare("PLSQL_CCFLAGS")) {
|
|
// then may plsql_ccflags, try it.
|
|
ret = OB_SUCCESS;
|
|
OX (plsql_ccflags = ctx.exec_ctx_.get_my_session()->get_plsql_ccflags());
|
|
OZ (get_key_value(plsql_ccflags, info->plsql_variable_, result));
|
|
OX (result.set_collation_type(ObCharset::get_system_collation()));
|
|
} else {
|
|
LOG_WARN("failed to get system variable by name", K(ret), K(info->plsql_variable_));
|
|
}
|
|
}
|
|
}
|
|
OZ(res.from_obj(result, expr.obj_datum_map_));
|
|
if (is_lob_storage(result.get_type())) {
|
|
OZ(ob_adjust_lob_datum(result, expr.obj_meta_, ctx.exec_ctx_.get_allocator(), res));
|
|
}
|
|
OZ(expr.deep_copy_datum(ctx, res));
|
|
return ret;
|
|
}
|
|
|
|
OB_DEF_SERIALIZE(ObPLSQLVariableInfo)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
LST_DO_CODE(OB_UNIS_ENCODE,
|
|
plsql_line_,
|
|
plsql_variable_,
|
|
result_type_);
|
|
return ret;
|
|
}
|
|
|
|
OB_DEF_DESERIALIZE(ObPLSQLVariableInfo)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
LST_DO_CODE(OB_UNIS_DECODE,
|
|
plsql_line_,
|
|
plsql_variable_,
|
|
result_type_);
|
|
return ret;
|
|
}
|
|
|
|
OB_DEF_SERIALIZE_SIZE(ObPLSQLVariableInfo)
|
|
{
|
|
int64_t len = 0;
|
|
LST_DO_CODE(OB_UNIS_ADD_LEN,
|
|
plsql_line_,
|
|
plsql_variable_,
|
|
result_type_);
|
|
return len;
|
|
}
|
|
|
|
int ObPLSQLVariableInfo::deep_copy(common::ObIAllocator &allocator,
|
|
const ObExprOperatorType type,
|
|
ObIExprExtraInfo *&copied_info) const
|
|
{
|
|
int ret = common::OB_SUCCESS;
|
|
OZ(ObExprExtraInfoFactory::alloc(allocator, type, copied_info));
|
|
ObPLSQLVariableInfo &other = *static_cast<ObPLSQLVariableInfo *>(copied_info);
|
|
other.plsql_line_ = plsql_line_;
|
|
other.result_type_ = result_type_;
|
|
OZ(ob_write_string(allocator, plsql_variable_, other.plsql_variable_));
|
|
return ret;
|
|
}
|
|
|
|
template <typename RE>
|
|
int ObPLSQLVariableInfo::from_raw_expr(RE &raw_expr, ObIAllocator &alloc)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObPLSQLVariableRawExpr &expr
|
|
= const_cast<ObPLSQLVariableRawExpr &> (static_cast<const ObPLSQLVariableRawExpr&>(raw_expr));
|
|
plsql_line_ = expr.get_plsql_line();
|
|
OZ(ob_write_string(alloc, expr.get_plsql_variable(), plsql_variable_));
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
}
|