diff options
Diffstat (limited to 'src/backend/commands/command.c')
-rw-r--r-- | src/backend/commands/command.c | 381 |
1 files changed, 380 insertions, 1 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 620c601b17d..b75f3840a87 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.69 2000/02/15 18:15:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.70 2000/03/09 05:00:23 inoue Exp $ * * NOTES * The PortalExecutorHeapMemory crap needs to be eliminated @@ -43,6 +43,15 @@ #include "utils/syscache.h" #include "utils/temprel.h" #include "commands/trigger.h" +#ifdef _DROP_COLUMN_HACK__ +#include "catalog/pg_index.h" +#include "catalog/pg_relcheck.h" +#include "commands/defrem.h" +#include "commands/comment.h" +#include "access/genam.h" +#include "optimizer/clauses.h" +#include "../parser/parse.h" +#endif /* _DROP_COLUMN_HACK__ */ /* ---------------- * PortalExecutorHeapMemory stuff @@ -668,6 +677,213 @@ drop_default(Oid relid, int16 attnum) } +#ifdef _DROP_COLUMN_HACK__ +/* + * ALTER TABLE DROP COLUMN trial implementation + * + */ + +/* + * system table scan(index scan/sequential scan) + */ +typedef struct SysScanDescData +{ + Relation heap_rel; + Relation irel; + HeapScanDesc scan; + IndexScanDesc iscan; + HeapTupleData tuple; + Buffer buffer; +} SysScanDescData, *SysScanDesc; + +static void * +systable_beginscan(Relation rel, const char *indexRelname, int nkeys, ScanKey entry) +{ + bool hasindex = (rel->rd_rel->relhasindex && !IsIgnoringSystemIndexes()); + SysScanDesc sysscan; + + sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData)); + sysscan->heap_rel = rel; + sysscan->irel = (Relation) NULL; + sysscan->tuple.t_datamcxt = NULL; + sysscan->tuple.t_data = NULL; + sysscan->buffer = InvalidBuffer; + if (hasindex) + { + sysscan->irel = index_openr((char *)indexRelname); + sysscan->iscan = index_beginscan(sysscan->irel, false, nkeys, entry); + } + else + sysscan->scan = heap_beginscan(rel, false, SnapshotNow, nkeys, entry); + return (void *) sysscan; +} +static void +systable_endscan(void *scan) +{ + SysScanDesc sysscan = (SysScanDesc) scan; + + if (sysscan->irel) + { + if (BufferIsValid(sysscan->buffer)) + ReleaseBuffer(sysscan->buffer); + index_endscan(sysscan->iscan); + index_close(sysscan->irel); + } + else + heap_endscan(sysscan->scan); + pfree(scan); +} +static HeapTuple +systable_getnext(void *scan) +{ + SysScanDesc sysscan = (SysScanDesc) scan; + HeapTuple htup = (HeapTuple) NULL; + RetrieveIndexResult indexRes; + + if (sysscan->irel) + { + if (BufferIsValid(sysscan->buffer)) + { + ReleaseBuffer(sysscan->buffer); + sysscan->buffer = InvalidBuffer; + } + while (indexRes = index_getnext(sysscan->iscan, ForwardScanDirection), indexRes != NULL) + { + sysscan->tuple.t_self = indexRes->heap_iptr; + heap_fetch(sysscan->heap_rel, SnapshotNow, &sysscan->tuple, &(sysscan->buffer)); + pfree(indexRes); + if (sysscan->tuple.t_data != NULL) + { + htup = &sysscan->tuple; + break; + } + } + } + else + htup = heap_getnext(sysscan->scan, 0); + return htup; +} + +/* + * find a specified attribute in a node entry + */ +static bool +find_attribute_walker(Node *node, int attnum) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + if (var->varlevelsup == 0 && var->varno == 1 && + var->varattno == attnum) + return true; + } + return expression_tree_walker(node, find_attribute_walker, (void *)attnum); +} +static bool +find_attribute_in_node(Node *node, int attnum) +{ + return expression_tree_walker(node, find_attribute_walker, (void *)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_relcheck relcheck; + 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,1 ,&entry); + + while (HeapTupleIsValid(htup = systable_getnext(sysscan))) + { + char *ccbin; + Node *node; + + relcheck = (Form_pg_relcheck) GETSTRUCT(htup); + ccbin = textout(&relcheck->rcbin); + if (!ccbin) + continue; + 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 + { + heap_delete(rcrel, &htup->t_self, NULL); + 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 + { + htup = SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(index->indexrelid), + 0, 0, 0); + RemoveIndex(NameStr(((Form_pg_class) GETSTRUCT(htup))->relname)); + } + break; + } + } + } + heap_endscan(scan); + heap_close(indexRelation, NoLock); + + return checkok; +} +#endif /* _DROP_COLUMN_HACK__ */ /* * ALTER TABLE DROP COLUMN @@ -677,7 +893,170 @@ AlterTableDropColumn(const char *relationName, bool inh, const char *colName, int behavior) { +#ifdef _DROP_COLUMN_HACK__ + Relation rel, attrdesc, adrel; + Oid myrelid, attoid; + 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]; + void *sysscan; + ScanKeyData scankeys[2]; + + if (inh) + elog(ERROR, "ALTER TABLE / DROP COLUMN with inherit option is not supported yet"); + /* + * 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 (!allowSystemTableMods && IsSystemRelationName(relationName)) + elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog", + relationName); +#ifndef NO_SECURITY + if (!pg_ownercheck(UserName, relationName, RELNAME)) + elog(ERROR, "ALTER TABLE: permission denied"); +#endif + + /* + * Grab an exclusive lock on the target table, which we will NOT release + * until end of transaction. + */ + rel = heap_openr(relationName, AccessExclusiveLock); + myrelid = RelationGetRelid(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 + */ + reltup = SearchSysCacheTuple(RELNAME, PointerGetDatum(relationName), + 0, 0, 0); + + if (!HeapTupleIsValid(reltup)) + elog(ERROR, "ALTER TABLE: relation \"%s\" not found", + relationName); + rel = heap_openr(RelationRelationName, RowExclusiveLock); + classtuple.t_self = reltup->t_self; + 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); + + /* + * XXX is the following check sufficient? + */ + if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION) + { + elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table", + relationName); + } + + attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock); + + /* + * Get the target pg_attribute tuple + */ + tup = SearchSysCacheTupleCopy(ATTNAME, + ObjectIdGetDatum(reltup->t_data->t_oid), + PointerGetDatum(colName), 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "ALTER TABLE: column name \"%s\" doesn't exist in table \"%s\"", + colName, relationName); + + attribute = (Form_pg_attribute) GETSTRUCT(tup); + if (attribute->attnum <= 0) + elog(ERROR, "ALTER TABLE: column name \"%s\" was already dropped", colName); + attnum = attribute->attnum; + attoid = tup->t_data->t_oid; + /* + * 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); + + heap_update(attrdesc, &tup->t_self, tup, NULL); + 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 comments */ + DeleteComments(attoid); + /* delete attrdef */ + adrel = heap_openr(AttrDefaultRelationName, RowExclusiveLock); + ScanKeyEntryInitialize(&scankeys[0], 0x0, Anum_pg_attrdef_adrelid, + F_OIDEQ, ObjectIdGetDatum(myrelid)); + /* Oops pg_attrdef doesn't have (adrelid,adnum) index + ScanKeyEntryInitialize(&scankeys[1], 0x0, Anum_pg_attrdef_adnum, + F_INT2EQ, Int16GetDatum(attnum)); + sysscan = systable_beginscan(adrel, AttrDefaultIndex, 2, scankeys); + */ + sysscan = systable_beginscan(adrel, AttrDefaultIndex, 1, scankeys); + while (HeapTupleIsValid(tup = systable_getnext(sysscan))) + { + if (((Form_pg_attrdef) GETSTRUCT(tup))->adnum == attnum) + { + heap_delete(adrel, &tup->t_self, NULL); + break; + } + } + systable_endscan(sysscan); + heap_close(adrel, NoLock); + /* + * Remove objects which reference this column + */ + if (behavior == CASCADE) + { + Relation ridescs[Num_pg_class_indices]; + + RemoveColumnReferences(myrelid, attnum, false, reltup); + /* update pg_class tuple */ + heap_update(rel, &reltup->t_self, reltup, NULL); + 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__ */ } |