提交 f9e6e27c 编写于 作者: T Tom Lane

Break transformCreateStmt() into multiple routines and make

transformAlterStmt() use these routines, instead of having lots of
duplicate (not to mention should-have-been-duplicate) code.
Adding a column with a CHECK constraint actually works now,
and the tests to reject unsupported DEFAULT and NOT NULL clauses
actually fire now.  ALTER TABLE ADD PRIMARY KEY works, modulo
having to have created the column(s) NOT NULL already.
上级 e98476eb
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.28 2001/09/12 02:13:25 ishii Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.29 2001/10/12 00:07:14 tgl Exp $
Postgres documentation Postgres documentation
--> -->
...@@ -24,8 +24,7 @@ Postgres documentation ...@@ -24,8 +24,7 @@ Postgres documentation
</refsynopsisdivinfo> </refsynopsisdivinfo>
<synopsis> <synopsis>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ] ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
class="PARAMETER">type</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ] ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ * ]
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET DEFAULT <replaceable ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> { SET DEFAULT <replaceable
class="PARAMETER">value</replaceable> | DROP DEFAULT } class="PARAMETER">value</replaceable> | DROP DEFAULT }
...@@ -202,21 +201,14 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable> ...@@ -202,21 +201,14 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
<para> <para>
In the current implementation of <literal>ADD COLUMN</literal>, In the current implementation of <literal>ADD COLUMN</literal>,
default and constraint clauses for the default and NOT NULL clauses for the new column are not supported.
new column will be ignored. You can use the <literal>SET DEFAULT</literal> You can use the <literal>SET DEFAULT</literal> form
form of <command>ALTER TABLE</command> to set the default later. of <command>ALTER TABLE</command> to set the default later.
(You may also want to update the already existing rows to the (You may also want to update the already existing rows to the
new default value, using <xref linkend="sql-update" new default value, using <xref linkend="sql-update"
endterm="sql-update-title">.) endterm="sql-update-title">.)
</para> </para>
<para>
In the current implementation, only UNIQUE, FOREIGN KEY and CHECK constraints can
be added to a table. To create a primary constraint, create
a unique, not null index (see <xref linkend="SQL-CREATEINDEX"
endterm="SQL-CREATEINDEX-title">).
</para>
<para> <para>
Currently only CHECK constraints can be dropped from a table. The RESTRICT Currently only CHECK constraints can be dropped from a table. The RESTRICT
keyword is required, although dependencies are not checked. The CASCADE keyword is required, although dependencies are not checked. The CASCADE
...@@ -318,7 +310,7 @@ ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zip ...@@ -318,7 +310,7 @@ ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zip
<title>SQL92</title> <title>SQL92</title>
<para> <para>
The <literal>ADD COLUMN</literal> form is compliant with the exception that The <literal>ADD COLUMN</literal> form is compliant with the exception that
it does not support defaults and constraints, as explained above. it does not support defaults and NOT NULL constraints, as explained above.
The <literal>ALTER COLUMN</literal> form is in full compliance. The <literal>ALTER COLUMN</literal> form is in full compliance.
</para> </para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.63 2001/09/06 02:07:42 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.64 2001/10/12 00:07:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -555,9 +555,8 @@ TypeRename(const char *oldTypeName, const char *newTypeName) ...@@ -555,9 +555,8 @@ TypeRename(const char *oldTypeName, const char *newTypeName)
* makeArrayTypeName(typeName); * makeArrayTypeName(typeName);
* - given a base type name, make an array of type name out of it * - given a base type name, make an array of type name out of it
* *
* the CALLER is responsible for pfreeing the * the caller is responsible for pfreeing the result
*/ */
char * char *
makeArrayTypeName(char *typeName) makeArrayTypeName(char *typeName)
{ {
...@@ -565,10 +564,8 @@ makeArrayTypeName(char *typeName) ...@@ -565,10 +564,8 @@ makeArrayTypeName(char *typeName)
if (!typeName) if (!typeName)
return NULL; return NULL;
arr = palloc(strlen(typeName) + 2); arr = palloc(NAMEDATALEN);
arr[0] = '_'; snprintf(arr, NAMEDATALEN,
strcpy(arr + 1, typeName); "_%.*s", NAMEDATALEN - 2, typeName);
return arr; return arr;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.143 2001/10/05 17:28:11 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.144 2001/10/12 00:07:14 tgl Exp $
* *
* NOTES * NOTES
* The PerformAddAttribute() code, like most of the relation * The PerformAddAttribute() code, like most of the relation
...@@ -309,9 +309,10 @@ AlterTableAddColumn(const char *relationName, ...@@ -309,9 +309,10 @@ AlterTableAddColumn(const char *relationName,
int i; int i;
int minattnum, int minattnum,
maxatts; maxatts;
Relation idescs[Num_pg_attr_indices]; HeapTuple typeTuple;
Relation ridescs[Num_pg_class_indices]; Form_pg_type tform;
bool hasindex; char *typename;
int attndims;
/* /*
* permissions checking. this would normally be done in utility.c, * permissions checking. this would normally be done in utility.c,
...@@ -339,57 +340,64 @@ AlterTableAddColumn(const char *relationName, ...@@ -339,57 +340,64 @@ AlterTableAddColumn(const char *relationName,
heap_close(rel, NoLock); /* close rel but keep lock! */ heap_close(rel, NoLock); /* close rel but keep lock! */
/* /*
* we can't add a not null attribute * Recurse to add the column to child classes, if requested.
*/
if (colDef->is_not_null)
elog(ERROR, "Can't add a NOT NULL attribute to an existing relation");
if (colDef->raw_default || colDef->cooked_default)
elog(ERROR, "Adding columns with defaults is not implemented.");
/*
* if the first element in the 'schema' list is a "*" then we are
* supposed to add this attribute to all classes that inherit from
* 'relationName' (as well as to 'relationName').
* *
* any permissions or problems with duplicate attributes will cause the * any permissions or problems with duplicate attributes will cause the
* whole transaction to abort, which is what we want -- all or * whole transaction to abort, which is what we want -- all or
* nothing. * nothing.
*/ */
if (colDef != NULL) if (inherits)
{ {
if (inherits) List *child,
{ *children;
List *child,
*children;
/* this routine is actually in the planner */ /* this routine is actually in the planner */
children = find_all_inheritors(myrelid); children = find_all_inheritors(myrelid);
/* /*
* find_all_inheritors does the recursive search of the * find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all * inheritance hierarchy, so all we have to do is process all
* of the relids in the list that it returns. * of the relids in the list that it returns.
*/ */
foreach(child, children) foreach(child, children)
{ {
Oid childrelid = lfirsti(child); Oid childrelid = lfirsti(child);
char *childrelname; char *childrelname;
if (childrelid == myrelid) if (childrelid == myrelid)
continue; continue;
rel = heap_open(childrelid, AccessExclusiveLock); rel = heap_open(childrelid, AccessExclusiveLock);
childrelname = pstrdup(RelationGetRelationName(rel)); childrelname = pstrdup(RelationGetRelationName(rel));
heap_close(rel, AccessExclusiveLock); heap_close(rel, AccessExclusiveLock);
AlterTableAddColumn(childrelname, false, colDef); AlterTableAddColumn(childrelname, false, colDef);
pfree(childrelname); pfree(childrelname);
}
} }
} }
/*
* OK, get on with it...
*
* Implementation restrictions: because we don't touch the table rows,
* the new column values will initially appear to be NULLs. (This
* happens because the heap tuple access routines always check for
* attnum > # of attributes in tuple, and return NULL if so.) Therefore
* we can't support a DEFAULT value in SQL92-compliant fashion, and
* we also can't allow a NOT NULL constraint.
*
* We do allow CHECK constraints, even though these theoretically
* could fail for NULL rows (eg, CHECK (newcol IS NOT NULL)).
*/
if (colDef->raw_default || colDef->cooked_default)
elog(ERROR, "Adding columns with defaults is not implemented."
"\n\tAdd the column, then use ALTER TABLE SET DEFAULT.");
if (colDef->is_not_null)
elog(ERROR, "Adding NOT NULL columns is not implemented."
"\n\tAdd the column, then use ALTER TABLE ADD CONSTRAINT.");
rel = heap_openr(RelationRelationName, RowExclusiveLock); rel = heap_openr(RelationRelationName, RowExclusiveLock);
reltup = SearchSysCache(RELNAME, reltup = SearchSysCache(RELNAME,
...@@ -400,22 +408,39 @@ AlterTableAddColumn(const char *relationName, ...@@ -400,22 +408,39 @@ AlterTableAddColumn(const char *relationName,
elog(ERROR, "ALTER TABLE: relation \"%s\" not found", elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
relationName); relationName);
if (SearchSysCacheExists(ATTNAME,
ObjectIdGetDatum(reltup->t_data->t_oid),
PointerGetDatum(colDef->colname),
0, 0))
elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
colDef->colname, relationName);
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts; minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
maxatts = minattnum + 1; maxatts = minattnum + 1;
if (maxatts > MaxHeapAttributeNumber) if (maxatts > MaxHeapAttributeNumber)
elog(ERROR, "ALTER TABLE: relations limited to %d columns", elog(ERROR, "ALTER TABLE: relations limited to %d columns",
MaxHeapAttributeNumber); MaxHeapAttributeNumber);
i = minattnum + 1;
attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock); attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock);
/* if (colDef->typename->arrayBounds)
* Open all (if any) pg_attribute indices {
*/ attndims = length(colDef->typename->arrayBounds);
hasindex = RelationGetForm(attrdesc)->relhasindex; typename = makeArrayTypeName(colDef->typename->name);
if (hasindex) }
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); else
{
attndims = 0;
typename = colDef->typename->name;
}
attributeD.attrelid = reltup->t_data->t_oid; typeTuple = SearchSysCache(TYPENAME,
PointerGetDatum(typename),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "ALTER TABLE: type \"%s\" does not exist", typename);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
attributeTuple = heap_addheader(Natts_pg_attribute, attributeTuple = heap_addheader(Natts_pg_attribute,
ATTRIBUTE_TUPLE_SIZE, ATTRIBUTE_TUPLE_SIZE,
...@@ -423,70 +448,36 @@ AlterTableAddColumn(const char *relationName, ...@@ -423,70 +448,36 @@ AlterTableAddColumn(const char *relationName,
attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple); attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
i = 1 + minattnum; attribute->attrelid = reltup->t_data->t_oid;
namestrcpy(&(attribute->attname), colDef->colname);
attribute->atttypid = typeTuple->t_data->t_oid;
attribute->attstattarget = DEFAULT_ATTSTATTARGET;
attribute->attlen = tform->typlen;
attribute->attcacheoff = -1;
attribute->atttypmod = colDef->typename->typmod;
attribute->attnum = i;
attribute->attbyval = tform->typbyval;
attribute->attndims = attndims;
attribute->attisset = (bool) (tform->typtype == 'c');
attribute->attstorage = tform->typstorage;
attribute->attalign = tform->typalign;
attribute->attnotnull = colDef->is_not_null;
attribute->atthasdef = (colDef->raw_default != NULL ||
colDef->cooked_default != NULL);
ReleaseSysCache(typeTuple);
heap_insert(attrdesc, attributeTuple);
/* Update indexes on pg_attribute */
if (RelationGetForm(attrdesc)->relhasindex)
{ {
HeapTuple typeTuple; Relation idescs[Num_pg_attr_indices];
Form_pg_type tform;
char *typename;
int attndims;
if (SearchSysCacheExists(ATTNAME,
ObjectIdGetDatum(reltup->t_data->t_oid),
PointerGetDatum(colDef->colname),
0, 0))
elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
colDef->colname, relationName);
/* CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
* check to see if it is an array attribute. CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, attributeTuple);
*/
typename = colDef->typename->name;
if (colDef->typename->arrayBounds)
{
attndims = length(colDef->typename->arrayBounds);
typename = makeArrayTypeName(colDef->typename->name);
}
else
attndims = 0;
typeTuple = SearchSysCache(TYPENAME,
PointerGetDatum(typename),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "ALTER TABLE: type \"%s\" does not exist", typename);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
namestrcpy(&(attribute->attname), colDef->colname);
attribute->atttypid = typeTuple->t_data->t_oid;
attribute->attlen = tform->typlen;
attribute->attstattarget = DEFAULT_ATTSTATTARGET;
attribute->attcacheoff = -1;
attribute->atttypmod = colDef->typename->typmod;
attribute->attnum = i;
attribute->attbyval = tform->typbyval;
attribute->attndims = attndims;
attribute->attisset = (bool) (tform->typtype == 'c');
attribute->attstorage = tform->typstorage;
attribute->attalign = tform->typalign;
attribute->attnotnull = false;
attribute->atthasdef = (colDef->raw_default != NULL ||
colDef->cooked_default != NULL);
ReleaseSysCache(typeTuple);
heap_insert(attrdesc, attributeTuple);
if (hasindex)
CatalogIndexInsert(idescs,
Num_pg_attr_indices,
attrdesc,
attributeTuple);
}
if (hasindex)
CatalogCloseIndices(Num_pg_attr_indices, idescs); CatalogCloseIndices(Num_pg_attr_indices, idescs);
}
heap_close(attrdesc, NoLock); heap_close(attrdesc, NoLock);
...@@ -499,20 +490,42 @@ AlterTableAddColumn(const char *relationName, ...@@ -499,20 +490,42 @@ AlterTableAddColumn(const char *relationName,
simple_heap_update(rel, &newreltup->t_self, newreltup); simple_heap_update(rel, &newreltup->t_self, newreltup);
/* keep catalog indices current */ /* keep catalog indices current */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); if (RelationGetForm(rel)->relhasindex)
CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, newreltup); {
CatalogCloseIndices(Num_pg_class_indices, ridescs); Relation ridescs[Num_pg_class_indices];
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, newreltup);
CatalogCloseIndices(Num_pg_class_indices, ridescs);
}
heap_freetuple(newreltup); heap_freetuple(newreltup);
ReleaseSysCache(reltup); ReleaseSysCache(reltup);
heap_close(rel, NoLock); heap_close(rel, NoLock);
/*
* Make our catalog updates visible for subsequent steps.
*/
CommandCounterIncrement();
/*
* Add any CHECK constraints attached to the new column.
*
* To do this we must re-open the rel so that its new attr list
* gets loaded into the relcache.
*/
if (colDef->constraints != NIL)
{
rel = heap_openr(relationName, AccessExclusiveLock);
AddRelationRawConstraints(rel, NIL, colDef->constraints);
heap_close(rel, NoLock);
}
/* /*
* Automatically create the secondary relation for TOAST if it * Automatically create the secondary relation for TOAST if it
* formerly had no such but now has toastable attributes. * formerly had no such but now has toastable attributes.
*/ */
CommandCounterIncrement();
AlterTableCreateToastTable(relationName, true); AlterTableCreateToastTable(relationName, true);
} }
...@@ -596,7 +609,6 @@ AlterTableAlterColumnDefault(const char *relationName, ...@@ -596,7 +609,6 @@ AlterTableAlterColumnDefault(const char *relationName,
if (newDefault) if (newDefault)
{ {
/* SET DEFAULT */ /* SET DEFAULT */
List *rawDefaults = NIL;
RawColumnDefault *rawEnt; RawColumnDefault *rawEnt;
/* Get rid of the old one first */ /* Get rid of the old one first */
...@@ -605,13 +617,12 @@ AlterTableAlterColumnDefault(const char *relationName, ...@@ -605,13 +617,12 @@ AlterTableAlterColumnDefault(const char *relationName,
rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
rawEnt->attnum = attnum; rawEnt->attnum = attnum;
rawEnt->raw_default = newDefault; rawEnt->raw_default = newDefault;
rawDefaults = lappend(rawDefaults, rawEnt);
/* /*
* This function is intended for CREATE TABLE, so it processes a * This function is intended for CREATE TABLE, so it processes a
* _list_ of defaults, but we just do one. * _list_ of defaults, but we just do one.
*/ */
AddRelationRawConstraints(rel, rawDefaults, NIL); AddRelationRawConstraints(rel, makeList1(rawEnt), NIL);
} }
else else
{ {
...@@ -1169,10 +1180,11 @@ AlterTableDropColumn(const char *relationName, ...@@ -1169,10 +1180,11 @@ AlterTableDropColumn(const char *relationName,
*/ */
void void
AlterTableAddConstraint(char *relationName, AlterTableAddConstraint(char *relationName,
bool inh, Node *newConstraint) bool inh, List *newConstraints)
{ {
if (newConstraint == NULL) Relation rel;
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint."); Oid myrelid;
List *listptr;
#ifndef NO_SECURITY #ifndef NO_SECURITY
if (!pg_ownercheck(GetUserId(), relationName, RELNAME)) if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
...@@ -1184,6 +1196,41 @@ AlterTableAddConstraint(char *relationName, ...@@ -1184,6 +1196,41 @@ AlterTableAddConstraint(char *relationName,
elog(ERROR, "ALTER TABLE ADD CONSTRAINT: %s is not a table", elog(ERROR, "ALTER TABLE ADD CONSTRAINT: %s is not a table",
relationName); relationName);
rel = heap_openr(relationName, AccessExclusiveLock);
myrelid = RelationGetRelid(rel);
if (inh) {
List *child,
*children;
/* this routine is actually in the planner */
children = find_all_inheritors(myrelid);
/*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all
* of the relids in the list that it returns.
*/
foreach(child, children)
{
Oid childrelid = lfirsti(child);
char *childrelname;
Relation childrel;
if (childrelid == myrelid)
continue;
childrel = heap_open(childrelid, AccessExclusiveLock);
childrelname = pstrdup(RelationGetRelationName(childrel));
heap_close(childrel, AccessExclusiveLock);
AlterTableAddConstraint(childrelname, false, newConstraints);
pfree(childrelname);
}
}
foreach(listptr, newConstraints)
{
Node *newConstraint = lfirst(listptr);
switch (nodeTag(newConstraint)) switch (nodeTag(newConstraint))
{ {
case T_Constraint: case T_Constraint:
...@@ -1202,33 +1249,14 @@ AlterTableAddConstraint(char *relationName, ...@@ -1202,33 +1249,14 @@ AlterTableAddConstraint(char *relationName,
HeapTuple tuple; HeapTuple tuple;
RangeTblEntry *rte; RangeTblEntry *rte;
List *qual; List *qual;
List *constlist;
Relation rel;
Node *expr; Node *expr;
char *name; char *name;
Oid myrelid;
if (constr->name) if (constr->name)
name = constr->name; name = constr->name;
else else
name = "<unnamed>"; name = "<unnamed>";
constlist = makeList1(constr);
rel = heap_openr(relationName, AccessExclusiveLock);
myrelid = RelationGetRelid(rel);
/* make sure it is not a view */
if (rel->rd_rel->relkind == RELKIND_VIEW)
elog(ERROR, "ALTER TABLE: cannot add constraint to a view");
/*
* Scan all of the rows, looking for a false
* match
*/
scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
AssertState(scan != NULL);
/* /*
* We need to make a parse state and range * We need to make a parse state and range
* table to allow us to transformExpr and * table to allow us to transformExpr and
...@@ -1280,6 +1308,8 @@ AlterTableAddConstraint(char *relationName, ...@@ -1280,6 +1308,8 @@ AlterTableAddConstraint(char *relationName,
* Scan through the rows now, checking the * Scan through the rows now, checking the
* expression at each row. * expression at each row.
*/ */
scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{ {
ExecStoreTuple(tuple, slot, InvalidBuffer, false); ExecStoreTuple(tuple, slot, InvalidBuffer, false);
...@@ -1291,11 +1321,11 @@ AlterTableAddConstraint(char *relationName, ...@@ -1291,11 +1321,11 @@ AlterTableAddConstraint(char *relationName,
ResetExprContext(econtext); ResetExprContext(econtext);
} }
heap_endscan(scan);
FreeExprContext(econtext); FreeExprContext(econtext);
pfree(slot); pfree(slot);
heap_endscan(scan);
if (!successful) if (!successful)
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name); elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
...@@ -1306,38 +1336,8 @@ AlterTableAddConstraint(char *relationName, ...@@ -1306,38 +1336,8 @@ AlterTableAddConstraint(char *relationName,
* the constraint against tuples already in * the constraint against tuples already in
* the table. * the table.
*/ */
AddRelationRawConstraints(rel, NIL, constlist); AddRelationRawConstraints(rel, NIL,
heap_close(rel, NoLock); makeList1(constr));
if (inh) {
List *child,
*children;
/* this routine is actually in the planner */
children = find_all_inheritors(myrelid);
/*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all
* of the relids in the list that it returns.
*/
foreach(child, children)
{
Oid childrelid = lfirsti(child);
char *childrelname;
if (childrelid == myrelid)
continue;
rel = heap_open(childrelid, AccessExclusiveLock);
childrelname = pstrdup(RelationGetRelationName(rel));
heap_close(rel, AccessExclusiveLock);
AlterTableAddConstraint(childrelname, false, newConstraint);
pfree(childrelname);
}
}
pfree(constlist);
break; break;
} }
...@@ -1345,7 +1345,6 @@ AlterTableAddConstraint(char *relationName, ...@@ -1345,7 +1345,6 @@ AlterTableAddConstraint(char *relationName,
{ {
char *iname = constr->name; char *iname = constr->name;
bool istemp = is_temp_rel_name(relationName); bool istemp = is_temp_rel_name(relationName);
Relation rel;
List *indexoidlist; List *indexoidlist;
List *indexoidscan; List *indexoidscan;
Form_pg_attribute *rel_attrs; Form_pg_attribute *rel_attrs;
...@@ -1389,7 +1388,6 @@ AlterTableAddConstraint(char *relationName, ...@@ -1389,7 +1388,6 @@ AlterTableAddConstraint(char *relationName,
} }
/* Need to check for unique key already on field(s) */ /* Need to check for unique key already on field(s) */
rel = heap_openr(relationName, AccessExclusiveLock);
/* /*
* First we check for limited correctness of the * First we check for limited correctness of the
...@@ -1490,9 +1488,6 @@ AlterTableAddConstraint(char *relationName, ...@@ -1490,9 +1488,6 @@ AlterTableAddConstraint(char *relationName,
elog(NOTICE, "Unique constraint supercedes existing index on relation \"%s\". Drop the existing index to remove redundancy.", relationName); elog(NOTICE, "Unique constraint supercedes existing index on relation \"%s\". Drop the existing index to remove redundancy.", relationName);
pfree(iname); pfree(iname);
/* Finally, close relation */
heap_close(rel, NoLock);
break; break;
} }
default: default:
...@@ -1503,8 +1498,7 @@ AlterTableAddConstraint(char *relationName, ...@@ -1503,8 +1498,7 @@ AlterTableAddConstraint(char *relationName,
case T_FkConstraint: case T_FkConstraint:
{ {
FkConstraint *fkconstraint = (FkConstraint *) newConstraint; FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
Relation rel, Relation pkrel;
pkrel;
HeapScanDesc scan; HeapScanDesc scan;
HeapTuple tuple; HeapTuple tuple;
Trigger trig; Trigger trig;
...@@ -1538,16 +1532,11 @@ AlterTableAddConstraint(char *relationName, ...@@ -1538,16 +1532,11 @@ AlterTableAddConstraint(char *relationName,
fkconstraint->pktable_name); fkconstraint->pktable_name);
/* /*
* Grab an exclusive lock on the fk table, and then scan * Scan through each tuple, calling the RI_FKey_Match_Ins
* through each tuple, calling the RI_FKey_Match_Ins
* (insert trigger) as if that tuple had just been * (insert trigger) as if that tuple had just been
* inserted. If any of those fail, it should elog(ERROR) * inserted. If any of those fail, it should elog(ERROR)
* and that's that. * and that's that.
*/ */
rel = heap_openr(relationName, AccessExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "referencing table \"%s\" not a relation",
relationName);
/* /*
* First we check for limited correctness of the * First we check for limited correctness of the
...@@ -1741,8 +1730,6 @@ AlterTableAddConstraint(char *relationName, ...@@ -1741,8 +1730,6 @@ AlterTableAddConstraint(char *relationName,
RI_FKey_check_ins(&fcinfo); RI_FKey_check_ins(&fcinfo);
} }
heap_endscan(scan); heap_endscan(scan);
heap_close(rel, NoLock); /* close rel but keep
* lock! */
pfree(trig.tgargs); pfree(trig.tgargs);
break; break;
...@@ -1750,6 +1737,10 @@ AlterTableAddConstraint(char *relationName, ...@@ -1750,6 +1737,10 @@ AlterTableAddConstraint(char *relationName,
default: default:
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed"); elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
} }
}
/* Close rel, but keep lock till commit */
heap_close(rel, NoLock);
} }
......
此差异已折叠。
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.119 2001/10/09 14:00:22 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.120 2001/10/12 00:07:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -450,7 +450,7 @@ ProcessUtility(Node *parsetree, ...@@ -450,7 +450,7 @@ ProcessUtility(Node *parsetree,
case 'C': /* ADD CONSTRAINT */ case 'C': /* ADD CONSTRAINT */
AlterTableAddConstraint(stmt->relname, AlterTableAddConstraint(stmt->relname,
interpretInhOption(stmt->inhOpt), interpretInhOption(stmt->inhOpt),
stmt->def); (List *) stmt->def);
break; break;
case 'X': /* DROP CONSTRAINT */ case 'X': /* DROP CONSTRAINT */
AlterTableDropConstraint(stmt->relname, AlterTableDropConstraint(stmt->relname,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: command.h,v 1.27 2001/05/07 00:43:25 tgl Exp $ * $Id: command.h,v 1.28 2001/10/12 00:07:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -56,7 +56,7 @@ extern void AlterTableDropColumn(const char *relationName, ...@@ -56,7 +56,7 @@ extern void AlterTableDropColumn(const char *relationName,
int behavior); int behavior);
extern void AlterTableAddConstraint(char *relationName, extern void AlterTableAddConstraint(char *relationName,
bool inh, Node *newConstraint); bool inh, List *newConstraints);
extern void AlterTableDropConstraint(const char *relationName, extern void AlterTableDropConstraint(const char *relationName,
bool inh, const char *constrName, bool inh, const char *constrName,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsenodes.h,v 1.145 2001/10/02 21:39:36 tgl Exp $ * $Id: parsenodes.h,v 1.146 2001/10/12 00:07:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -187,6 +187,12 @@ typedef struct CopyStmt ...@@ -187,6 +187,12 @@ typedef struct CopyStmt
/* ---------------------- /* ----------------------
* Create Table Statement * Create Table Statement
*
* NOTE: in the raw gram.y output, ColumnDef, Constraint, and FkConstraint
* nodes are intermixed in tableElts, and constraints is NIL. After parse
* analysis, tableElts contains just ColumnDefs, and constraints contains
* just Constraint nodes (in fact, only CONSTR_CHECK nodes, in the present
* implementation).
* ---------------------- * ----------------------
*/ */
typedef struct CreateStmt typedef struct CreateStmt
...@@ -196,8 +202,7 @@ typedef struct CreateStmt ...@@ -196,8 +202,7 @@ typedef struct CreateStmt
List *tableElts; /* column definitions (list of ColumnDef) */ List *tableElts; /* column definitions (list of ColumnDef) */
List *inhRelnames; /* relations to inherit from (list of List *inhRelnames; /* relations to inherit from (list of
* T_String Values) */ * T_String Values) */
List *constraints; /* constraints (list of Constraint and List *constraints; /* constraints (list of Constraint nodes) */
* FkConstraint nodes) */
bool istemp; /* is this a temp table? */ bool istemp; /* is this a temp table? */
bool hasoids; /* should it have OIDs? */ bool hasoids; /* should it have OIDs? */
} CreateStmt; } CreateStmt;
......
...@@ -287,25 +287,25 @@ INSERT INTO tmp3 values (1,20); ...@@ -287,25 +287,25 @@ INSERT INTO tmp3 values (1,20);
INSERT INTO tmp3 values (5,50); INSERT INTO tmp3 values (5,50);
-- Try (and fail) to add constraint due to invalid source columns -- Try (and fail) to add constraint due to invalid source columns
ALTER TABLE tmp3 add constraint tmpconstr foreign key(c) references tmp2 match full; ALTER TABLE tmp3 add constraint tmpconstr foreign key(c) references tmp2 match full;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: columns referenced in foreign key constraint not found. ERROR: ALTER TABLE: column "c" referenced in foreign key constraint does not exist
-- Try (and fail) to add constraint due to invalide destination columns explicitly given -- Try (and fail) to add constraint due to invalide destination columns explicitly given
ALTER TABLE tmp3 add constraint tmpconstr foreign key(a) references tmp2(b) match full; ALTER TABLE tmp3 add constraint tmpconstr foreign key(a) references tmp2(b) match full;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: UNIQUE constraint matching given keys for referenced table "tmp2" not found ERROR: UNIQUE constraint matching given keys for referenced table "tmp2" not found
-- Try (and fail) to add constraint due to invalid data -- Try (and fail) to add constraint due to invalid data
ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full; ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: tmpconstr referential integrity violation - key referenced from tmp3 not found in tmp2 ERROR: tmpconstr referential integrity violation - key referenced from tmp3 not found in tmp2
-- Delete failing row -- Delete failing row
DELETE FROM tmp3 where a=5; DELETE FROM tmp3 where a=5;
-- Try (and succeed) -- Try (and succeed)
ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full; ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-- Try (and fail) to create constraint from tmp5(a) to tmp4(a) - unique constraint on -- Try (and fail) to create constraint from tmp5(a) to tmp4(a) - unique constraint on
-- tmp4 is a,b -- tmp4 is a,b
ALTER TABLE tmp5 add constraint tmpconstr foreign key(a) references tmp4(a) match full; ALTER TABLE tmp5 add constraint tmpconstr foreign key(a) references tmp4(a) match full;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not found ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not found
DROP TABLE tmp5; DROP TABLE tmp5;
DROP TABLE tmp4; DROP TABLE tmp4;
...@@ -321,13 +321,13 @@ NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for ...@@ -321,13 +321,13 @@ NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for
CREATE TEMP TABLE FKTABLE (ftest1 text); CREATE TEMP TABLE FKTABLE (ftest1 text);
-- This next should fail, because text=int does not exist -- This next should fail, because text=int does not exist
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'text' and 'integer' ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
You will have to retype this query using an explicit cast You will have to retype this query using an explicit cast
-- This should also fail for the same reason, but here we -- This should also fail for the same reason, but here we
-- give the column name -- give the column name
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'text' and 'integer' ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
You will have to retype this query using an explicit cast You will have to retype this query using an explicit cast
-- This should succeed, even though they are different types -- This should succeed, even though they are different types
...@@ -335,10 +335,10 @@ ERROR: Unable to identify an operator '=' for types 'text' and 'integer' ...@@ -335,10 +335,10 @@ ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
DROP TABLE FKTABLE; DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 varchar); CREATE TEMP TABLE FKTABLE (ftest1 varchar);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-- As should this -- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE pktable; DROP TABLE pktable;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
...@@ -349,7 +349,7 @@ NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for ...@@ -349,7 +349,7 @@ NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for
-- This should fail, because we just chose really odd types -- This should fail, because we just chose really odd types
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 datetime); CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 datetime);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer' ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast You will have to retype this query using an explicit cast
-- Again, so should this... -- Again, so should this...
...@@ -357,7 +357,7 @@ DROP TABLE FKTABLE; ...@@ -357,7 +357,7 @@ DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 datetime); CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 datetime);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2); references pktable(ptest1, ptest2);
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer' ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast You will have to retype this query using an explicit cast
-- This fails because we mixed up the column ordering -- This fails because we mixed up the column ordering
...@@ -365,13 +365,13 @@ DROP TABLE FKTABLE; ...@@ -365,13 +365,13 @@ DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 text); CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 text);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1); references pktable(ptest2, ptest1);
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'integer' and 'text' ERROR: Unable to identify an operator '=' for types 'integer' and 'text'
You will have to retype this query using an explicit cast You will have to retype this query using an explicit cast
-- As does this... -- As does this...
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1) ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1)
references pktable(ptest1, ptest2); references pktable(ptest1, ptest2);
NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'text' and 'integer' ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
You will have to retype this query using an explicit cast You will have to retype this query using an explicit cast
-- temp tables should go away by themselves, need not drop them. -- temp tables should go away by themselves, need not drop them.
......
...@@ -694,7 +694,7 @@ CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); ...@@ -694,7 +694,7 @@ CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE); CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE);
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: columns referenced in foreign key constraint not found. ERROR: CREATE TABLE: column "ftest2" referenced in foreign key constraint does not exist
CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2)); CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: UNIQUE constraint matching given keys for referenced table "pktable" not found ERROR: UNIQUE constraint matching given keys for referenced table "pktable" not found
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册