diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-07-10 22:10:57 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-07-10 22:10:57 +0000 |
commit | 6e37be02e71ddc9cecad1ba962397cfc4a27e922 (patch) | |
tree | f7ed1387329d65363d020b15f3d1d9ae60c9a2fc /src | |
parent | acf40f39be47cd92ff394e9c42f9ffa4dfd741ed (diff) | |
download | postgresql-6e37be02e71ddc9cecad1ba962397cfc4a27e922.tar.gz postgresql-6e37be02e71ddc9cecad1ba962397cfc4a27e922.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/tablecmds.c | 69 |
1 files changed, 38 insertions, 31 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index bc28c596831..cf665a6eb4f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.142.4.5 2006/01/30 16:19:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.142.4.6 2006/07/10 22:10:57 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -122,6 +122,7 @@ typedef struct AlteredTableInfo /* 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 */ @@ -131,11 +132,11 @@ typedef struct AlteredTableInfo } 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, NOT_NULL, or FOREIGN */ - AttrNumber attnum; /* only relevant for NOT_NULL */ + ConstrType contype; /* CHECK or FOREIGN */ Oid refrelid; /* PK rel, if FOREIGN */ Node *qual; /* Check expr or FkConstraint struct */ List *qualstate; /* Execution state for CHECK */ @@ -2292,7 +2293,7 @@ ATRewriteTables(List **wqueue) * constraints generated by ALTER TABLE commands, but don't * rebuild data. */ - if (tab->constraints != NIL) + if (tab->constraints != NIL || tab->new_notnull) ATRewriteTable(tab, InvalidOid); /* @@ -2358,6 +2359,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) TupleDesc oldTupDesc; TupleDesc newTupDesc; bool needscan = false; + List *notnull_attrs; int i; ListCell *l; EState *estate; @@ -2408,9 +2410,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) case CONSTR_FOREIGN: /* Nothing to do here */ break; - case CONSTR_NOTNULL: - needscan = true; - break; default: elog(ERROR, "unrecognized constraint type: %d", (int) con->contype); @@ -2426,6 +2425,25 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) 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; @@ -2535,6 +2553,17 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) 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); @@ -2548,21 +2577,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) 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; @@ -3231,7 +3245,6 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, HeapTuple tuple; AttrNumber attnum; Relation attr_rel; - NewConstraint *newcon; /* * lookup the attribute @@ -3267,13 +3280,8 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, /* 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); + /* Tell Phase 3 it needs to test the constraint */ + tab->new_notnull = true; } heap_close(attr_rel, RowExclusiveLock); @@ -3741,7 +3749,6 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint) 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); |