From 7928b4a19c5d1b1ffa95a9e8793e9082f42fbac3 Mon Sep 17 00:00:00 2001 From: Tanzeel Khan Date: Mon, 13 Jan 2025 09:06:05 +0000 Subject: [PATCH] fix object resolution inside pltsql routines Signed-off-by: Tanzeel Khan --- .../babelfishpg_tsql/sql/sys_functions.sql | 4 +- .../babelfishpg_tsql--5.0.0--5.1.0.sql | 7 + contrib/babelfishpg_tsql/src/catalog.c | 48 ++-- contrib/babelfishpg_tsql/src/hooks.c | 37 ---- contrib/babelfishpg_tsql/src/iterative_exec.c | 81 +++++++ contrib/babelfishpg_tsql/src/pl_comp.c | 47 ++++ contrib/babelfishpg_tsql/src/pl_exec-2.c | 133 +++++------ contrib/babelfishpg_tsql/src/pl_exec.c | 207 +----------------- contrib/babelfishpg_tsql/src/pl_handler.c | 26 ++- contrib/babelfishpg_tsql/src/pltsql.h | 9 +- contrib/babelfishpg_tsql/src/pltsql_utils.c | 9 + contrib/babelfishpg_tsql/src/procedures.c | 10 +- contrib/babelfishpg_tsql/src/session.c | 73 +++--- contrib/babelfishpg_tsql/src/session.h | 6 + contrib/babelfishpg_tsql/src/tsqlIface.cpp | 47 +--- .../expected/BABEL-CROSS-DB-vu-cleanup.out | 3 + .../expected/BABEL-CROSS-DB-vu-prepare.out | 35 +++ .../expected/BABEL-CROSS-DB-vu-verify.out | 26 +++ test/JDBC/expected/BABEL-CROSS-DB.out | 73 ++++++ test/JDBC/input/BABEL-CROSS-DB.mix | 55 +++++ .../ownership/BABEL-CROSS-DB-vu-cleanup.mix | 3 + .../ownership/BABEL-CROSS-DB-vu-prepare.mix | 37 ++++ .../ownership/BABEL-CROSS-DB-vu-verify.mix | 6 + 23 files changed, 542 insertions(+), 440 deletions(-) diff --git a/contrib/babelfishpg_tsql/sql/sys_functions.sql b/contrib/babelfishpg_tsql/sql/sys_functions.sql index d9892b90f46..1c724fc95ac 100644 --- a/contrib/babelfishpg_tsql/sql/sys_functions.sql +++ b/contrib/babelfishpg_tsql/sql/sys_functions.sql @@ -2403,7 +2403,7 @@ LANGUAGE C PARALLEL SAFE IMMUTABLE; CREATE OR REPLACE FUNCTION sys.db_id() RETURNS SMALLINT AS 'babelfishpg_tsql', 'babelfish_db_id' -LANGUAGE C PARALLEL SAFE IMMUTABLE; +LANGUAGE C PARALLEL SAFE STABLE; CREATE OR REPLACE FUNCTION sys.db_name(int) RETURNS sys.nvarchar(128) AS 'babelfishpg_tsql', 'babelfish_db_name' @@ -2411,7 +2411,7 @@ LANGUAGE C PARALLEL SAFE IMMUTABLE; CREATE OR REPLACE FUNCTION sys.db_name() RETURNS sys.nvarchar(128) AS 'babelfishpg_tsql', 'babelfish_db_name' -LANGUAGE C PARALLEL SAFE IMMUTABLE; +LANGUAGE C PARALLEL SAFE STABLE; CREATE OR REPLACE FUNCTION sys.exp(IN arg DOUBLE PRECISION) RETURNS DOUBLE PRECISION diff --git a/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--5.0.0--5.1.0.sql b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--5.0.0--5.1.0.sql index 09c2678aec9..09253fd61b7 100644 --- a/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--5.0.0--5.1.0.sql +++ b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--5.0.0--5.1.0.sql @@ -39,6 +39,13 @@ $$; * So make sure that any SQL statement (DDL/DML) being added here can be executed multiple times without affecting * final behaviour. */ +CREATE OR REPLACE FUNCTION sys.db_id() RETURNS SMALLINT +AS 'babelfishpg_tsql', 'babelfish_db_id' +LANGUAGE C PARALLEL SAFE STABLE; + +CREATE OR REPLACE FUNCTION sys.db_name() RETURNS sys.nvarchar(128) +AS 'babelfishpg_tsql', 'babelfish_db_name' +LANGUAGE C PARALLEL SAFE STABLE; -- After upgrade, always run analyze for all babelfish catalogs. CALL sys.analyze_babelfish_catalogs(); diff --git a/contrib/babelfishpg_tsql/src/catalog.c b/contrib/babelfishpg_tsql/src/catalog.c index a9209fe2d8e..961d5c63e7a 100644 --- a/contrib/babelfishpg_tsql/src/catalog.c +++ b/contrib/babelfishpg_tsql/src/catalog.c @@ -1001,47 +1001,33 @@ get_authid_user_ext_physical_name(const char *db_name, const char *login) char * get_authid_user_ext_schema_name(const char *db_name, const char *user) { - Relation bbf_authid_user_ext_rel; HeapTuple tuple_user_ext; - ScanKeyData key[2]; - TableScanDesc scan; char *schema_name = NULL; - NameData *user_name; if (!db_name || !user) return NULL; - bbf_authid_user_ext_rel = table_open(get_authid_user_ext_oid(), - RowExclusiveLock); + tuple_user_ext = SearchSysCache1(AUTHIDUSEREXTROLENAME, CStringGetDatum(user)); - user_name = (NameData *) palloc0(NAMEDATALEN); - snprintf(user_name->data, NAMEDATALEN, "%s", user); - ScanKeyInit(&key[0], - Anum_bbf_authid_user_ext_rolname, - BTEqualStrategyNumber, F_NAMEEQ, - NameGetDatum(user_name)); - ScanKeyInit(&key[1], - Anum_bbf_authid_user_ext_database_name, - BTEqualStrategyNumber, F_TEXTEQ, - CStringGetTextDatum(db_name)); - - scan = table_beginscan_catalog(bbf_authid_user_ext_rel, 2, key); - - tuple_user_ext = heap_getnext(scan, ForwardScanDirection); if (HeapTupleIsValid(tuple_user_ext)) { - Datum datum; - bool is_null; - - datum = heap_getattr(tuple_user_ext, - Anum_bbf_authid_user_ext_default_schema_name, - bbf_authid_user_ext_rel->rd_att, - &is_null); - schema_name = pstrdup(TextDatumGetCString(datum)); - } + Datum datum = SysCacheGetAttrNotNull(AUTHIDUSEREXTROLENAME, tuple_user_ext, + Anum_bbf_authid_user_ext_database_name); + char *db_name_cstring = TextDatumGetCString(datum); - table_endscan(scan); - table_close(bbf_authid_user_ext_rel, RowExclusiveLock); + if (strcmp(db_name_cstring, db_name) == 0) + { + Datum schema_datum = SysCacheGetAttrNotNull(AUTHIDUSEREXTROLENAME, tuple_user_ext, + Anum_bbf_authid_user_ext_default_schema_name); + char *default_schema_name = TextDatumGetCString(schema_datum); + + if (strlen(default_schema_name)) + schema_name = default_schema_name; + } + + pfree(db_name_cstring); + ReleaseSysCache(tuple_user_ext); + } return schema_name; } diff --git a/contrib/babelfishpg_tsql/src/hooks.c b/contrib/babelfishpg_tsql/src/hooks.c index 3faacd2a3f7..ab019f59d20 100644 --- a/contrib/babelfishpg_tsql/src/hooks.c +++ b/contrib/babelfishpg_tsql/src/hooks.c @@ -188,7 +188,6 @@ static bool pltsql_bbfViewHasInsteadofTrigger(Relation view, CmdType event); static bool plsql_TriggerRecursiveCheck(ResultRelInfo *resultRelInfo); static bool bbf_check_rowcount_hook(int es_processed); -static char *get_local_schema_for_bbf_functions(Oid proc_nsp_oid); extern bool called_from_tsql_insert_exec(); extern bool called_for_tsql_itvf_func(); static void is_function_pg_stat_valid(FunctionCallInfo fcinfo, @@ -290,7 +289,6 @@ static table_variable_satisfies_update_hook_type prev_table_variable_satisfies_u static table_variable_satisfies_vacuum_hook_type prev_table_variable_satisfies_vacuum = NULL; static table_variable_satisfies_vacuum_horizon_hook_type prev_table_variable_satisfies_vacuum_horizon = NULL; static drop_relation_refcnt_hook_type prev_drop_relation_refcnt_hook = NULL; -static set_local_schema_for_func_hook_type prev_set_local_schema_for_func_hook = NULL; static bbf_get_sysadmin_oid_hook_type prev_bbf_get_sysadmin_oid_hook = NULL; static get_bbf_admin_oid_hook_type prev_get_bbf_admin_oid_hook = NULL; static transform_pivot_clause_hook_type pre_transform_pivot_clause_hook = NULL; @@ -473,9 +471,6 @@ InstallExtendedHooks(void) prev_drop_relation_refcnt_hook = drop_relation_refcnt_hook; drop_relation_refcnt_hook = pltsql_drop_relation_refcnt_hook; - prev_set_local_schema_for_func_hook = set_local_schema_for_func_hook; - set_local_schema_for_func_hook = get_local_schema_for_bbf_functions; - prev_bbf_get_sysadmin_oid_hook = bbf_get_sysadmin_oid_hook; bbf_get_sysadmin_oid_hook = get_sysadmin_oid; @@ -586,7 +581,6 @@ UninstallExtendedHooks(void) IsToastRelationHook = PrevIsToastRelationHook; IsToastClassHook = PrevIsToastClassHook; drop_relation_refcnt_hook = prev_drop_relation_refcnt_hook; - set_local_schema_for_func_hook = prev_set_local_schema_for_func_hook; bbf_get_sysadmin_oid_hook = prev_bbf_get_sysadmin_oid_hook; get_bbf_admin_oid_hook = prev_get_bbf_admin_oid_hook; transform_pivot_clause_hook = pre_transform_pivot_clause_hook; @@ -5093,37 +5087,6 @@ sort_nulls_first(SortGroupClause * sortcl, bool reverse) } } - -static char * -get_local_schema_for_bbf_functions(Oid proc_nsp_oid) -{ - HeapTuple tuple; - char *func_schema_name = NULL, - *new_search_path = NULL; - char *func_dbo_schema; - const char *cur_dbname = get_cur_db_name(); - - tuple = SearchSysCache1(NAMESPACEOID, - ObjectIdGetDatum(proc_nsp_oid)); - if(HeapTupleIsValid(tuple)) - { - func_schema_name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); - func_dbo_schema = get_dbo_schema_name(cur_dbname); - - if(strcmp(func_schema_name, func_dbo_schema) != 0 - && strcmp(func_schema_name, "sys") != 0) - new_search_path = psprintf("%s, %s, \"$user\", sys, pg_catalog", - quote_identifier(func_schema_name), - quote_identifier(func_dbo_schema)); - - ReleaseSysCache(tuple); - - pfree(func_dbo_schema); - } - - return new_search_path; -} - static ResTarget * make_restarget_from_cstr_list(List * l) { diff --git a/contrib/babelfishpg_tsql/src/iterative_exec.c b/contrib/babelfishpg_tsql/src/iterative_exec.c index 4329580cb55..fcd97954cf0 100644 --- a/contrib/babelfishpg_tsql/src/iterative_exec.c +++ b/contrib/babelfishpg_tsql/src/iterative_exec.c @@ -26,6 +26,7 @@ static void restore_ctx_full(PLtsql_execstate *estate); static ErrorData *restore_ctx_partial1(PLtsql_execstate *estate); static void restore_ctx_partial2(PLtsql_execstate *estate); static void set_exec_error_data(char *procedure, int number, int severity, int state, bool rethrow); +static void set_search_path_for_pltsql_stmt(PLtsql_stmt *stmt); static void reset_exec_error_data(PLtsql_execstate *estate); static void assert_equal_estate_err(PLtsql_estate_err *err1, PLtsql_estate_err *err2); static void read_raiserror_params(PLtsql_execstate *estate, List *params, int paramno, @@ -46,6 +47,9 @@ extern PLtsql_estate_err *pltsql_clone_estate_err(PLtsql_estate_err *err); extern void prepare_format_string(StringInfo buf, char *msg_string, int nargs, Datum *args, Oid *argtypes, bool *argisnull); +char *pltsql_search_path = NULL; +char *current_db_search_path = NULL; + static int exec_stmt_goto(PLtsql_execstate *estate, PLtsql_stmt_goto *stmt) { @@ -645,6 +649,8 @@ dispatch_stmt(PLtsql_execstate *estate, PLtsql_stmt *stmt) /* reset number of tuple processed in previous command */ estate->eval_processed = 0; + set_search_path_for_pltsql_stmt(stmt); + switch (stmt->cmd_type) { case PLTSQL_STMT_ASSIGN: @@ -2184,3 +2190,78 @@ send_env_change_token_on_txn_abort(void) if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->send_env_change_binary) ((*pltsql_protocol_plugin_ptr)->send_env_change_binary) (ENVCHANGE_ROLLBACKTXN, NULL, 0, &txnId, sizeof(uint64_t)); } + +/* + * We will keep verifying the search path is correct + * before executing any pltsql statement + */ +static void +set_search_path_for_pltsql_stmt(PLtsql_stmt *stmt) +{ + char *cur_dbname = get_cur_db_name(); + char *new_search_path = NULL; + + if (!IS_TDS_CONN()) + { + pfree(cur_dbname); + return; + } + + if(stmt->cmd_type == PLTSQL_STMT_EXECSQL) + { + PLtsql_stmt_execsql *execsqlstmt = (PLtsql_stmt_execsql *)stmt; + if (execsqlstmt->is_create_view && execsqlstmt->schema_name != NULL && strcmp(execsqlstmt->schema_name, "sys") != 0 + && strcmp(execsqlstmt->schema_name, "pg_catalog") != 0) + { + char *physical_schema = get_physical_schema_name(cur_dbname, execsqlstmt->schema_name); + char *dbo_schema = get_dbo_schema_name(cur_dbname); + + new_search_path = psprintf(PLTSQL_SEARCH_PATH_BUFFER, quote_identifier(physical_schema), quote_identifier(dbo_schema)); + + pfree(physical_schema); + pfree(dbo_schema); + } + else if (execsqlstmt->is_ddl) + { + char *dbo_schema = get_dbo_schema_name(cur_dbname); + char *schema = get_authid_user_ext_schema_name(cur_dbname, GetUserNameFromId(GetUserId(), false)); + char *physical_schema = NULL; + + if (schema) + { + physical_schema = get_physical_schema_name(cur_dbname, schema); + pfree(schema); + } + new_search_path = psprintf(PLTSQL_SEARCH_PATH_BUFFER, + physical_schema ? quote_identifier(physical_schema) : quote_identifier(dbo_schema), + quote_identifier(dbo_schema)); + } + } + + PG_TRY(); + { + pltsql_check_search_path = false; + + /* Override case */ + if (new_search_path) + { + SetConfigOption("search_path", new_search_path, + PGC_SUSET, PGC_S_SESSION); + pfree(new_search_path); + } + /* default search inside pltsql routines */ + else if (pltsql_search_path) + SetConfigOption("search_path", pltsql_search_path, + PGC_SUSET, PGC_S_SESSION); + /* current active logical database search path */ + else if (current_db_search_path) + SetConfigOption("search_path", current_db_search_path, + PGC_SUSET, PGC_S_SESSION); + } + PG_FINALLY(); + { + pltsql_check_search_path = true; + pfree(cur_dbname); + } + PG_END_TRY(); +} diff --git a/contrib/babelfishpg_tsql/src/pl_comp.c b/contrib/babelfishpg_tsql/src/pl_comp.c index 7ccc2380fe0..25292f5c6c9 100644 --- a/contrib/babelfishpg_tsql/src/pl_comp.c +++ b/contrib/babelfishpg_tsql/src/pl_comp.c @@ -42,6 +42,7 @@ #include "pltsql.h" #include "pltsql-2.h" #include "analyzer.h" +#include "catalog.h" #include "codegen.h" #include "iterative_exec.h" #include "multidb.h" @@ -147,6 +148,7 @@ static void delete_function(PLtsql_function *func); extern Portal ActivePortal; extern bool pltsql_function_parse_error_transpose(const char *prosrc); +static char *get_local_schema_for_bbf_functions(Oid proc_nsp_oid, int16 dbid); /* ---------- * pltsql_compile Make an execution tree for a PL/tsql function. @@ -967,6 +969,18 @@ do_compile(FunctionCallInfo fcinfo, for (i = 0; i < function->fn_nargs; i++) function->fn_argvarnos[i] = in_arg_varnos[i]; + /* store the logical db which contains this function */ + if (IS_TDS_CONN() && procStruct->provolatile != PROVOLATILE_IMMUTABLE) + { + function->fn_dbid = get_dbid_from_physical_schema_name(get_namespace_name(procStruct->pronamespace), true); + function->fn_search_path = get_local_schema_for_bbf_functions(procStruct->pronamespace, function->fn_dbid); + } + else + { + function->fn_dbid = InvalidDbid; + function->fn_search_path = NULL; + } + pltsql_finish_datums(function); /* Debug dump for completed functions */ @@ -1112,6 +1126,8 @@ pltsql_compile_inline(char *proc_source, InlineCodeBlockArgs *args) function->fn_retbyval = true; function->fn_rettyplen = sizeof(int32); function->fn_tupdesc = NULL; + function->fn_dbid = InvalidDbid; + function->fn_search_path = NULL; /* * Remember if function is STABLE/IMMUTABLE. XXX would it be better to @@ -3252,3 +3268,34 @@ reset_cached_batch() delete_cached_batch(cur_handle_id--); cur_handle_id = 1; } + +static char * +get_local_schema_for_bbf_functions(Oid proc_nsp_oid, int16 dbid) +{ + HeapTuple tuple; + char *func_schema_name = NULL, + *new_search_path = NULL; + char *func_dbo_schema; + + if (!IS_TDS_CONN() || !DbidIsValid(dbid) || proc_nsp_oid == get_sys_namespace_oid()) + return NULL; + + tuple = SearchSysCache1(NAMESPACEOID, + ObjectIdGetDatum(proc_nsp_oid)); + if(HeapTupleIsValid(tuple)) + { + func_schema_name = get_namespace_name(proc_nsp_oid); + func_dbo_schema = get_dbo_schema_name(get_db_name(dbid)); + + new_search_path = psprintf(PLTSQL_SEARCH_PATH_BUFFER, + quote_identifier(func_schema_name), + quote_identifier(func_dbo_schema)); + + ReleaseSysCache(tuple); + + pfree(func_schema_name); + pfree(func_dbo_schema); + } + + return new_search_path; +} diff --git a/contrib/babelfishpg_tsql/src/pl_exec-2.c b/contrib/babelfishpg_tsql/src/pl_exec-2.c index 42e098f12c6..8f3cc39d5bb 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec-2.c +++ b/contrib/babelfishpg_tsql/src/pl_exec-2.c @@ -835,57 +835,13 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt) volatile int rc; SimpleEcontextStackEntry *topEntry; SPIExecuteOptions options; - bool need_path_reset = false; + char *old_search_path = pstrdup(namespace_search_path); + bool reset_search_path = false; + char *save_db_name = get_cur_db_name(); - char *cur_dbname = get_cur_db_name(); - - /* fetch current search_path */ - char *old_search_path = NULL; - char *new_search_path; - /* whether procedure was created WITH RECOMPILE */ bool created_with_recompile = false; - estate->db_name = NULL; - if (stmt->proc_name == NULL) - stmt->proc_name = ""; - - if (stmt->is_cross_db) - { - estate->db_name = stmt->db_name; - } - - /* - * "sp_describe_first_result_set" needs special handling. It is a sys - * function and satisfies the below condition and it appends "master_dbo" - * to the search path which is not required for sys functions. - */ - if (strcmp(stmt->proc_name, "sp_describe_first_result_set") != 0) - { - if (strncmp(stmt->proc_name, "sp_", 3) == 0 && strcmp(cur_dbname, "master") != 0 - && ((stmt->schema_name == NULL || stmt->schema_name[0] == (char) '\0') || strcmp(stmt->schema_name, "dbo") == 0)) - { - if (!old_search_path) - { - List *path_oids = fetch_search_path(false); - - old_search_path = flatten_search_path(path_oids); - list_free(path_oids); - } - new_search_path = psprintf("%s, master_dbo", old_search_path); - - /* Add master_dbo to the new search path */ - (void) set_config_option("search_path", new_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - need_path_reset = true; - } - } - if (stmt->schema_name != NULL && stmt->schema_name[0] != (char) '\0') - estate->schema_name = stmt->schema_name; - else - estate->schema_name = NULL; - /* * We need to disable the explain gucs incase of sp_reset_connection * execution otherwise we will get explain output for it which is @@ -913,6 +869,42 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt) Tuplestorestate *tss; DestReceiver *dest; + /* + * "sp_describe_first_result_set" needs special handling. It is a sys + * function and satisfies the below condition and it appends "master_dbo" + * to the search path which is not required for sys functions. + */ + if (IS_TDS_CONN() && strcmp(stmt->proc_name, "sp_describe_first_result_set") != 0 + && strncmp(stmt->proc_name, "sp_rename", 9) != 0) + { + if (strncmp(stmt->proc_name, "sp_", 3) == 0 && ((stmt->schema_name == NULL || + stmt->schema_name[0] == (char) '\0') || strcmp(stmt->schema_name, "dbo") == 0)) + { + char *new_search_path; + new_search_path = psprintf("sys, %s, master_dbo", namespace_search_path); + + reset_search_path = true; + + /* Append master_dbo to the new search path */ + SetConfigOption("search_path", new_search_path, + PGC_SUSET, PGC_S_SESSION); + pfree(new_search_path); + } + else if (stmt->db_name != NULL && stmt->schema_name != NULL && + strcmp(stmt->db_name, save_db_name) != 0 && + strcmp(stmt->schema_name, "sys") == 0) + { + /* + * For sys pltsql routines switch to the database specified + * while calling it. We cannot do this in pltsql_call_handler + * because we do not have this information there and carrying + * it to pltsql_call_handler seems more difficult + * we do not switch db in pltsql_call_handler for sys routines + */ + set_cur_user_db_and_path(stmt->db_name); + } + } + if (plan == NULL) plan = prepare_stmt_exec(estate, estate->func, stmt, estate->atomic); @@ -1290,20 +1282,17 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt) dest->rDestroy(dest); } } - PG_CATCH(); + PG_FINALLY(); { - if (need_path_reset) - { - /* - * Note: there is no test case to validate restoring the search_path below. - * In fact, we don't know whether this restore is even required, since removing the - * call set_config_option("search_path") does not cause any test cases to fail. - * Nevertheless we're keeping the code out of an abundance of caution. - */ - (void) set_config_option("search_path", old_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - } + if (strcmp(get_current_pltsql_db_name(), save_db_name) != 0) + set_cur_user_db_and_path(save_db_name); + + if (reset_search_path) + SetConfigOption("search_path", (const char *)old_search_path, + PGC_SUSET, PGC_S_SESSION); + + pfree(old_search_path); + pfree(save_db_name); /* * If we aren't saving the plan, unset the pointer. Note that it @@ -1316,31 +1305,9 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt) expr->plan = NULL; SPI_freeplan(plan); } - PG_RE_THROW(); } PG_END_TRY(); - if (need_path_reset) - { - /* - * Note: there is no test case to validate restoring the search_path below. - * In fact, we don't know whether this restore is even required, since removing the - * call set_config_option("search_path") does not cause any test cases to fail. - * Nevertheless we're keeping the code out of an abundance of caution. - */ - (void) set_config_option("search_path", old_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - } - - if (expr->plan && !expr->plan->saved) - { - SPIPlanPtr plan = expr->plan; - - expr->plan = NULL; - SPI_freeplan(plan); - } - if (rc < 0) elog(ERROR, "SPI_execute_plan_with_paramlist failed executing query \"%s\": %s", expr->query, SPI_result_code_string(rc)); @@ -1585,7 +1552,7 @@ exec_stmt_exec_batch(PLtsql_execstate *estate, PLtsql_stmt_exec_batch *stmt) cur_db_name = get_cur_db_name(); if (strcmp(cur_db_name, old_db_name) != 0) - set_session_properties(old_db_name); + set_cur_user_db_and_path(old_db_name); } PG_END_TRY(); diff --git a/contrib/babelfishpg_tsql/src/pl_exec.c b/contrib/babelfishpg_tsql/src/pl_exec.c index e09c927b802..343987fd1ea 100644 --- a/contrib/babelfishpg_tsql/src/pl_exec.c +++ b/contrib/babelfishpg_tsql/src/pl_exec.c @@ -466,7 +466,6 @@ static void pltsql_clean_table_variables(PLtsql_execstate *estate, PLtsql_functi static void pltsql_init_exec_error_data(PLtsqlErrorData *error_data); static void pltsql_copy_exec_error_data(PLtsqlErrorData *src, PLtsqlErrorData *dst, MemoryContext dstCxt); PLtsql_estate_err *pltsql_clone_estate_err(PLtsql_estate_err *err); -static bool reset_search_path(PLtsql_stmt_execsql *stmt, char **old_search_path, bool *reset_session_properties, bool inside_trigger); extern void pltsql_init_anonymous_cursors(PLtsql_execstate *estate); extern void pltsql_cleanup_local_cursors(PLtsql_execstate *estate); @@ -4625,13 +4624,7 @@ exec_stmt_execsql(PLtsql_execstate *estate, bool enable_txn_in_triggers = !pltsql_disable_txn_in_triggers; bool support_tsql_trans = pltsql_support_tsql_transactions(); StringInfoData query; - bool need_path_reset = false; char *cur_dbname = get_cur_db_name(); - bool reset_session_properties = false; - bool inside_trigger = false; - - /* fetch current search_path */ - char *old_search_path = NULL; bool ro_func = (estate->func->fn_prokind == PROKIND_FUNCTION) && (estate->func->fn_is_trigger == PLTSQL_NOT_TRIGGER) && (strcmp(estate->func->fn_signature, "inline_code_block") != 0); @@ -4640,11 +4633,6 @@ exec_stmt_execsql(PLtsql_execstate *estate, if (stmt->is_cross_db) { - if (stmt->insert_exec) - { - estate->db_name = stmt->db_name; - } - /* * When there is cross db reference to sys or information_schema * schemas, Change the session property. @@ -4652,16 +4640,6 @@ exec_stmt_execsql(PLtsql_execstate *estate, if (stmt->schema_name != NULL && (strcmp(stmt->schema_name, "sys") == 0 || strcmp(stmt->schema_name, "information_schema") == 0)) set_session_properties(stmt->db_name); } - if (stmt->is_dml || stmt->is_ddl || stmt->is_create_view) - { - if (stmt->is_schema_specified) - estate->schema_name = stmt->schema_name; - else - estate->schema_name = NULL; - if (estate->trigdata) - inside_trigger = true; - need_path_reset = reset_search_path(stmt, &old_search_path, &reset_session_properties, inside_trigger); - } PG_TRY(); { @@ -4670,11 +4648,7 @@ exec_stmt_execsql(PLtsql_execstate *estate, { int ret = exec_stmt_insert_execute_select(estate, expr); - if (reset_session_properties) - { - set_session_properties(cur_dbname); - } - else if (stmt->is_cross_db) + if (stmt->is_cross_db) { if (stmt->schema_name != NULL && (strcmp(stmt->schema_name, "sys") == 0 || strcmp(stmt->schema_name, "information_schema") == 0)) set_session_properties(cur_dbname); @@ -5076,15 +5050,7 @@ exec_stmt_execsql(PLtsql_execstate *estate, { original_query_string = NULL; - if (need_path_reset) - (void) set_config_option("search_path", old_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - if (reset_session_properties) - { - set_session_properties(cur_dbname); - } - else if (stmt->is_cross_db) + if (stmt->is_cross_db) { if (stmt->schema_name != NULL && (strcmp(stmt->schema_name, "sys") == 0 || strcmp(stmt->schema_name, "information_schema") == 0)) set_session_properties(cur_dbname); @@ -10224,175 +10190,6 @@ pltsql_clone_estate_err(PLtsql_estate_err *err) return clone; } -static bool -reset_search_path(PLtsql_stmt_execsql *stmt, char **old_search_path, bool *reset_session_properties, bool inside_trigger) -{ - PLExecStateCallStack *top_es_entry; - char *cur_dbname = get_cur_db_name(); - char *new_search_path; - char *physical_schema; - char *dbo_schema = NULL; - - top_es_entry = exec_state_call_stack->next; - - while (top_es_entry != NULL) - { - /* - * Traverse through the estate stack. If the occurrence of exec in the - * call stack, update the search path accordingly. - */ - if (top_es_entry->estate && top_es_entry->estate->err_stmt && - (top_es_entry->estate->err_stmt->cmd_type == PLTSQL_STMT_EXEC || - (top_es_entry->estate->err_stmt->cmd_type == PLTSQL_STMT_EXECSQL && - ((PLtsql_stmt_execsql*)top_es_entry->estate->err_stmt)->insert_exec && - ((PLtsql_stmt_execsql*)top_es_entry->estate->err_stmt)->is_cross_db) - )) - { - if (top_es_entry->estate->schema_name != NULL && stmt->is_dml) - { - if (top_es_entry->estate->db_name == NULL) - { - /* - * Don't change the search path, if the statement inside - * the procedure is a function or schema qualified. - */ - if (stmt->func_call || stmt->is_schema_specified) - break; - else - { - physical_schema = get_physical_schema_name(cur_dbname, top_es_entry->estate->schema_name); - dbo_schema = get_dbo_schema_name(cur_dbname); - } - } - else - { - if (stmt->func_call || stmt->is_schema_specified) - { - set_session_properties(top_es_entry->estate->db_name); - *reset_session_properties = true; - break; - } - else - { - physical_schema = get_physical_schema_name((char *) top_es_entry->estate->db_name, top_es_entry->estate->schema_name); - dbo_schema = get_dbo_schema_name(top_es_entry->estate->db_name); - } - } - if (!*old_search_path) - { - List *path_oids = fetch_search_path(false); - - *old_search_path = flatten_search_path(path_oids); - list_free(path_oids); - } - new_search_path = psprintf("%s, %s, %s", quote_identifier(physical_schema), quote_identifier(dbo_schema), *old_search_path); - - pfree(physical_schema); - /* - * Add the schema where the object is referenced and dbo - * schema to the new search path - */ - (void) set_config_option("search_path", new_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - - pfree(new_search_path); - pfree(dbo_schema); - - return true; - } - else if (top_es_entry->estate->db_name != NULL && stmt->is_ddl) - { - set_session_properties(top_es_entry->estate->db_name); - *reset_session_properties = true; - break; - } - } - /* if the stmt is inside an exec_batch, return false */ - else if (top_es_entry->estate && top_es_entry->estate->err_stmt && - top_es_entry->estate->err_stmt->cmd_type == PLTSQL_STMT_EXEC_BATCH) - return false; - - /* - * Traverse through the estate stack, if the stmt is inside trigger we - * set the search path accordingly. - */ - else if (top_es_entry->estate && top_es_entry->estate->err_stmt && - top_es_entry->estate->err_stmt->cmd_type == PLTSQL_STMT_EXECSQL) - { - if (inside_trigger && top_es_entry->estate->schema_name) - { - /* - * If the object in the stmt is schema qualified or it's a ddl - * we don't need to update the searh path. - */ - if (stmt->is_schema_specified || stmt->is_ddl) - return false; - else - { - physical_schema = get_physical_schema_name(cur_dbname, top_es_entry->estate->schema_name); - dbo_schema = get_dbo_schema_name(cur_dbname); - if (!*old_search_path) - { - List *path_oids = fetch_search_path(false); - - *old_search_path = flatten_search_path(path_oids); - list_free(path_oids); - } - new_search_path = psprintf("%s, %s, %s", quote_identifier(physical_schema), quote_identifier(dbo_schema), *old_search_path); - - pfree(physical_schema); - /* - * Add the schema where the object is referenced and dbo - * schema to the new search path - */ - (void) set_config_option("search_path", new_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - pfree(new_search_path); - pfree(dbo_schema); - - return true; - } - } - } - top_es_entry = top_es_entry->next; - } - - if (stmt->is_create_view && stmt->schema_name != NULL && (strcmp(stmt->schema_name, "sys") != 0 - && strcmp(stmt->schema_name, "pg_catalog") != 0)) - { - cur_dbname = get_cur_db_name(); - physical_schema = get_physical_schema_name(cur_dbname, stmt->schema_name); - dbo_schema = get_dbo_schema_name(cur_dbname); - if (!*old_search_path) - { - List *path_oids = fetch_search_path(false); - - *old_search_path = flatten_search_path(path_oids); - list_free(path_oids); - } - new_search_path = psprintf("%s, %s, %s", quote_identifier(physical_schema), quote_identifier(dbo_schema), *old_search_path); - - pfree(physical_schema); - /* - * Add the schema where the object is referenced and dbo schema to the - * new search path - */ - (void) set_config_option("search_path", new_search_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); - pfree(new_search_path); - pfree(dbo_schema); - - return true; - } - - pfree(cur_dbname); - - return false; -} - /* * Get the original_query_string which stores the original query. */ diff --git a/contrib/babelfishpg_tsql/src/pl_handler.c b/contrib/babelfishpg_tsql/src/pl_handler.c index 5f8f3757c71..26a127284a6 100644 --- a/contrib/babelfishpg_tsql/src/pl_handler.c +++ b/contrib/babelfishpg_tsql/src/pl_handler.c @@ -5400,6 +5400,9 @@ pltsql_call_handler(PG_FUNCTION_ARGS) int saved_dialect = sql_dialect; int current_spi_stack_depth; bool send_error = false; + char *saved_namespace_search_path = pstrdup(namespace_search_path); + char *saved_pltsql_search_path = pltsql_search_path; + int16 saved_dbid = get_cur_db_id(); create_queryEnv2(CacheMemoryContext, false); @@ -5457,6 +5460,19 @@ pltsql_call_handler(PG_FUNCTION_ARGS) { set_procid(func->fn_oid); + /* for cross db func/proc calls switch to that db */ + if (DbidIsValid(func->fn_dbid) && get_cur_db_id() != func->fn_dbid) + set_cur_user_db_and_path(get_db_name(func->fn_dbid)); + + pltsql_search_path = func->fn_search_path; + if (pltsql_search_path) + { + pltsql_check_search_path = false; + SetConfigOption("search_path", pltsql_search_path, + PGC_SUSET, PGC_S_SESSION); + pltsql_check_search_path = true; + } + /* * Determine if called as function or trigger and call appropriate * subhandler @@ -5491,7 +5507,6 @@ pltsql_call_handler(PG_FUNCTION_ARGS) { set_procid(prev_procid); pltsql_trigger_depth = save_pltsql_trigger_depth; - send_error = true; } PG_END_TRY(2); @@ -5500,10 +5515,19 @@ pltsql_call_handler(PG_FUNCTION_ARGS) func->use_count--; func->cur_estate = save_cur_estate; + pltsql_search_path = saved_pltsql_search_path; + pltsql_check_search_path = true; pltsql_remove_current_query_env(); pltsql_revert_guc(save_nestlevel); pltsql_revert_last_scope_identity(scope_level); + + if (get_cur_db_id() != saved_dbid) + set_cur_user_db_and_path(get_db_name((saved_dbid))); + if (saved_namespace_search_path && strcmp(saved_namespace_search_path, namespace_search_path) != 0) + SetConfigOption("search_path", saved_namespace_search_path, + PGC_SUSET, PGC_S_SESSION); + pfree(saved_namespace_search_path); } PG_FINALLY(); { diff --git a/contrib/babelfishpg_tsql/src/pltsql.h b/contrib/babelfishpg_tsql/src/pltsql.h index 86772d9e29d..f0972e49111 100644 --- a/contrib/babelfishpg_tsql/src/pltsql.h +++ b/contrib/babelfishpg_tsql/src/pltsql.h @@ -1168,10 +1168,8 @@ typedef struct PLtsql_stmt_execsql bool is_cross_db; /* cross database reference */ bool is_dml; /* DML statement? */ bool is_ddl; /* DDL statement? */ - bool func_call; /* Function call? */ char *schema_name; /* Schema specified */ char *db_name; /* db_name: only for cross db query */ - bool is_schema_specified; /* is schema name specified? */ bool is_create_view; /* CREATE VIEW? */ bool is_set_tran_isolation; /* SET TRANSACTION ISOLATION? */ char *original_query; /* Only for batch level statement. */ @@ -1306,6 +1304,8 @@ typedef struct PLtsql_function int fetch_status_varno; int new_varno; int old_varno; + int16 fn_dbid; /* logical db which contains the function */ + char *fn_search_path; TupleDesc fn_tupdesc; /* tuple descriptor for return info */ @@ -1537,8 +1537,6 @@ typedef struct PLtsql_execstate bool insert_exec; List *explain_infos; - char *schema_name; - const char *db_name; instr_time planning_start; instr_time planning_end; instr_time execution_start; @@ -2022,6 +2020,8 @@ extern bool insert_bulk_check_constraints; * Function declarations **********************************************************************/ +# define PLTSQL_SEARCH_PATH_BUFFER "%s, %s, sys, pg_catalog" + /* * Functions in pl_comp.c */ @@ -2241,6 +2241,7 @@ extern bool pltsql_createFunction(ParseState *pstate, PlannedStmt *pstmt, const ParamListInfo params); extern Oid get_sys_varcharoid(void); extern Oid get_sysadmin_oid(void); +extern Oid get_sys_namespace_oid(void); extern bool is_tsql_varchar_or_char_datatype(Oid oid); /* sys.char / sys.varchar */ extern bool is_tsql_nchar_or_nvarchar_datatype(Oid oid); /* sys.nchar / sys.nvarchar */ extern bool is_tsql_binary_or_varbinary_datatype(Oid oid); /* sys.binary / sys.varbinary */ diff --git a/contrib/babelfishpg_tsql/src/pltsql_utils.c b/contrib/babelfishpg_tsql/src/pltsql_utils.c index 5bc8ff70039..561be6e72cc 100644 --- a/contrib/babelfishpg_tsql/src/pltsql_utils.c +++ b/contrib/babelfishpg_tsql/src/pltsql_utils.c @@ -56,6 +56,7 @@ PG_FUNCTION_INFO_V1(split_identifier_internal); /* To cache oid of sys.varchar */ static Oid sys_varcharoid = InvalidOid; static Oid sysadmin_oid = InvalidOid; +static Oid sys_namespace_oid = InvalidOid; /* * Following the rule for locktag fields of advisory locks: @@ -1908,6 +1909,14 @@ Oid get_sysadmin_oid(void) return sysadmin_oid; } +Oid get_sys_namespace_oid(void) +{ + if (!OidIsValid(sys_namespace_oid)) + sys_namespace_oid = get_namespace_oid("sys", false); + + return sys_namespace_oid; +} + /* * Generates the schema name for fulltext index statements * depending on whether it's master schema or not diff --git a/contrib/babelfishpg_tsql/src/procedures.c b/contrib/babelfishpg_tsql/src/procedures.c index 5ee3079e814..21a8e0435a6 100644 --- a/contrib/babelfishpg_tsql/src/procedures.c +++ b/contrib/babelfishpg_tsql/src/procedures.c @@ -1953,9 +1953,8 @@ sp_execute_postgresql(PG_FUNCTION_ARGS) } SetCurrentRoleId(GetSessionUserId(), false); - set_config_option("search_path", new_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); + SetConfigOption("search_path", new_path, + PGC_SUSET, PGC_S_SESSION); foreach(lc, crstmt->options) { @@ -2066,9 +2065,8 @@ sp_execute_postgresql(PG_FUNCTION_ARGS) set_config_option("babelfishpg_tsql.sql_dialect", saved_dialect, GUC_CONTEXT_CONFIG, PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); - set_config_option("search_path", saved_path, - PGC_USERSET, PGC_S_SESSION, - GUC_ACTION_SAVE, true, 0, false); + SetConfigOption("search_path", saved_path, + PGC_SUSET, PGC_S_SESSION); SetCurrentRoleId(current_user_id, false); } diff --git a/contrib/babelfishpg_tsql/src/session.c b/contrib/babelfishpg_tsql/src/session.c index d1367275e5c..621fd88d550 100644 --- a/contrib/babelfishpg_tsql/src/session.c +++ b/contrib/babelfishpg_tsql/src/session.c @@ -29,7 +29,6 @@ static int16 current_db_id = 0; static char current_db_name[MAX_BBF_NAMEDATALEND + 1] = {'\0'}; static Oid current_user_id = InvalidOid; -static void set_search_path_for_user_schema(const char *db_name, const char *user); void reset_cached_batch(void); /* Session Context */ @@ -128,8 +127,6 @@ set_session_properties(const char *db_name) (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", db_name))); - check_session_db_access(db_name); - set_cur_user_db_and_path(db_name); } @@ -163,6 +160,16 @@ set_cur_user_db_and_path(const char *db_name) const char *user = get_user_for_database(db_name); int16 db_id = get_db_id(db_name); + if (!user) + { + char *login = GetUserNameFromId(GetSessionUserId(), false); + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("The server principal \"%s\" is not able to access " + "the database \"%s\" under the current security context", + login, db_name))); + } + /* set current DB */ set_cur_db(db_id, db_name); @@ -177,44 +184,42 @@ set_cur_user_db_and_path(const char *db_name) set_db_collation_internal(db_name); } -static void +void set_search_path_for_user_schema(const char *db_name, const char *user) { - const char *path; - const char *buffer = "%s, \"$user\", sys, pg_catalog"; - char *physical_schema; - char *dbo_role_name = get_dbo_role_name(db_name); - char *guest_role_name = get_guest_role_name(db_name); + char *path; + char *db_name_copy = pstrdup(db_name); + char *schema; + char *physical_schema = NULL; + char *dbo_schema_name = get_dbo_schema_name(db_name); - if ((dbo_role_name && strcmp(user, dbo_role_name) == 0)) - { - physical_schema = get_dbo_schema_name(db_name); - } - else if (guest_role_name && strcmp(user, guest_role_name) == 0) - { - const char *guest_schema = get_authid_user_ext_schema_name(db_name, "guest"); + Assert (db_name != NULL && strlen(db_name) > 0); - if (!guest_schema) - guest_schema = "guest"; - physical_schema = get_physical_schema_name(pstrdup(db_name), guest_schema); - } - else + schema = get_authid_user_ext_schema_name(db_name, user); + if (schema) { - const char *schema; - - schema = get_authid_user_ext_schema_name(db_name, user); - physical_schema = get_physical_schema_name(pstrdup(db_name), schema); + physical_schema = get_physical_schema_name(db_name_copy, schema); + pfree(schema); } - - path = psprintf(buffer, quote_identifier(physical_schema)); - SetConfigOption("search_path", - path, - PGC_SUSET, - PGC_S_DATABASE_USER); + path = psprintf(PLTSQL_SEARCH_PATH_BUFFER, + physical_schema ? quote_identifier(physical_schema) : quote_identifier(dbo_schema_name), + quote_identifier(dbo_schema_name)); + + SetConfigOption("search_path", path, + PGC_USERSET, PGC_S_DATABASE_USER); + if (current_db_search_path) + pfree(current_db_search_path); + current_db_search_path = MemoryContextStrdup(TopMemoryContext, path); + + SetConfigOption("search_path", path, + PGC_SUSET, PGC_S_SESSION); - pfree(dbo_role_name); - pfree(guest_role_name); - pfree(physical_schema); + pfree(path); + pfree(dbo_schema_name); + pfree(db_name_copy); + + if (physical_schema) + pfree(physical_schema); } /* diff --git a/contrib/babelfishpg_tsql/src/session.h b/contrib/babelfishpg_tsql/src/session.h index 82f58e3bd9e..4b0b3fb993c 100644 --- a/contrib/babelfishpg_tsql/src/session.h +++ b/contrib/babelfishpg_tsql/src/session.h @@ -14,12 +14,18 @@ extern void check_session_db_access(const char *dn_name); extern void set_cur_user_db_and_path(const char *db_name); extern void restore_session_properties(void); extern void reset_session_properties(void); +extern void set_search_path_for_user_schema(const char *db_name, const char *user); extern void set_cur_db_name_for_parallel_worker(const char* logical_db_name); /* Hooks for parallel workers for babelfish fixed state */ extern void babelfixedparallelstate_insert(ParallelContext *pcxt, bool estimate); extern void babelfixedparallelstate_restore(shm_toc *toc); +/* search path when executing pltsql routines */ +extern char *pltsql_search_path; +/* search path for current active logical database */ +extern char *current_db_search_path; + /* Babelfish Fixed-size parallel state */ typedef struct BabelfishFixedParallelState { char logical_db_name[MAX_BBF_NAMEDATALEND + 1]; diff --git a/contrib/babelfishpg_tsql/src/tsqlIface.cpp b/contrib/babelfishpg_tsql/src/tsqlIface.cpp index 3be79c38669..c989a5f19fc 100644 --- a/contrib/babelfishpg_tsql/src/tsqlIface.cpp +++ b/contrib/babelfishpg_tsql/src/tsqlIface.cpp @@ -1420,8 +1420,6 @@ class tsqlBuilder : public tsqlCommonMutator bool is_cross_db = false; std::string schema_name; std::string db_name; - bool is_function = false; - bool is_schema_specified = false; bool in_func_body_return_select_body = false; // We keep a stack of the containers that are active during a traversal. @@ -1893,29 +1891,6 @@ class tsqlBuilder : public tsqlCommonMutator ctx->insert_statement()->insert_statement_value() && ctx->insert_statement()->insert_statement_value()->execute_statement(); - if (stmt->insert_exec) - { - TSqlParser::Func_proc_name_server_database_schemaContext *ctx_name = nullptr; - TSqlParser::Execute_bodyContext *body = nullptr; - - TSqlParser::Execute_statementContext *ctxES = ctx->insert_statement()->insert_statement_value()->execute_statement(); - body = ctxES->execute_body(); - Assert(body); - - ctx_name = body->func_proc_name_server_database_schema(); - if (ctx_name) - { - if (ctx_name->database) - { - db_name = stripQuoteFromId(ctx_name->database); - if (!string_matches(db_name.c_str(), get_cur_db_name())) - { - is_cross_db = true; - } - } - } - } - // record whether stmt is cross-db if (is_cross_db) { @@ -1924,18 +1899,12 @@ class tsqlBuilder : public tsqlCommonMutator } // record that the stmt is dml stmt->is_dml = true; - // record if a function call - if (is_function) - stmt->func_call = true; if (!schema_name.empty()) stmt->schema_name = pstrdup(downcase_truncate_identifier(schema_name.c_str(), schema_name.length(), true)); // record db name for the cross db query if (!db_name.empty()) stmt->db_name = pstrdup(downcase_truncate_identifier(db_name.c_str(), db_name.length(), true)); - // record if the SQL object is schema qualified - if (is_schema_specified) - stmt->is_schema_specified = true; if (is_compiling_create_function()) { @@ -2182,8 +2151,14 @@ class tsqlBuilder : public tsqlCommonMutator } PLtsql_stmt_execsql *stmt = (PLtsql_stmt_execsql *) getPLtsql_fragment(ctx); Assert(stmt); - // record that the stmt is ddl - stmt->is_ddl = true; + + /* + * This flag is only used while setting the search path for ddl statements in + * execsql. we do not change search path for alter user because it does not depend + * on search path and doing so has side effects like change to user default schema + * does not take effect in current session + */ + stmt->is_ddl = !ctx->alter_user(); if (is_compiling_create_function()) { @@ -2670,10 +2645,7 @@ class tsqlBuilder : public tsqlCommonMutator if (ctx && (ctx->DOT().size() <= 2) && ctx->schema) { schema_name = stripQuoteFromId(ctx->schema); - is_schema_specified = true; } - else - is_schema_specified = false; // The flag setSysSchema is used exclusively in case of rewriting a cross-DB catalog reference // that uses 'dbo' as schema: this puts 'sys' in tsqlBuilder::schema_name, which ends up @@ -2743,7 +2715,6 @@ class tsqlBuilder : public tsqlCommonMutator void exitFunction_call(TSqlParser::Function_callContext *ctx) override { - is_function = true; if (ctx->NEXT() && ctx->full_object_name()) { TSqlParser::Full_object_nameContext *fctx = (TSqlParser::Full_object_nameContext *) ctx->full_object_name(); @@ -6946,6 +6917,8 @@ makeExecuteProcedure(ParserRuleContext *ctx, std::string call_type) { result->proc_name = pstrdup(downcase_truncate_identifier(proc_name.c_str(), proc_name.length(), true)); } + else + result->proc_name = pstrdup(""); if (!schema_name.empty()) { result->schema_name = pstrdup(downcase_truncate_identifier(schema_name.c_str(), schema_name.length(), true)); diff --git a/test/JDBC/expected/BABEL-CROSS-DB-vu-cleanup.out b/test/JDBC/expected/BABEL-CROSS-DB-vu-cleanup.out index 867c5cdcf83..13cfa0725db 100644 --- a/test/JDBC/expected/BABEL-CROSS-DB-vu-cleanup.out +++ b/test/JDBC/expected/BABEL-CROSS-DB-vu-cleanup.out @@ -140,3 +140,6 @@ GO DROP SCHEMA babel_cross_db_vu_prepare_myschema GO + +DROP PROCEDURE babel_5448_p2, babel_5448_p1 +GO diff --git a/test/JDBC/expected/BABEL-CROSS-DB-vu-prepare.out b/test/JDBC/expected/BABEL-CROSS-DB-vu-prepare.out index 5a9cf7047e0..60a5a07ca7d 100644 --- a/test/JDBC/expected/BABEL-CROSS-DB-vu-prepare.out +++ b/test/JDBC/expected/BABEL-CROSS-DB-vu-prepare.out @@ -279,3 +279,38 @@ GO GRANT SELECT ON babel_cross_db_vu_prepare_t4 TO guest; GO + +------------------ BABEL-5448 ------------------ +------------------------------------------------ +USE master; +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is correct datbase dbname = master'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p2 AS +BEGIN + EXECUTE dbo.babel_5448_p1; -- schema.proc + EXECUTE master.dbo.babel_5448_p1; -- db.schema.proc + EXECUTE master..babel_5448_p1; -- db..proc + EXECUTE babel_5448_p1; -- proc +END; +GO + +USE my_babel_cross_db_vu_prepare_db1; +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is the incorrect database: dbname = my_babel_cross_db_vu_prepare_db1'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p3 AS +BEGIN + EXECUTE master.dbo.babel_5448_p2; +END; +GO diff --git a/test/JDBC/expected/BABEL-CROSS-DB-vu-verify.out b/test/JDBC/expected/BABEL-CROSS-DB-vu-verify.out index 93c9c832689..9d361d116ea 100644 --- a/test/JDBC/expected/BABEL-CROSS-DB-vu-verify.out +++ b/test/JDBC/expected/BABEL-CROSS-DB-vu-verify.out @@ -1921,3 +1921,29 @@ GO -- [TESTCASE GROUP END] Batches for testing default_database and default_schema setting USE master GO + +USE my_babel_cross_db_vu_prepare_db1; +GO + +EXECUTE dbo.babel_5448_p3; +GO +~~START~~ +varchar +This is correct datbase dbname = master +~~END~~ + +~~START~~ +varchar +This is correct datbase dbname = master +~~END~~ + +~~START~~ +varchar +This is correct datbase dbname = master +~~END~~ + +~~START~~ +varchar +This is correct datbase dbname = master +~~END~~ + diff --git a/test/JDBC/expected/BABEL-CROSS-DB.out b/test/JDBC/expected/BABEL-CROSS-DB.out index 39e9e149174..1988f774e11 100644 --- a/test/JDBC/expected/BABEL-CROSS-DB.out +++ b/test/JDBC/expected/BABEL-CROSS-DB.out @@ -1003,3 +1003,76 @@ GO DROP DATABASE db2; GO + +------------------ BABEL-5448 ------------------ +------------------------------------------------ +USE master; +GO + +CREATE DATABASE babel_5448_db1; +GO +CREATE DATABASE babel_5448_db2; +GO + +USE babel_5448_db1 +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is correct: babel_5448_db1.dbo.babel_5448_p1'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p2 AS +BEGIN + EXECUTE dbo.babel_5448_p1; -- schema.proc + EXECUTE babel_5448_db1.dbo.babel_5448_p1; -- db.schema.proc + EXECUTE babel_5448_db1..babel_5448_p1; -- db..proc + EXECUTE babel_5448_p1; -- proc +END; +GO + +USE babel_5448_db2; +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is the incorrect database: babel_5448_db2.dbo.sp_test_1'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p3 AS +BEGIN + EXECUTE babel_5448_db1.dbo.babel_5448_p2; +END; +GO + +EXECUTE dbo.babel_5448_p3; +GO +~~START~~ +varchar +This is correct: babel_5448_db1.dbo.babel_5448_p1 +~~END~~ + +~~START~~ +varchar +This is correct: babel_5448_db1.dbo.babel_5448_p1 +~~END~~ + +~~START~~ +varchar +This is correct: babel_5448_db1.dbo.babel_5448_p1 +~~END~~ + +~~START~~ +varchar +This is correct: babel_5448_db1.dbo.babel_5448_p1 +~~END~~ + + +USE master +GO +DROP DATABASE babel_5448_db2 +GO +DROP DATABASE babel_5448_db1 +GO diff --git a/test/JDBC/input/BABEL-CROSS-DB.mix b/test/JDBC/input/BABEL-CROSS-DB.mix index 05ebc64454e..04e329069a2 100644 --- a/test/JDBC/input/BABEL-CROSS-DB.mix +++ b/test/JDBC/input/BABEL-CROSS-DB.mix @@ -632,3 +632,58 @@ GO DROP DATABASE db2; GO + +------------------ BABEL-5448 ------------------ +------------------------------------------------ +USE master; +GO + +CREATE DATABASE babel_5448_db1; +GO +CREATE DATABASE babel_5448_db2; +GO + +USE babel_5448_db1 +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is correct: babel_5448_db1.dbo.babel_5448_p1'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p2 AS +BEGIN + EXECUTE dbo.babel_5448_p1; -- schema.proc + EXECUTE babel_5448_db1.dbo.babel_5448_p1; -- db.schema.proc + EXECUTE babel_5448_db1..babel_5448_p1; -- db..proc + EXECUTE babel_5448_p1; -- proc +END; +GO + +USE babel_5448_db2; +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is the incorrect database: babel_5448_db2.dbo.sp_test_1'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p3 AS +BEGIN + EXECUTE babel_5448_db1.dbo.babel_5448_p2; +END; +GO + +EXECUTE dbo.babel_5448_p3; +GO + +USE master +GO +DROP DATABASE babel_5448_db2 +GO +DROP DATABASE babel_5448_db1 +GO +----------- END OF BABEL-5448 ------------------ +------------------------------------------------ diff --git a/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-cleanup.mix b/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-cleanup.mix index 867c5cdcf83..aa424a4d5f3 100644 --- a/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-cleanup.mix +++ b/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-cleanup.mix @@ -140,3 +140,6 @@ GO DROP SCHEMA babel_cross_db_vu_prepare_myschema GO + +DROP PROCEDURE babel_5448_p2, babel_5448_p1 +GO \ No newline at end of file diff --git a/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-prepare.mix b/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-prepare.mix index 425e73d24f6..de6d3ec4adf 100644 --- a/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-prepare.mix +++ b/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-prepare.mix @@ -275,3 +275,40 @@ GO GRANT SELECT ON babel_cross_db_vu_prepare_t4 TO guest; GO + +------------------ BABEL-5448 ------------------ +------------------------------------------------ +USE master; +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is correct datbase dbname = master'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p2 AS +BEGIN + EXECUTE dbo.babel_5448_p1; -- schema.proc + EXECUTE master.dbo.babel_5448_p1; -- db.schema.proc + EXECUTE master..babel_5448_p1; -- db..proc + EXECUTE babel_5448_p1; -- proc +END; +GO + +USE my_babel_cross_db_vu_prepare_db1; +GO + +CREATE PROCEDURE dbo.babel_5448_p1 AS +BEGIN + SELECT 'This is the incorrect database: dbname = my_babel_cross_db_vu_prepare_db1'; +END; +GO + +CREATE PROCEDURE dbo.babel_5448_p3 AS +BEGIN + EXECUTE master.dbo.babel_5448_p2; +END; +GO +----------- END OF BABEL-5448 ------------------ +------------------------------------------------ diff --git a/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-verify.mix b/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-verify.mix index 4a20c1731fc..d717758ddce 100644 --- a/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-verify.mix +++ b/test/JDBC/input/ownership/BABEL-CROSS-DB-vu-verify.mix @@ -926,3 +926,9 @@ GO -- tsql USE master GO + +USE my_babel_cross_db_vu_prepare_db1; +GO + +EXECUTE dbo.babel_5448_p3; +GO