@@ -1457,6 +1457,241 @@ SET ROLE root
14571457statement ok
14581458DROP TABLE force_check CASCADE;
14591459
1460+ # Test to make sure that the scan to enforce uniqueness is done without RLS policies.
1461+ subtest uniq
1462+
1463+ statement ok
1464+ CREATE TABLE uniq (rls_col TEXT, uniq_col INT8 UNIQUE);
1465+
1466+ statement ok
1467+ CREATE USER uniq_user;
1468+
1469+ statement ok
1470+ GRANT ALL ON uniq TO uniq_user;
1471+
1472+ statement ok
1473+ ALTER TABLE uniq OWNER TO uniq_user;
1474+
1475+ statement ok
1476+ SET ROLE uniq_user;
1477+
1478+ statement ok
1479+ ALTER TABLE uniq NO FORCE ROW LEVEL SECURITY, ENABLE ROW LEVEL SECURITY;
1480+
1481+ statement ok
1482+ CREATE POLICY access ON uniq USING (rls_col = 'cat');
1483+
1484+ statement ok
1485+ INSERT INTO uniq VALUES ('cat', 1), ('cat', 2), ('dog', 3), ('cat', 4), ('hamster', 5);
1486+
1487+ statement ok
1488+ ALTER TABLE uniq FORCE ROW LEVEL SECURITY;
1489+
1490+ statement error pq: duplicate key value violates unique constraint "uniq_uniq_col_key"\nDETAIL: Key \(uniq_col\)=\(3\) already exists.
1491+ INSERT INTO uniq VALUES ('cat', 3);
1492+
1493+ statement ok
1494+ INSERT INTO uniq VALUES ('cat', 6);
1495+
1496+ statement error pq: new row violates row-level security policy for table "uniq"
1497+ INSERT INTO uniq VALUES ('dog', 6);
1498+
1499+ statement error pq: duplicate key value violates unique constraint "uniq_uniq_col_key"\nDETAIL: Key \(uniq_col\)=\(5\) already exists.
1500+ UPDATE uniq SET uniq_col = 5 WHERE uniq_col = 1;
1501+
1502+ statement ok
1503+ UPDATE uniq SET uniq_col = 7 WHERE uniq_col = 1;
1504+
1505+ # Ensure that any attempts to update an invisible row will be a no-op
1506+ query TI
1507+ UPDATE uniq SET uniq_col = 8 WHERE uniq_col = 5 RETURNING rls_col, uniq_col;
1508+ ----
1509+
1510+ statement ok
1511+ ALTER TABLE uniq NO FORCE ROW LEVEL SECURITY;
1512+
1513+ query TI
1514+ select rls_col, uniq_col FROM uniq ORDER BY uniq_col;
1515+ ----
1516+ cat 2
1517+ dog 3
1518+ cat 4
1519+ hamster 5
1520+ cat 6
1521+ cat 7
1522+
1523+ statement ok
1524+ SET ROLE root;
1525+
1526+ statement ok
1527+ DROP TABLE uniq;
1528+
1529+ statement ok
1530+ DROP USER uniq_user;
1531+
1532+ # Test to make sure that the scan to enforce foreign keys is exempt from RLS policies.
1533+ subtest fk
1534+
1535+ statement ok
1536+ CREATE TABLE parent (key INT8 NOT NULL PRIMARY KEY);
1537+
1538+ statement ok
1539+ CREATE TABLE child (
1540+ rls_col TEXT,
1541+ key INT8 NOT NULL,
1542+ CONSTRAINT fk FOREIGN KEY (key) REFERENCES parent(key) ON DELETE CASCADE
1543+ );
1544+
1545+ statement ok
1546+ CREATE USER fk_user;
1547+
1548+ statement ok
1549+ GRANT ALL ON parent TO fk_user;
1550+
1551+ statement ok
1552+ GRANT ALL ON child TO fk_user;
1553+
1554+ statement ok
1555+ ALTER TABLE parent OWNER TO fk_user;
1556+
1557+ statement ok
1558+ ALTER TABLE child OWNER TO fk_user;
1559+
1560+ statement ok
1561+ SET ROLE fk_user;
1562+
1563+ statement ok
1564+ INSERT INTO parent SELECT * FROM generate_series(1,6);
1565+
1566+ statement ok
1567+ INSERT INTO child VALUES ('bedroom', 1), ('office', 2)
1568+
1569+ # Set RLS at the parent and ensure FK still enforced.
1570+ statement ok
1571+ ALTER TABLE parent ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY;
1572+
1573+ statement error pq: insert on table "child" violates foreign key constraint "fk"
1574+ INSERT INTO child VALUES ('hall', 7);
1575+
1576+ statement ok
1577+ INSERT INTO child VALUES ('hall', 3);
1578+
1579+ # mimic the FK lookup to show that the given RLS policies will hide the value
1580+ query I
1581+ SELECT 1 FROM parent WHERE key = 3;
1582+ ----
1583+
1584+ query I
1585+ SELECT key FROM child ORDER BY key;
1586+ ----
1587+ 1
1588+ 2
1589+ 3
1590+
1591+ # Set RLS at the child and ensure FK still enforced
1592+ statement ok
1593+ ALTER TABLE child ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY;
1594+
1595+ statement ok
1596+ CREATE POLICY ins1 ON child FOR INSERT WITH CHECK (rls_col = 'bedroom');
1597+
1598+ statement error pq: insert on table "child" violates foreign key constraint "fk"
1599+ INSERT INTO child VALUES ('bedroom', 7);
1600+
1601+ statement error pq: new row violates row-level security policy for table "child"
1602+ INSERT INTO child VALUES ('deck', 7);
1603+
1604+ statement ok
1605+ INSERT INTO child VALUES ('bedroom', 4);
1606+
1607+ # Disable RLS at the parent so that we can do a delete and have it cascade down
1608+ statement ok
1609+ ALTER TABLE parent NO FORCE ROW LEVEL SECURITY;
1610+
1611+ statement ok
1612+ DELETE FROM parent WHERE key = 1;
1613+
1614+ statement ok
1615+ CREATE POLICY sel1 ON child FOR SELECT USING (true);
1616+
1617+ query I
1618+ SELECT key FROM child ORDER BY key;
1619+ ----
1620+ 2
1621+ 3
1622+ 4
1623+
1624+ statement ok
1625+ SET ROLE root
1626+
1627+ statement ok
1628+ DROP TABLE child;
1629+
1630+ statement ok
1631+ DROP TABLE parent;
1632+
1633+ statement ok
1634+ DROP USER fk_user;
1635+
1636+ # Ensure CHECK constraints can work alongside RLS policies
1637+ subtest check_constraint
1638+
1639+ statement ok
1640+ CREATE TABLE rgb_only (col text, check (col = 'red' or col = 'green' or col = 'blue'));
1641+
1642+ statement ok
1643+ CREATE USER rgb_only_user;
1644+
1645+ statement ok
1646+ ALTER TABLE rgb_only OWNER TO rgb_only_user;
1647+
1648+ statement ok
1649+ SET ROLE rgb_only_user;
1650+
1651+ statement ok
1652+ ALTER TABLE rgb_only ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY;
1653+
1654+ statement ok
1655+ CREATE POLICY p_sel ON rgb_only FOR SELECT USING (true);
1656+
1657+ statement ok
1658+ CREATE POLICY p_subset ON rgb_only FOR INSERT WITH CHECK (col = 'red' or col = 'brown');
1659+
1660+ statement ok
1661+ INSERT INTO rgb_only VALUES ('red')
1662+
1663+ # Allowed by policy, reject by CHECK constraint
1664+ statement error pq: failed to satisfy CHECK constraint \(\(\(col = 'red':::STRING\) OR \(col = 'green':::STRING\)\) OR \(col = 'blue':::STRING\)\)
1665+ INSERT INTO rgb_only VALUES ('brown')
1666+
1667+ # Disallowed by policy, accepted by CHECK constraint
1668+ statement error pq: new row violates row-level security policy for table "rgb_only"
1669+ INSERT INTO rgb_only VALUES ('green')
1670+
1671+ statement ok
1672+ DROP POLICY p_subset ON rgb_only;
1673+
1674+ statement ok
1675+ CREATE POLICY p_disjoint ON rgb_only FOR INSERT WITH CHECK (col = 'black');
1676+
1677+ # Disallowed by both.
1678+ statement error pq: new row violates row-level security policy for table "rgb_only"
1679+ INSERT INTO rgb_only VALUES ('blue')
1680+
1681+ query T
1682+ SELECT col FROM rgb_only ORDER BY col;
1683+ ----
1684+ red
1685+
1686+ statement ok
1687+ SET ROLE root;
1688+
1689+ statement ok
1690+ DROP TABLE rgb_only;
1691+
1692+ statement ok
1693+ DROP USER rgb_only_user;
1694+
14601695subtest truncate
14611696
14621697statement ok
0 commit comments