Fix Assert failure for MERGE into a partitioned table with RLS.
authorDean Rasheed <dean.a.rasheed@gmail.com>
Wed, 22 Feb 2023 10:51:34 +0000 (10:51 +0000)
committerDean Rasheed <dean.a.rasheed@gmail.com>
Wed, 22 Feb 2023 10:51:34 +0000 (10:51 +0000)
In ExecInitPartitionInfo(), the Assert when building the WITH CHECK
OPTION list for the new partition assumed that the command would be an
INSERT or UPDATE, but it can also be a MERGE. This can be triggered by
a MERGE into a partitioned table with RLS checks to enforce.

Fix, and back-patch to v15, where MERGE was introduced.

Discussion: https://postgr.es/m/CAEZATCWWFtQmW67F3XTyMU5Am10Oxa_b8oe0x%2BNu5Mo%2BCdRErg%40mail.gmail.com

src/backend/executor/execPartition.c
src/test/regress/expected/merge.out
src/test/regress/sql/merge.sql

index 651ad24fc10ac48c3814fe9d98bcd48e87b440fb..fd6ca8a5d9a312e2933a599d7a177209bbef85d1 100644 (file)
@@ -545,8 +545,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
         * Build WITH CHECK OPTION constraints for the partition.  Note that we
         * didn't build the withCheckOptionList for partitions within the planner,
         * but simple translation of varattnos will suffice.  This only occurs for
-        * the INSERT case or in the case of UPDATE tuple routing where we didn't
-        * find a result rel to reuse.
+        * the INSERT case or in the case of UPDATE/MERGE tuple routing where we
+        * didn't find a result rel to reuse.
         */
        if (node && node->withCheckOptionLists != NIL)
        {
@@ -557,12 +557,15 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
                /*
                 * In the case of INSERT on a partitioned table, there is only one
                 * plan.  Likewise, there is only one WCO list, not one per partition.
-                * For UPDATE, there are as many WCO lists as there are plans.
+                * For UPDATE/MERGE, there are as many WCO lists as there are plans.
                 */
                Assert((node->operation == CMD_INSERT &&
                                list_length(node->withCheckOptionLists) == 1 &&
                                list_length(node->resultRelations) == 1) ||
                           (node->operation == CMD_UPDATE &&
+                               list_length(node->withCheckOptionLists) ==
+                               list_length(node->resultRelations)) ||
+                          (node->operation == CMD_MERGE &&
                                list_length(node->withCheckOptionLists) ==
                                list_length(node->resultRelations)));
 
@@ -619,6 +622,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate,
                List       *returningList;
 
                /* See the comment above for WCO lists. */
+               /* (except no RETURNING support for MERGE yet) */
                Assert((node->operation == CMD_INSERT &&
                                list_length(node->returningLists) == 1 &&
                                list_length(node->resultRelations) == 1) ||
index c298bfa314cf135bd1280994d1e212ed685e437d..e1d363966c798e62d6e0c18106009b631136473a 100644 (file)
@@ -1736,6 +1736,18 @@ SELECT * FROM pa_target ORDER BY tid;
   14 |     140 | inserted by merge
 (14 rows)
 
+ROLLBACK;
+-- test RLS enforcement
+BEGIN;
+ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
+MERGE INTO pa_target t
+  USING pa_source s
+  ON t.tid = s.sid AND t.tid IN (1,2,3,4)
+  WHEN MATCHED THEN
+    UPDATE SET tid = tid - 1;
+ERROR:  new row violates row-level security policy for table "pa_target"
 ROLLBACK;
 DROP TABLE pa_source;
 DROP TABLE pa_target CASCADE;
index 9ed648ad1f6e06b3650cff1dafca67a7be30de27..f0854162a792c9bf28b1ced0b35670fde3ec07fe 100644 (file)
@@ -1082,6 +1082,18 @@ MERGE INTO pa_target t
 SELECT * FROM pa_target ORDER BY tid;
 ROLLBACK;
 
+-- test RLS enforcement
+BEGIN;
+ALTER TABLE pa_target ENABLE ROW LEVEL SECURITY;
+ALTER TABLE pa_target FORCE ROW LEVEL SECURITY;
+CREATE POLICY pa_target_pol ON pa_target USING (tid != 0);
+MERGE INTO pa_target t
+  USING pa_source s
+  ON t.tid = s.sid AND t.tid IN (1,2,3,4)
+  WHEN MATCHED THEN
+    UPDATE SET tid = tid - 1;
+ROLLBACK;
+
 DROP TABLE pa_source;
 DROP TABLE pa_target CASCADE;