summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/pg_dump/common.c4
-rw-r--r--src/bin/pg_dump/pg_backup.h1
-rw-r--r--src/bin/pg_dump/pg_backup_archiver.c9
-rw-r--r--src/bin/pg_dump/pg_dump.c308
-rw-r--r--src/bin/pg_dump/pg_dump.h22
-rw-r--r--src/bin/pg_dump/pg_dump_sort.c11
-rw-r--r--src/bin/pg_dump/pg_dumpall.c23
-rw-r--r--src/bin/pg_dump/pg_restore.c4
-rw-r--r--src/bin/psql/describe.c150
-rw-r--r--src/bin/psql/tab-complete.c158
10 files changed, 664 insertions, 26 deletions
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 94e9147b13..2f855cf706 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -244,6 +244,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
write_msg(NULL, "reading rewrite rules\n");
getRules(fout, &numRules);
+ if (g_verbose)
+ write_msg(NULL, "reading row-security policies\n");
+ getRowSecurity(fout, tblinfo, numTables);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 25780cfc1a..921bc1ba36 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -150,6 +150,7 @@ typedef struct _restoreOptions
bool single_txn;
bool *idWanted; /* array showing which dump IDs to emit */
+ int enable_row_security;
} RestoreOptions;
typedef void (*SetupWorkerPtr) (Archive *AH, RestoreOptions *ropt);
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index ded9135c36..5476a1e7e2 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -374,6 +374,14 @@ RestoreArchive(Archive *AHX)
}
/*
+ * Enable row-security if necessary.
+ */
+ if (!ropt->enable_row_security)
+ ahprintf(AH, "SET row_security = off;\n");
+ else
+ ahprintf(AH, "SET row_security = on;\n");
+
+ /*
* Establish important parameter values right away.
*/
_doSetFixedOutputState(AH);
@@ -3242,6 +3250,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
strcmp(te->desc, "INDEX") == 0 ||
strcmp(te->desc, "RULE") == 0 ||
strcmp(te->desc, "TRIGGER") == 0 ||
+ strcmp(te->desc, "ROW SECURITY") == 0 ||
strcmp(te->desc, "USER MAPPING") == 0)
{
/* these object types don't have separate owners */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c084ee9d9e..29153294e2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -137,6 +137,7 @@ static int no_security_labels = 0;
static int no_synchronized_snapshots = 0;
static int no_unlogged_table_data = 0;
static int serializable_deferrable = 0;
+static int enable_row_security = 0;
static void help(const char *progname);
@@ -247,6 +248,7 @@ static char *myFormatType(const char *typname, int32 typmod);
static void getBlobs(Archive *fout);
static void dumpBlob(Archive *fout, BlobInfo *binfo);
static int dumpBlobs(Archive *fout, void *arg);
+static void dumpRowSecurity(Archive *fout, RowSecurityInfo *rsinfo);
static void dumpDatabase(Archive *AH);
static void dumpEncoding(Archive *AH);
static void dumpStdStrings(Archive *AH);
@@ -345,6 +347,7 @@ main(int argc, char **argv)
{"column-inserts", no_argument, &column_inserts, 1},
{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
{"disable-triggers", no_argument, &disable_triggers, 1},
+ {"enable-row-security", no_argument, &enable_row_security, 1},
{"exclude-table-data", required_argument, NULL, 4},
{"if-exists", no_argument, &if_exists, 1},
{"inserts", no_argument, &dump_inserts, 1},
@@ -825,6 +828,7 @@ main(int argc, char **argv)
ropt->noTablespace = outputNoTablespaces;
ropt->disable_triggers = disable_triggers;
ropt->use_setsessauth = use_setsessauth;
+ ropt->enable_row_security = enable_row_security;
if (compressLevel == -1)
ropt->compression = 0;
@@ -897,6 +901,7 @@ help(const char *progname)
printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
printf(_(" --disable-triggers disable triggers during data-only restore\n"));
+ printf(_(" --enable-row-security enable row level security\n"));
printf(_(" --exclude-table-data=TABLE do NOT dump data for the named table(s)\n"));
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
@@ -1050,6 +1055,14 @@ setup_connection(Archive *AH, const char *dumpencoding, char *use_role)
else
AH->sync_snapshot_id = get_synchronized_snapshot(AH);
}
+
+ if (AH->remoteVersion >= 90500)
+ {
+ if (enable_row_security)
+ ExecuteSqlStatement(AH, "SET row_security TO ON");
+ else
+ ExecuteSqlStatement(AH, "SET row_security TO OFF");
+ }
}
static void
@@ -2757,6 +2770,240 @@ dumpBlobs(Archive *fout, void *arg)
return 1;
}
+/*
+ * getRowSecurity
+ * get information about every row-security policy on a dumpable table.
+ */
+void
+getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
+{
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ RowSecurityInfo *rsinfo;
+ int i_oid;
+ int i_tableoid;
+ int i_rsecpolname;
+ int i_rseccmd;
+ int i_rsecroles;
+ int i_rsecqual;
+ int i_rsecwithcheck;
+ int i, j, ntups;
+
+ if (fout->remoteVersion < 90500)
+ return;
+
+ for (i = 0; i < numTables; i++)
+ {
+ TableInfo *tbinfo = &tblinfo[i];
+
+ /* Ignore row-security on tables not to be dumped */
+ if (!tbinfo->dobj.dump)
+ continue;
+
+ if (g_verbose)
+ write_msg(NULL, "reading row-security enabled for table \"%s\"",
+ tbinfo->dobj.name);
+
+ /*
+ * Get row-security enabled information for the table.
+ * We represent RLS enabled on a table by creating RowSecurityInfo
+ * object with an empty policy.
+ */
+ if (tbinfo->hasrowsec)
+ {
+ /*
+ * Note: use tableoid 0 so that this object won't be mistaken for
+ * something that pg_depend entries apply to.
+ */
+ rsinfo = pg_malloc(sizeof(RowSecurityInfo));
+ rsinfo->dobj.objType = DO_ROW_SECURITY;
+ rsinfo->dobj.catId.tableoid = 0;
+ rsinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
+ AssignDumpId(&rsinfo->dobj);
+ rsinfo->dobj.namespace = tbinfo->dobj.namespace;
+ rsinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
+ rsinfo->rstable = tbinfo;
+ rsinfo->rsecpolname = NULL;
+ rsinfo->rseccmd = NULL;
+ rsinfo->rsecroles = NULL;
+ rsinfo->rsecqual = NULL;
+ rsinfo->rsecwithcheck = NULL;
+ }
+
+ if (g_verbose)
+ write_msg(NULL, "reading row-security policies for table \"%s\"\n",
+ tbinfo->dobj.name);
+
+ /*
+ * select table schema to ensure regproc name is qualified if needed
+ */
+ selectSourceSchema(fout, tbinfo->dobj.namespace->dobj.name);
+
+ resetPQExpBuffer(query);
+
+ /* Get the policies for the table. */
+ appendPQExpBuffer(query,
+ "SELECT oid, tableoid, s.rsecpolname, s.rseccmd, "
+ "CASE WHEN s.rsecroles = '{0}' THEN 'PUBLIC' ELSE "
+ " array_to_string(ARRAY(SELECT rolname from pg_roles WHERE oid = ANY(s.rsecroles)), ', ') END AS rsecroles, "
+ "pg_get_expr(s.rsecqual, s.rsecrelid) AS rsecqual, "
+ "pg_get_expr(s.rsecwithcheck, s.rsecrelid) AS rsecwithcheck "
+ "FROM pg_catalog.pg_rowsecurity s "
+ "WHERE rsecrelid = '%u'",
+ tbinfo->dobj.catId.oid);
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ if (ntups == 0)
+ {
+ /*
+ * No explicit policies to handle (only the default-deny policy,
+ * which is handled as part of the table definition. Clean up and
+ * return.
+ */
+ PQclear(res);
+ continue;
+ }
+
+ i_oid = PQfnumber(res, "oid");
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_rsecpolname = PQfnumber(res, "rsecpolname");
+ i_rseccmd = PQfnumber(res, "rseccmd");
+ i_rsecroles = PQfnumber(res, "rsecroles");
+ i_rsecqual = PQfnumber(res, "rsecqual");
+ i_rsecwithcheck = PQfnumber(res, "rsecwithcheck");
+
+ rsinfo = pg_malloc(ntups * sizeof(RowSecurityInfo));
+
+ for (j = 0; j < ntups; j++)
+ {
+ rsinfo[j].dobj.objType = DO_ROW_SECURITY;
+ rsinfo[j].dobj.catId.tableoid =
+ atooid(PQgetvalue(res, j, i_tableoid));
+ rsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
+ AssignDumpId(&rsinfo[j].dobj);
+ rsinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+ rsinfo[j].rstable = tbinfo;
+ rsinfo[j].rsecpolname = pg_strdup(PQgetvalue(res, j,
+ i_rsecpolname));
+
+ rsinfo[j].dobj.name = pg_strdup(rsinfo[j].rsecpolname);
+
+ if (PQgetisnull(res, j, i_rseccmd))
+ rsinfo[j].rseccmd = NULL;
+ else
+ rsinfo[j].rseccmd = pg_strdup(PQgetvalue(res, j, i_rseccmd));
+
+ rsinfo[j].rsecroles = pg_strdup(PQgetvalue(res, j, i_rsecroles));
+
+ if (PQgetisnull(res, j, i_rsecqual))
+ rsinfo[j].rsecqual = NULL;
+ else
+ rsinfo[j].rsecqual = pg_strdup(PQgetvalue(res, j, i_rsecqual));
+
+ if (PQgetisnull(res, j, i_rsecwithcheck))
+ rsinfo[j].rsecwithcheck = NULL;
+ else
+ rsinfo[j].rsecwithcheck
+ = pg_strdup(PQgetvalue(res, j, i_rsecwithcheck));
+ }
+ PQclear(res);
+ }
+ destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpRowSecurity
+ * dump the definition of the given row-security policy
+ */
+static void
+dumpRowSecurity(Archive *fout, RowSecurityInfo *rsinfo)
+{
+ TableInfo *tbinfo = rsinfo->rstable;
+ PQExpBuffer query;
+ PQExpBuffer delqry;
+ const char *cmd;
+
+ if (dataOnly)
+ return;
+
+ /*
+ * If rsecpolname is NULL, then this record is just indicating that ROW
+ * LEVEL SECURITY is enabled for the table.
+ * Dump as ALTER TABLE <table> ENABLE ROW LEVEL SECURITY.
+ */
+ if (rsinfo->rsecpolname == NULL)
+ {
+ query = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
+ fmtId(rsinfo->dobj.name));
+
+ ArchiveEntry(fout, rsinfo->dobj.catId, rsinfo->dobj.dumpId,
+ rsinfo->dobj.name,
+ rsinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "ROW SECURITY", SECTION_NONE,
+ query->data, "", NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+ return;
+ }
+
+ if (!rsinfo->rseccmd)
+ cmd = "ALL";
+ else if (strcmp(rsinfo->rseccmd, "r") == 0)
+ cmd = "SELECT";
+ else if (strcmp(rsinfo->rseccmd, "a") == 0)
+ cmd = "INSERT";
+ else if (strcmp(rsinfo->rseccmd, "u") == 0)
+ cmd = "UPDATE";
+ else if (strcmp(rsinfo->rseccmd, "d") == 0)
+ cmd = "DELETE";
+ else
+ {
+ write_msg(NULL, "unexpected command type: '%s'\n", rsinfo->rseccmd);
+ exit_nicely(1);
+ }
+
+ query = createPQExpBuffer();
+ delqry = createPQExpBuffer();
+
+ appendPQExpBuffer(query, "CREATE POLICY %s ON %s FOR %s",
+ rsinfo->rsecpolname, fmtId(tbinfo->dobj.name), cmd);
+
+ if (rsinfo->rsecroles != NULL)
+ appendPQExpBuffer(query, " TO %s", rsinfo->rsecroles);
+
+ if (rsinfo->rsecqual != NULL)
+ appendPQExpBuffer(query, " USING %s", rsinfo->rsecqual);
+
+ if (rsinfo->rsecwithcheck != NULL)
+ appendPQExpBuffer(query, " WITH CHECK %s", rsinfo->rsecwithcheck);
+
+ appendPQExpBuffer(query, ";\n");
+
+ appendPQExpBuffer(delqry, "DROP POLICY %s ON %s;\n",
+ rsinfo->rsecpolname, fmtId(tbinfo->dobj.name));
+
+ ArchiveEntry(fout, rsinfo->dobj.catId, rsinfo->dobj.dumpId,
+ rsinfo->dobj.name,
+ rsinfo->dobj.namespace->dobj.name,
+ NULL,
+ tbinfo->rolname, false,
+ "ROW SECURITY", SECTION_POST_DATA,
+ query->data, delqry->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(delqry);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -4287,6 +4534,7 @@ getTables(Archive *fout, int *numTables)
int i_relhastriggers;
int i_relhasindex;
int i_relhasrules;
+ int i_relhasrowsec;
int i_relhasoids;
int i_relfrozenxid;
int i_relminmxid;
@@ -4328,7 +4576,48 @@ getTables(Archive *fout, int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (fout->remoteVersion >= 90400)
+ if (fout->remoteVersion >= 90500)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any (note this dependency is AUTO as of 8.2)
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.relname, "
+ "c.relacl, c.relkind, c.relnamespace, "
+ "(%s c.relowner) AS rolname, "
+ "c.relchecks, c.relhastriggers, "
+ "c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relhasrowsecurity, "
+ "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
+ "tc.relfrozenxid AS tfrozenxid, "
+ "tc.relminmxid AS tminmxid, "
+ "c.relpersistence, c.relispopulated, "
+ "c.relreplident, c.relpages, "
+ "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+ "d.refobjid AS owning_tab, "
+ "d.refobjsubid AS owning_col, "
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
+ "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
+ "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
+ "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
+ "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+ "FROM pg_class c "
+ "LEFT JOIN pg_depend d ON "
+ "(c.relkind = '%c' AND "
+ "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.objsubid = 0 AND "
+ "d.refclassid = c.tableoid AND d.deptype = 'a') "
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c', '%c') "
+ "ORDER BY c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE);
+ }
+ else if (fout->remoteVersion >= 90400)
{
/*
* Left join to pick up dependency info linking sequences to their
@@ -4340,6 +4629,7 @@ getTables(Archive *fout, int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"tc.relminmxid AS tminmxid, "
@@ -4380,6 +4670,7 @@ getTables(Archive *fout, int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"tc.relminmxid AS tminmxid, "
@@ -4420,6 +4711,7 @@ getTables(Archive *fout, int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"0 AS tminmxid, "
@@ -4458,6 +4750,7 @@ getTables(Archive *fout, int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"0 AS tminmxid, "
@@ -4495,6 +4788,7 @@ getTables(Archive *fout, int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"0 AS tminmxid, "
@@ -4532,6 +4826,7 @@ getTables(Archive *fout, int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
"tc.relfrozenxid AS tfrozenxid, "
"0 AS tminmxid, "
@@ -4569,6 +4864,7 @@ getTables(Archive *fout, int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"0 AS relfrozenxid, 0 AS relminmxid,"
"0 AS toid, "
"0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4605,6 +4901,7 @@ getTables(Archive *fout, int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"0 AS relfrozenxid, 0 AS relminmxid,"
"0 AS toid, "
"0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4637,6 +4934,7 @@ getTables(Archive *fout, int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"0 AS relfrozenxid, 0 AS relminmxid,"
"0 AS toid, "
"0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4664,6 +4962,7 @@ getTables(Archive *fout, int *numTables)
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, "
"'t'::bool AS relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"0 AS relfrozenxid, 0 AS relminmxid,"
"0 AS toid, "
"0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4701,6 +5000,7 @@ getTables(Archive *fout, int *numTables)
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, "
"'t'::bool AS relhasoids, "
+ "'f'::bool AS relhasrowsecurity, "
"0 AS relfrozenxid, 0 AS relminmxid,"
"0 AS toid, "
"0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4748,6 +5048,7 @@ getTables(Archive *fout, int *numTables)
i_relhastriggers = PQfnumber(res, "relhastriggers");
i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasrules = PQfnumber(res, "relhasrules");
+ i_relhasrowsec = PQfnumber(res, "relhasrowsecurity");
i_relhasoids = PQfnumber(res, "relhasoids");
i_relfrozenxid = PQfnumber(res, "relfrozenxid");
i_relminmxid = PQfnumber(res, "relminmxid");
@@ -4799,6 +5100,7 @@ getTables(Archive *fout, int *numTables)
tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
+ tblinfo[i].hasrowsec = (strcmp(PQgetvalue(res, i, i_relhasrowsec), "t") == 0);
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
@@ -7930,6 +8232,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
NULL, 0,
dumpBlobs, NULL);
break;
+ case DO_ROW_SECURITY:
+ dumpRowSecurity(fout, (RowSecurityInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15332,6 +15637,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_TRIGGER:
case DO_EVENT_TRIGGER:
case DO_DEFAULT_ACL:
+ case DO_ROW_SECURITY:
/* Post-data objects: must come after the post-data boundary */
addObjectDependency(dobj, postDataBound->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index d184187580..b5d820e761 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -111,7 +111,8 @@ typedef enum
DO_PRE_DATA_BOUNDARY,
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
- DO_REFRESH_MATVIEW
+ DO_REFRESH_MATVIEW,
+ DO_ROW_SECURITY
} DumpableObjectType;
typedef struct _dumpableObject
@@ -245,6 +246,7 @@ typedef struct _tableInfo
bool hasindex; /* does it have any indexes? */
bool hasrules; /* does it have any rules? */
bool hastriggers; /* does it have any triggers? */
+ bool hasrowsec; /* does it have any row-security policy? */
bool hasoids; /* does it have OIDs? */
uint32 frozenxid; /* for restore frozen xid */
uint32 minmxid; /* for restore min multi xid */
@@ -486,6 +488,23 @@ typedef struct _blobInfo
char *blobacl;
} BlobInfo;
+/*
+ * The RowSecurityInfo struct is used to represent row policies on a table and
+ * to indicate if a table has RLS enabled (ENABLE ROW SECURITY). If
+ * rsecpolname is NULL, then the record indicates ENABLE ROW SECURITY, while if
+ * it's non-NULL then this is a regular policy definition.
+ */
+typedef struct _rowSecurityInfo
+{
+ DumpableObject dobj;
+ TableInfo *rstable;
+ char *rsecpolname; /* null indicates RLS is enabled on rel */
+ char *rseccmd;
+ char *rsecroles;
+ char *rsecqual;
+ char *rsecwithcheck;
+} RowSecurityInfo;
+
/* global decls */
extern bool force_quotes; /* double-quotes for identifiers flag */
extern bool g_verbose; /* verbose flag */
@@ -577,5 +596,6 @@ extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs);
extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions);
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
+extern void getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index f0caa6b659..90aedee7d2 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -70,7 +70,8 @@ static const int oldObjectTypePriority[] =
10, /* DO_PRE_DATA_BOUNDARY */
13, /* DO_POST_DATA_BOUNDARY */
20, /* DO_EVENT_TRIGGER */
- 15 /* DO_REFRESH_MATVIEW */
+ 15, /* DO_REFRESH_MATVIEW */
+ 21 /* DO_ROW_SECURITY */
};
/*
@@ -118,7 +119,8 @@ static const int newObjectTypePriority[] =
22, /* DO_PRE_DATA_BOUNDARY */
25, /* DO_POST_DATA_BOUNDARY */
32, /* DO_EVENT_TRIGGER */
- 33 /* DO_REFRESH_MATVIEW */
+ 33, /* DO_REFRESH_MATVIEW */
+ 34 /* DO_ROW_SECURITY */
};
static DumpId preDataBoundId;
@@ -1434,6 +1436,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"BLOB DATA (ID %d)",
obj->dumpId);
return;
+ case DO_ROW_SECURITY:
+ snprintf(buf, bufsize,
+ "ROW-SECURITY POLICY (ID %d OID %u)",
+ obj->dumpId, obj->catId.oid);
+ return;
case DO_PRE_DATA_BOUNDARY:
snprintf(buf, bufsize,
"PRE-DATA BOUNDARY (ID %d)",
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index b2b3e6feb7..c25ea851d3 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -663,17 +663,29 @@ dumpRoles(PGconn *conn)
i_rolpassword,
i_rolvaliduntil,
i_rolreplication,
+ i_rolbypassrls,
i_rolcomment,
i_is_current_user;
int i;
/* note: rolconfig is dumped later */
- if (server_version >= 90100)
+ if (server_version >= 90500)
+ printfPQExpBuffer(buf,
+ "SELECT oid, rolname, rolsuper, rolinherit, "
+ "rolcreaterole, rolcreatedb, "
+ "rolcanlogin, rolconnlimit, rolpassword, "
+ "rolvaliduntil, rolreplication, rolbypassrls, "
+ "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+ "rolname = current_user AS is_current_user "
+ "FROM pg_authid "
+ "ORDER BY 2");
+ else if (server_version >= 90100)
printfPQExpBuffer(buf,
"SELECT oid, rolname, rolsuper, rolinherit, "
"rolcreaterole, rolcreatedb, "
"rolcanlogin, rolconnlimit, rolpassword, "
"rolvaliduntil, rolreplication, "
+ "false as rolbypassrls, "
"pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
"rolname = current_user AS is_current_user "
"FROM pg_authid "
@@ -684,6 +696,7 @@ dumpRoles(PGconn *conn)
"rolcreaterole, rolcreatedb, "
"rolcanlogin, rolconnlimit, rolpassword, "
"rolvaliduntil, false as rolreplication, "
+ "false as rolbypassrls, "
"pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
"rolname = current_user AS is_current_user "
"FROM pg_authid "
@@ -694,6 +707,7 @@ dumpRoles(PGconn *conn)
"rolcreaterole, rolcreatedb, "
"rolcanlogin, rolconnlimit, rolpassword, "
"rolvaliduntil, false as rolreplication, "
+ "false as rolbypassrls, "
"null as rolcomment, "
"rolname = current_user AS is_current_user "
"FROM pg_authid "
@@ -724,6 +738,7 @@ dumpRoles(PGconn *conn)
"null::text as rolpassword, "
"null::abstime as rolvaliduntil, "
"false as rolreplication, "
+ "false as rolbypassrls, "
"null as rolcomment, false "
"FROM pg_group "
"WHERE NOT EXISTS (SELECT 1 FROM pg_shadow "
@@ -743,6 +758,7 @@ dumpRoles(PGconn *conn)
i_rolpassword = PQfnumber(res, "rolpassword");
i_rolvaliduntil = PQfnumber(res, "rolvaliduntil");
i_rolreplication = PQfnumber(res, "rolreplication");
+ i_rolbypassrls = PQfnumber(res, "rolbypassrls");
i_rolcomment = PQfnumber(res, "rolcomment");
i_is_current_user = PQfnumber(res, "is_current_user");
@@ -810,6 +826,11 @@ dumpRoles(PGconn *conn)
else
appendPQExpBufferStr(buf, " NOREPLICATION");
+ if (strcmp(PQgetvalue(res, i, i_rolbypassrls), "t") == 0)
+ appendPQExpBufferStr(buf, " BYPASSRLS");
+ else
+ appendPQExpBufferStr(buf, " NOBYPASSRLS");
+
if (strcmp(PQgetvalue(res, i, i_rolconnlimit), "-1") != 0)
appendPQExpBuffer(buf, " CONNECTION LIMIT %s",
PQgetvalue(res, i, i_rolconnlimit));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index fdfdc19c3f..1c1b80f137 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -70,6 +70,7 @@ main(int argc, char **argv)
Archive *AH;
char *inputFileSpec;
static int disable_triggers = 0;
+ static int enable_row_security = 0;
static int if_exists = 0;
static int no_data_for_failed_tables = 0;
static int outputNoTablespaces = 0;
@@ -111,6 +112,7 @@ main(int argc, char **argv)
* the following options don't have an equivalent short option letter
*/
{"disable-triggers", no_argument, &disable_triggers, 1},
+ {"enable-row-security", no_argument, &enable_row_security, 1},
{"if-exists", no_argument, &if_exists, 1},
{"no-data-for-failed-tables", no_argument, &no_data_for_failed_tables, 1},
{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
@@ -333,6 +335,7 @@ main(int argc, char **argv)
}
opts->disable_triggers = disable_triggers;
+ opts->enable_row_security = enable_row_security;
opts->noDataForFailedTables = no_data_for_failed_tables;
opts->noTablespace = outputNoTablespaces;
opts->use_setsessauth = use_setsessauth;
@@ -460,6 +463,7 @@ usage(const char *progname)
printf(_(" -x, --no-privileges skip restoration of access privileges (grant/revoke)\n"));
printf(_(" -1, --single-transaction restore as a single transaction\n"));
printf(_(" --disable-triggers disable triggers during data-only restore\n"));
+ printf(_(" --enable-row-security enable row level security\n"));
printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
printf(_(" --no-data-for-failed-tables do not restore data of tables that could not be\n"
" created\n"));
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 282cd432a2..97dc2dded2 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -742,7 +742,7 @@ permissionsList(const char *pattern)
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
- static const bool translate_columns[] = {false, false, true, false, false};
+ static const bool translate_columns[] = {false, false, true, false, false, false};
initPQExpBuffer(&buf);
@@ -778,7 +778,38 @@ permissionsList(const char *pattern)
" FROM pg_catalog.pg_attribute a\n"
" WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL\n"
" ), E'\\n') AS \"%s\"",
- gettext_noop("Column access privileges"));
+ gettext_noop("Column privileges"));
+
+ if (pset.sversion >= 90500)
+ appendPQExpBuffer(&buf,
+ ",\n pg_catalog.array_to_string(ARRAY(\n"
+ " SELECT rsecpolname\n"
+ " || CASE WHEN rseccmd IS NOT NULL THEN\n"
+ " E' (' || rseccmd || E')'\n"
+ " ELSE E':' \n"
+ " END\n"
+ " || CASE WHEN rs.rsecqual IS NOT NULL THEN\n"
+ " E'\\n (u): ' || pg_catalog.pg_get_expr(rsecqual, rsecrelid)\n"
+ " ELSE E''\n"
+ " END\n"
+ " || CASE WHEN rsecwithcheck IS NOT NULL THEN\n"
+ " E'\\n (c): ' || pg_catalog.pg_get_expr(rsecwithcheck, rsecrelid)\n"
+ " ELSE E''\n"
+ " END"
+ " || CASE WHEN rs.rsecroles <> '{0}' THEN\n"
+ " E'\\n to: ' || pg_catalog.array_to_string(\n"
+ " ARRAY(\n"
+ " SELECT rolname\n"
+ " FROM pg_catalog.pg_roles\n"
+ " WHERE oid = ANY (rs.rsecroles)\n"
+ " ORDER BY 1\n"
+ " ), E', ')\n"
+ " ELSE E''\n"
+ " END\n"
+ " FROM pg_catalog.pg_rowsecurity rs\n"
+ " WHERE rsecrelid = c.oid), E'\\n')\n"
+ " AS \"%s\"",
+ gettext_noop("Policies"));
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_class c\n"
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
@@ -1173,6 +1204,7 @@ describeOneTableDetails(const char *schemaname,
bool hasindex;
bool hasrules;
bool hastriggers;
+ bool hasrowsecurity;
bool hasoids;
Oid tablespace;
char *reloptions;
@@ -1194,11 +1226,28 @@ describeOneTableDetails(const char *schemaname,
initPQExpBuffer(&tmpbuf);
/* Get general table info */
- if (pset.sversion >= 90400)
+ if (pset.sversion >= 90500)
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
- "c.relhastriggers, c.relhasoids, "
+ "c.relhastriggers, c.relhasrowsecurity, c.relhasoids, "
+ "%s, c.reltablespace, "
+ "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
+ "c.relpersistence, c.relreplident\n"
+ "FROM pg_catalog.pg_class c\n "
+ "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+ "WHERE c.oid = '%s';",
+ (verbose ?
+ "pg_catalog.array_to_string(c.reloptions || "
+ "array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ')\n"
+ : "''"),
+ oid);
+ }
+ else if (pset.sversion >= 90400)
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
+ "c.relhastriggers, false, c.relhasoids, "
"%s, c.reltablespace, "
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
"c.relpersistence, c.relreplident\n"
@@ -1215,7 +1264,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
- "c.relhastriggers, c.relhasoids, "
+ "c.relhastriggers, false, c.relhasoids, "
"%s, c.reltablespace, "
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
"c.relpersistence\n"
@@ -1232,7 +1281,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
- "c.relhastriggers, c.relhasoids, "
+ "c.relhastriggers, false, c.relhasoids, "
"%s, c.reltablespace, "
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
"FROM pg_catalog.pg_class c\n "
@@ -1248,7 +1297,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
- "c.relhastriggers, c.relhasoids, "
+ "c.relhastriggers, false, c.relhasoids, "
"%s, c.reltablespace\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
@@ -1263,7 +1312,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
- "reltriggers <> 0, relhasoids, "
+ "reltriggers <> 0, false, relhasoids, "
"%s, reltablespace\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
@@ -1274,7 +1323,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
- "reltriggers <> 0, relhasoids, "
+ "reltriggers <> 0, false, relhasoids, "
"'', reltablespace\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
@@ -1283,7 +1332,7 @@ describeOneTableDetails(const char *schemaname,
{
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
- "reltriggers <> 0, relhasoids, "
+ "reltriggers <> 0, false, relhasoids, "
"'', ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
@@ -1306,18 +1355,19 @@ describeOneTableDetails(const char *schemaname,
tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
- tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
+ tableinfo.hasrowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
+ tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
tableinfo.reloptions = (pset.sversion >= 80200) ?
- pg_strdup(PQgetvalue(res, 0, 6)) : NULL;
+ pg_strdup(PQgetvalue(res, 0, 7)) : NULL;
tableinfo.tablespace = (pset.sversion >= 80000) ?
- atooid(PQgetvalue(res, 0, 7)) : 0;
+ atooid(PQgetvalue(res, 0, 8)) : 0;
tableinfo.reloftype = (pset.sversion >= 90000 &&
- strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
- pg_strdup(PQgetvalue(res, 0, 8)) : NULL;
+ strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+ pg_strdup(PQgetvalue(res, 0, 9)) : NULL;
tableinfo.relpersistence = (pset.sversion >= 90100) ?
- *(PQgetvalue(res, 0, 9)) : 0;
+ *(PQgetvalue(res, 0, 10)) : 0;
tableinfo.relreplident = (pset.sversion >= 90400) ?
- *(PQgetvalue(res, 0, 10)) : 'd';
+ *(PQgetvalue(res, 0, 11)) : 'd';
PQclear(res);
res = NULL;
@@ -1948,6 +1998,67 @@ describeOneTableDetails(const char *schemaname,
PQclear(result);
}
+
+ if (pset.sversion >= 90500)
+ appendPQExpBuffer(&buf,
+ ",\n pg_catalog.pg_get_expr(rs.rsecqual, c.oid) as \"%s\"",
+ gettext_noop("Row-security"));
+ if (verbose && pset.sversion >= 90500)
+ appendPQExpBuffer(&buf,
+ "\n LEFT JOIN pg_rowsecurity rs ON rs.rsecrelid = c.oid");
+
+ /* print any row-level policies */
+ if (tableinfo.hasrowsecurity)
+ {
+ printfPQExpBuffer(&buf,
+ "SELECT rs.rsecpolname,\n"
+ "CASE WHEN rs.rsecroles = '{0}' THEN NULL ELSE array(select rolname from pg_roles where oid = any (rs.rsecroles) order by 1) END,\n"
+ "pg_catalog.pg_get_expr(rs.rsecqual, rs.rsecrelid),\n"
+ "pg_catalog.pg_get_expr(rs.rsecwithcheck, rs.rsecrelid),\n"
+ "rs.rseccmd AS cmd\n"
+ "FROM pg_catalog.pg_rowsecurity rs\n"
+ "WHERE rs.rsecrelid = '%s' ORDER BY 1;",
+ oid);
+ result = PSQLexec(buf.data, false);
+ if (!result)
+ goto error_return;
+ else
+ tuples = PQntuples(result);
+
+ if (tuples > 0)
+ {
+ printTableAddFooter(&cont, _("Policies:"));
+ for (i = 0; i < tuples; i++)
+ {
+ printfPQExpBuffer(&buf, " POLICY \"%s\"",
+ PQgetvalue(result, i, 0));
+
+ if (!PQgetisnull(result, i, 4))
+ appendPQExpBuffer(&buf, " (%s)",
+ PQgetvalue(result, i, 4));
+
+ if (!PQgetisnull(result, i, 2))
+ appendPQExpBuffer(&buf, " EXPRESSION %s",
+ PQgetvalue(result, i, 2));
+
+ if (!PQgetisnull(result, i, 3))
+ appendPQExpBuffer(&buf, " WITH CHECK %s",
+ PQgetvalue(result, i, 3));
+
+ printTableAddFooter(&cont, buf.data);
+
+ if (!PQgetisnull(result, i, 1))
+ {
+ printfPQExpBuffer(&buf, " APPLIED TO %s",
+ PQgetvalue(result, i, 1));
+
+ printTableAddFooter(&cont, buf.data);
+ }
+ }
+ }
+ PQclear(result);
+ }
+
/* print rules */
if (tableinfo.hasrules && tableinfo.relkind != 'm')
{
@@ -2529,6 +2640,11 @@ describeRoles(const char *pattern, bool verbose)
appendPQExpBufferStr(&buf, "\n, r.rolreplication");
}
+ if (pset.sversion >= 90500)
+ {
+ appendPQExpBufferStr(&buf, "\n, r.rolbypassrls");
+ }
+
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
processSQLNamePattern(pset.db, &buf, pattern, false, false,
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index b80fe13168..a4594b6783 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -782,6 +782,7 @@ static const pgsql_thing_t words_after_create[] = {
* good idea. */
{"OWNED", NULL, NULL, THING_NO_CREATE}, /* for DROP OWNED BY ... */
{"PARSER", Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
+ {"POLICY", NULL, NULL},
{"ROLE", Query_for_list_of_roles},
{"RULE", "SELECT pg_catalog.quote_ident(rulename) FROM pg_catalog.pg_rules WHERE substring(pg_catalog.quote_ident(rulename),1,%d)='%s'"},
{"SCHEMA", Query_for_list_of_schemas},
@@ -971,7 +972,7 @@ psql_completion(const char *text, int start, int end)
{"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN",
"EVENT TRIGGER", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION",
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "MATERIALIZED VIEW", "OPERATOR",
- "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "SYSTEM", "TABLE",
+ "POLICY", "ROLE", "RULE", "SCHEMA", "SERVER", "SEQUENCE", "SYSTEM", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
"USER", "USER MAPPING FOR", "VIEW", NULL};
@@ -1398,6 +1399,44 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_LIST(list_ALTERMATVIEW);
}
+ /* ALTER POLICY <name> ON */
+ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev2_wd, "POLICY") == 0)
+ COMPLETE_WITH_CONST("ON");
+ /* ALTER POLICY <name> ON <table> */
+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev3_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev_wd, "ON") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ /* ALTER POLICY <name> ON <table> - show options */
+ else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev4_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev2_wd, "ON") == 0)
+ {
+ static const char *const list_ALTERPOLICY[] =
+ {"RENAME TO", "TO", "USING", "WITH CHECK", NULL};
+
+ COMPLETE_WITH_LIST(list_ALTERPOLICY);
+ }
+ /* ALTER POLICY <name> ON <table> TO <role> */
+ else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
+ /* ALTER POLICY <name> ON <table> USING ( */
+ else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 &&
+ pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "USING") == 0)
+ COMPLETE_WITH_CONST("(");
+ /* ALTER POLICY <name> ON <table> WITH CHECK ( */
+ else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev4_wd, "ON") == 0 &&
+ pg_strcasecmp(prev2_wd, "WITH") == 0 &&
+ pg_strcasecmp(prev_wd, "CHECK") == 0)
+ COMPLETE_WITH_CONST("(");
+
/* ALTER RULE <name>, add ON */
else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
pg_strcasecmp(prev2_wd, "RULE") == 0)
@@ -1462,7 +1501,7 @@ psql_completion(const char *text, int start, int end)
pg_strcasecmp(prev_wd, "ENABLE") == 0)
{
static const char *const list_ALTERENABLE[] =
- {"ALWAYS", "REPLICA", "RULE", "TRIGGER", NULL};
+ {"ALWAYS", "REPLICA", "ROW LEVEL SECURITY", "RULE", "TRIGGER", NULL};
COMPLETE_WITH_LIST(list_ALTERENABLE);
}
@@ -1529,7 +1568,7 @@ psql_completion(const char *text, int start, int end)
pg_strcasecmp(prev_wd, "DISABLE") == 0)
{
static const char *const list_ALTERDISABLE[] =
- {"RULE", "TRIGGER", NULL};
+ { "ROW LEVEL SECURITY", "RULE", "TRIGGER", NULL};
COMPLETE_WITH_LIST(list_ALTERDISABLE);
}
@@ -1549,6 +1588,16 @@ psql_completion(const char *text, int start, int end)
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_trigger_of_table);
}
+ else if (pg_strcasecmp(prev4_wd, "DISABLE") == 0 &&
+ pg_strcasecmp(prev3_wd, "ROW") == 0 &&
+ pg_strcasecmp(prev2_wd, "LEVEL") == 0 &&
+ pg_strcasecmp(prev_wd, "SECURITY") == 0)
+ {
+ static const char *const list_DISABLERLS[] =
+ { "CASCADE", NULL};
+
+ COMPLETE_WITH_LIST(list_DISABLERLS);
+ }
/* ALTER TABLE xxx ALTER */
else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 &&
@@ -2251,12 +2300,103 @@ psql_completion(const char *text, int start, int end)
pg_strcasecmp(prev_wd, "(") == 0)
COMPLETE_WITH_ATTR(prev4_wd, "");
/* Complete USING with an index method */
- else if (pg_strcasecmp(prev_wd, "USING") == 0)
+ else if ((pg_strcasecmp(prev6_wd, "INDEX") == 0 ||
+ pg_strcasecmp(prev5_wd, "INDEX") == 0 ||
+ pg_strcasecmp(prev4_wd, "INDEX") == 0) &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "USING") == 0)
COMPLETE_WITH_QUERY(Query_for_list_of_access_methods);
else if (pg_strcasecmp(prev4_wd, "ON") == 0 &&
+ (!(pg_strcasecmp(prev6_wd, "POLICY") == 0) &&
+ !(pg_strcasecmp(prev4_wd, "FOR") == 0)) &&
pg_strcasecmp(prev2_wd, "USING") == 0)
COMPLETE_WITH_CONST("(");
+ /* CREATE POLICY */
+ /* Complete "CREATE POLICY <name> ON" */
+ else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev2_wd, "POLICY") == 0)
+ COMPLETE_WITH_CONST("ON");
+ /* Complete "CREATE POLICY <name> ON <table>" */
+ else if (pg_strcasecmp(prev4_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev3_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev_wd, "ON") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+ /* Complete "CREATE POLICY <name> ON <table> FOR|TO|USING|WITH CHECK" */
+ else if (pg_strcasecmp(prev5_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev4_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev2_wd, "ON") == 0)
+ {
+ static const char *const list_POLICYOPTIONS[] =
+ {"FOR", "TO", "USING", "WITH CHECK", NULL};
+
+ COMPLETE_WITH_LIST(list_POLICYOPTIONS);
+ }
+ /* Complete "CREATE POLICY <name> ON <table> FOR ALL|SELECT|INSERT|UPDATE|DELETE" */
+ else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "FOR") == 0)
+ {
+ static const char *const list_POLICYCMDS[] =
+ {"ALL", "SELECT", "INSERT", "UPDATE", "DELETE", NULL};
+
+ COMPLETE_WITH_LIST(list_POLICYCMDS);
+ }
+ /* Complete "CREATE POLICY <name> ON <table> FOR INSERT TO|WITH CHECK" */
+ else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev4_wd, "ON") == 0 &&
+ pg_strcasecmp(prev2_wd, "FOR") == 0 &&
+ pg_strcasecmp(prev_wd, "INSERT") == 0)
+ {
+ static const char *const list_POLICYOPTIONS[] =
+ {"TO", "WITH CHECK", NULL};
+
+ COMPLETE_WITH_LIST(list_POLICYOPTIONS);
+ }
+ /*
+ * Complete "CREATE POLICY <name> ON <table> FOR SELECT TO|USING"
+ * Complete "CREATE POLICY <name> ON <table> FOR DELETE TO|USING"
+ */
+ else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev4_wd, "ON") == 0 &&
+ pg_strcasecmp(prev2_wd, "FOR") == 0 &&
+ (pg_strcasecmp(prev_wd, "SELECT") == 0 ||
+ pg_strcasecmp(prev_wd, "DELETE") == 0))
+ {
+ static const char *const list_POLICYOPTIONS[] =
+ {"TO", "USING", NULL};
+
+ COMPLETE_WITH_LIST(list_POLICYOPTIONS);
+ }
+ /*
+ * Complete "CREATE POLICY <name> ON <table> FOR ALL TO|USING|WITH CHECK"
+ * Complete "CREATE POLICY <name> ON <table> FOR UPDATE TO|USING|WITH CHECK"
+ */
+ else if (pg_strcasecmp(prev6_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev4_wd, "ON") == 0 &&
+ pg_strcasecmp(prev2_wd, "FOR") == 0 &&
+ (pg_strcasecmp(prev_wd, "ALL") == 0 ||
+ pg_strcasecmp(prev_wd, "UPDATE") == 0))
+ {
+ static const char *const list_POLICYOPTIONS[] =
+ {"TO", "USING", "WITH CHECK", NULL};
+
+ COMPLETE_WITH_LIST(list_POLICYOPTIONS);
+ }
+ /* Complete "CREATE POLICY <name> ON <table> TO <role>" */
+ else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "TO") == 0)
+ COMPLETE_WITH_QUERY(Query_for_list_of_grant_roles);
+ /* Complete "CREATE POLICY <name> ON <table> USING (" */
+ else if (pg_strcasecmp(prev6_wd, "CREATE") == 0 &&
+ pg_strcasecmp(prev5_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev3_wd, "ON") == 0 &&
+ pg_strcasecmp(prev_wd, "USING") == 0)
+ COMPLETE_WITH_CONST("(");
+
/* CREATE RULE */
/* Complete "CREATE RULE <sth>" with "AS" */
else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 &&
@@ -2726,6 +2866,16 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers);
}
+ /* DROP POLICY <name> ON */
+ else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev2_wd, "POLICY") == 0)
+ COMPLETE_WITH_CONST("ON");
+ /* DROP POLICY <name> ON <table> */
+ else if (pg_strcasecmp(prev4_wd, "DROP") == 0 &&
+ pg_strcasecmp(prev3_wd, "POLICY") == 0 &&
+ pg_strcasecmp(prev_wd, "ON") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL);
+
/* DROP RULE */
else if (pg_strcasecmp(prev3_wd, "DROP") == 0 &&
pg_strcasecmp(prev2_wd, "RULE") == 0)