Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 647a86e

Browse files
committed
Fix RLS with COPY (col1, col2) FROM tab
Attempting to COPY a subset of columns from a table with RLS enabled would fail due to an invalid query being constructed (using a single ColumnRef with the list of fields to exact in 'fields', but that's for the different levels of an indirection for a single column, not for specifying multiple columns). Correct by building a ColumnRef and then RestTarget for each column being requested and then adding those to the targetList for the select query. Include regression tests to hopefully catch if this is broken again in the future. Patch-By: Adam Brightwell Reviewed-By: Michael Paquier
1 parent 0f259bd commit 647a86e

File tree

3 files changed

+192
-12
lines changed

3 files changed

+192
-12
lines changed

src/backend/commands/copy.c

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -873,28 +873,67 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
873873
ColumnRef *cr;
874874
ResTarget *target;
875875
RangeVar *from;
876+
List *targetList = NIL;
876877

877878
if (is_from)
878879
ereport(ERROR,
879880
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
880881
errmsg("COPY FROM not supported with row-level security"),
881882
errhint("Use INSERT statements instead.")));
882883

883-
/* Build target list */
884-
cr = makeNode(ColumnRef);
885-
884+
/*
885+
* Build target list
886+
*
887+
* If no columns are specified in the attribute list of the COPY
888+
* command, then the target list is 'all' columns. Therefore, '*'
889+
* should be used as the target list for the resulting SELECT
890+
* statement.
891+
*
892+
* In the case that columns are specified in the attribute list,
893+
* create a ColumnRef and ResTarget for each column and add them to
894+
* the target list for the resulting SELECT statement.
895+
*/
886896
if (!stmt->attlist)
897+
{
898+
cr = makeNode(ColumnRef);
887899
cr->fields = list_make1(makeNode(A_Star));
888-
else
889-
cr->fields = stmt->attlist;
900+
cr->location = -1;
901+
902+
target = makeNode(ResTarget);
903+
target->name = NULL;
904+
target->indirection = NIL;
905+
target->val = (Node *) cr;
906+
target->location = -1;
890907

891-
cr->location = 1;
908+
targetList = list_make1(target);
909+
}
910+
else
911+
{
912+
ListCell *lc;
892913

893-
target = makeNode(ResTarget);
894-
target->name = NULL;
895-
target->indirection = NIL;
896-
target->val = (Node *) cr;
897-
target->location = 1;
914+
foreach(lc, stmt->attlist)
915+
{
916+
/*
917+
* Build the ColumnRef for each column. The ColumnRef
918+
* 'fields' property is a String 'Value' node (see
919+
* nodes/value.h) that corresponds to the column name
920+
* respectively.
921+
*/
922+
cr = makeNode(ColumnRef);
923+
cr->fields = list_make1(lfirst(lc));
924+
cr->location = -1;
925+
926+
/* Build the ResTarget and add the ColumnRef to it. */
927+
target = makeNode(ResTarget);
928+
target->name = NULL;
929+
target->indirection = NIL;
930+
target->val = (Node *) cr;
931+
target->location = -1;
932+
933+
/* Add each column to the SELECT statement's target list */
934+
targetList = lappend(targetList, target);
935+
}
936+
}
898937

899938
/*
900939
* Build RangeVar for from clause, fully qualified based on the
@@ -905,7 +944,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
905944

906945
/* Build query */
907946
select = makeNode(SelectStmt);
908-
select->targetList = list_make1(target);
947+
select->targetList = targetList;
909948
select->fromClause = list_make1(from);
910949

911950
query = (Node *) select;

src/test/regress/expected/copy2.out

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,9 +463,87 @@ select * from check_con_tbl;
463463

464464
(2 rows)
465465

466+
-- test with RLS enabled.
467+
CREATE ROLE regress_rls_copy_user;
468+
CREATE ROLE regress_rls_copy_user_colperms;
469+
CREATE TABLE rls_t1 (a int, b int, c int);
470+
COPY rls_t1 (a, b, c) from stdin;
471+
CREATE POLICY p1 ON rls_t1 FOR SELECT USING (a % 2 = 0);
472+
ALTER TABLE rls_t1 ENABLE ROW LEVEL SECURITY;
473+
ALTER TABLE rls_t1 FORCE ROW LEVEL SECURITY;
474+
GRANT SELECT ON TABLE rls_t1 TO regress_rls_copy_user;
475+
GRANT SELECT (a, b) ON TABLE rls_t1 TO regress_rls_copy_user_colperms;
476+
-- all columns
477+
COPY rls_t1 TO stdout;
478+
1 4 1
479+
2 3 2
480+
3 2 3
481+
4 1 4
482+
COPY rls_t1 (a, b, c) TO stdout;
483+
1 4 1
484+
2 3 2
485+
3 2 3
486+
4 1 4
487+
-- subset of columns
488+
COPY rls_t1 (a) TO stdout;
489+
1
490+
2
491+
3
492+
4
493+
COPY rls_t1 (a, b) TO stdout;
494+
1 4
495+
2 3
496+
3 2
497+
4 1
498+
-- column reordering
499+
COPY rls_t1 (b, a) TO stdout;
500+
4 1
501+
3 2
502+
2 3
503+
1 4
504+
SET SESSION AUTHORIZATION regress_rls_copy_user;
505+
-- all columns
506+
COPY rls_t1 TO stdout;
507+
2 3 2
508+
4 1 4
509+
COPY rls_t1 (a, b, c) TO stdout;
510+
2 3 2
511+
4 1 4
512+
-- subset of columns
513+
COPY rls_t1 (a) TO stdout;
514+
2
515+
4
516+
COPY rls_t1 (a, b) TO stdout;
517+
2 3
518+
4 1
519+
-- column reordering
520+
COPY rls_t1 (b, a) TO stdout;
521+
3 2
522+
1 4
523+
RESET SESSION AUTHORIZATION;
524+
SET SESSION AUTHORIZATION regress_rls_copy_user_colperms;
525+
-- attempt all columns (should fail)
526+
COPY rls_t1 TO stdout;
527+
ERROR: permission denied for relation rls_t1
528+
COPY rls_t1 (a, b, c) TO stdout;
529+
ERROR: permission denied for relation rls_t1
530+
-- try to copy column with no privileges (should fail)
531+
COPY rls_t1 (c) TO stdout;
532+
ERROR: permission denied for relation rls_t1
533+
-- subset of columns (should succeed)
534+
COPY rls_t1 (a) TO stdout;
535+
2
536+
4
537+
COPY rls_t1 (a, b) TO stdout;
538+
2 3
539+
4 1
540+
RESET SESSION AUTHORIZATION;
466541
DROP TABLE forcetest;
467542
DROP TABLE vistest;
468543
DROP FUNCTION truncate_in_subxact();
469544
DROP TABLE x, y;
545+
DROP TABLE rls_t1 CASCADE;
546+
DROP ROLE regress_rls_copy_user;
547+
DROP ROLE regress_rls_copy_user_colperms;
470548
DROP FUNCTION fn_x_before();
471549
DROP FUNCTION fn_x_after();

src/test/regress/sql/copy2.sql

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,72 @@ copy check_con_tbl from stdin;
327327
\.
328328
select * from check_con_tbl;
329329

330+
-- test with RLS enabled.
331+
CREATE ROLE regress_rls_copy_user;
332+
CREATE ROLE regress_rls_copy_user_colperms;
333+
CREATE TABLE rls_t1 (a int, b int, c int);
334+
335+
COPY rls_t1 (a, b, c) from stdin;
336+
1 4 1
337+
2 3 2
338+
3 2 3
339+
4 1 4
340+
\.
341+
342+
CREATE POLICY p1 ON rls_t1 FOR SELECT USING (a % 2 = 0);
343+
ALTER TABLE rls_t1 ENABLE ROW LEVEL SECURITY;
344+
ALTER TABLE rls_t1 FORCE ROW LEVEL SECURITY;
345+
346+
GRANT SELECT ON TABLE rls_t1 TO regress_rls_copy_user;
347+
GRANT SELECT (a, b) ON TABLE rls_t1 TO regress_rls_copy_user_colperms;
348+
349+
-- all columns
350+
COPY rls_t1 TO stdout;
351+
COPY rls_t1 (a, b, c) TO stdout;
352+
353+
-- subset of columns
354+
COPY rls_t1 (a) TO stdout;
355+
COPY rls_t1 (a, b) TO stdout;
356+
357+
-- column reordering
358+
COPY rls_t1 (b, a) TO stdout;
359+
360+
SET SESSION AUTHORIZATION regress_rls_copy_user;
361+
362+
-- all columns
363+
COPY rls_t1 TO stdout;
364+
COPY rls_t1 (a, b, c) TO stdout;
365+
366+
-- subset of columns
367+
COPY rls_t1 (a) TO stdout;
368+
COPY rls_t1 (a, b) TO stdout;
369+
370+
-- column reordering
371+
COPY rls_t1 (b, a) TO stdout;
372+
373+
RESET SESSION AUTHORIZATION;
374+
375+
SET SESSION AUTHORIZATION regress_rls_copy_user_colperms;
376+
377+
-- attempt all columns (should fail)
378+
COPY rls_t1 TO stdout;
379+
COPY rls_t1 (a, b, c) TO stdout;
380+
381+
-- try to copy column with no privileges (should fail)
382+
COPY rls_t1 (c) TO stdout;
383+
384+
-- subset of columns (should succeed)
385+
COPY rls_t1 (a) TO stdout;
386+
COPY rls_t1 (a, b) TO stdout;
387+
388+
RESET SESSION AUTHORIZATION;
389+
330390
DROP TABLE forcetest;
331391
DROP TABLE vistest;
332392
DROP FUNCTION truncate_in_subxact();
333393
DROP TABLE x, y;
394+
DROP TABLE rls_t1 CASCADE;
395+
DROP ROLE regress_rls_copy_user;
396+
DROP ROLE regress_rls_copy_user_colperms;
334397
DROP FUNCTION fn_x_before();
335398
DROP FUNCTION fn_x_after();

0 commit comments

Comments
 (0)