Fix Assert failure for MERGE into a partitioned table with RLS.

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
This commit is contained in:
Dean Rasheed 2023-02-22 10:51:34 +00:00
parent 83a611a259
commit 0d3b49d4af
3 changed files with 31 additions and 3 deletions

View 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) ||

View 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;

View 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;