diff --git a/src/sql/engine/expr/ob_pl_expr_subquery.cpp b/src/sql/engine/expr/ob_pl_expr_subquery.cpp index 88b75863bf..de7969a871 100644 --- a/src/sql/engine/expr/ob_pl_expr_subquery.cpp +++ b/src/sql/engine/expr/ob_pl_expr_subquery.cpp @@ -159,7 +159,7 @@ int ObExprOpSubQueryInPl::eval_subquery(const ObExpr &expr, SMART_VAR(ObSPIResultSet, spi_result) { OZ (spi_result.init(*session)); - OZ (spi_result.start_nested_stmt_if_need(&pl_exec_ctx, static_cast(info->type_), false)); + 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; diff --git a/src/sql/ob_spi.cpp b/src/sql/ob_spi.cpp index 3dfb9fc88b..f4d40b7a8b 100644 --- a/src/sql/ob_spi.cpp +++ b/src/sql/ob_spi.cpp @@ -225,9 +225,46 @@ int ObSPIResultSet::alloc_saved_value(sql::ObSQLSessionInfo::StmtSavedValue *&se return ret; } -int ObSPIResultSet::check_nested_stmt_legal(ObExecContext &exec_ctx, stmt::StmtType stmt_type, bool for_update) +int ObSPIResultSet::is_set_global_var(ObSQLSessionInfo &session, const ObString &sql, bool &has_global_variable) { int ret = OB_SUCCESS; + has_global_variable = false; + ObArenaAllocator allocator; + ParseResult parse_result; + ParseMode parse_mode = STD_MODE; + ObParser parser(allocator, session.get_sql_mode(), session.get_local_collation_connection()); + if (sql.empty()) { + } else if (OB_FAIL(parser.parse(sql, + parse_result, + parse_mode, + false/*is_batched_multi_stmt_split_on*/, + false/*no_throw_parser_error*/, + true))) { + LOG_WARN("generate syntax tree failed", K(sql), K(ret)); + } else if (OB_NOT_NULL(parse_result.result_tree_) && + parse_result.result_tree_->num_child_ > 0 && + OB_NOT_NULL(parse_result.result_tree_->children_[0])) { + ParseNode *set_node = NULL; + ParseNode *parse_tree = parse_result.result_tree_->children_[0]; + for (int64_t i = 0; OB_SUCC(ret) && !has_global_variable && i < parse_tree->num_child_; ++i) { + if (OB_ISNULL(set_node = parse_tree->children_[i])) { + ret = OB_ERR_UNEXPECTED; + LOG_ERROR("set node is NULL", K(ret)); + } else if (OB_UNLIKELY(T_VAR_VAL != set_node->type_)) { + ret = OB_ERR_UNEXPECTED; + LOG_ERROR("set_node->type_ must be T_VAR_VAL", K(ret), K(set_node->type_)); + } else if (1 == set_node->value_) { // global var + has_global_variable = true; + } + } + } + return ret; +} + +int ObSPIResultSet::check_nested_stmt_legal(ObExecContext &exec_ctx, const ObString &sql, stmt::StmtType stmt_type, bool for_update) +{ + int ret = OB_SUCCESS; + bool has_global_variable = false; stmt::StmtType parent_stmt_type = stmt::T_NONE; ObSqlCtx *sql_ctx = exec_ctx.get_sql_ctx(); ObPLContext *pl_ctx = exec_ctx.get_pl_stack_ctx(); @@ -238,6 +275,9 @@ int ObSPIResultSet::check_nested_stmt_legal(ObExecContext &exec_ctx, stmt::StmtT parent_stmt_type = sql_ctx->stmt_type_; LOG_DEBUG("check nested stmt legal", K(parent_stmt_type), K(pl_ctx->in_autonomous()), K(stmt_type)); } + if (stmt::T_VARIABLE_SET == stmt_type && OB_NOT_NULL(exec_ctx.get_my_session())) { + OZ (is_set_global_var(*exec_ctx.get_my_session(), sql, has_global_variable)); + } if (OB_SUCC(ret) && !pl_ctx->in_autonomous() && ObStmt::is_dml_stmt(parent_stmt_type)) { //only when parent stmt is dml or select, it can trigger a nested sql if (parent_stmt_type == stmt::T_SELECT && ObStmt::is_dml_write_stmt(stmt_type)) { @@ -262,7 +302,7 @@ int ObSPIResultSet::check_nested_stmt_legal(ObExecContext &exec_ctx, stmt::StmtT LOG_WARN("ORA-14551: cannot perform a DML operation inside a query", K(ret), K(stmt_type), K(exec_ctx.get_sql_ctx()), K(&exec_ctx), K(exec_ctx.get_my_session()->get_cur_exec_ctx())); - } else if (ObStmt::is_ddl_stmt(stmt_type, false) || ObStmt::is_tcl_stmt(stmt_type)) { + } else if (ObStmt::is_ddl_stmt(stmt_type, has_global_variable) || ObStmt::is_tcl_stmt(stmt_type)) { ret = lib::is_oracle_mode() ? OB_NOT_SUPPORTED : OB_ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG; LOG_WARN("ORA-14552: Cannot Perform a DDL Commit or Rollback Inside a Query or DML tips", K(ret), K(stmt_type), K(lbt())); @@ -358,7 +398,7 @@ void ObSPIResultSet::end_cursor_stmt(ObPLExecCtx *pl_ctx, int &result) return; } -int ObSPIResultSet::start_nested_stmt_if_need(ObPLExecCtx *pl_ctx, stmt::StmtType stmt_type, bool for_update) +int ObSPIResultSet::start_nested_stmt_if_need(ObPLExecCtx *pl_ctx, const ObString &sql, stmt::StmtType stmt_type, bool for_update) { int ret = OB_SUCCESS; ObSQLSessionInfo *session = NULL; @@ -371,7 +411,7 @@ int ObSPIResultSet::start_nested_stmt_if_need(ObPLExecCtx *pl_ctx, stmt::StmtTyp } else if (OB_NOT_NULL(pl_ctx->exec_ctx_->get_pl_stack_ctx()) && pl_ctx->exec_ctx_->get_pl_stack_ctx()->in_nested_sql_ctrl()) { // 嵌套的顶层语句一定是一个DML语句, 并且开启了事务, 此时走fast_select流程 - OZ (check_nested_stmt_legal(*pl_ctx->exec_ctx_, stmt_type, for_update)); + OZ (check_nested_stmt_legal(*pl_ctx->exec_ctx_, sql, stmt_type, for_update)); OZ (begin_nested_session(*session)); OX (session->set_query_start_time(ObTimeUtility::current_time())); OX (need_end_nested_stmt_ = EST_END_NESTED_SESSION); @@ -1313,7 +1353,8 @@ int ObSPIService::spi_end_trans(ObPLExecCtx *ctx, const char *sql, bool is_rollb ret = OB_ERR_UNEXPECTED; LOG_ERROR("session ptr is null", K(ret)); } else { - OZ (ObSPIResultSet::check_nested_stmt_legal(*(ctx->exec_ctx_), stmt::T_END_TRANS)); + ObString sqlstr(sql); + OZ (ObSPIResultSet::check_nested_stmt_legal(*(ctx->exec_ctx_), sqlstr, stmt::T_END_TRANS)); int64_t saved_query_start_time = my_session->get_query_start_time(); my_session->set_query_start_time(ObTimeUtility::current_time()); if (OB_SUCC(ret)) { @@ -1432,8 +1473,9 @@ int ObSPIService::spi_inner_execute(ObPLExecCtx *ctx, HEAP_VAR(ObSPIResultSet, spi_result) { stmt::StmtType stmt_type = stmt::T_NONE; bool is_diagnostics_stmt = false; + ObString sqlstr(sql); OZ (spi_result.init(*session)); - OZ (spi_result.start_nested_stmt_if_need(ctx, static_cast(type), for_update)); + OZ (spi_result.start_nested_stmt_if_need(ctx, sqlstr, static_cast(type), for_update)); if (OB_SUCC(ret)) { int64_t row_count = 0; @@ -1655,7 +1697,7 @@ int ObSPIService::dbms_cursor_execute(ObPLExecCtx *ctx, HEAP_VAR(ObSPIResultSet, spi_result) { OZ (spi_result.init(*session)); - OZ (spi_result.start_nested_stmt_if_need(ctx, static_cast(stmt_type), cursor.is_for_update())); + OZ (spi_result.start_nested_stmt_if_need(ctx, sql_stmt, static_cast(stmt_type), cursor.is_for_update())); if (OB_SUCC(ret)) { int64_t row_count = 0; ObQueryRetryCtrl retry_ctrl; @@ -2600,7 +2642,7 @@ int ObSPIService::spi_execute_immediate(ObPLExecCtx *ctx, } } OX (session->set_stmt_type(saved_stmt_type)); - OZ (spi_result.start_nested_stmt_if_need(ctx, stmt_type, for_update)); + OZ (spi_result.start_nested_stmt_if_need(ctx, sql_str.string(), stmt_type, for_update)); // Step2: execute dynamic SQL now! if (OB_FAIL(ret)) { @@ -3472,8 +3514,9 @@ int ObSPIService::spi_cursor_open(ObPLExecCtx *ctx, cursor->set_last_execute_time(ObTimeUtility::current_time()); } else { //MySQL Cursor/Updated Cursor/Server Cursor(REF_CURSOR, PACKAGE CURSOR) HEAP_VAR(ObSPIResultSet, spi_result) { + ObString sqlstr(sql); OZ (spi_result.init(*session_info)); - OZ (spi_result.start_nested_stmt_if_need(ctx, static_cast(type), for_update)); + OZ (spi_result.start_nested_stmt_if_need(ctx, sqlstr, static_cast(type), for_update)); int64_t old_query_start_time = session_info->get_query_start_time(); // query_start_time_ set to 0 in begin_nested_session, here we reset it. session_info->set_query_start_time(ObTimeUtility::current_time()); @@ -3746,7 +3789,7 @@ int ObSPIService::dbms_cursor_open(ObPLExecCtx *ctx, uint64_t size = 0; OZ (session->get_tmp_table_size(size)); OZ (spi_result.init(*ctx->exec_ctx_->get_my_session())); - OZ (spi_result.start_nested_stmt_if_need(ctx, static_cast(stmt_type), for_update), + OZ (spi_result.start_nested_stmt_if_need(ctx, sql_stmt, static_cast(stmt_type), for_update), sql_stmt, ps_sql, exec_params); if (OB_SUCC(ret)) { diff --git a/src/sql/ob_spi.h b/src/sql/ob_spi.h index 68b46a8266..be98b087c8 100644 --- a/src/sql/ob_spi.h +++ b/src/sql/ob_spi.h @@ -187,7 +187,8 @@ private: int end_nested_session(ObSQLSessionInfo &session); int alloc_saved_value(sql::ObSQLSessionInfo::StmtSavedValue *&session_value); public: - static int check_nested_stmt_legal(ObExecContext &exec_ctx, stmt::StmtType stmt_type, bool for_update = false); + static int is_set_global_var(ObSQLSessionInfo &session, const ObString &sql, bool &has_global_variable); + static int check_nested_stmt_legal(ObExecContext &exec_ctx, const ObString &sql, stmt::StmtType stmt_type, bool for_update = false); int start_trans(ObExecContext &ctx); int set_cursor_env(ObSQLSessionInfo &session); int reset_cursor_env(ObSQLSessionInfo &session); @@ -195,7 +196,7 @@ public: stmt::StmtType type = stmt::StmtType::T_NONE, bool is_for_update = false); void end_cursor_stmt(pl::ObPLExecCtx *pl_ctx, int &result); - int start_nested_stmt_if_need(pl::ObPLExecCtx *pl_ctx, stmt::StmtType stmt_type, bool for_update); + int start_nested_stmt_if_need(pl::ObPLExecCtx *pl_ctx, const ObString &sql, stmt::StmtType stmt_type, bool for_update); void end_nested_stmt_if_need(pl::ObPLExecCtx *pl_ctx, int &result); private: bool is_inited_;