Skip site navigation (1) Skip section navigation (2)

Re: Domain Support -- another round

From: Peter Eisentraut <peter_e(at)gmx(dot)net>
To: Rod Taylor <rbt(at)zort(dot)ca>
Cc: <pgsql-patches(at)postgresql(dot)org>
Subject: Re: Domain Support -- another round
Date: 2002-03-12 00:14:46
Message-ID: Pine.LNX.4.30.0203111912100.690-100000@peter.localdomain (view raw or flat)
Thread:
Lists: pgsql-hackerspgsql-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(at)gmx(dot)net


In response to

Responses

pgsql-hackers by date

Next:From: Peter EisentrautDate: 2002-03-12 00:19:17
Subject: Re: psql and output from \?
Previous:From: Ian BarwickDate: 2002-03-11 23:55:27
Subject: Re: psql and output from \?

pgsql-patches by date

Next:From: Peter EisentrautDate: 2002-03-12 00:17:51
Subject: Re: psql: backslash fix
Previous:From: Neil ConwayDate: 2002-03-12 00:05:40
Subject: Re: support for POSIX 1003.1-2001 hosts

Privacy Policy | About PostgreSQL
Copyright © 1996-2014 The PostgreSQL Global Development Group