Index: tablecmds.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.192 diff -c -r1.192 tablecmds.c *** tablecmds.c 3 Jul 2006 22:45:38 -0000 1.192 --- tablecmds.c 4 Jul 2006 22:29:34 -0000 *************** *** 123,128 **** --- 123,129 ---- /* Information saved by Phases 1/2 for Phase 3: */ List *constraints; /* List of NewConstraint */ List *newvals; /* List of NewColumnValue */ + bool new_notnull; /* T if we added new NOT NULL constraints */ Oid newTableSpace; /* new tablespace; 0 means no change */ /* Objects to rebuild after completing ALTER TYPE operations */ List *changedConstraintOids; /* OIDs of constraints to rebuild */ *************** *** 132,142 **** } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ typedef struct NewConstraint { char *name; /* Constraint name, or NULL if none */ ! ConstrType contype; /* CHECK, NOT_NULL, or FOREIGN */ ! AttrNumber attnum; /* only relevant for NOT_NULL */ Oid refrelid; /* PK rel, if FOREIGN */ Node *qual; /* Check expr or FkConstraint struct */ List *qualstate; /* Execution state for CHECK */ --- 133,143 ---- } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ + /* Note: new NOT NULL constraints are handled elsewhere */ typedef struct NewConstraint { char *name; /* Constraint name, or NULL if none */ ! ConstrType contype; /* CHECK or FOREIGN */ Oid refrelid; /* PK rel, if FOREIGN */ Node *qual; /* Check expr or FkConstraint struct */ List *qualstate; /* Execution state for CHECK */ *************** *** 2438,2444 **** * Test the current data within the table against new constraints * generated by ALTER TABLE commands, but don't rebuild data. */ ! if (tab->constraints != NIL) ATRewriteTable(tab, InvalidOid); /* --- 2439,2445 ---- * Test the current data within the table against new constraints * generated by ALTER TABLE commands, but don't rebuild data. */ ! if (tab->constraints != NIL || tab->new_notnull) ATRewriteTable(tab, InvalidOid); /* *************** *** 2504,2509 **** --- 2505,2511 ---- TupleDesc oldTupDesc; TupleDesc newTupDesc; bool needscan = false; + List *notnull_attrs; int i; ListCell *l; EState *estate; *************** *** 2554,2562 **** case CONSTR_FOREIGN: /* Nothing to do here */ break; - case CONSTR_NOTNULL: - needscan = true; - break; default: elog(ERROR, "unrecognized constraint type: %d", (int) con->contype); --- 2556,2561 ---- *************** *** 2572,2577 **** --- 2571,2595 ---- ex->exprstate = ExecPrepareExpr((Expr *) ex->expr, estate); } + notnull_attrs = NIL; + if (newrel || tab->new_notnull) + { + /* + * If we are rebuilding the tuples OR if we added any new NOT NULL + * constraints, check all not-null constraints. This is a bit of + * overkill but it minimizes risk of bugs, and heap_attisnull is + * a pretty cheap test anyway. + */ + for (i = 0; i < newTupDesc->natts; i++) + { + if (newTupDesc->attrs[i]->attnotnull && + !newTupDesc->attrs[i]->attisdropped) + notnull_attrs = lappend_int(notnull_attrs, i); + } + if (notnull_attrs) + needscan = true; + } + if (needscan) { ExprContext *econtext; *************** *** 2672,2677 **** --- 2690,2706 ---- ExecStoreTuple(tuple, newslot, InvalidBuffer, false); econtext->ecxt_scantuple = newslot; + foreach(l, notnull_attrs) + { + int attn = lfirst_int(l); + + if (heap_attisnull(tuple, attn+1)) + ereport(ERROR, + (errcode(ERRCODE_NOT_NULL_VIOLATION), + errmsg("column \"%s\" contains null values", + NameStr(newTupDesc->attrs[attn]->attname)))); + } + foreach(l, tab->constraints) { NewConstraint *con = lfirst(l); *************** *** 2685,2705 **** errmsg("check constraint \"%s\" is violated by some row", con->name))); break; - case CONSTR_NOTNULL: - { - Datum d; - bool isnull; - - d = heap_getattr(tuple, con->attnum, newTupDesc, - &isnull); - if (isnull) - ereport(ERROR, - (errcode(ERRCODE_NOT_NULL_VIOLATION), - errmsg("column \"%s\" contains null values", - get_attname(tab->relid, - con->attnum)))); - } - break; case CONSTR_FOREIGN: /* Nothing to do here */ break; --- 2714,2719 ---- *************** *** 3398,3404 **** HeapTuple tuple; AttrNumber attnum; Relation attr_rel; - NewConstraint *newcon; /* * lookup the attribute --- 3412,3417 ---- *************** *** 3434,3446 **** /* keep the system catalog indexes current */ CatalogUpdateIndexes(attr_rel, tuple); ! /* Tell Phase 3 to test the constraint */ ! newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); ! newcon->contype = CONSTR_NOTNULL; ! newcon->attnum = attnum; ! newcon->name = "NOT NULL"; ! ! tab->constraints = lappend(tab->constraints, newcon); } heap_close(attr_rel, RowExclusiveLock); --- 3447,3454 ---- /* keep the system catalog indexes current */ CatalogUpdateIndexes(attr_rel, tuple); ! /* Tell Phase 3 it needs to test the constraint */ ! tab->new_notnull = true; } heap_close(attr_rel, RowExclusiveLock); *************** *** 3909,3915 **** newcon = (NewConstraint *) palloc0(sizeof(NewConstraint)); newcon->name = ccon->name; newcon->contype = ccon->contype; - newcon->attnum = ccon->attnum; /* ExecQual wants implicit-AND format */ newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr); --- 3917,3922 ----