public inbox for [email protected]
help / color / mirror / Atom feedFrom: Akshay Joshi <[email protected]>
To: Rui Zhao <[email protected]>
Cc: solai v <[email protected]>
Cc: Ilmar Y <[email protected]>
Cc: [email protected]
Subject: Re: [PATCH] Add pg_get_policy_ddl() function to reconstruct CREATE POLICY statement
Date: Thu, 9 Jul 2026 16:27:41 +0530
Message-ID: <CANxoLDd4uL=EU+4cGVWp1F9oqVaye4uybUkJhhS7QfeHaqeDXQ@mail.gmail.com> (raw)
In-Reply-To: <CAHWVJhEpUPQkR0rkeTz88Z06aLxfxrv8-OiAt6Tmhd2q+HTBrA@mail.gmail.com>
References: <CANxoLDdJsRJqnjMXV3yjsk07Z5iRWxG-c2hZJC7bAKqf8ZXj_A@mail.gmail.com>
<CANxoLDffrZGRTGpW_sPQ-hPEYs0hgjaFgJQh3PJFpPu5Zsbgvg@mail.gmail.com>
<177997571870.313758.10720313850275742354.pgcf@coridan.postgresql.org>
<CANxoLDe7xiCWY-UEmzoK_uQdKh3PPNcGXJ3qdzFy3c0o_F+P_Q@mail.gmail.com>
<SY7PR01MB1092118DD149062E230159D89B6162@SY7PR01MB10921.ausprd01.prod.outlook.com>
<CAF0whuefRgV3_xgMhPoKaid1LN34CTLhTU-r5dgfFyEGVAFrPg@mail.gmail.com>
<CANxoLDfBQn+q32DKo5yvb63eD+965umMi8zxqqV8GcsxdN6Trg@mail.gmail.com>
<CANxoLDf80gXFLSqWM7d-idA=Grot49HsmP9YXZw49nCFe+Mi+g@mail.gmail.com>
<CANxoLDeqR6wVCoXjsEWv4M_mhng-GCtt1KxFdfM-8PKwushUCA@mail.gmail.com>
<CANxoLDcQyd=ojcmLCmU5zFT8j475AbXHpjSWHoVP4a5-vdY3Jw@mail.gmail.com>
<CAF0whue4v=bH8beH0sBxu9UoFKRcZHTW-zH0qeVEt0jW2x7JiQ@mail.gmail.com>
<CANxoLDd+Pfi=Do-+1ECG-eZg4=DXfxuUK+yX1KhjAMnQzyidwQ@mail.gmail.com>
<CAF0whuc22Tdf-V=1g21jgmKG0YiGk3ZhLoO7Ttog6tD5P91a2Q@mail.gmail.com>
<CANxoLDfUeoh+X1AeazXAQjYL312nTa6bY+=iPNJjmN4bXwSKWw@mail.gmail.com>
<CAHWVJhEpUPQkR0rkeTz88Z06aLxfxrv8-OiAt6Tmhd2q+HTBrA@mail.gmail.com>
Thanks Rui for the review. I have tried to fix the issues you raised.
The v16 patch is ready for review/commit.
On Wed, Jul 8, 2026 at 10:28 PM Rui Zhao <[email protected]> wrote:
> Hi Akshay,
>
> I tested v15 on current master (57f93af36f): builds, make check passes, and
> basic reconstruction, pretty mode, NULL handling, and default-clause
> omission
> all look good. Two things.
>
> 1) Back in the v3/v4 discussion you decided the ON <table> name should
> always
> be schema-qualified for safety (the pg_get_triggerdef_worker thread with
> jian he), and that pretty here only controls formatting, not schema (Phil's
> point). That reasoning didn't reach the USING / WITH CHECK expressions,
> though: object references inside them are still qualified only by the
> caller's
> search_path, so they can lose their schema and rebind to a different object
> when the DDL is replayed elsewhere:
>
> CREATE FUNCTION s1.f(int) RETURNS bool LANGUAGE sql AS 'SELECT $1 > 0';
> CREATE FUNCTION s2.f(int) RETURNS bool LANGUAGE sql AS 'SELECT $1 < 0';
> CREATE POLICY pf ON t2 USING (s1.f(a));
>
> SET search_path = public, s1;
> SELECT ddl FROM pg_get_policy_ddl('t2', 'pf') AS ddl;
> -- CREATE POLICY pf ON public.t2 USING (f(a)); -- s1. dropped
>
> SET search_path = public, s2;
> CREATE POLICY pf ON t2 USING (f(a)); -- now s2.f,
> opposite meaning
>
> So within one statement the ON clause is always qualified but the
> expression
> body isn't -- and as Phil noted, the pretty flag can't fix this, since
> pg_get_expr qualifies by search_path visibility regardless of pretty.
>
> Worth settling the contract, given where this function sits. The old
> pg_get_viewdef / ruledef / indexdef functions are search_path-aware and
> leave
> qualification to the caller (pg_dump sets search_path empty around them so
> the
> output is portable). The new pg_get_*_ddl functions committed so far (role,
> database, tablespace) are on global, schemaless objects, so the question
> never
> came up -- pg_get_policy_ddl is the first of the family whose output embeds
> schema-qualifiable references. Your own pg_get_table_ddl patch already hit
> this and deparses under a controlled search_path (narrowed to pg_catalog),
> so
> the most consistent fix is to do the same here (NewGUCNestLevel +
> set_config);
> an empty search_path already yields the fully-qualified s1.f(a). If instead
> the intent is to follow the caller's search_path, that's fine too, but it
> should be documented (and SET search_path = '' noted as the way to get
> portable DDL).
>
> 2) The doc calls the second parameter policy_name, but the actual argument
> is
> policyname, so the documented named-argument call fails:
>
> SELECT * FROM pg_get_policy_ddl("table" => 't'::regclass,
> policy_name => 'p_all');
> ERROR: function pg_get_policy_ddl(table => regclass, policy_name
> => unknown) does not exist
>
> Regards,
> Rui
>
Attachments:
[application/octet-stream] v16-0001-Add-pg_get_policy_ddl-to-reconstruct-CREATE.patch (34.0K, ../CANxoLDd4uL=EU+4cGVWp1F9oqVaye4uybUkJhhS7QfeHaqeDXQ@mail.gmail.com/3-v16-0001-Add-pg_get_policy_ddl-to-reconstruct-CREATE.patch)
download | inline diff:
From 4d82fcf9b04607804ecdcc4b3d90d57ac12ca671 Mon Sep 17 00:00:00 2001
From: Akshay Joshi <[email protected]>
Date: Fri, 22 May 2026 18:18:07 +0530
Subject: [PATCH v16] Add pg_get_policy_ddl() to reconstruct CREATE POLICY
statements
pg_get_policy_ddl(table regclass,
policyname name,
pretty bool DEFAULT false)
RETURNS SETOF text
reconstructs the CREATE POLICY statement for the named row-level
security policy on the specified table. Although a single statement is
produced, the function returns a SETOF text result so its calling
convention matches the rest of the pg_get_*_ddl family.
The reconstructed DDL includes all clauses of the CREATE POLICY syntax:
the policy's permissiveness (AS RESTRICTIVE), command type (FOR
SELECT/INSERT/UPDATE/DELETE), role list (TO <roles>), USING
qualification, and WITH CHECK expression. Clauses whose value equals
the parser's default -- PERMISSIVE, FOR ALL, and TO PUBLIC -- are
omitted from the output, matching the convention used by pg_get_indexdef,
pg_get_constraintdef, pg_get_viewdef, and similar functions. The result
is therefore semantically equivalent to the original DDL, not lexically
identical.
The pretty parameter controls output formatting and defaults to false,
following the same convention as pg_get_role_ddl, pg_get_tablespace_ddl,
and pg_get_database_ddl. NULL passed explicitly for pretty is treated
as false.
NULL inputs for the table or policy name yield no rows. An invalid
relation name surfaces the regclass resolution error; a non-existent
policy raises an explicit "policy ... does not exist" error.
Usage examples:
-- compact form (default)
SELECT * FROM pg_get_policy_ddl('rls_table', 'pol1');
SELECT * FROM pg_get_policy_ddl(16564, 'pol1');
-- pretty-printed form
SELECT * FROM pg_get_policy_ddl('rls_table', 'pol1', true);
Regression coverage is added to src/test/regress/sql/rowsecurity.sql
and exercises all valid combinations of the CREATE POLICY syntax:
PERMISSIVE/RESTRICTIVE, all FOR command variants (ALL/SELECT/INSERT/
UPDATE/DELETE), multi-role TO lists, USING-only, WITH CHECK-only, and
USING+WITH CHECK policies for both ALL and UPDATE commands (the only
two that accept both), RESTRICTIVE on a specific command type,
subquery expressions, pretty and non-pretty output, all boolean
representations for the pretty argument (true/false, on/off, 1/0),
NULL and error paths, and a round-trip test that drops and re-executes
the generated DDL.
Author: Akshay Joshi <[email protected]>
Reviewed-by: Rui Zhao <[email protected]>
---
doc/src/sgml/func/func-info.sgml | 19 ++
src/backend/utils/adt/ddlutils.c | 274 +++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 +
src/test/regress/expected/rowsecurity.out | 315 ++++++++++++++++++++++
src/test/regress/sql/rowsecurity.sql | 150 +++++++++++
5 files changed, 764 insertions(+)
diff --git a/doc/src/sgml/func/func-info.sgml b/doc/src/sgml/func/func-info.sgml
index 69ef3857cfa..cfdef22ad5a 100644
--- a/doc/src/sgml/func/func-info.sgml
+++ b/doc/src/sgml/func/func-info.sgml
@@ -3960,6 +3960,25 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
is false, the <literal>OWNER</literal> clause is omitted.
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_get_policy_ddl</primary>
+ </indexterm>
+ <function>pg_get_policy_ddl</function>
+ ( <parameter>table</parameter> <type>regclass</type>,
+ <parameter>policyname</parameter> <type>name</type>
+ <optional>, <parameter>pretty</parameter> <type>boolean</type>
+ </optional> )
+ <returnvalue>setof text</returnvalue>
+ </para>
+ <para>
+ Reconstructs the <command>CREATE POLICY</command> statement for the
+ named row-level security policy on the specified table. The result
+ is returned as a single row. If <parameter>pretty</parameter> is
+ true, the output is formatted for readability; the default is false.
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/utils/adt/ddlutils.c b/src/backend/utils/adt/ddlutils.c
index a70f1c28655..2ada0e25fde 100644
--- a/src/backend/utils/adt/ddlutils.c
+++ b/src/backend/utils/adt/ddlutils.c
@@ -26,6 +26,7 @@
#include "catalog/pg_collation.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_policy.h"
#include "catalog/pg_tablespace.h"
#include "commands/tablespace.h"
#include "common/relpath.h"
@@ -56,6 +57,9 @@ static List *pg_get_tablespace_ddl_internal(Oid tsid, bool pretty, bool no_owner
static Datum pg_get_tablespace_ddl_srf(FunctionCallInfo fcinfo, Oid tsid);
static List *pg_get_database_ddl_internal(Oid dbid, bool pretty,
bool no_owner, bool no_tablespace);
+static List *pg_get_policy_ddl_internal(Oid tableID, const char *policyName,
+ bool pretty);
+static const char *get_policy_cmd_name(char cmd);
/*
@@ -975,3 +979,273 @@ pg_get_database_ddl(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx);
}
}
+
+/*
+ * get_policy_cmd_name
+ * Map a non-ALL pg_policy.polcmd char to its SQL keyword.
+ *
+ * Callers must guard against '*' (ALL) themselves; it is not passed here.
+ */
+static const char *
+get_policy_cmd_name(char cmd)
+{
+ switch (cmd)
+ {
+ case ACL_SELECT_CHR:
+ return "SELECT";
+ case ACL_INSERT_CHR:
+ return "INSERT";
+ case ACL_UPDATE_CHR:
+ return "UPDATE";
+ case ACL_DELETE_CHR:
+ return "DELETE";
+ default:
+ elog(ERROR, "unrecognized policy command: %d", (int) cmd);
+ return NULL; /* keep compiler quiet */
+ }
+}
+
+/*
+ * pg_get_policy_ddl_internal
+ * Generate the DDL statement to recreate a row-level security policy.
+ *
+ * Returns a List containing a single palloc'd string with the CREATE POLICY
+ * statement. Returning a List keeps the calling convention consistent with
+ * the rest of the pg_get_*_ddl family even though only one row is produced.
+ */
+static List *
+pg_get_policy_ddl_internal(Oid tableID, const char *policyName, bool pretty)
+{
+ Relation pgPolicyRel;
+ HeapTuple tuplePolicy;
+ Form_pg_policy policyForm;
+ ScanKeyData skey[2];
+ SysScanDesc sscan;
+ StringInfoData buf;
+ Datum valueDatum;
+ bool attrIsNull;
+ char *relname;
+ char *nspname;
+ char *targetTable;
+ int save_nestlevel;
+ List *statements = NIL;
+
+ /* Validate that the relation exists and build its qualified name. */
+ relname = get_rel_name(tableID);
+ if (relname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation with OID %u does not exist", tableID)));
+
+ nspname = get_namespace_name(get_rel_namespace(tableID));
+ if (nspname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("schema for relation with OID %u does not exist",
+ tableID)));
+
+ targetTable = quote_qualified_identifier(nspname, relname);
+ pfree(relname);
+ pfree(nspname);
+
+ pgPolicyRel = table_open(PolicyRelationId, AccessShareLock);
+
+ /* Set key - policy's relation id. */
+ ScanKeyInit(&skey[0],
+ Anum_pg_policy_polrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(tableID));
+
+ /* Set key - policy's name. */
+ ScanKeyInit(&skey[1],
+ Anum_pg_policy_polname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(policyName));
+
+ sscan = systable_beginscan(pgPolicyRel,
+ PolicyPolrelidPolnameIndexId, true, NULL, 2,
+ skey);
+
+ tuplePolicy = systable_getnext(sscan);
+ if (!HeapTupleIsValid(tuplePolicy))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("policy \"%s\" for table \"%s\" does not exist",
+ policyName, targetTable)));
+
+ policyForm = (Form_pg_policy) GETSTRUCT(tuplePolicy);
+
+ initStringInfo(&buf);
+
+ /* Build the CREATE POLICY statement */
+ appendStringInfo(&buf, "CREATE POLICY %s ON %s",
+ quote_identifier(policyName),
+ targetTable);
+ pfree(targetTable);
+
+ /*
+ * Emit AS RESTRICTIVE only when it differs from the default (PERMISSIVE).
+ */
+ if (!policyForm->polpermissive)
+ append_ddl_option(&buf, pretty, 4, "AS RESTRICTIVE");
+
+ /*
+ * Emit FOR <cmd> only when it differs from the default (ALL, encoded as
+ * '*').
+ */
+ if (policyForm->polcmd != '*')
+ append_ddl_option(&buf, pretty, 4, "FOR %s",
+ get_policy_cmd_name(policyForm->polcmd));
+
+ /*
+ * Emit TO <roles> only when it differs from the default (PUBLIC). PUBLIC
+ * is encoded in polroles as a single InvalidOid element, so we omit the
+ * clause whenever every entry is InvalidOid. polroles is always non-NULL.
+ */
+ valueDatum = heap_getattr(tuplePolicy,
+ Anum_pg_policy_polroles,
+ RelationGetDescr(pgPolicyRel),
+ &attrIsNull);
+ Assert(!attrIsNull);
+ {
+ ArrayType *policy_roles = DatumGetArrayTypeP(valueDatum);
+ int nitems = ARR_DIMS(policy_roles)[0];
+ Oid *roles = (Oid *) ARR_DATA_PTR(policy_roles);
+ StringInfoData role_names;
+
+ initStringInfo(&role_names);
+
+ for (int i = 0; i < nitems; i++)
+ {
+ if (OidIsValid(roles[i]))
+ {
+ char *rolename = GetUserNameFromId(roles[i], false);
+
+ if (role_names.len > 0)
+ appendStringInfoString(&role_names, ", ");
+ appendStringInfoString(&role_names, quote_identifier(rolename));
+ pfree(rolename);
+ }
+ }
+
+ if (role_names.len > 0)
+ append_ddl_option(&buf, pretty, 4, "TO %s", role_names.data);
+
+ pfree(role_names.data);
+ }
+
+ /*
+ * Deparse USING and WITH CHECK expressions under an empty search_path so
+ * that all object references are fully schema-qualified. This makes the
+ * reconstructed DDL portable regardless of the caller's search_path
+ * setting, consistent with how pg_dump handles pg_get_viewdef and similar
+ * functions.
+ */
+ save_nestlevel = NewGUCNestLevel();
+ (void) set_config_option("search_path", "",
+ PGC_USERSET, PGC_S_SESSION,
+ GUC_ACTION_SAVE, true, 0, false);
+
+ /* USING expression */
+ valueDatum = heap_getattr(tuplePolicy,
+ Anum_pg_policy_polqual,
+ RelationGetDescr(pgPolicyRel),
+ &attrIsNull);
+ if (!attrIsNull)
+ {
+ Datum expr;
+
+ expr = DirectFunctionCall3(pg_get_expr_ext,
+ valueDatum,
+ ObjectIdGetDatum(policyForm->polrelid),
+ BoolGetDatum(pretty));
+ append_ddl_option(&buf, pretty, 4, "USING (%s)",
+ TextDatumGetCString(expr));
+ }
+
+ /* WITH CHECK expression */
+ valueDatum = heap_getattr(tuplePolicy,
+ Anum_pg_policy_polwithcheck,
+ RelationGetDescr(pgPolicyRel),
+ &attrIsNull);
+ if (!attrIsNull)
+ {
+ Datum expr;
+
+ expr = DirectFunctionCall3(pg_get_expr_ext,
+ valueDatum,
+ ObjectIdGetDatum(policyForm->polrelid),
+ BoolGetDatum(pretty));
+ append_ddl_option(&buf, pretty, 4, "WITH CHECK (%s)",
+ TextDatumGetCString(expr));
+ }
+
+ AtEOXact_GUC(false, save_nestlevel);
+
+ appendStringInfoChar(&buf, ';');
+
+ statements = lappend(statements, pstrdup(buf.data));
+
+ systable_endscan(sscan);
+ table_close(pgPolicyRel, AccessShareLock);
+ pfree(buf.data);
+
+ return statements;
+}
+
+/*
+ * pg_get_policy_ddl
+ * Return DDL to recreate a row-level security policy as a single text row.
+ */
+Datum
+pg_get_policy_ddl(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ List *statements;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ MemoryContext oldcontext;
+ Oid tableID;
+ Name policyName;
+ bool pretty;
+
+ funcctx = SRF_FIRSTCALL_INIT();
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
+ {
+ MemoryContextSwitchTo(oldcontext);
+ SRF_RETURN_DONE(funcctx);
+ }
+
+ tableID = PG_GETARG_OID(0);
+ policyName = PG_GETARG_NAME(1);
+ pretty = !PG_ARGISNULL(2) && PG_GETARG_BOOL(2);
+
+ statements = pg_get_policy_ddl_internal(tableID,
+ NameStr(*policyName),
+ pretty);
+ funcctx->user_fctx = statements;
+ funcctx->max_calls = list_length(statements);
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ statements = (List *) funcctx->user_fctx;
+
+ if (funcctx->call_cntr < funcctx->max_calls)
+ {
+ char *stmt;
+
+ stmt = list_nth(statements, funcctx->call_cntr);
+
+ SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(stmt));
+ }
+ else
+ {
+ list_free_deep(statements);
+ SRF_RETURN_DONE(funcctx);
+ }
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3cb84359adf..cd029b483f1 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8634,6 +8634,12 @@
proargtypes => 'regdatabase bool bool bool',
proargnames => '{database,pretty,owner,tablespace}',
proargdefaults => '{false,true,true}', prosrc => 'pg_get_database_ddl' },
+{ oid => '9683', descr => 'get DDL to recreate a row-level security policy',
+ proname => 'pg_get_policy_ddl', prorows => '1', proisstrict => 'f',
+ proretset => 't', provolatile => 's', pronargdefaults => '1',
+ prorettype => 'text', proargtypes => 'regclass name bool',
+ proargnames => '{table,policyname,pretty}', proargdefaults => '{false}',
+ prosrc => 'pg_get_policy_ddl' },
{ oid => '2509',
descr => 'deparse an encoded expression with pretty-print option',
proname => 'pg_get_expr', provolatile => 's', prorettype => 'text',
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 3a5e82c35bd..da174eeadbf 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -5195,6 +5195,321 @@ reset rls_test.blah;
drop function rls_f(text);
drop table rls_t, test_t;
--
+-- Test for pg_get_policy_ddl(table, policy_name[, pretty]) function.
+--
+CREATE TABLE rls_tbl_1 (
+ did int primary key,
+ cid int,
+ dlevel int not null,
+ dauthor name,
+ dtitle text
+);
+GRANT ALL ON rls_tbl_1 TO public;
+CREATE TABLE rls_tbl_2 (
+ pguser name primary key,
+ seclv int
+);
+GRANT SELECT ON rls_tbl_2 TO public;
+-- Test PERMISSIVE and RESTRICTIVE
+CREATE POLICY rls_p1 ON rls_tbl_1 AS PERMISSIVE
+ USING (dlevel <= (SELECT seclv FROM rls_tbl_2 WHERE pguser = current_user));
+CREATE POLICY rls_p2 ON rls_tbl_1 AS RESTRICTIVE USING (cid <> 44 AND cid < 50);
+-- Test FOR ALL | SELECT | INSERT | UPDATE | DELETE
+CREATE POLICY rls_p3 ON rls_tbl_1 FOR ALL USING (dauthor = current_user);
+CREATE POLICY rls_p4 ON rls_tbl_1 FOR SELECT USING (cid % 2 = 0);
+CREATE POLICY rls_p5 ON rls_tbl_1 FOR INSERT WITH CHECK (cid % 2 = 1);
+CREATE POLICY rls_p6 ON rls_tbl_1 FOR UPDATE USING (cid % 2 = 0);
+CREATE POLICY rls_p7 ON rls_tbl_1 FOR DELETE USING (cid < 8);
+-- Test TO { role_name ... }
+CREATE POLICY rls_p8 ON rls_tbl_1 TO regress_rls_dave, regress_rls_alice USING (true);
+CREATE POLICY rls_p9 ON rls_tbl_1 TO regress_rls_exempt_user WITH CHECK (cid = (SELECT seclv FROM rls_tbl_2));
+-- Test UPDATE policy with both USING and WITH CHECK
+CREATE POLICY rls_p10 ON rls_tbl_1 FOR UPDATE
+ USING (dlevel > 0) WITH CHECK (dlevel < 100);
+-- Test ALL policy with both USING and WITH CHECK (AS PERMISSIVE and FOR ALL are defaults, omitted)
+CREATE POLICY rls_p11 ON rls_tbl_1
+ USING (dlevel >= 1) WITH CHECK (dlevel <= 99);
+-- Test RESTRICTIVE on a specific command
+CREATE POLICY rls_p12 ON rls_tbl_1 AS RESTRICTIVE FOR SELECT
+ USING (cid > 0);
+-- NULL inputs should return no rows
+SELECT count(*) FROM pg_get_policy_ddl(NULL, 'rls_p1');
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pg_get_policy_ddl('rls_tbl_1', NULL);
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM pg_get_policy_ddl(NULL, NULL);
+ count
+-------
+ 0
+(1 row)
+
+-- Table does not exist
+SELECT * FROM pg_get_policy_ddl('nonexistent_tbl', 'rls_p1');
+ERROR: relation "nonexistent_tbl" does not exist
+LINE 1: SELECT * FROM pg_get_policy_ddl('nonexistent_tbl', 'rls_p1')...
+ ^
+-- Policy does not exist
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'nonexistent_pol');
+ERROR: policy "nonexistent_pol" for table "regress_rls_schema.rls_tbl_1" does not exist
+-- Without pretty formatting (default); also verify explicit false equals default
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p1');
+ pg_get_policy_ddl
+-------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p1 ON regress_rls_schema.rls_tbl_1 USING ((dlevel <= ( SELECT rls_tbl_2.seclv+
+ FROM regress_rls_schema.rls_tbl_2 +
+ WHERE (rls_tbl_2.pguser = CURRENT_USER))));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p2');
+ pg_get_policy_ddl
+-----------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p2 ON regress_rls_schema.rls_tbl_1 AS RESTRICTIVE USING (((cid <> 44) AND (cid < 50)));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3');
+ pg_get_policy_ddl
+----------------------------------------------------------------------------------------
+ CREATE POLICY rls_p3 ON regress_rls_schema.rls_tbl_1 USING ((dauthor = CURRENT_USER));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p4');
+ pg_get_policy_ddl
+------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p4 ON regress_rls_schema.rls_tbl_1 FOR SELECT USING (((cid % 2) = 0));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p5');
+ pg_get_policy_ddl
+-----------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p5 ON regress_rls_schema.rls_tbl_1 FOR INSERT WITH CHECK (((cid % 2) = 1));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p6');
+ pg_get_policy_ddl
+------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p6 ON regress_rls_schema.rls_tbl_1 FOR UPDATE USING (((cid % 2) = 0));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p7');
+ pg_get_policy_ddl
+------------------------------------------------------------------------------------
+ CREATE POLICY rls_p7 ON regress_rls_schema.rls_tbl_1 FOR DELETE USING ((cid < 8));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p8');
+ pg_get_policy_ddl
+-----------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p8 ON regress_rls_schema.rls_tbl_1 TO regress_rls_dave, regress_rls_alice USING (true);
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p9');
+ pg_get_policy_ddl
+-----------------------------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p9 ON regress_rls_schema.rls_tbl_1 TO regress_rls_exempt_user WITH CHECK ((cid = ( SELECT rls_tbl_2.seclv+
+ FROM regress_rls_schema.rls_tbl_2)));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10');
+ pg_get_policy_ddl
+--------------------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p10 ON regress_rls_schema.rls_tbl_1 FOR UPDATE USING ((dlevel > 0)) WITH CHECK ((dlevel < 100));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p11');
+ pg_get_policy_ddl
+----------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p11 ON regress_rls_schema.rls_tbl_1 USING ((dlevel >= 1)) WITH CHECK ((dlevel <= 99));
+(1 row)
+
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p12');
+ pg_get_policy_ddl
+----------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p12 ON regress_rls_schema.rls_tbl_1 AS RESTRICTIVE FOR SELECT USING ((cid > 0));
+(1 row)
+
+-- Explicit false must match omitting the argument
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10', false);
+ pg_get_policy_ddl
+--------------------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p10 ON regress_rls_schema.rls_tbl_1 FOR UPDATE USING ((dlevel > 0)) WITH CHECK ((dlevel < 100));
+(1 row)
+
+-- NULL pretty must also default to non-pretty
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10', NULL);
+ pg_get_policy_ddl
+--------------------------------------------------------------------------------------------------------------------
+ CREATE POLICY rls_p10 ON regress_rls_schema.rls_tbl_1 FOR UPDATE USING ((dlevel > 0)) WITH CHECK ((dlevel < 100));
+(1 row)
+
+-- pretty accepts all standard boolean representations: true/false, on/off, 1/0
+\pset format unaligned
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', 'on'::bool);
+pg_get_policy_ddl
+CREATE POLICY rls_p3 ON regress_rls_schema.rls_tbl_1
+ USING (dauthor = CURRENT_USER);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', '1'::bool);
+pg_get_policy_ddl
+CREATE POLICY rls_p3 ON regress_rls_schema.rls_tbl_1
+ USING (dauthor = CURRENT_USER);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', 'off'::bool);
+pg_get_policy_ddl
+CREATE POLICY rls_p3 ON regress_rls_schema.rls_tbl_1 USING ((dauthor = CURRENT_USER));
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', '0'::bool);
+pg_get_policy_ddl
+CREATE POLICY rls_p3 ON regress_rls_schema.rls_tbl_1 USING ((dauthor = CURRENT_USER));
+(1 row)
+\pset format aligned
+-- With pretty formatting
+\pset format unaligned
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p1', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p1 ON regress_rls_schema.rls_tbl_1
+ USING (dlevel <= (( SELECT rls_tbl_2.seclv
+ FROM regress_rls_schema.rls_tbl_2
+ WHERE rls_tbl_2.pguser = CURRENT_USER)));
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p2', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p2 ON regress_rls_schema.rls_tbl_1
+ AS RESTRICTIVE
+ USING (cid <> 44 AND cid < 50);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p3 ON regress_rls_schema.rls_tbl_1
+ USING (dauthor = CURRENT_USER);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p4', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p4 ON regress_rls_schema.rls_tbl_1
+ FOR SELECT
+ USING ((cid % 2) = 0);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p5', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p5 ON regress_rls_schema.rls_tbl_1
+ FOR INSERT
+ WITH CHECK ((cid % 2) = 1);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p6', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p6 ON regress_rls_schema.rls_tbl_1
+ FOR UPDATE
+ USING ((cid % 2) = 0);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p7', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p7 ON regress_rls_schema.rls_tbl_1
+ FOR DELETE
+ USING (cid < 8);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p8', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p8 ON regress_rls_schema.rls_tbl_1
+ TO regress_rls_dave, regress_rls_alice
+ USING (true);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p9', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p9 ON regress_rls_schema.rls_tbl_1
+ TO regress_rls_exempt_user
+ WITH CHECK (cid = (( SELECT rls_tbl_2.seclv
+ FROM regress_rls_schema.rls_tbl_2)));
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p10 ON regress_rls_schema.rls_tbl_1
+ FOR UPDATE
+ USING (dlevel > 0)
+ WITH CHECK (dlevel < 100);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p11', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p11 ON regress_rls_schema.rls_tbl_1
+ USING (dlevel >= 1)
+ WITH CHECK (dlevel <= 99);
+(1 row)
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p12', true);
+pg_get_policy_ddl
+CREATE POLICY rls_p12 ON regress_rls_schema.rls_tbl_1
+ AS RESTRICTIVE
+ FOR SELECT
+ USING (cid > 0);
+(1 row)
+\pset format aligned
+-- Round-trip: the generated DDL must be re-executable, including for atomic
+-- boolean expressions that pg_get_expr() does not parenthesize.
+CREATE TABLE rls_rt (a int);
+CREATE POLICY rt_true ON rls_rt USING (true);
+CREATE POLICY rt_false ON rls_rt FOR INSERT WITH CHECK (false);
+CREATE TEMP TABLE rt_ddl AS
+ SELECT pg_get_policy_ddl('rls_rt', 'rt_true') AS ddl
+ UNION ALL
+ SELECT pg_get_policy_ddl('rls_rt', 'rt_false');
+DROP POLICY rt_true ON rls_rt;
+DROP POLICY rt_false ON rls_rt;
+SELECT ddl FROM rt_ddl ORDER BY ddl \gexec
+CREATE POLICY rt_false ON regress_rls_schema.rls_rt FOR INSERT WITH CHECK (false);
+CREATE POLICY rt_true ON regress_rls_schema.rls_rt USING (true);
+SELECT polname FROM pg_policy WHERE polrelid = 'rls_rt'::regclass ORDER BY polname;
+ polname
+----------
+ rt_false
+ rt_true
+(2 rows)
+
+DROP TABLE rls_rt;
+-- Schema-qualification: function references in expressions must be
+-- fully qualified regardless of the caller's search_path.
+CREATE SCHEMA rls_s1;
+CREATE SCHEMA rls_s2;
+CREATE FUNCTION rls_s1.rls_f(int) RETURNS bool LANGUAGE sql AS 'SELECT $1 > 0';
+CREATE FUNCTION rls_s2.rls_f(int) RETURNS bool LANGUAGE sql AS 'SELECT $1 < 0';
+CREATE TABLE rls_tbl_3 (a int);
+CREATE POLICY rls_pf ON rls_tbl_3 USING (rls_s1.rls_f(a));
+-- With rls_s1 in path, rls_f should still appear schema-qualified in DDL.
+SET search_path = regress_rls_schema, rls_s1;
+\pset format unaligned
+SELECT * FROM pg_get_policy_ddl('rls_tbl_3', 'rls_pf');
+pg_get_policy_ddl
+CREATE POLICY rls_pf ON regress_rls_schema.rls_tbl_3 USING (rls_s1.rls_f(a));
+(1 row)
+\pset format aligned
+-- Restore the test's search_path before cleanup.
+SET search_path = regress_rls_schema;
+DROP POLICY rls_pf ON rls_tbl_3;
+DROP TABLE rls_tbl_3;
+DROP FUNCTION rls_s1.rls_f(int);
+DROP FUNCTION rls_s2.rls_f(int);
+DROP SCHEMA rls_s1;
+DROP SCHEMA rls_s2;
+-- Clean up objects created for testing pg_get_policy_ddl function.
+DROP POLICY rls_p1 ON rls_tbl_1;
+DROP POLICY rls_p2 ON rls_tbl_1;
+DROP POLICY rls_p3 ON rls_tbl_1;
+DROP POLICY rls_p4 ON rls_tbl_1;
+DROP POLICY rls_p5 ON rls_tbl_1;
+DROP POLICY rls_p6 ON rls_tbl_1;
+DROP POLICY rls_p7 ON rls_tbl_1;
+DROP POLICY rls_p8 ON rls_tbl_1;
+DROP POLICY rls_p9 ON rls_tbl_1;
+DROP POLICY rls_p10 ON rls_tbl_1;
+DROP POLICY rls_p11 ON rls_tbl_1;
+DROP POLICY rls_p12 ON rls_tbl_1;
+DROP TABLE rls_tbl_1;
+DROP TABLE rls_tbl_2;
+--
-- Clean up objects
--
RESET SESSION AUTHORIZATION;
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index 6b3566271df..cdc50d74cde 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -2598,6 +2598,156 @@ reset rls_test.blah;
drop function rls_f(text);
drop table rls_t, test_t;
+--
+-- Test for pg_get_policy_ddl(table, policy_name[, pretty]) function.
+--
+CREATE TABLE rls_tbl_1 (
+ did int primary key,
+ cid int,
+ dlevel int not null,
+ dauthor name,
+ dtitle text
+);
+GRANT ALL ON rls_tbl_1 TO public;
+CREATE TABLE rls_tbl_2 (
+ pguser name primary key,
+ seclv int
+);
+GRANT SELECT ON rls_tbl_2 TO public;
+
+-- Test PERMISSIVE and RESTRICTIVE
+CREATE POLICY rls_p1 ON rls_tbl_1 AS PERMISSIVE
+ USING (dlevel <= (SELECT seclv FROM rls_tbl_2 WHERE pguser = current_user));
+CREATE POLICY rls_p2 ON rls_tbl_1 AS RESTRICTIVE USING (cid <> 44 AND cid < 50);
+
+-- Test FOR ALL | SELECT | INSERT | UPDATE | DELETE
+CREATE POLICY rls_p3 ON rls_tbl_1 FOR ALL USING (dauthor = current_user);
+CREATE POLICY rls_p4 ON rls_tbl_1 FOR SELECT USING (cid % 2 = 0);
+CREATE POLICY rls_p5 ON rls_tbl_1 FOR INSERT WITH CHECK (cid % 2 = 1);
+CREATE POLICY rls_p6 ON rls_tbl_1 FOR UPDATE USING (cid % 2 = 0);
+CREATE POLICY rls_p7 ON rls_tbl_1 FOR DELETE USING (cid < 8);
+
+-- Test TO { role_name ... }
+CREATE POLICY rls_p8 ON rls_tbl_1 TO regress_rls_dave, regress_rls_alice USING (true);
+CREATE POLICY rls_p9 ON rls_tbl_1 TO regress_rls_exempt_user WITH CHECK (cid = (SELECT seclv FROM rls_tbl_2));
+
+-- Test UPDATE policy with both USING and WITH CHECK
+CREATE POLICY rls_p10 ON rls_tbl_1 FOR UPDATE
+ USING (dlevel > 0) WITH CHECK (dlevel < 100);
+
+-- Test ALL policy with both USING and WITH CHECK (AS PERMISSIVE and FOR ALL are defaults, omitted)
+CREATE POLICY rls_p11 ON rls_tbl_1
+ USING (dlevel >= 1) WITH CHECK (dlevel <= 99);
+
+-- Test RESTRICTIVE on a specific command
+CREATE POLICY rls_p12 ON rls_tbl_1 AS RESTRICTIVE FOR SELECT
+ USING (cid > 0);
+
+-- NULL inputs should return no rows
+SELECT count(*) FROM pg_get_policy_ddl(NULL, 'rls_p1');
+SELECT count(*) FROM pg_get_policy_ddl('rls_tbl_1', NULL);
+SELECT count(*) FROM pg_get_policy_ddl(NULL, NULL);
+
+-- Table does not exist
+SELECT * FROM pg_get_policy_ddl('nonexistent_tbl', 'rls_p1');
+-- Policy does not exist
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'nonexistent_pol');
+
+-- Without pretty formatting (default); also verify explicit false equals default
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p1');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p2');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p4');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p5');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p6');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p7');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p8');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p9');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p11');
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p12');
+-- Explicit false must match omitting the argument
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10', false);
+-- NULL pretty must also default to non-pretty
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10', NULL);
+
+-- pretty accepts all standard boolean representations: true/false, on/off, 1/0
+\pset format unaligned
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', 'on'::bool);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', '1'::bool);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', 'off'::bool);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', '0'::bool);
+\pset format aligned
+
+-- With pretty formatting
+\pset format unaligned
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p1', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p2', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p3', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p4', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p5', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p6', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p7', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p8', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p9', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p10', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p11', true);
+SELECT * FROM pg_get_policy_ddl('rls_tbl_1', 'rls_p12', true);
+\pset format aligned
+
+-- Round-trip: the generated DDL must be re-executable, including for atomic
+-- boolean expressions that pg_get_expr() does not parenthesize.
+CREATE TABLE rls_rt (a int);
+CREATE POLICY rt_true ON rls_rt USING (true);
+CREATE POLICY rt_false ON rls_rt FOR INSERT WITH CHECK (false);
+CREATE TEMP TABLE rt_ddl AS
+ SELECT pg_get_policy_ddl('rls_rt', 'rt_true') AS ddl
+ UNION ALL
+ SELECT pg_get_policy_ddl('rls_rt', 'rt_false');
+DROP POLICY rt_true ON rls_rt;
+DROP POLICY rt_false ON rls_rt;
+SELECT ddl FROM rt_ddl ORDER BY ddl \gexec
+SELECT polname FROM pg_policy WHERE polrelid = 'rls_rt'::regclass ORDER BY polname;
+DROP TABLE rls_rt;
+
+-- Schema-qualification: function references in expressions must be
+-- fully qualified regardless of the caller's search_path.
+CREATE SCHEMA rls_s1;
+CREATE SCHEMA rls_s2;
+CREATE FUNCTION rls_s1.rls_f(int) RETURNS bool LANGUAGE sql AS 'SELECT $1 > 0';
+CREATE FUNCTION rls_s2.rls_f(int) RETURNS bool LANGUAGE sql AS 'SELECT $1 < 0';
+CREATE TABLE rls_tbl_3 (a int);
+CREATE POLICY rls_pf ON rls_tbl_3 USING (rls_s1.rls_f(a));
+-- With rls_s1 in path, rls_f should still appear schema-qualified in DDL.
+SET search_path = regress_rls_schema, rls_s1;
+\pset format unaligned
+SELECT * FROM pg_get_policy_ddl('rls_tbl_3', 'rls_pf');
+\pset format aligned
+-- Restore the test's search_path before cleanup.
+SET search_path = regress_rls_schema;
+DROP POLICY rls_pf ON rls_tbl_3;
+DROP TABLE rls_tbl_3;
+DROP FUNCTION rls_s1.rls_f(int);
+DROP FUNCTION rls_s2.rls_f(int);
+DROP SCHEMA rls_s1;
+DROP SCHEMA rls_s2;
+
+-- Clean up objects created for testing pg_get_policy_ddl function.
+DROP POLICY rls_p1 ON rls_tbl_1;
+DROP POLICY rls_p2 ON rls_tbl_1;
+DROP POLICY rls_p3 ON rls_tbl_1;
+DROP POLICY rls_p4 ON rls_tbl_1;
+DROP POLICY rls_p5 ON rls_tbl_1;
+DROP POLICY rls_p6 ON rls_tbl_1;
+DROP POLICY rls_p7 ON rls_tbl_1;
+DROP POLICY rls_p8 ON rls_tbl_1;
+DROP POLICY rls_p9 ON rls_tbl_1;
+DROP POLICY rls_p10 ON rls_tbl_1;
+DROP POLICY rls_p11 ON rls_tbl_1;
+DROP POLICY rls_p12 ON rls_tbl_1;
+DROP TABLE rls_tbl_1;
+DROP TABLE rls_tbl_2;
+
--
-- Clean up objects
--
--
2.51.0
view thread (44+ messages)
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: [PATCH] Add pg_get_policy_ddl() function to reconstruct CREATE POLICY statement
In-Reply-To: <CANxoLDd4uL=EU+4cGVWp1F9oqVaye4uybUkJhhS7QfeHaqeDXQ@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox