From 526040436d37a5a211ef76112b872ae540870239 Mon Sep 17 00:00:00 2001 From: Noah Misch Date: Mon, 11 Nov 2024 06:23:43 -0800 Subject: [PATCH 1/6] Block environment variable mutations from trusted PL/Perl. Many process environment variables (e.g. PATH), bypass the containment expected of a trusted PL. Hence, trusted PLs must not offer features that achieve setenv(). Otherwise, an attacker having USAGE privilege on the language often can achieve arbitrary code execution, even if the attacker lacks a database server operating system user. To fix PL/Perl, replace trusted PL/Perl %ENV with a tied hash that just replaces each modification attempt with a warning. Sites that reach these warnings should evaluate the application-specific implications of proceeding without the environment modification: Can the application reasonably proceed without the modification? If no, switch to plperlu or another approach. If yes, the application should change the code to stop attempting environment modifications. If that's too difficult, add "untie %main::ENV" in any code executed before the warning. For example, one might add it to the start of the affected function or even to the plperl.on_plperl_init setting. In passing, link to Perl's guidance about the Perl features behind the security posture of PL/Perl. Back-patch to v12 (all supported versions). Andrew Dunstan and Noah Misch Security: CVE-2024-10979 --- doc/src/sgml/plperl.sgml | 13 +++++++ src/pl/plperl/GNUmakefile | 4 +- src/pl/plperl/input/plperl_env.source | 52 ++++++++++++++++++++++++++ src/pl/plperl/output/plperl_env.source | 49 ++++++++++++++++++++++++ src/pl/plperl/plc_trusted.pl | 24 ++++++++++++ src/test/regress/regress.c | 24 ++++++++++++ 6 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/pl/plperl/input/plperl_env.source create mode 100644 src/pl/plperl/output/plperl_env.source diff --git a/doc/src/sgml/plperl.sgml b/doc/src/sgml/plperl.sgml index 01f9870773d..ee02c01f106 100644 --- a/doc/src/sgml/plperl.sgml +++ b/doc/src/sgml/plperl.sgml @@ -1082,6 +1082,19 @@ $$ LANGUAGE plperl; be permitted to use this language. + + + Trusted PL/Perl relies on the Perl Opcode module to + preserve security. + Perl + documents + that the module is not effective for the trusted PL/Perl use case. If + your security needs are incompatible with the uncertainty in that warning, + consider executing REVOKE USAGE ON LANGUAGE plperl FROM + PUBLIC. + + + Here is an example of a function that will not work because file system operations are not allowed for security reasons: diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile index 06a6c1d57c1..aff976572e5 100644 --- a/src/pl/plperl/GNUmakefile +++ b/src/pl/plperl/GNUmakefile @@ -55,11 +55,11 @@ endif # win32 SHLIB_LINK = $(perl_embed_ldflags) -REGRESS_OPTS = --dbname=$(PL_TESTDB) +REGRESS_OPTS = --dbname=$(PL_TESTDB) --dlpath=$(top_builddir)/src/test/regress REGRESS_OPTS += --init-file=init_file REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \ plperl_elog plperl_util plperl_init plperlu plperl_array \ - plperl_call plperl_transaction + plperl_call plperl_transaction plperl_env # if Perl can support two interpreters in one backend, # test plperl-and-plperlu cases ifneq ($(PERL),) diff --git a/src/pl/plperl/input/plperl_env.source b/src/pl/plperl/input/plperl_env.source new file mode 100644 index 00000000000..8fe526e1b8b --- /dev/null +++ b/src/pl/plperl/input/plperl_env.source @@ -0,0 +1,52 @@ +-- +-- Test the environment setting +-- + +CREATE FUNCTION get_environ() + RETURNS text[] + AS '@libdir@/regress@DLSUFFIX@', 'get_environ' + LANGUAGE C STRICT; + +-- fetch the process environment + +CREATE FUNCTION process_env () RETURNS text[] +LANGUAGE plpgsql AS +$$ + +declare + res text[]; + tmp text[]; + f record; +begin + for f in select unnest(get_environ()) as t loop + tmp := regexp_split_to_array(f.t, '='); + if array_length(tmp, 1) = 2 then + res := res || tmp; + end if; + end loop; + return res; +end + +$$; + +-- plperl should not be able to affect the process environment + +DO +$$ + $ENV{TEST_PLPERL_ENV_FOO} = "shouldfail"; + untie %ENV; + $ENV{TEST_PLPERL_ENV_FOO} = "testval"; + my $penv = spi_exec_query("select unnest(process_env()) as pe"); + my %received; + for (my $f = 0; $f < $penv->{processed}; $f += 2) + { + my $k = $penv->{rows}[$f]->{pe}; + my $v = $penv->{rows}[$f+1]->{pe}; + $received{$k} = $v; + } + unless (exists $received{TEST_PLPERL_ENV_FOO}) + { + elog(NOTICE, "environ unaffected") + } + +$$ LANGUAGE plperl; diff --git a/src/pl/plperl/output/plperl_env.source b/src/pl/plperl/output/plperl_env.source new file mode 100644 index 00000000000..37b7e23d5ce --- /dev/null +++ b/src/pl/plperl/output/plperl_env.source @@ -0,0 +1,49 @@ +-- +-- Test the environment setting +-- +CREATE FUNCTION get_environ() + RETURNS text[] + AS '@libdir@/regress@DLSUFFIX@', 'get_environ' + LANGUAGE C STRICT; +-- fetch the process environment +CREATE FUNCTION process_env () RETURNS text[] +LANGUAGE plpgsql AS +$$ + +declare + res text[]; + tmp text[]; + f record; +begin + for f in select unnest(get_environ()) as t loop + tmp := regexp_split_to_array(f.t, '='); + if array_length(tmp, 1) = 2 then + res := res || tmp; + end if; + end loop; + return res; +end + +$$; +-- plperl should not be able to affect the process environment +DO +$$ + $ENV{TEST_PLPERL_ENV_FOO} = "shouldfail"; + untie %ENV; + $ENV{TEST_PLPERL_ENV_FOO} = "testval"; + my $penv = spi_exec_query("select unnest(process_env()) as pe"); + my %received; + for (my $f = 0; $f < $penv->{processed}; $f += 2) + { + my $k = $penv->{rows}[$f]->{pe}; + my $v = $penv->{rows}[$f+1]->{pe}; + $received{$k} = $v; + } + unless (exists $received{TEST_PLPERL_ENV_FOO}) + { + elog(NOTICE, "environ unaffected") + } + +$$ LANGUAGE plperl; +WARNING: attempted alteration of $ENV{TEST_PLPERL_ENV_FOO} at line 12. +NOTICE: environ unaffected diff --git a/src/pl/plperl/plc_trusted.pl b/src/pl/plperl/plc_trusted.pl index 2ca71e6e12d..eba3877f31d 100644 --- a/src/pl/plperl/plc_trusted.pl +++ b/src/pl/plperl/plc_trusted.pl @@ -30,3 +30,27 @@ package PostgreSQL::InServer::safe; ## no critic (RequireFilenameMatchesPackage) require Carp::Heavy; require warnings; require feature if $] >= 5.010000; + +#<<< protect next line from perltidy so perlcritic annotation works +package PostgreSQL::InServer::WarnEnv; ## no critic (RequireFilenameMatchesPackage) +#>>> + +use strict; +use warnings; +use Tie::Hash; +our @ISA = qw(Tie::StdHash); + +sub STORE { warn "attempted alteration of \$ENV{$_[1]}"; } +sub DELETE { warn "attempted deletion of \$ENV{$_[1]}"; } +sub CLEAR { warn "attempted clearance of ENV hash"; } + +# Remove magic property of %ENV. Changes to this will now not be reflected in +# the process environment. +*main::ENV = {%ENV}; + +# Block %ENV changes from trusted PL/Perl, and warn. We changed %ENV to just a +# normal hash, yet the application may be expecting the usual Perl %ENV +# magic. Blocking and warning avoids silent application breakage. The user can +# untie or otherwise disable this, e.g. if the lost mutation is unimportant +# and modifying the code to stop that mutation would be onerous. +tie %main::ENV, 'PostgreSQL::InServer::WarnEnv', %ENV or die $!; diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index d8fb52ebdd2..0a0153e5551 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -39,6 +39,7 @@ #include "parser/parse_coerce.h" #include "port/atomics.h" #include "storage/spin.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/geo_decls.h" #include "utils/memutils.h" @@ -628,6 +629,29 @@ make_tuple_indirect(PG_FUNCTION_ARGS) PG_RETURN_POINTER(newtup->t_data); } +PG_FUNCTION_INFO_V1(get_environ); + +Datum +get_environ(PG_FUNCTION_ARGS) +{ + extern char **environ; + int nvals = 0; + ArrayType *result; + Datum *env; + + for (char **s = environ; *s; s++) + nvals++; + + env = palloc(nvals * sizeof(Datum)); + + for (int i = 0; i < nvals; i++) + env[i] = CStringGetTextDatum(environ[i]); + + result = construct_array(env, nvals, TEXTOID, -1, false, TYPALIGN_INT); + + PG_RETURN_POINTER(result); +} + PG_FUNCTION_INFO_V1(regress_setenv); Datum From 2a023e5f080ca60ed18176d30b20e1d106b6fb4b Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Mon, 11 Nov 2024 09:00:00 -0600 Subject: [PATCH 2/6] Ensure cached plans are correctly marked as dependent on role. If a CTE, subquery, sublink, security invoker view, or coercion projection references a table with row-level security policies, we neglected to mark the plan as potentially dependent on which role is executing it. This could lead to later executions in the same session returning or hiding rows that should have been hidden or returned instead. Reported-by: Wolfgang Walther Reviewed-by: Noah Misch Security: CVE-2024-10976 Backpatch-through: 12 --- src/backend/executor/functions.c | 6 ++ src/backend/rewrite/rewriteHandler.c | 67 +++++++++++++++++-- src/test/regress/expected/rowsecurity.out | 78 +++++++++++++++++++++++ src/test/regress/sql/rowsecurity.sql | 44 +++++++++++++ src/tools/pgindent/typedefs.list | 1 + 5 files changed, 190 insertions(+), 6 deletions(-) diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 41da04f652b..f1b287d8ff4 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -2120,6 +2120,12 @@ check_sql_fn_retval(List *queryTreeLists, rtr->rtindex = 1; newquery->jointree = makeFromExpr(list_make1(rtr), NULL); + /* + * Make sure the new query is marked as having row security if the + * original one does. + */ + newquery->hasRowSecurity = parse->hasRowSecurity; + /* Replace original query in the correct element of the query list */ lfirst(parse_cell) = newquery; } diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 2938edf1c6a..9670eb5d34a 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -59,6 +59,12 @@ typedef struct acquireLocksOnSubLinks_context bool for_execute; /* AcquireRewriteLocks' forExecute param */ } acquireLocksOnSubLinks_context; +typedef struct fireRIRonSubLink_context +{ + List *activeRIRs; + bool hasRowSecurity; +} fireRIRonSubLink_context; + static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context); static Query *rewriteRuleAction(Query *parsetree, @@ -1910,6 +1916,12 @@ ApplyRetrieveRule(Query *parsetree, */ rule_action = fireRIRrules(rule_action, activeRIRs); + /* + * Make sure the query is marked as having row security if the view query + * does. + */ + parsetree->hasRowSecurity |= rule_action->hasRowSecurity; + /* * Now, plug the view query in as a subselect, converting the relation's * original RTE to a subquery RTE. @@ -2021,7 +2033,7 @@ markQueryForLocking(Query *qry, Node *jtnode, * the SubLink's subselect link with the possibly-rewritten subquery. */ static bool -fireRIRonSubLink(Node *node, List *activeRIRs) +fireRIRonSubLink(Node *node, fireRIRonSubLink_context *context) { if (node == NULL) return false; @@ -2031,7 +2043,13 @@ fireRIRonSubLink(Node *node, List *activeRIRs) /* Do what we came for */ sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect, - activeRIRs); + context->activeRIRs); + + /* + * Remember if any of the sublinks have row security. + */ + context->hasRowSecurity |= ((Query *) sub->subselect)->hasRowSecurity; + /* Fall through to process lefthand args of SubLink */ } @@ -2040,7 +2058,7 @@ fireRIRonSubLink(Node *node, List *activeRIRs) * subselects of subselects for us. */ return expression_tree_walker(node, fireRIRonSubLink, - (void *) activeRIRs); + (void *) context); } @@ -2101,6 +2119,13 @@ fireRIRrules(Query *parsetree, List *activeRIRs) if (rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_TABLEFUNCTION) { rte->subquery = fireRIRrules(rte->subquery, activeRIRs); + + /* + * While we are here, make sure the query is marked as having row + * security if any of its subqueries do. + */ + parsetree->hasRowSecurity |= rte->subquery->hasRowSecurity; + continue; } @@ -2218,6 +2243,12 @@ fireRIRrules(Query *parsetree, List *activeRIRs) cte->ctequery = (Node *) fireRIRrules((Query *) cte->ctequery, activeRIRs); + + /* + * While we are here, make sure the query is marked as having row + * security if any of its CTEs do. + */ + parsetree->hasRowSecurity |= ((Query *) cte->ctequery)->hasRowSecurity; } /* @@ -2225,9 +2256,22 @@ fireRIRrules(Query *parsetree, List *activeRIRs) * the rtable and cteList. */ if (parsetree->hasSubLinks) - query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs, + { + fireRIRonSubLink_context context; + + context.activeRIRs = activeRIRs; + context.hasRowSecurity = false; + + query_tree_walker(parsetree, fireRIRonSubLink, (void *) &context, QTW_IGNORE_RC_SUBQUERIES); + /* + * Make sure the query is marked as having row security if any of its + * sublinks do. + */ + parsetree->hasRowSecurity |= context.hasRowSecurity; + } + /* * Apply any row-level security policies. We do this last because it * requires special recursion detection if the new quals have sublink @@ -2266,6 +2310,7 @@ fireRIRrules(Query *parsetree, List *activeRIRs) if (hasSubLinks) { acquireLocksOnSubLinks_context context; + fireRIRonSubLink_context fire_context; /* * Recursively process the new quals, checking for infinite @@ -2296,11 +2341,21 @@ fireRIRrules(Query *parsetree, List *activeRIRs) * Now that we have the locks on anything added by * get_row_security_policies, fire any RIR rules for them. */ + fire_context.activeRIRs = activeRIRs; + fire_context.hasRowSecurity = false; + expression_tree_walker((Node *) securityQuals, - fireRIRonSubLink, (void *) activeRIRs); + fireRIRonSubLink, (void *) &fire_context); expression_tree_walker((Node *) withCheckOptions, - fireRIRonSubLink, (void *) activeRIRs); + fireRIRonSubLink, (void *) &fire_context); + + /* + * We can ignore the value of fire_context.hasRowSecurity + * since we only reach this code in cases where hasRowSecurity + * is already true. + */ + Assert(hasRowSecurity); activeRIRs = list_delete_last(activeRIRs); } diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 2fc722f5c41..e57da544f2b 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -4194,6 +4194,84 @@ execute q; --------------+--- (0 rows) +-- make sure RLS dependencies in CTEs are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ with cte as (select * from rls_t) select * from cte $$; +prepare r as select current_user, * from rls_f(); +set role regress_rls_alice; +execute r; + current_user | c +-------------------+------------------ + regress_rls_alice | invisible to bob +(1 row) + +set role regress_rls_bob; +execute r; + current_user | c +--------------+--- +(0 rows) + +-- make sure RLS dependencies in subqueries are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select * from rls_t) _ $$; +prepare s as select current_user, * from rls_f(); +set role regress_rls_alice; +execute s; + current_user | c +-------------------+------------------ + regress_rls_alice | invisible to bob +(1 row) + +set role regress_rls_bob; +execute s; + current_user | c +--------------+--- +(0 rows) + +-- make sure RLS dependencies in sublinks are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select exists(select * from rls_t)::text $$; +prepare t as select current_user, * from rls_f(); +set role regress_rls_alice; +execute t; + current_user | c +-------------------+------ + regress_rls_alice | true +(1 row) + +set role regress_rls_bob; +execute t; + current_user | c +-----------------+------- + regress_rls_bob | false +(1 row) + +-- make sure RLS dependencies are handled when coercion projections are inserted +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select array_agg(c) as cs from rls_t) _ group by cs $$; +prepare u as select current_user, * from rls_f(); +set role regress_rls_alice; +execute u; + current_user | c +-------------------+---------------------- + regress_rls_alice | {"invisible to bob"} +(1 row) + +set role regress_rls_bob; +execute u; + current_user | c +-----------------+--- + regress_rls_bob | +(1 row) + RESET ROLE; DROP FUNCTION rls_f(); DROP TABLE rls_t; diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index e828368a4e5..225086be62e 100644 --- a/src/test/regress/sql/rowsecurity.sql +++ b/src/test/regress/sql/rowsecurity.sql @@ -1884,6 +1884,50 @@ execute q; set role regress_rls_bob; execute q; +-- make sure RLS dependencies in CTEs are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ with cte as (select * from rls_t) select * from cte $$; +prepare r as select current_user, * from rls_f(); +set role regress_rls_alice; +execute r; +set role regress_rls_bob; +execute r; + +-- make sure RLS dependencies in subqueries are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select * from rls_t) _ $$; +prepare s as select current_user, * from rls_f(); +set role regress_rls_alice; +execute s; +set role regress_rls_bob; +execute s; + +-- make sure RLS dependencies in sublinks are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select exists(select * from rls_t)::text $$; +prepare t as select current_user, * from rls_f(); +set role regress_rls_alice; +execute t; +set role regress_rls_bob; +execute t; + +-- make sure RLS dependencies are handled when coercion projections are inserted +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select array_agg(c) as cs from rls_t) _ group by cs $$; +prepare u as select current_user, * from rls_f(); +set role regress_rls_alice; +execute u; +set role regress_rls_bob; +execute u; + RESET ROLE; DROP FUNCTION rls_f(); DROP TABLE rls_t; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 2eb501b6e74..fe36db36936 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3133,6 +3133,7 @@ fill_string_relopt finalize_primnode_context find_dependent_phvs_context find_expr_references_context +fireRIRonSubLink_context fix_join_expr_context fix_scan_expr_context fix_upper_expr_context From 1d604b4a52e8fffd4364237cb9b6953f1a1e66ef Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 11 Nov 2024 10:42:32 -0500 Subject: [PATCH 3/6] Add needed .gitignore files in back branches. v14 and earlier use generated test files, which require being .gitignore'd to avoid git complaints when testing in-tree. Security: CVE-2024-10979 --- src/pl/plperl/expected/.gitignore | 1 + src/pl/plperl/sql/.gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 src/pl/plperl/expected/.gitignore create mode 100644 src/pl/plperl/sql/.gitignore diff --git a/src/pl/plperl/expected/.gitignore b/src/pl/plperl/expected/.gitignore new file mode 100644 index 00000000000..0cee2ff3f88 --- /dev/null +++ b/src/pl/plperl/expected/.gitignore @@ -0,0 +1 @@ +/plperl_env.out diff --git a/src/pl/plperl/sql/.gitignore b/src/pl/plperl/sql/.gitignore new file mode 100644 index 00000000000..01795fdcd5a --- /dev/null +++ b/src/pl/plperl/sql/.gitignore @@ -0,0 +1 @@ +/plperl_env.sql From e92256aeb3c4d98490733ee5b937f631e969930b Mon Sep 17 00:00:00 2001 From: Noah Misch Date: Mon, 11 Nov 2024 10:55:18 -0800 Subject: [PATCH 4/6] src/tools/msvc: Respect REGRESS_OPTS in plcheck. v16 commit 8fe3e697a1a83a722b107c7cb9c31084e1f4d077 used REGRESS_OPTS in a way needing this. That broke "vcregress plcheck". Back-patch v16..v12; newer versions don't have this build system. --- src/tools/msvc/vcregress.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/msvc/vcregress.pl b/src/tools/msvc/vcregress.pl index d4e7d69df2e..f19b0a4f755 100644 --- a/src/tools/msvc/vcregress.pl +++ b/src/tools/msvc/vcregress.pl @@ -458,13 +458,15 @@ sub plcheck # Move on if no tests are listed. next if (scalar @tests == 0); + my @opts = fetchRegressOpts(); + print "============================================================\n"; print "Checking $lang\n"; my @args = ( "$topdir/$Config/pg_regress/pg_regress", "--bindir=$topdir/$Config/psql", - "--dbname=pl_regression", @lang_args, @tests); + "--dbname=pl_regression", @lang_args, @opts, @tests); system(@args); my $status = $? >> 8; exit $status if $status; From c8e607b1289384c08f03b794ed5fbed247119aa2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 11 Nov 2024 13:57:21 -0500 Subject: [PATCH 5/6] Fix cross-version upgrade tests. TestUpgradeXversion knows how to make the main regression database's references to pg_regress.so be version-independent. But it doesn't do that for plperl's database, so that the C function added by commit b7e3a52a8 is causing cross-version upgrade test failures. Path of least resistance is to just drop the function at the end of the new test. In <= v14, also take the opportunity to clean up the generated test files. Security: CVE-2024-10979 --- src/pl/plperl/GNUmakefile | 1 + src/pl/plperl/input/plperl_env.source | 3 +++ src/pl/plperl/output/plperl_env.source | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile index aff976572e5..671ac7b7bb7 100644 --- a/src/pl/plperl/GNUmakefile +++ b/src/pl/plperl/GNUmakefile @@ -130,6 +130,7 @@ submake: clean distclean maintainer-clean: clean-lib rm -f SPI.c Util.c $(OBJS) perlchunks.h plperl_opmask.h + rm -f sql/plperl_env.sql expected/plperl_env.out rm -rf $(pg_regress_clean_files) ifeq ($(PORTNAME), win32) rm -f $(perlwithver).def diff --git a/src/pl/plperl/input/plperl_env.source b/src/pl/plperl/input/plperl_env.source index 8fe526e1b8b..49f03acb1dc 100644 --- a/src/pl/plperl/input/plperl_env.source +++ b/src/pl/plperl/input/plperl_env.source @@ -50,3 +50,6 @@ $$ } $$ LANGUAGE plperl; + +-- clean up to simplify cross-version upgrade testing +DROP FUNCTION get_environ(); diff --git a/src/pl/plperl/output/plperl_env.source b/src/pl/plperl/output/plperl_env.source index 37b7e23d5ce..ef75d5d5556 100644 --- a/src/pl/plperl/output/plperl_env.source +++ b/src/pl/plperl/output/plperl_env.source @@ -47,3 +47,5 @@ $$ $$ LANGUAGE plperl; WARNING: attempted alteration of $ENV{TEST_PLPERL_ENV_FOO} at line 12. NOTICE: environ unaffected +-- clean up to simplify cross-version upgrade testing +DROP FUNCTION get_environ(); From 66c178f8778fc33f2a837f38cb5eb7dba040ed6b Mon Sep 17 00:00:00 2001 From: reshke Date: Thu, 4 Jun 2026 19:04:10 +0000 Subject: [PATCH 6/6] Fix for CBDB regression suite --- .../expected/rowsecurity_optimizer.out | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/test/regress/expected/rowsecurity_optimizer.out b/src/test/regress/expected/rowsecurity_optimizer.out index 5717d5bf9a7..b6868c48786 100644 --- a/src/test/regress/expected/rowsecurity_optimizer.out +++ b/src/test/regress/expected/rowsecurity_optimizer.out @@ -4726,6 +4726,100 @@ DETAIL: Falling back to Postgres-based planner because GPORCA does not support --------------+--- (0 rows) +-- make sure RLS dependencies in CTEs are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ with cte as (select * from rls_t) select * from cte $$; +prepare r as select current_user, * from rls_f(); +set role regress_rls_alice; +execute r; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +-------------------+------------------ + regress_rls_alice | invisible to bob +(1 row) + +set role regress_rls_bob; +execute r; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +--------------+--- +(0 rows) + +-- make sure RLS dependencies in subqueries are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select * from rls_t) _ $$; +prepare s as select current_user, * from rls_f(); +set role regress_rls_alice; +execute s; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +-------------------+------------------ + regress_rls_alice | invisible to bob +(1 row) + +set role regress_rls_bob; +execute s; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +--------------+--- +(0 rows) + +-- make sure RLS dependencies in sublinks are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select exists(select * from rls_t)::text $$; +prepare t as select current_user, * from rls_f(); +set role regress_rls_alice; +execute t; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +-------------------+------ + regress_rls_alice | true +(1 row) + +set role regress_rls_bob; +execute t; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +-----------------+------- + regress_rls_bob | false +(1 row) + +-- make sure RLS dependencies are handled when coercion projections are inserted +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select array_agg(c) as cs from rls_t) _ group by cs $$; +prepare u as select current_user, * from rls_f(); +set role regress_rls_alice; +execute u; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +-------------------+---------------------- + regress_rls_alice | {"invisible to bob"} +(1 row) + +set role regress_rls_bob; +execute u; +INFO: GPORCA failed to produce a plan, falling back to Postgres-based planner +DETAIL: Falling back to Postgres-based planner because GPORCA does not support the following feature: Non-default collation + current_user | c +-----------------+--- + regress_rls_bob | +(1 row) + RESET ROLE; DROP FUNCTION rls_f(); DROP TABLE rls_t;