What
pgcolumnar.import_parquet() on a partitioned table with relam = pgcolumnar segfaults the backend and restarts the cluster. This is not
#1259's class and #1261 does not fix it: import_parquet never asks whether its
target is columnar at all.
LOG: server process (PID 3328813) was terminated by signal 11: Segmentation fault
DETAIL: Failed process was running: SELECT pgcolumnar.import_parquet('pp'::regclass, '/tmp/v2.parquet');
LOG: all server processes terminated; reinitializing
LOG: database system was not properly shut down; automatic recovery in progress
Signal 11, not an assert — so a production build is affected
#1259's family is an Assert that a non-assert build compiles out, leaving a
wrong answer. This one is a segfault, and I measured both:
/usr/local/pg17a PostgreSQL 17.6 --enable-cassert signal 11
/usr/local/pg18n PostgreSQL 18 cassert flags: 0 signal 11
Neither run produced a failed Assert line — traps before = 0, traps after = 0. The crash is in ordinary code.
Reproduction, with both controls
PREMISE file bytes=[757]
PREMISE parent relkind=[p] relfilenode=[0]
CONTROL alive before = [up]
import_parquet(PARENT, valid file) rc=2 cluster DOWN, signal 11
LEAF control import_parquet('pp1', same file) rc=0 imported 50 rows
SIBLING control import_arrow('pp', valid file) rc=1
ERROR: relation "pp" is not a columnar table
The leaf control says the call works and the file is good. The sibling
control is the sharper one: import_arrow on the same parent with the same
shape refuses cleanly, so the refusal is something this code could have done and
does not.
CREATE TABLE src (a int, b text) USING pgcolumnar;
INSERT INTO src SELECT g,'x'||g FROM generate_series(1,50) g;
CREATE TABLE pp (a int, b text) PARTITION BY RANGE (a) USING pgcolumnar;
CREATE TABLE pp1 PARTITION OF pp FOR VALUES FROM (0) TO (100);
SELECT pgcolumnar.export_parquet('src'::regclass, '/tmp/v.parquet');
SELECT pgcolumnar.import_parquet('pp'::regclass, '/tmp/v.parquet'); -- SIGSEGV
Cause: the check is absent, not wrong
grep -c PgColumnarIsColumnarRelation src/columnar_parquet_reader.c -> 0
pgcolumnar_import_parquet checks pg_read_server_files, then ACL_INSERT,
then RLS, then resolves the path, then table_open(relid, RowExclusiveLock) and
proceeds. It never asks whether the relation is columnar, so it is not one of
the 31 call sites #1261 changed and the predicate fix cannot reach it.
Swept across the tree, it is the only file in that position:
file entrypoints getarg_oid(0) predicate uses
columnar_arrow.c 2 2 2
columnar_parallel_copy.c 2 1 1
columnar_parallel_export.c 1 1 2
columnar_parquet.c 1 1 1
columnar_parquet_reader.c 5 1 0 <-
columnar_projection.c 4 4 5
columnar_vacuum.c 14 11 10
columnar_visibilitymap.c 2 2 2
columnar_vacuum.c at 11 against 10 is a count and not a proof, so it is worth
a look rather than a claim.
Who can reach it
Not an ordinary user. import_parquet requires superuser or
pg_read_server_files, plus INSERT on the target:
if (!has_privs_of_role(GetUserId(), ROLE_PG_READ_SERVER_FILES))
ereport(ERROR, ... "must be superuser or a member of the
pg_read_server_files role to read a server file");
So the population is roles a deployment has deliberately granted file-reading to
— narrower than #1259's "grant USAGE on the schema", and still a role that is
not superuser taking the cluster down.
Found how
Following up my own caveat on #1259 that survived meant "did not abort" rather
than "correct". The earlier 22-probe recorded import_parquet as surviving —
because the file did not exist, so it failed at the open before reaching the
crash. It only appears when the path resolves, which is why a sweep that reuses
one fixture misses it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MpajdQbkVJ9ey1XyYHcikP
What
pgcolumnar.import_parquet()on a partitioned table withrelam = pgcolumnarsegfaults the backend and restarts the cluster. This is not#1259's class and #1261 does not fix it:
import_parquetnever asks whether itstarget is columnar at all.
Signal 11, not an assert — so a production build is affected
#1259's family is an
Assertthat a non-assert build compiles out, leaving awrong answer. This one is a segfault, and I measured both:
Neither run produced a
failed Assertline —traps before = 0,traps after = 0. The crash is in ordinary code.Reproduction, with both controls
The leaf control says the call works and the file is good. The sibling
control is the sharper one:
import_arrowon the same parent with the sameshape refuses cleanly, so the refusal is something this code could have done and
does not.
Cause: the check is absent, not wrong
pgcolumnar_import_parquetcheckspg_read_server_files, thenACL_INSERT,then RLS, then resolves the path, then
table_open(relid, RowExclusiveLock)andproceeds. It never asks whether the relation is columnar, so it is not one of
the 31 call sites #1261 changed and the predicate fix cannot reach it.
Swept across the tree, it is the only file in that position:
columnar_vacuum.cat 11 against 10 is a count and not a proof, so it is wortha look rather than a claim.
Who can reach it
Not an ordinary user.
import_parquetrequires superuser orpg_read_server_files, plusINSERTon the target:So the population is roles a deployment has deliberately granted file-reading to
— narrower than #1259's "grant USAGE on the schema", and still a role that is
not superuser taking the cluster down.
Found how
Following up my own caveat on #1259 that
survivedmeant "did not abort" ratherthan "correct". The earlier 22-probe recorded
import_parquetas surviving —because the file did not exist, so it failed at the open before reaching the
crash. It only appears when the path resolves, which is why a sweep that reuses
one fixture misses it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01MpajdQbkVJ9ey1XyYHcikP