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: