Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2002-04-15 05:22:04 +0000
committerTom Lane2002-04-15 05:22:04 +0000
commit71dc300a375f4932693b064b161a400448c9bc2e (patch)
treeab20c095125a6eab33644befd187f936cf4fd093 /src/backend/commands/command.c
parentab1ead6b9761531787c78e0c80daf98b421ac06f (diff)
The contents of command.c, creatinh.c, define.c, remove.c and rename.c
have been divided according to the type of object manipulated - so ALTER TABLE code is in tablecmds.c, aggregate commands in aggregatecmds.c and so on. A few common support routines remain in define.c (prototypes in src/include/commands/defrem.h). No code has been changed except for includes to reflect the new files. The prototypes for aggregatecmds.c, functioncmds.c, operatorcmds.c, and typecmds.c remain in src/include/commands/defrem.h. From John Gray <jgray@azuli.co.uk>
Diffstat (limited to 'src/backend/commands/command.c')
-rw-r--r--src/backend/commands/command.c1997
1 files changed, 0 insertions, 1997 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
deleted file mode 100644
index 9a20d8329ad..00000000000
--- a/src/backend/commands/command.c
+++ /dev/null
@@ -1,1997 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * command.c
- * random postgres portal and utility support code
- *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.175 2002/04/14 16:47:16 momjian Exp $
- *
- * NOTES
- * The PerformAddAttribute() code, like most of the relation
- * manipulating code in the commands/ directory, should go
- * someplace closer to the lib/catalog code.
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/genam.h"
-#include "access/tuptoaster.h"
-#include "catalog/catalog.h"
-#include "catalog/catname.h"
-#include "catalog/heap.h"
-#include "catalog/index.h"
-#include "catalog/indexing.h"
-#include "catalog/namespace.h"
-#include "catalog/pg_attrdef.h"
-#include "catalog/pg_index.h"
-#include "catalog/pg_namespace.h"
-#include "catalog/pg_opclass.h"
-#include "catalog/pg_relcheck.h"
-#include "catalog/pg_type.h"
-#include "commands/command.h"
-#include "commands/trigger.h"
-#include "executor/execdefs.h"
-#include "executor/executor.h"
-#include "miscadmin.h"
-#include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
-#include "optimizer/planmain.h"
-#include "optimizer/prep.h"
-#include "parser/analyze.h"
-#include "parser/parse.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_oper.h"
-#include "parser/parse_relation.h"
-#include "parser/parse_type.h"
-#include "tcop/utility.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-#include "utils/relcache.h"
-
-
-static void drop_default(Oid relid, int16 attnum);
-static bool needs_toast_table(Relation rel);
-static void CheckTupleType(Form_pg_class tuple_class);
-
-
-/*
- * PortalCleanup
- */
-void
-PortalCleanup(Portal portal)
-{
- MemoryContext oldcontext;
-
- /*
- * sanity checks
- */
- AssertArg(PortalIsValid(portal));
- AssertArg(portal->cleanup == PortalCleanup);
-
- /*
- * set proper portal-executor context before calling ExecMain.
- */
- oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
- /*
- * tell the executor to shutdown the query
- */
- ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
-
- /*
- * switch back to previous context
- */
- MemoryContextSwitchTo(oldcontext);
-}
-
-
-/*
- * PerformPortalFetch
- *
- * name: name of portal
- * forward: forward or backward fetch?
- * count: # of tuples to fetch (0 implies all)
- * dest: where to send results
- * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
- * in which to store a command completion status string.
- *
- * completionTag may be NULL if caller doesn't want a status string.
- */
-void
-PerformPortalFetch(char *name,
- bool forward,
- int count,
- CommandDest dest,
- char *completionTag)
-{
- Portal portal;
- QueryDesc *queryDesc;
- EState *estate;
- MemoryContext oldcontext;
- ScanDirection direction;
- CommandId savedId;
- bool temp_desc = false;
-
- /* initialize completion status in case of early exit */
- if (completionTag)
- strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0");
-
- /*
- * sanity checks
- */
- if (name == NULL)
- {
- elog(WARNING, "PerformPortalFetch: missing portal name");
- return;
- }
-
- /*
- * get the portal from the portal name
- */
- portal = GetPortalByName(name);
- if (!PortalIsValid(portal))
- {
- elog(WARNING, "PerformPortalFetch: portal \"%s\" not found",
- name);
- return;
- }
-
- /*
- * switch into the portal context
- */
- oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
- queryDesc = PortalGetQueryDesc(portal);
- estate = PortalGetState(portal);
-
- /*
- * If the requested destination is not the same as the query's
- * original destination, make a temporary QueryDesc with the proper
- * destination. This supports MOVE, for example, which will pass in
- * dest = None.
- *
- * EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a
- * binary cursor) and the request is Remote, we do NOT override the
- * original dest. This is necessary since a FETCH command will pass
- * dest = Remote, not knowing whether the cursor is binary or not.
- */
- if (dest != queryDesc->dest &&
- !(queryDesc->dest == RemoteInternal && dest == Remote))
- {
- QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
-
- memcpy(qdesc, queryDesc, sizeof(QueryDesc));
- qdesc->dest = dest;
- queryDesc = qdesc;
- temp_desc = true;
- }
-
- /*
- * Restore the scanCommandId that was current when the cursor was
- * opened. This ensures that we see the same tuples throughout the
- * execution of the cursor.
- */
- savedId = GetScanCommandId();
- SetScanCommandId(PortalGetCommandId(portal));
-
- /*
- * Determine which direction to go in, and check to see if we're
- * already at the end of the available tuples in that direction. If
- * so, set the direction to NoMovement to avoid trying to fetch any
- * tuples. (This check exists because not all plan node types
- * are robust about being called again if they've already returned
- * NULL once.) Then call the executor (we must not skip this, because
- * the destination needs to see a setup and shutdown even if no tuples
- * are available). Finally, update the atStart/atEnd state depending
- * on the number of tuples that were retrieved.
- */
- if (forward)
- {
- if (portal->atEnd)
- direction = NoMovementScanDirection;
- else
- direction = ForwardScanDirection;
-
- ExecutorRun(queryDesc, estate, direction, (long) count);
-
- if (estate->es_processed > 0)
- portal->atStart = false; /* OK to back up now */
- if (count <= 0 || (int) estate->es_processed < count)
- portal->atEnd = true; /* we retrieved 'em all */
- }
- else
- {
- if (portal->atStart)
- direction = NoMovementScanDirection;
- else
- direction = BackwardScanDirection;
-
- ExecutorRun(queryDesc, estate, direction, (long) count);
-
- if (estate->es_processed > 0)
- portal->atEnd = false; /* OK to go forward now */
- if (count <= 0 || (int) estate->es_processed < count)
- portal->atStart = true; /* we retrieved 'em all */
- }
-
- /* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
- (dest == None) ? "MOVE" : "FETCH",
- estate->es_processed);
-
- /*
- * Restore outer command ID.
- */
- SetScanCommandId(savedId);
-
- /*
- * Clean up and switch back to old context.
- */
- if (temp_desc)
- pfree(queryDesc);
-
- MemoryContextSwitchTo(oldcontext);
-}
-
-/*
- * PerformPortalClose
- */
-void
-PerformPortalClose(char *name, CommandDest dest)
-{
- Portal portal;
-
- /*
- * sanity checks
- */
- if (name == NULL)
- {
- elog(WARNING, "PerformPortalClose: missing portal name");
- return;
- }
-
- /*
- * get the portal from the portal name
- */
- portal = GetPortalByName(name);
- if (!PortalIsValid(portal))
- {
- elog(WARNING, "PerformPortalClose: portal \"%s\" not found",
- name);
- return;
- }
-
- /*
- * Note: PortalCleanup is called as a side-effect
- */
- PortalDrop(portal);
-}
-
-/* ----------------
- * AlterTableAddColumn
- * (formerly known as PerformAddAttribute)
- *
- * adds an additional attribute to a relation
- *
- * Adds attribute field(s) to a relation. Each new attribute
- * is given attnums in sequential order and is added to the
- * ATTRIBUTE relation. If the AMI fails, defunct tuples will
- * remain in the ATTRIBUTE relation for later vacuuming.
- * Later, there may be some reserved attribute names???
- *
- * (If needed, can instead use elog to handle exceptions.)
- *
- * Note:
- * Initial idea of ordering the tuple attributes so that all
- * the variable length domains occured last was scratched. Doing
- * so would not speed access too much (in general) and would create
- * many complications in formtuple, heap_getattr, and addattribute.
- *
- * scan attribute catalog for name conflict (within rel)
- * scan type catalog for absence of data type (if not arg)
- * create attnum magically???
- * create attribute tuple
- * insert attribute in attribute catalog
- * modify reldesc
- * create new relation tuple
- * insert new relation in relation catalog
- * delete original relation from relation catalog
- * ----------------
- */
-void
-AlterTableAddColumn(Oid myrelid,
- bool inherits,
- ColumnDef *colDef)
-{
- Relation rel,
- pgclass,
- attrdesc;
- HeapTuple reltup;
- HeapTuple newreltup;
- HeapTuple attributeTuple;
- Form_pg_attribute attribute;
- FormData_pg_attribute attributeD;
- int i;
- int minattnum,
- maxatts;
- HeapTuple typeTuple;
- Form_pg_type tform;
- int attndims;
-
- /*
- * 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));
-
- /*
- * 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
- && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * Recurse to add the column to child classes, if requested.
- *
- * any permissions or problems with duplicate attributes will cause the
- * whole transaction to abort, which is what we want -- all or
- * nothing.
- */
- if (inherits)
- {
- 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);
-
- if (childrelid == myrelid)
- continue;
-
- AlterTableAddColumn(childrelid, false, colDef);
- }
- }
-
- /*
- * 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 ... SET NOT NULL.");
-
- pgclass = heap_openr(RelationRelationName, RowExclusiveLock);
-
- reltup = SearchSysCache(RELOID,
- ObjectIdGetDatum(myrelid),
- 0, 0, 0);
- if (!HeapTupleIsValid(reltup))
- elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
- RelationGetRelationName(rel));
-
- if (SearchSysCacheExists(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colDef->colname),
- 0, 0))
- elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
- colDef->colname, RelationGetRelationName(rel));
-
- minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
- maxatts = minattnum + 1;
- if (maxatts > MaxHeapAttributeNumber)
- elog(ERROR, "ALTER TABLE: relations limited to %d columns",
- MaxHeapAttributeNumber);
- i = minattnum + 1;
-
- attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- if (colDef->typename->arrayBounds)
- attndims = length(colDef->typename->arrayBounds);
- else
- attndims = 0;
-
- typeTuple = typenameType(colDef->typename);
- tform = (Form_pg_type) GETSTRUCT(typeTuple);
-
- attributeTuple = heap_addheader(Natts_pg_attribute,
- ATTRIBUTE_TUPLE_SIZE,
- (void *) &attributeD);
-
- attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
-
- attribute->attrelid = myrelid;
- 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)
- {
- Relation idescs[Num_pg_attr_indices];
-
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, attributeTuple);
- CatalogCloseIndices(Num_pg_attr_indices, idescs);
- }
-
- heap_close(attrdesc, RowExclusiveLock);
-
- /*
- * Update number of attributes in pg_class tuple
- */
- newreltup = heap_copytuple(reltup);
-
- ((Form_pg_class) GETSTRUCT(newreltup))->relnatts = maxatts;
- simple_heap_update(pgclass, &newreltup->t_self, newreltup);
-
- /* keep catalog indices current */
- if (RelationGetForm(pgclass)->relhasindex)
- {
- Relation ridescs[Num_pg_class_indices];
-
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
- CatalogIndexInsert(ridescs, Num_pg_class_indices, pgclass, newreltup);
- CatalogCloseIndices(Num_pg_class_indices, ridescs);
- }
-
- heap_freetuple(newreltup);
- ReleaseSysCache(reltup);
-
- heap_close(pgclass, NoLock);
-
- heap_close(rel, NoLock); /* close rel but keep lock! */
-
- /*
- * 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_open(myrelid, AccessExclusiveLock);
- AddRelationRawConstraints(rel, NIL, colDef->constraints);
- heap_close(rel, NoLock);
- }
-
- /*
- * Automatically create the secondary relation for TOAST if it
- * formerly had no such but now has toastable attributes.
- */
- AlterTableCreateToastTable(myrelid, true);
-}
-
-/*
- * ALTER TABLE ALTER COLUMN DROP NOT NULL
- */
-void
-AlterTableAlterColumnDropNotNull(Oid myrelid,
- bool inh, const char *colName)
-{
- Relation rel;
- HeapTuple tuple;
- AttrNumber attnum;
- Relation attr_rel;
- List *indexoidlist;
- List *indexoidscan;
-
- 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
- && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * Propagate to children if desired
- */
- 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);
-
- if (childrelid == myrelid)
- continue;
- AlterTableAlterColumnDropNotNull(childrelid,
- false, colName);
- }
- }
-
- /* -= now do the thing on this relation =- */
-
- /*
- * get the number of the attribute
- */
- tuple = SearchSysCache(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
- ReleaseSysCache(tuple);
-
- /* Prevent them from altering a system attribute */
- if (attnum < 0)
- elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
- colName);
-
- /*
- * Check that the attribute is not in a primary key
- */
-
- /* Loop over all indices on the relation */
- indexoidlist = RelationGetIndexList(rel);
-
- foreach(indexoidscan, indexoidlist)
- {
- Oid indexoid = lfirsti(indexoidscan);
- HeapTuple indexTuple;
- Form_pg_index indexStruct;
- int i;
-
- indexTuple = SearchSysCache(INDEXRELID,
- ObjectIdGetDatum(indexoid),
- 0, 0, 0);
- if (!HeapTupleIsValid(indexTuple))
- elog(ERROR, "ALTER TABLE: Index %u not found",
- indexoid);
- indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
-
- /* If the index is not a primary key, skip the check */
- if (indexStruct->indisprimary)
- {
- /*
- * Loop over each attribute in the primary key and
- * see if it matches the to-be-altered attribute
- */
- for (i = 0; i < INDEX_MAX_KEYS &&
- indexStruct->indkey[i] != InvalidAttrNumber; i++)
- {
- if (indexStruct->indkey[i] == attnum)
- elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
- }
- }
-
- ReleaseSysCache(indexTuple);
- }
-
- freeList(indexoidlist);
-
- /*
- * Okay, actually perform the catalog change
- */
- attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
-
- simple_heap_update(attr_rel, &tuple->t_self, tuple);
-
- /* keep the system catalog indices current */
- if (RelationGetForm(attr_rel)->relhasindex)
- {
- Relation idescs[Num_pg_attr_indices];
-
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
- CatalogCloseIndices(Num_pg_attr_indices, idescs);
- }
-
- heap_close(attr_rel, RowExclusiveLock);
-
- heap_close(rel, NoLock);
-}
-
-/*
- * ALTER TABLE ALTER COLUMN SET NOT NULL
- */
-void
-AlterTableAlterColumnSetNotNull(Oid myrelid,
- bool inh, const char *colName)
-{
- Relation rel;
- HeapTuple tuple;
- AttrNumber attnum;
- Relation attr_rel;
- HeapScanDesc scan;
- TupleDesc tupdesc;
-
- 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
- && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * Propagate to children if desired
- */
- 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);
-
- if (childrelid == myrelid)
- continue;
- AlterTableAlterColumnSetNotNull(childrelid,
- false, colName);
- }
- }
-
- /* -= now do the thing on this relation =- */
-
- /*
- * get the number of the attribute
- */
- tuple = SearchSysCache(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
- ReleaseSysCache(tuple);
-
- /* Prevent them from altering a system attribute */
- if (attnum < 0)
- elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
- colName);
-
- /*
- * Perform a scan to ensure that there are no NULL
- * values already in the relation
- */
- tupdesc = RelationGetDescr(rel);
-
- scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
-
- while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
- {
- Datum d;
- bool isnull;
-
- d = heap_getattr(tuple, attnum, tupdesc, &isnull);
-
- if (isnull)
- elog(ERROR, "ALTER TABLE: Attribute \"%s\" contains NULL values",
- colName);
- }
-
- heap_endscan(scan);
-
- /*
- * Okay, actually perform the catalog change
- */
- attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
-
- simple_heap_update(attr_rel, &tuple->t_self, tuple);
-
- /* keep the system catalog indices current */
- if (RelationGetForm(attr_rel)->relhasindex)
- {
- Relation idescs[Num_pg_attr_indices];
-
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
- CatalogCloseIndices(Num_pg_attr_indices, idescs);
- }
-
- heap_close(attr_rel, RowExclusiveLock);
-
- heap_close(rel, NoLock);
-}
-
-
-/*
- * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
- */
-void
-AlterTableAlterColumnDefault(Oid myrelid,
- bool inh, const char *colName,
- Node *newDefault)
-{
- Relation rel;
- HeapTuple tuple;
- AttrNumber attnum;
-
- 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
- && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * Propagate to children if desired
- */
- 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);
-
- if (childrelid == myrelid)
- continue;
- AlterTableAlterColumnDefault(childrelid,
- false, colName, newDefault);
- }
- }
-
- /* -= now do the thing on this relation =- */
-
- /*
- * get the number of the attribute
- */
- tuple = SearchSysCache(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
- ReleaseSysCache(tuple);
-
- if (newDefault)
- {
- /* SET DEFAULT */
- RawColumnDefault *rawEnt;
-
- /* Get rid of the old one first */
- drop_default(myrelid, attnum);
-
- rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
- rawEnt->attnum = attnum;
- rawEnt->raw_default = newDefault;
-
- /*
- * This function is intended for CREATE TABLE, so it processes a
- * _list_ of defaults, but we just do one.
- */
- AddRelationRawConstraints(rel, makeList1(rawEnt), NIL);
- }
- else
- {
- /* DROP DEFAULT */
- Relation attr_rel;
-
- /* Fix the pg_attribute row */
- attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = FALSE;
-
- simple_heap_update(attr_rel, &tuple->t_self, tuple);
-
- /* keep the system catalog indices current */
- if (RelationGetForm(attr_rel)->relhasindex)
- {
- Relation idescs[Num_pg_attr_indices];
-
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
- CatalogCloseIndices(Num_pg_attr_indices, idescs);
- }
-
- heap_close(attr_rel, RowExclusiveLock);
-
- /* get rid of actual default definition in pg_attrdef */
- drop_default(myrelid, attnum);
- }
-
- heap_close(rel, NoLock);
-}
-
-
-static void
-drop_default(Oid relid, int16 attnum)
-{
- ScanKeyData scankeys[2];
- HeapScanDesc scan;
- Relation attrdef_rel;
- HeapTuple tuple;
-
- attrdef_rel = heap_openr(AttrDefaultRelationName, RowExclusiveLock);
- ScanKeyEntryInitialize(&scankeys[0], 0x0,
- Anum_pg_attrdef_adrelid, F_OIDEQ,
- ObjectIdGetDatum(relid));
- ScanKeyEntryInitialize(&scankeys[1], 0x0,
- Anum_pg_attrdef_adnum, F_INT2EQ,
- Int16GetDatum(attnum));
-
- scan = heap_beginscan(attrdef_rel, false, SnapshotNow, 2, scankeys);
-
- if (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
- simple_heap_delete(attrdef_rel, &tuple->t_self);
-
- heap_endscan(scan);
-
- heap_close(attrdef_rel, NoLock);
-}
-
-
-/*
- * ALTER TABLE ALTER COLUMN SET STATISTICS / STORAGE
- */
-void
-AlterTableAlterColumnFlags(Oid myrelid,
- bool inh, const char *colName,
- Node *flagValue, const char *flagType)
-{
- Relation rel;
- int newtarget = 1;
- char newstorage = 'x';
- char *storagemode;
- Relation attrelation;
- HeapTuple tuple;
-
- rel = heap_open(myrelid, AccessExclusiveLock);
-
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
- RelationGetRelationName(rel));
-
- /*
- * we allow statistics case for system tables
- */
- if (*flagType != 'S' && !allowSystemTableMods && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * Check the supplied parameters before anything else
- */
- if (*flagType == 'S')
- {
- /* STATISTICS */
- Assert(IsA(flagValue, Integer));
- newtarget = intVal(flagValue);
-
- /*
- * Limit target to sane range (should we raise an error instead?)
- */
- if (newtarget < 0)
- newtarget = 0;
- else if (newtarget > 1000)
- newtarget = 1000;
- }
- else if (*flagType == 'M')
- {
- /* STORAGE */
- Assert(IsA(flagValue, Value));
-
- storagemode = strVal(flagValue);
- if (strcasecmp(storagemode, "plain") == 0)
- newstorage = 'p';
- else if (strcasecmp(storagemode, "external") == 0)
- newstorage = 'e';
- else if (strcasecmp(storagemode, "extended") == 0)
- newstorage = 'x';
- else if (strcasecmp(storagemode, "main") == 0)
- newstorage = 'm';
- else
- elog(ERROR, "ALTER TABLE: \"%s\" storage not recognized",
- storagemode);
- }
- else
- {
- elog(ERROR, "ALTER TABLE: Invalid column flag: %c",
- (int) *flagType);
- }
-
- /*
- * Propagate to children if desired
- */
- 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);
-
- if (childrelid == myrelid)
- continue;
- AlterTableAlterColumnFlags(childrelid,
- false, colName, flagValue, flagType);
- }
- }
-
- /* -= now do the thing on this relation =- */
-
- attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy(ATTNAME,
- ObjectIdGetDatum(myrelid),
- PointerGetDatum(colName),
- 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
- RelationGetRelationName(rel), colName);
-
- if (((Form_pg_attribute) GETSTRUCT(tuple))->attnum < 0)
- elog(ERROR, "ALTER TABLE: cannot change system attribute \"%s\"",
- colName);
- /*
- * Now change the appropriate field
- */
- if (*flagType == 'S')
- ((Form_pg_attribute) GETSTRUCT(tuple))->attstattarget = newtarget;
- else
- {
- if ((newstorage == 'p') ||
- (((Form_pg_attribute) GETSTRUCT(tuple))->attlen == -1))
- ((Form_pg_attribute) GETSTRUCT(tuple))->attstorage = newstorage;
- else
- {
- elog(ERROR,
- "ALTER TABLE: Fixed-length columns can only have storage \"plain\"");
- }
- }
- simple_heap_update(attrelation, &tuple->t_self, tuple);
-
- /* keep system catalog indices current */
- {
- Relation irelations[Num_pg_attr_indices];
-
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
- CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, tuple);
- CatalogCloseIndices(Num_pg_attr_indices, irelations);
- }
-
- heap_freetuple(tuple);
- heap_close(attrelation, NoLock);
- heap_close(rel, NoLock); /* close rel, but keep lock! */
-}
-
-
-
-/*
- * ALTER TABLE DROP COLUMN
- */
-void
-AlterTableDropColumn(Oid myrelid,
- bool inh, const char *colName,
- int behavior)
-{
- elog(ERROR, "ALTER TABLE / DROP COLUMN is not implemented");
-}
-
-
-
-/*
- * ALTER TABLE ADD CONSTRAINT
- */
-void
-AlterTableAddConstraint(Oid myrelid,
- bool inh, List *newConstraints)
-{
- Relation rel;
- List *listptr;
-
- /*
- * 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
- && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(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);
-
- if (childrelid == myrelid)
- continue;
- AlterTableAddConstraint(childrelid, false, newConstraints);
- }
- }
-
- foreach(listptr, newConstraints)
- {
- Node *newConstraint = lfirst(listptr);
-
- switch (nodeTag(newConstraint))
- {
- case T_Constraint:
- {
- Constraint *constr = (Constraint *) newConstraint;
-
- /*
- * Currently, we only expect to see CONSTR_CHECK nodes
- * arriving here (see the preprocessing done in
- * parser/analyze.c). Use a switch anyway to make it
- * easier to add more code later.
- */
- switch (constr->contype)
- {
- case CONSTR_CHECK:
- {
- ParseState *pstate;
- bool successful = true;
- HeapScanDesc scan;
- ExprContext *econtext;
- TupleTableSlot *slot;
- HeapTuple tuple;
- RangeTblEntry *rte;
- List *qual;
- Node *expr;
- char *name;
-
- if (constr->name)
- name = constr->name;
- else
- name = "<unnamed>";
-
- /*
- * We need to make a parse state and range
- * table to allow us to transformExpr and
- * fix_opids to get a version of the
- * expression we can pass to ExecQual
- */
- pstate = make_parsestate(NULL);
- rte = addRangeTableEntryForRelation(pstate,
- myrelid,
- makeAlias(RelationGetRelationName(rel), NIL),
- false,
- true);
- addRTEtoQuery(pstate, rte, true, true);
-
- /*
- * Convert the A_EXPR in raw_expr into an
- * EXPR
- */
- expr = transformExpr(pstate, constr->raw_expr);
-
- /*
- * Make sure it yields a boolean result.
- */
- if (exprType(expr) != BOOLOID)
- elog(ERROR, "CHECK '%s' does not yield boolean result",
- name);
-
- /*
- * Make sure no outside relations are
- * referred to.
- */
- if (length(pstate->p_rtable) != 1)
- elog(ERROR, "Only relation '%s' can be referenced in CHECK",
- RelationGetRelationName(rel));
-
- /*
- * Might as well try to reduce any
- * constant expressions.
- */
- expr = eval_const_expressions(expr);
-
- /* And fix the opids */
- fix_opids(expr);
-
- qual = makeList1(expr);
-
- /* Make tuple slot to hold tuples */
- slot = MakeTupleTableSlot();
- ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
- /* Make an expression context for ExecQual */
- econtext = MakeExprContext(slot, CurrentMemoryContext);
-
- /*
- * Scan through the rows now, checking the
- * expression at each row.
- */
- scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
-
- while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
- {
- ExecStoreTuple(tuple, slot, InvalidBuffer, false);
- if (!ExecQual(qual, econtext, true))
- {
- successful = false;
- break;
- }
- ResetExprContext(econtext);
- }
-
- heap_endscan(scan);
-
- FreeExprContext(econtext);
- pfree(slot);
-
- if (!successful)
- elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
-
- /*
- * Call AddRelationRawConstraints to do
- * the real adding -- It duplicates some
- * of the above, but does not check the
- * validity of the constraint against
- * tuples already in the table.
- */
- AddRelationRawConstraints(rel, NIL,
- makeList1(constr));
-
- break;
- }
- default:
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
- }
- break;
- }
- case T_FkConstraint:
- {
- FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
- Relation pkrel;
- HeapScanDesc scan;
- HeapTuple tuple;
- Trigger trig;
- List *list;
- int count;
-
- /*
- * Grab an exclusive lock on the pk table, so that
- * someone doesn't delete rows out from under us.
- *
- * XXX wouldn't a lesser lock be sufficient?
- */
- pkrel = heap_openrv(fkconstraint->pktable,
- AccessExclusiveLock);
-
- /*
- * Validity checks
- */
- if (pkrel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "referenced table \"%s\" not a relation",
- fkconstraint->pktable->relname);
-
- if (isTempNamespace(RelationGetNamespace(pkrel)) &&
- !isTempNamespace(RelationGetNamespace(rel)))
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
-
- /*
- * First we check for limited correctness of the
- * constraint.
- *
- * NOTE: we assume parser has already checked for
- * existence of an appropriate unique index on the
- * referenced relation, and that the column datatypes
- * are comparable.
- *
- * Scan through each tuple, calling RI_FKey_check_ins
- * (insert trigger) as if that tuple had just been
- * inserted. If any of those fail, it should
- * elog(ERROR) and that's that.
- */
- MemSet(&trig, 0, sizeof(trig));
- trig.tgoid = InvalidOid;
- if (fkconstraint->constr_name)
- trig.tgname = fkconstraint->constr_name;
- else
- trig.tgname = "<unknown>";
- trig.tgenabled = TRUE;
- trig.tgisconstraint = TRUE;
- trig.tgconstrrelid = RelationGetRelid(pkrel);
- trig.tgdeferrable = FALSE;
- trig.tginitdeferred = FALSE;
-
- trig.tgargs = (char **) palloc(
- sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
- + length(fkconstraint->pk_attrs)));
-
- trig.tgargs[0] = trig.tgname;
- trig.tgargs[1] = RelationGetRelationName(rel);
- trig.tgargs[2] = RelationGetRelationName(pkrel);
- trig.tgargs[3] = fkconstraint->match_type;
- count = 4;
- foreach(list, fkconstraint->fk_attrs)
- {
- Ident *fk_at = lfirst(list);
-
- trig.tgargs[count] = fk_at->name;
- count += 2;
- }
- count = 5;
- foreach(list, fkconstraint->pk_attrs)
- {
- Ident *pk_at = lfirst(list);
-
- trig.tgargs[count] = pk_at->name;
- count += 2;
- }
- trig.tgnargs = count - 1;
-
- scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
-
- while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
- {
- /* Make a call to the check function */
-
- /*
- * No parameters are passed, but we do set a
- * context
- */
- FunctionCallInfoData fcinfo;
- TriggerData trigdata;
-
- MemSet(&fcinfo, 0, sizeof(fcinfo));
-
- /*
- * We assume RI_FKey_check_ins won't look at
- * flinfo...
- */
-
- trigdata.type = T_TriggerData;
- trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
- trigdata.tg_relation = rel;
- trigdata.tg_trigtuple = tuple;
- trigdata.tg_newtuple = NULL;
- trigdata.tg_trigger = &trig;
-
- fcinfo.context = (Node *) &trigdata;
-
- RI_FKey_check_ins(&fcinfo);
- }
- heap_endscan(scan);
-
- pfree(trig.tgargs);
-
- heap_close(pkrel, NoLock);
-
- break;
- }
- default:
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
- }
- }
-
- /* Close rel, but keep lock till commit */
- heap_close(rel, NoLock);
-}
-
-
-
-/*
- * ALTER TABLE DROP CONSTRAINT
- * Note: It is legal to remove a constraint with name "" as it is possible
- * to add a constraint with name "".
- * Christopher Kings-Lynne
- */
-void
-AlterTableDropConstraint(Oid myrelid,
- bool inh, const char *constrName,
- int behavior)
-{
- Relation rel;
- int deleted;
-
- /*
- * We don't support CASCADE yet - in fact, RESTRICT doesn't work to
- * the spec either!
- */
- if (behavior == CASCADE)
- elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
-
- /*
- * Acquire an exclusive lock on the target relation for the duration
- * of the operation.
- */
- rel = heap_open(myrelid, AccessExclusiveLock);
-
- /* Disallow DROP CONSTRAINT on views, indexes, sequences, etc */
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
- RelationGetRelationName(rel));
-
- if (!allowSystemTableMods
- && IsSystemRelation(rel))
- elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(myrelid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * Since all we have is the name of the constraint, we have to look
- * through all catalogs that could possibly contain a constraint for
- * this relation. We also keep a count of the number of constraints
- * removed.
- */
-
- deleted = 0;
-
- /*
- * First, we remove all CHECK constraints with the given name
- */
-
- deleted += RemoveCheckConstraint(rel, constrName, inh);
-
- /*
- * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
- * constraints.
- *
- * Unimplemented.
- */
-
- /* Close the target relation */
- heap_close(rel, NoLock);
-
- /* If zero constraints deleted, complain */
- if (deleted == 0)
- elog(ERROR, "ALTER TABLE / DROP CONSTRAINT: %s does not exist",
- constrName);
- /* Otherwise if more than one constraint deleted, notify */
- else if (deleted > 1)
- elog(NOTICE, "Multiple constraints dropped");
-}
-
-/*
- * ALTER TABLE OWNER
- */
-void
-AlterTableOwner(Oid relationOid, int32 newOwnerSysId)
-{
- Relation target_rel;
- Relation class_rel;
- HeapTuple tuple;
- Relation idescs[Num_pg_class_indices];
- Form_pg_class tuple_class;
-
- /* Get exclusive lock till end of transaction on the target table */
- target_rel = heap_open(relationOid, AccessExclusiveLock);
-
- /* Get its pg_class tuple, too */
- class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy(RELOID,
- ObjectIdGetDatum(relationOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "ALTER TABLE: relation %u not found", relationOid);
- tuple_class = (Form_pg_class) GETSTRUCT(tuple);
-
- /* Can we change the ownership of this tuple? */
- CheckTupleType(tuple_class);
-
- /*
- * Okay, this is a valid tuple: change its ownership and
- * write to the heap.
- */
- tuple_class->relowner = newOwnerSysId;
- simple_heap_update(class_rel, &tuple->t_self, tuple);
-
- /* Keep the catalog indices up to date */
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
- CatalogIndexInsert(idescs, Num_pg_class_indices, class_rel, tuple);
- CatalogCloseIndices(Num_pg_class_indices, idescs);
-
- /*
- * If we are operating on a table, also change the ownership of any
- * indexes that belong to the table, as well as the table's toast
- * table (if it has one)
- */
- if (tuple_class->relkind == RELKIND_RELATION ||
- tuple_class->relkind == RELKIND_TOASTVALUE)
- {
- List *index_oid_list, *i;
-
- /* Find all the indexes belonging to this relation */
- index_oid_list = RelationGetIndexList(target_rel);
-
- /* For each index, recursively change its ownership */
- foreach(i, index_oid_list)
- {
- AlterTableOwner(lfirsti(i), newOwnerSysId);
- }
-
- freeList(index_oid_list);
- }
-
- if (tuple_class->relkind == RELKIND_RELATION)
- {
- /* If it has a toast table, recurse to change its ownership */
- if (tuple_class->reltoastrelid != InvalidOid)
- {
- AlterTableOwner(tuple_class->reltoastrelid, newOwnerSysId);
- }
- }
-
- heap_freetuple(tuple);
- heap_close(class_rel, RowExclusiveLock);
- heap_close(target_rel, NoLock);
-}
-
-static void
-CheckTupleType(Form_pg_class tuple_class)
-{
- switch (tuple_class->relkind)
- {
- case RELKIND_RELATION:
- case RELKIND_INDEX:
- case RELKIND_VIEW:
- case RELKIND_SEQUENCE:
- case RELKIND_TOASTVALUE:
- /* ok to change owner */
- break;
- default:
- elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
- NameStr(tuple_class->relname));
- }
-}
-
-/*
- * ALTER TABLE CREATE TOAST TABLE
- */
-void
-AlterTableCreateToastTable(Oid relOid, bool silent)
-{
- Relation rel;
- HeapTuple reltup;
- HeapTupleData classtuple;
- TupleDesc tupdesc;
- Relation class_rel;
- Buffer buffer;
- Relation ridescs[Num_pg_class_indices];
- Oid toast_relid;
- Oid toast_idxid;
- char toast_relname[NAMEDATALEN + 1];
- char toast_idxname[NAMEDATALEN + 1];
- IndexInfo *indexInfo;
- Oid classObjectId[2];
-
- /*
- * Grab an exclusive lock on the target table, which we will NOT
- * release until end of transaction.
- */
- rel = heap_open(relOid, AccessExclusiveLock);
-
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
- RelationGetRelationName(rel));
-
- if (!pg_class_ownercheck(relOid, GetUserId()))
- elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
- RelationGetRelationName(rel));
-
- /*
- * lock the pg_class tuple for update (is that really needed?)
- */
- class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
-
- reltup = SearchSysCache(RELOID,
- ObjectIdGetDatum(relOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(reltup))
- elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
- RelationGetRelationName(rel));
- classtuple.t_self = reltup->t_self;
- ReleaseSysCache(reltup);
-
- switch (heap_mark4update(class_rel, &classtuple, &buffer))
- {
- case HeapTupleSelfUpdated:
- case HeapTupleMayBeUpdated:
- break;
- default:
- elog(ERROR, "couldn't lock pg_class tuple");
- }
- reltup = heap_copytuple(&classtuple);
- ReleaseBuffer(buffer);
-
- /*
- * Is it already toasted?
- */
- if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid)
- {
- if (silent)
- {
- heap_close(rel, NoLock);
- heap_close(class_rel, NoLock);
- heap_freetuple(reltup);
- return;
- }
-
- elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
- RelationGetRelationName(rel));
- }
-
- /*
- * Check to see whether the table actually needs a TOAST table.
- */
- if (!needs_toast_table(rel))
- {
- if (silent)
- {
- heap_close(rel, NoLock);
- heap_close(class_rel, NoLock);
- heap_freetuple(reltup);
- return;
- }
-
- elog(ERROR, "ALTER TABLE: relation \"%s\" does not need a toast table",
- RelationGetRelationName(rel));
- }
-
- /*
- * Create the toast table and its index
- */
- sprintf(toast_relname, "pg_toast_%u", relOid);
- sprintf(toast_idxname, "pg_toast_%u_idx", relOid);
-
- /* this is pretty painful... need a tuple descriptor */
- tupdesc = CreateTemplateTupleDesc(3);
- TupleDescInitEntry(tupdesc, (AttrNumber) 1,
- "chunk_id",
- OIDOID,
- -1, 0, false);
- TupleDescInitEntry(tupdesc, (AttrNumber) 2,
- "chunk_seq",
- INT4OID,
- -1, 0, false);
- TupleDescInitEntry(tupdesc, (AttrNumber) 3,
- "chunk_data",
- BYTEAOID,
- -1, 0, false);
-
- /*
- * Ensure that the toast table doesn't itself get toasted, or we'll be
- * toast :-(. This is essential for chunk_data because type bytea is
- * toastable; hit the other two just to be sure.
- */
- tupdesc->attrs[0]->attstorage = 'p';
- tupdesc->attrs[1]->attstorage = 'p';
- tupdesc->attrs[2]->attstorage = 'p';
-
- /*
- * Note: the toast relation is placed in the regular pg_toast namespace
- * even if its master relation is a temp table. There cannot be any
- * naming collision, and the toast rel will be destroyed when its master
- * is, so there's no need to handle the toast rel as temp.
- */
- toast_relid = heap_create_with_catalog(toast_relname,
- PG_TOAST_NAMESPACE,
- tupdesc,
- RELKIND_TOASTVALUE,
- false,
- true);
-
- /* make the toast relation visible, else index creation will fail */
- CommandCounterIncrement();
-
- /*
- * Create unique index on chunk_id, chunk_seq.
- *
- * NOTE: the tuple toaster could actually function with a single-column
- * index on chunk_id only. However, it couldn't be unique then. We
- * want it to be unique as a check against the possibility of
- * duplicate TOAST chunk OIDs. Too, the index might be a little more
- * efficient this way, since btree isn't all that happy with large
- * numbers of equal keys.
- */
-
- indexInfo = makeNode(IndexInfo);
- indexInfo->ii_NumIndexAttrs = 2;
- indexInfo->ii_NumKeyAttrs = 2;
- indexInfo->ii_KeyAttrNumbers[0] = 1;
- indexInfo->ii_KeyAttrNumbers[1] = 2;
- indexInfo->ii_Predicate = NIL;
- indexInfo->ii_FuncOid = InvalidOid;
- indexInfo->ii_Unique = true;
-
- classObjectId[0] = OID_BTREE_OPS_OID;
- classObjectId[1] = INT4_BTREE_OPS_OID;
-
- toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
- BTREE_AM_OID, classObjectId,
- true, true);
-
- /*
- * Update toast rel's pg_class entry to show that it has an index. The
- * index OID is stored into the reltoastidxid field for easy access by
- * the tuple toaster.
- */
- setRelhasindex(toast_relid, true, true, toast_idxid);
-
- /*
- * Store the toast table's OID in the parent relation's tuple
- */
- ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
- simple_heap_update(class_rel, &reltup->t_self, reltup);
-
- /*
- * Keep catalog indices current
- */
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
- CatalogIndexInsert(ridescs, Num_pg_class_indices, class_rel, reltup);
- CatalogCloseIndices(Num_pg_class_indices, ridescs);
-
- heap_freetuple(reltup);
-
- /*
- * Close relations and make changes visible
- */
- heap_close(class_rel, NoLock);
- heap_close(rel, NoLock);
-
- CommandCounterIncrement();
-}
-
-/*
- * Check to see whether the table needs a TOAST table. It does only if
- * (1) there are any toastable attributes, and (2) the maximum length
- * of a tuple could exceed TOAST_TUPLE_THRESHOLD. (We don't want to
- * create a toast table for something like "f1 varchar(20)".)
- */
-static bool
-needs_toast_table(Relation rel)
-{
- int32 data_length = 0;
- bool maxlength_unknown = false;
- bool has_toastable_attrs = false;
- TupleDesc tupdesc;
- Form_pg_attribute *att;
- int32 tuple_length;
- int i;
-
- tupdesc = rel->rd_att;
- att = tupdesc->attrs;
-
- for (i = 0; i < tupdesc->natts; i++)
- {
- data_length = att_align(data_length, att[i]->attlen, att[i]->attalign);
- if (att[i]->attlen >= 0)
- {
- /* Fixed-length types are never toastable */
- data_length += att[i]->attlen;
- }
- else
- {
- int32 maxlen = type_maximum_size(att[i]->atttypid,
- att[i]->atttypmod);
-
- if (maxlen < 0)
- maxlength_unknown = true;
- else
- data_length += maxlen;
- if (att[i]->attstorage != 'p')
- has_toastable_attrs = true;
- }
- }
- if (!has_toastable_attrs)
- return false; /* nothing to toast? */
- if (maxlength_unknown)
- return true; /* any unlimited-length attrs? */
- tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
- BITMAPLEN(tupdesc->natts)) +
- MAXALIGN(data_length);
- return (tuple_length > TOAST_TUPLE_THRESHOLD);
-}
-
-/*
- * LOCK TABLE
- */
-void
-LockTableCommand(LockStmt *lockstmt)
-{
- List *p;
-
- /*
- * Iterate over the list and open, lock, and close the relations one
- * at a time
- */
-
- foreach(p, lockstmt->relations)
- {
- RangeVar *relation = lfirst(p);
- Oid reloid;
- int32 aclresult;
- Relation rel;
-
- /*
- * We don't want to open the relation until we've checked privilege.
- * So, manually get the relation OID.
- */
- reloid = RangeVarGetRelid(relation, false);
-
- if (lockstmt->mode == AccessShareLock)
- aclresult = pg_class_aclcheck(reloid, GetUserId(),
- ACL_SELECT);
- else
- aclresult = pg_class_aclcheck(reloid, GetUserId(),
- ACL_UPDATE | ACL_DELETE);
-
- if (aclresult != ACLCHECK_OK)
- elog(ERROR, "LOCK TABLE: permission denied");
-
- rel = relation_open(reloid, lockstmt->mode);
-
- /* Currently, we only allow plain tables to be locked */
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "LOCK TABLE: %s is not a table",
- relation->relname);
-
- relation_close(rel, NoLock); /* close rel, keep lock */
- }
-}
-
-
-/*
- * CREATE SCHEMA
- */
-void
-CreateSchemaCommand(CreateSchemaStmt *stmt)
-{
- const char *schemaName = stmt->schemaname;
- const char *authId = stmt->authid;
- List *parsetree_list;
- List *parsetree_item;
- const char *owner_name;
- Oid owner_userid;
- Oid saved_userid;
-
- saved_userid = GetUserId();
-
- if (!authId)
- {
- owner_userid = saved_userid;
- owner_name = GetUserName(owner_userid);
- }
- else if (superuser())
- {
- owner_name = authId;
- /* The following will error out if user does not exist */
- owner_userid = get_usesysid(owner_name);
- /*
- * Set the current user to the requested authorization so
- * that objects created in the statement have the requested
- * owner. (This will revert to session user on error or at
- * the end of this routine.)
- */
- SetUserId(owner_userid);
- }
- else /* not superuser */
- {
- owner_userid = saved_userid;
- owner_name = GetUserName(owner_userid);
- if (strcmp(authId, owner_name) != 0)
- elog(ERROR, "CREATE SCHEMA: permission denied"
- "\n\t\"%s\" is not a superuser, so cannot create a schema for \"%s\"",
- owner_name, authId);
- }
-
- if (!allowSystemTableMods && IsReservedName(schemaName))
- elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas",
- schemaName);
-
- /* Create the schema's namespace */
- NamespaceCreate(schemaName, owner_userid);
-
- /* Let commands in the schema-element-list know about the schema */
- CommandCounterIncrement();
-
- /*
- * Examine the list of commands embedded in the CREATE SCHEMA command,
- * and reorganize them into a sequentially executable order with no
- * forward references. Note that the result is still a list of raw
- * parsetrees in need of parse analysis --- we cannot, in general,
- * run analyze.c on one statement until we have actually executed the
- * prior ones.
- */
- parsetree_list = analyzeCreateSchemaStmt(stmt);
-
- /*
- * Analyze and execute each command contained in the CREATE SCHEMA
- */
- foreach(parsetree_item, parsetree_list)
- {
- Node *parsetree = (Node *) lfirst(parsetree_item);
- List *querytree_list,
- *querytree_item;
-
- querytree_list = parse_analyze(parsetree, NULL);
-
- foreach(querytree_item, querytree_list)
- {
- Query *querytree = (Query *) lfirst(querytree_item);
-
- /* schemas should contain only utility stmts */
- Assert(querytree->commandType == CMD_UTILITY);
- /* do this step */
- ProcessUtility(querytree->utilityStmt, None, NULL);
- /* make sure later steps can see the object created here */
- CommandCounterIncrement();
- }
- }
-
- /* Reset current user */
- SetUserId(saved_userid);
-}