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 ----
