Re: Domain Support -- another round - Mailing list pgsql-patches

From Peter Eisentraut
Subject Re: Domain Support -- another round
Date
Msg-id Pine.LNX.4.30.0203111912100.690-100000@peter.localdomain
Whole thread Raw
In response to Domain Support -- another round  (Rod Taylor <rbt@zort.ca>)
List pgsql-patches
Random nitpicking below.  Also, have you created a regression test?


> diff -rc pgsql.orig/doc/src/sgml/catalogs.sgml pgsqldomain/doc/src/sgml/catalogs.sgml
> *** pgsql.orig/doc/src/sgml/catalogs.sgml    Thu Mar  7 11:35:32 2002
> --- pgsqldomain/doc/src/sgml/catalogs.sgml    Thu Mar  7 22:24:23 2002
> ***************
> *** 2511,2516 ****
> --- 2511,2563 ----
>        </row>
>
>        <row>
> +       <entry>typbasetype</entry>
> +       <entry><type>oid</type></entry>
> +       <entry></entry>
> +       <entry><para>
> +        <structfield>typbasetype</structfield> is the type that this one is based
> +        off of.  Normally references the domains parent type, and is 0 otherwise.

"based on"

> +       </para></entry>
> +      </row>
> +
> +      <row>
> +       <entry>typnotnull</entry>
> +       <entry><type>boolean</type></entry>
> +       <entry></entry>
> +       <entry><para>
> +        <structfield>typnotnull</structfield> represents a NOT NULL
> +        constraint on a type.  Normally used only for domains.

And unnormally...?

> +       </para></entry>
> +      </row>
> +
> +      <row>
> +       <entry>typmod</entry>
> +       <entry><type>integer</type></entry>
> +       <entry></entry>
> +       <entry><para>
> +        <structfield>typmod</structfield> records type-specific data
> +        supplied at table creation time (for example, the maximum
> +        length of a <type>varchar</type> column).  It is passed to
> +        type-specific input and output functions as the third
> +        argument. The value will generally be -1 for types that do not
> +        need typmod.  This data is copied to
> +        <structfield>pg_attribute.atttypmod</structfield> on creation
> +        of a table using a domain as it's field type.
> +        </para></entry>
> +      </row>
> +
> +      <row>
> +       <entry>typdefaultbin</entry>
> +       <entry><type>text</type></entry>
> +       <entry></entry>
> +       <entry><para>
> +        <structfield>typdefaultbin</structfield> is NULL for types without a
> +        default value.  If it's not NULL, it contains the internal string
> +        representation of the default expression node.
> +       </para></entry>
> +      </row>
> +
> +      <row>
>         <entry>typdefault</entry>
>         <entry><type>text</type></entry>
>         <entry></entry>
> diff -rc pgsql.orig/doc/src/sgml/ref/allfiles.sgml pgsqldomain/doc/src/sgml/ref/allfiles.sgml
> *** pgsql.orig/doc/src/sgml/ref/allfiles.sgml    Thu Mar  7 11:35:32 2002
> --- pgsqldomain/doc/src/sgml/ref/allfiles.sgml    Thu Mar  7 22:24:23 2002
> ***************
> *** 52,57 ****
> --- 52,58 ----
>   <!entity createAggregate    system "create_aggregate.sgml">
>   <!entity createConstraint   system "create_constraint.sgml">
>   <!entity createDatabase     system "create_database.sgml">
> + <!entity createDomain       system "create_domain.sgml">

I don't see this file included.

>   <!entity createFunction     system "create_function.sgml">
>   <!entity createGroup        system "create_group.sgml">
>   <!entity createIndex        system "create_index.sgml">
> ***************
> *** 69,74 ****
> --- 70,76 ----
>   <!entity delete             system "delete.sgml">
>   <!entity dropAggregate      system "drop_aggregate.sgml">
>   <!entity dropDatabase       system "drop_database.sgml">
> + <!entity dropDomain         system "drop_domain.sgml">
>   <!entity dropFunction       system "drop_function.sgml">
>   <!entity dropGroup          system "drop_group.sgml">
>   <!entity dropIndex          system "drop_index.sgml">
> diff -rc pgsql.orig/doc/src/sgml/ref/comment.sgml pgsqldomain/doc/src/sgml/ref/comment.sgml
> *** pgsql.orig/doc/src/sgml/ref/comment.sgml    Thu Mar  7 11:35:33 2002
> --- pgsqldomain/doc/src/sgml/ref/comment.sgml    Thu Mar  7 22:24:23 2002
> ***************
> *** 25,31 ****
>     <synopsis>
>   COMMENT ON
>   [
> !   [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ] <replaceable
class="PARAMETER">object_name</replaceable>| 
>     COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable
class="PARAMETER">column_name</replaceable>| 
>     AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable
class="PARAMETER">agg_type</replaceable>)| 
>     FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable
class="PARAMETER">arg1</replaceable>,<replaceable class="PARAMETER">arg2</replaceable>, ...) | 
> --- 25,31 ----
>     <synopsis>
>   COMMENT ON
>   [
> !   [ DATABASE | DOMAIN | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ] <replaceable
class="PARAMETER">object_name</replaceable>| 
>     COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable
class="PARAMETER">column_name</replaceable>| 
>     AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable
class="PARAMETER">agg_type</replaceable>)| 
>     FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable
class="PARAMETER">arg1</replaceable>,<replaceable class="PARAMETER">arg2</replaceable>, ...) | 
> ***************
> *** 33,39 ****
>     TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable
class="PARAMETER">table_name</replaceable>
>   ] IS <replaceable class="PARAMETER">'text'</replaceable>
>     </synopsis>
> !
>     <refsect2 id="R2-SQL-COMMENT-1">
>      <refsect2info>
>       <date>1999-10-25</date>
> --- 33,39 ----
>     TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable
class="PARAMETER">table_name</replaceable>
>   ] IS <replaceable class="PARAMETER">'text'</replaceable>
>     </synopsis>
> !
>     <refsect2 id="R2-SQL-COMMENT-1">
>      <refsect2info>
>       <date>1999-10-25</date>
> ***************
> *** 64,70 ****
>       </variablelist>
>      </para>
>     </refsect2>
> !
>     <refsect2 id="R2-SQL-COMMENT-2">
>      <refsect2info>
>       <date>1998-09-08</date>
> --- 64,70 ----
>       </variablelist>
>      </para>
>     </refsect2>
> !
>     <refsect2 id="R2-SQL-COMMENT-2">
>      <refsect2info>
>       <date>1998-09-08</date>
> ***************
> *** 99,105 ****
>     </title>
>     <para>
>      <command>COMMENT</command> stores a comment about a database object.
> !     Comments can be
>       easily retrieved with <command>psql</command>'s
>       <command>\dd</command>, <command>\d+</command>, or <command>\l+</command>
>       commands.  Other user interfaces to retrieve comments can be built atop
> --- 99,105 ----
>     </title>
>     <para>
>      <command>COMMENT</command> stores a comment about a database object.
> !     Comments can be
>       easily retrieved with <command>psql</command>'s
>       <command>\dd</command>, <command>\d+</command>, or <command>\l+</command>
>       commands.  Other user interfaces to retrieve comments can be built atop
> ***************
> *** 141,146 ****
> --- 141,147 ----
>
>      <programlisting>
>   COMMENT ON DATABASE my_database IS 'Development Database';
> + COMMENT ON DOMAIN my_domain IS 'Domains are like abstracted fields';

This comment describes domains in general, not a specific domain.

>   COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee id';
>   COMMENT ON RULE my_rule IS 'Logs UPDATES of employee records';
>   COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
> ***************
> *** 155,166 ****
>      </programlisting>
>     </para>
>    </refsect1>
> !
>    <refsect1 id="R1-SQL-COMMENT-3">
>     <title>
>      Compatibility
>     </title>
> !
>     <refsect2 id="R2-SQL-COMMENT-4">
>      <refsect2info>
>       <date>1998-09-08</date>
> --- 156,167 ----
>      </programlisting>
>     </para>
>    </refsect1>
> !
>    <refsect1 id="R1-SQL-COMMENT-3">
>     <title>
>      Compatibility
>     </title>
> !
>     <refsect2 id="R2-SQL-COMMENT-4">
>      <refsect2info>
>       <date>1998-09-08</date>
> diff -rc pgsql.orig/doc/src/sgml/reference.sgml pgsqldomain/doc/src/sgml/reference.sgml
> *** pgsql.orig/doc/src/sgml/reference.sgml    Thu Mar  7 11:35:32 2002
> --- pgsqldomain/doc/src/sgml/reference.sgml    Thu Mar  7 22:24:23 2002
> ***************
> *** 61,66 ****
> --- 61,67 ----
>      &createAggregate;
>      &createConstraint;
>      &createDatabase;
> +    &createDomain;
>      &createFunction;
>      &createGroup;
>      &createIndex;
> ***************
> *** 78,83 ****
> --- 79,85 ----
>      &delete;
>      &dropAggregate;
>      &dropDatabase;
> +    &dropDomain;
>      &dropFunction;
>      &dropGroup;
>      &dropIndex;
> ***************
> *** 115,121 ****
>      &unlisten;
>      &update;
>      &vacuum;
> !
>    </reference>
>
>   <!--
> --- 117,123 ----
>      &unlisten;
>      &update;
>      &vacuum;
> !
>    </reference>
>
>   <!--
> diff -rc pgsql.orig/src/backend/catalog/heap.c pgsqldomain/src/backend/catalog/heap.c
> *** pgsql.orig/src/backend/catalog/heap.c    Thu Mar  7 11:35:33 2002
> --- pgsqldomain/src/backend/catalog/heap.c    Thu Mar  7 22:24:23 2002
> ***************
> *** 49,54 ****
> --- 49,55 ----
>   #include "optimizer/planmain.h"
>   #include "optimizer/prep.h"
>   #include "optimizer/var.h"
> + #include "parser/parse_coerce.h"
>   #include "parser/parse_expr.h"
>   #include "parser/parse_relation.h"
>   #include "parser/parse_target.h"
> ***************
> *** 698,707 ****
>                  "oidin",            /* receive procedure */
>                  "oidout",        /* send procedure */
>                  NULL,            /* array element type - irrelevant */
>                  NULL,            /* default type value - none */
>                  true,            /* passed by value */
>                  'i',                /* default alignment - same as for OID */
> !                'p');            /* Not TOASTable */
>   }
>
>   /* --------------------------------
> --- 699,713 ----
>                  "oidin",            /* receive procedure */
>                  "oidout",        /* send procedure */
>                  NULL,            /* array element type - irrelevant */
> +                NULL,            /* baseType Name -- typically for domaains */

spello

>                  NULL,            /* default type value - none */
> +                NULL,            /* default type binary representation */
>                  true,            /* passed by value */
>                  'i',                /* default alignment - same as for OID */
> !                'p',                /* Not TOASTable */
> !                -1,                /* Type mod length */
> !                0,                /* array dimensions for typBaseType */
> !                false);            /* Type NOT NULL */
>   }
>
>   /* --------------------------------
> ***************
> *** 1584,1589 ****
> --- 1590,1599 ----
>       int            numchecks;
>       List       *listptr;
>
> +     /* Probably shouldn't be null by default */
> +     Node       *expr = NULL;
> +
> +
>       /*
>        * Get info about existing constraints.
>        */
> ***************
> *** 1614,1681 ****
>       foreach(listptr, rawColDefaults)
>       {
>           RawColumnDefault *colDef = (RawColumnDefault *) lfirst(listptr);
> -         Node       *expr;
> -         Oid            type_id;
>
> -         Assert(colDef->raw_default != NULL);
>
> !         /*
> !          * Transform raw parsetree to executable expression.
> !          */
> !         expr = transformExpr(pstate, colDef->raw_default, EXPR_COLUMN_FIRST);
>
> !         /*
> !          * Make sure default expr does not refer to any vars.
> !          */
> !         if (contain_var_clause(expr))
> !             elog(ERROR, "cannot use column references in DEFAULT clause");
> !
> !         /*
> !          * No subplans or aggregates, either...
> !          */
> !         if (contain_subplans(expr))
> !             elog(ERROR, "cannot use subselects in DEFAULT clause");
> !         if (contain_agg_clause(expr))
> !             elog(ERROR, "cannot use aggregate functions in DEFAULT clause");
> !
> !         /*
> !          * Check that it will be possible to coerce the expression to the
> !          * column's type.  We store the expression without coercion,
> !          * however, to avoid premature coercion in cases like
> !          *
> !          * CREATE TABLE tbl (fld datetime DEFAULT 'now'::text);
> !          *
> !          * NB: this should match the code in optimizer/prep/preptlist.c that
> !          * will actually do the coercion, to ensure we don't accept an
> !          * unusable default expression.
> !          */
> !         type_id = exprType(expr);
> !         if (type_id != InvalidOid)
> !         {
> !             Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
> !
> !             if (type_id != atp->atttypid)
> !             {
> !                 if (CoerceTargetExpr(NULL, expr, type_id,
> !                                   atp->atttypid, atp->atttypmod) == NULL)
> !                     elog(ERROR, "Column \"%s\" is of type %s"
> !                          " but default expression is of type %s"
> !                     "\n\tYou will need to rewrite or cast the expression",
> !                          NameStr(atp->attname),
> !                          format_type_be(atp->atttypid),
> !                          format_type_be(type_id));
> !             }
> !         }
> !
> !         /*
> !          * Might as well try to reduce any constant expressions.
> !          */
> !         expr = eval_const_expressions(expr);
> !
> !         /*
> !          * Must fix opids, in case any operators remain...
> !          */
> !         fix_opids(expr);
>
>           /*
>            * OK, store it.
> --- 1624,1636 ----
>       foreach(listptr, rawColDefaults)
>       {
>           RawColumnDefault *colDef = (RawColumnDefault *) lfirst(listptr);
>
>
> !         Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
>
> !         expr = cookDefault(pstate, colDef->raw_default
> !                         , atp->atttypid, atp->atttypmod
> !                         , NameStr(atp->attname));
>
>           /*
>            * OK, store it.
> ***************
> *** 1891,1896 ****
> --- 1846,1933 ----
>       heap_freetuple(reltup);
>       heap_close(relrel, RowExclusiveLock);
>   }
> +
> + /*
> +  * Take a raw default and convert it to a cooked format ready for
> +  * storage.
> +  *
> +  * Parse state, attypid, attypmod and attname are required for
> +  * CoerceTargetExpr() and more importantly transformExpr().
> +  */
> + Node *
> + cookDefault(ParseState *pstate
> +             , Node *raw_default
> +             , Oid atttypid
> +             , int32 atttypmod
> +             , char *attname) {

Stick to the formatting please.

> +
> +     Oid            type_id;
> +     Node        *expr;
> +
> +     Assert(raw_default != NULL);
> +
> +     /*
> +      * Transform raw parsetree to executable expression.
> +      */
> +     expr = transformExpr(pstate, raw_default, EXPR_COLUMN_FIRST);
> +
> +     /*
> +      * Make sure default expr does not refer to any vars.
> +      */
> +     if (contain_var_clause(expr))
> +         elog(ERROR, "cannot use column references in DEFAULT clause");
> +
> +     /*
> +      * No subplans or aggregates, either...
> +      */
> +     if (contain_subplans(expr))
> +         elog(ERROR, "cannot use subselects in DEFAULT clause");
> +     if (contain_agg_clause(expr))
> +         elog(ERROR, "cannot use aggregate functions in DEFAULT clause");
> +
> +     /*
> +      * Check that it will be possible to coerce the expression to the
> +      * column's type.  We store the expression without coercion,
> +      * however, to avoid premature coercion in cases like
> +      *
> +      * CREATE TABLE tbl (fld datetime DEFAULT 'now'::text);
> +      *
> +      * NB: this should match the code in optimizer/prep/preptlist.c that
> +      * will actually do the coercion, to ensure we don't accept an
> +      * unusable default expression.
> +      */
> +     type_id = exprType(expr);
> +     if (type_id != InvalidOid && atttypid != InvalidOid) {
> +         if (type_id != atttypid) {
> +
> +             /* Try coercing to the base type of the domain if available */
> +             if (CoerceTargetExpr(pstate, expr, type_id,
> +                                  getBaseType(atttypid),
> +                                  atttypmod) == NULL) {
> +
> +                 elog(ERROR, "Column \"%s\" is of type %s"
> +                     " but default expression is of type %s"
> +                     "\n\tYou will need to rewrite or cast the expression",
> +                      attname,
> +                      format_type_be(atttypid),
> +                      format_type_be(type_id));
> +             }
> +         }
> +     }
> +
> +     /*
> +      * Might as well try to reduce any constant expressions.
> +      */
> +     expr = eval_const_expressions(expr);
> +
> +     /*
> +      * Must fix opids, in case any operators remain...
> +      */
> +     fix_opids(expr);
> +
> +     return(expr);
> + }
> +
>
>   static void
>   RemoveAttrDefaults(Relation rel)

> diff -rc pgsql.orig/src/backend/commands/creatinh.c pgsqldomain/src/backend/commands/creatinh.c
> *** pgsql.orig/src/backend/commands/creatinh.c    Thu Mar  7 11:35:34 2002
> --- pgsqldomain/src/backend/commands/creatinh.c    Thu Mar  7 23:16:06 2002
> ***************
> *** 39,45 ****
>   static void StoreCatalogInheritance(Oid relationId, List *supers);
>   static int    findAttrByName(const char *attributeName, List *schema);
>   static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
> !
>
>   /* ----------------------------------------------------------------
>    *        DefineRelation
> --- 39,45 ----
>   static void StoreCatalogInheritance(Oid relationId, List *supers);
>   static int    findAttrByName(const char *attributeName, List *schema);
>   static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
> ! static List *MergeDomainAttributes(List *schema);
>
>   /* ----------------------------------------------------------------
>    *        DefineRelation
> ***************
> *** 70,75 ****
> --- 70,82 ----
>       StrNCpy(relname, stmt->relname, NAMEDATALEN);
>
>       /*
> +      * Inherit domain attributes into the known columns before table inheritance
> +      * applies it's changes otherwise we risk adding double constraints
> +      * to a domain thats inherited.
> +      */
> +     schema = MergeDomainAttributes(schema);
> +
> +     /*
>        * Look up inheritance ancestors and generate relation schema,
>        * including inherited attributes.
>        */
> ***************
> *** 235,240 ****
> --- 242,307 ----
>   {
>       AssertArg(name);
>       heap_truncate(name);
> + }
> +
> +
> + /*
> +  * MergeDomainAttributes
> +  *      Returns a new table schema with the constraints, types, and other
> +  *      attributes of the domain resolved for fields using the domain as
> +  *        their type.

I didn't know we had schemas yet.  You should probably not overload that
term to mean "a list of database objects".

> +  *
> +  * Defaults are pulled out by the table attribute as required, similar to
> +  * how all types defaults are processed.
> +  */
> + static List *
> + MergeDomainAttributes(List *schema)
> + {
> +     List       *entry;
> +
> +     /*
> +      * Loop through the table elements supplied. These should
> +      * never include inherited domains else they'll be
> +      * double (or more) processed.
> +      */
> +     foreach(entry, schema)
> +     {
> +         ColumnDef  *coldef = lfirst(entry);
> +         HeapTuple  tuple;
> +         Form_pg_type typeTup;
> +
> +
> +         tuple = SearchSysCache(TYPENAME,
> +                                CStringGetDatum(coldef->typename->name),
> +                                0,0,0);
> +
> +         if (!HeapTupleIsValid(tuple))
> +             elog(ERROR, "MergeDomainAttributes: Type %s does not exist",
> +                  coldef->typename->name);
> +
> +         typeTup = (Form_pg_type) GETSTRUCT(tuple);
> +         if (typeTup->typtype == 'd') {
> +             /*
> +              * This is a domain, lets force the properties of the domain on to
> +              * the new column.
> +              */
> +
> +             /* Enforce the typmod value */
> +             coldef->typename->typmod = typeTup->typmod;
> +
> +             /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
> +             coldef->is_not_null |= typeTup->typnotnull;
> +
> +             /* Enforce the element type in the event the domain is an array
> +              *
> +              * BUG: How do we fill out arrayBounds and attrname from typelem and typNDimms?
> +              */
> +
> +         }
> +         ReleaseSysCache(tuple);
> +     }
> +
> +     return schema;
>   }
>
>   /*----------
> diff -rc pgsql.orig/src/backend/commands/define.c pgsqldomain/src/backend/commands/define.c
> *** pgsql.orig/src/backend/commands/define.c    Thu Mar  7 11:35:34 2002
> --- pgsqldomain/src/backend/commands/define.c    Thu Mar  7 22:24:23 2002
> ***************
> *** 40,45 ****
> --- 40,46 ----
>
>   #include "access/heapam.h"
>   #include "catalog/catname.h"
> + #include "catalog/heap.h"
>   #include "catalog/pg_aggregate.h"
>   #include "catalog/pg_language.h"
>   #include "catalog/pg_operator.h"
> ***************
> *** 476,481 ****
> --- 477,798 ----
>   }
>
>   /*
> +  * DefineDomain
> +  *        Registers a new domain.
> +  */
> + void
> + DefineDomain(CreateDomainStmt *stmt)
> + {
> +     int16        internalLength = -1;    /* int2 */
> +     int16        externalLength = -1;    /* int2 */
> +     char       *inputName = NULL;
> +     char       *outputName = NULL;
> +     char       *sendName = NULL;
> +     char       *receiveName = NULL;
> +
> +     /*
> +      * Domains store the external representation in defaultValue
> +      * and the interal Node representation in defaultValueBin
> +      */
> +     char       *defaultValue = NULL;
> +     char       *defaultValueBin = NULL;
> +
> +     bool        byValue = false;
> +     char        delimiter = DEFAULT_TYPDELIM;
> +     char        alignment = 'i';    /* default alignment */
> +     char        storage = 'p';    /* default TOAST storage method */
> +     char        typtype;
> +     Datum        datum;
> +     bool        typNotNull = false;
> +     char        *elemName = NULL;
> +     int32        typNDims = 0;    /* No array dimensions by default */
> +
> +     bool        isnull;
> +     Relation    pg_type_rel;
> +     TupleDesc    pg_type_dsc;
> +     HeapTuple    typeTup;
> +     char       *typeName = stmt->typename->name;
> +
> +     List       *listptr;
> +     List       *schema = stmt->constraints;
> +
> +     /*
> +      * Domainnames, unlike typenames don't need to account for the '_'
> +      * prefix.  So they can be one character longer.
> +      */
> +     if (strlen(stmt->domainname) > (NAMEDATALEN - 1))
> +         elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
> +              NAMEDATALEN - 1);
> +
> +
> +     /* Test for existing Domain (or type) of that name */
> +     typeTup = SearchSysCache( TYPENAME
> +                             , PointerGetDatum(stmt->domainname)
> +                             , 0, 0, 0
> +                             );
> +
> +     if (HeapTupleIsValid(typeTup))
> +     {
> +         elog(ERROR, "CREATE DOMAIN: domain or type  %s already exists",
> +              stmt->domainname);
> +     }
> +
> +     /*
> +      * Get the information about old types
> +      */
> +     pg_type_rel = heap_openr(TypeRelationName, RowExclusiveLock);
> +     pg_type_dsc = RelationGetDescr(pg_type_rel);
> +
> +
> +     /*
> +      * When the type is an array for some reason we don't actually receive
> +      * the name here.  We receive the base types name.  Lets set Dims while
> +      * were at it.
> +      */
> +     if (stmt->typename->arrayBounds > 0) {
> +         typeName = makeArrayTypeName(stmt->typename->name);
> +
> +         typNDims = length(stmt->typename->arrayBounds);
> +     }
> +
> +
> +     typeTup = SearchSysCache( TYPENAME
> +                             , PointerGetDatum(typeName)
> +                             , 0, 0, 0
> +                             );
> +
> +     if (!HeapTupleIsValid(typeTup))
> +     {
> +         elog(ERROR, "CREATE DOMAIN: type %s does not exist",
> +              stmt->typename->name);
> +     }
> +
> +
> +     /* Check that this is a basetype */
> +     typtype = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typtype, pg_type_dsc, &isnull));
> +     Assert(!isnull);
> +
> +     /*
> +      * What we really don't want is domains of domains.  This could cause all sorts
> +      * of neat issues if we allow that.
> +      *
> +      * With testing, we may determine complex types should be allowed
> +      */
> +     if (typtype != 'b') {
> +         elog(ERROR, "DefineDomain: %s is not a basetype", stmt->typename->name);
> +     }
> +
> +     /* passed by value */
> +     byValue =             DatumGetBool(heap_getattr(typeTup, Anum_pg_type_typbyval, pg_type_dsc, &isnull));
> +     Assert(!isnull);

You don't have to use heap_getattr here.  You can use

    byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval

Same for all the other ones that are fixed-length.

> +
> +     /* Required Alignment */
> +     alignment =         DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typalign, pg_type_dsc, &isnull));
> +     Assert(!isnull);
> +
> +     /* Storage Length */
> +     internalLength =     DatumGetInt16(heap_getattr(typeTup, Anum_pg_type_typlen, pg_type_dsc, &isnull));
> +     Assert(!isnull);
> +
> +     /* External Length (unused) */
> +     externalLength =     DatumGetInt16(heap_getattr(typeTup, Anum_pg_type_typprtlen, pg_type_dsc, &isnull));
> +     Assert(!isnull);
> +
> +     /* Array element Delimiter */
> +     delimiter =         DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typdelim, pg_type_dsc, &isnull));
> +     Assert(!isnull);
> +
> +     /* Input Function Name */
> +     datum =             heap_getattr(typeTup, Anum_pg_type_typinput, pg_type_dsc, &isnull);
> +     Assert(!isnull);
> +
> +     inputName =         DatumGetCString(DirectFunctionCall1(regprocout, datum));
> +
> +     /* Output Function Name */
> +     datum =             heap_getattr(typeTup, Anum_pg_type_typoutput, pg_type_dsc, &isnull);
> +     Assert(!isnull);
> +
> +     outputName =         DatumGetCString(DirectFunctionCall1(regprocout, datum));
> +
> +     /* ReceiveName */
> +     datum =             heap_getattr(typeTup, Anum_pg_type_typreceive, pg_type_dsc, &isnull);
> +     Assert(!isnull);
> +
> +     receiveName =         DatumGetCString(DirectFunctionCall1(regprocout, datum));
> +
> +     /* SendName */
> +     datum =             heap_getattr(typeTup, Anum_pg_type_typsend, pg_type_dsc, &isnull);
> +     Assert(!isnull);
> +
> +     sendName =             DatumGetCString(DirectFunctionCall1(regprocout, datum));
> +
> +     /* TOAST Strategy */
> +     storage =             DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typstorage, pg_type_dsc, &isnull));
> +     Assert(!isnull);
> +
> +     /* Inherited default value */
> +     datum =             heap_getattr(typeTup, Anum_pg_type_typdefault, pg_type_dsc, &isnull);
> +     if (!isnull) {
> +         defaultValue =     DatumGetCString(DirectFunctionCall1(textout, datum));
> +     }
> +
> +     /*
> +      * Pull out the typelem name of the parent OID.
> +      *
> +      * This is what enables us to make a domain of an array
> +      */
> +     datum =             heap_getattr(typeTup, Anum_pg_type_typelem, pg_type_dsc, &isnull);
> +     Assert(!isnull);
> +
> +     if (DatumGetObjectId(datum) != InvalidOid) {
> +         HeapTuple tup;
> +
> +         tup = SearchSysCache( TYPEOID
> +                             , datum
> +                             , 0, 0, 0
> +                             );
> +
> +         elemName = NameStr(((Form_pg_type) GETSTRUCT(tup))->typname);
> +
> +         ReleaseSysCache(tup);
> +     }
> +
> +
> +     /*
> +      * Run through constraints manually avoids the additional
> +      * processing conducted by DefineRelation() and friends.
> +      *
> +      * Besides, we don't want any constraints to be cooked.  We'll
> +      * do that when the table is created via MergeDomainAttributes().
> +      */
> +     foreach(listptr, schema)
> +     {
> +         bool nullDefined = false;
> +         Node       *expr;
> +         Constraint *colDef = lfirst(listptr);
> +
> +         /* Used for the statement transformation */
> +         ParseState *pstate;
> +
> +         /*
> +          * Create a dummy ParseState and insert the target relation as its
> +          * sole rangetable entry.  We need a ParseState for transformExpr.
> +          */
> +         pstate = make_parsestate(NULL);
> +
> +         switch(colDef->contype) {
> +             /*
> +               * The inherited default value may be overridden by the user
> +              * with the DEFAULT <expr> statement.
> +              *
> +               * We have to search the entire constraint tree returned as we
> +              * don't want to cook or fiddle too much.
> +              */
> +             case CONSTR_DEFAULT:
> +
> +                 /*
> +                  * Cook the colDef->raw_expr into an expression to ensure
> +                  * that it can be done.  We store the text version of the
> +                  * raw value.
> +                  *
> +                  * Note: Name is strictly for error message
> +                  */
> +                 expr = cookDefault(pstate, colDef->raw_expr
> +                                 , typeTup->t_data->t_oid
> +                                 , stmt->typename->typmod
> +                                 , stmt->typename->name);
> +
> +                 /* Binary default required */
> +                 defaultValue = deparse_expression(expr,
> +                                 deparse_context_for(stmt->domainname,
> +                                                     InvalidOid),
> +                                                    false);
> +
> +                 defaultValueBin = nodeToString(expr);
> +
> +                 break;
> +
> +             /*
> +              * Find the NULL constraint.
> +              */
> +             case CONSTR_NOTNULL:
> +                 if (nullDefined) {
> +                     elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
> +                 } else {
> +                     typNotNull = true;
> +                     nullDefined = true;
> +                 }
> +
> +                   break;
> +
> +             case CONSTR_NULL:
> +                 if (nullDefined) {
> +                     elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
> +                 } else {
> +                     typNotNull = false;
> +                     nullDefined = true;
> +                 }
> +
> +                   break;
> +
> +               case CONSTR_UNIQUE:
> +                   elog(ERROR, "CREATE DOMAIN / UNIQUE indecies not supported");
> +                   break;
> +
> +               case CONSTR_PRIMARY:
> +                   elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indecies not supported");
> +                   break;
> +
> +
> +               case CONSTR_CHECK:
> +
> +                   elog(ERROR, "defineDomain: CHECK Constraints not supported");
> +                   break;
> +
> +               case CONSTR_ATTR_DEFERRABLE:
> +               case CONSTR_ATTR_NOT_DEFERRABLE:
> +               case CONSTR_ATTR_DEFERRED:
> +               case CONSTR_ATTR_IMMEDIATE:
> +                   elog(ERROR, "defineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
> +                   break;
> +         }
> +
> +     }
> +
> +     /*
> +      * Have TypeCreate do all the real work.
> +      */
> +     TypeCreate(stmt->domainname,    /* type name */
> +                InvalidOid,            /* preassigned type oid (not done here) */
> +                InvalidOid,            /* relation oid (n/a here) */
> +                internalLength,        /* internal size */
> +                externalLength,        /* external size */
> +                'd',                    /* type-type (domain type) */
> +                delimiter,            /* array element delimiter */
> +                inputName,            /* input procedure */
> +                outputName,            /* output procedure */
> +                receiveName,            /* receive procedure */
> +                sendName,            /* send procedure */
> +                elemName,            /* element type name */
> +                typeName,            /* base type name */
> +                defaultValue,        /* default type value */
> +                defaultValueBin,        /* default type value */
> +                byValue,                /* passed by value */
> +                alignment,            /* required alignment */
> +                storage,                /* TOAST strategy */
> +                stmt->typename->typmod, /* typeMod value */
> +                typNDims,            /* Array dimensions for base type */
> +                typNotNull);    /* Type NOT NULL */
> +
> +     /*
> +      * Now we can clean up.
> +      */
> +     ReleaseSysCache(typeTup);
> +     heap_close(pg_type_rel, NoLock);
> + }
> +
> +
> + /*
>    * DefineType
>    *        Registers a new type.
>    */
> ***************
> *** 490,495 ****
> --- 807,814 ----
>       char       *sendName = NULL;
>       char       *receiveName = NULL;
>       char       *defaultValue = NULL;
> +     char       *defaultValueBin = NULL;
> +     Node       *defaultRaw = (Node *) NULL;
>       bool        byValue = false;
>       char        delimiter = DEFAULT_TYPDELIM;
>       char       *shadow_type;
> ***************
> *** 531,537 ****
>           else if (strcasecmp(defel->defname, "element") == 0)
>               elemName = defGetString(defel);
>           else if (strcasecmp(defel->defname, "default") == 0)
> !             defaultValue = defGetString(defel);
>           else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
>               byValue = true;
>           else if (strcasecmp(defel->defname, "alignment") == 0)
> --- 850,856 ----
>           else if (strcasecmp(defel->defname, "element") == 0)
>               elemName = defGetString(defel);
>           else if (strcasecmp(defel->defname, "default") == 0)
> !             defaultRaw = defel->arg;
>           else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
>               byValue = true;
>           else if (strcasecmp(defel->defname, "alignment") == 0)
> ***************
> *** 591,596 ****
> --- 910,941 ----
>       if (outputName == NULL)
>           elog(ERROR, "Define: \"output\" unspecified");
>
> +
> +     if (defaultRaw) {
> +         Node   *expr;
> +         ParseState *pstate;
> +
> +         /*
> +          * Create a dummy ParseState and insert the target relation as its
> +          * sole rangetable entry.  We need a ParseState for transformExpr.
> +          */
> +         pstate = make_parsestate(NULL);
> +
> +         expr = cookDefault(pstate, defaultRaw,
> +                            InvalidOid,
> +                            -1,
> +                            typeName);
> +
> +         /* Binary default required */
> +         defaultValue = deparse_expression(expr,
> +                         deparse_context_for(typeName,
> +                                             InvalidOid),
> +                                            false);
> +
> +         defaultValueBin = nodeToString(expr);
> +     }
> +
> +
>       /*
>        * now have TypeCreate do all the real work.
>        */
> ***************
> *** 606,615 ****
>                  receiveName,        /* receive procedure */
>                  sendName,        /* send procedure */
>                  elemName,        /* element type name */
>                  defaultValue,    /* default type value */
>                  byValue,            /* passed by value */
>                  alignment,        /* required alignment */
> !                storage);        /* TOAST strategy */
>
>       /*
>        * When we create a base type (as opposed to a complex type) we need
> --- 951,965 ----
>                  receiveName,        /* receive procedure */
>                  sendName,        /* send procedure */
>                  elemName,        /* element type name */
> +                NULL,            /* base type name (Non-zero for domains) */
>                  defaultValue,    /* default type value */
> +                defaultValueBin,    /* default type value (Binary form) */
>                  byValue,            /* passed by value */
>                  alignment,        /* required alignment */
> !                storage,            /* TOAST strategy */
> !                -1,                /* typMod (Domains only) */
> !                0,                /* Array Dimensions of typbasetype */
> !                'f');            /* Type NOT NULL */
>
>       /*
>        * When we create a base type (as opposed to a complex type) we need
> ***************
> *** 632,641 ****
>                  "array_in",        /* receive procedure */
>                  "array_out",        /* send procedure */
>                  typeName,        /* element type name */
>                  NULL,            /* never a default type value */
>                  false,            /* never passed by value */
>                  alignment,        /* see above */
> !                'x');            /* ARRAY is always toastable */
>
>       pfree(shadow_type);
>   }
> --- 982,996 ----
>                  "array_in",        /* receive procedure */
>                  "array_out",        /* send procedure */
>                  typeName,        /* element type name */
> +                NULL,            /* base type name */
>                  NULL,            /* never a default type value */
> +                NULL,            /* binary default isn't sent either */
>                  false,            /* never passed by value */
>                  alignment,        /* see above */
> !                'x',                /* ARRAY is always toastable */
> !                -1,                /* typMod (Domains only) */
> !                0,                /* Array dimensions of typbasetype */
> !                'f');            /* Type NOT NULL */
>
>       pfree(shadow_type);
>   }

> diff -rc pgsql.orig/src/backend/nodes/copyfuncs.c pgsqldomain/src/backend/nodes/copyfuncs.c
> *** pgsql.orig/src/backend/nodes/copyfuncs.c    Thu Mar  7 11:35:34 2002
> --- pgsqldomain/src/backend/nodes/copyfuncs.c    Thu Mar  7 22:53:19 2002
> ***************
> *** 2227,2232 ****
> --- 2227,2247 ----
>       return newnode;
>   }
>
> + static CreateDomainStmt *
> + _copyCreateDomainStmt(CreateDomainStmt *from)
> + {
> +     CreateDomainStmt *newnode = makeNode(CreateDomainStmt);
> +
> +     if (from->domainname)
> +         newnode->domainname = pstrdup(from->domainname);
> +     if (from->typename)
> +         newnode->typename = from->typename;

That's not a copy.

> +     if (from->constraints)
> +         newnode->constraints = from->constraints;
> +
> +     return newnode;
> + }
> +
>   static CreatedbStmt *
>   _copyCreatedbStmt(CreatedbStmt *from)
>   {
> ***************
> *** 3026,3031 ****
> --- 3041,3049 ----
>               break;
>           case T_FuncWithArgs:
>               retval = _copyFuncWithArgs(from);
> +             break;
> +         case T_CreateDomainStmt:
> +             retval = _copyCreateDomainStmt(from);
>               break;
>
>           default:

> diff -rc pgsql.orig/src/backend/parser/gram.y pgsqldomain/src/backend/parser/gram.y
> *** pgsql.orig/src/backend/parser/gram.y    Thu Mar  7 11:35:35 2002
> --- pgsqldomain/src/backend/parser/gram.y    Thu Mar  7 22:34:00 2002
> ***************
> *** 97,103 ****
>
>   %}
>
> -
>   %union
>   {
>       int                    ival;
> --- 97,102 ----
> ***************
> *** 135,141 ****
>           ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
>           CopyStmt, CreateAsStmt, CreateGroupStmt, CreatePLangStmt,
>           CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
> !         CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
>           DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
>           DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
>           GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
> --- 134,141 ----
>           ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
>           CopyStmt, CreateAsStmt, CreateGroupStmt, CreatePLangStmt,
>           CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
> !         CreateUserStmt, CreateDomainStmt, CreatedbStmt, CursorStmt,

Alphabetical order?

> !         DefineStmt, DeleteStmt,
>           DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
>           DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
>           GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
> ***************
> *** 289,294 ****
> --- 289,296 ----
>   %type <list>    constraints_set_namelist
>   %type <boolean>    constraints_set_mode
>
> + %type <boolean> opt_as
> +
>   /*
>    * If you make any token changes, remember to:
>    *        - use "yacc -d" and update parse.h
> ***************
> *** 343,349 ****
>           WITHOUT
>
>   /* Keywords (in SQL92 non-reserved words) */
> ! %token    COMMITTED, SERIALIZABLE, TYPE_P
>
>   /* Keywords for Postgres support (not in SQL92 reserved words)
>    *
> --- 345,351 ----
>           WITHOUT
>
>   /* Keywords (in SQL92 non-reserved words) */
> ! %token    COMMITTED, SERIALIZABLE, TYPE_P, DOMAIN_P
>
>   /* Keywords for Postgres support (not in SQL92 reserved words)
>    *
> ***************
> *** 446,451 ****
> --- 448,454 ----
>           | CopyStmt
>           | CreateStmt
>           | CreateAsStmt
> +         | CreateDomainStmt
>           | CreateSchemaStmt
>           | CreateGroupStmt
>           | CreateSeqStmt
> ***************
> *** 776,783 ****
> --- 779,789 ----
>                       n->dbname = $3;
>                       $$ = (Node *)n;
>                   }
> +         ;
>
>
> +
> +
>   /*****************************************************************************
>    *
>    * Set PG internal variable
> ***************
> *** 1461,1467 ****
>                       n->name = NULL;
>                       if (exprIsNullConstant($2))
>                       {
> !                         /* DEFAULT NULL should be reported as empty expr */
>                           n->raw_expr = NULL;
>                       }
>                       else
> --- 1467,1476 ----
>                       n->name = NULL;
>                       if (exprIsNullConstant($2))
>                       {
> !                         /*
> !                          * DEFAULT NULL should be reported as empty expr
> !                          * Required for NOT NULL Domain overrides
> !                          */
>                           n->raw_expr = NULL;
>                       }
>                       else
> ***************
> *** 2043,2055 ****
>           | def_list ',' def_elem                { $$ = lappend($1, $3); }
>           ;
>
> ! def_elem:  ColLabel '=' def_arg
>                   {
>                       $$ = makeNode(DefElem);
>                       $$->defname = $1;
>                       $$->arg = (Node *)$3;
>                   }
> !         | ColLabel
>                   {
>                       $$ = makeNode(DefElem);
>                       $$->defname = $1;
> --- 2052,2073 ----
>           | def_list ',' def_elem                { $$ = lappend($1, $3); }
>           ;
>
> ! def_elem:  DEFAULT '=' c_expr
> !                 {
> !                     $$ = makeNode(DefElem);
> !                     $$->defname = "default";
> !                     if (exprIsNullConstant($3))
> !                         $$->arg = (Node *)NULL;
> !                     else
> !                         $$->arg = $3;
> !                 }
> !         | ColId '=' def_arg
>                   {
>                       $$ = makeNode(DefElem);
>                       $$->defname = $1;
>                       $$->arg = (Node *)$3;
>                   }
> !         | ColId
>                   {
>                       $$ = makeNode(DefElem);
>                       $$->defname = $1;
> ***************
> *** 2078,2083 ****
> --- 2096,2110 ----
>                       DropStmt *n = makeNode(DropStmt);
>                       n->removeType = $2;
>                       n->names = $3;
> +                     n->behavior = RESTRICT;        /* Restricted by default */
> +                     $$ = (Node *)n;
> +                 }
> +         | DROP DOMAIN_P name_list drop_behavior
> +                 {
> +                     DropStmt *n = makeNode(DropStmt);
> +                     n->removeType = DROP_DOMAIN_P;
> +                     n->names = $3;
> +                     n->behavior = $4;
>                       $$ = (Node *)n;
>                   }
>           ;
> ***************
> *** 2110,2116 ****
>    *  The COMMENT ON statement can take different forms based upon the type of
>    *  the object associated with the comment. The form of the statement is:
>    *
> !  *  COMMENT ON [ [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
>    *               <objname> | AGGREGATE <aggname> (<aggtype>) | FUNCTION
>    *         <funcname> (arg1, arg2, ...) | OPERATOR <op>
>    *         (leftoperand_typ rightoperand_typ) | TRIGGER <triggername> ON
> --- 2137,2143 ----
>    *  The COMMENT ON statement can take different forms based upon the type of
>    *  the object associated with the comment. The form of the statement is:
>    *
> !  *  COMMENT ON [ [ DATABASE | DOMAIN | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
>    *               <objname> | AGGREGATE <aggname> (<aggtype>) | FUNCTION
>    *         <funcname> (arg1, arg2, ...) | OPERATOR <op>
>    *         (leftoperand_typ rightoperand_typ) | TRIGGER <triggername> ON
> ***************
> *** 2196,2201 ****
> --- 2223,2229 ----
>           | RULE { $$ = RULE; }
>           | SEQUENCE { $$ = SEQUENCE; }
>           | TABLE { $$ = TABLE; }
> +         | DOMAIN_P { $$ = TYPE_P; }
>           | TYPE_P { $$ = TYPE_P; }
>           | VIEW { $$ = VIEW; }
>           ;
> ***************
> *** 3178,3183 ****
> --- 3206,3227 ----
>                   {
>                       $$ = lconsi(3, makeListi1(-1));
>                   }
> +         ;
> +
> +
> + /*****************************************************************************
> +  *
> +  *        DROP DATABASE
> +  *
> +  *
> +  *****************************************************************************/
> +
> + DropdbStmt:    DROP DATABASE database_name
> +                 {
> +                     DropdbStmt *n = makeNode(DropdbStmt);
> +                     n->dbname = $3;
> +                     $$ = (Node *)n;
> +                 }
>           | OWNER opt_equal name
>                   {
>                       $$ = lconsi(4, makeList1($3));

This doesn't look right.

> ***************
> *** 3222,3243 ****
>                   }
>           ;
>
> -
>   /*****************************************************************************
>    *
> !  *        DROP DATABASE
>    *
>    *
>    *****************************************************************************/
>
> ! DropdbStmt:    DROP DATABASE database_name
>                   {
> !                     DropdbStmt *n = makeNode(DropdbStmt);
> !                     n->dbname = $3;
>                       $$ = (Node *)n;
>                   }
>           ;
>
>
>   /*****************************************************************************
>    *
> --- 3266,3295 ----
>                   }
>           ;
>
>   /*****************************************************************************
>    *
> !  * Manipulate a domain
>    *
>    *
>    *****************************************************************************/
>
> ! CreateDomainStmt:  CREATE DOMAIN_P name opt_as Typename ColQualList opt_collate
>                   {
> !                     CreateDomainStmt *n = makeNode(CreateDomainStmt);
> !                     n->domainname = $3;
> !                     n->typename = $5;
> !                     n->constraints = $6;
> !
> !                     if ($7 != NULL)
> !                         elog(NOTICE,"CREATE DOMAIN / COLLATE %s not yet "
> !                             "implemented; clause ignored", $7);
>                       $$ = (Node *)n;
>                   }
>           ;
>
> + opt_as:    AS    {$$ = TRUE; }
> +     | /* EMPTY */    {$$ = FALSE; }
> +     ;
>
>   /*****************************************************************************
>    *
> ***************
> *** 5879,5884 ****
> --- 5931,5937 ----
>           | DEFERRED                        { $$ = "deferred"; }
>           | DELETE                        { $$ = "delete"; }
>           | DELIMITERS                    { $$ = "delimiters"; }
> +         | DOMAIN_P                        { $$ = "domain"; }
>           | DOUBLE                        { $$ = "double"; }
>           | DROP                            { $$ = "drop"; }
>           | EACH                            { $$ = "each"; }

> diff -rc pgsql.orig/src/backend/parser/parse_coerce.c pgsqldomain/src/backend/parser/parse_coerce.c
> *** pgsql.orig/src/backend/parser/parse_coerce.c    Thu Mar  7 11:35:35 2002
> --- pgsqldomain/src/backend/parser/parse_coerce.c    Thu Mar  7 22:24:24 2002
> ***************
> *** 38,43 ****
> --- 38,44 ----
>   {
>       Node       *result;
>
> +

No.

>       if (targetTypeId == inputTypeId ||
>           targetTypeId == InvalidOid ||
>           node == NULL)
> ***************
> *** 605,607 ****
> --- 606,637 ----
>       }
>       return result;
>   }    /* PreferredType() */
> +
> +
> + /*
> +  * If the targetTypeId is a domain, we really want to coerce
> +  * the tuple to the domain type -- not the domain itself
> +  */
> + Oid
> + getBaseType(Oid inType)
> + {
> +     HeapTuple    tup;
> +     Form_pg_type typTup;
> +
> +     tup = SearchSysCache(TYPEOID,
> +                          ObjectIdGetDatum(inType),
> +                          0, 0, 0);
> +
> +     typTup = ((Form_pg_type) GETSTRUCT(tup));
> +
> +     /*
> +      * Assume that typbasetype exists and is a base type, where inType
> +      * was a domain
> +      */
> +     if (typTup->typtype == 'd')
> +         inType = typTup->typbasetype;
> +
> +     ReleaseSysCache(tup);
> +
> +     return inType;
> + }

> diff -rc pgsql.orig/src/backend/tcop/postgres.c pgsqldomain/src/backend/tcop/postgres.c
> *** pgsql.orig/src/backend/tcop/postgres.c    Wed Mar  6 01:10:09 2002
> --- pgsqldomain/src/backend/tcop/postgres.c    Thu Mar  7 22:24:24 2002
> ***************
> *** 2212,2217 ****
> --- 2212,2218 ----
>               }
>               break;
>
> +         case T_CreateDomainStmt:
>           case T_CreateStmt:
>               tag = "CREATE";
>               break;

The result tag for CREATE DOMAIN is CREATE DOMAIN.  (Yes, there's actually
a standard about this.)

--
Peter Eisentraut   peter_e@gmx.net


pgsql-patches by date:

Previous
From: Neil Conway
Date:
Subject: Re: support for POSIX 1003.1-2001 hosts
Next
From: Peter Eisentraut
Date:
Subject: Re: psql: backslash fix