|
|
|
@ -8,7 +8,7 @@ |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.171 2002/04/01 22:36:09 tgl Exp $ |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.172 2002/04/02 08:51:50 inoue Exp $ |
|
|
|
|
* |
|
|
|
|
* NOTES |
|
|
|
|
* The PerformAddAttribute() code, like most of the relation |
|
|
|
@ -1115,140 +1115,6 @@ AlterTableAlterColumnFlags(Oid myrelid, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
/*
|
|
|
|
|
* ALTER TABLE DROP COLUMN trial implementation |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* find a specified attribute in a node entry |
|
|
|
|
*/ |
|
|
|
|
static bool |
|
|
|
|
find_attribute_walker(Node *node, int *attnump) |
|
|
|
|
{ |
|
|
|
|
if (node == NULL) |
|
|
|
|
return false; |
|
|
|
|
if (IsA(node, Var)) |
|
|
|
|
{ |
|
|
|
|
Var *var = (Var *) node; |
|
|
|
|
|
|
|
|
|
if (var->varlevelsup == 0 && var->varno == 1 && |
|
|
|
|
var->varattno == *attnump) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
return expression_tree_walker(node, find_attribute_walker, |
|
|
|
|
(void *) attnump); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static bool |
|
|
|
|
find_attribute_in_node(Node *node, int attnum) |
|
|
|
|
{ |
|
|
|
|
return find_attribute_walker(node, &attnum); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove/check references for the column |
|
|
|
|
*/ |
|
|
|
|
static bool |
|
|
|
|
RemoveColumnReferences(Oid reloid, int attnum, bool checkonly, HeapTuple reltup) |
|
|
|
|
{ |
|
|
|
|
Relation indexRelation, |
|
|
|
|
rcrel; |
|
|
|
|
ScanKeyData entry; |
|
|
|
|
HeapScanDesc scan; |
|
|
|
|
void *sysscan; |
|
|
|
|
HeapTuple htup, |
|
|
|
|
indexTuple; |
|
|
|
|
Form_pg_index index; |
|
|
|
|
Form_pg_class pgcform = (Form_pg_class) NULL; |
|
|
|
|
int i; |
|
|
|
|
bool checkok = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!checkonly) |
|
|
|
|
pgcform = (Form_pg_class) GETSTRUCT(reltup); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove/check constraints here |
|
|
|
|
*/ |
|
|
|
|
ScanKeyEntryInitialize(&entry, (bits16) 0x0, |
|
|
|
|
Anum_pg_relcheck_rcrelid, |
|
|
|
|
(RegProcedure) F_OIDEQ, |
|
|
|
|
ObjectIdGetDatum(reloid)); |
|
|
|
|
|
|
|
|
|
rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock); |
|
|
|
|
sysscan = systable_beginscan(rcrel, RelCheckIndex, true, |
|
|
|
|
SnapshotNow, |
|
|
|
|
1, &entry); |
|
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(htup = systable_getnext(sysscan))) |
|
|
|
|
{ |
|
|
|
|
Form_pg_relcheck relcheck; |
|
|
|
|
char *ccbin; |
|
|
|
|
Node *node; |
|
|
|
|
|
|
|
|
|
relcheck = (Form_pg_relcheck) GETSTRUCT(htup); |
|
|
|
|
ccbin = DatumGetCString(DirectFunctionCall1(textout, |
|
|
|
|
PointerGetDatum(&relcheck->rcbin))); |
|
|
|
|
node = stringToNode(ccbin); |
|
|
|
|
pfree(ccbin); |
|
|
|
|
if (find_attribute_in_node(node, attnum)) |
|
|
|
|
{ |
|
|
|
|
if (checkonly) |
|
|
|
|
{ |
|
|
|
|
checkok = false; |
|
|
|
|
elog(ERROR, "target column is used in a constraint"); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
simple_heap_delete(rcrel, &htup->t_self); |
|
|
|
|
pgcform->relchecks--; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
systable_endscan(sysscan); |
|
|
|
|
heap_close(rcrel, NoLock); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* What to do with triggers/rules/views/procedues ? |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove/check indexes |
|
|
|
|
*/ |
|
|
|
|
indexRelation = heap_openr(IndexRelationName, RowExclusiveLock); |
|
|
|
|
ScanKeyEntryInitialize(&entry, 0, Anum_pg_index_indrelid, F_OIDEQ, |
|
|
|
|
ObjectIdGetDatum(reloid)); |
|
|
|
|
scan = heap_beginscan(indexRelation, false, SnapshotNow, 1, &entry); |
|
|
|
|
while (HeapTupleIsValid(indexTuple = heap_getnext(scan, 0))) |
|
|
|
|
{ |
|
|
|
|
index = (Form_pg_index) GETSTRUCT(indexTuple); |
|
|
|
|
for (i = 0; i < INDEX_MAX_KEYS; i++) |
|
|
|
|
{ |
|
|
|
|
if (index->indkey[i] == InvalidAttrNumber) |
|
|
|
|
break; |
|
|
|
|
else if (index->indkey[i] == attnum) |
|
|
|
|
{ |
|
|
|
|
if (checkonly) |
|
|
|
|
{ |
|
|
|
|
checkok = false; |
|
|
|
|
elog(ERROR, "target column is used in an index"); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
index_drop(index->indexrelid); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
heap_endscan(scan); |
|
|
|
|
heap_close(indexRelation, NoLock); |
|
|
|
|
|
|
|
|
|
return checkok; |
|
|
|
|
} |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ALTER TABLE DROP COLUMN |
|
|
|
@ -1258,171 +1124,7 @@ AlterTableDropColumn(Oid myrelid, |
|
|
|
|
bool inh, const char *colName, |
|
|
|
|
int behavior) |
|
|
|
|
{ |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
Relation rel, |
|
|
|
|
attrdesc; |
|
|
|
|
HeapTuple reltup; |
|
|
|
|
HeapTupleData classtuple; |
|
|
|
|
Buffer buffer; |
|
|
|
|
Form_pg_attribute attribute; |
|
|
|
|
HeapTuple tup; |
|
|
|
|
Relation idescs[Num_pg_attr_indices]; |
|
|
|
|
int attnum; |
|
|
|
|
bool hasindex; |
|
|
|
|
char dropColname[32]; |
|
|
|
|
|
|
|
|
|
if (inh) |
|
|
|
|
elog(ERROR, "ALTER TABLE / DROP COLUMN with inherit option is not supported yet"); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Grab an exclusive lock on the target table, which we will NOT |
|
|
|
|
* release until end of transaction. |
|
|
|
|
*/ |
|
|
|
|
rel = heap_open(myrelid, AccessExclusiveLock); |
|
|
|
|
|
|
|
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION) |
|
|
|
|
elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table", |
|
|
|
|
RelationGetRelationName(rel)); |
|
|
|
|
|
|
|
|
|
if (!allowSystemTableMods |
|
|
|
|
&& IsSystemRelationName(RelationGetRelationName(rel))) |
|
|
|
|
elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", |
|
|
|
|
RelationGetRelationName(rel)); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* permissions checking. this would normally be done in utility.c, |
|
|
|
|
* but this particular routine is recursive. |
|
|
|
|
* |
|
|
|
|
* normally, only the owner of a class can change its schema. |
|
|
|
|
*/ |
|
|
|
|
if (!pg_class_ownercheck(myrelid, GetUserId())) |
|
|
|
|
elog(ERROR, "ALTER TABLE: \"%s\": permission denied", |
|
|
|
|
RelationGetRelationName(rel)); |
|
|
|
|
|
|
|
|
|
heap_close(rel, NoLock); /* close rel but keep lock! */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* What to do when rel has inheritors ? |
|
|
|
|
*/ |
|
|
|
|
if (length(find_all_inheritors(myrelid)) > 1) |
|
|
|
|
elog(ERROR, "ALTER TABLE: cannot drop a column on table that is inherited from"); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* lock the pg_class tuple for update |
|
|
|
|
*/ |
|
|
|
|
rel = heap_openr(RelationRelationName, RowExclusiveLock); |
|
|
|
|
reltup = SearchSysCache(RELOID, |
|
|
|
|
ObjectIdGetDatum(myrelid), |
|
|
|
|
0, 0, 0); |
|
|
|
|
if (!HeapTupleIsValid(reltup)) |
|
|
|
|
{ |
|
|
|
|
Relation myrel; |
|
|
|
|
char *myrelname; |
|
|
|
|
|
|
|
|
|
myrel = heap_open(myrelid, AccessExclusiveLock); |
|
|
|
|
myrelname = pstrdup(RelationGetRelationName(myrel)); |
|
|
|
|
heap_close(myrel, AccessExclusiveLock); |
|
|
|
|
|
|
|
|
|
elog(ERROR, "ALTER TABLE: relation \"%s\" not found", |
|
|
|
|
myrelname); |
|
|
|
|
} |
|
|
|
|
classtuple.t_self = reltup->t_self; |
|
|
|
|
ReleaseSysCache(reltup); |
|
|
|
|
|
|
|
|
|
switch (heap_mark4update(rel, &classtuple, &buffer)) |
|
|
|
|
{ |
|
|
|
|
case HeapTupleSelfUpdated: |
|
|
|
|
case HeapTupleMayBeUpdated: |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
elog(ERROR, "couldn't lock pg_class tuple"); |
|
|
|
|
} |
|
|
|
|
reltup = heap_copytuple(&classtuple); |
|
|
|
|
ReleaseBuffer(buffer); |
|
|
|
|
|
|
|
|
|
attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get the target pg_attribute tuple and make a modifiable copy |
|
|
|
|
*/ |
|
|
|
|
tup = SearchSysCacheCopy(ATTNAME, |
|
|
|
|
ObjectIdGetDatum(myrelid), |
|
|
|
|
PointerGetDatum(colName), |
|
|
|
|
0, 0); |
|
|
|
|
if (!HeapTupleIsValid(tup)) |
|
|
|
|
{ |
|
|
|
|
Relation myrel; |
|
|
|
|
char *myrelname; |
|
|
|
|
|
|
|
|
|
myrel = heap_open(myrelid, AccessExclusiveLock); |
|
|
|
|
myrelname = pstrdup(RelationGetRelationName(myrel)); |
|
|
|
|
heap_close(myrel, AccessExclusiveLock); |
|
|
|
|
|
|
|
|
|
elog(ERROR, "ALTER TABLE: column name \"%s\" doesn't exist in table \"%s\"", |
|
|
|
|
colName, myrelname); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
attribute = (Form_pg_attribute) GETSTRUCT(tup); |
|
|
|
|
attnum = attribute->attnum; |
|
|
|
|
if (attnum <= 0) |
|
|
|
|
elog(ERROR, "ALTER TABLE: column name \"%s\" was already dropped", |
|
|
|
|
colName); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check constraints/indices etc here |
|
|
|
|
*/ |
|
|
|
|
if (behavior != CASCADE) |
|
|
|
|
{ |
|
|
|
|
if (!RemoveColumnReferences(myrelid, attnum, true, NULL)) |
|
|
|
|
elog(ERROR, "the column is referenced"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* change the target pg_attribute tuple |
|
|
|
|
*/ |
|
|
|
|
sprintf(dropColname, "*already Dropped*%d", attnum); |
|
|
|
|
namestrcpy(&(attribute->attname), dropColname); |
|
|
|
|
ATTRIBUTE_DROP_COLUMN(attribute); |
|
|
|
|
|
|
|
|
|
simple_heap_update(attrdesc, &tup->t_self, tup); |
|
|
|
|
hasindex = (!IsIgnoringSystemIndexes() && RelationGetForm(attrdesc)->relhasindex); |
|
|
|
|
if (hasindex) |
|
|
|
|
{ |
|
|
|
|
CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs); |
|
|
|
|
CatalogIndexInsert(idescs, Num_pg_attr_indices, |
|
|
|
|
attrdesc, tup); |
|
|
|
|
CatalogCloseIndices(Num_pg_attr_indices, idescs); |
|
|
|
|
} |
|
|
|
|
heap_close(attrdesc, NoLock); |
|
|
|
|
heap_freetuple(tup); |
|
|
|
|
|
|
|
|
|
/* delete comment for this attribute only */ |
|
|
|
|
CreateComments(RelationGetRelid(rel), RelOid_pg_class, |
|
|
|
|
(int32) attnum, NULL); |
|
|
|
|
|
|
|
|
|
/* delete attrdef */ |
|
|
|
|
drop_default(myrelid, attnum); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove objects which reference this column |
|
|
|
|
*/ |
|
|
|
|
if (behavior == CASCADE) |
|
|
|
|
{ |
|
|
|
|
Relation ridescs[Num_pg_class_indices]; |
|
|
|
|
|
|
|
|
|
RemoveColumnReferences(myrelid, attnum, false, reltup); |
|
|
|
|
/* update pg_class tuple */ |
|
|
|
|
simple_heap_update(rel, &reltup->t_self, reltup); |
|
|
|
|
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); |
|
|
|
|
CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, reltup); |
|
|
|
|
CatalogCloseIndices(Num_pg_class_indices, ridescs); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
heap_freetuple(reltup); |
|
|
|
|
heap_close(rel, NoLock); |
|
|
|
|
#else |
|
|
|
|
elog(ERROR, "ALTER TABLE / DROP COLUMN is not implemented"); |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|