mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Don't create a shell type for function returning an array
Refactor the checks in the function to move all the conditions for when to attempt creating a shell type into one place. Add a check for the array syntax. In addition to rejecting array syntax, another user-visible effect is that the error message is now different if the type specified a typmod. You now get "type does not exist" instead of the more specific "type modifier cannot be specified for shell type". That seems better; the implicit shell type creation exists only for backwards compatibility, and it never worked with type modifiers, so if there's a type modifier it's most likely not because the user tried to create a shell type, Add test for the array syntax, the type modifier, and some other cases for which we don't create shell types. Discussion: https://www.postgresql.org/message-id/de673feb-41b4-4685-b24b-6408b95e58ab@iki.fi Backpatch-through: 14
This commit is contained in:
@@ -97,11 +97,32 @@ compute_return_type(TypeName *returnType, Oid languageOid,
|
||||
Oid rettype;
|
||||
Type typtup;
|
||||
AclResult aclresult;
|
||||
bool attempt_shell_creation;
|
||||
|
||||
/*
|
||||
* If this looks like it could be an input function, and the type doesn't
|
||||
* exist, we'll create it as a shell type.
|
||||
*
|
||||
* If the type name contains any modifiers like %TYPE, type[] array
|
||||
* syntax, or typmod decoration, it's not an input function, or at least
|
||||
* not one for which we'd want to automatically create a shell type.
|
||||
*
|
||||
* Only C-coded functions can be I/O functions. We enforce this
|
||||
* restriction here mainly to prevent littering the catalogs with shell
|
||||
* types due to simple typos in user-defined function definitions.
|
||||
*/
|
||||
attempt_shell_creation =
|
||||
!returnType->pct_type && returnType->arrayBounds == NULL &&
|
||||
returnType->typmods == NIL &&
|
||||
(languageOid == INTERNALlanguageId || languageOid == ClanguageId);
|
||||
|
||||
typtup = LookupTypeName(NULL, returnType, NULL, false);
|
||||
|
||||
if (typtup)
|
||||
{
|
||||
/*
|
||||
* Found an existing type with the given name. Check if it's a shell
|
||||
* type.
|
||||
*/
|
||||
if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
|
||||
{
|
||||
if (languageOid == SQLlanguageId)
|
||||
@@ -118,38 +139,28 @@ compute_return_type(TypeName *returnType, Oid languageOid,
|
||||
rettype = typeTypeId(typtup);
|
||||
ReleaseSysCache(typtup);
|
||||
}
|
||||
else if (!attempt_shell_creation)
|
||||
{
|
||||
/* Type not found and we don't want to create a shell type */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("type \"%s\" does not exist",
|
||||
TypeNameToString(returnType))));
|
||||
}
|
||||
else
|
||||
{
|
||||
char *typnam = TypeNameToString(returnType);
|
||||
/* Make a shell type */
|
||||
Oid namespaceId;
|
||||
AclResult aclresult;
|
||||
char *typname;
|
||||
ObjectAddress address;
|
||||
|
||||
/*
|
||||
* Only C-coded functions can be I/O functions. We enforce this
|
||||
* restriction here mainly to prevent littering the catalogs with
|
||||
* shell types due to simple typos in user-defined function
|
||||
* definitions.
|
||||
*/
|
||||
if (languageOid != INTERNALlanguageId &&
|
||||
languageOid != ClanguageId)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("type \"%s\" does not exist", typnam)));
|
||||
|
||||
/* Reject if there's typmod decoration, too */
|
||||
if (returnType->typmods != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("type modifier cannot be specified for shell type \"%s\"",
|
||||
typnam)));
|
||||
|
||||
/* Otherwise, go ahead and make a shell type */
|
||||
ereport(NOTICE,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("type \"%s\" is not yet defined", typnam),
|
||||
errmsg("type \"%s\" is not yet defined",
|
||||
TypeNameToString(returnType)),
|
||||
errdetail("Creating a shell type definition.")));
|
||||
|
||||
namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
|
||||
&typname);
|
||||
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
|
||||
@@ -157,6 +168,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, OBJECT_SCHEMA,
|
||||
get_namespace_name(namespaceId));
|
||||
|
||||
address = TypeShellMake(typname, namespaceId, GetUserId());
|
||||
rettype = address.objectId;
|
||||
Assert(OidIsValid(rettype));
|
||||
|
||||
@@ -244,12 +244,26 @@ NOTICE: return type myvarchar is only a shell
|
||||
-- fail, it's still a shell:
|
||||
ALTER TYPE myvarchar SET (storage = extended);
|
||||
ERROR: type "myvarchar" is only a shell
|
||||
-- fail: typmods not allowed for a shell type
|
||||
CREATE FUNCTION myvarchar_lower(text) RETURNS myvarchar(100)
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
ERROR: type modifier cannot be specified for shell type "myvarchar"
|
||||
CREATE FUNCTION myvarchar_lower(myvarchar(100)) RETURNS text
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
ERROR: type modifier cannot be specified for shell type "myvarchar"
|
||||
CREATE TYPE myvarchar (
|
||||
input = myvarcharin,
|
||||
output = myvarcharout,
|
||||
alignment = integer,
|
||||
storage = main
|
||||
);
|
||||
-- fail: typmods not allowed because 'typmod_in' / 'typmod_out' were not specified.
|
||||
CREATE FUNCTION myvarchar_lower(text) RETURNS myvarchar(100)
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
ERROR: type modifier is not allowed for type "myvarchar"
|
||||
CREATE FUNCTION myvarchar_lower(myvarchar(100)) RETURNS text
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
ERROR: type modifier is not allowed for type "myvarchar"
|
||||
-- want to check updating of a domain over the target type, too
|
||||
CREATE DOMAIN myvarchardom AS myvarchar;
|
||||
ALTER TYPE myvarchar SET (storage = plain); -- not allowed
|
||||
@@ -296,6 +310,9 @@ FROM pg_type WHERE typname = '_myvarchardom';
|
||||
array_in | array_out | array_recv | array_send | - | - | array_typanalyze | array_subscript_handler | x
|
||||
(1 row)
|
||||
|
||||
-- typmods are now accepted in CREATE FUNCTION, although they are not stored
|
||||
CREATE FUNCTION myvarchar_lower(myvarchar(100)) RETURNS myvarchar(100)
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
-- ensure dependencies are straight
|
||||
DROP FUNCTION myvarcharsend(myvarchar); -- fail
|
||||
ERROR: cannot drop function myvarcharsend(myvarchar) because other objects depend on it
|
||||
@@ -303,6 +320,7 @@ DETAIL: type myvarchar depends on function myvarcharsend(myvarchar)
|
||||
function myvarcharin(cstring,oid,integer) depends on type myvarchar
|
||||
function myvarcharout(myvarchar) depends on type myvarchar
|
||||
function myvarcharrecv(internal,oid,integer) depends on type myvarchar
|
||||
function myvarchar_lower(myvarchar) depends on type myvarchar
|
||||
type myvarchardom depends on function myvarcharsend(myvarchar)
|
||||
HINT: Use DROP ... CASCADE to drop the dependent objects too.
|
||||
DROP TYPE myvarchar; -- fail
|
||||
@@ -312,11 +330,13 @@ function myvarcharout(myvarchar) depends on type myvarchar
|
||||
function myvarcharsend(myvarchar) depends on type myvarchar
|
||||
function myvarcharrecv(internal,oid,integer) depends on type myvarchar
|
||||
type myvarchardom depends on type myvarchar
|
||||
function myvarchar_lower(myvarchar) depends on type myvarchar
|
||||
HINT: Use DROP ... CASCADE to drop the dependent objects too.
|
||||
DROP TYPE myvarchar CASCADE;
|
||||
NOTICE: drop cascades to 5 other objects
|
||||
NOTICE: drop cascades to 6 other objects
|
||||
DETAIL: drop cascades to function myvarcharin(cstring,oid,integer)
|
||||
drop cascades to function myvarcharout(myvarchar)
|
||||
drop cascades to function myvarcharsend(myvarchar)
|
||||
drop cascades to function myvarcharrecv(internal,oid,integer)
|
||||
drop cascades to type myvarchardom
|
||||
drop cascades to function myvarchar_lower(myvarchar)
|
||||
|
||||
@@ -23,3 +23,28 @@ CREATE FUNCTION int44out(city_budget)
|
||||
RETURNS cstring
|
||||
AS '@libdir@/regress@DLSUFFIX@'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
|
||||
|
||||
-- If the specified type includes typmods or array syntax, don't create a shell type
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS bogus_shell(123)
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS bogus_shell[]
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
|
||||
-- If the column specified with %TYPE does not exist, don't try to create a shell type
|
||||
CREATE TEMP TABLE bogus_tbl (col int);
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS bogus_tbl.nonexistent_col%TYPE
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
|
||||
-- If the schema does not exist, don't try to create a shell type
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS nonexistent_schema.bogus_shell
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
|
||||
@@ -24,3 +24,27 @@ CREATE FUNCTION int44out(city_budget)
|
||||
AS '@libdir@/regress@DLSUFFIX@'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
NOTICE: argument type city_budget is only a shell
|
||||
-- If the specified type includes typmods or array syntax, don't create a shell type
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS bogus_shell(123)
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
ERROR: type "bogus_shell" does not exist
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS bogus_shell[]
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
ERROR: type "bogus_shell[]" does not exist
|
||||
-- If the column specified with %TYPE does not exist, don't try to create a shell type
|
||||
CREATE TEMP TABLE bogus_tbl (col int);
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS bogus_tbl.nonexistent_col%TYPE
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
ERROR: column "nonexistent_col" of relation "bogus_tbl" does not exist
|
||||
-- If the schema does not exist, don't try to create a shell type
|
||||
CREATE FUNCTION bogus_in(cstring)
|
||||
RETURNS nonexistent_schema.bogus_shell
|
||||
AS '@libdir@/regress@DLSUFFIX@', 'widget_in'
|
||||
LANGUAGE C STRICT IMMUTABLE;
|
||||
ERROR: schema "nonexistent_schema" does not exist
|
||||
|
||||
@@ -188,6 +188,12 @@ LANGUAGE internal STABLE PARALLEL SAFE STRICT AS 'varcharrecv';
|
||||
-- fail, it's still a shell:
|
||||
ALTER TYPE myvarchar SET (storage = extended);
|
||||
|
||||
-- fail: typmods not allowed for a shell type
|
||||
CREATE FUNCTION myvarchar_lower(text) RETURNS myvarchar(100)
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
CREATE FUNCTION myvarchar_lower(myvarchar(100)) RETURNS text
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
|
||||
CREATE TYPE myvarchar (
|
||||
input = myvarcharin,
|
||||
output = myvarcharout,
|
||||
@@ -195,6 +201,12 @@ CREATE TYPE myvarchar (
|
||||
storage = main
|
||||
);
|
||||
|
||||
-- fail: typmods not allowed because 'typmod_in' / 'typmod_out' were not specified.
|
||||
CREATE FUNCTION myvarchar_lower(text) RETURNS myvarchar(100)
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
CREATE FUNCTION myvarchar_lower(myvarchar(100)) RETURNS text
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
|
||||
-- want to check updating of a domain over the target type, too
|
||||
CREATE DOMAIN myvarchardom AS myvarchar;
|
||||
|
||||
@@ -228,6 +240,10 @@ SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
|
||||
typanalyze, typsubscript, typstorage
|
||||
FROM pg_type WHERE typname = '_myvarchardom';
|
||||
|
||||
-- typmods are now accepted in CREATE FUNCTION, although they are not stored
|
||||
CREATE FUNCTION myvarchar_lower(myvarchar(100)) RETURNS myvarchar(100)
|
||||
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'lower';
|
||||
|
||||
-- ensure dependencies are straight
|
||||
DROP FUNCTION myvarcharsend(myvarchar); -- fail
|
||||
DROP TYPE myvarchar; -- fail
|
||||
|
||||
Reference in New Issue
Block a user