/** * 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. */ #ifndef OCEANBASE_SRC_PL_OB_PL_H_ #define OCEANBASE_SRC_PL_OB_PL_H_ #include "lib/container/ob_bit_set.h" #include "lib/container/ob_array.h" #include "lib/container/ob_se_array.h" #include "lib/container/ob_2d_array.h" #include "lib/allocator/ob_allocator.h" #include "lib/rc/context.h" #include "lib/utility/ob_macro_utils.h" #include "lib/oblog/ob_log_module.h" #include "sql/engine/expr/ob_expr_res_type.h" #include "objit/ob_llvm_helper.h" #include "objit/ob_llvm_di_helper.h" #include "share/ob_errno.h" #include "sql/resolver/expr/ob_raw_expr.h" #include "sql/session/ob_basic_session_info.h" #include "sql/parser/parse_node.h" #include "pl/parser/parse_stmt_node.h" #include "pl/ob_pl_type.h" #include "pl/ob_pl_package_manager.h" #include "pl/ob_pl_interface_pragma.h" #include "sql/plan_cache/ob_cache_object_factory.h" #include "pl/pl_cache/ob_pl_cache.h" #include "pl/pl_cache/ob_pl_cache_object.h" #ifdef OB_BUILD_ORACLE_PL #include "pl/ob_pl_call_stack_trace.h" #endif #include "pl/ob_pl_allocator.h" namespace test { class MockCacheObjectFactory; } namespace oceanbase { namespace sql { class ObPsCache; class ObSQLSessionInfo; class ObAlterRoutineResolver; } namespace jit { class ObDWARFHelper; } using common::ObPsStmtId; namespace pl { typedef common::ObFixedBitSet<128> ObPLFlag; typedef void* ObPointer; typedef uint64_t ObFuncPtr; typedef common::ParamStore ParamStore; class ObPLCacheCtx; class ObPLAllocator1; class ObPLProfilerTimeStack; enum ObPLObjectType { INVALID_OBJECT_TYPE = -1, TABLE, PL, PACKAGE_SPEC }; class ObPLINS { public: ObPLINS() {} virtual ~ObPLINS() {} virtual int get_user_type(uint64_t type_id, const ObUserDefinedType *&user_type, ObIAllocator *allocator = NULL) const = 0; virtual int get_size(ObPLTypeSize type, const ObPLDataType &pl_type, int64_t &size, ObIAllocator *allocator = NULL) const; virtual int get_element_data_type(const ObPLDataType &pl_type, ObDataType &elem_type, ObIAllocator *allocator = NULL) const; virtual int get_not_null(const ObPLDataType &pl_type, bool ¬_null, ObIAllocator *allocator = NULL) const; virtual int init_complex_obj(ObIAllocator &allocator, ObIAllocator &expr_allocator, const ObPLDataType &pl_type, common::ObObjParam &obj, bool set_allocator = false, bool set_null = true); virtual int calc_expr(uint64_t package_id, int64_t expr_idx, ObObjParam &result); }; class ObPLFunctionBase { static const int64_t DEBUG_MODE = 1; // DEBUG INFO static const int64_t CONTAINS_DYNAMIC_SQL = 2; // PS static const int64_t MULTI_RESULTS = 3; // SELECT static const int64_t HAS_COMMIT_OR_ROLLBACK = 4; // DDL static const int64_t HAS_SET_AUTOCOMMIT_STMT = 5; // SET AUTOCOMMIT static const int64_t IS_AUTONOMOUS_TRANSACTION = 6; static const int64_t IS_UDT_ROUTINE = 7; // function inside udt object static const int64_t HAS_OPEN_EXTERNAL_REF_CURSOR = 8; // A SUBPROGRAM MAY OPEN PARENT REF CURSOR static const int64_t IS_UDT_CONS = 9; // udt constructor static const int64_t HAS_DEBUG_PRIV = 10; public: ObPLFunctionBase() : proc_type_(INVALID_PROC_TYPE), routine_id_(common::OB_INVALID_ID), package_id_(common::OB_INVALID_ID), database_id_(common::OB_INVALID_ID), tenant_id_(common::OB_INVALID_ID), package_version_(common::OB_INVALID_VERSION), owner_(common::OB_INVALID_ID), priv_user_(common::OB_INVALID_ID), gmt_create_(0), ret_type_(common::ObNullType), arg_count_(0), flag_() {} virtual ~ObPLFunctionBase() {} public: inline ObProcType get_proc_type() const { return proc_type_; } inline void set_proc_type(ObProcType type) { proc_type_ = type; } inline uint64_t get_routine_id() const { return routine_id_; } inline uint64_t get_package_id() const { return package_id_; } inline void set_routine_id(uint64_t routine_id) { routine_id_ = routine_id; } inline void set_package_id(uint64_t package_id) { package_id_ = package_id; } inline uint64_t get_database_id() const { return database_id_; } inline void set_database_id(uint64_t database_id) { database_id_ = database_id; } // inline uint64_t get_tenant_id() const { return tenant_id_; } // inline void set_tenant_id(uint64_t tenant_id) { tenant_id_ = tenant_id; } inline uint64_t get_package_version() const { return package_version_; } inline void set_package_version(uint64_t package_version) { package_version_ = package_version; } inline uint64_t get_owner() const { return owner_; } inline void set_owner(uint64_t owner) { owner_ = owner; } inline uint64_t get_gmt_create() const { return gmt_create_; } inline void set_gmt_create(uint64_t gmt_create) { gmt_create_ = gmt_create; } inline int64_t get_arg_count() const { return arg_count_; } inline void set_arg_count(int64_t arg_count) { arg_count_ = arg_count; } inline const ObPLFlag &get_flag() const { return flag_; } inline int add_members(const ObPLFlag &flag) { return flag_.add_members(flag); } inline void add_flag(int64_t index) { flag_.add_member(index); } inline bool has_flag(int64_t index) const { return flag_.has_member(index); } inline void set_contain_dynamic_sql() { flag_.add_member(CONTAINS_DYNAMIC_SQL); } inline bool get_contain_dynamic_sql() const { return flag_.has_member(CONTAINS_DYNAMIC_SQL); } inline void set_multi_results() { flag_.add_member(MULTI_RESULTS); } inline bool get_multi_results() const { return flag_.has_member(MULTI_RESULTS); } inline void set_has_commit_or_rollback() { flag_.add_member(HAS_COMMIT_OR_ROLLBACK); } inline bool get_has_commit_or_rollback() const { return flag_.has_member(HAS_COMMIT_OR_ROLLBACK); } inline void set_has_set_autocommit_stmt() { flag_.add_member(HAS_SET_AUTOCOMMIT_STMT); } inline bool get_has_set_autocommit_stmt() const { return flag_.has_member(HAS_SET_AUTOCOMMIT_STMT); } inline void set_autonomous() { flag_.add_member(IS_AUTONOMOUS_TRANSACTION); } inline bool is_autonomous() const { return flag_.has_member(IS_AUTONOMOUS_TRANSACTION); } inline const ObPLDataType &get_ret_type() const { return ret_type_; } inline void set_ret_type(const ObPLDataType &ret_type) { ret_type_ = ret_type; } inline int set_ret_type_info(const common::ObIArray& type_info, ObPLEnumSetCtx *enum_set_ctx) { ret_type_.set_enum_set_ctx(enum_set_ctx); return ret_type_.set_type_info(type_info); } inline bool is_function() { return STANDALONE_FUNCTION == proc_type_ || PACKAGE_FUNCTION == proc_type_ || NESTED_FUNCTION == proc_type_ || UDT_FUNCTION == proc_type_; } inline bool is_udt_routine() const { return has_flag(IS_UDT_ROUTINE); } inline void set_is_udt_routine() { flag_.add_member(IS_UDT_ROUTINE); } inline bool has_open_external_ref_cursor() const { return has_flag(HAS_OPEN_EXTERNAL_REF_CURSOR); } inline void set_open_external_ref_cursor() { flag_.add_member(HAS_OPEN_EXTERNAL_REF_CURSOR); } inline void set_is_udt_cons() { flag_.add_member(IS_UDT_CONS); } inline bool is_udt_cons() const { return has_flag(IS_UDT_CONS); } inline bool has_debug_priv() const { return has_flag(HAS_DEBUG_PRIV); } inline void set_debug_priv() { flag_.add_member(HAS_DEBUG_PRIV); } inline void clean_debug_priv() { flag_.del_member(HAS_DEBUG_PRIV); } private: //基础信息 ObProcType proc_type_; uint64_t routine_id_; uint64_t package_id_; uint64_t database_id_; uint64_t tenant_id_; uint64_t package_version_; uint64_t owner_; uint64_t priv_user_; int64_t gmt_create_; ObPLDataType ret_type_; int64_t arg_count_; //参数一定在符号表的最前面 ObPLFlag flag_; DISALLOW_COPY_AND_ASSIGN(ObPLFunctionBase); }; class ObPLCompileUnit : public ObPLCacheObject { friend class ::test::MockCacheObjectFactory; public: ObPLCompileUnit(sql::ObLibCacheNameSpace ns, lib::MemoryContext &mem_context); virtual ~ObPLCompileUnit(); inline bool get_can_cached() { return can_cached_; } inline void set_can_cached(bool can_cached) { can_cached_ = can_cached; } inline bool has_incomplete_rt_dep_error() { return has_incomplete_rt_dep_error_; } inline void set_has_incomplete_rt_dep_error(bool has_incomplete_rt_dep_error) { has_incomplete_rt_dep_error_ = has_incomplete_rt_dep_error; } inline const ObIArray &get_routine_table() const { return routine_table_; } inline ObIArray &get_routine_table() { return routine_table_; } inline int set_routine_table(ObIArray &table) { return routine_table_.assign(table); } int add_routine(ObPLFunction *routine); int get_routine(int64_t routine_idx, ObPLFunction *&routine) const; void init_routine_table(int64_t count) { routine_table_.set_capacity(static_cast(count)); } inline const ObIArray &get_type_table() const { return type_table_; } inline ObPLEnumSetCtx & get_enum_set_ctx() { return enum_set_ctx_; } inline jit::ObLLVMHelper &get_helper() { return helper_; } inline jit::ObLLVMDIHelper &get_di_helper() { return di_helper_; } inline const sql::ObExecEnv &get_exec_env() const { return exec_env_; } inline sql::ObExecEnv &get_exec_env() { return exec_env_; } inline void set_exec_env(const sql::ObExecEnv &env) { exec_env_ = env; } jit::ObDIRawData get_debug_info() const { return helper_.get_debug_info(); } virtual void reset(); virtual void dump_deleted_log_info(const bool is_debug_log = true) const; virtual int check_need_add_cache_obj_stat(ObILibCacheCtx &ctx, bool &need_real_add); OB_INLINE std::pair get_profiler_unit_info() const { return profiler_unit_info_; } OB_INLINE void set_profiler_unit_info(uint64_t unit_id, ObProcType type) { profiler_unit_info_ = std::make_pair(unit_id, type); } OB_INLINE void set_profiler_unit_info(const std::pair &unit_info) { profiler_unit_info_ = unit_info; } TO_STRING_KV(K_(routine_table), K_(can_cached), K_(tenant_schema_version), K_(sys_schema_version), K_(stat)); protected: common::ObFixedArray routine_table_; common::ObArray type_table_; pl::ObPLEnumSetCtx enum_set_ctx_; jit::ObLLVMHelper helper_; jit::ObLLVMDIHelper di_helper_; bool can_cached_; bool has_incomplete_rt_dep_error_; sql::ObExecEnv exec_env_; std::pair profiler_unit_info_; DISALLOW_COPY_AND_ASSIGN(ObPLCompileUnit); }; class ObPLSymbolTable; class ObPLSymbolDebugInfoTable; class ObPLFunctionAST; class ObPLSqlStmt; class ObPLSqlInfo { public: ObPLSqlInfo() : loc_(0), forall_sql_(false), for_update_(false), has_hidden_rowid_(false), sql_(), params_(), array_binding_params_(), ps_sql_(), stmt_type_(sql::stmt::StmtType::T_NONE), rowid_table_id_(OB_INVALID_ID), into_(), not_null_flags_(), pl_integer_ranges_(), data_type_(), bulk_(false), allocator_(nullptr) {} ObPLSqlInfo(common::ObIAllocator &allocator) : loc_(0), forall_sql_(false), for_update_(false), has_hidden_rowid_(false), sql_(), params_(allocator), array_binding_params_(allocator), ps_sql_(), stmt_type_(sql::stmt::StmtType::T_NONE), rowid_table_id_(OB_INVALID_ID), into_(allocator), not_null_flags_(allocator), pl_integer_ranges_(allocator), data_type_(allocator), bulk_(false), allocator_(&allocator) {} virtual ~ObPLSqlInfo() {} int generate(const ObPLSqlStmt &sql, ObIArray &exprs); TO_STRING_KV(K(loc_), K(forall_sql_), K(for_update_), K(has_hidden_rowid_), K(sql_), K(params_), K(array_binding_params_), K(ps_sql_), K(stmt_type_), K(rowid_table_id_), K(into_), K(not_null_flags_), K(pl_integer_ranges_), K(data_type_), K(bulk_)); public: uint64_t loc_; bool forall_sql_; bool for_update_; bool has_hidden_rowid_; common::ObString sql_; ObFixedArray params_; ObFixedArray array_binding_params_; common::ObString ps_sql_; sql::stmt::StmtType stmt_type_; uint64_t rowid_table_id_; ObFixedArray into_; ObFixedArray not_null_flags_; ObFixedArray pl_integer_ranges_; ObFixedArray data_type_; bool bulk_; ObIAllocator *allocator_; }; class ObPLVarDebugInfo { public: struct ObPLVarScope { ObPLVarScope() : start_(-1), end_(-1) {} ObPLVarScope(int start, int end) : start_(start), end_(end) {} inline bool contain(int line) { return start_ <= line && end_ >= line; } int start_; int end_; TO_STRING_KV(K_(start), K_(end)); }; ObPLVarDebugInfo() : name_(), type_(ObPLType::PL_INVALID_TYPE), scope_() {} ObPLVarDebugInfo(const common::ObString &name, ObPLType type, ObPLVarScope scope) : name_(name), type_(type), scope_(scope) {} inline bool contain(int line) { return scope_.contain(line); } inline bool is_obj() { return ObPLType::PL_OBJ_TYPE == type_; } inline bool is_collection() { return ObPLType::PL_NESTED_TABLE_TYPE == type_ || ObPLType::PL_ASSOCIATIVE_ARRAY_TYPE == type_ || ObPLType::PL_VARRAY_TYPE == type_; } inline bool is_record() { return ObPLType::PL_RECORD_TYPE == type_; } inline const common::ObString& get_name() const { return name_; } int deep_copy(ObIAllocator &allocator, const ObPLVarDebugInfo& other); TO_STRING_KV(K_(name), K_(type), K_(scope)); private: common::ObString name_; ObPLType type_; ObPLVarScope scope_; }; class ObPLNameDebugInfo { public: ObPLNameDebugInfo() : owner_name_(), package_name_(), routine_name_() {} public: ObString owner_name_; ObString package_name_; ObString routine_name_; }; class ObPLFunction : public ObPLFunctionBase, public ObPLCompileUnit { public: ObPLFunction(lib::MemoryContext &mem_context) : ObPLFunctionBase(), ObPLCompileUnit(sql::ObLibCacheNameSpace::NS_PRCR, mem_context), variables_(allocator_), variables_debuginfo_(allocator_), default_idxs_(allocator_), sql_infos_(allocator_), in_args_(), out_args_(), action_(0), di_buf_(NULL), di_len_(0), is_all_sql_stmt_(true), is_invoker_right_(false), is_pipelined_(false), name_debuginfo_(), function_name_(), has_parallel_affect_factor_(false) { } virtual ~ObPLFunction(); inline const common::ObIArray &get_variables() const { return variables_; } inline const common::ObIArray &get_variables_debuginfo() const { return variables_debuginfo_; } inline const common::ObIArray &get_default_idxs() const { return default_idxs_; } inline sql::ObSqlExpression* get_default_expr(int64_t param) { int64_t idx = param >= 0 && param < default_idxs_.count() ? default_idxs_.at(param) : -1; return idx >= 0 && idx < expressions_.count() ? expressions_.at(idx) : NULL; } inline const ObPLNameDebugInfo& get_name_debuginfo() const { return name_debuginfo_; } int set_name_debuginfo(const ObPLFunctionAST &ast); int set_variables(const ObPLSymbolTable &symbol_table); int set_variables_debuginfo(const ObPLSymbolDebugInfoTable &symbol_debuginfo_table); int set_types(const ObPLUserTypeTable &type_table); inline const common::ObBitSet &get_in_args() const { return in_args_; } inline void set_in_args(const common::ObBitSet &out_idx) { in_args_ = out_idx; } inline int add_in_arg(int64_t i) { return in_args_.add_member(i); } inline const common::ObBitSet &get_out_args() const { return out_args_; } inline void set_out_args(const common::ObBitSet &out_idx) { out_args_ = out_idx; } inline int add_out_arg(int64_t i) { return out_args_.add_member(i); } inline ObFuncPtr get_action() const { return action_; } inline void set_action(ObFuncPtr action) { action_ = action; } inline bool is_debug_mode() const { return !get_debug_info().empty(); } inline bool get_is_all_sql_stmt() const { return is_all_sql_stmt_; } inline void set_is_all_sql_stmt(bool is_all_sql_stmt) { is_all_sql_stmt_ = is_all_sql_stmt; } inline bool is_invoker_right() const { return is_invoker_right_; } inline void set_invoker_right() { is_invoker_right_ = true; } inline bool is_pipelined() const { return is_pipelined_; } inline void set_pipelined(bool is_pipelined) { is_pipelined_ = is_pipelined; } inline bool get_has_parallel_affect_factor() const { return has_parallel_affect_factor_; } inline void set_has_parallel_affect_factor(bool value) { has_parallel_affect_factor_ = value; } int get_subprogram(const ObIArray &path, ObPLFunction *&routine) const; inline const common::ObString &get_function_name() const { return function_name_; } inline const common::ObString &get_package_name() const { return package_name_; } inline const common::ObString &get_database_name() const { return database_name_; } inline const common::ObString &get_priv_user() const { return priv_user_; } int set_function_name(const ObString &function_name) { return ob_write_string(get_allocator(), function_name, function_name_); } int set_package_name(const ObString &package_name) { return ob_write_string(get_allocator(), package_name, package_name_); } int set_database_name(const ObString &database_name) { return ob_write_string(get_allocator(), database_name, database_name_); } int set_priv_user(const ObString &priv_user) { return ob_write_string(get_allocator(), priv_user, priv_user_); } inline bool need_register_debug_info() { return is_debug_mode() && get_tenant_id() != OB_SYS_TENANT_ID && has_debug_priv() && !ObTriggerInfo::is_trigger_package_id(get_package_id()) && !ObUDTObjectType::is_object_id(get_package_id()); } bool should_init_as_session_cursor(); /* * some package subprogram has special invoker right, though the package may have definer privs * for example: dbms_utility package is definer privs, but some function such as * name_resolve must be run as current_user, oracle do it in interface functions * see: * we hacked it using name compared, for the interface funtion can't get the origin db name and id * test -> oceanbase, we see oceanbase in interface but can't see test. */ int is_special_pkg_invoke_right(ObSchemaGetterGuard &guard, bool &flag); int gen_action_from_precompiled(const ObString &name, size_t length, const char *ptr); common::ObFixedArray& get_sql_infos() { return sql_infos_; } TO_STRING_KV(K_(ns), K_(ref_count), K_(tenant_schema_version), K_(sys_schema_version), K_(object_id), K_(dependency_tables), K_(params_info), K_(variables), K_(default_idxs), K_(function_name), K_(priv_user), K_(stat)); private: //符号表信息 common::ObFixedArray variables_; //根据ObPLSymbolTable的全局符号表生成,所有输入输出参数和PL体内使用的所有变量 common::ObFixedArray variables_debuginfo_; common::ObFixedArray default_idxs_; common::ObFixedArray sql_infos_; common::ObBitSet in_args_; common::ObBitSet out_args_; ObFuncPtr action_; char *di_buf_; int64_t di_len_; bool is_all_sql_stmt_; bool is_invoker_right_; bool is_pipelined_; ObPLNameDebugInfo name_debuginfo_; common::ObString function_name_; common::ObString package_name_; common::ObString database_name_; common::ObString priv_user_; bool has_parallel_affect_factor_; DISALLOW_COPY_AND_ASSIGN(ObPLFunction); }; struct ObPLExecRecursionCtx { public: ObPLExecRecursionCtx() : init_(false), max_recursion_depth_(0), recursion_depth_map_() {} ~ObPLExecRecursionCtx() {} int init(sql::ObSQLSessionInfo &session_info); int inc_and_check_depth(uint64_t package_id, uint64_t proc_id, bool is_function); int dec_and_check_depth(uint64_t package_id, uint64_t proc_id); private: static const int64_t RECURSION_ARRAY_SIZE = 4; static const int64_t RECURSION_MAP_SIZE = 10; private: bool init_; int64_t max_recursion_depth_; // To avoid hash map initialize cost, we store recursion depth in array // if routines less than RECURSION_ARRAY_SIZE. common::ObSEArray, int64_t>, RECURSION_MAP_SIZE> recursion_depth_array_; common::hash::ObHashMap, int64_t> recursion_depth_map_; }; struct ObPLSqlCodeInfo { public: ObPLSqlCodeInfo() : sqlcode_(OB_SUCCESS), sqlmsg_() {} inline void set_sqlcode(int sqlcode, const ObString &sqlmsg = ObString("")) { sqlcode_ = sqlcode; sqlmsg_ = sqlmsg; } inline void reset() { sqlcode_ = OB_SUCCESS; sqlmsg_ = ObString(""); stakced_warning_buff_.reset(); } inline void set_sqlmsg(const ObString &sqlmsg) { sqlmsg_ = sqlmsg; } inline int get_sqlcode() const { return sqlcode_; } inline const ObString& get_sqlmsg() const { return sqlmsg_; } inline common::ObIArray& get_stack_warning_buf() { return stakced_warning_buff_; } private: int sqlcode_; ObString sqlmsg_; common::ObSEArray stakced_warning_buff_; }; struct ObPLException; struct ObPLCtx { public: ObPLCtx() {} ~ObPLCtx(); int add(ObObj &obj) { return objects_.push_back(obj); } void clear() { objects_.reset(); } void reset_obj(); void reset_obj_range_to_end(int64_t index); common::ObIArray& get_objects() { return objects_; } private: // 用于收集在PL执行过程中使用到的Allocator, // 有些数据的生命周期是整个执行期的, 所以不能在PL执行完成后马上释放, 如OUT参数 // 这里的Allocator需要保证由ObExecContext->allocator分配, // 这样才能保证释放这里的allocator时指针是有效的 ObSEArray objects_; }; class ObPLPackageGuard; #define IDX_PLEXECCTX_VTABLE 0 #define IDX_PLEXECCTX_ALLOCATOR 1 #define IDX_PLEXECCTX_CTX 2 #define IDX_PLEXECCTX_PARAMS 3 #define IDX_PLEXECCTX_RESULT 4 #define IDX_PLEXECCTX_STATUS 5 #define IDX_PLEXECCTX_FUNC 6 #define IDX_PLEXECCTX_IN 7 #define IDX_PLEXECCTX_PL_CTX 8 struct ObPLExecCtx : public ObPLINS { ObPLExecCtx(common::ObIAllocator *allocator, sql::ObExecContext *exec_ctx, ParamStore *params, common::ObObj *result, int *status, ObPLFunction *func, bool in_function = false, const common::ObIArray *nocopy_params = NULL, ObPLPackageGuard *guard = NULL) : allocator_(allocator), exec_ctx_(exec_ctx), params_(params), result_(result), status_(status), func_(func), in_function_(in_function), pl_ctx_(NULL), nocopy_params_(nocopy_params), guard_(guard), expr_alloc_("PlBlockExpr", OB_MALLOC_NORMAL_BLOCK_SIZE, MTL_ID()) { if (NULL != exec_ctx && NULL != exec_ctx_->get_my_session()) { pl_ctx_ = exec_ctx_->get_my_session()->get_pl_context(); } } static uint32_t allocator_offset_bits() { return offsetof(ObPLExecCtx, allocator_) * 8; } static uint32_t exec_ctx_offset_bits() { return offsetof(ObPLExecCtx, exec_ctx_) * 8; } static uint32_t params_offset_bits() { return offsetof(ObPLExecCtx, params_) * 8; } static uint32_t result_offset_bits() { return offsetof(ObPLExecCtx, result_) * 8; } static uint32_t pl_ctx_offset_bits() { return offsetof(ObPLExecCtx, pl_ctx_) * 8; } bool valid(); ObArenaAllocator *get_top_expr_allocator(); virtual int get_user_type(uint64_t type_id, const ObUserDefinedType *&user_type, ObIAllocator *allocator = NULL) const; virtual int calc_expr(uint64_t package_id, int64_t expr_idx, ObObjParam &result); common::ObIAllocator *allocator_; sql::ObExecContext *exec_ctx_; ParamStore *params_; // param stroe, 对应PL Function的符号表 common::ObObj *result_; int *status_; //PL里面的sql发生错误会直接抛出异常,走不到函数正常结束的返回值返回错误码,所以要在这里记录错误码 ObPLFunction *func_; // 对应该执行上下文的func_ bool in_function_; //记录当前是否在function中 ObPLContext *pl_ctx_; // for error stack const common::ObIArray *nocopy_params_; //用于描述nocopy参数 ObPLPackageGuard *guard_; //对应该次执行的package_guard ObArenaAllocator expr_alloc_; }; // backup and restore ObExecContext attributes struct ExecCtxBak { #define PL_EXEC_CTX_BAK_ATTRS phy_plan_ctx_,expr_op_ctx_store_,expr_op_size_,has_non_trivial_expr_op_ctx_,frames_,frame_cnt_ #define DEF_BACKUP_ATTR(x) typeof(sql::ObExecContext::x) x = 0 LST_DO_CODE(DEF_BACKUP_ATTR, EXPAND(PL_EXEC_CTX_BAK_ATTRS)); #undef DEF_BACKUP_ATTR void backup(sql::ObExecContext &ctx); void restore(sql::ObExecContext &ctx); }; class ObPLContext; class ObPLExecState { public: ObPLExecState(common::ObIAllocator &in_allocator, common::ObIAllocator &allocator, sql::ObExecContext &ctx, ObPLPackageGuard &guard, ObPLFunction &func, common::ObObj &result, int &status, bool top_call = false, bool inner_call = false, bool in_function = false, const common::ObIArray *nocopy_params = NULL, uint64_t loc = 0, bool is_called_from_sql = false) : func_(func), phy_plan_ctx_(in_allocator), eval_ctx_(ctx), result_(result), ctx_(&allocator, &ctx, &phy_plan_ctx_.get_param_store_for_update(), &result, &status, &func_, in_function, nocopy_params, &guard), inner_call_(inner_call), top_call_(top_call), need_reset_physical_plan_(false), top_context_(NULL), loc_(loc), is_called_from_sql_(is_called_from_sql), dwarf_helper_(NULL), pure_sql_exec_time_(0), pure_plsql_exec_time_(0), pure_sub_plsql_exec_time_(0), profiler_time_stack_(nullptr), need_free_() { } virtual ~ObPLExecState(); int init(const ParamStore *params = NULL, bool is_anonymous = false); int defend_stored_routine_change(const ObObjParam &actual_param, const ObPLDataType &formal_param_type); int check_routine_param_legal(ParamStore *params = NULL); int check_anonymous_collection_compatible(const ObPLComposite &composite, const ObPLDataType &dest_type, bool &need_cast); int convert_composite(ObObjParam ¶m, const ObPLDataType &dest_type); int init_params(const ParamStore *params = NULL, bool is_anonymous = false); int execute(); int final(int ret); int deep_copy_result_if_need(common::ObIAllocator &allocator); int init_complex_obj(common::ObIAllocator &allocator, const ObPLDataType &pl_type, common::ObObjParam &obj, bool set_null = true); inline const common::ObObj &get_result() const { return result_; } inline common::ObIAllocator *get_allocator() { return ctx_.allocator_; } inline const sql::ObPhysicalPlanCtx &get_physical_plan_ctx() const { return phy_plan_ctx_; } inline sql::ObPhysicalPlanCtx &get_physical_plan_ctx() { return phy_plan_ctx_; } inline const ParamStore &get_params() const { return phy_plan_ctx_.get_param_store(); } inline ParamStore &get_params() { return phy_plan_ctx_.get_param_store_for_update(); } ObPLFunction &get_function() { return func_; } int get_var(int64_t var_idx, ObObjParam& result); int set_var(int64_t var_idx, const ObObjParam& value); ObPLExecCtx& get_exec_ctx() { return ctx_; } int check_pl_execute_priv(ObSchemaGetterGuard &guard, const uint64_t tenant_id, const uint64_t user_id, const ObSchemaObjVersion &schema_obj, const ObIArray &role_id_array); int check_pl_priv(share::schema::ObSchemaGetterGuard &guard, const uint64_t tenant_id, const uint64_t user_id, const sql::DependenyTableStore &dep_obj); inline bool is_top_call() const { return top_call_; } inline uint64_t get_loc() const { return loc_; } inline void set_loc(uint64_t loc) { loc_ = loc; } inline uint64_t get_current_line() { return (get_loc() >> 32) + 1; } inline void set_current_line(int64_t current_line) { current_line = (current_line > 0) ? (current_line - 1) : current_line; set_loc((current_line) << 32 | (get_loc() & 0x00000000ffffffff)); } inline bool is_called_from_sql() const { return is_called_from_sql_; } inline void set_is_called_from_sql(bool flag) { is_called_from_sql_ = flag; } inline void set_dwarf_helper(jit::ObDWARFHelper *dwarf_helper) { dwarf_helper_ = dwarf_helper; } inline jit::ObDWARFHelper* get_dwarf_helper() { return dwarf_helper_; } inline void add_pure_sql_exec_time(int64_t sql_exec_time) { pure_sql_exec_time_ += sql_exec_time; } inline void reset_pure_sql_exec_time() { pure_sql_exec_time_ = 0; } int64_t get_pure_sql_exec_time() { return pure_sql_exec_time_; } int add_pl_exec_time(int64_t pl_exec_time, bool is_called_from_sql); void reset_plsql_exec_time() { pure_plsql_exec_time_ = 0; } void add_plsql_exec_time(int64_t plsql_exec_time) { pure_plsql_exec_time_ = plsql_exec_time; } int64_t get_plsql_exec_time() { return pure_plsql_exec_time_; } void add_sub_plsql_exec_time(int64_t sub_plsql_exec_time) { pure_sub_plsql_exec_time_ += sub_plsql_exec_time; } int64_t get_sub_plsql_exec_time() { return pure_sub_plsql_exec_time_; } void reset_sub_plsql_exec_time() { pure_sub_plsql_exec_time_ = 0; } inline void set_profiler_time_stack(ObPLProfilerTimeStack *time_stack) { profiler_time_stack_ = time_stack;} inline ObPLProfilerTimeStack *get_profiler_time_stack() { return profiler_time_stack_; } bool need_free_arg(int64_t i) { return need_free_.count() > i ? need_free_.at(i) : false; } ObPLContext *get_top_pl_context() { return top_context_; } TO_STRING_KV(K_(inner_call), K_(top_call), K_(need_reset_physical_plan), K_(loc), K_(is_called_from_sql), K_(pure_sql_exec_time), K_(pure_plsql_exec_time), K_(pure_sub_plsql_exec_time)); private: private: ObPLFunction &func_; sql::ObPhysicalPlanCtx phy_plan_ctx_; //运行态的param值放在这里面,跟ObPLFunction里的variables_一一对应,初始化的时候需要设置default值 sql::ObEvalCtx eval_ctx_; common::ObObj &result_; ObPLExecCtx ctx_; bool inner_call_; bool top_call_; ExecCtxBak exec_ctx_bak_; bool need_reset_physical_plan_; ObPLContext *top_context_; uint64_t loc_; // combine of line and column number bool is_called_from_sql_; jit::ObDWARFHelper *dwarf_helper_; // for decode dwarf debuginfo int64_t pure_sql_exec_time_; int64_t pure_plsql_exec_time_; int64_t pure_sub_plsql_exec_time_; ObPLProfilerTimeStack *profiler_time_stack_; common::ObSEArray need_free_; }; class ObPLCallStackTrace; class ObPLContext { friend class LinkPLStackGuard; public: ObPLContext() #ifdef OB_BUILD_ORACLE_PL : call_stack_trace_(nullptr), alloc_("PlCallStack", OB_MALLOC_NORMAL_BLOCK_SIZE, MTL_ID()) #endif { reset(); } virtual ~ObPLContext() { reset(); } void reset() { inc_recursion_depth_ = false; reset_autocommit_ = false; has_stash_savepoint_ = false; has_implicit_savepoint_ = false; has_inner_dml_write_ = false; is_top_stack_ = false; exception_handler_illegal_ = false; need_reset_exec_env_ = false; is_autonomous_ = false; saved_session_.reset(); saved_has_implicit_savepoint_ = false; database_id_ = OB_INVALID_ID; need_reset_default_database_ = false; session_info_ = NULL; exec_stack_.reset(); need_reset_role_id_array_ = false; old_role_id_array_.reset(); old_priv_user_id_ = OB_INVALID_ID; old_in_definer_ = false; has_output_arguments_ = false; #ifdef OB_BUILD_ORACLE_PL if (call_stack_trace_ != nullptr) { call_stack_trace_->~ObPLCallStackTrace(); } call_stack_trace_ = nullptr; #endif old_worker_timeout_ts_ = 0; old_phy_plan_timeout_ts_ = 0; parent_stack_ctx_ = nullptr; top_stack_ctx_ = nullptr; my_exec_ctx_ = nullptr; cur_query_.reset(); is_function_or_trigger_ = false; last_insert_id_ = 0; trace_id_.reset(); old_user_priv_set_ = OB_PRIV_SET_EMPTY; old_db_priv_set_ = OB_PRIV_SET_EMPTY; is_inner_mock_ = false; is_system_trigger_ = false; } int is_inited() { return session_info_ != NULL; } int init(sql::ObSQLSessionInfo &session_info, sql::ObExecContext &ctx, ObPLFunction *routine, bool is_function_or_trigger, ObIAllocator *allocator = NULL, const bool is_dblink = false); void destory(sql::ObSQLSessionInfo &session_info, sql::ObExecContext &ctx, int &ret); inline ObPLCursorInfo& get_cursor_info() { return cursor_info_; } inline ObPLSqlCodeInfo& get_sqlcode_info() { return sqlcode_info_; } inline bool has_implicit_savepoint() { return has_implicit_savepoint_; } inline void clear_implicit_savepoint() { has_implicit_savepoint_ = false; } inline void set_has_implicit_savepoint(bool v) { has_implicit_savepoint_ = v; } bool is_top_stack() { return is_top_stack_; } inline bool is_exception_handler_illegal() const { return exception_handler_illegal_; } inline void set_exception_handler_illegal() { exception_handler_illegal_ = true; } inline void set_reset_autocommit() { reset_autocommit_ = true; } inline bool get_reset_autocommit() const { return reset_autocommit_; } static int valid_execute_context(sql::ObExecContext &ctx); static int check_stack_overflow(); static int check_routine_legal(ObPLFunction &routine, bool in_function, bool in_tg); static int debug_start(sql::ObSQLSessionInfo *sql_session); static int debug_stop(sql::ObSQLSessionInfo *sql_session); static int notify(sql::ObSQLSessionInfo *sql_session); static int get_exec_state_from_local(sql::ObSQLSessionInfo &session_info, int64_t package_id, int64_t routine_id, ObPLExecState *&plstate); static int get_param_store_from_local(ObSQLSessionInfo &session_info, int64_t package_id, int64_t routine_id, ParamStore *¶ms); static int get_routine_from_local(sql::ObSQLSessionInfo &session_info, int64_t package_id, int64_t routine_id, ObPLFunction *&routine); static int get_subprogram_var_from_local(sql::ObSQLSessionInfo &session_info, int64_t package_id, int64_t routine_id, int64_t var_idx, ObObjParam &result); static int set_subprogram_var_from_local(sql::ObSQLSessionInfo &session_info, int64_t package_id, int64_t routine_id, int64_t var_idx, const ObObjParam &value); static int check_debug_priv(ObSchemaGetterGuard *guard, sql::ObSQLSessionInfo *sess_info, ObPLFunction *func); int inc_and_check_depth(int64_t package_id, int64_t routine_id, bool is_function); void dec_and_check_depth(int64_t package_id, int64_t routine_id, int &ret, bool inner_call); int set_exec_env(ObPLFunction &routine); int set_default_database(ObPLFunction &routine, share::schema::ObSchemaGetterGuard &guard); void reset_exec_env(int &ret); void reset_default_database(int &ret); int set_role_id_array(ObPLFunction &routine, share::schema::ObSchemaGetterGuard &guard); void reset_role_id_array(int &ret); ObIArray &get_exec_stack() { return exec_stack_; } ObPLExecState *get_current_state() { return exec_stack_.empty() ? NULL : exec_stack_.at(exec_stack_.count() - 1); } ObPLFunction *get_current_routine() { return NULL == get_current_state() ? NULL : &get_current_state()->get_function(); } ObPLExecCtx *get_current_ctx() { return NULL == get_current_state() ? NULL : &get_current_state()->get_exec_ctx(); } bool has_output_arguments() { return has_output_arguments_; } void set_has_output_arguments(bool has_output_arguments) { has_output_arguments_ = has_output_arguments; } static int implicit_end_trans( sql::ObSQLSessionInfo &session, sql::ObExecContext &ctx, bool is_rollback, bool can_async = false); inline ObString get_database_name() const { return database_name_.string(); } inline uint64_t get_database_id() const { return database_id_; } inline bool is_function_or_trigger() const { return is_function_or_trigger_; } inline bool is_system_trigger() const { return is_system_trigger_; } inline void set_is_system_trigger(bool v) { is_system_trigger_ = v; } bool is_autonomous() const { return is_autonomous_; } void clear_autonomous() { is_autonomous_ = false; } bool in_autonomous() const; int end_autonomous(ObExecContext &ctx, sql::ObSQLSessionInfo &session_info); bool in_nested_sql_ctrl() const { return ObStmt::is_dml_stmt(my_exec_ctx_->get_sql_ctx()->stmt_type_) && !in_autonomous(); } pl::ObPLContext *get_parent_stack_ctx() { return parent_stack_ctx_; } pl::ObPLContext *get_top_stack_ctx() { return top_stack_ctx_; } sql::ObExecContext *get_my_exec_ctx() { return my_exec_ctx_; } ObCurTraceId::TraceId get_trace_id() const { return trace_id_; } void set_is_inner_mock(bool is_inner_mock) { is_inner_mock_ = is_inner_mock; } bool get_is_inner_mock() const { return is_inner_mock_; } #ifdef OB_BUILD_ORACLE_PL ObPLCallStackTrace *get_call_stack_trace(); ObIAllocator &get_allocator() { return alloc_; } #endif private: ObPLContext* get_stack_pl_ctx(); void set_parent_stack_ctx(pl::ObPLContext *parent_stack_ctx) { parent_stack_ctx_ = parent_stack_ctx; top_stack_ctx_ = (parent_stack_ctx == nullptr) ? this : parent_stack_ctx->get_top_stack_ctx(); } void set_my_exec_ctx(sql::ObExecContext *my_exec_ctx) { my_exec_ctx_ = my_exec_ctx; } static void record_tx_id_before_begin_autonomous_session_for_deadlock_(ObSQLSessionInfo &session_info, transaction::ObTransID &last_trans_id); static void register_after_begin_autonomous_session_for_deadlock_(ObSQLSessionInfo &session_info, const transaction::ObTransID last_trans_id); private: ObPLCursorInfo cursor_info_; ObPLSqlCodeInfo sqlcode_info_; ObPLExecRecursionCtx recursion_ctx_; bool inc_recursion_depth_; bool reset_autocommit_; bool has_stash_savepoint_; bool has_implicit_savepoint_; bool has_inner_dml_write_; bool is_top_stack_; bool is_autonomous_; sql::ObBasicSessionInfo::TransSavedValue saved_session_; bool saved_has_implicit_savepoint_; bool exception_handler_illegal_; sql::ObExecEnv exec_env_; bool need_reset_exec_env_; ObSqlString database_name_; uint64_t database_id_; common::ObSEArray old_role_id_array_; uint64_t old_priv_user_id_; ObPrivSet old_user_priv_set_; ObPrivSet old_db_priv_set_; bool old_in_definer_; bool need_reset_default_database_; bool need_reset_role_id_array_; bool has_output_arguments_; int64_t old_worker_timeout_ts_; int64_t old_phy_plan_timeout_ts_; sql::ObSQLSessionInfo *session_info_; common::ObString cur_query_; common::ObSEArray exec_stack_; #ifdef OB_BUILD_ORACLE_PL ObPLCallStackTrace *call_stack_trace_; ObArenaAllocator alloc_; #endif ObPLContext *parent_stack_ctx_; ObPLContext *top_stack_ctx_; sql::ObExecContext *my_exec_ctx_; //my exec context bool is_function_or_trigger_; uint64_t last_insert_id_; ObCurTraceId::TraceId trace_id_; bool is_inner_mock_; bool is_system_trigger_; }; struct PlTransformTreeCtx { ObIAllocator *allocator_; ParamStore *params_; char *buf_; // 反拼后的参数化字符串 int64_t buf_len_; int64_t buf_size_; ObString raw_sql_; // 原始匿名块字符串 int64_t raw_anonymous_off_; // 原始匿名块相对于用户输入首字符的偏移, 解决单个分隔符内存在多个sql场景 ObString raw_sql_or_expr_; // 匿名块内部单个expr或者sql原始字符串 ObString no_param_sql_; // 匿名块内部单个expr或者sql对应的fast parser后字符串 int64_t copied_idx_; ParamList *p_list_; // 存储匿名块内部所有expr和sql语句fast parser后得到的raw param node int64_t raw_param_num_; // 匿名块内部单个expr或者sql fast parser后raw param node的个数, 每个expr和sql fast parser后, 会将param num存储在node节点中 PlTransformTreeCtx() : allocator_(NULL), params_(NULL), buf_(NULL), buf_len_(0), buf_size_(0), raw_sql_(), raw_anonymous_off_(0), raw_sql_or_expr_(), no_param_sql_(), copied_idx_(0), p_list_(NULL), raw_param_num_(0) {} }; class ObPL { friend class sql::ObAlterRoutineResolver; public: ObPL() : sql_proxy_(NULL), package_manager_(), interface_service_(), codegen_lock_() {} virtual ~ObPL() {} int init(common::ObMySQLProxy &sql_proxy); void destory(); public: // for anonymous + ps int execute(sql::ObExecContext &ctx, ParamStore ¶ms, uint64_t stmt_id, const common::ObString &sql, ObBitSet &out_args); int parameter_anonymous_block(ObExecContext &ctx, const ObStmtNodeTree *block, ParamStore ¶ms, ObIAllocator &allocator, ObCacheObjGuard &cacheobj_guard); int transform_tree(PlTransformTreeCtx &trans_ctx, ParseNode *block, ParseNode *no_param_root, ObExecContext &ctx, ParseResult &parse_result); int trans_sql(PlTransformTreeCtx &trans_ctx, ParseNode *root, ObExecContext &ctx); // for anonymous int execute(sql::ObExecContext &ctx, ParamStore ¶ms, const ObStmtNodeTree *block); // for normal routine or package routine int execute(sql::ObExecContext &ctx, ObIAllocator &allocator, uint64_t package_id, uint64_t routine_id, const ObIArray &subprogram_path, ParamStore ¶ms, const ObIArray &nocopy_params, common::ObObj &result, int *status = NULL, bool inner_call = false, bool in_function = false, uint64_t loc = 0, bool is_called_from_sql = false, uint64_t dblink_id = OB_INVALID_ID, const ObRoutineInfo *dblink_routine_info = NULL); int check_exec_priv(sql::ObExecContext &ctx, const ObString &database_name, ObPLFunction *routine); private: // for normal routine int get_pl_function(sql::ObExecContext &ctx, ObPLPackageGuard &package_guard, int64_t package_id, int64_t routine_id, const ObIArray &subprogram_path, ObCacheObjGuard& cacheobj_guard, ObPLFunction *&local_routine); // for anonymous + ps int get_pl_function(sql::ObExecContext &ctx, ParamStore ¶ms, uint64_t stmt_id, const ObString &sql, ObCacheObjGuard& cacheobj_guard); // for anonymous + ps int generate_pl_function(sql::ObExecContext &ctx, const ObString &anonymouse_sql, ParamStore ¶ms, ParseNode &node, ObCacheObjGuard& cacheobj_guard, const uint64_t stmt_id, bool is_anonymous_text = false); // for inner common execute int execute(sql::ObExecContext &ctx, ObIAllocator &allocator, ObPLPackageGuard &package_guard, ObPLFunction &routine, ParamStore *params, const ObIArray *nocopy_params, ObObj *result, int *status = NULL, bool is_top_stack = false, bool is_inner_call = false, bool is_in_function = false, bool is_anonymous = false, uint64_t loc = 0, bool is_called_from_sql = false); public: // for normal routine static int generate_pl_function(sql::ObExecContext &ctx, uint64_t proc_id, ObCacheObjGuard& cacheobj_guard); // add pl to cache static int add_pl_lib_cache(ObPLFunction *pl_func, ObPLCacheCtx &pc_ctx); static int execute_proc(ObPLExecCtx &ctx, uint64_t package_id, uint64_t proc_id, int64_t *subprogram_path, int64_t path_length, uint64_t line_num, /* call position line number, for call_stack info*/ int64_t argc, common::ObObjParam **argv, int64_t *nocopy_argv, uint64_t dblink_id); static int set_user_type_var(ObPLExecCtx *ctx, int64_t var_index, int64_t var_addr, int64_t init_size); static int set_implicit_cursor_in_forall(ObPLExecCtx *ctx, bool save_exception); static int unset_implicit_cursor_in_forall(ObPLExecCtx *ctx); inline ObPLPackageManager &get_package_manager() { return package_manager_; } inline common::ObMySQLProxy *get_sql_proxy() { return sql_proxy_; } inline const ObPLInterfaceService &get_interface_service() const { return interface_service_; } static int insert_error_msg(int errcode); static int simple_execute(ObPLExecCtx *ctx, int64_t argc, int64_t *argv); static int check_trigger_arg(ParamStore ¶ms, const ObPLFunction &func, ObPLContext &pl_ctx, ObExecContext &ctx); std::pair& get_jit_lock() { return jit_lock_; } static int check_session_alive(const ObBasicSessionInfo &session); private: common::ObMySQLProxy *sql_proxy_; ObPLPackageManager package_manager_; ObPLInterfaceService interface_service_; common::ObBucketLock codegen_lock_; // first bucket is for deduplication, second bucket is for concurrency control std::pair jit_lock_; }; class LinkPLStackGuard { public: LinkPLStackGuard(sql::ObExecContext &exec_ctx, ObPLContext &pl_stack) : exec_ctx_(exec_ctx), parent_stack_(nullptr) { //last_pl_stack means the last pl stack in all execution stack //such as: pl_stack1->sql_stack->pl_stack2, pl_stack2.last_pl_stack is pl_stack1 ObPLContext *last_pl_stack = nullptr; //parent_stack means the direct parent pl stack in current execution stack //such as: pl_stack1->sql_stack->pl_stack2, pl_stack2.parent_stack is nullptr ObExecContext *cur_exec_ctx = &exec_ctx; while (cur_exec_ctx != nullptr && last_pl_stack == nullptr) { if (cur_exec_ctx->get_pl_stack_ctx() != nullptr) { last_pl_stack = cur_exec_ctx->get_pl_stack_ctx(); if (cur_exec_ctx == &exec_ctx) { parent_stack_ = cur_exec_ctx->get_pl_stack_ctx(); } } else { //find the parent execution stack cur_exec_ctx = cur_exec_ctx->get_parent_ctx(); } } pl_stack.set_parent_stack_ctx(last_pl_stack); pl_stack.set_my_exec_ctx(&exec_ctx_); exec_ctx_.set_pl_stack_ctx(&pl_stack); } ~LinkPLStackGuard() { //pop the pl stack from current execution stack exec_ctx_.set_pl_stack_ctx(parent_stack_); } private: sql::ObExecContext &exec_ctx_; ObPLContext *parent_stack_; }; class ObPLASHGuard { public: enum ObPLASHStatus { INVALID_ASH_STATUS, IS_PLSQL_COMPILATION, IS_PLSQL_EXECUTION, IS_SQL_EXECUTION, }; ObPLASHGuard(ObPLASHStatus status); ObPLASHGuard(int64_t package_id, int64_t routine_id); ObPLASHGuard(int64_t package_id, int64_t routine_id, const ObString &routine_name); ~ObPLASHGuard(); private: char plsql_current_subprogram_name_[common::OB_MAX_ASH_PL_NAME_LENGTH + 1]; bool in_plsql_compilation_; bool in_plsql_execution_; int64_t plsql_entry_object_id_; int64_t plsql_entry_subprogram_id_; int64_t plsql_current_object_id_; int64_t plsql_current_subprogram_id_; bool set_entry_info_; bool set_entry_name_; bool set_current_name_; ObPLASHStatus pl_ash_status_; }; } } #endif /* OCEANBASE_SRC_PL_OB_PL_H_ */