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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions doc/src/sgml/func/func-info.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -3797,4 +3797,59 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}

</sect2>

<sect2 id="functions-get-object-ddl">
<title>Get Object DDL Functions</title>

<para>
The functions described in <xref linkend="functions-get-object-ddl-table"/>
return the Data Definition Language (DDL) statement for any given database object.
This feature is implemented as a set of distinct functions for each object type.
</para>

<table id="functions-get-object-ddl-table">
<title>Get Object DDL Functions</title>
<tgroup cols="1">
<thead>
<row>
<entry role="func_table_entry"><para role="func_signature">
Function
</para>
<para>
Description
</para></entry>
</row>
</thead>

<tbody>
<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>policy_name</parameter> <type>name</type>, <optional> <parameter>pretty</parameter> <type>boolean</type> </optional> )
<returnvalue>text</returnvalue>
</para>
<para>
Reconstructs the <command>CREATE POLICY</command> statement from the
system catalogs for a specified table and policy name. The result is a
comprehensive <command>CREATE POLICY</command> statement.
</para></entry>
</row>
</tbody>
</tgroup>
</table>

<para>
Most of the functions that reconstruct (decompile) database objects have an
optional <parameter>pretty</parameter> flag, which if
<literal>true</literal> causes the result to be
<quote>pretty-printed</quote>. Pretty-printing adds tab character and new
line character for legibility. Passing <literal>false</literal> for the
<parameter>pretty</parameter> parameter yields the same result as omitting
the parameter.
</para>

</sect2>

</sect1>
6 changes: 6 additions & 0 deletions src/backend/catalog/system_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@ LANGUAGE INTERNAL
STRICT VOLATILE PARALLEL UNSAFE
AS 'pg_replication_origin_session_setup';

CREATE OR REPLACE FUNCTION
pg_get_policy_ddl(tableID regclass, policyName name, pretty bool DEFAULT false)
RETURNS text
LANGUAGE INTERNAL
AS 'pg_get_policy_ddl';

--
-- The default permissions for functions mean that anyone can execute them.
-- A number of functions shouldn't be executable by just anyone, but rather
Expand Down
27 changes: 27 additions & 0 deletions src/backend/commands/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,33 @@ parse_policy_command(const char *cmd_name)
return polcmd;
}

/*
* get_policy_cmd_name -
* helper function to convert char representation to full command strings.
*
* cmd - Valid values are '*', 'r', 'a', 'w' and 'd'.
*
*/
char *
get_policy_cmd_name(char cmd)
{
switch (cmd)
{
case '*':
return "ALL";
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");
}
}

/*
* policy_role_list_to_array
* helper function to convert a list of RoleSpecs to an array of
Expand Down
212 changes: 212 additions & 0 deletions src/backend/utils/adt/ruleutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "commands/policy.h"
#include "common/keywords.h"
#include "executor/spi.h"
#include "funcapi.h"
Expand Down Expand Up @@ -94,6 +96,10 @@
((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
: PRETTYFLAG_INDENT)

#define GET_DDL_PRETTY_FLAGS(pretty) \
((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
: 0)

/* Default line length for pretty-print wrapping: 0 means wrap always */
#define WRAP_COLUMN_DEFAULT 0

Expand Down Expand Up @@ -546,6 +552,12 @@ static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
deparse_context *context,
bool showimplicit,
bool needcomma);
static void get_formatted_string(StringInfo buf,
int prettyFlags,
int nTabChars,
const char *fmt,...) pg_attribute_printf(4, 5);
static char *pg_get_policy_ddl_worker(Oid tableID, Name policyName,
int prettyFlags);

#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")

Expand Down Expand Up @@ -13743,3 +13755,203 @@ get_range_partbound_string(List *bound_datums)

return buf.data;
}

/*
* get_formatted_string
*
* Return a formatted version of the string.
*
* prettyFlags - Based on prettyFlags the output includes tabs (\t) and
* newlines (\n).
* nTabChars - indent with specified no of tabs.
* fmt - printf-style format string used by appendStringInfoVA.
*/
static void
get_formatted_string(StringInfo buf, int prettyFlags, int nTabChars, const char *fmt,...)
{
va_list args;

if (prettyFlags & PRETTYFLAG_INDENT)
{
appendStringInfoChar(buf, '\n');
/* Indent with tabs */
for (int i = 0; i < nTabChars; i++)
{
appendStringInfoChar(buf, '\t');
}
}
else
appendStringInfoChar(buf, ' ');

va_start(args, fmt);
appendStringInfoVA(buf, fmt, args);
va_end(args);
}

/*
* pg_get_policy_ddl
*
* Generate a CREATE POLICY statement for the specified policy.
*
* tableID - Table ID of the policy.
* policyName - Name of the policy for which to generate the DDL.
* pretty - If true, format the DDL with indentation and line breaks.
*/
Datum
pg_get_policy_ddl(PG_FUNCTION_ARGS)
{
Oid tableID = PG_GETARG_OID(0);
Name policyName = PG_GETARG_NAME(1);
bool pretty = PG_GETARG_BOOL(2);
int prettyFlags;
char *res;

prettyFlags = GET_DDL_PRETTY_FLAGS(pretty);
res = pg_get_policy_ddl_worker(tableID, policyName, prettyFlags);

if (res == NULL)
PG_RETURN_NULL();

PG_RETURN_TEXT_P(string_to_text(res));
}

static char *
pg_get_policy_ddl_worker(Oid tableID, Name policyName, int prettyFlags)
{
bool attrIsNull;
Datum valueDatum;
HeapTuple tuplePolicy;
Relation pgPolicyRel;
char *targetTable;
ScanKeyData skey[2];
SysScanDesc sscan;
Form_pg_policy policyForm;
StringInfoData buf;

/* Validate that the relation exists */
if (!OidIsValid(tableID) || get_rel_name(tableID) == NULL)
return NULL;

initStringInfo(&buf);

targetTable = generate_qualified_relation_name(tableID);
/* Find policy to begin scan */
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(NameStr(*policyName)));

sscan = systable_beginscan(pgPolicyRel,
PolicyPolrelidPolnameIndexId, true, NULL, 2,
skey);

tuplePolicy = systable_getnext(sscan);
/* Check that the policy is found, raise an error if not. */
if (!HeapTupleIsValid(tuplePolicy))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("policy \"%s\" for table \"%s\" does not exist",
NameStr(*policyName),
targetTable)));

policyForm = (Form_pg_policy) GETSTRUCT(tuplePolicy);

/* Build the CREATE POLICY statement */
appendStringInfo(&buf, "CREATE POLICY %s ON %s",
quote_identifier(NameStr(*policyName)),
targetTable);

/* Check the type is PERMISSIVE or RESTRICTIVE */
get_formatted_string(&buf, prettyFlags, 1,
policyForm->polpermissive ? "AS PERMISSIVE" : "AS RESTRICTIVE");

/* Check command to which the policy applies */
get_formatted_string(&buf, prettyFlags, 1, "FOR %s",
get_policy_cmd_name(policyForm->polcmd));

/* Check if the policy has a TO list */
valueDatum = heap_getattr(tuplePolicy,
Anum_pg_policy_polroles,
RelationGetDescr(pgPolicyRel),
&attrIsNull);
if (!attrIsNull)
{
ArrayType *policy_roles = DatumGetArrayTypePCopy(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 (i > 0)
appendStringInfoString(&role_names, ", ");
appendStringInfoString(&role_names, rolename);
}
}

if (role_names.len > 0)
get_formatted_string(&buf, prettyFlags, 1, "TO %s", role_names.data);
else

/*
* When no specific role is provided, generate the TO clause with
* the PUBLIC role.
*/
get_formatted_string(&buf, prettyFlags, 1, "TO PUBLIC");
}

/* Check if the policy has a USING expr */
valueDatum = heap_getattr(tuplePolicy,
Anum_pg_policy_polqual,
RelationGetDescr(pgPolicyRel),
&attrIsNull);
if (!attrIsNull)
{
text *exprtext = DatumGetTextPP(valueDatum);
text *usingExpression = pg_get_expr_worker(exprtext,
policyForm->polrelid,
prettyFlags);

get_formatted_string(&buf, prettyFlags, 1, "USING (%s)",
text_to_cstring(usingExpression));
}

/* Check if the policy has a WITH CHECK expr */
valueDatum = heap_getattr(tuplePolicy,
Anum_pg_policy_polwithcheck,
RelationGetDescr(pgPolicyRel),
&attrIsNull);
if (!attrIsNull)
{
text *exprtext = DatumGetTextPP(valueDatum);
text *checkExpression = pg_get_expr_worker(exprtext,
policyForm->polrelid,
prettyFlags);

get_formatted_string(&buf, prettyFlags, 1, "WITH CHECK (%s)",
text_to_cstring(checkExpression));
}

appendStringInfoChar(&buf, ';');

/* Clean up. */
systable_endscan(sscan);
table_close(pgPolicyRel, AccessShareLock);

return buf.data;
}
3 changes: 3 additions & 0 deletions src/include/catalog/pg_proc.dat
Original file line number Diff line number Diff line change
Expand Up @@ -4021,6 +4021,9 @@
proname => 'pg_get_function_sqlbody', provolatile => 's',
prorettype => 'text', proargtypes => 'oid',
prosrc => 'pg_get_function_sqlbody' },
{ oid => '8811', descr => 'get CREATE statement for policy',
proname => 'pg_get_policy_ddl', prorettype => 'text',
proargtypes => 'regclass name bool', prosrc => 'pg_get_policy_ddl' },

{ oid => '1686', descr => 'list of SQL keywords',
proname => 'pg_get_keywords', procost => '10', prorows => '500',
Expand Down
2 changes: 2 additions & 0 deletions src/include/commands/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ extern ObjectAddress rename_policy(RenameStmt *stmt);

extern bool relation_has_policies(Relation rel);

extern char *get_policy_cmd_name(char cmd);

#endif /* POLICY_H */
Loading