Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions test/check_ledger.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -2080,6 +2080,7 @@ rollup rollup and after the refresh that ran beside an insert the rollup equals
rollup rollup and after the small append the rollup equals a full recompute 15;16;17;18;19 2026-10-13 refresh_rollup combining min with greatest;refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup folding only the first new group;refresh_rollup recording the folded groups from a fresh read after the fold;row_tid mapping 290 rows a block instead of the AM's
rollup rollup and after two refreshes started together the rollup equals a full recompute 15;16;17;18;19 2026-10-13 refresh_rollup combining min with greatest;refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup folding only the first new group;refresh_rollup recording the folded groups from a fresh read after the fold;row_tid mapping 290 rows a block instead of the AM's
rollup rollup and cannot refresh it 15;16;17;18;19 2026-10-13 the rollup catalogs without row-level security;the rollup catalogs without their grants
rollup rollup and its unqualified UPDATE and DELETE reach none of the owner's catalog rows 15;16;17;18;19 2026-10-14 a permissive UPDATE policy on pgcolumnar.rollup and DELETE policy on rollup_group, USING (true);the rollup catalog policies given WITH CHECK (true)
rollup rollup and leaves the rollup as it was 15;16;17;18;19 2026-10-13 refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup folding only the first new group
rollup rollup and leaves the rollup's rows alone 15;16;17;18;19 2026-10-13 rebase_rollup clearing the rollup's rows
rollup rollup and no refused rollup left a table behind 15;16;17;18;19 2026-10-13 create_rollup allowing * with any function;create_rollup without the equal-length check;create_rollup without the months refusal
Expand All @@ -2092,14 +2093,18 @@ rollup rollup and the refresh after the rewrite recomputes nothing 15;16;17;18;1
rollup rollup and the refresh rewrote no partial outside the changed groups' time range 15;16;17;18;19 2026-10-13 refresh_rollup recomputing every bucket once any group changed
rollup rollup and the rollup no longer records the groups retention retired 15;16;17;18;19 2026-10-13 refresh_rollup keeping retired groups in its record
rollup rollup and the rollup still equals a full recompute 15;16;17;18;19 2026-10-13 refresh_rollup combining min with greatest;refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup folding only the first new group;row_tid mapping 290 rows a block instead of the AM's
rollup rollup another role cannot add a group row to the owner's rollup (42501) 15;16;17;18;19 2026-10-14 the rollup catalog policies given WITH CHECK (true)
rollup rollup another role cannot read the source's groups through rollup_source_groups (42501) 15;16;17;18;19 2026-10-14 rollup_source_groups without its require_caller_select guard
rollup rollup another role sees none of its rollup catalog rows 15;16;17;18;19 2026-10-13 the rollup catalogs without row-level security;the rollup catalogs without their grants
rollup rollup each row group's TID range holds exactly that group's rows 15;16;17;18;19 2026-10-13 row_tid mapping 290 rows a block instead of the AM's
rollup rollup nor its retired groups through rollup_source_retired (42501) 15;16;17;18;19 2026-10-14 rollup_source_retired without its require_caller_select guard
rollup rollup premise: and the fixture has NULL-key and NULL-bucket groups 15;16;17;18;19 never -
rollup rollup premise: compact_rewrite rewrote a row group 15;16;17;18;19 never -
rollup rollup premise: expire retired the group in bucket D alone and kept the one that shares it 15;16;17;18;19 never -
rollup rollup premise: expire retired the group the delete thinned 15;16;17;18;19 never -
rollup rollup premise: expire retired the old row groups 15;16;17;18;19 never -
rollup rollup premise: pgcolumnar.vacuum gave the source's row groups new identities 15;16;17;18;19 never -
rollup rollup premise: the owner's rollup has group rows another role could reach 15;16;17;18;19 never -
rollup rollup premise: the rollup functions exist 15;16;17;18;19 never -
rollup rollup premise: the rollup holds partials the delete below cannot reach 15;16;17;18;19 never -
rollup rollup premise: the source holds every row 15;16;17;18;19 never -
Expand All @@ -2109,6 +2114,7 @@ rollup rollup the first refresh folds every row 15;16;17;18;19 2026-10-13 refres
rollup rollup the history rollup folded every row 15;16;17;18;19 2026-10-13 refresh_rollup folding only the first new group
rollup rollup the history rollup holds old buckets 15;16;17;18;19 never -
rollup rollup the next refresh folds the rows committed during the last one, once 15;16;17;18;19 2026-10-13 refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup folding only the first new group;refresh_rollup recording the folded groups from a fresh read after the fold;row_tid mapping 290 rows a block instead of the AM's
rollup rollup the owner reads its source's groups through the definer helpers 15;16;17;18;19 never -
rollup rollup the paused refresh folded what was there before it began 15;16;17;18;19 2026-10-13 refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup folding only the first new group;row_tid mapping 290 rows a block instead of the AM's
rollup rollup the refresh after a small append read the source at all 15;16;17;18;19 never -
rollup rollup the rollup holds rows before the rewrite 15;16;17;18;19 never -
Expand Down
17 changes: 16 additions & 1 deletion test/check_ledger_budget.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1832,4 +1832,19 @@ suites_not_covered 246
# never 1798 - 2 = 1796
# dated 545 + 2 = 547
# 1796 + 547 == 2343
checks_never_observed_red 1796
#
# #1341, the rollup catalogs' write side and the definer helpers (OffgridwithJD):
# 1796 -> 1798. Six new rollup checks. Four are dated by a mutation of the extension
# script that main's suite survives:
# - each helper's require_caller_select deleted (one arm each);
# - WITH CHECK (true) on the catalog policies (the INSERT arm, and the write arm,
# which counts the inserted row);
# - a permissive UPDATE and DELETE policy USING (true) (the write arm).
# The premise that the owner's rollup has group rows, and the owner's own call
# through the helpers, arrive never. Five-major runs: rollup 78/78 on 15-19.
#
# ledger rows 2343 + 6 = 2349
# never 1796 + 2 = 1798
# dated 547 + 4 = 551
# 1798 + 551 == 2349
checks_never_observed_red 1798
28 changes: 28 additions & 0 deletions test/pytest/test_rollup.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,34 @@ def as_role(role, sql):
_one(conn, f"SELECT string_agg(b || ':' || n || ':' || m, ' ' ORDER BY b) FROM"
f" (SELECT date_bin('1 day', at, '{ORIGIN}') b, count(*) n, max(kwh) m FROM meter GROUP BY 1) x"),
"and reads what a recompute gives")
# THE WRITE SIDE AND THE DEFINER HELPERS (#1341). The policies have USING only,
# so they also check an INSERT's new row; an UPDATE or DELETE with no WHERE reads
# nothing, so only the policy's USING keeps it off another owner's rows. Asked
# before the read-side arms, so a write that got through is what fails.
rg_sql = "SELECT count(*) FROM pgcolumnar.rollup_group WHERE rollup = 'meter_daily'::regclass"
rg_own = _one(conn, rg_sql)
expect.text("some" if rg_own > 0 else f"none ({rg_own})", "some",
"premise: the owner's rollup has group rows another role could reach")
expect.text(as_role("rp_other", "INSERT INTO pgcolumnar.rollup_group VALUES"
" ('public.meter_daily'::regclass, 999999, 1, 0, NULL, NULL, false)"),
"42501", "another role cannot add a group row to the owner's rollup (42501)")
as_role("rp_other", "UPDATE pgcolumnar.rollup SET history_has_null = true")
as_role("rp_other", "DELETE FROM pgcolumnar.rollup_group")
has_null = _one(conn, "SELECT history_has_null FROM pgcolumnar.rollup"
" WHERE rollup = 'meter_daily'::regclass")
expect.text(f"{has_null}|{_one(conn, rg_sql)}", f"False|{rg_own}",
"and its unqualified UPDATE and DELETE reach none of the owner's catalog rows")
# rollup_source_groups and rollup_source_retired run as their definer, so the
# catalogs they read refuse no one; the caller's SELECT on the source is checked
# by require_caller_select against the OUTER user.
own_groups = as_role("rp_owner", "SELECT (count(*) > 0)::text FROM pgcolumnar.rollup_source_groups('meter')")
own_retired = as_role("rp_owner", "SELECT count(*) >= 0 FROM pgcolumnar.rollup_source_retired('meter')")
expect.text(f"{own_groups}|{own_retired}", "true|True",
"the owner reads its source's groups through the definer helpers")
expect.text(as_role("rp_other", "SELECT count(*) FROM pgcolumnar.rollup_source_groups('public.meter')"), "42501",
"another role cannot read the source's groups through rollup_source_groups (42501)")
expect.text(as_role("rp_other", "SELECT count(*) FROM pgcolumnar.rollup_source_retired('public.meter')"), "42501",
"nor its retired groups through rollup_source_retired (42501)")
expect.text(f"{as_role('rp_other', 'SELECT count(*) FROM pgcolumnar.rollup')} "
f"{as_role('rp_other', 'SELECT count(*) FROM pgcolumnar.rollup_group')}", "0 0",
"another role sees none of its rollup catalog rows")
Expand Down
24 changes: 24 additions & 0 deletions test/rollup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,30 @@ check_num "and refreshes it" "$(as_role ru_a "SELECT pgcolumnar.refresh_rollup('
check "and reads what a recompute gives" \
"$(as_role ru_a "SELECT string_agg(bucket || ':' || count || ':' || sum_v, ' ' ORDER BY bucket) FROM own_r")" \
"$(q "SELECT string_agg(b || ':' || n || ':' || s, ' ' ORDER BY b) FROM (SELECT date_bin('1 day', ts, '$ORIGIN') b, count(*) n, sum(v)::numeric s FROM own_src GROUP BY 1) x;")"
# THE WRITE SIDE AND THE DEFINER HELPERS (#1341). The policies have USING only, so
# they also check an INSERT's new row; and an UPDATE or DELETE with no WHERE reads
# nothing, so only the policy's USING keeps it off another owner's rows. Asked
# before the read-side arms below, so a write that got through is what fails.
_st() { sed -n 's/^ERROR: *\([0-9A-Z]\{5\}\).*/\1/p'; }
_rg_own="$(q "SELECT count(*) FROM pgcolumnar.rollup_group WHERE rollup = 'own_r'::regclass;")"
check "premise: the owner's rollup has group rows another role could reach" \
"$([ "${_rg_own:-0}" -gt 0 ] 2>/dev/null && echo some || echo "none ($_rg_own)")" "some"
check "another role cannot add a group row to the owner's rollup (42501)" \
"$(as_role ru_b "INSERT INTO pgcolumnar.rollup_group VALUES ('public.own_r'::regclass, 999999, 1, 0, NULL, NULL, false)" | _st)" "42501"
as_role ru_b "UPDATE pgcolumnar.rollup SET history_has_null = true" >/dev/null
as_role ru_b "DELETE FROM pgcolumnar.rollup_group" >/dev/null
check "and its unqualified UPDATE and DELETE reach none of the owner's catalog rows" \
"$(q "SELECT history_has_null FROM pgcolumnar.rollup WHERE rollup = 'own_r'::regclass;")|$(q "SELECT count(*) FROM pgcolumnar.rollup_group WHERE rollup = 'own_r'::regclass;")" \
"f|$_rg_own"
# rollup_source_groups and rollup_source_retired run as their definer, so the
# catalogs they read refuse no one; the caller's SELECT on the source is checked
# by require_caller_select against the OUTER user.
check "the owner reads its source's groups through the definer helpers" \
"$(as_role ru_a "SELECT (count(*) > 0)::text FROM pgcolumnar.rollup_source_groups('own_src')")|$(as_role ru_a "SELECT count(*) >= 0 FROM pgcolumnar.rollup_source_retired('own_src')")" "true|t"
check "another role cannot read the source's groups through rollup_source_groups (42501)" \
"$(as_role ru_b "SELECT count(*) FROM pgcolumnar.rollup_source_groups('public.own_src')" | _st)" "42501"
check "nor its retired groups through rollup_source_retired (42501)" \
"$(as_role ru_b "SELECT count(*) FROM pgcolumnar.rollup_source_retired('public.own_src')" | _st)" "42501"
check "another role sees none of its rollup catalog rows" \
"$(as_role ru_b "SELECT count(*) FROM pgcolumnar.rollup") $(as_role ru_b "SELECT count(*) FROM pgcolumnar.rollup_group")" "0 0"
# Row-level security hides the catalog row, so to that role it is no rollup at
Expand Down
Loading