Commit 3f936aac authored by Tom Lane's avatar Tom Lane

Add a "LIKE = typename" clause to CREATE TYPE for base types. This allows

the basic representational details (typlen, typalign, typbyval, typstorage)
to be copied from an existing type rather than listed explicitly in the
CREATE TYPE command.  The immediate reason for this is to provide a simple
solution for add-on modules that want to define types represented as int8,
float4, or float8: as of 8.4 the appropriate PASSEDBYVALUE setting is
platform-specific and so it's hard for a SQL script to know what to do.

This patch fixes the contrib/isn breakage reported by Rushabh Lathia.
parent 0ddede58
/* $PostgreSQL: pgsql/contrib/isn/isn.sql.in,v 1.8 2007/11/13 04:24:28 momjian Exp $ */ /* $PostgreSQL: pgsql/contrib/isn/isn.sql.in,v 1.9 2008/11/30 19:01:29 tgl Exp $ */
-- Adjust this setting to control where the objects get created. -- Adjust this setting to control where the objects get created.
SET search_path = public; SET search_path = public;
...@@ -28,9 +28,7 @@ CREATE OR REPLACE FUNCTION ean13_out(ean13) ...@@ -28,9 +28,7 @@ CREATE OR REPLACE FUNCTION ean13_out(ean13)
CREATE TYPE ean13 ( CREATE TYPE ean13 (
INPUT = ean13_in, INPUT = ean13_in,
OUTPUT = ean13_out, OUTPUT = ean13_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE ean13 COMMENT ON TYPE ean13
IS 'International European Article Number (EAN13)'; IS 'International European Article Number (EAN13)';
...@@ -48,9 +46,7 @@ CREATE OR REPLACE FUNCTION ean13_out(isbn13) ...@@ -48,9 +46,7 @@ CREATE OR REPLACE FUNCTION ean13_out(isbn13)
CREATE TYPE isbn13 ( CREATE TYPE isbn13 (
INPUT = isbn13_in, INPUT = isbn13_in,
OUTPUT = ean13_out, OUTPUT = ean13_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE isbn13 COMMENT ON TYPE isbn13
IS 'International Standard Book Number 13 (ISBN13)'; IS 'International Standard Book Number 13 (ISBN13)';
...@@ -68,9 +64,7 @@ CREATE OR REPLACE FUNCTION ean13_out(ismn13) ...@@ -68,9 +64,7 @@ CREATE OR REPLACE FUNCTION ean13_out(ismn13)
CREATE TYPE ismn13 ( CREATE TYPE ismn13 (
INPUT = ismn13_in, INPUT = ismn13_in,
OUTPUT = ean13_out, OUTPUT = ean13_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE ismn13 COMMENT ON TYPE ismn13
IS 'International Standard Music Number 13 (ISMN13)'; IS 'International Standard Music Number 13 (ISMN13)';
...@@ -88,9 +82,7 @@ CREATE OR REPLACE FUNCTION ean13_out(issn13) ...@@ -88,9 +82,7 @@ CREATE OR REPLACE FUNCTION ean13_out(issn13)
CREATE TYPE issn13 ( CREATE TYPE issn13 (
INPUT = issn13_in, INPUT = issn13_in,
OUTPUT = ean13_out, OUTPUT = ean13_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE issn13 COMMENT ON TYPE issn13
IS 'International Standard Serial Number 13 (ISSN13)'; IS 'International Standard Serial Number 13 (ISSN13)';
...@@ -110,9 +102,7 @@ CREATE OR REPLACE FUNCTION isn_out(isbn) ...@@ -110,9 +102,7 @@ CREATE OR REPLACE FUNCTION isn_out(isbn)
CREATE TYPE isbn ( CREATE TYPE isbn (
INPUT = isbn_in, INPUT = isbn_in,
OUTPUT = isn_out, OUTPUT = isn_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE isbn COMMENT ON TYPE isbn
IS 'International Standard Book Number (ISBN)'; IS 'International Standard Book Number (ISBN)';
...@@ -130,9 +120,7 @@ CREATE OR REPLACE FUNCTION isn_out(ismn) ...@@ -130,9 +120,7 @@ CREATE OR REPLACE FUNCTION isn_out(ismn)
CREATE TYPE ismn ( CREATE TYPE ismn (
INPUT = ismn_in, INPUT = ismn_in,
OUTPUT = isn_out, OUTPUT = isn_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE ismn COMMENT ON TYPE ismn
IS 'International Standard Music Number (ISMN)'; IS 'International Standard Music Number (ISMN)';
...@@ -150,9 +138,7 @@ CREATE OR REPLACE FUNCTION isn_out(issn) ...@@ -150,9 +138,7 @@ CREATE OR REPLACE FUNCTION isn_out(issn)
CREATE TYPE issn ( CREATE TYPE issn (
INPUT = issn_in, INPUT = issn_in,
OUTPUT = isn_out, OUTPUT = isn_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE issn COMMENT ON TYPE issn
IS 'International Standard Serial Number (ISSN)'; IS 'International Standard Serial Number (ISSN)';
...@@ -170,9 +156,7 @@ CREATE OR REPLACE FUNCTION isn_out(upc) ...@@ -170,9 +156,7 @@ CREATE OR REPLACE FUNCTION isn_out(upc)
CREATE TYPE upc ( CREATE TYPE upc (
INPUT = upc_in, INPUT = upc_in,
OUTPUT = isn_out, OUTPUT = isn_out,
INTERNALLENGTH = 8, LIKE = pg_catalog.int8
ALIGNMENT = double,
STORAGE = PLAIN
); );
COMMENT ON TYPE upc COMMENT ON TYPE upc
IS 'Universal Product Code (UPC)'; IS 'Universal Product Code (UPC)';
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.78 2008/11/14 10:22:46 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.79 2008/11/30 19:01:29 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -39,6 +39,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ( ...@@ -39,6 +39,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , PASSEDBYVALUE ] [ , PASSEDBYVALUE ]
[ , ALIGNMENT = <replaceable class="parameter">alignment</replaceable> ] [ , ALIGNMENT = <replaceable class="parameter">alignment</replaceable> ]
[ , STORAGE = <replaceable class="parameter">storage</replaceable> ] [ , STORAGE = <replaceable class="parameter">storage</replaceable> ]
[ , LIKE = <replaceable class="parameter">like_type</replaceable> ]
[ , CATEGORY = <replaceable class="parameter">category</replaceable> ] [ , CATEGORY = <replaceable class="parameter">category</replaceable> ]
[ , PREFERRED = <replaceable class="parameter">preferred</replaceable> ] [ , PREFERRED = <replaceable class="parameter">preferred</replaceable> ]
[ , DEFAULT = <replaceable class="parameter">default</replaceable> ] [ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
...@@ -290,6 +291,21 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ...@@ -290,6 +291,21 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<literal>external</literal> items.) <literal>external</literal> items.)
</para> </para>
<para>
The <replaceable class="parameter">like_type</replaceable> parameter
provides an alternative method for specifying the basic representation
properties of a data type: copy them from some existing type. The values of
<replaceable class="parameter">internallength</replaceable>,
<replaceable class="parameter">passedbyvalue</replaceable>,
<replaceable class="parameter">alignment</replaceable>, and
<replaceable class="parameter">storage</replaceable> are copied from the
named type. (It is possible, though usually undesirable, to override
some of these values by specifying them along with the <literal>LIKE</>
clause.) Specifying representation this way is especially useful when
the low-level implementation of the new type <quote>piggybacks</> on an
existing type in some fashion.
</para>
<para> <para>
The <replaceable class="parameter">category</replaceable> and The <replaceable class="parameter">category</replaceable> and
<replaceable class="parameter">preferred</replaceable> parameters can be <replaceable class="parameter">preferred</replaceable> parameters can be
...@@ -524,6 +540,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ...@@ -524,6 +540,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><replaceable class="parameter">like_type</replaceable></term>
<listitem>
<para>
The name of an existing data type that the new type will have the
same representation as. The values of
<replaceable class="parameter">internallength</replaceable>,
<replaceable class="parameter">passedbyvalue</replaceable>,
<replaceable class="parameter">alignment</replaceable>, and
<replaceable class="parameter">storage</replaceable>
are copied from that type, unless overridden by explicit
specification elsewhere in this <command>CREATE TYPE</> command.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">category</replaceable></term> <term><replaceable class="parameter">category</replaceable></term>
<listitem> <listitem>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.126 2008/11/02 01:45:28 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.127 2008/11/30 19:01:29 tgl Exp $
* *
* DESCRIPTION * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -100,7 +100,6 @@ DefineType(List *names, List *parameters) ...@@ -100,7 +100,6 @@ DefineType(List *names, List *parameters)
char *typeName; char *typeName;
Oid typeNamespace; Oid typeNamespace;
int16 internalLength = -1; /* default: variable-length */ int16 internalLength = -1; /* default: variable-length */
Oid elemType = InvalidOid;
List *inputName = NIL; List *inputName = NIL;
List *outputName = NIL; List *outputName = NIL;
List *receiveName = NIL; List *receiveName = NIL;
...@@ -108,13 +107,31 @@ DefineType(List *names, List *parameters) ...@@ -108,13 +107,31 @@ DefineType(List *names, List *parameters)
List *typmodinName = NIL; List *typmodinName = NIL;
List *typmodoutName = NIL; List *typmodoutName = NIL;
List *analyzeName = NIL; List *analyzeName = NIL;
char *defaultValue = NULL;
bool byValue = false;
char category = TYPCATEGORY_USER; char category = TYPCATEGORY_USER;
bool preferred = false; bool preferred = false;
char delimiter = DEFAULT_TYPDELIM; char delimiter = DEFAULT_TYPDELIM;
Oid elemType = InvalidOid;
char *defaultValue = NULL;
bool byValue = false;
char alignment = 'i'; /* default alignment */ char alignment = 'i'; /* default alignment */
char storage = 'p'; /* default TOAST storage method */ char storage = 'p'; /* default TOAST storage method */
DefElem *likeTypeEl = NULL;
DefElem *internalLengthEl = NULL;
DefElem *inputNameEl = NULL;
DefElem *outputNameEl = NULL;
DefElem *receiveNameEl = NULL;
DefElem *sendNameEl = NULL;
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
DefElem *elemTypeEl = NULL;
DefElem *defaultValueEl = NULL;
DefElem *byValueEl = NULL;
DefElem *alignmentEl = NULL;
DefElem *storageEl = NULL;
Oid inputOid; Oid inputOid;
Oid outputOid; Oid outputOid;
Oid receiveOid = InvalidOid; Oid receiveOid = InvalidOid;
...@@ -124,10 +141,10 @@ DefineType(List *names, List *parameters) ...@@ -124,10 +141,10 @@ DefineType(List *names, List *parameters)
Oid analyzeOid = InvalidOid; Oid analyzeOid = InvalidOid;
char *array_type; char *array_type;
Oid array_oid; Oid array_oid;
ListCell *pl;
Oid typoid; Oid typoid;
Oid resulttype; Oid resulttype;
Relation pg_type; Relation pg_type;
ListCell *pl;
/* /*
* As of Postgres 8.4, we require superuser privilege to create a base * As of Postgres 8.4, we require superuser privilege to create a base
...@@ -202,111 +219,175 @@ DefineType(List *names, List *parameters) ...@@ -202,111 +219,175 @@ DefineType(List *names, List *parameters)
errmsg("type \"%s\" already exists", typeName))); errmsg("type \"%s\" already exists", typeName)));
} }
/* Extract the parameters from the parameter list */
foreach(pl, parameters) foreach(pl, parameters)
{ {
DefElem *defel = (DefElem *) lfirst(pl); DefElem *defel = (DefElem *) lfirst(pl);
DefElem **defelp;
if (pg_strcasecmp(defel->defname, "internallength") == 0) if (pg_strcasecmp(defel->defname, "like") == 0)
internalLength = defGetTypeLength(defel); defelp = &likeTypeEl;
else if (pg_strcasecmp(defel->defname, "internallength") == 0)
defelp = &internalLengthEl;
else if (pg_strcasecmp(defel->defname, "input") == 0) else if (pg_strcasecmp(defel->defname, "input") == 0)
inputName = defGetQualifiedName(defel); defelp = &inputNameEl;
else if (pg_strcasecmp(defel->defname, "output") == 0) else if (pg_strcasecmp(defel->defname, "output") == 0)
outputName = defGetQualifiedName(defel); defelp = &outputNameEl;
else if (pg_strcasecmp(defel->defname, "receive") == 0) else if (pg_strcasecmp(defel->defname, "receive") == 0)
receiveName = defGetQualifiedName(defel); defelp = &receiveNameEl;
else if (pg_strcasecmp(defel->defname, "send") == 0) else if (pg_strcasecmp(defel->defname, "send") == 0)
sendName = defGetQualifiedName(defel); defelp = &sendNameEl;
else if (pg_strcasecmp(defel->defname, "typmod_in") == 0) else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
typmodinName = defGetQualifiedName(defel); defelp = &typmodinNameEl;
else if (pg_strcasecmp(defel->defname, "typmod_out") == 0) else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
typmodoutName = defGetQualifiedName(defel); defelp = &typmodoutNameEl;
else if (pg_strcasecmp(defel->defname, "analyze") == 0 || else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0) pg_strcasecmp(defel->defname, "analyse") == 0)
analyzeName = defGetQualifiedName(defel); defelp = &analyzeNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0) else if (pg_strcasecmp(defel->defname, "category") == 0)
{ defelp = &categoryEl;
char *p = defGetString(defel);
category = p[0];
/* restrict to non-control ASCII */
if (category < 32 || category > 126)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid type category \"%s\": must be simple ASCII",
p)));
}
else if (pg_strcasecmp(defel->defname, "preferred") == 0) else if (pg_strcasecmp(defel->defname, "preferred") == 0)
preferred = defGetBoolean(defel); defelp = &preferredEl;
else if (pg_strcasecmp(defel->defname, "delimiter") == 0) else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
{ defelp = &delimiterEl;
char *p = defGetString(defel);
delimiter = p[0];
/* XXX shouldn't we restrict the delimiter? */
}
else if (pg_strcasecmp(defel->defname, "element") == 0) else if (pg_strcasecmp(defel->defname, "element") == 0)
{ defelp = &elemTypeEl;
elemType = typenameTypeId(NULL, defGetTypeName(defel), NULL);
/* disallow arrays of pseudotypes */
if (get_typtype(elemType) == TYPTYPE_PSEUDO)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array element type cannot be %s",
format_type_be(elemType))));
}
else if (pg_strcasecmp(defel->defname, "default") == 0) else if (pg_strcasecmp(defel->defname, "default") == 0)
defaultValue = defGetString(defel); defelp = &defaultValueEl;
else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0) else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
byValue = defGetBoolean(defel); defelp = &byValueEl;
else if (pg_strcasecmp(defel->defname, "alignment") == 0) else if (pg_strcasecmp(defel->defname, "alignment") == 0)
{ defelp = &alignmentEl;
char *a = defGetString(defel);
/*
* Note: if argument was an unquoted identifier, parser will have
* applied translations to it, so be prepared to recognize
* translated type names as well as the nominal form.
*/
if (pg_strcasecmp(a, "double") == 0 ||
pg_strcasecmp(a, "float8") == 0 ||
pg_strcasecmp(a, "pg_catalog.float8") == 0)
alignment = 'd';
else if (pg_strcasecmp(a, "int4") == 0 ||
pg_strcasecmp(a, "pg_catalog.int4") == 0)
alignment = 'i';
else if (pg_strcasecmp(a, "int2") == 0 ||
pg_strcasecmp(a, "pg_catalog.int2") == 0)
alignment = 's';
else if (pg_strcasecmp(a, "char") == 0 ||
pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
alignment = 'c';
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("alignment \"%s\" not recognized", a)));
}
else if (pg_strcasecmp(defel->defname, "storage") == 0) else if (pg_strcasecmp(defel->defname, "storage") == 0)
{ defelp = &storageEl;
char *a = defGetString(defel);
if (pg_strcasecmp(a, "plain") == 0)
storage = 'p';
else if (pg_strcasecmp(a, "external") == 0)
storage = 'e';
else if (pg_strcasecmp(a, "extended") == 0)
storage = 'x';
else if (pg_strcasecmp(a, "main") == 0)
storage = 'm';
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("storage \"%s\" not recognized", a)));
}
else else
{
/* WARNING, not ERROR, for historical backwards-compatibility */
ereport(WARNING, ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR), (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type attribute \"%s\" not recognized", errmsg("type attribute \"%s\" not recognized",
defel->defname))); defel->defname)));
continue;
}
if (*defelp != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
*defelp = defel;
}
/*
* Now interpret the options; we do this separately so that LIKE can
* be overridden by other options regardless of the ordering in the
* parameter list.
*/
if (likeTypeEl)
{
Type likeType;
Form_pg_type likeForm;
likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
likeForm = (Form_pg_type) GETSTRUCT(likeType);
internalLength = likeForm->typlen;
byValue = likeForm->typbyval;
alignment = likeForm->typalign;
storage = likeForm->typstorage;
ReleaseSysCache(likeType);
}
if (internalLengthEl)
internalLength = defGetTypeLength(internalLengthEl);
if (inputNameEl)
inputName = defGetQualifiedName(inputNameEl);
if (outputNameEl)
outputName = defGetQualifiedName(outputNameEl);
if (receiveNameEl)
receiveName = defGetQualifiedName(receiveNameEl);
if (sendNameEl)
sendName = defGetQualifiedName(sendNameEl);
if (typmodinNameEl)
typmodinName = defGetQualifiedName(typmodinNameEl);
if (typmodoutNameEl)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
category = p[0];
/* restrict to non-control ASCII */
if (category < 32 || category > 126)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid type category \"%s\": must be simple ASCII",
p)));
}
if (preferredEl)
preferred = defGetBoolean(preferredEl);
if (delimiterEl)
{
char *p = defGetString(delimiterEl);
delimiter = p[0];
/* XXX shouldn't we restrict the delimiter? */
}
if (elemTypeEl)
{
elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl), NULL);
/* disallow arrays of pseudotypes */
if (get_typtype(elemType) == TYPTYPE_PSEUDO)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array element type cannot be %s",
format_type_be(elemType))));
}
if (defaultValueEl)
defaultValue = defGetString(defaultValueEl);
if (byValueEl)
byValue = defGetBoolean(byValueEl);
if (alignmentEl)
{
char *a = defGetString(alignmentEl);
/*
* Note: if argument was an unquoted identifier, parser will have
* applied translations to it, so be prepared to recognize
* translated type names as well as the nominal form.
*/
if (pg_strcasecmp(a, "double") == 0 ||
pg_strcasecmp(a, "float8") == 0 ||
pg_strcasecmp(a, "pg_catalog.float8") == 0)
alignment = 'd';
else if (pg_strcasecmp(a, "int4") == 0 ||
pg_strcasecmp(a, "pg_catalog.int4") == 0)
alignment = 'i';
else if (pg_strcasecmp(a, "int2") == 0 ||
pg_strcasecmp(a, "pg_catalog.int2") == 0)
alignment = 's';
else if (pg_strcasecmp(a, "char") == 0 ||
pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
alignment = 'c';
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("alignment \"%s\" not recognized", a)));
}
if (storageEl)
{
char *a = defGetString(storageEl);
if (pg_strcasecmp(a, "plain") == 0)
storage = 'p';
else if (pg_strcasecmp(a, "external") == 0)
storage = 'e';
else if (pg_strcasecmp(a, "extended") == 0)
storage = 'x';
else if (pg_strcasecmp(a, "main") == 0)
storage = 'm';
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("storage \"%s\" not recognized", a)));
} }
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment