Some random comments after a quick read-through of the patch:
* Missing documentation. For a major feature like this, reference pages for the CREATE/DROP POLICY statements are not sufficient. We'll need a whole new chapter for this.
* In CREATE POLICY, the "USING" and "WITH CHECK" keywords are not very descriptive. I kind of understand WITH CHECK - it's applied to new rows like a CHECK constraint - but USING seems rather arbitrary and WITH CHECK isn't all that clear either. Perhaps "ON SELECT CHECK" and "ON UPDATE CHECK" or something like that would be better. I guess USING makes sense when that's the only expression given, so that it applies to both SELECTs and UPDATEs. But when a WITH CHECK expression is given together with USING, it gets confusing.
+ if (is_from) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("COPY FROM not supported with row security."), + errhint("Use direct INSERT statements instead."))); +
Why is that not implemented? I think it should be. * In src/backend/commands/createas.c:
@@ -420,6 +421,19 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo) ExecCheckRTPerms(list_make1(rte), true); /* + * Make sure the constructed table does not have RLS enabled. + * + * check_enable_rls() will ereport(ERROR) itself if the user has requested + * something invalid, and otherwise will return RLS_ENABLED if RLS should + * be enabled here. We don't actually support that currently, so throw + * our own ereport(ERROR) if that happens. + */ + if (check_enable_rls(intoRelationId, InvalidOid) == RLS_ENABLED) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + (errmsg("policies not yet implemented for this command")))); + + /* * Tentatively mark the target as populated, if it's a matview and we're * going to fill it; otherwise, no change needed. */
Hmm. So, if a table we just created with CREATE TABLE AS has row-level security enabled, we throw an error? Can that actually happen? AFAICS a freshly-created table shouldn't can't have row-level security enabled. Or is row-level security enabled/disabled status copied from the source table?
* Row-level security is not allowed for foreign tables. Why not? It's perhaps not a very useful feature, but I don't see any immediate reason to forbid it either. How about views?
* The new pg_class.relhasrowsecurity column is not updated lazily like most other relhas* columns. That's intentional and documented, but perhaps it would've been better to name the column differently, to avoid confusion. Maybe just "relrowsecurity"
* In rewrite/rowsecurity:
+ * Policies can be defined for specific roles, specific commands, or provided + * by an extension.
How do you define a policy for a specific role? There's a hook for the extensions in there, but I didn't see any documentation mentioning that an extension might provide policies, nor any developer documentation on how to use the hook.
* In pg_backup_archiver.c:
/* * 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");
That makes pg_restore to throw an error if you try to restore to a pre-9.5 server. I'm not sure if using a newer pg_restore against an older server is supported in general, but without the above it works in simple cases, at least. It would be trivi
* The new --enable-row-security option to pg_restore is not documented in the user manual.
* in src/bin/psql/describe.c:
@@ -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");
The rolbypassrls column isn't actually used for anything in this function. In addition to the above, attached is a patch with some trivial fixes. - Heikki
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 76d6405..bae08ee 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1943,6 +1943,7 @@ <row> <entry><structfield>relhasrowsecurity</structfield></entry> <entry><type>bool</type></entry> + <entry></entry> <entry> True if table has row-security enabled; see <link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecurity</structname></link> catalog diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 70e47aa..9494439 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5457,9 +5457,9 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; <para> The allowed values of <varname>row_security</> are - <literal>on</> (apply normally- not to superuser or table owner), + <literal>on</> (apply normally - not to superuser or table owner), <literal>off</> (fail if row security would be applied), and - <literal>force</> (apply always- even to superuser and table owner). + <literal>force</> (apply always - even to superuser and table owner). </para> <para> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 1b35756..b5ef09e 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -429,7 +429,7 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable> These forms control the application of row security policies belonging to the table. If enabled and no policies exist for the table, then a default-deny policy is applied. Note that policies can exist for a table - even if row level security is disabled- in this case, the policies will + even if row level security is disabled - in this case, the policies will NOT be applied and the policies will be ignored. See also <xref linkend="SQL-CREATEPOLICY">. diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 2f4df48..fc430b3 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -108,7 +108,7 @@ parse_row_security_command(const char *cmd_name) char cmd; if (!cmd_name) - elog(ERROR, "Unregonized command."); + elog(ERROR, "unregonized command"); if (strcmp(cmd_name, "all") == 0) cmd = 0; @@ -121,8 +121,7 @@ parse_row_security_command(const char *cmd_name) else if (strcmp(cmd_name, "delete") == 0) cmd = ACL_DELETE_CHR; else - elog(ERROR, "Unregonized command."); - /* error unrecognized command */ + elog(ERROR, "unregonized command"); return cmd; } @@ -484,7 +483,7 @@ CreatePolicy(CreatePolicyStmt *stmt) if (rseccmd == ACL_INSERT_CHR && stmt->qual != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Only WITH CHECK expression allowed for INSERT"))); + errmsg("only WITH CHECK expression allowed for INSERT"))); /* Collect role ids */ @@ -731,7 +730,7 @@ AlterPolicy(AlterPolicyStmt *stmt) if (!HeapTupleIsValid(rsec_tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("policy '%s' for does not exist on table %s", + errmsg("policy \"%s\" on table \"%s\" does not exist", stmt->policy_name, RelationGetRelationName(target_table)))); @@ -850,7 +849,7 @@ rename_policy(RenameStmt *stmt) pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock); - /* First pass- check for conflict */ + /* First pass -- check for conflict */ /* Add key - row security relation id. */ ScanKeyInit(&skey[0], diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e1ccd12..b595cfa 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -177,7 +177,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index) * all of them OR'd together. However, to avoid the situation of an * extension granting more access to a table than the internal policies * would allow, the extension's policies are AND'd with the internal - * policies. In other words- extensions can only provide further + * policies. In other words - extensions can only provide further * filtering of the result set (or further reduce the set of records * allowed to be added). * @@ -285,7 +285,6 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index) * * Returns the list of policies to be added for this relation, based on the * type of command and the roles to which it applies, from the relation cache. - * */ static List * pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id) diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index 1c1b80f..21715dc 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -463,7 +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(_(" --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/include/commands/policy.h b/src/include/commands/policy.h index 95d8a6d..8ca4cb0 100644 --- a/src/include/commands/policy.h +++ b/src/include/commands/policy.h @@ -24,10 +24,10 @@ extern void RemovePolicyById(Oid policy_id); extern Oid CreatePolicy(CreatePolicyStmt *stmt); extern Oid AlterPolicy(AlterPolicyStmt *stmt); -Oid get_relation_policy_oid(Oid relid, - const char *policy_name, bool missing_ok); +extern Oid get_relation_policy_oid(Oid relid, const char *policy_name, + bool missing_ok); -Oid rename_policy(RenameStmt *stmt); +extern Oid rename_policy(RenameStmt *stmt); #endif /* POLICY_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers