| From: | PG Doc comments form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-docs(at)lists(dot)postgresql(dot)org |
| Cc: | kengruven(at)gmail(dot)com |
| Subject: | Shadowing type names because I am not smart |
| Date: | 2026-07-29 22:19:06 |
| Message-ID: | 178536354641.1228.15444395280389125710@wrigleys.postgresql.org |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-docs |
The following documentation comment has been logged on the website:
Page: https://www.postgresql.org/docs/18/sql-syntax-lexical.html
Description:
Hello, Postgres!
When playing around, I accidentally created a composite type called "text",
and this caused all kinds of trouble that took a while for me to sort out.
(Some GUI clients really don't behave nicely in this situation.) Even after
I realized what I'd done, I found that it behaved in strange ways that
differed from user type names which shadow other built-in type names.
This is all on Linux (Debian stable), using Postgres 17.9.
(1) New type with unique name: works as expected, of course.
typetest=> create type t as (x smallint);
CREATE TYPE
typetest=> \dT+
List of data types
Schema | Name | Internal name | Size | Elements | Owner | Access
privileges | Description
--------+------+---------------+-------+----------+-------+-------------------+-------------
public | t | t | tuple | | ken |
|
(1 row)
typetest=> drop type t;
DROP TYPE
(2) New type which shadows an existing name: works as long as you "quote" it
(but I'm not sure this is correct: see below).
typetest=> create type integer as (x smallint);
CREATE TYPE
typetest=> \dT+
List of data types
Schema | Name | Internal name | Size | Elements | Owner | Access
privileges | Description
--------+-----------+---------------+-------+----------+-------+-------------------+-------------
public | "integer" | integer | tuple | | ken |
|
(1 row)
typetest=> drop type integer;
ERROR: 42501: must be owner of type integer
LOCATION: aclcheck_error, aclchk.c:2981
typetest=> drop type "integer";
DROP TYPE
(3) New type which shadows the specific name "text": can't view it (with
psql \dT+), and can't drop it (in the same way as before).
typetest=> create type text as (x smallint);
CREATE TYPE
typetest=> \dT+
List of data types
Schema | Name | Internal name | Size | Elements | Owner | Access privileges
| Description
--------+------+---------------+------+----------+-------+-------------------+-------------
(0 rows)
typetest=> drop type text;
ERROR: 42501: must be owner of type text
LOCATION: aclcheck_error, aclchk.c:2981
typetest=> drop type "text";
ERROR: 42501: must be owner of type text
LOCATION: aclcheck_error, aclchk.c:2981
But it definitely exists:
typetest=> select user_defined_type_catalog, user_defined_type_schema,
user_defined_type_name from information_schema.user_defined_types;
user_defined_type_catalog | user_defined_type_schema |
user_defined_type_name
---------------------------+--------------------------+------------------------
typetest | public | text
(1 row)
The documentation for CREATE TYPE says: "The type name must be distinct from
the name of any existing type or domain in the same schema." The built-in
types are in "pg_catalog", and I'm definitely not creating any new types
there. It seems wrong for it to create an un-listable/un-droppable type.
I would expect that CREATE TYPE would give an error message, rather than
create a type which it can't deal with.
I would also expect that if "integer" works, then "text" should also work
the same -- they're both built-in types.
Eventually I discovered that I could drop the type by schema-qualifying it:
typetest=> drop type public.text;
DROP TYPE
I also discovered that names like "int2", "int4", "int8" act more like
"text": you can't list them, or drop them by unqualified name. But even
those are slightly nicer than "text" because they give you a hint when you
try to drop them. Compare:
typetest=> drop type "text";
ERROR: must be owner of type text
typetest=> drop type "int8";
ERROR: must be owner of type bigint
It's like "int8" is an alias for "bigint", so if you try to delete (your
own) "int8", it complains about "bigint". But if you try to delete (your
own) "text", it complains about "text", leaving the poor user confused
because they thought that's what they just said.
Upon further reading, 5.10.5 says that "pg_catalog" is effectively first in
the search path, if not explicitly placed later, so I'm not sure why DROP
TYPE "integer"; works. Even quoted, shouldn't that "integer" get resolved
to "pg_catalog"."integer", and therefore report an error?
I know the answer to all of this is "So don't do that!", but I'm confused
anyway.
Thanks for the nice database!
- Ken
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Laurenz Albe | 2026-07-30 11:37:11 | Re: Shadowing type names because I am not smart |
| Previous Message | Daniel Gustafsson | 2026-07-29 11:36:38 | Re: Fix data checksum progress reporting docs |