/** * 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_pl_expr_subquery.h" #include "observer/ob_server.h" #include "sql/session/ob_sql_session_info.h" #include "sql/engine/ob_exec_context.h" #include "sql/ob_spi.h" #include "observer/ob_inner_sql_result.h" #include "pl/ob_pl.h" #include "pl/ob_pl_user_type.h" #include "pl/ob_pl_resolver.h" #include "sql/engine/expr/ob_expr_lob_utils.h" namespace oceanbase { using namespace common; namespace sql { OB_SERIALIZE_MEMBER( (ObExprOpSubQueryInPl, ObFuncExprOperator), id_, type_, route_sql_, result_type_, is_ignore_fail_, ps_sql_); ObExprOpSubQueryInPl::ObExprOpSubQueryInPl(common::ObIAllocator &alloc) : ObFuncExprOperator( alloc, T_FUN_SUBQUERY, N_PL_SUBQUERY_CONSTRUCT, PARAM_NUM_UNKNOWN, VALID_FOR_GENERATED_COL, NOT_ROW_DIMENSION, INTERNAL_IN_MYSQL_MODE), id_(common::OB_INVALID_ID), ps_sql_(ObString()), type_(stmt::T_NONE), route_sql_(ObString()), result_type_(), is_ignore_fail_(false), allocator_(alloc) {} ObExprOpSubQueryInPl::~ObExprOpSubQueryInPl() {} int ObExprOpSubQueryInPl::assign(const ObExprOperator &other) { int ret = OB_SUCCESS; const ObExprOpSubQueryInPl *tmp_other = dynamic_cast(&other); if (OB_UNLIKELY(OB_ISNULL(tmp_other))) { ret = OB_INVALID_ARGUMENT; LOG_WARN("cast failed, type of argument is wrong", K(ret), K(other)); } else if (OB_LIKELY(this != tmp_other)) { if (OB_FAIL(ObExprOperator::assign(other))) { LOG_WARN("copy in Base class ObExprOperator failed", K(other), K(ret)); } else { OZ (deep_copy_ps_sql(tmp_other->ps_sql_)); OX (this->type_ = tmp_other->type_); OX (this->result_type_ = tmp_other->result_type_); OZ (deep_copy_route_sql(tmp_other->route_sql_)); OX (this->is_ignore_fail_ = tmp_other->is_ignore_fail_); } } return ret; } int ObExprOpSubQueryInPl::calc_result_typeN(ObExprResType &type, ObExprResType *types, int64_t param_num, ObExprTypeCtx &type_ctx) const { int ret = OB_SUCCESS; UNUSED(types); UNUSED(param_num); UNUSED(type_ctx); OX (type.set_type(result_type_.get_type())); OX (type.set_accuracy(result_type_.get_accuracy())); OX (type.set_meta(result_type_.get_obj_meta())); OX (type.set_length(result_type_.get_length())); return ret; } int ObExprOpSubQueryInPl::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 ObPlQueryRefRawExpr &fun_sys = static_cast(raw_expr); ObExprPlSubQueryInfo *info = OB_NEWx(ObExprPlSubQueryInfo, (&alloc), alloc, T_FUN_SUBQUERY); if (NULL == info) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("allocate memory failed", K(ret)); } else { OZ(info->from_raw_expr(fun_sys, alloc)); rt_expr.extra_info_ = info; rt_expr.eval_func_ = eval_subquery; } return ret; } int ObExprOpSubQueryInPl::eval_subquery(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res) { int ret = OB_SUCCESS; void *param_buf = nullptr; ParamStore *params = nullptr; ObSQLSessionInfo *session = nullptr; ObObj result; ObIAllocator &alloc = ctx.exec_ctx_.get_allocator(); const ObExprPlSubQueryInfo *info = static_cast(expr.extra_info_); ObObj *objs = nullptr; CK(0 == expr.arg_cnt_ || OB_NOT_NULL(objs = static_cast (alloc.alloc(expr.arg_cnt_ * sizeof(ObObj))))); CK(OB_NOT_NULL(info)); CK(OB_NOT_NULL(session = ctx.exec_ctx_.get_my_session())); CK (OB_NOT_NULL(ctx.exec_ctx_.get_sql_ctx())); observer::ObQueryRetryCtrl retry_ctrl; int64_t tenant_version = 0; int64_t sys_version = 0; bool is_stack_overflow = false; if (info->id_ != common::OB_INVALID_ID) { ret = OB_NOT_SUPPORTED; LOG_USER_ERROR(OB_NOT_SUPPORTED, "observer version need upate to 4.1 ver"); } OZ(check_stack_overflow(is_stack_overflow)); if (OB_SUCC(ret) && is_stack_overflow) { ret = OB_SIZE_OVERFLOW; LOG_WARN("too deep recursive", K(ret), K(is_stack_overflow), K(info->ps_sql_), K(info->type_)); } if (OB_FAIL(ret)) { } else if (OB_FAIL(expr.eval_param_value(ctx))) { LOG_WARN("failed eval param value", K(ret)); } else if (OB_ISNULL(param_buf = alloc.alloc(sizeof(ParamStore)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("failed to allocate memory", K(ret)); } else if (FALSE_IT(params = new(param_buf)ParamStore(ObWrapperAllocator(alloc)))) { } else if (OB_FAIL(fill_obj_stack(expr, ctx, objs))) { LOG_WARN("failed to fill objs", K(ret)); } else if (OB_FAIL(fill_param_store(objs, expr.arg_cnt_, *params))) { LOG_WARN("failed to process in params", K(ret)); } else { pl::ObPLExecCtx pl_exec_ctx(&alloc, &ctx.exec_ctx_, params, nullptr, &ret, nullptr); SMART_VAR(ObSPIResultSet, spi_result) { OZ (spi_result.init(*session)); OZ (spi_result.start_nested_stmt_if_need(&pl_exec_ctx, info->route_sql_, static_cast(info->type_), false)); if (OB_SUCC(ret)) { ObSPIOutParams out_params; int64_t old_query_start_time = session->get_query_start_time(); session->set_query_start_time(ObTimeUtility::current_time()); bool is_retry = false; do { if (is_retry) { out_params.reset(); spi_result.reset_member_for_retry(*session); } retry_ctrl.clear_state_before_each_retry(session->get_retry_info_for_update()); if (OB_FAIL(GCTX.schema_service_->get_tenant_schema_guard(session->get_effective_tenant_id(), spi_result.get_scheme_guard()))) { LOG_WARN("get schema guard failed", K(ret)); } else if (OB_FAIL(spi_result.get_scheme_guard().get_schema_version(session->get_effective_tenant_id(), tenant_version))) { LOG_WARN("fail get schema version", K(ret)); } else if (OB_FAIL(spi_result.get_scheme_guard().get_schema_version(OB_SYS_TENANT_ID, sys_version))) { LOG_WARN("fail get sys schema version", K(ret)); } else { retry_ctrl.set_tenant_local_schema_version(tenant_version); retry_ctrl.set_sys_local_schema_version(sys_version); spi_result.get_sql_ctx().schema_guard_ = &spi_result.get_scheme_guard(); OZ (ObSPIService::inner_open(&pl_exec_ctx, expr.arg_cnt_ == 0 ? info->route_sql_.ptr() : nullptr, info->ps_sql_.ptr(), info->type_, *params, spi_result, out_params)); if (OB_FAIL(ret)) { LOG_WARN("inner open error", K(ret)); } else if (OB_FAIL(get_result(spi_result.get_result_set(), result, alloc))) { if (OB_ERR_TOO_MANY_ROWS != ret) { int cli_ret = OB_SUCCESS; retry_ctrl.test_and_save_retry_state(GCTX, spi_result.get_sql_ctx(), *spi_result.get_result_set(), ret, cli_ret, true, true, true); ret = cli_ret; } } session->set_session_in_retry(retry_ctrl.need_retry()); int64_t close_ret = spi_result.get_result_set()->close(); if (OB_SUCCESS != close_ret) { LOG_WARN("close spi result failed", K(ret), K(close_ret)); } ret = OB_SUCCESS == ret ? close_ret : ret; } is_retry = true; } while (observer::RETRY_TYPE_NONE != retry_ctrl.get_retry_type()); session->get_retry_info_for_update().clear(); session->set_query_start_time(old_query_start_time); } spi_result.end_nested_stmt_if_need(&pl_exec_ctx, ret); } } if (OB_FAIL(ret)) { LOG_WARN("get result obj failed", K(ret)); } else { if (!info->result_type_.is_ext() && (info->result_type_.get_obj_meta() != result.get_meta())) { ObObj conv_res; OZ (sql::ObSPIService::spi_convert(*session, alloc, result, info->result_type_, conv_res, info->is_ignore_fail_)); if (OB_FAIL(ret)) { LOG_WARN("convert type error", K(ret)); } else { result = conv_res; } } } if (OB_SUCC(ret)) { OZ(res.from_obj(result)); 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; } int ObExprOpSubQueryInPl::get_result(void *result_set, ObObj &result, ObIAllocator &alloc) { int ret = OB_SUCCESS; ObNewRow current_row; int64_t row_count = 0; if (OB_FAIL(fetch_row(result_set, row_count, current_row))) { LOG_WARN("read result error", K(ret), K(row_count)); } else { OZ (deep_copy_obj(alloc, current_row.get_cell(0), result)); } /* check result has more row */ if (OB_FAIL(ret)) { } else if (OB_FAIL(fetch_row(result_set, row_count, current_row))) { if (OB_ITER_END == ret) { ret = OB_SUCCESS; } else { LOG_WARN("read result error", K(ret)); } } else { ret = OB_ERR_TOO_MANY_ROWS; } return ret; } int ObExprOpSubQueryInPl::fetch_row(void *result_set, int64_t &row_count, ObNewRow &cur_row) { int ret = OB_SUCCESS; if (OB_ISNULL(result_set)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Argument passed in is NULL", K(result_set), K(ret)); } else { ObResultSet *ob_result_set = static_cast(result_set); const ObNewRow *row = NULL; if (OB_FAIL(ob_result_set->get_next_row(row))) { //上层判断返回值,这里不打印信息 } else { cur_row = *row; ++row_count; } } LOG_DEBUG("spi fetch row", K(cur_row), K(row_count), K(ret)); return ret; } int ObExprOpSubQueryInPl::fill_param_store(const ObObj *objs_stack, int64_t param_num, ParamStore& params) { int ret = OB_SUCCESS; CK (0 == param_num || OB_NOT_NULL(objs_stack)); for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) { ObObjParam param; param.reset(); objs_stack[i].copy_value_or_obj(param, true); param.set_param_meta(); OZ (params.push_back(param)); } return ret; } int ObExprOpSubQueryInPl::fill_obj_stack(const ObExpr &expr, ObEvalCtx &ctx, ObObj *objs) { int ret = OB_SUCCESS; for (int64_t i = 0; i < expr.arg_cnt_ && OB_SUCC(ret); ++i) { ObDatum ¶m = expr.args_[i]->locate_expr_datum(ctx); if (OB_FAIL(param.to_obj(objs[i], expr.args_[i]->obj_meta_))) { LOG_WARN("failed to convert obj", K(ret), K(i)); } } return ret; } OB_DEF_SERIALIZE(ObExprPlSubQueryInfo) { int ret = OB_SUCCESS; LST_DO_CODE(OB_UNIS_ENCODE, id_, type_, route_sql_, result_type_, is_ignore_fail_, ps_sql_); return ret; } OB_DEF_DESERIALIZE(ObExprPlSubQueryInfo) { int ret = OB_SUCCESS; LST_DO_CODE(OB_UNIS_DECODE, id_, type_, route_sql_, result_type_, is_ignore_fail_, ps_sql_); return ret; } OB_DEF_SERIALIZE_SIZE(ObExprPlSubQueryInfo) { int64_t len = 0; LST_DO_CODE(OB_UNIS_ADD_LEN, id_, type_, route_sql_, result_type_, is_ignore_fail_, ps_sql_); return len; } int ObExprPlSubQueryInfo::deep_copy(common::ObIAllocator &allocator, const ObExprOperatorType type, ObIExprExtraInfo *&copied_info) const { int ret = common::OB_SUCCESS; OZ(ObExprExtraInfoFactory::alloc(allocator, type, copied_info)); ObExprPlSubQueryInfo &other = *static_cast(copied_info); other.type_ = type_; other.result_type_ = result_type_; other.is_ignore_fail_ = is_ignore_fail_; OZ(ob_write_string(allocator, route_sql_, other.route_sql_, true)); OZ(ob_write_string(allocator, ps_sql_, other.ps_sql_, true)); return ret; } template int ObExprPlSubQueryInfo::from_raw_expr(RE &raw_expr, ObIAllocator &alloc) { int ret = OB_SUCCESS; ObPlQueryRefRawExpr &subquery_expr = const_cast (static_cast(raw_expr)); id_ = common::OB_INVALID_ID; type_ = subquery_expr.get_stmt_type(); result_type_ = subquery_expr.get_result_type(); is_ignore_fail_ = subquery_expr.is_ignore_fail(); OZ(ob_write_string(alloc, subquery_expr.get_route_sql(), route_sql_, true)); OZ(ob_write_string(alloc, subquery_expr.get_route_sql(), ps_sql_, true)); return ret; } } /* sql */ } /* oceanbase */