diff --git a/test/check_ledger.tsv b/test/check_ledger.tsv index 8ec955d96..6255ed994 100644 --- a/test/check_ledger.tsv +++ b/test/check_ledger.tsv @@ -1875,11 +1875,12 @@ partition_lifecycle partition_lifecycle a call that would create more than 1000 partition_lifecycle partition_lifecycle a cutoff before every partition plans nothing 15;16;17;18;19 2026-10-13 the cutoff filter removed: every partition planned partition_lifecycle partition_lifecycle a cutoff of the other timestamp type is refused 15;16;17;18;19 2026-10-13 a cutoff of the other timestamp type planned instead of refused partition_lifecycle partition_lifecycle a cutoff on a partition's upper bound includes it 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;a plain DROP, not refusing while the partition is still attached;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing -partition_lifecycle partition_lifecycle a drop plan for a parent not partitioned on a timestamp is refused 15;16;17;18;19 2026-10-13 a parent partitioned on a non-timestamp key planned instead of refused +partition_lifecycle partition_lifecycle a drop plan for a parent not RANGE-partitioned on one column is refused, LIST and two-column RANGE included 15;16;17;18;19 2026-10-14 partition_drop_candidates refusing only a parent with no partition key, not LIST or a multi-column RANGE;partition_drop_candidates without its RANGE-on-one-column check +partition_lifecycle partition_lifecycle a drop plan for a parent not partitioned on a timestamp is refused 15;16;17;18;19 2026-10-14 a parent partitioned on a non-timestamp key planned instead of refused;partition_drop_candidates without its timestamp-key check partition_lifecycle partition_lifecycle a drop plan for a parent with a DEFAULT partition is refused 15;16;17;18;19 2026-10-13 a parent with a DEFAULT partition planned instead of refused partition_lifecycle partition_lifecycle a parent partitioned on a key that is not a timestamp is refused before any DDL 15;16;17;18;19 2026-10-13 partition_create_ahead without its timestamp key check partition_lifecycle partition_lifecycle a parent that is not RANGE-partitioned on one column is refused before any DDL 15;16;17;18;19 2026-10-13 partition_create_ahead without its RANGE-on-one-column check -partition_lifecycle partition_lifecycle a parent with a 63-character name gets partitions whose names keep their dates 15;16;17;18;19 2026-10-12 partition_create_ahead stopping one width short of now plus the lead;partition_create_ahead without shortening a long parent name +partition_lifecycle partition_lifecycle a parent with a 63-byte name gets partitions whose names keep their dates 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead stopping one width short of now plus the lead;partition_create_ahead without shortening a long parent name;partition_create_ahead's name bound one byte too long partition_lifecycle partition_lifecycle a parent with a DEFAULT partition is refused 15;16;17;18;19 2026-10-12 partition_create_ahead without the DEFAULT-partition refusal partition_lifecycle partition_lifecycle a parent with a generated column gets its partitions, and the column stays generated 15;16;17;18;19 2026-10-12 partition_create_ahead stopping one width short of now plus the lead;partition_create_ahead without INCLUDING GENERATED partition_lifecycle partition_lifecycle a parent with a partition bounded by MAXVALUE is refused, and nothing is created 15;16;17;18;19 2026-10-12 partition_create_ahead without the MAXVALUE refusal @@ -1888,30 +1889,30 @@ partition_lifecycle partition_lifecycle a partition named with the drop block's partition_lifecycle partition_lifecycle a partition that is itself partitioned is planned whole 15;16;17;18;19 2026-10-13 a partition that is itself partitioned left out of the plan;a partition whose upper bound equals the cutoff left out;a plain DROP, not refusing while the partition is still attached;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing;the plan detaches without CONCURRENTLY partition_lifecycle partition_lifecycle a pending detach is finalised, not started again 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;a pending detach planned as a new concurrent detach, not FINALIZE;a plain DROP, not refusing while the partition is still attached;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing partition_lifecycle partition_lifecycle a plan for names that need quoting runs as returned 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;names in the plan with their quotes removed;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing -partition_lifecycle partition_lifecycle a second call creates nothing 15;16;17;18;19 2026-10-12 partition_create_ahead starting from now, not the highest upper bound +partition_lifecycle partition_lifecycle a second call creates nothing 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead starting from now, not the highest upper bound partition_lifecycle partition_lifecycle a second plan for the same cutoff is empty, and the partitions it kept are still attached 15;16;17;18;19 2026-10-13 the cutoff filter removed: every partition planned;the lower bound read where the upper is meant partition_lifecycle partition_lifecycle a tiered partition is planned with DROP FOREIGN TABLE 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;a plain DROP, not refusing while the partition is still attached;a tiered partition planned with DROP TABLE;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing;the plan detaches without CONCURRENTLY partition_lifecycle partition_lifecycle a timestamp key's plan for a timestamp cutoff is the same from sessions west and east of UTC 15;16;17;18;19 2026-10-13 a plain DROP, not refusing while the partition is still attached;the lower bound read where the upper is meant;the plan detaches but drops nothing;the plan detaches without CONCURRENTLY;the timestamp overload compares the bound read as UTC with the cutoff converted in the session's zone partition_lifecycle partition_lifecycle a timestamp parent made east of UTC covers the local now plus the lead 15;16;17;18;19 2026-10-12 partition_create_ahead stopping one width short of now plus the lead partition_lifecycle partition_lifecycle a timestamp parent with a month width starts at this month's first and steps by calendar months 15;16;17;18;19 2026-10-13 partition_create_ahead without its month and year branch, so date_bin is handed the width partition_lifecycle partition_lifecycle a timestamp parent's partitions are contiguous across a non-UTC session 15;16;17;18;19 2026-10-12 partition_create_ahead leaving a minute between partitions;partition_create_ahead reading a timestamp key's bounds in the session's zone and writing them from UTC (the prototype);partition_create_ahead starting from now, not the highest upper bound -partition_lifecycle partition_lifecycle a timestamptz parent with a month width starts at this month's first and steps by calendar months 15;16;17;18;19 2026-10-13 partition_create_ahead without its month and year branch, so date_bin is handed the width +partition_lifecycle partition_lifecycle a timestamptz parent with a month width starts at this month's first and steps by calendar months 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead without its month and year branch, so date_bin is handed the width partition_lifecycle partition_lifecycle a timestamptz parent's partitions are contiguous across a non-UTC session 15;16;17;18;19 2026-10-12 partition_create_ahead leaving a minute between partitions;partition_create_ahead starting from now, not the highest upper bound partition_lifecycle partition_lifecycle a width that is not positive, or a negative lead, is refused before any DDL 15;16;17;18;19 2026-10-13 partition_create_ahead without its width and ahead check -partition_lifecycle partition_lifecycle a year width starts at this month's first and steps by calendar years 15;16;17;18;19 2026-10-13 partition_create_ahead without its month and year branch, so date_bin is handed the width +partition_lifecycle partition_lifecycle a year width starts at this month's first and steps by calendar years 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead without its month and year branch, so date_bin is handed the width partition_lifecycle partition_lifecycle after a DETACH that fails, the plan's DROP refuses and the partition stays attached 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;a plain DROP, not refusing while the partition is still attached;the plan detaches but drops nothing partition_lifecycle partition_lifecycle and a row at every boundary has exactly one partition to go to (timestamp) 15;16;17;18;19 2026-10-12 partition_create_ahead leaving a minute between partitions;partition_create_ahead reading a timestamp key's bounds in the session's zone and writing them from UTC (the prototype);partition_create_ahead starting from now, not the highest upper bound partition_lifecycle partition_lifecycle and a row at every boundary has exactly one partition to go to (timestamptz) 15;16;17;18;19 2026-10-12 partition_create_ahead leaving a minute between partitions;partition_create_ahead starting from now, not the highest upper bound partition_lifecycle partition_lifecycle and every row the remaining partitions held is still there 15;16;17;18;19 2026-10-13 the cutoff filter removed: every partition planned;the lower bound read where the upper is meant partition_lifecycle partition_lifecycle and its partitions start on the width's boundary in the key's own wall clock 15;16;17;18;19 2026-10-12 partition_create_ahead leaving a minute between partitions;partition_create_ahead reading a timestamp key's bounds in the session's zone and writing them from UTC (the prototype) -partition_lifecycle partition_lifecycle and land in columnar partitions 15;16;17;18;19 2026-10-12 partition_create_ahead creating heap partitions;partition_create_ahead starting from now, not the highest upper bound;partition_create_ahead stopping one width short of now plus the lead +partition_lifecycle partition_lifecycle and land in columnar partitions 15;16;17;18;19 2026-10-14 partition_create_ahead creating heap partitions;partition_create_ahead creating its last partition without attaching it;partition_create_ahead starting from now, not the highest upper bound;partition_create_ahead stopping one width short of now plus the lead partition_lifecycle partition_lifecycle and nothing was created for it 15;16;17;18;19 2026-10-12 partition_create_ahead without the DEFAULT-partition refusal partition_lifecycle partition_lifecycle and running it drops the foreign table and leaves its Parquet file 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;a tiered partition planned with DROP TABLE;the plan detaches but drops nothing partition_lifecycle partition_lifecycle and running it drops the pending partition 15;16;17;18;19 2026-10-13 a partition whose upper bound equals the cutoff left out;a pending detach planned as a new concurrent detach, not FINALIZE;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing partition_lifecycle partition_lifecycle and running the plan drops it with its children 15;16;17;18;19 2026-10-13 a partition that is itself partitioned left out of the plan;a partition whose upper bound equals the cutoff left out;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing partition_lifecycle partition_lifecycle and the dropped partitions' pgcolumnar storage rows are gone 15;16;17;18;19 2026-10-13 the drop hook leaves a dropped table's pgcolumnar storage behind;the plan detaches but drops nothing -partition_lifecycle partition_lifecycle and they cover now() plus the lead 15;16;17;18;19 2026-10-12 partition_create_ahead stopping one width short of now plus the lead -partition_lifecycle partition_lifecycle asking further ahead creates only the difference 15;16;17;18;19 2026-10-12 partition_create_ahead starting from now, not the highest upper bound +partition_lifecycle partition_lifecycle and they cover now() plus the lead 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead stopping one width short of now plus the lead +partition_lifecycle partition_lifecycle asking further ahead creates only the difference 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead starting from now, not the highest upper bound partition_lifecycle partition_lifecycle every partition it creates is columnar 15;16;17;18;19 2026-10-12 partition_create_ahead creating heap partitions partition_lifecycle partition_lifecycle from no partitions it creates enough to reach now plus the lead 15;16;17;18;19 2026-10-12 partition_create_ahead stopping one width short of now plus the lead partition_lifecycle partition_lifecycle it never takes AccessExclusiveLock on the parent 15;16;17;18;19 2026-10-12 partition_create_ahead using CREATE TABLE ... PARTITION OF @@ -1924,9 +1925,9 @@ partition_lifecycle partition_lifecycle premise: the call inside the transaction partition_lifecycle partition_lifecycle premise: the drop fixture has several partitions, each holding rows 15;16;17;18;19 never - partition_lifecycle partition_lifecycle premise: the function exists 15;16;17;18;19 never - partition_lifecycle partition_lifecycle premise: the interrupted detach left the partition pending 15;16;17;18;19 never - -partition_lifecycle partition_lifecycle premise: the parent now has the partitions it reported 15;16;17;18;19 never - -partition_lifecycle partition_lifecycle rows within the covered range go in 15;16;17;18;19 2026-10-12 partition_create_ahead starting from now, not the highest upper bound;partition_create_ahead stopping one width short of now plus the lead +partition_lifecycle partition_lifecycle rows within the covered range go in 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it;partition_create_ahead starting from now, not the highest upper bound;partition_create_ahead stopping one width short of now plus the lead partition_lifecycle partition_lifecycle running the plan leaves only the partitions it did not name 15;16;17;18;19 2026-10-13 the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing +partition_lifecycle partition_lifecycle the parent now has the partitions it reported 15;16;17;18;19 2026-10-14 partition_create_ahead creating its last partition without attaching it partition_lifecycle partition_lifecycle the plan retires the partitions wholly before the cutoff, in range order 15;16;17;18;19 2026-10-13 a plain DROP, not refusing while the partition is still attached;the cutoff filter removed: every partition planned;the lower bound read where the upper is meant;the plan detaches but drops nothing;the plan detaches without CONCURRENTLY;the plan in catalog order, not range order partition_lifecycle partition_lifecycle they are contiguous, each one width wide 15;16;17;18;19 2026-10-12 partition_create_ahead leaving a minute between partitions partman_columnar partman_columnar and no row-group row behind 15;16;17;18;19 2026-10-12 the drop hook stops deleting row_group rows, so they outlive their deleted storage row diff --git a/test/check_ledger_budget.txt b/test/check_ledger_budget.txt index b4353c1cb..ff626ea71 100644 --- a/test/check_ledger_budget.txt +++ b/test/check_ledger_budget.txt @@ -1858,4 +1858,17 @@ suites_not_covered 246 # never 1798 = 1798 # dated 551 + 4 = 555 # 1798 + 555 == 2353 -checks_never_observed_red 1798 +# +# #1339 audit, partition_lifecycle (OffgridwithJD): 1798 -> 1797. One new arm, a +# drop plan refusing a LIST and a two-column RANGE parent, dated by the RANGE check +# reduced to NOT FOUND (main's suite survives that). "premise: the parent now has the +# partitions it reported" is an arm and loses the prefix (#1236), dated with eight +# neighbours by a create_ahead that leaves its last partition unattached. The +# 63-character arm is renamed 63-byte, as its fixtures are; both renames keep their +# rows' history. Five-major runs: partition_lifecycle 60/60 on 15-19. +# +# ledger rows 2353 + 1 = 2354 +# never 1798 - 1 = 1797 +# dated 555 + 2 = 557 +# 1797 + 557 == 2354 +checks_never_observed_red 1797 diff --git a/test/partition_lifecycle.sh b/test/partition_lifecycle.sh index 9eeac5af6..9c649e886 100755 --- a/test/partition_lifecycle.sh +++ b/test/partition_lifecycle.sh @@ -70,7 +70,7 @@ q "CREATE TABLE tl (ts timestamptz NOT NULL, v int) PARTITION BY RANGE (ts);" >/ made="$(q "SELECT pgcolumnar.partition_create_ahead('tl', '1 day', 3);")" check_num "from no partitions it creates enough to reach now plus the lead" \ "$made" "$(q "SELECT 4;")" -check_num "premise: the parent now has the partitions it reported" "$(kids tl)" "$made" +check_num "the parent now has the partitions it reported" "$(kids tl)" "$made" check "every partition it creates is columnar" "$(ams tl)" "pgcolumnar" check "they are contiguous, each one width wide" \ "$(bounds tl | awk -F'|' 'NR>1 && $1!=prev {gap++} {prev=$2} END {print gap+0}')|$(q "SELECT count(DISTINCT upper - lower) FROM (SELECT (regexp_match(pg_get_expr(c.relpartbound, c.oid), 'TO \\(''([^'']+)''\\)'))[1]::timestamptz AS upper, (regexp_match(pg_get_expr(c.relpartbound, c.oid), 'FROM \\(''([^'']+)''\\)'))[1]::timestamptz AS lower FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid WHERE i.inhparent = 'tl'::regclass) b;")" \ @@ -161,12 +161,12 @@ gen_made="$(sqlstate_or "SELECT pgcolumnar.partition_create_ahead('gen', '1 day' check "a parent with a generated column gets its partitions, and the column stays generated" \ "$gen_made|$(q "INSERT INTO gen VALUES (now(), 21); SELECT w FROM gen;" | tail -1)" "2|42" -# A 63-character name: the 16-character date suffix must survive, or the second -# partition's name truncates onto the first's. +# A 63-byte name, the longest an identifier holds: the 16-byte date suffix must +# survive, or the second partition's name truncates onto the first's. LONG="p_$(printf 'x%.0s' $(seq 1 61))" q "CREATE TABLE $LONG (ts timestamptz NOT NULL, v int) PARTITION BY RANGE (ts);" >/dev/null long_made="$(sqlstate_or "SELECT pgcolumnar.partition_create_ahead('$LONG', '1 day', 2);")" -check "a parent with a 63-character name gets partitions whose names keep their dates" \ +check "a parent with a 63-byte name gets partitions whose names keep their dates" \ "$long_made|$(q "SELECT count(*) FILTER (WHERE c.relname ~ '_[0-9]{8}T[0-9]{6}\$' AND octet_length(c.relname) <= 63) FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid WHERE i.inhparent = '$LONG'::regclass;")" "3|3" @@ -444,8 +444,18 @@ check "a partition named with the drop block's dollar-quote tag is dropped by th check "a drop plan for a parent with a DEFAULT partition is refused" \ "$(sqlstate_or "SELECT count(*) FROM pgcolumnar.partition_drop_plan('td', now())")" "55000" q "CREATE TABLE dn (k int NOT NULL) PARTITION BY RANGE (k); - CREATE TABLE dnl (k int);" >/dev/null + CREATE TABLE dnl (k int); + CREATE TABLE dls (ts timestamptz NOT NULL) PARTITION BY LIST (ts); + CREATE TABLE dr2 (ts timestamptz NOT NULL, k int NOT NULL) PARTITION BY RANGE (ts, k);" >/dev/null +# Each by its own check, so the message is in the want (#1339): both answer 55000. check "a drop plan for a parent not partitioned on a timestamp is refused" \ - "$(sqlstate_or "SELECT count(*) FROM pgcolumnar.partition_drop_plan('dn', now())")|$(sqlstate_or "SELECT count(*) FROM pgcolumnar.partition_drop_plan('dnl', now())")" "55000|55000" + "$(refusal "SELECT count(*) FROM pgcolumnar.partition_drop_plan('dn', now())")" \ + "55000 $(rc dn) is partitioned on integer, not on a timestamp" +# The RANGE-on-one-column check had only an unpartitioned table to refuse, which +# NOT FOUND refuses alone. A LIST parent and a two-column RANGE parent, both led by +# a timestamptz, pass the key check: only strat and natts stop them. +check "a drop plan for a parent not RANGE-partitioned on one column is refused, LIST and two-column RANGE included" \ + "$(for t in dnl dls dr2; do refusal "SELECT count(*) FROM pgcolumnar.partition_drop_plan('$t', now())"; done | paste -sd'|')" \ + "55000 $(rc dnl) is not RANGE-partitioned on one column|55000 $(rc dls) is not RANGE-partitioned on one column|55000 $(rc dr2) is not RANGE-partitioned on one column" pgc_summary diff --git a/test/pytest/test_partition_lifecycle.py b/test/pytest/test_partition_lifecycle.py index 3ad1e84cb..c7bcad322 100644 --- a/test/pytest/test_partition_lifecycle.py +++ b/test/pytest/test_partition_lifecycle.py @@ -89,7 +89,7 @@ def test_partition_create_ahead(pgc_conn, expect): cur.execute("CREATE TABLE gauge (at timestamptz NOT NULL, reading float8) PARTITION BY RANGE (at)") made = _create_ahead(conn, "gauge") expect.num(made, LEAD + 1, "from no partitions it creates enough to reach now plus the lead") - expect.num(_kids(conn, "gauge"), made, "premise: the parent now has the partitions it reported") + expect.num(_kids(conn, "gauge"), made, "the parent now has the partitions it reported") expect.text(_one(conn, "SELECT string_agg(DISTINCT a.amname, ',') FROM pg_inherits i" " JOIN pg_class c ON c.oid = i.inhrelid JOIN pg_am a ON a.oid = c.relam" " WHERE i.inhparent = 'gauge'::regclass"), @@ -196,16 +196,18 @@ def value_or_state(sql): expect.text(f"{gen}|{doubled}", "3|4.5", "a parent with a generated column gets its partitions, and the column stays generated") - # 31 two-byte characters, 62 bytes: the date suffix must still fit, so the - # prefix is shortened by whole characters, never by a broken byte. - long_name = "\u00e9" * 31 + # 63 bytes, the longest an identifier holds, as the shell twin's name is: 31 + # two-byte characters and one ASCII (#1339; it was 62 bytes under a name that + # said 63 characters). The date suffix must still fit, so the prefix is + # shortened by whole characters, never by a broken byte. + long_name = "\u00e9" * 31 + "x" with conn.cursor() as cur: cur.execute(f'CREATE TABLE "{long_name}" (at timestamptz NOT NULL, reading float8) PARTITION BY RANGE (at)') long_made = value_or_state(f"""SELECT pgcolumnar.partition_create_ahead('"{long_name}"', '6 hours', 2)""") kept = _one(conn, "SELECT count(*) FILTER (WHERE c.relname ~ '_[0-9]{8}T[0-9]{6}$' AND octet_length(c.relname) <= 63)" f""" FROM pg_inherits i JOIN pg_class c ON c.oid = i.inhrelid WHERE i.inhparent = '"{long_name}"'::regclass""") expect.text(f"{long_made}|{kept}", "3|3", - "a parent with a 63-character name gets partitions whose names keep their dates") + "a parent with a 63-byte name gets partitions whose names keep their dates") with conn.cursor() as cur: cur.execute("CREATE TABLE swarm (at timestamptz NOT NULL, reading float8) PARTITION BY RANGE (at)") @@ -567,8 +569,29 @@ def sqlstate(sql): cur.execute("CREATE TABLE xd_default PARTITION OF xd DEFAULT USING pgcolumnar") cur.execute("CREATE TABLE xn (k bigint NOT NULL) PARTITION BY RANGE (k)") cur.execute("CREATE TABLE xnl (k int)") + cur.execute("CREATE TABLE xls (ts timestamptz NOT NULL) PARTITION BY LIST (ts)") + cur.execute("CREATE TABLE xr2 (ts timestamptz NOT NULL, k int NOT NULL) PARTITION BY RANGE (ts, k)") expect.text(sqlstate("SELECT * FROM pgcolumnar.partition_drop_plan('xd', now())"), "55000", "a drop plan for a parent with a DEFAULT partition is refused") - expect.text(sqlstate("SELECT * FROM pgcolumnar.partition_drop_plan('xn', now())") + "|" - + sqlstate("SELECT * FROM pgcolumnar.partition_drop_plan('xnl', now())"), "55000|55000", + # Each by its own check, so the message is in the want (#1339): both answer + # 55000, and with one check gone the other could still answer it. + def refusal(sql): + try: + return f"ok: {_one(conn, sql)}" + except psycopg.Error as e: + return f"{e.sqlstate} {e.diag.message_primary}" + + def name(t): + return _one(conn, "SELECT format('%I.%I', n.nspname, c.relname) FROM pg_class c" + f" JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.oid = '{t}'::regclass") + + expect.text(refusal("SELECT count(*) FROM pgcolumnar.partition_drop_plan('xn', now())"), + f"55000 {name('xn')} is partitioned on bigint, not on a timestamp", "a drop plan for a parent not partitioned on a timestamp is refused") + # The RANGE-on-one-column check had only an unpartitioned table to refuse, which + # NOT FOUND refuses alone. A LIST parent and a two-column RANGE parent, both led + # by a timestamptz, pass the key check: only strat and natts stop them. + expect.text("|".join(refusal(f"SELECT count(*) FROM pgcolumnar.partition_drop_plan('{t}', now())") + for t in ("xnl", "xls", "xr2")), + "|".join(f"55000 {name(t)} is not RANGE-partitioned on one column" for t in ("xnl", "xls", "xr2")), + "a drop plan for a parent not RANGE-partitioned on one column is refused, LIST and two-column RANGE included")