Re: Implicit shell type creation, little fixes

From: Ewan Young <kdbase(dot)hack(at)gmail(dot)com>
To: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
Cc: "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Implicit shell type creation, little fixes
Date: 2026-08-31 06:53:38
Message-ID: CAON2xHN8y=9KeFnEb4KAKPvVks5VQaMUFJSGTv3T9VY777EKRw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Wed, Aug 19, 2026 at 5:52 PM Heikki Linnakangas <hlinnaka(at)iki(dot)fi> wrote:
>
> I noticed this with the old style implicit shell type creation:
>
> postgres=# create function blah(text, text, text) returns bogus_type[]
> immutable strict language internal as 'int8out';
> NOTICE: type "bogus_type[]" is not yet defined
> DETAIL: Creating a shell type definition.
> CREATE FUNCTION
>
> Surely we shouldn't create a shell type when the array syntax was used?
> This path is for old scripts that created the I/O functions before the
> type definition, and you wouldn't specify I/O functions for an array
> like this.
>
> If you then repeat the command, after the shell type's been created, you
> unsurprisingly get this:
>
> postgres=# create function blah(text, text, text) returns bogus_type[]
> strict language internal as 'int8out';
> NOTICE: type "bogus_type[]" is not yet defined
> DETAIL: Creating a shell type definition.
> ERROR: duplicate key value violates unique constraint
> "pg_type_typname_nsp_index"
> DETAIL: Key (typname, typnamespace)=(bogus_type, 2200) already exists.
>
>
> I propose the attached to fix that. It also changes the error message
> you get if you specify a typmod and the type doesn't exist. Currently on
> master:
>
> postgres=# create function blah(text, text, text) returns
> bogus_type(100) strict language internal as 'int8out';
> ERROR: type modifier cannot be specified for shell type "bogus_type"
>
> And with the patch:
>
> postgres=# create function blah(text, text, text) returns
> bogus_type(100) strict language internal as 'int8out';
> ERROR: type "bogus_type" does not exist
>
> I think "type does not exist" is better, it's unlikely that the user
> really intended to create a shell type.
>
>
> We could narrow down further the criteria for shell type creation. It's
> really only needed for creating the input function, so we could check
> that the signature looks like an input function. For example, the above
> 'blah' function takes text args, so it's surely not an input function
> and could be rejected on those grounds. But I didn't include that in
> this patch yet.

Here's one more case in the same family that commit 2866d8c7dbf didn't
cover: RETURNS SETOF. An input function never returns a set, but
attempt_shell_creation doesn't check TypeName->setof, so a typo in a
set-returning C function's return type still litters a shell type:

regression=# create function f() returns setof no_such_type
as '$libdir/pageinspect', 'heap_page_items' language c;
NOTICE: type "no_such_type" is not yet defined
DETAIL: Creating a shell type definition.
CREATE FUNCTION
regression=# drop function f();
DROP FUNCTION
regression=# select typname, typisdefined from pg_type
where typname = 'no_such_type';
typname | typisdefined
---------------+--------------
no_such_type | f
(1 row)

The shell type persists even after dropping the function.

Some knock-on effects of that, beyond the litter itself:

* The typo isn't caught where it was made: CREATE FUNCTION succeeds
with only a NOTICE. If the function is left in place, the error
surfaces later, at call time, and in a form two steps removed from
the actual mistake:

select * from f();
ERROR: function "f" in FROM has unsupported return type no_such_type

* The leftover shell keeps producing confusing errors under the
mistyped name: CREATE TABLE t (x no_such_type) fails with
'type "no_such_type" is only a shell', and CREATE DOMAIN
no_such_type AS ... fails with 'type "no_such_type" already exists'.

* pg_dump emits the bare shell ("CREATE TYPE public.no_such_type;"
plus an ALTER TYPE ... OWNER TO), so the junk propagates through
dump/restore until someone notices and drops it manually.

The attached patch adds !returnType->setof to the conditions, alongside
the existing %TYPE/array/typmod checks, plus a regression test in the
block your commit added. make check passes with no other changes.

(The fuller "check that the signature looks like an input function"
idea would subsume this, but until someone writes that, this seems
worth closing the same way as the array case.)

>
> - Heikki
>
> P.S. I know the 'int8out' internal function is wouldn't work for the
> definitions in the above examples anyway, please ignore that

--
Regards,
Ewan Young

Attachment Content-Type Size
v1-0001-Don-t-create-a-shell-type-for-a-function-returning-SETOF.patch application/octet-stream 3.5 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message yangboyu 2026-08-31 07:02:34 logical decoding: skip unnecessary snapshot distribution.
Previous Message Bertrand Drouvot 2026-08-31 06:44:42 Re: scary patch contest