Fix ALTER TABLE ADD COLUMN so that constraints of domain types are
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 24 Jan 2005 23:21:57 +0000 (23:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 24 Jan 2005 23:21:57 +0000 (23:21 +0000)
enforced properly when there is no explicit default value for the new
column.  Per report from Craig Perras.

src/backend/commands/tablecmds.c
src/backend/commands/typecmds.c

index b788db35cbb985c46df41fbfa0f7d6a6348dc2de..89dd8dffae015371e5841448428b9107cfc22338 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.142 2005/01/10 20:02:20 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.143 2005/01/24 23:21:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,7 @@
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
+#include "commands/typecmds.h"
 #include "executor/executor.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
@@ -2853,6 +2854,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
        int                     minattnum,
                                maxatts;
        HeapTuple       typeTuple;
+       Oid                     typeOid;
        Form_pg_type tform;
        Expr       *defval;
 
@@ -2930,9 +2932,10 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 
        typeTuple = typenameType(colDef->typename);
        tform = (Form_pg_type) GETSTRUCT(typeTuple);
+       typeOid = HeapTupleGetOid(typeTuple);
 
        /* make sure datatype is legal for a column */
-       CheckAttributeType(colDef->colname, HeapTupleGetOid(typeTuple));
+       CheckAttributeType(colDef->colname, typeOid);
 
        attributeTuple = heap_addheader(Natts_pg_attribute,
                                                                        false,
@@ -2943,7 +2946,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 
        attribute->attrelid = myrelid;
        namestrcpy(&(attribute->attname), colDef->colname);
-       attribute->atttypid = HeapTupleGetOid(typeTuple);
+       attribute->atttypid = typeOid;
        attribute->attstattarget = -1;
        attribute->attlen = tform->typlen;
        attribute->attcacheoff = -1;
@@ -3015,11 +3018,37 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
         * and return NULL if so, so without any modification of the tuple
         * data we will get the effect of NULL values in the new column.
         *
+        * An exception occurs when the new column is of a domain type: the
+        * domain might have a NOT NULL constraint, or a check constraint that
+        * indirectly rejects nulls.  If there are any domain constraints then
+        * we construct an explicit NULL default value that will be passed through
+        * CoerceToDomain processing.  (This is a tad inefficient, since it
+        * causes rewriting the table which we really don't have to do, but
+        * the present design of domain processing doesn't offer any simple way
+        * of checking the constraints more directly.)
+        *
         * Note: we use build_column_default, and not just the cooked default
         * returned by AddRelationRawConstraints, so that the right thing
         * happens when a datatype's default applies.
         */
        defval = (Expr *) build_column_default(rel, attribute->attnum);
+
+       if (!defval && GetDomainConstraints(typeOid) != NIL)
+       {
+               Oid             basetype = getBaseType(typeOid);
+
+               defval = (Expr *) makeNullConst(basetype);
+               defval = (Expr *) coerce_to_target_type(NULL,
+                                                                                               (Node *) defval,
+                                                                                               basetype,
+                                                                                               typeOid,
+                                                                                               colDef->typename->typmod,
+                                                                                               COERCION_ASSIGNMENT,
+                                                                                               COERCE_IMPLICIT_CAST);
+               if (defval == NULL)             /* should not happen */
+                       elog(ERROR, "failed to coerce base type to domain");
+       }
+
        if (defval)
        {
                NewColumnValue *newval;
index efeb3f3bab2ee27fb39e47c3c025e16a0c1c6c32..d9fffbec271fe5a63aeed7e55e88a231b3ce085e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.65 2004/12/31 21:59:42 pgsql Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.66 2005/01/24 23:21:57 tgl Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
@@ -1929,6 +1929,9 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
  * This is called by the executor during plan startup for a CoerceToDomain
  * expression node.  The given constraints will be checked for each value
  * passed through the node.
+ *
+ * We allow this to be called for non-domain types, in which case the result
+ * is always NIL.
  */
 List *
 GetDomainConstraints(Oid typeOid)
@@ -1954,6 +1957,13 @@ GetDomainConstraints(Oid typeOid)
                        elog(ERROR, "cache lookup failed for type %u", typeOid);
                typTup = (Form_pg_type) GETSTRUCT(tup);
 
+               if (typTup->typtype != 'd')
+               {
+                       /* Not a domain, so done */
+                       ReleaseSysCache(tup);
+                       break;
+               }
+
                /* Test for NOT NULL Constraint */
                if (typTup->typnotnull)
                        notNull = true;
@@ -2010,14 +2020,7 @@ GetDomainConstraints(Oid typeOid)
 
                systable_endscan(scan);
 
-               if (typTup->typtype != 'd')
-               {
-                       /* Not a domain, so done */
-                       ReleaseSysCache(tup);
-                       break;
-               }
-
-               /* else loop to next domain in stack */
+               /* loop to next domain in stack */
                typeOid = typTup->typbasetype;
                ReleaseSysCache(tup);
        }