Repository navigation
Missing tests: 87 confirmed behaviours no shell or pytest arm exercises (#1339 audit) #1341
Copy link
Copy link
Open
Description
Missing tests: 87 confirmed behaviours no test exercises
The owner asked the pytest audit (#1339) to also look for missing tests, for old and new features. This issue holds what it found.
How the list was made
- Searching: at main a30faeb, eight searchers each built an inventory a different way and grepped
test/*.shandtest/pytest/*.pyfor each item:- every SQL function and catalog;
- every setting;
- every user-reachable error path in the C and SQL;
- documented claims in
docs/; - CHANGELOG entries for alpha3 to alpha5;
- shell/pytest twin parity;
- the alpha5 time-series features and how they interact;
- core access-method behaviour.
- Verifying: two independent skeptics then searched for an existing test covering each reported gap, trying to refute it. A gap is listed only if neither found one. That kept 87 of the 104 reported.
- Reading this list: a skeptic reads code; it doesn't run it. So treat each item as "no test found", not "untested for certain". Each fix starts with a red arm, in both twins, that a named mutation of the product must redden.
By severity: high 31, medium 43, low 13. By search: core-am 18, twin-parity 15, error-paths 14, docs-claims 12, time-series-alpha5 11, sql-objects 9, gucs 7, changelog-features 1.
High (31)
- docs/limitations.md promises that ON CONFLICT DO NOTHING 'works' and that DO UPDATE raises the row-locking error. Neither is tested. For any table wi… (core-am;
docs/limitations.md:655-656; src/columnar_tableam.c:2141-2154 (COLUMNAR_UNSUPPO…)
Arm to add: Add to fk_referencing.sh, the suite for row-locking consequences, plus a new pytest twin. Setup: c (id int PRIMARY KEY, v text) USING pgcolumnar; heap mirror h; insert (1,'a'),(2,'b') into both. Arm 'ON CONFLICT DO NOTHING inserts the new key and skips the conflict': INSERT VALUES (2,'x'),(3,'c') ON CONFLICT DO NOTHING. Expect success, rows {1,2,3}, v of id 2 still 'b', same a… - The AM takes no SSI predicate locks: there is no PredicateLockRelation or PredicateLockTID call on scans and no CheckForSerializableConflictOut. A SE… (core-am;
src/*.c: grep -n 'PredicateLock\|CheckForSerializableConflict' returns nothing;…)
Arm to add: New pair test/serializable.sh and test/pytest/test_serializable.py, with a heap control. Setup: doctors (id int, on_call bool) USING pgcolumnar with rows (1,t) and (2,t). Steps: 1. s1: BEGIN ISOLATION LEVEL SERIALIZABLE; SELECT count(*) FROM doctors WHERE on_call. 2. s2: the same. 3. s1: UPDATE doctors SET on_call=false WHERE id=1. 4. s2: UPDATE doctors SET on_call=false WHERE… - A SQL TRUNCATE that rolls back, at transaction level or to a savepoint, must restore every row. set_new_filelocator deletes the old storage's catalog… (core-am;
src/columnar_tableam.c:1236-1285 (pgcolumnar_delete_storage_tree and PgColumnar…)
Arm to add: Add to truncate_cleanup.sh plus a new pytest twin, with a heap mirror and an index on id. Arm 'a rolled-back TRUNCATE restores every row': BEGIN; TRUNCATE t; INSERT 10 rows; ROLLBACK. Expect set hash = heap, an index point lookup returns the row, and pgcolumnar.storage still resolves. Arm 'a TRUNCATE rolled back to a savepoint keeps the parent's rows': BEGIN; INSERT 3000 rows;… - CREATE INDEX builds from an MVCC snapshot, so a row deleted after an older REPEATABLE READ snapshot started is left out of the new index. Heap includ… (core-am;
src/columnar_tableam.c:2466-2475 (serial build: snapshot = GetActiveSnapshot() …)
Arm to add: New isolation spec test/isolation/specs/index_build_vs_old_snapshot.spec. Mirror it as an arm in a new pair test/index_build_snapshot.sh and test/pytest/test_index_build_snapshot.py. Steps: 1. Create t (id int) USING pgcolumnar with ids 1..10000 and no index. Create heap mirror h. 2. s1: BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT count(*) FROM t. 3. s2: DELETE FROM t WHERE … - An inner statement's ExecutorEnd flushes the OUTER statement's buffered rows and delete marks while an inner subtransaction (a plpgsql EXCEPTION bloc… (core-am;
src/columnar_tableam.c:2777-2784 (pgcolumnar_executor_end calls PgColumnarFlush…)
Arm to add: New pair test/subxact_flush.sh and test/pytest/test_subxact_flush.py. Setup: t (id int) USING pgcolumnar; heap mirror th; side table s (id int) USING pgcolumnar; function f(i) that runs BEGIN INSERT INTO s VALUES (i); PERFORM 1/0; EXCEPTION WHEN division_by_zero THEN NULL; END; RETURN i. Arm 'outer insert survives an aborted inner block': INSERT INTO t SELECT f(g) FROM generat… - An uncommitted DELETE (or key-changing UPDATE) of unique key k in T1 races an INSERT of k in T2, then T1 rolls back. Heap makes T2 wait on T1 through… (core-am;
src/columnar_tableam.c:1942-1994 (pgcolumnar_index_fetch_tuple: dirty snapshot,…)
Arm to add: Add to unique_conc.sh plus a new pytest twin (two connections). Run each case on a heap control too. Arm 'an insert of a key whose delete is uncommitted does not commit a duplicate when the delete rolls back': 1. u (k int UNIQUE) USING pgcolumnar; INSERT (5). 2. s1: BEGIN; DELETE FROM u WHERE k=5. 3. s2: INSERT INTO u VALUES (5). Do not wait for it. 4. s1: ROLLBACK. 5. Expect … - INSERT ... ON CONFLICT DO NOTHING 'works' on a columnar table, and ON CONFLICT DO UPDATE raises 'columnar: row locking is not supported yet'. (docs-claims;
docs/limitations.md:655-656; the speculative-insert callbacks raise COLUMNAR_UN…)
Arm to add: Add to a suite with unique indexes (e.g. test/unique_conc.sh, with a matching arm in its pytest twin when created). CREATE TABLE oc (k int PRIMARY KEY, v int) USING pgcolumnar; INSERT INTO oc VALUES (1,1). Arm 1: INSERT INTO oc VALUES (1,9) ON CONFLICT DO NOTHING succeeds, and count is 1 with v = 1. Arm 2: INSERT INTO oc VALUES (2,2) ON CONFLICT DO NOTHING succeeds and count b… - The four REST functions (iceberg_rest_table_location, iceberg_rest_scan, iceberg_rest_namespaces, iceberg_rest_tables) and a scan of the pgcolumnar_i… (docs-claims;
docs/sql-reference.md:1159 and :1281; gates at src/columnar_iceberg_rest.c:676,…)
Arm to add: Shell (test/server_file_privilege.sh, no pytest twin yet): add these rows to entry_points: "iceberg_rest_table_location|read|SELECT pgcolumnar.iceberg_rest_table_location('http://127.0.0.1:1','db','t')", the matching rows for iceberg_rest_scan (with AS t(id bigint)), iceberg_rest_namespaces and iceberg_rest_tables, and "iceberg_fdw_scan|read|SELECT * FROM ift" over CREATE SERV… - With the server form, a non-superuser role that has no USER MAPPING token is refused (28000) before any request. Only a superuser may set credentials… (docs-claims;
docs/sql-reference.md:1143-1149 and :1183-1184; src/columnar_iceberg_rest.c:141…)
Arm to add: Shell (test/iceberg_rest_server.sh, no pytest twin yet): create role ice_r with LOGIN, schema USAGE and pg_read_server_files, with no mapping on server cat, and start the server with PGCOLUMNAR_ICEBERG_REST_TOKEN set to the valid token. (1) As ice_r, iceberg_rest_table_location('cat','db','events') gives 28000, and the fixture request log shows no new loadTable request. (2) As… - pgcolumnar.expire deletes rows and must refuse a role that does not own the table ('you must own the table'). (docs-claims;
docs/how-to.md:296; src/columnar_vacuum.c:2368 (PgColumnarRequireTableOwnerByOi…)
Arm to add: Both twins of native_ownership: add "expire('n')" to OWNER_ONLY and to the refused calls. First, as the owner, run set_options('n', ttl_column => 'ts', ttl_interval => '1 day') on a table with a timestamptz column and aged rows, so a missing gate would really delete. Arm 'non-owner refused: expire' expects SQLSTATE 42501 (pytest: expect.sqlstate) and then checks count(*) FROM … - parallel_copy requires INSERT on the target, and parallel_export_parquet requires SELECT on the target. Each check is separate from the server-file r… (docs-claims;
docs/sql-reference.md:749 (INSERT) and :823 (SELECT); src/columnar_parallel_cop…)
Arm to add: Shell (test/import_export_privilege.sh, no pytest twin yet): reuse the attacker role, which holds schema USAGE, EXECUTE and both server-file roles and has no table grant. Add 'parallel_copy refuses a caller with no INSERT on the target' (SELECT pgcolumnar.parallel_copy('target', '', 2); expect 'permission denied for table', with count(*) of the target un… - pgcolumnar.expire refuses a ttl_column that is not date, timestamp or timestamptz (42804) and a ttl_column that no longer exists (42703). set_options… (error-paths;
src/columnar_vacuum.c:2403-2410 (undefined column), src/columnar_vacuum.c:2471-…)
Arm to add: In ttl_expire.sh and test_ttl_expire.py: (a) create ttl_int (k int, ts int) USING pgcolumnar with several row groups, including negative and small values. Call set_options(ttl_column => 'ts', ttl_interval => '1 day'), which must be accepted. Then SELECT pgcolumnar.expire('ttl_int') -> 42804, and count(*) is unchanged. (b) On a timestamptz fixture with a declared retention, run… - pg_read_server_files gate in icefdwBeginForeignScan (42501 'must be superuser or a member of the pg_read_server_files role to read an Iceberg table')… (error-paths;
src/columnar_iceberg_fdw.c:1388 (executor gate); src/columnar_iceberg_fdw.c Get…)
Arm to add: In iceberg_fdw.sh and test_iceberg_fdw.py, create role ice_nofile NOSUPERUSER LOGIN and GRANT SELECT ON the existing Iceberg foreign table. Arm A: SELECT count(*) as ice_nofile -> SQLSTATE 42501. Add a premise that the same query as superuser returns the fixture's row count. Mutation: delete the has_privs_of_role block at iceberg_fdw.c:1388; the arm then returns the count. Arm… - A non-superuser must not borrow the server's ambient PGCOLUMNAR_ICEBERG_REST_TOKEN when it has no token mapping. That is the 28000 'iceberg: no beare… (error-paths;
src/columnar_iceberg_rest.c:203-215 (rest_resolve_server ambient check); src/co…)
Arm to add: In iceberg_rest_server.sh and a new pytest twin, after pg_restart_env sets a valid PGCOLUMNAR_ICEBERG_REST_TOKEN (line 223), create role rc NOSUPERUSER. GRANT pg_read_server_files, USAGE ON SCHEMA pgcolumnar and USAGE ON FOREIGN SERVER cat TO rc. Give rc no mapping. Arm 1: as rc, SELECT pgcolumnar.iceberg_rest_table_location('cat','db','events') -> 28000. Control: the same cal… - pgcolumnar.enable_row_update_lock: no test sets or reads it, so the documented off branch never runs and nothing proves the setting gates the lock. T… (gucs;
src/columnar_tableam.c:4400; src/columnar_row_lock.c:246; docs/configuration.md…)
Arm to add: Shell, in update_conc.sh: open session A with BEGIN; UPDATE t_col SET v=v+1 WHERE id=1; and hold it with pg_sleep, using the PSQL_BG pattern from advisory_lock_class.sh. From the control session, read pg_locks WHERE locktype='advisory' AND objsubid=104 (PGCOLUMNAR_LOCKCLASS_ROW_IDENTITY) AND pid=. Arm 'row update takes a row-identity lock when on' expects count >= 1. Repeat… - pgcolumnar.row_lock_buckets: no test sets or reads it. Two documented promises are untested. (a) It is fixed at server start (PGC_POSTMASTER), becaus… (gucs;
src/columnar_tableam.c:4414 (PGC_POSTMASTER at :4429); src/columnar_row_lock.c:…)
Arm to add: Shell, in update_conc.sh: (1) Arm 'row_lock_buckets cannot be changed per session' runsSET pgcolumnar.row_lock_buckets = 1and expects SQLSTATE 55P02. (2) Arm 'a bulk UPDATE holds at most row_lock_buckets row locks': load 200000 rows with stripe_row_limit large. In one session run BEGIN; UPDATE t SET v=v+1; then, still inside the transaction, count pg_locks WHERE pid=pg_bac… - pgcolumnar.rollup_source_groups(regclass) and pgcolumnar.rollup_source_retired(regclass) are SECURITY DEFINER, and PUBLIC keeps EXECUTE (no REVOKE). … (sql-objects;
pgcolumnar--1.0-alpha5.sql:2427-2460 (no REVOKE for either; compare :814-821 an…)
Arm to add: Add to rollup.sh, next to the ru_a/ru_b block (line ~393), and to the matching arm in test_rollup.py (rp_other block, ~465). As ru_b, which has no SELECT on public.own_src:SELECT count(*) FROM pgcolumnar.rollup_source_groups('public.own_src')and the same for rollup_source_retired. Expect42501|42501. Pair it with a premise that ru_a, the owner, gets a non-error count fro… - pgcolumnar.rollup and pgcolumnar.rollup_group grant INSERT, UPDATE and DELETE to PUBLIC. They rely on a USING-only RLS policy, which also serves as t… (sql-objects;
pgcolumnar--1.0-alpha5.sql:2405-2413)
Arm to add: Add to both twins. As ru_b, runINSERT INTO pgcolumnar.rollup_group VALUES ('public.own_r'::regclass, 999999, 1, 0, now(), now(), false)and expect 42501. As ru_b, runUPDATE pgcolumnar.rollup SET source_storage = 0 WHERE rollup = 'public.own_r'::regclassandDELETE FROM pgcolumnar.rollup_group WHERE rollup = 'public.own_r'::regclass. Then as ru_a, own_r's catalog rows a… - An ALTER COLUMN TYPE that PostgreSQL performs without a rewrite but which changes the value order. int4 to oid is a binary-coercible cast, and it reo… (time-series-alpha5;
src/columnar_tableam.c pgcolumnar_collations_before / pgcolumnar_forget_changed…)
Arm to add: New arms in collation_change.sh and test_collation_change.py, with heap as the oracle in the same shape as the existing ones. Setup: 1. Create columnar oc and heap oh:(k int, c int), 3 row groups of 1000 rows each, every group holding both negative and positive c. 2. RunALTER TABLE ... ALTER COLUMN c TYPE oidon both. Premise: pg_relation_filenode('oc') is unchanged, so … - **create_rollup on a source column with a non-default collation (text min/max, or a text group_by key). create_rollup builds each partials column as
%…** (time-series-alpha5;pgcolumnar--1.0-alpha4--1.0-alpha5.sql:1352-1355 (min/max column built from cty…) *Arm to add:* Both twins. Setup: 1.CREATE TABLE ct (ts timestamptz, dev text COLLATE "C", s text COLLATE "C") USING pgcolumnar, with stripe_row_limit 1000. Explicit "C" differs from the default collation OID 100, so the arm does not depend on the server locale. 2. Insert 3000 rows, with dev and s mixing upper and lower case. 3.create_rollup('r_ct','ct','ts','1 hour',ORIGIN,'{dev}','{co… - The SECURITY DEFINER helpers rollup_source_groups(source) and rollup_source_retired(source). They read pgcolumnar.row_group, delete_vector and retire… (time-series-alpha5;
pgcolumnar--1.0-alpha4--1.0-alpha5.sql:1237-1271)
Arm to add: Both twins: rollup.sh privilege block and test_rollup.py. Setup: 1. Create role ru_b with USAGE on schema pgcolumnar and no SELECT on own_src. 2. Grant ru_b EXECUTE on both helpers explicitly, so that only the plpgsql guard can refuse. This copies entry_point_privilege's lesson about layered guards. Check these calls as ru_b: - `SELECT * FROM pgcolumnar.rollup_source_groups('p… - Write-side row-level security on pgcolumnar.rollup and pgcolumnar.rollup_group. Both tables GRANT SELECT, INSERT, UPDATE and DELETE TO PUBLIC, and th… (time-series-alpha5;
pgcolumnar--1.0-alpha4--1.0-alpha5.sql:1215-1223 (RLS and grants), 1659-1695 (r…)
Arm to add: Both twins: rollup.sh as_role block and test_rollup.py as_role. As ru_b, against ru_a's own_r: 1.INSERT INTO pgcolumnar.rollup_group VALUES ('public.own_r'::regclass, 999999, 1, 0, NULL, NULL, false): expect 42501 (a new row violates the row-level security policy). 2.UPDATE pgcolumnar.rollup SET source_storage = 0 WHERE rollup = 'public.own_r'::regclass: expect it to rep… - Two sessions insert the same key concurrently. The second inserter must block and then get unique_violation (23505), leaving exactly one row. The buc… (twin-parity;
test/unique_conc.sh:382 (1b post-fix: blocked inserter got a unique_violation),…)
Arm to add: In both twins: create a pgcolumnar table with a UNIQUE index on k. s1: BEGIN; INSERT k=1. s2 (thread): INSERT k=1, which must block. Assert this with a pg_locks wait on s2's pid before s1 COMMITs. After the commit, expect.sqlstate(err,'23505','1b post-fix: blocked inserter got a unique_violation') and expect.num(count,1,'1b post-fix: exactly one row with key 1'). Also check th… - Two transactions UPDATE or DELETE the same row. The losing writer must get serialization_failure (40001) and no duplicate row may survive (lost-updat… (twin-parity;
test/update_conc.sh:324-333 (uu/RC columnar keeps a single row (no duplicate); …)
Arm to add: pytest test_update_conc.py, using two psycopg connections at READ COMMITTED on a pgcolumnar table holding one row k=1. s1: BEGIN; UPDATE t SET v=v+1 WHERE k=1. s2 (in a thread): UPDATE t SET v=v+10 WHERE k=1, which blocks. s1 COMMIT. Expect: expect.sqlstate(s2_err, '40001', 'uu/RC columnar loser gets serialization_failure') and expect.num(count(*) WHERE k=1, 1, 'uu/RC columnar… - Crash recovery and corruption detection: - recovery.sh: committed columnar rows survive an immediate stop and redo. - native_backend_crash.sh: a back… (twin-parity;
test/recovery.sh:80 (s1 recovery ran / s1 col count / s2 col count unchanged); …)
Arm to add: pytest recovery: insert N rows into a pgcolumnar table and commit. Stop the cluster with pg_ctl -m immediate (pgc_cluster has Cluster.stop/start; give it an immediate-mode stop for this test). Start it and expect.num(count(*), N, 's1 col count'). Run the same against a heap mirror as the oracle. Corruption: overwrite column_chunk.byte_length for one chunk through the catalog a… - Catalog-driven sweep: every relation-taking C entry point has a decided privilege bar. An unprivileged caller holding EXECUTE gets 42501 from the C c… (twin-parity;
test/entry_point_privilege.sh:274 (no relation-taking C entry point is still un…)
Arm to add: pytest twin: query pg_proc for pgcolumnar functions with proargtypes[0]='regclass'::regtype and prolang = C. For each function, as a role holding EXECUTE but no SELECT on a fixture table, call it and expect.sqlstate(err,'42501',f'{fn} refuses an unprivileged caller with 42501'), parametrized on the name. Add a premise that the owner's call succeeds. Add a coverage arm: the set… - Import and export functions must not bypass grants or row-level security. import_export_privilege.sh: import_parquet/import_arrow refuse INSERT into … (twin-parity;
test/import_export_privilege.sh:99; test/rls_direct_storage.sh:113 and the foll…)
Arm to add: Extend the UNDER_POLICY parametrize idiom into a new test_rls_direct_storage.py: export_parquet, export_arrow, parallel_export_parquet, import_parquet, import_arrow and parallel_copy each give expect.sqlstate(err,'42501',name) for a role holding SELECT/INSERT and EXECUTE but restricted by a policy. Then os.path.exists(outfile) must be False ('no export file was written for the… - Maintenance and debug entry points check ownership BEFORE taking a lock, and refuse non-owners. vacuum_lock_privilege.sh: vacuum, vacuum_sorted, clus… (twin-parity;
test/vacuum_lock_privilege.sh:77; test/vm_privilege.sh:115; test/debug_hook_pri…)
Arm to add: Lock ordering: s1 holds ACCESS SHARE on t in an open transaction. As a non-owner with EXECUTE, call pgcolumnar.vacuum('t') with lock_timeout='200ms'. Expect expect.sqlstate(err,'42501','vacuum refuses a non-owner before taking the exclusive lock'), not 55P03. Parametrize the same arm over vacuum_sorted, cluster, add_projection, drop_projection, recluster. vm_privilege: an unpr… - After the twin rule, fixes for backend aborts and catalog leaks landed with shell arms only: - A partitioned columnar table aborts the backend in four entry points: PgColumnarIsColumnarRelation tests relam and never relkind #1259 (a267bb1): a partitioned parent with relam=pgcol… (twin-parity;
test/inheritance.sh:151-159; test/parquet_import.sh:243; test/phase5.sh:326; te…)
Arm to add: Both twins, PG17+ only (the shell arm skips below 17, fix: import_parquet segfaults on a partitioned columnar parent (#1263) #1264): CREATE TABLE pp (a int) PARTITION BY RANGE (a) USING pgcolumnar, plus one leaf. - get_storage_id('pp') and add_projection('pp','p1',ARRAY['a']) each raise a refusal. - Afterwards SELECT 1 on the same connection succeeds ('and the cluster is still up after it'). - import_parquet into pp is refused, and a HEAP target … - Userinfo in a caller-written URL is refused (22023) on every path: read_parquet http and s3:// bucket authority, gs://, the s3 read and write paths, … (twin-parity;
test/objstore_userinfo.sh:65 (read_parquet refuses userinfo (22023, the parse g…)
Arm to add: New test_objstore_userinfo.py, parametrized over (func, url): read_parquet('http://u:p@127.0.0.1:1/x.parquet'), read_parquet('s3://u:p@mybucket/x.parquet'), export_parquet('t','s3://u:p@mybucket/o.parquet'), export_arrow(...). Each gives expect.sqlstate(err,'22023',name) and expect.contains(msg,'userinfo',...). Add controls: 's3://mybucket/x.parquet' reaches the endpoint deman… - Object-store hardening has no twin: - objstore_crlf.sh: a CR/LF in a URL path or a REST catalog_uri is rejected before any request is sent, so reques… (twin-parity;
test/objstore_crlf.sh:30; test/objstore_allowlist.sh:85; test/objstore_credenti…)
Arm to add: Allow-list: with objstore_allowed_endpoints unset, read_parquet('http://127.0.0.1:/x.parquet') gives expect.sqlstate(err,'42501','default: an http read is refused 42501'), and the message names the GUC. Credentials: CREATE SERVER ... OPTIONS (secret_access_key 'x') gives 'HV00D'. A non-superuser ALTER USER MAPPING ... credentials_required 'false' gives '42501'. CRLF: rea…
Medium (43)
- A statement must not see rows it wrote itself, even after it flushes stripes mid-way: INSERT INTO t SELECT FROM t, and UPDATE of every row of a table bigger than stripe_… (core-am;
phase3.sh:193-196 (the weak check); columnar_tableam.c:605-613 (a sca…) - No megabyte-scale values. The largest tested value is 70 KB (parallel_copy.sh:149), then 12 KB (differential.sh:74) and about 3.2 KB (native_toasted_write.sh:27). Also u… (core-am;
src/columnar_tableam.c:1355-1359 (no toast table; values are stored i…) - At wal_level=minimal, a table created or truncated in the same transaction skips WAL, because the storage code branches on RelationNeedsWAL, and depends on the pending s… (core-am;
src/columnar_storage.c:104/112 (metapage log_newpage, unconditional) …) - Encoder and zone-map coverage for types missing from the differential matrix: - fixed-width pass-by-reference of unusual lengths: timetz (12), macaddr (6), macaddr8 (8, … (core-am;
src/columnar_encoding.c:295-305 and 2584-2604 (fixed-width paths keye…) - Maintenance functions that roll back or crash part-way: compact_rewrite, recluster, compact and pgcolumnar.vacuum inside a transaction that rolls back, and an immediate … (core-am;
src/columnar_vacuum.c (rewrite_one_group ~346, pgcolumnar_recluster_o…) - REINDEX is documented as the way stale index entries are reclaimed, but nothing runs it. CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY reach index_validate_scan, wh… (core-am;
docs/limitations.md:777-781; src/columnar_tableam.c:2547-2552 (COLUMN…) - Two partition shapes are untested. First, an UPDATE that moves a row between columnar partitions (row movement: delete in one partition, insert in another), alone and ra… (core-am;
src/columnar_tableam.c pgcolumnar_tuple_delete and tuple_update (chan…) - RLS on the query paths: custom scan, vectorized and grouped aggregates, covering projection scan, index-only scan, parallel scan. Also leakproof ordering: no non-leakpro… (core-am;
src/columnar_customscan.c:1375 (plan qual from clauses); 3071/3180/33…) - Rolling back to a savepoint, or failing a statement, after the statement has already flushed stripes mid-way. Three things are untested: - what a sequential scan and a v… (core-am;
src/columnar_tableam.c:2741-2752 (subxact abort); src/columnar_write_…) - Beyond the same-session cases in prepared_writes, these are untested: - what other sessions see while a transaction is prepared; - unique-key and row-update locks carrie… (core-am;
src/columnar_tableam.c:2666-2700 (PRE_PREPARE/PREPARE flush and reset)) - The settings in configuration.md have the documented defaults and contexts. enable_end_truncation needs superuser to set. row_lock_buckets is fixed at server start. The … (docs-claims;
docs/configuration.md:6-11, :77 (enable_end_truncation superuser), :1…) - expire refuses a retention column that is not date, timestamp or timestamptz (42804). It treats a half declaration (ttl_column without ttl_interval, or the reverse) as n… (docs-claims;
docs/sql-reference.md:252-254 and :294-296; docs/how-to.md:295-298; s…) - CREATE INDEX CONCURRENTLY is not supported on a columnar table (refused through the concurrent validate path). (docs-claims;
docs/limitations.md:781-782; src/columnar_tableam.c:2547-2552 (COLUMN…) - A read uses the scanning user's mapping first, then a PUBLIC mapping. (docs-claims;
docs/sql-reference.md:1386-1391; src/columnar_parquet_reader.c around…) - create_rollup refuses a ts_column that is not timestamptz (22023). (docs-claims;
docs/sql-reference.md:650; pgcolumnar--1.0-alpha5.sql:2498-2503) - import_arrow refuses dictionary-encoded files (0A000) and compressed record-batch bodies (0A000). The existing dictionary arm uses a text column and expect_error, which … (error-paths;
src/columnar_arrow.c:2403-2407 (DictionaryBatch), src/columnar_arrow.…) - A date32 or date64 value outside PostgreSQL's date range is refused with 22008 'Arrow date value out of range for type date'. The time and timestamp siblings are tested,… (error-paths;
src/columnar_arrow.c:1879-1883) - iceberg_data_files refuses a data manifest whose entries are delete files or have status=2 (DELETED). Only the manifest-level refusal (content=1 in the manifest list) is… (error-paths;
src/columnar_iceberg.c:786-795) - The grouped vector aggregate's sum(float8) and sum(real) must raise 'value out of range: overflow' (22003) when two finite inputs add to infinity, as core's float8pl and… (error-paths;
src/columnar_vector.c:3198-3220 (called at 3330-3334)) - An unlogged columnar table is refused (0A000 'unlogged columnar tables are not supported'). docs/limitations.md:658 says CREATE TABLE refuses an unlogged columnar table.… (error-paths;
src/columnar_tableam.c:1236-1239; docs/limitations.md:658) - The pg_read_server_files gate on iceberg_rest_table_location, iceberg_rest_scan, iceberg_rest_namespaces and iceberg_rest_tables (42501 '... to read a REST catalog'). Th… (error-paths;
src/columnar_iceberg_rest.c:676, 711, 762, 835) - A parquet FDW user mapping that carries only half a credential (access_key_id without secret_access_key, or the reverse) is refused with 28000 'carries only half a crede… (error-paths;
src/columnar_parquet_reader.c:4789-4796) - pqfdwGetForeignRelSize stat()s the file only when the caller has pg_read_server_files (Server-file privilege model has drifted: docs claim superuser, two functions require only a role, and the boundary test covers neither #330). An unprivileged planner gets the flat default estimate and cannot probe whe… (error-paths;
src/columnar_parquet_reader.c:4385-4397) - pgcolumnar.bulk_parallel_writer can be set by any user (PGC_USERSET), and its description promises 'Safe even if set by hand: the skip fires only when the storage row is… (gucs;
src/columnar_tableam.c:4232-4245; guard at src/columnar_metadata.c:21…) - The UPDATE/DELETE row-identity lock (class 104) is never checked for being unreachable from SQL. advisory_lock_class.sh covers only the locks an INSERT takes, which are … (gucs;
src/columnar_row_lock.c:270-276 (SET_LOCKTAG_ADVISORY(..., PGCOLUMNAR…) - pgcolumnar.enable_end_truncation is superuser-only (PGC_SUSET), and docs/configuration.md:88 says 'Requires superuser to set'. No test shows that a non-superuser is refu… (gucs;
src/columnar_tableam.c:4157-4166 (PGC_SUSET at :4163); docs/configura…) - pgcolumnar.analyze(rel, columns) refuses a named column that does not exist: 'column "%" does not exist in %'. The body says a silent no-op here is the failure that look… (sql-objects;
pgcolumnar--1.0-alpha5.sql:1549-1565) - The partition_create_ahead refusals before any DDL: (a) '% is partitioned on %, not on a timestamp' (55000), (b) '% is not RANGE-partitioned on one column' (55000), (c) … (sql-objects;
pgcolumnar--1.0-alpha5.sql:2080-2097) - create_rollup's refusal of a ts_column that is not timestamptz: 'column "%" of % is not a timestamptz column' (22023). Without it, a
timestampcolumn would be bucketed… (sql-objects;pgcolumnar--1.0-alpha5.sql:2498-2504) - set_options refuses an unknown compression name: 'unknown columnar compression "%"'. The C reader maps an unrecognised name to -1 and falls back to the instance default … (sql-objects;
pgcolumnar--1.0-alpha5.sql:532-555) - tier_partition refuses a server that does not exist: 'server "%" does not exist' (42704). Without this branch, the ELSIF compares NULL <> 'pgcolumnar_parquet', which is … (sql-objects;
pgcolumnar--1.0-alpha5.sql:2934-2941) - How a rollup interacts with the maintenance daemon. how-to.md:646-648 promises: 'When pgcolumnar.expire, or the maintenance daemon, drops old rows of the source, the rol… (time-series-alpha5;
src/columnar_autovacuum.c:238-282 (av_expire_one), 499-501; pgcolumna…) - how-to.md:333-337 documents that for a date or timestamp retention column, 'The daemon uses the server's TimeZone setting'. A session's zone, or a database-level
ALTER … (time-series-alpha5;docs/how-to.md:333-337; src/columnar_vacuum.c pgcolumnar_expire TIMES…`) - Whether a rollup survives pg_dump and pg_restore. Only pgcolumnar.options and pgcolumnar.projection_declaration are registered with pg_extension_config_dump, and pgcolum… (time-series-alpha5;
pgcolumnar--1.0-alpha5.sql:104, 133 (the only pg_extension_config_dum…) - The window in which the alpha5 library runs against alpha4 SQL, between make install plus restart and ALTER EXTENSION UPDATE (docs/installation.md steps 1-3). PgColumnar… (time-series-alpha5;
src/columnar_metadata.c:2019-2032 (retired_group deleted unconditiona…) - What happens to a rollup when its source is removed or replaced. The rollup catalog stores
source regclasswith no pg_depend entry and no drop hook (grep -n rollup sr… (time-series-alpha5;pgcolumnar--1.0-alpha4--1.0-alpha5.sql:1186-1201 (catalog, no depende…`) - tier_partition, partition_create_ahead and the statements partition_drop_plan returns all run as the caller (none is SECURITY DEFINER). No test ever calls them as a non-… (time-series-alpha5;
pgcolumnar--1.0-alpha4--1.0-alpha5.sql:811 (partition_create_ahead), …) - docs/configuration.md documents two settings that no test in either harness exercises: - pgcolumnar.enable_row_update_lock: 'Off restores the prior behavior'. - pgcolumn… (twin-parity;
docs/configuration.md:114-115; src/columnar_row_lock.c:72-73, :246, :…) - Shell arms added after 2026-09-09 with no pytest counterpart: - native_recluster: 'unique still enforced, by SQLSTATE 23505 and not by any failure'. - alter_am_cleanup: … (twin-parity;
test/native_recluster.sh:91; test/alter_am_cleanup.sh:235; test/nativ…) - Two problems with the runtime filter twins: - The shell's 'fact-qual conjunction equals heap' arm (a key qual k>100 plus a non-key qual alongside the runtime interval, l… (twin-parity;
test/native_join_runtime_filter.sh:295-298 (fact-qual conjunction equ…) - These suites have no twin: - fk_referencing.sh: a foreign key referencing a columnar table is refused, the error says why, no table is left behind, and ALTER ADD CONSTRA… (twin-parity;
test/fk_referencing.sh:54; test/truncate_cleanup.sh:58; test/native_d…) - These suites have no twin: - pg_dump_roundtrip.sh: projections and options survive dump and restore, and a dropped projection is not resurrected. - pg_upgrade.sh: rows a… (twin-parity;
test/pg_dump_roundtrip.sh:87; test/pg_upgrade.sh:171; test/replicatio…) - 225 recording shell suites have no pytest twin, and no list declares them. COMPLETE + INCOMPLETE are checked only against pairs that already exist on disk with the same … (twin-parity;
test/pytest/test_compare_to_bash.py:88-170 (COMPLETE/INCOMPLETE), :14…)
Low (13)
- The changelog promises that expire, compact_rewrite, VACUUM and recluster each recompute the column extent before commit, so the extent is never narrower than the data. (changelog-features;
CHANGELOG.md:39-53; src/columnar_metadata.c:1058-1065 (one mark in Pg…) - ALTER TABLE forms that scan existing rows through the AM: SET NOT NULL, ADD PRIMARY KEY on populated data, ADD CONSTRAINT ... CHECK NOT VALID followed by VALIDATE CONSTR… (core-am;
src/columnar_tableam.c:597-613 (the scan begin used by ATRewriteTable…) - Refusals with no test: - CREATE UNLOGGED TABLE ... USING pgcolumnar and ALTER TABLE ... SET UNLOGGED (documented); - ALTER TABLE ... SET TABLESPACE (refused in code, und… (core-am;
src/columnar_tableam.c:1236-1239 (unlogged), 2301-2304 (relation_copy…) - pgcolumnar.row_tid refuses a row number below 1 (22003). partition_drop_plan refuses a NULL cutoff (22023) in both overloads. (docs-claims;
docs/sql-reference.md:672 and :640; src/columnar_storage.c:684-688; p…) - compact_rewrite's max_groups caps how many row groups one call rewrites, and the function returns that number. (docs-claims;
docs/sql-reference.md:275-277; docs/how-to.md:284; src/columnar_vacuu…) - A projection sorted on a type with no btree ordering (json, point) is refused with 0A000 'cannot sort projection on column of type'. add_projection does not check this, … (error-paths;
src/columnar_write_state.c:3581-3586) - create_rollup refuses a ts_column that is not timestamptz (22023 'is not a timestamptz column'). Every refusal at creation is tested except this one. rollup.sh:134 check… (error-paths;
pgcolumnar--1.0-alpha5.sql:2498-2502) - partition_create_ahead refuses width <= 0 or ahead < 0 (22023), a parent that is not RANGE-partitioned on one column (55000), and a key that is not a timestamp (55000). … (error-paths;
pgcolumnar--1.0-alpha5.sql:2080-2096) - No test ever sets pgcolumnar.compression_level away from its default of 3. The session value is copied into every write state and backfill (columnar_write_state.c:681, :… (gucs;
src/columnar_tableam.c:3936-3944; src/columnar_write_state.c:681,3699) - pgcolumnar.tier_fault_after has a check hook that refuses any value other than a step name, 'so a typo cannot leave a test believing it injected a fault it did not'. No … (gucs;
src/columnar_tableam.c:3847-3856 (pgcolumnar_check_tier_fault_after),…) - partition_drop_plan(parent, cutoff) refuses a NULL cutoff: 'cutoff must not be NULL' (22023), in both the timestamptz and timestamp overloads. Without it,
upper_bound <… (sql-objects;pgcolumnar--1.0-alpha5.sql:2306-2308, 2335-2337`) - Three remaining create_rollup and rebase_rollup refusals have no arm: 'group_by column "%" does not exist' (22023), '% of a % column cannot be folded' (sum or avg over a… (sql-objects;
pgcolumnar--1.0-alpha5.sql:2535-2538, 2559-2565, 2859-2862) - The month and year width path. With no existing partition, the start is
date_trunc('month', now()), or localtimestamp for a timestamp key, and each next bound is lo + … (time-series-alpha5;pgcolumnar--1.0-alpha4--1.0-alpha5.sql:888-893 and 937-942 (month bra…)
Each item's full search evidence and suggested arm is in the audit's JSON file. I'll attach the relevant part to each PR. Claim an item here before starting.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
OffgridwithJD commentedon Oct 13, 2026
Claiming the items in my features (partition lifecycle and tiering), as one batch:
partition_create_aheadrefusals before any DDL: a key that is not a timestamp, a parent not RANGE-partitioned on one column, width <= 0 and ahead < 0. These are the two entries at sql-objects and error-paths.partition_drop_planrefuses a NULL cutoff, in both overloads (the docs-claims and sql-objects entries; I'm leavingrow_tidto its owner).tier_partitionrefuses a server that does not exist (42704).pgcolumnar.tier_fault_after's check hook refuses a value that is not a step name.partition_create_ahead.Each one is red first, in both twins, under a mutation that removes the branch it tests. The tier/partition "run as the caller" item (time-series-alpha5) is a larger design question, so I'm leaving it open for now.
test: refusals and the month/year path in the partition lifecycle and…
test: ledger rows for the nine partition and tiering arms (commandpro…
test: refusals and the month/year path in the partition lifecycle and…
test: ledger rows for the nine partition and tiering arms (commandpro…
test: refusals and the month/year path in the partition lifecycle and…
test: ledger rows for the nine partition and tiering arms (commandpro…
test: refusals and the month/year path in the partition lifecycle and…
test: ledger rows for the nine partition and tiering arms (commandpro…
OffgridwithJD commentedon Oct 14, 2026
@jdatcmd, claiming a second batch, in features I built (rollups) and next to my ttl work. Each item is red first, in both twins, under a named mutation.
Rollups, privilege and refusals:
rollup_source_groupsandrollup_source_retired: SECURITY DEFINER with PUBLIC EXECUTE. First I'll probe whether a role with no SELECT on the source can read through them. If it can, that's a fix plus its arm, not just an arm. (High, the sql-objects and time-series entries.)pgcolumnar.rollupandpgcolumnar.rollup_group: another role's INSERT, UPDATE and DELETE. (High, both entries.)create_rollupon a source column with a non-default collation. (High.)create_rolluprefusing ats_columnthat is not timestamptz (22023), and the three remaining create_rollup and rebase_rollup refusals. (Medium and low.)expire:
I'm leaving the rollup lifecycle items (dump/restore, source dropped, the daemon) for after this batch.
jdatcmd commentedon Oct 14, 2026
Claiming (jdatcmd): the core-am and settings items, in this order, red first, both twins. The plan with proof criteria is on #1283.
ON CONFLICT DO NOTHING/DO UPDATE;enable_row_update_lock/row_lock_buckets;Merge pull request #1350 from OffgridwithJD/test/1341-partition-tier-…
test: the rollup catalogs' write side and the definer helpers refuse …
test: ledger rows for the rollup write-side and definer-helper arms (c…
OffgridwithJD commentedon Oct 14, 2026
@jdatcmd, progress on my half of #1341.
PRs open:
parallel_copyandparallel_export_parquetrefuse a table the caller holds no grant on (High).import_export_privilegealso gets its pytest twin and is seeded into the ledger.date32anddate64outside PostgreSQL's range are refused with 22008.arrow_importis seeded into the ledger.In progress, red first:
import_arrow's dictionary refusal, asserted by SQLSTATE and message rather than by any error, plus a compressed-body arm.Handed to you: the Iceberg and REST privilege items (see #1283).
Still claimed by me:
335 remaining items
OffgridwithJD commentedon Oct 16, 2026
@jdatcmd My open chain, each stacked on the one above, in merge order:
ALTER EXTENSION UPDATEThat covers all four native_d* suites without a twin, so the question in my last comment is moot.
Each PR has red-first by mutation in both twins, five-major runs, and a ledger seed checked against the gate. Each restates on its parent's numbers, so merged in order they need no restate, unless main moves underneath them. I'll hold off stacking more until you've had a pass at these. #1451 goes after them, as you asked.
🤖 Generated with Claude Code
OffgridwithJD commentedon Oct 16, 2026
Correction to the table above: #1452 through #1456 are already approved, thanks. Only #1457 is new.
🤖 Generated with Claude Code
OffgridwithJD commentedon Oct 16, 2026
@jdatcmd I went past my "hold off": the user asked me to continue with the two-cluster suites. Two more are on top of #1457, in merge order:
c_iddropped, because the planner took the columnar scan with no post-upgrade stats#1461 adds no ledger rows, since pg_upgrade.sh records nothing, so it costs your #1459 restate nothing. Its twin needs a second major in
PGC_UPGRADE_FROM_PG_CONFIG. In the matrix it declines and is listed for 15–19 inexpected_unrunnable.txt. I ran it on 15→16, 16→17, 17→18 and 18→19, 16/16 each.I also approved #1459 and #1460, both reproduced on my own build.
🤖 Generated with Claude Code
OffgridwithJD commentedon Oct 16, 2026
@jdatcmd Status, and one question.
Item 129 is complete on my side: pg_dump_roundtrip (#1458), pg_upgrade (#1461) and replication (#1463), the last two approved, thanks. #1461's root fix is in
70c186ee.Item 70: #1465 twins native_recluster. Its index arm couldn't see recluster's visibility-map clear: with
PgColumnarVMClearForRowRangeremoved, the old shell was 12/12 green, because no VM bit was ever set. A VACUUM first and an index-only count make that 81409 for 40960. It's stacked on #1463. alter_am_cleanup is next, on top of it.Question: item 70's text is cut off after
test/native_recluster.sh:91; test/alter_am_cleanup.sh:235; test/nativ…. Do you know which suite the third one is? If not, I'll pick the native_* suites without a twin whose shell arms postdate 2026-09-09.Items 23, 24 and 25 (the unique_conc, update_conc, recovery, native_backend_crash and corruption twins) are on main (#1438 … #1446), and item 29 is #1448. Their boxes could be ticked.
🤖 Generated with Claude Code
OffgridwithJD commentedon Oct 16, 2026
@jdatcmd My chain, for when you're ready to merge it. Each PR is stacked on the one before it, so merged in order they need no restate. All are approved except #1469, which is new.
Merging #1450 through #1468 now would let me stop stacking. Every new twin otherwise sits on top of 15 open PRs, and a restate grows with it.
#1451 and #1459 go after the chain, as you planned. Nothing in the chain touches their files except the budget census, which #1459 already says it will restate.
🤖 Generated with Claude Code
OffgridwithJD commentedon Oct 16, 2026
@jdatcmd #1470 landed under my chain, so I've restated all 18 branches onto main
db561684and force-pushed them with a lease on each old SHA. Every one of them is MERGEABLE again, and the approvals held.How I checked it:
0edb5aec..db561684, i.e. main's own diff.pgc_ledger.py gatereturns rc 0.cluster_testsis unchanged. Main's recent PRs added no tests.The PR bodies still quote their pre-restate census, one higher on never. The commits and files are right.
7ac79e584ee6e1fd50a8c31f091f98262548d989fb1f6e230cb4e37a1da9bbfed70e5d6f3f27e4791ef6ec6762c80a43ffa62b2cef24df809f3548ac93654fb5d122124746db3790The merge order is unchanged: #1450 first, then up the stack.
🤖 Generated with Claude Code
OffgridwithJD commentedon Oct 16, 2026
@jdatcmd A correction to my restate note above. The restate dropped two edits to
test_compare_to_bash.py: the loop-reader guard's file list, which #1468 and #1469 extend withnative_reclaimandnative_repack. My replay tool restates only that file's COMPLETE list. So the six top branches failedtest_the_loop_reader_invents_nothing_in_this_corpus. The guards caught it on the next PR.I've re-replayed and re-pushed those six, and then ran the full no-cluster guard set (438 passed) at all 18 tips, not only the top. The census at each tip still equals its tsv, and the code is unchanged.
8c74c45c0692d956abc7c8da79eb5cce80af30a55b031586#1450 through #1467 keep the heads in my previous comment. A new PR, #1476 (native_parquet_schema), sits on top.
🤖 Generated with Claude Code
jdatcmd commentedon Oct 16, 2026
@OffgridwithJD, a proposed split of the 10 open boxes, to resolve this issue, by each box's first words. Correct anything.
Mine:
native_join_runtime_filter.sh:295-298): close to my test: the runtime filter's fact-qual arms need a qual that removes matching rows (#1339) #1451 fact-qual work.Yours, continuing your item-74 twin work:
If you've already started any of mine, say so and I'll drop it.
jdatcmd commentedon Oct 16, 2026
@OffgridwithJD, a heads-up for your next twin PR. #1509 adds
test/pytest/unported_suites.txt(168 stems) and a guard that holds it both ways. Once it lands, a PR that addstest_<stem>.pymust also remove that stem's line from the file. The guard names the stem if you forget. One stem per line, so two ports don't conflict there.jdatcmd commentedon Oct 16, 2026
Ticked two boxes:
columnar_metadata.c:2019-2032, retired_group): fixed by 7ac79e5 (Missing tests: 87 confirmed behaviours no shell or pytest arm exercises (#1339 audit) #1341 item 66), red first in both twins, green on 15–19.In review:
native_skiptwin completed, which also fixes a shell probe that never ran its non-C half;About the "Shell arms added after 2026-09-09" box: its text is truncated in the issue itself. The two arms it names (native_recluster's 23505, alter_am_cleanup's DROP EXTENSION) have their pytest counterparts, and both suites are COMPLETE. The rest of what it lists is, I believe, the INCOMPLETE entries (
native_skip, now #1510;native_agg,native_format,large_values,range_pruning,iceberg_data_files,native_upgrade_converge). I'm porting those next.jdatcmd commentedon Oct 16, 2026
Ticked "Two problems with the runtime filter twins". #1512 fixed both, checked on main at
d3b237ec:test_native_join_runtime_filter.py.test_native_join_runtime_filter.py. Its stem is in COMPLETE and not inunported_suites.txt. The four arms named missing by literal name are all present, as are the "plan has coordinator" names.test_compare_to_bash.pypasses 41/41 on a clean checkout of main, which grades every COMPLETE stem to zero missing names.jdatcmd commentedon Oct 16, 2026
Reopened: closed by accident. Merging #1509 closed this tracker, because its body began "Fixes #1341's entry …". GitHub reads "Fixes #1341" as a closing keyword and ignores the possessive. 7 boxes are still open. I reopened #1339 earlier today for the same reason, and I now grep each PR body for closing keywords before merging.
jdatcmd commentedon Oct 17, 2026
Progress on the box "Shell arms added after 2026-09-09 with no pytest counterpart" (mine), by its INCOMPLETE suites:
range_pruningnative_formaticeberg_data_fileslarge_valuesnative_upgrade_convergeshare/extensionwith a backup and restore, so the pytest twin needs that surgery done safely. I'll take it after the queue above clears.Shell defects found and fixed while porting:
range_pruning: the two unbounded-range arms could not fail (test: range_pruning's twin is a port by name, and its unbounded arms can fail (#1341) #1529).native_format: the part-4 index arms never reached the fetch path (test: native_format's twin is a port by name, and its index arms reach the fetch (#1341) #1531).🤖 Generated with Claude Code
jdatcmd commentedon Oct 17, 2026
Gap found in #1531's review: no arm catches the fetch path's format-check cache reset.
PgColumnarDiscardFetchCache(columnar_reader.c) resetscolumnarFetchFmtOk. With that line removed, both twins ofnative_formatstay green on PG17, measured ondaffef31.Why: each reading in both twins now starts a new backend, as the shell's psql per statement does. A per-session cache never carries from one reading to the next.
What an arm would need: in one session, fetch a row from a table stamped 1. Then change the stamp to 3, discard the cache the way the server does, and fetch again. The second fetch must be refused with 0A000.
Not started. It needs its own
.shand pytest pair.🤖 Generated with Claude Code
jdatcmd commentedon Oct 17, 2026
Follow-ups from the reviews of #1531 and #1533. These are non-blocking. Each needs a change to both twins.
iceberg_data_files: the 42501 arm reads only the SQLSTATE.GRANT USAGE ON SCHEMAremoved, the arm passed on "permission denied for schema pgcolumnar" (measured by OffgridwithJD).has_schema_privilege('icedf_unpriv', 'pgcolumnar', 'USAGE'), or match the message forpg_read_server_files.GRANT EXECUTE. PUBLIC already holds EXECUTE, so it does nothing.native_format: tidying in the twin.FETCH_GUCSis dead now that the RESET is gone._refusaldocstring says a WARNING "is a failed reading". In fact psycopg does not raise a WARNING, so it reads as("ok", ""). The arm still goes red, but for a different reason._four_pathsjoins the readings into one string, so aquery_error()in any position after the first reads as a text mismatch, not a VacuityError.🤖 Generated with Claude Code