Index: contrib/isn/isn.sql.in =================================================================== RCS file: /cvsroot/pgsql/contrib/isn/isn.sql.in,v retrieving revision 1.8 diff -c -r1.8 isn.sql.in *** contrib/isn/isn.sql.in 13 Nov 2007 04:24:28 -0000 1.8 --- contrib/isn/isn.sql.in 28 Nov 2008 22:03:45 -0000 *************** *** 28,36 **** CREATE TYPE ean13 ( INPUT = ean13_in, OUTPUT = ean13_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE ean13 IS 'International European Article Number (EAN13)'; --- 28,34 ---- CREATE TYPE ean13 ( INPUT = ean13_in, OUTPUT = ean13_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE ean13 IS 'International European Article Number (EAN13)'; *************** *** 48,56 **** CREATE TYPE isbn13 ( INPUT = isbn13_in, OUTPUT = ean13_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE isbn13 IS 'International Standard Book Number 13 (ISBN13)'; --- 46,52 ---- CREATE TYPE isbn13 ( INPUT = isbn13_in, OUTPUT = ean13_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE isbn13 IS 'International Standard Book Number 13 (ISBN13)'; *************** *** 68,76 **** CREATE TYPE ismn13 ( INPUT = ismn13_in, OUTPUT = ean13_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE ismn13 IS 'International Standard Music Number 13 (ISMN13)'; --- 64,70 ---- CREATE TYPE ismn13 ( INPUT = ismn13_in, OUTPUT = ean13_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE ismn13 IS 'International Standard Music Number 13 (ISMN13)'; *************** *** 88,96 **** CREATE TYPE issn13 ( INPUT = issn13_in, OUTPUT = ean13_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE issn13 IS 'International Standard Serial Number 13 (ISSN13)'; --- 82,88 ---- CREATE TYPE issn13 ( INPUT = issn13_in, OUTPUT = ean13_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE issn13 IS 'International Standard Serial Number 13 (ISSN13)'; *************** *** 110,118 **** CREATE TYPE isbn ( INPUT = isbn_in, OUTPUT = isn_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE isbn IS 'International Standard Book Number (ISBN)'; --- 102,108 ---- CREATE TYPE isbn ( INPUT = isbn_in, OUTPUT = isn_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE isbn IS 'International Standard Book Number (ISBN)'; *************** *** 130,138 **** CREATE TYPE ismn ( INPUT = ismn_in, OUTPUT = isn_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE ismn IS 'International Standard Music Number (ISMN)'; --- 120,126 ---- CREATE TYPE ismn ( INPUT = ismn_in, OUTPUT = isn_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE ismn IS 'International Standard Music Number (ISMN)'; *************** *** 150,158 **** CREATE TYPE issn ( INPUT = issn_in, OUTPUT = isn_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE issn IS 'International Standard Serial Number (ISSN)'; --- 138,144 ---- CREATE TYPE issn ( INPUT = issn_in, OUTPUT = isn_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE issn IS 'International Standard Serial Number (ISSN)'; *************** *** 170,178 **** CREATE TYPE upc ( INPUT = upc_in, OUTPUT = isn_out, ! INTERNALLENGTH = 8, ! ALIGNMENT = double, ! STORAGE = PLAIN ); COMMENT ON TYPE upc IS 'Universal Product Code (UPC)'; --- 156,162 ---- CREATE TYPE upc ( INPUT = upc_in, OUTPUT = isn_out, ! LIKE = pg_catalog.int8 ); COMMENT ON TYPE upc IS 'Universal Product Code (UPC)'; Index: doc/src/sgml/ref/create_type.sgml =================================================================== RCS file: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v retrieving revision 1.78 diff -c -r1.78 create_type.sgml *** doc/src/sgml/ref/create_type.sgml 14 Nov 2008 10:22:46 -0000 1.78 --- doc/src/sgml/ref/create_type.sgml 28 Nov 2008 22:03:45 -0000 *************** *** 39,44 **** --- 39,45 ---- [ , PASSEDBYVALUE ] [ , ALIGNMENT = alignment ] [ , STORAGE = storage ] + [ , LIKE = like_type ] [ , CATEGORY = category ] [ , PREFERRED = preferred ] [ , DEFAULT = default ] *************** *** 291,296 **** --- 292,312 ---- + 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 used to help control which implicit cast will be applied in ambiguous *************** *** 525,530 **** --- 541,562 ---- + 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 Index: src/backend/commands/typecmds.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/commands/typecmds.c,v retrieving revision 1.126 diff -c -r1.126 typecmds.c *** src/backend/commands/typecmds.c 2 Nov 2008 01:45:28 -0000 1.126 --- src/backend/commands/typecmds.c 28 Nov 2008 22:03:45 -0000 *************** *** 100,106 **** char *typeName; Oid typeNamespace; int16 internalLength = -1; /* default: variable-length */ - Oid elemType = InvalidOid; List *inputName = NIL; List *outputName = NIL; List *receiveName = NIL; --- 100,105 ---- *************** *** 108,120 **** 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; char alignment = 'i'; /* default alignment */ char storage = 'p'; /* default TOAST storage method */ Oid inputOid; Oid outputOid; Oid receiveOid = InvalidOid; --- 107,137 ---- List *typmodinName = NIL; List *typmodoutName = NIL; List *analyzeName = NIL; 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,133 **** Oid analyzeOid = InvalidOid; char *array_type; Oid array_oid; - ListCell *pl; Oid typoid; Oid resulttype; Relation pg_type; /* * As of Postgres 8.4, we require superuser privilege to create a base --- 141,150 ---- Oid analyzeOid = InvalidOid; char *array_type; Oid array_oid; Oid typoid; Oid resulttype; Relation pg_type; + ListCell *pl; /* * As of Postgres 8.4, we require superuser privilege to create a base *************** *** 202,312 **** errmsg("type \"%s\" already exists", typeName))); } foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); ! if (pg_strcasecmp(defel->defname, "internallength") == 0) ! internalLength = defGetTypeLength(defel); else if (pg_strcasecmp(defel->defname, "input") == 0) ! inputName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "output") == 0) ! outputName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "receive") == 0) ! receiveName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "send") == 0) ! sendName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "typmod_in") == 0) ! typmodinName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "typmod_out") == 0) ! typmodoutName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "analyze") == 0 || pg_strcasecmp(defel->defname, "analyse") == 0) ! analyzeName = defGetQualifiedName(defel); 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))); ! } else if (pg_strcasecmp(defel->defname, "preferred") == 0) ! preferred = defGetBoolean(defel); else if (pg_strcasecmp(defel->defname, "delimiter") == 0) ! { ! char *p = defGetString(defel); ! ! delimiter = p[0]; ! /* XXX shouldn't we restrict the delimiter? */ ! } 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)))); ! } else if (pg_strcasecmp(defel->defname, "default") == 0) ! defaultValue = defGetString(defel); else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0) ! byValue = defGetBoolean(defel); 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))); ! } 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))); ! } else ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("type attribute \"%s\" not recognized", defel->defname))); } /* --- 219,393 ---- 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, "like") == 0) ! defelp = &likeTypeEl; ! else if (pg_strcasecmp(defel->defname, "internallength") == 0) ! defelp = &internalLengthEl; else if (pg_strcasecmp(defel->defname, "input") == 0) ! defelp = &inputNameEl; else if (pg_strcasecmp(defel->defname, "output") == 0) ! defelp = &outputNameEl; else if (pg_strcasecmp(defel->defname, "receive") == 0) ! defelp = &receiveNameEl; else if (pg_strcasecmp(defel->defname, "send") == 0) ! defelp = &sendNameEl; else if (pg_strcasecmp(defel->defname, "typmod_in") == 0) ! defelp = &typmodinNameEl; else if (pg_strcasecmp(defel->defname, "typmod_out") == 0) ! defelp = &typmodoutNameEl; else if (pg_strcasecmp(defel->defname, "analyze") == 0 || pg_strcasecmp(defel->defname, "analyse") == 0) ! defelp = &analyzeNameEl; else if (pg_strcasecmp(defel->defname, "category") == 0) ! defelp = &categoryEl; else if (pg_strcasecmp(defel->defname, "preferred") == 0) ! defelp = &preferredEl; else if (pg_strcasecmp(defel->defname, "delimiter") == 0) ! defelp = &delimiterEl; else if (pg_strcasecmp(defel->defname, "element") == 0) ! defelp = &elemTypeEl; else if (pg_strcasecmp(defel->defname, "default") == 0) ! defelp = &defaultValueEl; else if (pg_strcasecmp(defel->defname, "passedbyvalue") == 0) ! defelp = &byValueEl; else if (pg_strcasecmp(defel->defname, "alignment") == 0) ! defelp = &alignmentEl; else if (pg_strcasecmp(defel->defname, "storage") == 0) ! 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))); } /*