Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/pl/ob_pl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ int ObPL::init(common::ObMySQLProxy &sql_proxy)
WRAP_SPI_CALL(sql::ObSPIService::spi_cursor_close));
jit::ObLLVMHelper::add_symbol(ObString("spi_process_resignal"),
WRAP_SPI_CALL(sql::ObSPIService::spi_process_resignal));
jit::ObLLVMHelper::add_symbol(ObString("spi_pl_set_user_error_msg"),
WRAP_SPI_CALL(sql::ObSPIService::spi_pl_set_user_error_msg));
jit::ObLLVMHelper::add_symbol(ObString("spi_destruct_collection"),
WRAP_SPI_CALL(sql::ObSPIService::spi_destruct_collection));
jit::ObLLVMHelper::add_symbol(ObString("spi_reset_composite"),
Expand Down
43 changes: 43 additions & 0 deletions src/pl/ob_pl_code_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2051,6 +2051,38 @@ int ObPLCodeGenerateVisitor::visit(const ObPLSignalStmt &s)
OZ (generator_.get_helper().get_int64(s.get_stmt_id(), stmt_id));
// Temporarily use stmtid, this id is the combination of col and line
OZ (generator_.get_helper().get_int64(s.get_stmt_id(), loc));
// For deferred resolver errors (e.g. OB_ERR_BAD_TABLE captured in a
// trigger/SP body), the formatted user message — which embeds the
// offending object name — was preserved on the stmt. Push it back into
// the thread-local warning buffer before raising so the runtime error
// text matches the original LOG_USER_ERROR output instead of the bare
// error template.
if (s.has_user_msg()) {
ObLLVMValue user_err_code;
ObLLVMValue user_sql_state;
ObLLVMValue user_sql_state_len;
ObLLVMValue user_msg_val;
ObLLVMValue user_msg_len;
ObSEArray<ObLLVMValue, 5> set_msg_args;
ObLLVMValue set_msg_ret;
OZ (set_msg_args.push_back(generator_.get_vars().at(generator_.CTX_IDX)));
// Use the OB-internal error code (e.g. -5201) so the value matches the
// err code that ObMPPacketSender::send_error_packet compares against
// ObWarningBuffer::get_err_code() when deciding whether to read the
// formatted user message.
OZ (generator_.get_helper().get_int64(s.get_ob_error_code(), user_err_code));
OZ (set_msg_args.push_back(user_err_code));
OZ (generator_.generate_global_string(ObString(s.get_str_len(), s.get_sql_state()),
user_sql_state, user_sql_state_len));
OZ (set_msg_args.push_back(user_sql_state));
OZ (generator_.generate_global_string(s.get_user_msg(), user_msg_val, user_msg_len));
OZ (set_msg_args.push_back(user_msg_val));
OZ (set_msg_args.push_back(user_msg_len));
OZ (generator_.get_helper().create_call(ObString("spi_pl_set_user_error_msg"),
generator_.get_spi_service().spi_pl_set_user_error_msg_,
set_msg_args,
set_msg_ret));
}
OZ (generator_.generate_destruct_out_params());
OZ (generator_.generate_exception(type, ob_err_code, err_code, sql_state, str_len, stmt_id,
normal, loc, s.get_block()->in_notfound(),
Expand Down Expand Up @@ -3462,6 +3494,17 @@ int ObPLCodeGenerator::init_spi_service()
OZ (ObLLVMFunctionType::get(int32_type, arg_types, ft));
OZ (helper_.create_function(ObString("spi_process_resignal"), ft, spi_service_.spi_process_resignal_error_));
}
if (OB_SUCC(ret)) {
// spi_pl_set_user_error_msg(ctx, err_code, sql_state, msg, msg_len)
arg_types.reset();
OZ (arg_types.push_back(pl_exec_context_pointer_type));
OZ (arg_types.push_back(int64_type));
OZ (arg_types.push_back(char_type));
OZ (arg_types.push_back(char_type));
OZ (arg_types.push_back(int64_type));
OZ (ObLLVMFunctionType::get(int32_type, arg_types, ft));
OZ (helper_.create_function(ObString("spi_pl_set_user_error_msg"), ft, spi_service_.spi_pl_set_user_error_msg_));
}
if (OB_SUCC(ret)) {
arg_types.reset();
OZ (arg_types.push_back(pl_exec_context_pointer_type));
Expand Down
1 change: 1 addition & 0 deletions src/pl/ob_pl_code_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ friend class ObPLCGBufferGuard;
jit::ObLLVMFunction spi_update_package_change_info_;
jit::ObLLVMFunction spi_check_composite_not_null_;
jit::ObLLVMFunction spi_process_resignal_error_;
jit::ObLLVMFunction spi_pl_set_user_error_msg_;
jit::ObLLVMFunction spi_check_autonomous_trans_;
jit::ObLLVMFunction spi_opaque_assign_null_;
jit::ObLLVMFunction spi_pl_profiler_before_record_;
Expand Down
24 changes: 24 additions & 0 deletions src/pl/ob_pl_resolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,27 @@ int ObPLResolver::resolve(const ObStmtNodeTree *parse_tree, ObPLFunctionAST &fun
&& lib::is_mysql_mode()) {
ObPLSignalStmt *signal_stmt = NULL;
int save_ret = ret;
// Capture the formatted user error message from the thread-local
// warning buffer before we destroy the failed stmt. Some deferred
// errors (e.g. OB_ERR_BAD_TABLE) embed object names in the message
// template via LOG_USER_ERROR; without preserving the formatted text
// here, the runtime SIGNAL would only carry the bare error template
// and the object name (e.g. "'t.notable'") would be lost.
common::ObString saved_user_msg;
{
common::ObWarningBuffer *wb = common::ob_get_tsi_warning_buffer();
if (OB_NOT_NULL(wb) && wb->get_err_code() == save_ret) {
const char *err_msg = wb->get_err_msg();
if (OB_NOT_NULL(err_msg) && '\0' != err_msg[0]) {
const int64_t msg_len = STRLEN(err_msg);
char *buf = static_cast<char *>(resolve_ctx_.allocator_.alloc(msg_len));
if (OB_NOT_NULL(buf)) {
MEMCPY(buf, err_msg, msg_len);
saved_user_msg.assign_ptr(buf, static_cast<int32_t>(msg_len));
}
}
}
}
if (NULL != stmt) {
stmt->~ObPLStmt();
stmt = NULL;
Expand All @@ -630,6 +651,9 @@ int ObPLResolver::resolve(const ObStmtNodeTree *parse_tree, ObPLFunctionAST &fun
signal_stmt->set_ob_error_code(save_ret);
signal_stmt->set_sql_state(ob_sqlstate(save_ret));
signal_stmt->set_str_len(STRLEN(ob_sqlstate(save_ret)));
if (!saved_user_msg.empty()) {
signal_stmt->set_user_msg(saved_user_msg);
}
func.set_has_incomplete_rt_dep_error(true);
func.set_is_all_sql_stmt(false);
if (lib::is_mysql_mode() && !func.is_reads_sql_data() && !func.is_modifies_sql_data()) {
Expand Down
13 changes: 12 additions & 1 deletion src/pl/ob_pl_stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -2592,7 +2592,8 @@ class ObPLSignalStmt : public ObPLStmt
item_to_expr_idx_(),
ob_error_code_(0),
is_signal_null_(false),
is_resignal_stmt_(false) {}
is_resignal_stmt_(false),
user_msg_() {}
virtual ~ObPLSignalStmt() { item_to_expr_idx_.destroy(); }

int accept(ObPLStmtVisitor &visitor) const;
Expand Down Expand Up @@ -2622,6 +2623,13 @@ class ObPLSignalStmt : public ObPLStmt
inline void set_is_resignal_stmt() { is_resignal_stmt_ = true; }
inline bool is_resignal_stmt() const { return is_resignal_stmt_; }

// Carries a pre-formatted user error message captured at PL resolve time
// for deferred errors (e.g. OB_ERR_BAD_TABLE) that were turned into a
// PL_SIGNAL stub. Empty when the SIGNAL was written by the user.
inline const common::ObString &get_user_msg() const { return user_msg_; }
inline void set_user_msg(const common::ObString &msg) { user_msg_ = msg; }
inline bool has_user_msg() const { return !user_msg_.empty(); }

TO_STRING_KV(K_(type), K_(label), K_(value));

private:
Expand All @@ -2630,6 +2638,9 @@ class ObPLSignalStmt : public ObPLStmt
int ob_error_code_;
bool is_signal_null_; // In Oracle mode, RAISE; statement without specifying an exception name, in this case, the current exception needs to be thrown
bool is_resignal_stmt_;
// Pre-formatted user error message preserved across compile->runtime for
// deferred errors. Memory is owned by the PL function's allocator.
common::ObString user_msg_;
};

class ObPLCallStmt : public ObPLStmt
Expand Down
28 changes: 28 additions & 0 deletions src/sql/ob_spi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4448,6 +4448,34 @@ int ObSPIService::spi_process_resignal(pl::ObPLExecCtx *ctx,
}

#undef GET_INTEGER_FROM_OBJ
int ObSPIService::spi_pl_set_user_error_msg(pl::ObPLExecCtx *ctx,
int64_t err_code,
const char *sql_state,
const char *msg,
int64_t msg_len)
{
int ret = OB_SUCCESS;
UNUSED(ctx);
if (OB_NOT_NULL(msg) && msg_len > 0) {
ObWarningBuffer *wb = common::ob_get_tsi_warning_buffer();
if (OB_NOT_NULL(wb)) {
// ObWarningBuffer caps each item at WarningItem::STR_LEN, so truncation
// here matches the runtime behaviour of LOG_USER_ERROR.
char buf[ObWarningBuffer::WarningItem::STR_LEN] = {0};
int64_t copy_len = msg_len < (ObWarningBuffer::WarningItem::STR_LEN - 1)
? msg_len
: (ObWarningBuffer::WarningItem::STR_LEN - 1);
MEMCPY(buf, msg, copy_len);
buf[copy_len] = '\0';
wb->set_error(buf, static_cast<int>(err_code));
if (OB_NOT_NULL(sql_state)) {
wb->set_sql_state(sql_state);
}
}
}
return ret;
}

int ObSPIService::spi_destruct_collection(ObPLExecCtx *ctx, int64_t idx)
{
int ret = OB_SUCCESS;
Expand Down
11 changes: 11 additions & 0 deletions src/sql/ob_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,17 @@ class ObSPIService
const char *resignal_sql_state,
bool is_signal);

// Populate the current thread-local warning buffer with a pre-formatted
// user error message captured at PL resolve time. Used by deferred
// OB_ERR_BAD_TABLE (and similar) signals emitted from trigger/SP bodies so
// that the runtime error text matches the resolver-time text (which
// contains object names) instead of the bare error template.
static int spi_pl_set_user_error_msg(pl::ObPLExecCtx *ctx,
int64_t err_code,
const char *sql_state,
const char *msg,
int64_t msg_len);

static int acquire_spi_conn(ObMySQLProxy &sql_proxy,
ObSQLSessionInfo &session_info,
observer::ObInnerSQLConnection *&spi_conn);
Expand Down
Loading