Fix ALTER TABLE ADD COLUMN so that constraints of domain types are
authorTom Lane
Mon, 24 Jan 2005 23:21:57 +0000 (23:21 +0000)
committerTom Lane
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);
    }