static void init_params(ParseState *pstate, List *options, bool for_identity,
bool isInit,
Form_pg_sequence seqform,
- bool *changed_seqform,
- Form_pg_sequence_data seqdataform, List **owned_by,
+ Form_pg_sequence_data seqdataform,
+ bool *need_seq_rewrite,
- List **owned_by);
++ List **owned_by,
+ bool *is_restart);
static void do_setval(Oid relid, int64 next, bool iscalled);
static void process_owned_by(Relation seqrel, List *owned_by, bool for_identity);
}
/* Check and set all option values */
- init_params(pstate, seq->options, seq->for_identity, true, &seqform,
- &changed_seqform, &seqdataform, &owned_by, &is_restart);
+ init_params(pstate, seq->options, seq->for_identity, true,
+ &seqform, &seqdataform,
- &need_seq_rewrite, &owned_by);
++ &need_seq_rewrite, &owned_by,
++ &is_restart);
/*
* Create relation (and fill value[] and null[] for the tuple)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqdatatuple;
+ HeapTupleData datatuple;
Form_pg_sequence seqform;
- Form_pg_sequence_data seqdata;
- FormData_pg_sequence_data newseqdata;
- bool changed_seqform = false;
+ Form_pg_sequence_data newdataform;
+ bool need_seq_rewrite;
List *owned_by;
+#ifdef PGXC
+ GTM_Sequence start_value;
+ GTM_Sequence last_value;
+ GTM_Sequence min_value;
+ GTM_Sequence max_value;
+ GTM_Sequence increment;
+ bool cycle;
+ bool is_restart;
+#endif
ObjectAddress address;
Relation rel;
- HeapTuple tuple;
+ HeapTuple seqtuple;
+ HeapTuple newdatatuple;
/* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence,
elog(ERROR, "cache lookup failed for sequence %u",
relid);
- seqform = (Form_pg_sequence) GETSTRUCT(tuple);
+ seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
/* lock page's buffer and read tuple into new sequence structure */
- seqdata = read_seq_tuple(seqrel, &buf, &seqdatatuple);
+ (void) read_seq_tuple(seqrel, &buf, &datatuple);
+
+ /* copy the existing sequence data tuple, so it can be modified localy */
+ newdatatuple = heap_copytuple(&datatuple);
+ newdataform = (Form_pg_sequence_data) GETSTRUCT(newdatatuple);
- /* Copy old sequence data into workspace */
- memcpy(&newseqdata, seqdata, sizeof(FormData_pg_sequence_data));
+ UnlockReleaseBuffer(buf);
/* Check and set new values */
- init_params(pstate, stmt->options, stmt->for_identity, false, seqform,
- &changed_seqform, &newseqdata, &owned_by, &is_restart);
+ init_params(pstate, stmt->options, stmt->for_identity, false,
+ seqform, newdataform,
- &need_seq_rewrite, &owned_by);
++ &need_seq_rewrite, &owned_by,
++ &is_restart);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
elm->cached = elm->last;
- /* check the comment above nextval_internal()'s equivalent call. */
- if (RelationNeedsWAL(seqrel))
- GetTopTransactionId();
-
+ /* Now okay to update the on-disk tuple */
+#ifdef PGXC
+ increment = seqform->seqincrement;
+ min_value = seqform->seqmin;
+ max_value = seqform->seqmax;
+ start_value = seqform->seqstart;
+ last_value = elm->last;
+ cycle = seqform->seqcycle;
+#endif
+
- START_CRIT_SECTION();
-
- memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data));
-
- MarkBufferDirty(buf);
-
- /* XLOG stuff */
- if (RelationNeedsWAL(seqrel))
+ /* If needed, rewrite the sequence relation itself */
+ if (need_seq_rewrite)
{
- xl_seq_rec xlrec;
- XLogRecPtr recptr;
- Page page = BufferGetPage(buf);
-
- XLogBeginInsert();
- XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
-
- xlrec.node = seqrel->rd_node;
- XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
-
- XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
+ /* check the comment above nextval_internal()'s equivalent call. */
+ if (RelationNeedsWAL(seqrel))
+ GetTopTransactionId();
- recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
+ /*
+ * Create a new storage file for the sequence, making the state
+ * changes transactional. We want to keep the sequence's relfrozenxid
+ * at 0, since it won't contain any unfrozen XIDs. Same with
+ * relminmxid, since a sequence will never contain multixacts.
+ */
+ RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
+ InvalidTransactionId, InvalidMultiXactId);
- PageSetLSN(page, recptr);
+ /*
+ * Insert the modified tuple into the new storage file.
+ */
+ fill_seq_with_data(seqrel, newdatatuple);
}
- END_CRIT_SECTION();
-
- UnlockReleaseBuffer(buf);
-
/* process OWNED BY if given */
if (owned_by)
process_owned_by(seqrel, owned_by, stmt->for_identity);
ObjectAddressSet(address, RelationRelationId, relid);
- if (changed_seqform)
- CatalogTupleUpdate(rel, &tuple->t_self, tuple);
heap_close(rel, RowExclusiveLock);
-
relation_close(seqrel, NoLock);
+#ifdef PGXC
+ /*
+ * Remote Coordinator is in charge of create sequence in GTM
+ * If sequence is temporary, no need to go through GTM.
+ */
+ if (IS_PGXC_LOCAL_COORDINATOR && seqrel->rd_backend != MyBackendId)
+ {
+ char *seqname = GetGlobalSeqName(seqrel, NULL, NULL);
+
+ /* We also need to create it on the GTM */
+ if (AlterSequenceGTM(seqname,
+ increment,
+ min_value,
+ max_value,
+ start_value,
+ last_value,
+ cycle,
+ is_restart) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CONNECTION_FAILURE),
+ errmsg("GTM error, could not alter sequence")));
+ pfree(seqname);
+ }
+#endif
return address;
}
init_params(ParseState *pstate, List *options, bool for_identity,
bool isInit,
Form_pg_sequence seqform,
- bool *changed_seqform,
Form_pg_sequence_data seqdataform,
- List **owned_by)
+ bool *need_seq_rewrite,
+ List **owned_by,
+ bool *is_restart)
{
DefElem *as_type = NULL;
DefElem *start_value = NULL;
bool reset_max_value = false;
bool reset_min_value = false;
+#ifdef PGXC
+ *is_restart = false;
+#endif
+
+ *need_seq_rewrite = false;
*owned_by = NIL;
foreach(option, options)
-- exercise CREATE SERVER
CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR
-ERROR: foreign-data wrapper "foo" does not exist
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE FOREIGN DATA WRAPPER foo OPTIONS ("test wrapper" 'true');
+ERROR: Postgres-XL does not support FOREIGN DATA WRAPPER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s1 FOREIGN DATA WRAPPER foo;
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s1 FOREIGN DATA WRAPPER foo; -- ERROR
-ERROR: server "s1" already exists
-CREATE SERVER IF NOT EXISTS s1 FOREIGN DATA WRAPPER foo; -- No ERROR, just NOTICE
-NOTICE: server "s1" already exists, skipping
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s2 FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s3 TYPE 'oracle' FOREIGN DATA WRAPPER foo;
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s4 TYPE 'oracle' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s5 VERSION '15.0' FOREIGN DATA WRAPPER foo;
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s6 VERSION '16.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s7 TYPE 'oracle' VERSION '17.0' FOREIGN DATA WRAPPER foo OPTIONS (host 'a', dbname 'b');
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (foo '1'); -- ERROR
-ERROR: invalid option "foo"
-HINT: Valid options in this context are: authtype, service, connect_timeout, dbname, host, hostaddr, port, tty, options, requiressl, sslmode, gsslib
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
CREATE SERVER s8 FOREIGN DATA WRAPPER postgresql OPTIONS (host 'localhost', dbname 's8db');
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
\des+
- List of foreign servers
- Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW options | Description
-------+---------------------------+----------------------+-------------------+--------+---------+-----------------------------------+-------------
- s1 | regress_foreign_data_user | foo | | | | |
- s2 | regress_foreign_data_user | foo | | | | (host 'a', dbname 'b') |
- s3 | regress_foreign_data_user | foo | | oracle | | |
- s4 | regress_foreign_data_user | foo | | oracle | | (host 'a', dbname 'b') |
- s5 | regress_foreign_data_user | foo | | | 15.0 | |
- s6 | regress_foreign_data_user | foo | | | 16.0 | (host 'a', dbname 'b') |
- s7 | regress_foreign_data_user | foo | | oracle | 17.0 | (host 'a', dbname 'b') |
- s8 | regress_foreign_data_user | postgresql | | | | (host 'localhost', dbname 's8db') |
-(8 rows)
-
+ List of foreign servers
+ Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW Options | Description
+------+-------+----------------------+-------------------+------+---------+-------------+-------------
+(0 rows)
-
SET ROLE regress_test_role;
CREATE SERVER t1 FOREIGN DATA WRAPPER foo; -- ERROR: no usage on FDW
-ERROR: permission denied for foreign-data wrapper foo
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
RESET ROLE;
GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role;
+ERROR: foreign-data wrapper "foo" does not exist
SET ROLE regress_test_role;
CREATE SERVER t1 FOREIGN DATA WRAPPER foo;
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
RESET ROLE;
\des+
- List of foreign servers
- Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW options | Description
-------+---------------------------+----------------------+-------------------+--------+---------+-----------------------------------+-------------
- s1 | regress_foreign_data_user | foo | | | | |
- s2 | regress_foreign_data_user | foo | | | | (host 'a', dbname 'b') |
- s3 | regress_foreign_data_user | foo | | oracle | | |
- s4 | regress_foreign_data_user | foo | | oracle | | (host 'a', dbname 'b') |
- s5 | regress_foreign_data_user | foo | | | 15.0 | |
- s6 | regress_foreign_data_user | foo | | | 16.0 | (host 'a', dbname 'b') |
- s7 | regress_foreign_data_user | foo | | oracle | 17.0 | (host 'a', dbname 'b') |
- s8 | regress_foreign_data_user | postgresql | | | | (host 'localhost', dbname 's8db') |
- t1 | regress_test_role | foo | | | | |
-(9 rows)
+ List of foreign servers
+ Name | Owner | Foreign-data wrapper | Access privileges | Type | Version | FDW Options | Description
+------+-------+----------------------+-------------------+------+---------+-------------+-------------
+(0 rows)
REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role;
+ERROR: foreign-data wrapper "foo" does not exist
GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_indirect;
+ERROR: foreign-data wrapper "foo" does not exist
SET ROLE regress_test_role;
CREATE SERVER t2 FOREIGN DATA WRAPPER foo; -- ERROR
-ERROR: permission denied for foreign-data wrapper foo
+ERROR: Postgres-XL does not support SERVER yet
+DETAIL: The feature is not currently supported
RESET ROLE;
GRANT regress_test_indirect TO regress_test_role;
SET ROLE regress_test_role;
(11 rows)
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE f_leak(b);
- QUERY PLAN
----------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Append
- -> Seq Scan on t1
- Filter: f_leak(b)
- -> Seq Scan on t2
- Filter: f_leak(b)
- -> Seq Scan on t3
- Filter: f_leak(b)
-(7 rows)
+ -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Seq Scan on t1
+ Filter: f_leak(b)
+ -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Seq Scan on t2
+ Filter: f_leak(b)
+ -> Remote Subquery Scan on all (datanode_1,datanode_2)
+ -> Seq Scan on t3
+ Filter: f_leak(b)
+(10 rows)
+ --
+ -- Partitioned Tables
+ --
+ SET SESSION AUTHORIZATION regress_rls_alice;
+ CREATE TABLE part_document (
+ did int,
+ cid int,
+ dlevel int not null,
+ dauthor name,
+ dtitle text
+ ) PARTITION BY RANGE (cid);
+ GRANT ALL ON part_document TO public;
+ -- Create partitions for document categories
+ CREATE TABLE part_document_fiction PARTITION OF part_document FOR VALUES FROM (11) to (12);
+ CREATE TABLE part_document_satire PARTITION OF part_document FOR VALUES FROM (55) to (56);
+ CREATE TABLE part_document_nonfiction PARTITION OF part_document FOR VALUES FROM (99) to (100);
+ GRANT ALL ON part_document_fiction TO public;
+ GRANT ALL ON part_document_satire TO public;
+ GRANT ALL ON part_document_nonfiction TO public;
+ INSERT INTO part_document VALUES
+ ( 1, 11, 1, 'regress_rls_bob', 'my first novel'),
+ ( 2, 11, 2, 'regress_rls_bob', 'my second novel'),
+ ( 3, 99, 2, 'regress_rls_bob', 'my science textbook'),
+ ( 4, 55, 1, 'regress_rls_bob', 'my first satire'),
+ ( 5, 99, 2, 'regress_rls_bob', 'my history book'),
+ ( 6, 11, 1, 'regress_rls_carol', 'great science fiction'),
+ ( 7, 99, 2, 'regress_rls_carol', 'great technology book'),
+ ( 8, 55, 2, 'regress_rls_carol', 'great satire'),
+ ( 9, 11, 1, 'regress_rls_dave', 'awesome science fiction'),
+ (10, 99, 2, 'regress_rls_dave', 'awesome technology book');
+ ALTER TABLE part_document ENABLE ROW LEVEL SECURITY;
+ -- Create policy on parent
+ -- user's security level must be higher than or equal to document's
+ CREATE POLICY pp1 ON part_document AS PERMISSIVE
+ USING (dlevel <= (SELECT seclv FROM uaccount WHERE pguser = current_user));
+ -- Dave is only allowed to see cid < 55
+ CREATE POLICY pp1r ON part_document AS RESTRICTIVE TO regress_rls_dave
+ USING (cid < 55);
+ \d+ part_document
+ Table "regress_rls_schema.part_document"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+ ---------+---------+-----------+----------+---------+----------+--------------+-------------
+ did | integer | | | | plain | |
+ cid | integer | | | | plain | |
+ dlevel | integer | | not null | | plain | |
+ dauthor | name | | | | plain | |
+ dtitle | text | | | | extended | |
+ Partition key: RANGE (cid)
+ Policies:
+ POLICY "pp1"
+ USING ((dlevel <= ( SELECT uaccount.seclv
+ FROM uaccount
+ WHERE (uaccount.pguser = CURRENT_USER))))
+ POLICY "pp1r" AS RESTRICTIVE
+ TO regress_rls_dave
+ USING ((cid < 55))
+ Partitions: part_document_fiction FOR VALUES FROM (11) TO (12),
+ part_document_nonfiction FOR VALUES FROM (99) TO (100),
+ part_document_satire FOR VALUES FROM (55) TO (56)
+
+ SELECT * FROM pg_policies WHERE schemaname = 'regress_rls_schema' AND tablename like '%part_document%' ORDER BY policyname;
+ schemaname | tablename | policyname | permissive | roles | cmd | qual | with_check
+ --------------------+---------------+------------+-------------+--------------------+-----+--------------------------------------------+------------
+ regress_rls_schema | part_document | pp1 | PERMISSIVE | {public} | ALL | (dlevel <= ( SELECT uaccount.seclv +|
+ | | | | | | FROM uaccount +|
+ | | | | | | WHERE (uaccount.pguser = CURRENT_USER))) |
+ regress_rls_schema | part_document | pp1r | RESTRICTIVE | {regress_rls_dave} | ALL | (cid < 55) |
+ (2 rows)
+
+ -- viewpoint from regress_rls_bob
+ SET SESSION AUTHORIZATION regress_rls_bob;
+ SET row_security TO ON;
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => awesome science fiction
+ NOTICE: f_leak => my first satire
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ (4 rows)
+
+ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+ QUERY PLAN
+ -----------------------------------------------------
+ Append
+ InitPlan 1 (returns $0)
+ -> Index Scan using uaccount_pkey on uaccount
+ Index Cond: (pguser = CURRENT_USER)
+ -> Seq Scan on part_document_fiction
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ -> Seq Scan on part_document_satire
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ -> Seq Scan on part_document_nonfiction
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ (10 rows)
+
+ -- viewpoint from regress_rls_carol
+ SET SESSION AUTHORIZATION regress_rls_carol;
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => my second novel
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => awesome science fiction
+ NOTICE: f_leak => my first satire
+ NOTICE: f_leak => great satire
+ NOTICE: f_leak => my science textbook
+ NOTICE: f_leak => my history book
+ NOTICE: f_leak => great technology book
+ NOTICE: f_leak => awesome technology book
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 3 | 99 | 2 | regress_rls_bob | my science textbook
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 5 | 99 | 2 | regress_rls_bob | my history book
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 7 | 99 | 2 | regress_rls_carol | great technology book
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ 10 | 99 | 2 | regress_rls_dave | awesome technology book
+ (10 rows)
+
+ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+ QUERY PLAN
+ -----------------------------------------------------
+ Append
+ InitPlan 1 (returns $0)
+ -> Index Scan using uaccount_pkey on uaccount
+ Index Cond: (pguser = CURRENT_USER)
+ -> Seq Scan on part_document_fiction
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ -> Seq Scan on part_document_satire
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ -> Seq Scan on part_document_nonfiction
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ (10 rows)
+
+ -- viewpoint from regress_rls_dave
+ SET SESSION AUTHORIZATION regress_rls_dave;
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => my second novel
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => awesome science fiction
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ (4 rows)
+
+ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+ QUERY PLAN
+ --------------------------------------------------------------------
+ Append
+ InitPlan 1 (returns $0)
+ -> Index Scan using uaccount_pkey on uaccount
+ Index Cond: (pguser = CURRENT_USER)
+ -> Seq Scan on part_document_fiction
+ Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle))
+ (6 rows)
+
+ -- pp1 ERROR
+ INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_dave', 'testing pp1'); -- fail
+ ERROR: new row violates row-level security policy for table "part_document"
+ -- pp1r ERROR
+ INSERT INTO part_document VALUES (100, 99, 1, 'regress_rls_dave', 'testing pp1r'); -- fail
+ ERROR: new row violates row-level security policy "pp1r" for table "part_document"
+ -- Show that RLS policy does not apply for direct inserts to children
+ -- This should fail with RLS POLICY pp1r violation.
+ INSERT INTO part_document VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail
+ ERROR: new row violates row-level security policy "pp1r" for table "part_document"
+ -- But this should succeed.
+ INSERT INTO part_document_satire VALUES (100, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- success
+ -- We still cannot see the row using the parent
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => my second novel
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => awesome science fiction
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ (4 rows)
+
+ -- But we can if we look directly
+ SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first satire
+ NOTICE: f_leak => great satire
+ NOTICE: f_leak => testing RLS with partitions
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (3 rows)
+
+ -- Turn on RLS and create policy on child to show RLS is checked before constraints
+ SET SESSION AUTHORIZATION regress_rls_alice;
+ ALTER TABLE part_document_satire ENABLE ROW LEVEL SECURITY;
+ CREATE POLICY pp3 ON part_document_satire AS RESTRICTIVE
+ USING (cid < 55);
+ -- This should fail with RLS violation now.
+ SET SESSION AUTHORIZATION regress_rls_dave;
+ INSERT INTO part_document_satire VALUES (101, 55, 1, 'regress_rls_dave', 'testing RLS with partitions'); -- fail
+ ERROR: new row violates row-level security policy for table "part_document_satire"
+ -- And now we cannot see directly into the partition either, due to RLS
+ SELECT * FROM part_document_satire WHERE f_leak(dtitle) ORDER BY did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+---------+--------
+ (0 rows)
+
+ -- The parent looks same as before
+ -- viewpoint from regress_rls_dave
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => my second novel
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => awesome science fiction
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ (4 rows)
+
+ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+ QUERY PLAN
+ --------------------------------------------------------------------
+ Append
+ InitPlan 1 (returns $0)
+ -> Index Scan using uaccount_pkey on uaccount
+ Index Cond: (pguser = CURRENT_USER)
+ -> Seq Scan on part_document_fiction
+ Filter: ((cid < 55) AND (dlevel <= $0) AND f_leak(dtitle))
+ (6 rows)
+
+ -- viewpoint from regress_rls_carol
+ SET SESSION AUTHORIZATION regress_rls_carol;
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => my second novel
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => awesome science fiction
+ NOTICE: f_leak => my first satire
+ NOTICE: f_leak => great satire
+ NOTICE: f_leak => testing RLS with partitions
+ NOTICE: f_leak => my science textbook
+ NOTICE: f_leak => my history book
+ NOTICE: f_leak => great technology book
+ NOTICE: f_leak => awesome technology book
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 3 | 99 | 2 | regress_rls_bob | my science textbook
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 5 | 99 | 2 | regress_rls_bob | my history book
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 7 | 99 | 2 | regress_rls_carol | great technology book
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ 10 | 99 | 2 | regress_rls_dave | awesome technology book
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (11 rows)
+
+ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+ QUERY PLAN
+ -----------------------------------------------------
+ Append
+ InitPlan 1 (returns $0)
+ -> Index Scan using uaccount_pkey on uaccount
+ Index Cond: (pguser = CURRENT_USER)
+ -> Seq Scan on part_document_fiction
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ -> Seq Scan on part_document_satire
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ -> Seq Scan on part_document_nonfiction
+ Filter: ((dlevel <= $0) AND f_leak(dtitle))
+ (10 rows)
+
+ -- only owner can change policies
+ ALTER POLICY pp1 ON part_document USING (true); --fail
+ ERROR: must be owner of relation part_document
+ DROP POLICY pp1 ON part_document; --fail
+ ERROR: must be owner of relation part_document
+ SET SESSION AUTHORIZATION regress_rls_alice;
+ ALTER POLICY pp1 ON part_document USING (dauthor = current_user);
+ -- viewpoint from regress_rls_bob again
+ SET SESSION AUTHORIZATION regress_rls_bob;
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => my first novel
+ NOTICE: f_leak => my second novel
+ NOTICE: f_leak => my first satire
+ NOTICE: f_leak => my science textbook
+ NOTICE: f_leak => my history book
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-----------------+---------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 3 | 99 | 2 | regress_rls_bob | my science textbook
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 5 | 99 | 2 | regress_rls_bob | my history book
+ (5 rows)
+
+ -- viewpoint from rls_regres_carol again
+ SET SESSION AUTHORIZATION regress_rls_carol;
+ SELECT * FROM part_document WHERE f_leak(dtitle) ORDER BY did;
+ NOTICE: f_leak => great science fiction
+ NOTICE: f_leak => great satire
+ NOTICE: f_leak => great technology book
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 7 | 99 | 2 | regress_rls_carol | great technology book
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ (3 rows)
+
+ EXPLAIN (COSTS OFF) SELECT * FROM part_document WHERE f_leak(dtitle);
+ QUERY PLAN
+ ---------------------------------------------------------------
+ Append
+ -> Seq Scan on part_document_fiction
+ Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+ -> Seq Scan on part_document_satire
+ Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+ -> Seq Scan on part_document_nonfiction
+ Filter: ((dauthor = CURRENT_USER) AND f_leak(dtitle))
+ (7 rows)
+
+ -- database superuser does bypass RLS policy when enabled
+ RESET SESSION AUTHORIZATION;
+ SET row_security TO ON;
+ SELECT * FROM part_document ORDER BY did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 3 | 99 | 2 | regress_rls_bob | my science textbook
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 5 | 99 | 2 | regress_rls_bob | my history book
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 7 | 99 | 2 | regress_rls_carol | great technology book
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ 10 | 99 | 2 | regress_rls_dave | awesome technology book
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (11 rows)
+
+ SELECT * FROM part_document_satire ORDER by did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (3 rows)
+
+ -- database non-superuser with bypass privilege can bypass RLS policy when disabled
+ SET SESSION AUTHORIZATION regress_rls_exempt_user;
+ SET row_security TO OFF;
+ SELECT * FROM part_document ORDER BY did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 3 | 99 | 2 | regress_rls_bob | my science textbook
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 5 | 99 | 2 | regress_rls_bob | my history book
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 7 | 99 | 2 | regress_rls_carol | great technology book
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ 10 | 99 | 2 | regress_rls_dave | awesome technology book
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (11 rows)
+
+ SELECT * FROM part_document_satire ORDER by did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (3 rows)
+
+ -- RLS policy does not apply to table owner when RLS enabled.
+ SET SESSION AUTHORIZATION regress_rls_alice;
+ SET row_security TO ON;
+ SELECT * FROM part_document ORDER by did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 1 | 11 | 1 | regress_rls_bob | my first novel
+ 2 | 11 | 2 | regress_rls_bob | my second novel
+ 3 | 99 | 2 | regress_rls_bob | my science textbook
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 5 | 99 | 2 | regress_rls_bob | my history book
+ 6 | 11 | 1 | regress_rls_carol | great science fiction
+ 7 | 99 | 2 | regress_rls_carol | great technology book
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 9 | 11 | 1 | regress_rls_dave | awesome science fiction
+ 10 | 99 | 2 | regress_rls_dave | awesome technology book
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (11 rows)
+
+ SELECT * FROM part_document_satire ORDER by did;
+ did | cid | dlevel | dauthor | dtitle
+ -----+-----+--------+-------------------+-----------------------------
+ 4 | 55 | 1 | regress_rls_bob | my first satire
+ 8 | 55 | 2 | regress_rls_carol | great satire
+ 100 | 55 | 1 | regress_rls_dave | testing RLS with partitions
+ (3 rows)
+
+ -- When RLS disabled, other users get ERROR.
+ SET SESSION AUTHORIZATION regress_rls_dave;
+ SET row_security TO OFF;
+ SELECT * FROM part_document ORDER by did;
+ ERROR: query would be affected by row-level security policy for table "part_document"
+ SELECT * FROM part_document_satire ORDER by did;
+ ERROR: query would be affected by row-level security policy for table "part_document_satire"
+ -- Check behavior with a policy that uses a SubPlan not an InitPlan.
+ SET SESSION AUTHORIZATION regress_rls_alice;
+ SET row_security TO ON;
+ CREATE POLICY pp3 ON part_document AS RESTRICTIVE
+ USING ((SELECT dlevel <= seclv FROM uaccount WHERE pguser = current_user));
+ SET SESSION AUTHORIZATION regress_rls_carol;
+ INSERT INTO part_document VALUES (100, 11, 5, 'regress_rls_carol', 'testing pp3'); -- fail
+ ERROR: new row violates row-level security policy "pp3" for table "part_document"
----- Dependencies -----
SET SESSION AUTHORIZATION regress_rls_alice;
SET row_security TO ON;