| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | 303677365(at)qq(dot)com |
| Subject: | BUG #19819: Foreign key DDL can fail with internal errors (XX000) from elog(): "key columns are not both collata |
| Date: | 2026-10-30 06:55:38 |
| Message-ID: | 19819-8aec9277898c3c06@postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
The following bug has been logged on the website:
Bug reference: 19819
Logged by: chunling qin
Email address: 303677365(at)qq(dot)com
PostgreSQL version: 18.6
Operating system: Linux (TencentOS 4.4, x86_64)
Description:
Hello,
ATAddForeignKeyConstraint() (src/backend/commands/tablecmds.c) contains
two elog(ERROR, ...) calls that are reachable from ordinary user DDL.
elog() produces SQLSTATE XX000 (internal error), which per convention
should mean "this should never happen" -- but both sites can be reached
by plain CREATE TABLE ... REFERENCES / ALTER TABLE ... ADD CONSTRAINT,
and one of them rejects a combination that ought to work.
1) "key columns are not both collatable"
The referencing and referenced columns have an assignable implicit cast
(pg_cast: castcontext='i'), but the two types differ in collatability.
Reproducer:
CREATE TABLE q1p (a text PRIMARY KEY);
CREATE TABLE q1f (a "char" REFERENCES q1p(a));
-- ERROR: XX000: key columns are not both collatable
The same happens for "char" -> varchar (implicit function cast), and for
binary casts of the stats types:
CREATE TABLE q2p (a bytea PRIMARY KEY);
CREATE TABLE q2f (a pg_mcv_list REFERENCES q2p(a));
-- ERROR: XX000: key columns are not both collatable
For comparison, the reverse combinations (no implicit cast exists) are
rejected with a proper user-level error, so the normal rejection path
does exist:
CREATE TABLE q4p (a "char" PRIMARY KEY);
CREATE TABLE q4f (a text REFERENCES q4p(a));
-- ERROR: 42804: ... incompatible types: text and "char"
Expected for case 1: since "char" -> text is an implicit function cast
(pg_cast: castmethod='f', castcontext='i'), this foreign key combination
should either be accepted, or be rejected with a normal user-level
SQLSTATE (42804 "datatype mismatch" / "foreign key constraint cannot be
implemented"), not an XX000 internal error.
2) "missing operator %d(%u,%u) in opfamily %u"
CREATE UNIQUE INDEX accepts an operator class that lacks an equality
operator (strategy 3), and the failure only surfaces later, as an
internal error, when the foreign key is added:
CREATE OPERATOR CLASS cbh_noeq_ops FOR TYPE int4 USING btree AS
OPERATOR 1 pg_catalog.<(int4,int4), OPERATOR 2
pg_catalog.<=(int4,int4),
OPERATOR 4 pg_catalog.>=(int4,int4), OPERATOR 5
pg_catalog.>(int4,int4),
FUNCTION 1 pg_catalog.btint4cmp(int4,int4); -- deliberately no
strategy 3
CREATE TABLE n1p (a int4);
CREATE UNIQUE INDEX n1i ON n1p (a cbh_noeq_ops); -- accepted
CREATE TABLE n1f (a int4 REFERENCES n1p(a));
-- ERROR: XX000: missing operator 3(23,23) in opfamily 61711
Expected for case 2: a user-level error such as "operator class
cbh_noeq_ops has no equality operator", or rejecting the index at
CREATE UNIQUE INDEX time.
Both elog() sites are in ATAddForeignKeyConstraint()
(src/backend/commands/tablecmds.c): the collatability check and the
equality-operator lookup. Turning them into ereport() with proper
SQLSTATEs (and ideally allowing case 1, since the implicit cast exists)
would match how the surrounding code already reports user-reachable
conditions.
Reproduced on:
- PostgreSQL 20devel (snapshot of 2026-10-27, x86_64-pc-linux-gnu)
- PostgreSQL 18.6 (REL_18_STABLE, self-built)
Both DDL paths (CREATE TABLE ... REFERENCES and ALTER TABLE ... ADD
CONSTRAINT) were checked; re-running after DROP and from fresh sessions
gives identical results.
Regards,
Chunling Qin
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-10-30 06:58:56 | BUG #19820: ANALYZE can store empty (0-dimensional) stavalues that the statistics import functions refuse to res |
| Previous Message | Gaurav Singh | 2026-10-30 06:50:13 | Re: postgres_fdw returns wrong rows for age(timestamptz, timestamptz) filters |