diff --git a/contrib/isn/isn.sql.in b/contrib/isn/isn.sql.in
index 48f14134af7..1963fbbee34 100644
--- a/contrib/isn/isn.sql.in
+++ b/contrib/isn/isn.sql.in
@@ -1,4 +1,4 @@
-/* $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.
SET search_path = public;
@@ -28,9 +28,7 @@ CREATE OR REPLACE FUNCTION ean13_out(ean13)
CREATE TYPE ean13 (
INPUT = ean13_in,
OUTPUT = ean13_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE ean13
IS 'International European Article Number (EAN13)';
@@ -48,9 +46,7 @@ CREATE OR REPLACE FUNCTION ean13_out(isbn13)
CREATE TYPE isbn13 (
INPUT = isbn13_in,
OUTPUT = ean13_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE isbn13
IS 'International Standard Book Number 13 (ISBN13)';
@@ -68,9 +64,7 @@ CREATE OR REPLACE FUNCTION ean13_out(ismn13)
CREATE TYPE ismn13 (
INPUT = ismn13_in,
OUTPUT = ean13_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE ismn13
IS 'International Standard Music Number 13 (ISMN13)';
@@ -88,9 +82,7 @@ CREATE OR REPLACE FUNCTION ean13_out(issn13)
CREATE TYPE issn13 (
INPUT = issn13_in,
OUTPUT = ean13_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE issn13
IS 'International Standard Serial Number 13 (ISSN13)';
@@ -110,9 +102,7 @@ CREATE OR REPLACE FUNCTION isn_out(isbn)
CREATE TYPE isbn (
INPUT = isbn_in,
OUTPUT = isn_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE isbn
IS 'International Standard Book Number (ISBN)';
@@ -130,9 +120,7 @@ CREATE OR REPLACE FUNCTION isn_out(ismn)
CREATE TYPE ismn (
INPUT = ismn_in,
OUTPUT = isn_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE ismn
IS 'International Standard Music Number (ISMN)';
@@ -150,9 +138,7 @@ CREATE OR REPLACE FUNCTION isn_out(issn)
CREATE TYPE issn (
INPUT = issn_in,
OUTPUT = isn_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE issn
IS 'International Standard Serial Number (ISSN)';
@@ -170,9 +156,7 @@ CREATE OR REPLACE FUNCTION isn_out(upc)
CREATE TYPE upc (
INPUT = upc_in,
OUTPUT = isn_out,
- INTERNALLENGTH = 8,
- ALIGNMENT = double,
- STORAGE = PLAIN
+ LIKE = pg_catalog.int8
);
COMMENT ON TYPE upc
IS 'Universal Product Code (UPC)';
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 222d41d28bb..78b11b8a80e 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -1,5 +1,5 @@
@@ -39,6 +39,7 @@ CREATE TYPE name (
[ , PASSEDBYVALUE ]
[ , ALIGNMENT = alignment ]
[ , STORAGE = storage ]
+ [ , LIKE = like_type ]
[ , CATEGORY = category ]
[ , PREFERRED = preferred ]
[ , DEFAULT = default ]
@@ -290,6 +291,21 @@ CREATE TYPE name
external items.)
+
+ The like_type parameter
+ provides an alternative method for specifying the basic representation
+ properties of a data type: copy them from some existing type. The values of
+ internallength,
+ passedbyvalue,
+ alignment, and
+ storage are copied from the
+ named type. (It is possible, though usually undesirable, to override
+ some of these values by specifying them along with the LIKE>
+ clause.) Specifying representation this way is especially useful when
+ the low-level implementation of the new type piggybacks> on an
+ existing type in some fashion.
+
+
The category and
preferred parameters can be
@@ -524,6 +540,22 @@ CREATE TYPE name
+
+ like_type
+
+
+ The name of an existing data type that the new type will have the
+ same representation as. The values of
+ internallength,
+ passedbyvalue,
+ alignment, and
+ storage
+ are copied from that type, unless overridden by explicit
+ specification elsewhere in this CREATE TYPE> command.
+
+
+
+
category
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 2ea9021a9ba..38416fa67f2 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
*
*
* 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
* The "DefineFoo" routines take the parse tree and pick out the
@@ -100,7 +100,6 @@ DefineType(List *names, List *parameters)
char *typeName;
Oid typeNamespace;
int16 internalLength = -1; /* default: variable-length */
- Oid elemType = InvalidOid;
List *inputName = NIL;
List *outputName = NIL;
List *receiveName = NIL;
@@ -108,13 +107,31 @@ DefineType(List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
- char *defaultValue = NULL;
- bool byValue = false;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
+ Oid elemType = InvalidOid;
+ char *defaultValue = NULL;
+ bool byValue = false;
char alignment = 'i'; /* default alignment */
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 outputOid;
Oid receiveOid = InvalidOid;
@@ -124,10 +141,10 @@ DefineType(List *names, List *parameters)
Oid analyzeOid = InvalidOid;
char *array_type;
Oid array_oid;
- ListCell *pl;
Oid typoid;
Oid resulttype;
Relation pg_type;
+ ListCell *pl;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -202,111 +219,175 @@ DefineType(List *names, List *parameters)
errmsg("type \"%s\" already exists", typeName)));
}
+ /* Extract the parameters from the parameter list */
foreach(pl, parameters)
{
DefElem *defel = (DefElem *) lfirst(pl);
+ DefElem **defelp;
- if (pg_strcasecmp(defel->defname, "internallength") == 0)
- internalLength = defGetTypeLength(defel);
+ if (pg_strcasecmp(defel->defname, "like") == 0)
+ defelp = &likeTypeEl;
+ else if (pg_strcasecmp(defel->defname, "internallength") == 0)
+ defelp = &internalLengthEl;
else if (pg_strcasecmp(defel->defname, "input") == 0)
- inputName = defGetQualifiedName(defel);
+ defelp = &inputNameEl;
else if (pg_strcasecmp(defel->defname, "output") == 0)
- outputName = defGetQualifiedName(defel);
+ defelp = &outputNameEl;
else if (pg_strcasecmp(defel->defname, "receive") == 0)
- receiveName = defGetQualifiedName(defel);
+ defelp = &receiveNameEl;
else if (pg_strcasecmp(defel->defname, "send") == 0)
- sendName = defGetQualifiedName(defel);
+ defelp = &sendNameEl;
else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
- typmodinName = defGetQualifiedName(defel);
+ defelp = &typmodinNameEl;
else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
- typmodoutName = defGetQualifiedName(defel);
+ defelp = &typmodoutNameEl;
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
- analyzeName = defGetQualifiedName(defel);
+ defelp = &analyzeNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
- {
- 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)));
- }
+ defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
- preferred = defGetBoolean(defel);
+ defelp = &preferredEl;
else if (pg_strcasecmp(defel->defname, "delimiter") == 0)
- {
- char *p = defGetString(defel);
-
- delimiter = p[0];
- /* XXX shouldn't we restrict the delimiter? */
- }
+ defelp = &delimiterEl;
else if (pg_strcasecmp(defel->defname, "element") == 0)
- {
- 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))));
- }
+ defelp = &elemTypeEl;
else if (pg_strcasecmp(defel->defname, "default") == 0)
- defaultValue = defGetString(defel);
+ defelp = &defaultValueEl;
else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0)
- byValue = defGetBoolean(defel);
+ defelp = &byValueEl;
else if (pg_strcasecmp(defel->defname, "alignment") == 0)
- {
- 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)));
- }
+ defelp = &alignmentEl;
else if (pg_strcasecmp(defel->defname, "storage") == 0)
- {
- 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)));
- }
+ defelp = &storageEl;
else
+ {
+ /* WARNING, not ERROR, for historical backwards-compatibility */
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("type attribute \"%s\" not recognized",
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)));
}
/*