Allow ALTER TYPE to update an existing type's typsubscript value.

This is essential if we'd like to allow existing extension data types
to support subscripting in future, since dropping and recreating the
type isn't a practical thing for an extension upgrade script, and
direct manipulation of pg_type isn't a great answer either.

There was some discussion about also allowing alteration of typelem,
but it's less clear whether that's a good idea or not, so for now
I forebore.

Discussion: https://postgr.es/m/3724341.1607551174@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2020-12-11 18:07:02 -05:00
parent 653aa603f5
commit 8c15a29745
4 changed files with 54 additions and 22 deletions

View File

@ -194,6 +194,14 @@ ALTER TYPE <replaceable class="parameter">name</replaceable> SET ( <replaceable
requires superuser privilege.
</para>
</listitem>
<listitem>
<para>
<literal>SUBSCRIPT</literal> can be set to the name of a type-specific
subscripting handler function, or <literal>NONE</literal> to remove
the type's subscripting handler function. Using this option
requires superuser privilege.
</para>
</listitem>
<listitem>
<para>
<literal>STORAGE</literal><indexterm>

View File

@ -94,6 +94,7 @@ typedef struct
bool updateTypmodin;
bool updateTypmodout;
bool updateAnalyze;
bool updateSubscript;
/* New values for relevant attributes */
char storage;
Oid receiveOid;
@ -101,6 +102,7 @@ typedef struct
Oid typmodinOid;
Oid typmodoutOid;
Oid analyzeOid;
Oid subscriptOid;
} AlterTypeRecurseParams;
/* Potentially set by pg_upgrade_support functions */
@ -3885,6 +3887,18 @@ AlterType(AlterTypeStmt *stmt)
/* Replacing an analyze function requires superuser. */
requireSuper = true;
}
else if (strcmp(defel->defname, "subscript") == 0)
{
if (defel->arg != NULL)
atparams.subscriptOid =
findTypeSubscriptingFunction(defGetQualifiedName(defel),
typeOid);
else
atparams.subscriptOid = InvalidOid; /* NONE, remove function */
atparams.updateSubscript = true;
/* Replacing a subscript function requires superuser. */
requireSuper = true;
}
/*
* The rest of the options that CREATE accepts cannot be changed.
@ -4042,6 +4056,11 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
replaces[Anum_pg_type_typanalyze - 1] = true;
values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(atparams->analyzeOid);
}
if (atparams->updateSubscript)
{
replaces[Anum_pg_type_typsubscript - 1] = true;
values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(atparams->subscriptOid);
}
newtup = heap_modify_tuple(tup, RelationGetDescr(catalog),
values, nulls, replaces);
@ -4098,6 +4117,7 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
atparams->updateReceive = false; /* domains use F_DOMAIN_RECV */
atparams->updateTypmodin = false; /* domains don't have typmods */
atparams->updateTypmodout = false;
atparams->updateSubscript = false; /* domains don't have subscriptors */
/* Skip the scan if nothing remains to be done */
if (!(atparams->updateStorage ||

View File

@ -260,38 +260,40 @@ ALTER TYPE myvarchar SET (
receive = myvarcharrecv,
typmod_in = varchartypmodin,
typmod_out = varchartypmodout,
analyze = array_typanalyze -- bogus, but it doesn't matter
-- these are bogus, but it's safe as long as we don't use the type:
analyze = ts_typanalyze,
subscript = raw_array_subscript_handler
);
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = 'myvarchar';
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typstorage
-------------+--------------+---------------+---------------+-----------------+------------------+------------------+------------
myvarcharin | myvarcharout | myvarcharrecv | myvarcharsend | varchartypmodin | varchartypmodout | array_typanalyze | x
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typsubscript | typstorage
-------------+--------------+---------------+---------------+-----------------+------------------+---------------+-----------------------------+------------
myvarcharin | myvarcharout | myvarcharrecv | myvarcharsend | varchartypmodin | varchartypmodout | ts_typanalyze | raw_array_subscript_handler | x
(1 row)
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = '_myvarchar';
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typstorage
----------+-----------+------------+------------+-----------------+------------------+------------------+------------
array_in | array_out | array_recv | array_send | varchartypmodin | varchartypmodout | array_typanalyze | x
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typsubscript | typstorage
----------+-----------+------------+------------+-----------------+------------------+------------------+-------------------------+------------
array_in | array_out | array_recv | array_send | varchartypmodin | varchartypmodout | array_typanalyze | array_subscript_handler | x
(1 row)
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = 'myvarchardom';
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typstorage
-----------+--------------+-------------+---------------+----------+-----------+------------------+------------
domain_in | myvarcharout | domain_recv | myvarcharsend | - | - | array_typanalyze | x
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typsubscript | typstorage
-----------+--------------+-------------+---------------+----------+-----------+---------------+--------------+------------
domain_in | myvarcharout | domain_recv | myvarcharsend | - | - | ts_typanalyze | - | x
(1 row)
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = '_myvarchardom';
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typstorage
----------+-----------+------------+------------+----------+-----------+------------------+------------
array_in | array_out | array_recv | array_send | - | - | array_typanalyze | x
typinput | typoutput | typreceive | typsend | typmodin | typmodout | typanalyze | typsubscript | typstorage
----------+-----------+------------+------------+----------+-----------+------------------+-------------------------+------------
array_in | array_out | array_recv | array_send | - | - | array_typanalyze | array_subscript_handler | x
(1 row)
-- ensure dependencies are straight

View File

@ -207,23 +207,25 @@ ALTER TYPE myvarchar SET (
receive = myvarcharrecv,
typmod_in = varchartypmodin,
typmod_out = varchartypmodout,
analyze = array_typanalyze -- bogus, but it doesn't matter
-- these are bogus, but it's safe as long as we don't use the type:
analyze = ts_typanalyze,
subscript = raw_array_subscript_handler
);
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = 'myvarchar';
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = '_myvarchar';
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = 'myvarchardom';
SELECT typinput, typoutput, typreceive, typsend, typmodin, typmodout,
typanalyze, typstorage
typanalyze, typsubscript, typstorage
FROM pg_type WHERE typname = '_myvarchardom';
-- ensure dependencies are straight