pg_dump: assert failure sorting casts/transforms
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253500psql -h localhost -U postgresBuilt from patchset v8 (message #8), October 06, 2026 at 07:43 AM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t253500_8 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t253500_8 && git checkout t253500_8Patchset v8 (message #8) is on t253500_8
Hi hackers,
pg_dump's DOTypeNameCompare() can reach its Assert(false) fall-through
(pg_dump_sort.c) when a database contains two casts, or two transforms,
whose types share a typname across different schemas. On an
assertion-enabled build this aborts the dump:
pg_dump: pg_dump_sort.c:479: DOTypeNameCompare: Assertion `0' failed.
I hit this in the field with an extension that defines its own json type
alongside pg_catalog.json and casts to both, but it reproduces trivially
without any extension:
CREATE SCHEMA s;
CREATE TYPE public.tgt AS ENUM ('a');
CREATE TYPE s.tgt AS ENUM ('a');
CREATE TYPE public.src AS ENUM ('a');
CREATE CAST (public.src AS public.tgt) WITH INOUT;
CREATE CAST (public.src AS s.tgt) WITH INOUT;
$ pg_dump --schema-only ... # aborts on an --enable-cassert build
The cause: casts and transforms have no namespace of their own, and
getCasts() / getTransforms() build their sort name from the unqualified
type (and language) names. Two casts therefore tie on the full sort key
whenever their source/target type names match but the types live in
different schemas ("sourcetype tgt" in the example); transforms tie the same
way as "typname langname". Since DOTypeNameCompare() has no DO_CAST or
DO_TRANSFORM tiebreaker, such pairs fall through to the assert on master and
RL_19_STABLE branches.
The attached patch adds the missing tiebreakers, comparing the referenced
types by their full natural key via the existing pgTypeNameCompare()
(nspname, then typname) — the same helper already used for function
arguments and operator operands. For transforms, comparing trftype alone
is sufficient, since a name tie already implies the same unqualified typname
and language name. It also adds regression coverage to 002_pg_dump.pl (two
casts and two transforms sharing a typname across schemas), which aborts an
unpatched assert-enabled run and passes with the fix.
Regards,
--
Alexander Kukushkin
Hi Alexander,
The fix looks reasonable to me. It matches the existing natural-key
approach in DOTypeNameCompare(), and using pgTypeNameCompare() for the
referenced types seems like the right way to avoid falling back to OID
order.
One small test-coverage suggestion - the added cast test exercises the
casttarget tie-breaker, because both casts use the same source type
and only the target type differs by schema. Since the patch also adds
a castsource tie-breaker, it may be worth adding a symmetric case
where two source types share the same typname across schemas and cast
to the same target type. That would cover both new comparisons
explicitly.
Best Regards,
Nitin Jadhav
Azure Database for PostgreSQL
Microsoft
Hi,
On Thu, 20 Aug 2026 at 13:04, Nitin Jadhav <nitinjadhavpostgres@gmail.com>
wrote:
One small test-coverage suggestion - the added cast test exercises the
casttarget tie-breaker, because both casts use the same source type
and only the target type differs by schema. Since the patch also adds
a castsource tie-breaker, it may be worth adding a symmetric case
where two source types share the same typname across schemas and cast
to the same target type. That would cover both new comparisons
explicitly.
Thank you for a review Nitin, here is a v2 version of the patch with
extended test coverage.
--
Regards,
--
Alexander Kukushkin
Thank you for a review Nitin, here is a v2 version of the patch with extended test coverage.
Thanks Alexander, v2 addresses my earlier comment. The added src2
casts now cover the case where the source type's schema is the
distinguishing part of the cast key, so both the castsource and
casttarget comparisons are exercised.
I noticed one small test-completeness nit: the test creates four
casts, but I see regex checks for only three emitted CREATE CAST
statements. The unchecked one appears to be: CREATE CAST
(public.dump_cast_src2 AS public.dump_cast_tgt) WITH INOUT; Since this
is the public-side member of the new source-type ambiguity pair, would
it be worth adding a matching regexp for symmetry/completeness?
A couple of minor readability thoughts:
Now that the test covers two separate cases, target-side and
source-side ambiguity, perhaps the test type names could make that
distinction explicit. For example, instead of using dump_cast_src2,
names along these lines might make the intent clearer:
public.dump_cast_src_for_target_test,
public.dump_cast_src_for_source_test,dump_cast_schema.dump_cast_src_for_source_test.
That would make it easier to see which casts exercise the target-type
tie-breaker and which casts exercise the source-type tie-breaker.
The DO_TRANSFORM comment says "Same unqualified-typname ambiguity as
casts; break by type." That is understandable, but maybe it could
mention why comparing only trftype is enough here: the language name
has already been compared as part of dobj.name, so trftype is the
remaining natural-key field that can distinguish the two transform
objects.
These are minor; the code change itself looks reasonable to me.
Best Regards,
Nitin Jadhav
Azure Database for PostgreSQL
Microsoft
On Fri, 21 Aug 2026 at 09:40, Nitin Jadhav <nitinjadhavpostgres@gmail.com>
wrote:
These are minor; the code change itself looks reasonable to me.
Thank you Nitin,
here is v3 version of the patch addressing all nit-picks
Regards,
--
Alexander Kukushkin
here is v3 version of the patch addressing all nit-picks
Thanks for the v3 patch. It addresses my earlier comments, and the fix
and regression coverage look good to me. I have no other comments.
One optional follow-up thought: the current tests exercise the
assertion-failure case well. Order-aware checks could additionally
verify the OID-independent ordering property and help detect future
regressions to an OID fallback. I realize the earlier natural-key
ordering changes do not consistently include explicit output-order
checks either, so I do not think this should delay the current patch
or require a v4. If there is interest, I can investigate a separate
follow-up patch for suitable coverage of this case and the earlier
OID-independence cases.
Since the issue also affects REL_19_STABLE, I think the fix should be
applied there along with master. No older back-branches appear to be
affected.
Best Regards,
Nitin Jadhav
Azure Database for PostgreSQL
Microsoft
On Fri, Aug 21, 2026 at 11:59:53AM +0200, Alexander Kukushkin wrote:
here is v3 version of the patch addressing all nit-picks
Thanks.
With no DO_CAST or DO_TRANSFORM tiebreaker, such pairs reach the
Assert(false) fall-through added in commit 0decd5e89db (aborting
assert-enabled pg_dump), or on non-assert builds sort by OID, reintroducing
exactly the schema-diff instability that commit and its follow-ups
(b61a5c4bed7, 4921a5972a3, d49936f3028) have been eliminating.
Since this is already the fourth follow-up to my original change, I had Opus 5
look for more ways to reach the assertion. It found one more:
D1 DO_POLICY: the "RLS enabled" pseudo-object borrows its table's relname, so
it ties with a policy named after that same table. An assert-enabled
pg_dump aborts; a production build orders the two by comparing a pg_class
OID against a pg_policy OID, which pg_upgrade inverts.
Let's fix that at the same time. Would you like to add that, or would you
like me to add it?
I'm attaching the larger Opus 5 report as an FYI. It found many other pg_dump
ordering problems distinct from the DOTypeNameCompare() assertion, and D3 is a
notable functional bug. They're off-topic for $SUBJECT, though.
Hi Noah,
sorry that it took so long to get back.
On Wed, 9 Sept 2026 at 02:04, Noah Misch <noah@leadboat.com> wrote:
Since this is already the fourth follow-up to my original change, I had
Opus 5
look for more ways to reach the assertion. It found one more:D1 DO_POLICY: the "RLS enabled" pseudo-object borrows its table's
relname, so
it ties with a policy named after that same table. An assert-enabled
pg_dump aborts; a production build orders the two by comparing a
pg_class
OID against a pg_policy OID, which pg_upgrade inverts.Let's fix that at the same time. Would you like to add that, or would you
like me to add it?
Please find the attached v4 version of the patch that also handles RLS
policies.
--
Regards,
--
Alexander Kukushkin