Greg Stark <[EMAIL PROTECTED]> writes:
> I was going to say something like that but couldn't come up with a  
> nice syntax. The syntax you gave seems obvious in retrospect.

Here's a draft patch for this.  It actually works out pretty well --
the bulk of the diff is just to allow processing the CREATE TYPE
parameters in an order that's independent of the syntax.

> I wonder if this should be tied in some way with creating binary- 
> compatible casts or anything. Probably not since the data type can  
> just make them itself.

Yeah.  I thought for awhile about copying *all* the create-type
parameters from the LIKE type, but soon decided it was a bad idea.
We should keep the inheritance to a minimal level, just the basic
type-storage parameters.

                        regards, tom lane

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 = <replaceable class="parameter">alignment</replaceable> ]
      [ , STORAGE = <replaceable class="parameter">storage</replaceable> ]
+     [ , LIKE = <replaceable class="parameter">like_type</replaceable> ]
      [ , CATEGORY = <replaceable class="parameter">category</replaceable> ]
      [ , PREFERRED = <replaceable class="parameter">preferred</replaceable> ]
      [ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
***************
*** 291,296 ****
--- 292,312 ----
    </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>
     The <replaceable class="parameter">category</replaceable> and
     <replaceable class="parameter">preferred</replaceable> parameters can be
     used to help control which implicit cast will be applied in ambiguous
***************
*** 525,530 ****
--- 541,562 ----
     </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>
      <term><replaceable class="parameter">category</replaceable></term>
      <listitem>
       <para>
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)));
        }
  
        /*
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to