diff --git a/test/check_ledger.tsv b/test/check_ledger.tsv index 8dfd9fe22..5452efc59 100644 --- a/test/check_ledger.tsv +++ b/test/check_ledger.tsv @@ -2220,6 +2220,34 @@ projection_parallel projection_parallel premise: the table holds every inserted projection_parallel projection_parallel the parallel count is a parallel covering projection scan 15;16;17;18;19 never - projection_parallel projection_parallel the workers' rows add up to the covering rows, each returned once 15;16;17;18;19 2026-10-13 a parallel worker never wires the shared row-group counter, so every worker scans every group projection_parallel projection_parallel workers share the covering projection scan, it is not a single claimer 15;16;17;18;19 2026-10-13 first-wins-claimer;only the first parallel worker claims row groups, so one worker scans the whole table (#1336) +projection_privilege projection_privilege a caller with no SELECT is told a privilege is missing, not that RLS is in force 15;16;17;18;19 never - +projection_privilege projection_privilege a role WITH SELECT still reads the projection 15;16;17;18;19 2026-10-14 read_projection stores no rows: its tuplestore_putvalues call removed, so every caller gets zero rows +projection_privilege projection_privilege a role with EXECUTE but no SELECT is refused read_projection 15;16;17;18;19 never - +projection_privilege projection_privilege a role with only schema USAGE is refused read_projection 15;16;17;18;19 never - +projection_privilege projection_privilege and THAT refusal is the C check, which raises 42501 from aclcheck_error 15;16;17;18;19 never - +projection_privilege projection_privilege and is refused reconstruct_via_projection 15;16;17;18;19 never - +projection_privilege projection_privilege and is refused reconstruct_via_projection, which leaks non-covered columns 15;16;17;18;19 never - +projection_privilege projection_privilege and read_projection does NOT, so the two differ as the issue says 15;16;17;18;19 2026-10-14 read_projection bound to the reconstruct_via_projection C symbol in the extension SQL, so it returns the non-covered columns too;read_projection stores no rows: its tuplestore_putvalues call removed, so every caller gets zero rows +projection_privilege projection_privilege and so does reconstruct_via_projection 15;16;17;18;19 never - +projection_privilege projection_privilege and still reconstructs 15;16;17;18;19 2026-10-14 reconstruct_via_projection stores no rows: its tuplestore_putvalues call removed, so every caller gets zero rows +projection_privilege projection_privilege and the refusal says the feature is not supported, not that a privilege is missing 15;16;17;18;19 never - +projection_privilege projection_privilege premise: t_prjexec can open a session, so a refusal below is a refusal 15;16;17;18;19 never - +projection_privilege projection_privilege premise: t_prjexec really does hold EXECUTE, so it reaches the C gate 15;16;17;18;19 never - +projection_privilege projection_privilege premise: t_prjsel CAN read the table, so its arm below tests the bar 15;16;17;18;19 never - +projection_privilege projection_privilege premise: the test roles are not superusers 15;16;17;18;19 never - +projection_privilege projection_privilege premise: the unprivileged role cannot read the table by any ordinary route 15;16;17;18;19 never - +projection_privilege projection_privilege read_projection now refuses a policy-restricted caller (#563) 15;16;17;18;19 never - +projection_privilege projection_privilege reconstruct really does return the NON-COVERED column 15;16;17;18;19 2026-10-14 reconstruct_via_projection renders every column the projection does not cover as NULL instead of the base value;reconstruct_via_projection stores no rows: its tuplestore_putvalues call removed, so every caller gets zero rows +projection_privilege projection_privilege reconstruct: a caller with no SELECT is told a privilege is missing, not that RLS is in force 15;16;17;18;19 never - +projection_privilege projection_privilege reconstruct: the EXECUTE role reaches the projection lookup on the same call 15;16;17;18;19 never - +projection_privilege projection_privilege reconstruct: the base ACL is checked before the projection name is looked up 15;16;17;18;19 never - +projection_privilege projection_privilege reconstruct: the no-EXECUTE role never reaches the body, so the grant is what stopped it 15;16;17;18;19 never - +projection_privilege projection_privilege the base ACL is checked before the projection name is looked up 15;16;17;18;19 never - +projection_privilege projection_privilege the no-EXECUTE role never reaches the body, so the grant is what stopped it 15;16;17;18;19 never - +projection_privilege projection_privilege the owner reads the projection, and gets rows 15;16;17;18;19 2026-10-14 read_projection stores no rows: its tuplestore_putvalues call removed, so every caller gets zero rows +projection_privilege projection_privilege the owner reconstructs, and gets rows 15;16;17;18;19 2026-10-14 reconstruct_via_projection stores no rows: its tuplestore_putvalues call removed, so every caller gets zero rows +projection_privilege projection_privilege the policy is in force for ordinary SQL 15;16;17;18;19 2026-10-14 the columnar custom scan drops its restriction quals (plan qual set to NIL), so the row-level-security policy qual is not applied +projection_privilege projection_privilege while the EXECUTE role reaches the projection lookup on the same call 15;16;17;18;19 never - projection_scan_cost projection_scan_cost a clause that mentions the sort key but cannot prune on it does not cheapen a covering projection 15;16;17;18;19 2026-10-12 a covering projection is discounted for any clause that mentions the sort key, as before #1126 projection_scan_cost projection_scan_cost a non-sort-key restriction does not cheapen a covering projection 15;16;17;18;19 2026-09-18 - projection_scan_cost projection_scan_cost a tight covering projection is cheaper relative to the base than a loose one 15;16;17;18;19 2026-10-12 no restriction ever earns the sort-key discount diff --git a/test/check_ledger_budget.txt b/test/check_ledger_budget.txt index c130dd4e3..1f2808e78 100644 --- a/test/check_ledger_budget.txt +++ b/test/check_ledger_budget.txt @@ -15,7 +15,7 @@ # # Adding a check to a suite that is already covered does not move it, which is # what makes it safe to bound. -suites_not_covered 238 +suites_not_covered 237 # # checks_never_observed_red -- A CENSUS. NOT a ceiling, and it must not become # one. @@ -2114,4 +2114,27 @@ suites_not_covered 238 # never 1986 + 35 + 9 - 10 = 2020 # dated 598 + 10 = 608 # 2020 + 608 == 2628 -checks_never_observed_red 2020 +# +# #1339, five projection_privilege checks were premises about product code +# (jdatcmd): 2020 -> 2041, and suites_not_covered 238 -> 237. Rule #1236: a +# `premise:` check must be one no repository change can turn red. These five test +# what read_projection, reconstruct_via_projection and the columnar scan return, so +# a product change can redden them. They drop the prefix in both twins, with the +# rest of each name unchanged (#1356 had already renamed the owner-read arm). The +# suite had no rows. It enters with all 28 checks from five-major runs (28/28 on +# each of 15-19). Seven are dated on PG18 by four +# mutations of the extension and one of its SQL file: +# - read_projection stores no rows: the owner arm, the "does NOT" arm and the +# SELECT-holder's read arm go red; +# - reconstruct_via_projection stores no rows: the owner arm, the NON-COVERED arm +# and "and still reconstructs" go red; +# - reconstruct renders every non-covered column as NULL: the NON-COVERED arm; +# - read_projection bound to the reconstruct C symbol: the "does NOT" arm; +# - the columnar custom scan drops its quals: "the policy is in force". +# The other 21 arrive never. +# +# ledger rows 2628 + 28 = 2656 +# never 2020 + 21 = 2041 +# dated 608 + 7 = 615 +# 2041 + 615 == 2656 +checks_never_observed_red 2041 diff --git a/test/projection_privilege.sh b/test/projection_privilege.sh index b2966ceb9..579903195 100755 --- a/test/projection_privilege.sh +++ b/test/projection_privilege.sh @@ -122,16 +122,16 @@ state_as() { # state_as # function that does nothing. check_num "the owner reads the projection, and gets rows" \ "$(q "SELECT count(*) FROM pgcolumnar.read_projection('secret','p1');")" "$ROWS" -check_num "premise: the owner reconstructs, and gets rows" \ +check_num "the owner reconstructs, and gets rows" \ "$(q "SELECT count(*) FROM pgcolumnar.reconstruct_via_projection('secret','p1');")" "$ROWS" # Both functions RETURN SETOF text: one row is a single pipe-joined string, not # a record. A column definition list is rejected outright, which is how an # earlier probe of these functions produced five arms that all looked like # refusals and were actually call errors. Match the text. -check "premise: reconstruct really does return the NON-COVERED column" \ +check "reconstruct really does return the NON-COVERED column" \ "$(q "SELECT pgcolumnar.reconstruct_via_projection('secret','p1') LIKE '%note-%' LIMIT 1;")" \ "t" -check "premise: and read_projection does NOT, so the two differ as the issue says" \ +check "and read_projection does NOT, so the two differ as the issue says" \ "$(q "SELECT pgcolumnar.read_projection('secret','p1') LIKE '%note-%' LIMIT 1;")" \ "f" check "premise: the test roles are not superusers" \ @@ -236,7 +236,7 @@ check "and still reconstructs" \ psql_run "ALTER TABLE secret ENABLE ROW LEVEL SECURITY;" psql_run "CREATE POLICY p_one ON secret FOR SELECT TO t_prjsel USING (id = 1);" -check_num "premise: the policy is in force for ordinary SQL" \ +check_num "the policy is in force for ordinary SQL" \ "$(as t_prjsel 'SELECT count(*) FROM secret;' | head -1)" "1" check "read_projection now refuses a policy-restricted caller (#563)" \ "$(count_as t_prjsel read_projection)" "refused" diff --git a/test/pytest/test_projection_privilege.py b/test/pytest/test_projection_privilege.py index 564e0fdf9..40e8b986c 100644 --- a/test/pytest/test_projection_privilege.py +++ b/test/pytest/test_projection_privilege.py @@ -190,18 +190,18 @@ def test_the_premises_the_fixture_is_what_the_suite_assumes(pgc_cluster, pgc_con cur.execute(f"SELECT count(*) FROM pgcolumnar.{f}('prj_secret','p1')") expect.num(cur.fetchone()[0], ROWS, "the owner reads the projection, and gets rows" if f == "read_projection" - else "premise: the owner reconstructs, and gets rows") + else "the owner reconstructs, and gets rows") cur.execute( "SELECT pgcolumnar.reconstruct_via_projection('prj_secret','p1') " "LIKE '%note-%' LIMIT 1" ) expect.text(str(cur.fetchone()[0]), "True", - "premise: reconstruct really does return the NON-COVERED column") + "reconstruct really does return the NON-COVERED column") cur.execute( "SELECT pgcolumnar.read_projection('prj_secret','p1') LIKE '%note-%' LIMIT 1" ) expect.text(str(cur.fetchone()[0]), "False", - "premise: and read_projection does NOT, so the two differ as the issue says") + "and read_projection does NOT, so the two differ as the issue says") cur.execute( "SELECT count(*) FROM pg_roles WHERE rolname = ANY(%s) AND rolsuper", (list(ROLES),) ) @@ -357,7 +357,7 @@ def test_a_policy_restricted_caller_is_refused(pgc_cluster, pgc_conn, expect, fu `PgColumnarRequireNoRowSecurity` raises ERRCODE_FEATURE_NOT_SUPPORTED, so this arm asserts 0A000 where the shell suite greps `^ERROR:.*row-level security`. The codes are from different SQLSTATE CLASSES -- 0A and 42 -- so this cannot be confused with either - ACL refusal by any amount of rewording, and the premise below shows the policy really + ACL refusal by any amount of rewording, and the check below shows the policy really took effect rather than the call having broken for some other reason. """ with pgc_conn.cursor() as cur: @@ -368,7 +368,7 @@ def test_a_policy_restricted_caller_is_refused(pgc_cluster, pgc_conn, expect, fu rows, err = _as(pgc_cluster, SEL, "SELECT count(*) FROM prj_secret", schema) assert err is None, f"{SEL} could not read the table under the policy: {err}" - expect.num(rows[0][0], 1, "premise: the policy is in force for ordinary SQL") + expect.num(rows[0][0], 1, "the policy is in force for ordinary SQL") _, err = _as(pgc_cluster, SEL, f"SELECT count(*) FROM pgcolumnar.{func}('prj_secret','p1')", schema)