From 5333592e4f411730e8f6e5dce5ee7fbe074db3f0 Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Wed, 14 Oct 2026 00:24:50 +0000 Subject: [PATCH 1/2] test: create_rollup's last refusals and rebase_rollup's, in both twins (#1341) Co-Authored-By: Claude Opus 5.5 --- test/pytest/test_rollup.py | 16 +++++++++++++++- test/rollup.sh | 13 ++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/test/pytest/test_rollup.py b/test/pytest/test_rollup.py index f1cac101e..5c7bd6b46 100644 --- a/test/pytest/test_rollup.py +++ b/test/pytest/test_rollup.py @@ -533,5 +533,19 @@ def refused(name, width, funcs, cols): expect.text(refused("q_c", "1 hour", "{min}", "{humidity}"), "22023", "an aggregate column the source does not have is refused") expect.text(refused("q_s", "1 hour", "{max}", "{*}"), "22023", "* with anything but count is refused") - expect.num(_one(conn, "SELECT count(*) FROM pg_class WHERE relname IN ('q_m', 'q_p', 'q_l', 'q_c', 'q_s')"), 0, + # The rest of create_rollup's refusals, and rebase_rollup's (#1341). A timestamp + # (no zone) source column would be bucketed in whatever zone the refresh runs in. + with conn.cursor() as cur: + cur.execute("CREATE TABLE feed_lt (at timestamp, temp float8) USING pgcolumnar") + expect.text(_state(conn, f"SELECT pgcolumnar.create_rollup('q_t', 'feed_lt', 'at', '1 hour', '{ORIGIN}'," + f" '{{}}', '{{count}}', '{{*}}')"), + "22023", "a ts_column that is not timestamptz is refused") + expect.text(_state(conn, f"SELECT pgcolumnar.create_rollup('q_g', 'feed', 'at', '1 hour', '{ORIGIN}'," + f" '{{humidity}}', '{{count}}', '{{*}}')"), + "22023", "a group_by column the source does not have is refused") + expect.text(refused("q_f", "1 hour", "{sum}", "{at}"), "22023", + "a sum over a column that cannot be folded is refused") + expect.text(_state(conn, "SELECT pgcolumnar.rebase_rollup('feed')"), "42704", + "rebase_rollup of a table that is not a rollup is refused (42704)") + expect.num(_one(conn, "SELECT count(*) FROM pg_class WHERE relname IN ('q_m', 'q_p', 'q_l', 'q_c', 'q_s', 'q_t', 'q_g', 'q_f')"), 0, "and no refused rollup left a table behind") diff --git a/test/rollup.sh b/test/rollup.sh index 2fa7421a6..2ef271f42 100755 --- a/test/rollup.sh +++ b/test/rollup.sh @@ -445,9 +445,20 @@ check "aggregate functions and columns of different lengths are refused" \ check "an aggregate column the source does not have is refused" \ "$(refused_create r_c '1 hour' '{min}' '{nosuch}')" "22023" check "* with anything but count is refused" "$(refused_create r_s '1 hour' '{sum}' '{*}')" "22023" +# The rest of create_rollup's refusals, and rebase_rollup's (#1341). A timestamp +# (no zone) source column would be bucketed by date_bin in whatever zone the +# refresh runs in, so it is refused rather than converted. +q "CREATE TABLE src_lt (ts timestamp, v int) USING pgcolumnar;" >/dev/null +check "a ts_column that is not timestamptz is refused" \ + "$(sqlstate "SELECT pgcolumnar.create_rollup('r_t', 'src_lt', 'ts', '1 hour', '$ORIGIN', '{}', '{count}', '{*}');")" "22023" +check "a group_by column the source does not have is refused" \ + "$(sqlstate "SELECT pgcolumnar.create_rollup('r_g', 'src', 'ts', '1 hour', '$ORIGIN', '{nosuch}', '{count}', '{*}');")" "22023" +check "a sum over a column that cannot be folded is refused" "$(refused_create r_f '1 hour' '{sum}' '{ts}')" "22023" +check "rebase_rollup of a table that is not a rollup is refused (42704)" \ + "$(sqlstate "SELECT pgcolumnar.rebase_rollup('src');")" "42704" # An arm, not a premise: with a refusal removed, the create succeeds and leaves a # table (the months-check mutation did), so a repository change reddens it. check_num "and no refused rollup left a table behind" \ - "$(q "SELECT count(*) FROM pg_class WHERE relname IN ('r_m', 'r_p', 'r_l', 'r_c', 'r_s');")" "0" + "$(q "SELECT count(*) FROM pg_class WHERE relname IN ('r_m', 'r_p', 'r_l', 'r_c', 'r_s', 'r_t', 'r_g', 'r_f');")" "0" pgc_summary From 7af80c998bd26cd1ad38133ddc280017d55dbbdd Mon Sep 17 00:00:00 2001 From: "Joshua D. Drake" Date: Wed, 14 Oct 2026 00:46:47 +0000 Subject: [PATCH 2/2] test: ledger rows for create_rollup's and rebase_rollup's refusal arms (#1341) Co-Authored-By: Claude Opus 5.5 --- test/check_ledger.tsv | 6 +++++- test/check_ledger_budget.txt | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/test/check_ledger.tsv b/test/check_ledger.tsv index d9db3d2ef..8ec955d96 100644 --- a/test/check_ledger.tsv +++ b/test/check_ledger.tsv @@ -2049,6 +2049,7 @@ rollup rollup a delete in a bucket that holds retired history is refused, and th rollup rollup a delete in a bucket whose history the same refresh retires is refused, and the rollup is unchanged 15;16;17;18;19 2026-10-13 refresh_rollup blocking on stored history only, not on groups the same refresh retires;refresh_rollup recording new groups without their time range;refresh_rollup without the history refusal rollup rollup a delete in a group that expire then retires is refused, and the rollup is unchanged 15;16;17;18;19 2026-10-13 expire recording a retired group's deleted count as 0;refresh_rollup not refusing a retiring group whose deleted count moved rollup rollup a delete in buckets with no history, next to retired ones, folds: those buckets equal a recompute and D keeps its history 15;16;17;18;19 2026-10-13 refresh_rollup recomputing every bucket once any group changed;refresh_rollup recording new groups without their time range;refresh_rollup refusing any change but appends (phase 1, main before this branch) +rollup rollup a group_by column the source does not have is refused 15;16;17;18;19 2026-10-14 create_rollup without its group_by column check rollup rollup a refresh after a delete is not refused 15;16;17;18;19 2026-10-13 refresh_rollup refusing any change but appends (phase 1, main before this branch) rollup rollup a refresh after a small append reads under a fifth of the source 15;16;17;18;19 2026-10-13 refresh_rollup folding every group, ignoring the recorded snapshot;refresh_rollup's per-group subquery without OFFSET 0, so it becomes a join and reads every group rollup rollup a refresh after appends folds exactly the appended rows, late ones included 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 @@ -2061,7 +2062,9 @@ rollup rollup a refresh with nothing new folds nothing 15;16;17;18;19 2026-10-13 rollup rollup a role that cannot read the source cannot create a rollup of it 15;16;17;18;19 2026-10-13 get_storage_id without its SELECT check, so create_rollup no longer refuses a caller that cannot read the source rollup rollup a rollup kept in a heap table refreshes and equals a full recompute 15;16;17;18;19 2026-10-13 create_rollup ignoring access_method;refresh_rollup folding only the first new group;row_tid mapping 290 rows a block instead of the AM's rollup rollup a rollup with NULL keys and NULL timestamps 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 matching keys with = instead of IS NOT DISTINCT FROM;row_tid mapping 290 rows a block instead of the AM's +rollup rollup a sum over a column that cannot be folded is refused 15;16;17;18;19 2026-10-14 create_rollup without its cannot-be-folded check rollup rollup a table's owner, not a superuser, creates a rollup of it 15;16;17;18;19 2026-10-13 the rollup catalogs without their grants +rollup rollup a ts_column that is not timestamptz is refused 15;16;17;18;19 2026-10-14 create_rollup without its timestamptz check on ts_column rollup rollup a width with months is refused 15;16;17;18;19 2026-10-13 create_rollup without the months refusal rollup rollup after a delete and an append that straddles its bucket, a refresh counts the new rows once 15;16;17;18;19 2026-10-13 rebase_rollup recording no time ranges;refresh_rollup folding new groups' rows into dirty buckets too;refresh_rollup recomputing every bucket once any group changed;refresh_rollup refusing any change but appends (phase 1, main before this branch) rollup rollup after a delete of rows whose ts is NULL the NULL-bucket rollup equals a full recompute 15;16;17;18;19 2026-10-13 refresh_rollup never dirtying the NULL bucket;refresh_rollup recomputing every bucket once any group changed;refresh_rollup refusing any change but appends (phase 1, main before this branch) @@ -2083,7 +2086,7 @@ rollup rollup and cannot refresh it 15;16;17;18;19 2026-10-13 the rollup catalog 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 +rollup rollup and no refused rollup left a table behind 15;16;17;18;19 2026-10-14 create_rollup allowing * with any function;create_rollup without its timestamptz check on ts_column;create_rollup without the equal-length check;create_rollup without the months refusal rollup rollup and reads what a recompute gives 15;16;17;18;19 2026-10-13 refresh_rollup bucketing with date_trunc, which follows the session's zone;rollup_source_groups without SECURITY DEFINER;the rollup catalogs without their grants rollup rollup and refreshes it 15;16;17;18;19 2026-10-13 rollup_source_groups without SECURITY DEFINER;the rollup catalogs without their grants rollup rollup and that is not zero 15;16;17;18;19 never - @@ -2108,6 +2111,7 @@ rollup rollup premise: the owner's rollup has group rows another role could reac 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 - +rollup rollup rebase_rollup of a table that is not a rollup is refused (42704) 15;16;17;18;19 2026-10-14 rebase_rollup without its not-a-rollup check rollup rollup rebase_rollup records the source's current row groups 15;16;17;18;19 2026-10-13 rebase_rollup returning 0 rollup rollup refreshes from sessions in different time zones build the same rollup 15;16;17;18;19 2026-10-13 refresh_rollup bucketing with date_trunc, which follows the session's zone rollup rollup the first refresh folds every row 15;16;17;18;19 2026-10-13 refresh_rollup folding only the first new group diff --git a/test/check_ledger_budget.txt b/test/check_ledger_budget.txt index a0cc267ee..b4353c1cb 100644 --- a/test/check_ledger_budget.txt +++ b/test/check_ledger_budget.txt @@ -1847,4 +1847,15 @@ suites_not_covered 246 # never 1796 + 2 = 1798 # dated 547 + 4 = 551 # 1798 + 551 == 2349 +# +# #1341, create_rollup's last refusals and rebase_rollup's (OffgridwithJD): 1798 -> 1798. +# Four new rollup arms, each dated by deleting the check it names: a ts_column that is +# not timestamptz (which also reddens "no refused rollup left a table behind", since +# the create then succeeds), a group_by column the source lacks, a sum that cannot be +# folded, and rebase_rollup of a non-rollup. Five-major runs: rollup 76/76 on 15-19. +# +# ledger rows 2349 + 4 = 2353 +# never 1798 = 1798 +# dated 551 + 4 = 555 +# 1798 + 555 == 2353 checks_never_observed_red 1798