88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.192 2006/07/03 22:45:38 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.193 2006/07/10 22:10:39 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -123,6 +123,7 @@ typedef struct AlteredTableInfo
123123/* Information saved by Phases 1/2 for Phase 3: */
124124List * constraints ;/* List of NewConstraint */
125125List * newvals ;/* List of NewColumnValue */
126+ bool new_notnull ;/* T if we added new NOT NULL constraints */
126127Oid newTableSpace ;/* new tablespace; 0 means no change */
127128/* Objects to rebuild after completing ALTER TYPE operations */
128129List * changedConstraintOids ;/* OIDs of constraints to rebuild */
@@ -132,11 +133,11 @@ typedef struct AlteredTableInfo
132133}AlteredTableInfo ;
133134
134135/* Struct describing one new constraint to check in Phase 3 scan */
136+ /* Note: new NOT NULL constraints are handled elsewhere */
135137typedef struct NewConstraint
136138{
137139char * name ;/* Constraint name, or NULL if none */
138- ConstrType contype ;/* CHECK, NOT_NULL, or FOREIGN */
139- AttrNumber attnum ;/* only relevant for NOT_NULL */
140+ ConstrType contype ;/* CHECK or FOREIGN */
140141Oid refrelid ;/* PK rel, if FOREIGN */
141142Node * qual ;/* Check expr or FkConstraint struct */
142143List * qualstate ;/* Execution state for CHECK */
@@ -2438,7 +2439,7 @@ ATRewriteTables(List **wqueue)
24382439 * Test the current data within the table against new constraints
24392440 * generated by ALTER TABLE commands, but don't rebuild data.
24402441 */
2441- if (tab -> constraints != NIL )
2442+ if (tab -> constraints != NIL || tab -> new_notnull )
24422443ATRewriteTable (tab ,InvalidOid );
24432444
24442445/*
@@ -2504,6 +2505,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25042505TupleDesc oldTupDesc ;
25052506TupleDesc newTupDesc ;
25062507bool needscan = false;
2508+ List * notnull_attrs ;
25072509int i ;
25082510ListCell * l ;
25092511EState * estate ;
@@ -2554,9 +2556,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25542556case CONSTR_FOREIGN :
25552557/* Nothing to do here */
25562558break ;
2557- case CONSTR_NOTNULL :
2558- needscan = true;
2559- break ;
25602559default :
25612560elog (ERROR ,"unrecognized constraint type: %d" ,
25622561 (int )con -> contype );
@@ -2572,6 +2571,25 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25722571ex -> exprstate = ExecPrepareExpr ((Expr * )ex -> expr ,estate );
25732572}
25742573
2574+ notnull_attrs = NIL ;
2575+ if (newrel || tab -> new_notnull )
2576+ {
2577+ /*
2578+ * If we are rebuilding the tuples OR if we added any new NOT NULL
2579+ * constraints, check all not-null constraints. This is a bit of
2580+ * overkill but it minimizes risk of bugs, and heap_attisnull is
2581+ * a pretty cheap test anyway.
2582+ */
2583+ for (i = 0 ;i < newTupDesc -> natts ;i ++ )
2584+ {
2585+ if (newTupDesc -> attrs [i ]-> attnotnull &&
2586+ !newTupDesc -> attrs [i ]-> attisdropped )
2587+ notnull_attrs = lappend_int (notnull_attrs ,i );
2588+ }
2589+ if (notnull_attrs )
2590+ needscan = true;
2591+ }
2592+
25752593if (needscan )
25762594{
25772595ExprContext * econtext ;
@@ -2672,6 +2690,17 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
26722690ExecStoreTuple (tuple ,newslot ,InvalidBuffer , false);
26732691econtext -> ecxt_scantuple = newslot ;
26742692
2693+ foreach (l ,notnull_attrs )
2694+ {
2695+ int attn = lfirst_int (l );
2696+
2697+ if (heap_attisnull (tuple ,attn + 1 ))
2698+ ereport (ERROR ,
2699+ (errcode (ERRCODE_NOT_NULL_VIOLATION ),
2700+ errmsg ("column \"%s\" contains null values" ,
2701+ NameStr (newTupDesc -> attrs [attn ]-> attname ))));
2702+ }
2703+
26752704foreach (l ,tab -> constraints )
26762705{
26772706NewConstraint * con = lfirst (l );
@@ -2685,21 +2714,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
26852714errmsg ("check constraint \"%s\" is violated by some row" ,
26862715con -> name )));
26872716break ;
2688- case CONSTR_NOTNULL :
2689- {
2690- Datum d ;
2691- bool isnull ;
2692-
2693- d = heap_getattr (tuple ,con -> attnum ,newTupDesc ,
2694- & isnull );
2695- if (isnull )
2696- ereport (ERROR ,
2697- (errcode (ERRCODE_NOT_NULL_VIOLATION ),
2698- errmsg ("column \"%s\" contains null values" ,
2699- get_attname (tab -> relid ,
2700- con -> attnum ))));
2701- }
2702- break ;
27032717case CONSTR_FOREIGN :
27042718/* Nothing to do here */
27052719break ;
@@ -3398,7 +3412,6 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
33983412HeapTuple tuple ;
33993413AttrNumber attnum ;
34003414Relation attr_rel ;
3401- NewConstraint * newcon ;
34023415
34033416/*
34043417 * lookup the attribute
@@ -3434,13 +3447,8 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
34343447/* keep the system catalog indexes current */
34353448CatalogUpdateIndexes (attr_rel ,tuple );
34363449
3437- /* Tell Phase 3 to test the constraint */
3438- newcon = (NewConstraint * )palloc0 (sizeof (NewConstraint ));
3439- newcon -> contype = CONSTR_NOTNULL ;
3440- newcon -> attnum = attnum ;
3441- newcon -> name = "NOT NULL" ;
3442-
3443- tab -> constraints = lappend (tab -> constraints ,newcon );
3450+ /* Tell Phase 3 it needs to test the constraint */
3451+ tab -> new_notnull = true;
34443452}
34453453
34463454heap_close (attr_rel ,RowExclusiveLock );
@@ -3909,7 +3917,6 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
39093917newcon = (NewConstraint * )palloc0 (sizeof (NewConstraint ));
39103918newcon -> name = ccon -> name ;
39113919newcon -> contype = ccon -> contype ;
3912- newcon -> attnum = ccon -> attnum ;
39133920/* ExecQual wants implicit-AND format */
39143921newcon -> qual = (Node * )
39153922make_ands_implicit ((Expr * )ccon -> expr );