*
*
* 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
#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
}
+#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
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__ */
}