diff --git a/contrib/isn/isn.sql.in b/contrib/isn/isn.sql.in index 48f14134af..1963fbbee3 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 222d41d28b..78b11b8a80 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 2ea9021a9b..38416fa67f 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))); } /*