Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit cb33de7

Browse files
committed
Fix ALTER TABLE to check pre-existing NOT NULL constraints when rewriting
a table. Otherwise a USING clause that yields NULL can leave the table violating its constraint (possibly there are other cases too). Per report from Alexander Pravking.
1 parent c5133e5 commit cb33de7

File tree

1 file changed

+38
-31
lines changed

1 file changed

+38
-31
lines changed

src/backend/commands/tablecmds.c

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
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: */
124124
List *constraints; /* List of NewConstraint */
125125
List *newvals; /* List of NewColumnValue */
126+
bool new_notnull; /* T if we added new NOT NULL constraints */
126127
Oid newTableSpace; /* new tablespace; 0 means no change */
127128
/* Objects to rebuild after completing ALTER TYPE operations */
128129
List *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 */
135137
typedef struct NewConstraint
136138
{
137139
char *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 */
140141
Oid refrelid; /* PK rel, if FOREIGN */
141142
Node *qual; /* Check expr or FkConstraint struct */
142143
List *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)
24422443
ATRewriteTable(tab, InvalidOid);
24432444

24442445
/*
@@ -2504,6 +2505,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25042505
TupleDesc oldTupDesc;
25052506
TupleDesc newTupDesc;
25062507
bool needscan = false;
2508+
List *notnull_attrs;
25072509
int i;
25082510
ListCell *l;
25092511
EState *estate;
@@ -2554,9 +2556,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25542556
case CONSTR_FOREIGN:
25552557
/* Nothing to do here */
25562558
break;
2557-
case CONSTR_NOTNULL:
2558-
needscan = true;
2559-
break;
25602559
default:
25612560
elog(ERROR, "unrecognized constraint type: %d",
25622561
(int) con->contype);
@@ -2572,6 +2571,25 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
25722571
ex->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+
25752593
if (needscan)
25762594
{
25772595
ExprContext *econtext;
@@ -2672,6 +2690,17 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
26722690
ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
26732691
econtext->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+
26752704
foreach(l, tab->constraints)
26762705
{
26772706
NewConstraint *con = lfirst(l);
@@ -2685,21 +2714,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
26852714
errmsg("check constraint \"%s\" is violated by some row",
26862715
con->name)));
26872716
break;
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;
27032717
case CONSTR_FOREIGN:
27042718
/* Nothing to do here */
27052719
break;
@@ -3398,7 +3412,6 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
33983412
HeapTuple tuple;
33993413
AttrNumber attnum;
34003414
Relation 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 */
34353448
CatalogUpdateIndexes(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

34463454
heap_close(attr_rel, RowExclusiveLock);
@@ -3909,7 +3917,6 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint)
39093917
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
39103918
newcon->name = ccon->name;
39113919
newcon->contype = ccon->contype;
3912-
newcon->attnum = ccon->attnum;
39133920
/* ExecQual wants implicit-AND format */
39143921
newcon->qual = (Node *)
39153922
make_ands_implicit((Expr *) ccon->expr);

0 commit comments

Comments
 (0)