]> granicus.if.org Git - postgresql/commitdiff
The contents of command.c, creatinh.c, define.c, remove.c and rename.c
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 15 Apr 2002 05:22:04 +0000 (05:22 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 15 Apr 2002 05:22:04 +0000 (05:22 +0000)
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>

29 files changed:
src/backend/commands/Makefile
src/backend/commands/aggregatecmds.c [new file with mode: 0644]
src/backend/commands/cluster.c
src/backend/commands/creatinh.c [deleted file]
src/backend/commands/define.c
src/backend/commands/functioncmds.c [new file with mode: 0644]
src/backend/commands/lockcmds.c [new file with mode: 0644]
src/backend/commands/operatorcmds.c [new file with mode: 0644]
src/backend/commands/portalcmds.c [new file with mode: 0644]
src/backend/commands/proclang.c
src/backend/commands/remove.c [deleted file]
src/backend/commands/rename.c [deleted file]
src/backend/commands/schemacmds.c [new file with mode: 0644]
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c [moved from src/backend/commands/command.c with 52% similarity]
src/backend/commands/typecmds.c [new file with mode: 0644]
src/backend/commands/view.c
src/backend/executor/execMain.c
src/backend/executor/spi.c
src/backend/tcop/pquery.c
src/backend/tcop/utility.c
src/include/commands/command.h [deleted file]
src/include/commands/creatinh.h [deleted file]
src/include/commands/defrem.h
src/include/commands/lockcmds.h [new file with mode: 0644]
src/include/commands/portalcmds.h [new file with mode: 0644]
src/include/commands/rename.h [deleted file]
src/include/commands/schemacmds.h [new file with mode: 0644]
src/include/commands/tablecmds.h [new file with mode: 0644]

index b27e6d77b2abeddfbc7d16faa3fbabe3dc6b115e..190b0fd64f64d75290ce2767864f39c1e606f26f 100644 (file)
@@ -1,10 +1,10 @@
 #-------------------------------------------------------------------------
 #
 # Makefile--
-#    Makefile for commands
+#    Makefile for backend/commands
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.27 2001/07/13 22:55:59 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.28 2002/04/15 05:22:03 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,10 +12,11 @@ subdir = src/backend/commands
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = async.o creatinh.o command.o comment.o copy.o indexcmds.o define.o \
-       remove.o rename.o vacuum.o vacuumlazy.o analyze.o view.o cluster.o \
-       explain.o sequence.o trigger.o user.o proclang.o \
-       dbcommands.o variable.o
+OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o copy.o \
+       dbcommands.o define.o explain.o functioncmds.o \
+       indexcmds.o lockcmds.o operatorcmds.o portalcmds.o proclang.o \
+       schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \
+       vacuum.o vacuumlazy.o variable.o view.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
new file mode 100644 (file)
index 0000000..7ee0c8f
--- /dev/null
@@ -0,0 +1,208 @@
+/*-------------------------------------------------------------------------
+ *
+ * aggregatecmds.c
+ *
+ *       Routines for aggregate-manipulation commands
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ * DESCRIPTION
+ *       The "DefineFoo" routines take the parse tree and pick out the
+ *       appropriate arguments/flags, passing the results to the
+ *       corresponding "FooDefine" routines (in src/catalog) that do
+ *       the actual catalog-munging.  These routines also verify permission
+ *       of the user to execute the command.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_aggregate.h"
+#include "commands/comment.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse_func.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+/*
+ *     DefineAggregate
+ */
+void
+DefineAggregate(List *names, List *parameters)
+{
+       char       *aggName;
+       Oid                     aggNamespace;
+       List       *transfuncName = NIL;
+       List       *finalfuncName = NIL;
+       TypeName   *baseType = NULL;
+       TypeName   *transType = NULL;
+       char       *initval = NULL;
+       Oid                     baseTypeId;
+       Oid                     transTypeId;
+       List       *pl;
+
+       /* Convert list of names to a name and namespace */
+       aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
+
+       foreach(pl, parameters)
+       {
+               DefElem    *defel = (DefElem *) lfirst(pl);
+
+               /*
+                * sfunc1, stype1, and initcond1 are accepted as obsolete
+                * spellings for sfunc, stype, initcond.
+                */
+               if (strcasecmp(defel->defname, "sfunc") == 0)
+                       transfuncName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "sfunc1") == 0)
+                       transfuncName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "finalfunc") == 0)
+                       finalfuncName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "basetype") == 0)
+                       baseType = defGetTypeName(defel);
+               else if (strcasecmp(defel->defname, "stype") == 0)
+                       transType = defGetTypeName(defel);
+               else if (strcasecmp(defel->defname, "stype1") == 0)
+                       transType = defGetTypeName(defel);
+               else if (strcasecmp(defel->defname, "initcond") == 0)
+                       initval = defGetString(defel);
+               else if (strcasecmp(defel->defname, "initcond1") == 0)
+                       initval = defGetString(defel);
+               else
+                       elog(WARNING, "DefineAggregate: attribute \"%s\" not recognized",
+                                defel->defname);
+       }
+
+       /*
+        * make sure we have our required definitions
+        */
+       if (baseType == NULL)
+               elog(ERROR, "Define: \"basetype\" unspecified");
+       if (transType == NULL)
+               elog(ERROR, "Define: \"stype\" unspecified");
+       if (transfuncName == NIL)
+               elog(ERROR, "Define: \"sfunc\" unspecified");
+
+       /*
+        * Handle the aggregate's base type (input data type).  This can be
+        * specified as 'ANY' for a data-independent transition function, such
+        * as COUNT(*).
+        */
+       baseTypeId = LookupTypeName(baseType);
+       if (OidIsValid(baseTypeId))
+       {
+               /* no need to allow aggregates on as-yet-undefined types */
+               if (!get_typisdefined(baseTypeId))
+                       elog(ERROR, "Type \"%s\" is only a shell",
+                                TypeNameToString(baseType));
+       }
+       else
+       {
+               char      *typnam = TypeNameToString(baseType);
+
+               if (strcasecmp(typnam, "ANY") != 0)
+                       elog(ERROR, "Type \"%s\" does not exist", typnam);
+               baseTypeId = InvalidOid;
+       }
+
+       /* handle transtype --- no special cases here */
+       transTypeId = typenameTypeId(transType);
+
+       /*
+        * Most of the argument-checking is done inside of AggregateCreate
+        */
+       AggregateCreate(aggName,        /* aggregate name */
+                                       aggNamespace,   /* namespace */
+                                       transfuncName,          /* step function name */
+                                       finalfuncName,          /* final function name */
+                                       baseTypeId,     /* type of data being aggregated */
+                                       transTypeId,    /* transition data type */
+                                       initval);       /* initial condition */
+}
+
+
+void
+RemoveAggregate(List *aggName, TypeName *aggType)
+{
+       Relation        relation;
+       HeapTuple       tup;
+       Oid                     basetypeID;
+       Oid                     procOid;
+
+       /*
+        * if a basetype is passed in, then attempt to find an aggregate for
+        * that specific type.
+        *
+        * else if the basetype is blank, then attempt to find an aggregate with
+        * a basetype of zero.  This is valid. It means that the aggregate is
+        * to apply to all basetypes (eg, COUNT).
+        */
+       if (aggType)
+               basetypeID = typenameTypeId(aggType);
+       else
+               basetypeID = InvalidOid;
+
+       procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
+
+       /* Permission check */
+
+       if (!pg_proc_ownercheck(procOid, GetUserId()))
+       {
+               if (basetypeID == InvalidOid)
+                       elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied",
+                                NameListToString(aggName));
+               else
+                       elog(ERROR, "RemoveAggregate: aggregate %s for type %s: permission denied",
+                                NameListToString(aggName), format_type_be(basetypeID));
+       }
+
+       /* Remove the pg_proc tuple */
+
+       relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
+
+       tup = SearchSysCache(PROCOID,
+                                                ObjectIdGetDatum(procOid),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup))     /* should not happen */
+               elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s",
+                        NameListToString(aggName));
+
+       /* Delete any comments associated with this function */
+       DeleteComments(procOid, RelationGetRelid(relation));
+
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       heap_close(relation, RowExclusiveLock);
+
+       /* Remove the pg_aggregate tuple */
+
+       relation = heap_openr(AggregateRelationName, RowExclusiveLock);
+
+       tup = SearchSysCache(AGGFNOID,
+                                                ObjectIdGetDatum(procOid),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup))     /* should not happen */
+               elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s",
+                        NameListToString(aggName));
+
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       heap_close(relation, RowExclusiveLock);
+}
index aad5361b6b837d085c4ed7b8df261ece8b8b21ee..8883eddc49f7d0c9342507e2d063f14031dfdae4 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.77 2002/03/31 07:49:30 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.78 2002/04/15 05:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,8 +29,7 @@
 #include "catalog/pg_index.h"
 #include "catalog/pg_proc.h"
 #include "commands/cluster.h"
-#include "commands/command.h"
-#include "commands/rename.h"
+#include "commands/tablecmds.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c
deleted file mode 100644 (file)
index 6a0cf81..0000000
+++ /dev/null
@@ -1,932 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * creatinh.c
- *       POSTGRES create/destroy relation with inheritance utility 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/creatinh.c,v 1.96 2002/04/12 20:38:22 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/catalog.h"
-#include "catalog/catname.h"
-#include "catalog/heap.h"
-#include "catalog/indexing.h"
-#include "catalog/namespace.h"
-#include "catalog/pg_inherits.h"
-#include "catalog/pg_type.h"
-#include "commands/creatinh.h"
-#include "miscadmin.h"
-#include "optimizer/clauses.h"
-#include "parser/parse_type.h"
-#include "utils/acl.h"
-#include "utils/syscache.h"
-
-
-/* ----------------
- *             local stuff
- * ----------------
- */
-
-static List *MergeAttributes(List *schema, List *supers, bool istemp,
-                               List **supOids, List **supconstr, bool *supHasOids);
-static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
-static void StoreCatalogInheritance(Oid relationId, List *supers);
-static int     findAttrByName(const char *attributeName, List *schema);
-static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
-static List *MergeDomainAttributes(List *schema);
-
-
-/* ----------------------------------------------------------------
- *             DefineRelation
- *                             Creates a new relation.
- *
- * If successful, returns the OID of the new relation.
- * ----------------------------------------------------------------
- */
-Oid
-DefineRelation(CreateStmt *stmt, char relkind)
-{
-       char       *relname = palloc(NAMEDATALEN);
-       Oid                     namespaceId;
-       List       *schema = stmt->tableElts;
-       int                     numberOfAttributes;
-       Oid                     relationId;
-       Relation        rel;
-       TupleDesc       descriptor;
-       List       *inheritOids;
-       List       *old_constraints;
-       bool            parentHasOids;
-       List       *rawDefaults;
-       List       *listptr;
-       int                     i;
-       AttrNumber      attnum;
-
-       /*
-        * Truncate relname to appropriate length (probably a waste of time,
-        * as parser should have done this already).
-        */
-       StrNCpy(relname, (stmt->relation)->relname, NAMEDATALEN);
-
-       /*
-        * Look up the namespace in which we are supposed to create the
-        * relation.
-        */
-       namespaceId = RangeVarGetCreationNamespace(stmt->relation);
-
-       /*
-        * Merge domain attributes into the known columns before processing table
-        * inheritance.  Otherwise we risk adding double constraints to a
-        * domain-type column that's inherited.
-        */
-       schema = MergeDomainAttributes(schema);
-
-       /*
-        * Look up inheritance ancestors and generate relation schema,
-        * including inherited attributes.
-        */
-       schema = MergeAttributes(schema, stmt->inhRelations,
-                                                        stmt->relation->istemp,
-                                                        &inheritOids, &old_constraints, &parentHasOids);
-
-       numberOfAttributes = length(schema);
-       if (numberOfAttributes <= 0)
-               elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute");
-
-       /*
-        * Create a relation descriptor from the relation schema and create
-        * the relation.  Note that in this stage only inherited (pre-cooked)
-        * defaults and constraints will be included into the new relation.
-        * (BuildDescForRelation takes care of the inherited defaults, but we
-        * have to copy inherited constraints here.)
-        */
-       descriptor = BuildDescForRelation(schema);
-
-       if (old_constraints != NIL)
-       {
-               ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
-                                                                                                       sizeof(ConstrCheck));
-               int                     ncheck = 0;
-
-               foreach(listptr, old_constraints)
-               {
-                       Constraint *cdef = (Constraint *) lfirst(listptr);
-
-                       if (cdef->contype != CONSTR_CHECK)
-                               continue;
-
-                       if (cdef->name != NULL)
-                       {
-                               for (i = 0; i < ncheck; i++)
-                               {
-                                       if (strcmp(check[i].ccname, cdef->name) == 0)
-                                               elog(ERROR, "Duplicate CHECK constraint name: '%s'",
-                                                        cdef->name);
-                               }
-                               check[ncheck].ccname = cdef->name;
-                       }
-                       else
-                       {
-                               check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
-                               snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
-                       }
-                       Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
-                       check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
-                       ncheck++;
-               }
-               if (ncheck > 0)
-               {
-                       if (descriptor->constr == NULL)
-                       {
-                               descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
-                               descriptor->constr->defval = NULL;
-                               descriptor->constr->num_defval = 0;
-                               descriptor->constr->has_not_null = false;
-                       }
-                       descriptor->constr->num_check = ncheck;
-                       descriptor->constr->check = check;
-               }
-       }
-
-       relationId = heap_create_with_catalog(relname,
-                                                                                 namespaceId,
-                                                                                 descriptor,
-                                                                                 relkind,
-                                                                                 stmt->hasoids || parentHasOids,
-                                                                                 allowSystemTableMods);
-
-       StoreCatalogInheritance(relationId, inheritOids);
-
-       /*
-        * We must bump the command counter to make the newly-created relation
-        * tuple visible for opening.
-        */
-       CommandCounterIncrement();
-
-       /*
-        * Open the new relation and acquire exclusive lock on it.      This isn't
-        * really necessary for locking out other backends (since they can't
-        * see the new rel anyway until we commit), but it keeps the lock
-        * manager from complaining about deadlock risks.
-        */
-       rel = heap_open(relationId, AccessExclusiveLock);
-
-       /*
-        * Now add any newly specified column default values and CHECK
-        * constraints to the new relation.  These are passed to us in the
-        * form of raw parsetrees; we need to transform them to executable
-        * expression trees before they can be added. The most convenient way
-        * to do that is to apply the parser's transformExpr routine, but
-        * transformExpr doesn't work unless we have a pre-existing relation.
-        * So, the transformation has to be postponed to this final step of
-        * CREATE TABLE.
-        *
-        * First, scan schema to find new column defaults.
-        */
-       rawDefaults = NIL;
-       attnum = 0;
-
-       foreach(listptr, schema)
-       {
-               ColumnDef  *colDef = lfirst(listptr);
-               RawColumnDefault *rawEnt;
-
-               attnum++;
-
-               if (colDef->raw_default == NULL)
-                       continue;
-               Assert(colDef->cooked_default == NULL);
-
-               rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
-               rawEnt->attnum = attnum;
-               rawEnt->raw_default = colDef->raw_default;
-               rawDefaults = lappend(rawDefaults, rawEnt);
-       }
-
-       /*
-        * Parse and add the defaults/constraints, if any.
-        */
-       if (rawDefaults || stmt->constraints)
-               AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
-
-       /*
-        * Clean up.  We keep lock on new relation (although it shouldn't be
-        * visible to anyone else anyway, until commit).
-        */
-       heap_close(rel, NoLock);
-
-       return relationId;
-}
-
-/*
- * RemoveRelation
- *             Deletes a relation.
- *
- * Exceptions:
- *             BadArg if name is invalid.
- *
- * Note:
- *             If the relation has indices defined on it, then the index relations
- * themselves will be destroyed, too.
- */
-void
-RemoveRelation(const RangeVar *relation)
-{
-       Oid                     relOid;
-
-       relOid = RangeVarGetRelid(relation, false);
-       heap_drop_with_catalog(relOid, allowSystemTableMods);
-}
-
-/*
- * TruncateRelation
- *                               Removes all the rows from a relation
- *
- * Exceptions:
- *                               BadArg if name is invalid
- *
- * Note:
- *                               Rows are removed, indices are truncated and reconstructed.
- */
-void
-TruncateRelation(const RangeVar *relation)
-{
-       Relation        rel;
-       Oid                     relid;
-
-       /* Grab exclusive lock in preparation for truncate */
-       rel = heap_openrv(relation, AccessExclusiveLock);
-       relid = RelationGetRelid(rel);
-
-       if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
-               elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
-                        RelationGetRelationName(rel));
-
-       if (rel->rd_rel->relkind == RELKIND_VIEW)
-               elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
-                        RelationGetRelationName(rel));
-
-       if (!allowSystemTableMods && IsSystemRelation(rel))
-               elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
-                        RelationGetRelationName(rel));
-
-       if (!pg_class_ownercheck(relid, GetUserId()))
-               elog(ERROR, "you do not own relation \"%s\"",
-                        RelationGetRelationName(rel));
-
-       /* Keep the lock until transaction commit */
-       heap_close(rel, NoLock);
-
-       heap_truncate(relid);
-}
-
-
-/*
- * MergeDomainAttributes
- *      Returns a new table schema with the constraints, types, and other
- *      attributes of domains resolved for fields using a domain as
- *      their type.
- */
-static List *
-MergeDomainAttributes(List *schema)
-{
-       List       *entry;
-
-       /*
-        * Loop through the table elements supplied. These should
-        * never include inherited domains else they'll be
-        * double (or more) processed.
-        */
-       foreach(entry, schema)
-       {
-               ColumnDef  *coldef = lfirst(entry);
-               HeapTuple  tuple;
-               Form_pg_type typeTup;
-
-               tuple = typenameType(coldef->typename);
-               typeTup = (Form_pg_type) GETSTRUCT(tuple);
-
-               if (typeTup->typtype == 'd')
-               {
-                       /* Force the column to have the correct typmod. */
-                       coldef->typename->typmod = typeTup->typtypmod;
-                       /* XXX more to do here? */
-               }
-
-               /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
-               /* Currently only used for domains, but could be valid for all */
-               coldef->is_not_null |= typeTup->typnotnull;
-
-               ReleaseSysCache(tuple);
-       }
-
-       return schema;
-}
-
-/*----------
- * MergeAttributes
- *             Returns new schema given initial schema and superclasses.
- *
- * Input arguments:
- * 'schema' is the column/attribute definition for the table. (It's a list
- *             of ColumnDef's.) It is destructively changed.
- * 'supers' is a list of names (as RangeVar nodes) of parent relations.
- * 'istemp' is TRUE if we are creating a temp relation.
- *
- * Output arguments:
- * 'supOids' receives an integer list of the OIDs of the parent relations.
- * 'supconstr' receives a list of constraints belonging to the parents,
- *             updated as necessary to be valid for the child.
- * 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE.
- *
- * Return value:
- * Completed schema list.
- *
- * Notes:
- *       The order in which the attributes are inherited is very important.
- *       Intuitively, the inherited attributes should come first. If a table
- *       inherits from multiple parents, the order of those attributes are
- *       according to the order of the parents specified in CREATE TABLE.
- *
- *       Here's an example:
- *
- *             create table person (name text, age int4, location point);
- *             create table emp (salary int4, manager text) inherits(person);
- *             create table student (gpa float8) inherits (person);
- *             create table stud_emp (percent int4) inherits (emp, student);
- *
- *       The order of the attributes of stud_emp is:
- *
- *                                                     person {1:name, 2:age, 3:location}
- *                                                     /        \
- *                        {6:gpa}      student   emp {4:salary, 5:manager}
- *                                                     \        /
- *                                                stud_emp {7:percent}
- *
- *        If the same attribute name appears multiple times, then it appears
- *        in the result table in the proper location for its first appearance.
- *
- *        Constraints (including NOT NULL constraints) for the child table
- *        are the union of all relevant constraints, from both the child schema
- *        and parent tables.
- *
- *        The default value for a child column is defined as:
- *             (1) If the child schema specifies a default, that value is used.
- *             (2) If neither the child nor any parent specifies a default, then
- *                     the column will not have a default.
- *             (3) If conflicting defaults are inherited from different parents
- *                     (and not overridden by the child), an error is raised.
- *             (4) Otherwise the inherited default is used.
- *             Rule (3) is new in Postgres 7.1; in earlier releases you got a
- *             rather arbitrary choice of which parent default to use.
- *----------
- */
-static List *
-MergeAttributes(List *schema, List *supers, bool istemp,
-                               List **supOids, List **supconstr, bool *supHasOids)
-{
-       List       *entry;
-       List       *inhSchema = NIL;
-       List       *parentOids = NIL;
-       List       *constraints = NIL;
-       bool            parentHasOids = false;
-       bool            have_bogus_defaults = false;
-       char       *bogus_marker = "Bogus!";            /* marks conflicting
-                                                                                                * defaults */
-       int                     child_attno;
-
-       /*
-        * Check for duplicate names in the explicit list of attributes.
-        *
-        * Although we might consider merging such entries in the same way that
-        * we handle name conflicts for inherited attributes, it seems to make
-        * more sense to assume such conflicts are errors.
-        */
-       foreach(entry, schema)
-       {
-               ColumnDef  *coldef = lfirst(entry);
-               List       *rest;
-
-               foreach(rest, lnext(entry))
-               {
-                       ColumnDef  *restdef = lfirst(rest);
-
-                       if (strcmp(coldef->colname, restdef->colname) == 0)
-                               elog(ERROR, "CREATE TABLE: attribute \"%s\" duplicated",
-                                        coldef->colname);
-               }
-       }
-
-       /*
-        * Scan the parents left-to-right, and merge their attributes to form
-        * a list of inherited attributes (inhSchema).  Also check to see if
-        * we need to inherit an OID column.
-        */
-       child_attno = 0;
-       foreach(entry, supers)
-       {
-               RangeVar   *parent = (RangeVar *) lfirst(entry);
-               Relation        relation;
-               TupleDesc       tupleDesc;
-               TupleConstr *constr;
-               AttrNumber *newattno;
-               AttrNumber      parent_attno;
-
-               relation = heap_openrv(parent, AccessShareLock);
-
-               if (relation->rd_rel->relkind != RELKIND_RELATION)
-                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" is not a table",
-                                parent->relname);
-               /* Permanent rels cannot inherit from temporary ones */
-               if (!istemp && isTempNamespace(RelationGetNamespace(relation)))
-                       elog(ERROR, "CREATE TABLE: cannot inherit from temp relation \"%s\"",
-                                parent->relname);
-
-               /*
-                * We should have an UNDER permission flag for this, but for now,
-                * demand that creator of a child table own the parent.
-                */
-               if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
-                       elog(ERROR, "you do not own table \"%s\"",
-                                parent->relname);
-
-               /*
-                * Reject duplications in the list of parents.
-                */
-               if (intMember(RelationGetRelid(relation), parentOids))
-                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated",
-                                parent->relname);
-
-               parentOids = lappendi(parentOids, RelationGetRelid(relation));
-               setRelhassubclassInRelation(RelationGetRelid(relation), true);
-
-               parentHasOids |= relation->rd_rel->relhasoids;
-
-               tupleDesc = RelationGetDescr(relation);
-               constr = tupleDesc->constr;
-
-               /*
-                * newattno[] will contain the child-table attribute numbers for
-                * the attributes of this parent table.  (They are not the same
-                * for parents after the first one.)
-                */
-               newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber));
-
-               for (parent_attno = 1; parent_attno <= tupleDesc->natts;
-                        parent_attno++)
-               {
-                       Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
-                       char       *attributeName = NameStr(attribute->attname);
-                       int                     exist_attno;
-                       ColumnDef  *def;
-                       TypeName   *typename;
-
-                       /*
-                        * Does it conflict with some previously inherited column?
-                        */
-                       exist_attno = findAttrByName(attributeName, inhSchema);
-                       if (exist_attno > 0)
-                       {
-                               /*
-                                * Yes, try to merge the two column definitions. They must
-                                * have the same type and typmod.
-                                */
-                               elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"",
-                                        attributeName);
-                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
-                               if (typenameTypeId(def->typename) != attribute->atttypid ||
-                                       def->typename->typmod != attribute->atttypmod)
-                                       elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)",
-                                                attributeName,
-                                                TypeNameToString(def->typename),
-                                                typeidTypeName(attribute->atttypid));
-                               /* Merge of NOT NULL constraints = OR 'em together */
-                               def->is_not_null |= attribute->attnotnull;
-                               /* Default and other constraints are handled below */
-                               newattno[parent_attno - 1] = exist_attno;
-                       }
-                       else
-                       {
-                               /*
-                                * No, create a new inherited column
-                                */
-                               def = makeNode(ColumnDef);
-                               def->colname = pstrdup(attributeName);
-                               typename = makeNode(TypeName);
-                               typename->typeid = attribute->atttypid;
-                               typename->typmod = attribute->atttypmod;
-                               def->typename = typename;
-                               def->is_not_null = attribute->attnotnull;
-                               def->raw_default = NULL;
-                               def->cooked_default = NULL;
-                               def->constraints = NIL;
-                               inhSchema = lappend(inhSchema, def);
-                               newattno[parent_attno - 1] = ++child_attno;
-                       }
-
-                       /*
-                        * Copy default if any
-                        */
-                       if (attribute->atthasdef)
-                       {
-                               char       *this_default = NULL;
-                               AttrDefault *attrdef;
-                               int                     i;
-
-                               /* Find default in constraint structure */
-                               Assert(constr != NULL);
-                               attrdef = constr->defval;
-                               for (i = 0; i < constr->num_defval; i++)
-                               {
-                                       if (attrdef[i].adnum == parent_attno)
-                                       {
-                                               this_default = attrdef[i].adbin;
-                                               break;
-                                       }
-                               }
-                               Assert(this_default != NULL);
-
-                               /*
-                                * If default expr could contain any vars, we'd need to
-                                * fix 'em, but it can't; so default is ready to apply to
-                                * child.
-                                *
-                                * If we already had a default from some prior parent, check
-                                * to see if they are the same.  If so, no problem; if
-                                * not, mark the column as having a bogus default. Below,
-                                * we will complain if the bogus default isn't overridden
-                                * by the child schema.
-                                */
-                               Assert(def->raw_default == NULL);
-                               if (def->cooked_default == NULL)
-                                       def->cooked_default = pstrdup(this_default);
-                               else if (strcmp(def->cooked_default, this_default) != 0)
-                               {
-                                       def->cooked_default = bogus_marker;
-                                       have_bogus_defaults = true;
-                               }
-                       }
-               }
-
-               /*
-                * Now copy the constraints of this parent, adjusting attnos using
-                * the completed newattno[] map
-                */
-               if (constr && constr->num_check > 0)
-               {
-                       ConstrCheck *check = constr->check;
-                       int                     i;
-
-                       for (i = 0; i < constr->num_check; i++)
-                       {
-                               Constraint *cdef = makeNode(Constraint);
-                               Node       *expr;
-
-                               cdef->contype = CONSTR_CHECK;
-                               if (check[i].ccname[0] == '$')
-                                       cdef->name = NULL;
-                               else
-                                       cdef->name = pstrdup(check[i].ccname);
-                               cdef->raw_expr = NULL;
-                               /* adjust varattnos of ccbin here */
-                               expr = stringToNode(check[i].ccbin);
-                               change_varattnos_of_a_node(expr, newattno);
-                               cdef->cooked_expr = nodeToString(expr);
-                               constraints = lappend(constraints, cdef);
-                       }
-               }
-
-               pfree(newattno);
-
-               /*
-                * Close the parent rel, but keep our AccessShareLock on it until
-                * xact commit.  That will prevent someone else from deleting or
-                * ALTERing the parent before the child is committed.
-                */
-               heap_close(relation, NoLock);
-       }
-
-       /*
-        * If we had no inherited attributes, the result schema is just the
-        * explicitly declared columns.  Otherwise, we need to merge the
-        * declared columns into the inherited schema list.
-        */
-       if (inhSchema != NIL)
-       {
-               foreach(entry, schema)
-               {
-                       ColumnDef  *newdef = lfirst(entry);
-                       char       *attributeName = newdef->colname;
-                       int                     exist_attno;
-
-                       /*
-                        * Does it conflict with some previously inherited column?
-                        */
-                       exist_attno = findAttrByName(attributeName, inhSchema);
-                       if (exist_attno > 0)
-                       {
-                               ColumnDef  *def;
-
-                               /*
-                                * Yes, try to merge the two column definitions. They must
-                                * have the same type and typmod.
-                                */
-                               elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition",
-                                        attributeName);
-                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
-                               if (typenameTypeId(def->typename) != typenameTypeId(newdef->typename) ||
-                                       def->typename->typmod != newdef->typename->typmod)
-                                       elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)",
-                                                attributeName,
-                                                TypeNameToString(def->typename),
-                                                TypeNameToString(newdef->typename));
-                               /* Merge of NOT NULL constraints = OR 'em together */
-                               def->is_not_null |= newdef->is_not_null;
-                               /* If new def has a default, override previous default */
-                               if (newdef->raw_default != NULL)
-                               {
-                                       def->raw_default = newdef->raw_default;
-                                       def->cooked_default = newdef->cooked_default;
-                               }
-                       }
-                       else
-                       {
-                               /*
-                                * No, attach new column to result schema
-                                */
-                               inhSchema = lappend(inhSchema, newdef);
-                       }
-               }
-
-               schema = inhSchema;
-       }
-
-       /*
-        * If we found any conflicting parent default values, check to make
-        * sure they were overridden by the child.
-        */
-       if (have_bogus_defaults)
-       {
-               foreach(entry, schema)
-               {
-                       ColumnDef  *def = lfirst(entry);
-
-                       if (def->cooked_default == bogus_marker)
-                               elog(ERROR, "CREATE TABLE: attribute \"%s\" inherits conflicting default values"
-                                        "\n\tTo resolve the conflict, specify a default explicitly",
-                                        def->colname);
-               }
-       }
-
-       *supOids = parentOids;
-       *supconstr = constraints;
-       *supHasOids = parentHasOids;
-       return schema;
-}
-
-/*
- * complementary static functions for MergeAttributes().
- *
- * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
- * constraints from parent classes, since the inherited attributes could
- * be given different column numbers in multiple-inheritance cases.
- *
- * Note that the passed node tree is modified in place!
- */
-static bool
-change_varattnos_walker(Node *node, const AttrNumber *newattno)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
-
-               if (var->varlevelsup == 0 && var->varno == 1 &&
-                       var->varattno > 0)
-               {
-                       /*
-                        * ??? the following may be a problem when the node is
-                        * multiply referenced though stringToNode() doesn't create
-                        * such a node currently.
-                        */
-                       Assert(newattno[var->varattno - 1] > 0);
-                       var->varattno = newattno[var->varattno - 1];
-               }
-               return false;
-       }
-       return expression_tree_walker(node, change_varattnos_walker,
-                                                                 (void *) newattno);
-}
-
-static bool
-change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
-{
-       return change_varattnos_walker(node, newattno);
-}
-
-/*
- * StoreCatalogInheritance
- *             Updates the system catalogs with proper inheritance information.
- *
- * supers is an integer list of the OIDs of the new relation's direct
- * ancestors.  NB: it is destructively changed to include indirect ancestors.
- */
-static void
-StoreCatalogInheritance(Oid relationId, List *supers)
-{
-       Relation        relation;
-       TupleDesc       desc;
-       int16           seqNumber;
-       List       *entry;
-       HeapTuple       tuple;
-
-       /*
-        * sanity checks
-        */
-       AssertArg(OidIsValid(relationId));
-
-       if (supers == NIL)
-               return;
-
-       /*
-        * Catalog INHERITS information using direct ancestors only.
-        */
-       relation = heap_openr(InheritsRelationName, RowExclusiveLock);
-       desc = RelationGetDescr(relation);
-
-       seqNumber = 1;
-       foreach(entry, supers)
-       {
-               Oid                     entryOid = lfirsti(entry);
-               Datum           datum[Natts_pg_inherits];
-               char            nullarr[Natts_pg_inherits];
-
-               datum[0] = ObjectIdGetDatum(relationId);                /* inhrel */
-               datum[1] = ObjectIdGetDatum(entryOid);  /* inhparent */
-               datum[2] = Int16GetDatum(seqNumber);    /* inhseqno */
-
-               nullarr[0] = ' ';
-               nullarr[1] = ' ';
-               nullarr[2] = ' ';
-
-               tuple = heap_formtuple(desc, datum, nullarr);
-
-               heap_insert(relation, tuple);
-
-               if (RelationGetForm(relation)->relhasindex)
-               {
-                       Relation        idescs[Num_pg_inherits_indices];
-
-                       CatalogOpenIndices(Num_pg_inherits_indices, Name_pg_inherits_indices, idescs);
-                       CatalogIndexInsert(idescs, Num_pg_inherits_indices, relation, tuple);
-                       CatalogCloseIndices(Num_pg_inherits_indices, idescs);
-               }
-
-               heap_freetuple(tuple);
-
-               seqNumber += 1;
-       }
-
-       heap_close(relation, RowExclusiveLock);
-
-       /* ----------------
-        * Expand supers list to include indirect ancestors as well.
-        *
-        * Algorithm:
-        *      0. begin with list of direct superclasses.
-        *      1. append after each relationId, its superclasses, recursively.
-        *      2. remove all but last of duplicates.
-        * ----------------
-        */
-
-       /*
-        * 1. append after each relationId, its superclasses, recursively.
-        */
-       foreach(entry, supers)
-       {
-               HeapTuple       tuple;
-               Oid                     id;
-               int16           number;
-               List       *next;
-               List       *current;
-
-               id = (Oid) lfirsti(entry);
-               current = entry;
-               next = lnext(entry);
-
-               for (number = 1;; number += 1)
-               {
-                       tuple = SearchSysCache(INHRELID,
-                                                                  ObjectIdGetDatum(id),
-                                                                  Int16GetDatum(number),
-                                                                  0, 0);
-                       if (!HeapTupleIsValid(tuple))
-                               break;
-
-                       lnext(current) = lconsi(((Form_pg_inherits)
-                                                                        GETSTRUCT(tuple))->inhparent,
-                                                                       NIL);
-
-                       ReleaseSysCache(tuple);
-
-                       current = lnext(current);
-               }
-               lnext(current) = next;
-       }
-
-       /*
-        * 2. remove all but last of duplicates.
-        */
-       foreach(entry, supers)
-       {
-               Oid                     thisone;
-               bool            found;
-               List       *rest;
-
-again:
-               thisone = lfirsti(entry);
-               found = false;
-               foreach(rest, lnext(entry))
-               {
-                       if (thisone == lfirsti(rest))
-                       {
-                               found = true;
-                               break;
-                       }
-               }
-               if (found)
-               {
-                       /*
-                        * found a later duplicate, so remove this entry.
-                        */
-                       lfirsti(entry) = lfirsti(lnext(entry));
-                       lnext(entry) = lnext(lnext(entry));
-
-                       goto again;
-               }
-       }
-}
-
-/*
- * Look for an existing schema entry with the given name.
- *
- * Returns the index (starting with 1) if attribute already exists in schema,
- * 0 if it doesn't.
- */
-static int
-findAttrByName(const char *attributeName, List *schema)
-{
-       List       *s;
-       int                     i = 0;
-
-       foreach(s, schema)
-       {
-               ColumnDef  *def = lfirst(s);
-
-               ++i;
-               if (strcmp(attributeName, def->colname) == 0)
-                       return i;
-       }
-       return 0;
-}
-
-/*
- * Update a relation's pg_class.relhassubclass entry to the given value
- */
-static void
-setRelhassubclassInRelation(Oid relationId, bool relhassubclass)
-{
-       Relation        relationRelation;
-       HeapTuple       tuple;
-       Relation        idescs[Num_pg_class_indices];
-
-       /*
-        * Fetch a modifiable copy of the tuple, modify it, update pg_class.
-        */
-       relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
-       tuple = SearchSysCacheCopy(RELOID,
-                                                          ObjectIdGetDatum(relationId),
-                                                          0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "setRelhassubclassInRelation: cache lookup failed for relation %u", relationId);
-
-       ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass = relhassubclass;
-       simple_heap_update(relationRelation, &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, relationRelation, tuple);
-       CatalogCloseIndices(Num_pg_class_indices, idescs);
-
-       heap_freetuple(tuple);
-       heap_close(relationRelation, RowExclusiveLock);
-}
index 692fc9f957c4a389dd1536d1d2deb1c6c1c9f10c..889ddd0f44026a578d78c8cfb1d67d890cd0ec54 100644 (file)
@@ -1,16 +1,15 @@
 /*-------------------------------------------------------------------------
  *
  * define.c
+ *       Support routines for various kinds of object creation.
  *
- *       These routines execute some of the CREATE statements.  In an earlier
- *       version of Postgres, these were "define" statements.
  *
  * 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/define.c,v 1.75 2002/04/11 19:59:57 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.76 2002/04/15 05:22:03 tgl Exp $
  *
  * DESCRIPTION
  *       The "DefineFoo" routines take the parse tree and pick out the
@@ -28,8 +27,6 @@
  *             "create operator":
  *                             operators
  *
- *             Most of the parse-tree manipulation routines are defined in
- *             commands/manip.c.
  *
  *-------------------------------------------------------------------------
  */
 #include <ctype.h>
 #include <math.h>
 
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/heap.h"
-#include "catalog/namespace.h"
-#include "catalog/pg_aggregate.h"
-#include "catalog/pg_language.h"
-#include "catalog/pg_operator.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
 #include "commands/defrem.h"
-#include "fmgr.h"
-#include "miscadmin.h"
-#include "optimizer/cost.h"
-#include "parser/parse_func.h"
 #include "parser/parse_type.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-
-
-static Oid     findTypeIOFunction(List *procname, bool isOutput);
-static char *defGetString(DefElem *def);
-static double defGetNumeric(DefElem *def);
-static List *defGetQualifiedName(DefElem *def);
-static TypeName *defGetTypeName(DefElem *def);
-static int     defGetTypeLength(DefElem *def);
-
-#define DEFAULT_TYPDELIM               ','
 
 
 /*
  * Translate the input language name to lower case.
+ *
+ * Output buffer should be NAMEDATALEN long.
  */
-static void
+void
 case_translate_language_name(const char *input, char *output)
 {
        int                     i;
@@ -86,1021 +57,9 @@ case_translate_language_name(const char *input, char *output)
 
 
 /*
- *      Examine the "returns" clause returnType of the CREATE FUNCTION statement
- *      and return information about it as *prorettype_p and *returnsSet.
- *
- * This is more complex than the average typename lookup because we want to
- * allow a shell type to be used, or even created if the specified return type
- * doesn't exist yet.  (Without this, there's no way to define the I/O procs
- * for a new type.)  But SQL function creation won't cope, so error out if
- * the target language is SQL.
- */
-static void
-compute_return_type(TypeName *returnType, Oid languageOid,
-                                       Oid *prorettype_p, bool *returnsSet_p)
-{
-       Oid             rettype;
-
-       rettype = LookupTypeName(returnType);
-
-       if (OidIsValid(rettype))
-       {
-               if (!get_typisdefined(rettype))
-               {
-                       if (languageOid == SQLlanguageId)
-                               elog(ERROR, "SQL functions cannot return shell types");
-                       else
-                               elog(WARNING, "Return type \"%s\" is only a shell",
-                                        TypeNameToString(returnType));
-               }
-       }
-       else
-       {
-               char      *typnam = TypeNameToString(returnType);
-
-               if (strcmp(typnam, "opaque") == 0)
-                       rettype = InvalidOid;
-               else
-               {
-                       Oid                     namespaceId;
-                       char       *typname;
-
-                       if (languageOid == SQLlanguageId)
-                               elog(ERROR, "Type \"%s\" does not exist", typnam);
-                       elog(WARNING, "ProcedureCreate: type %s is not yet defined",
-                                typnam);
-                       namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
-                                                                                                                       &typname);
-                       rettype = TypeShellMake(typname, namespaceId);
-                       if (!OidIsValid(rettype))
-                               elog(ERROR, "could not create type %s", typnam);
-               }
-       }
-
-       *prorettype_p = rettype;
-       *returnsSet_p = returnType->setof;
-}
-
-/*
- * Interpret the argument-types list of the CREATE FUNCTION statement.
- */
-static int
-compute_parameter_types(List *argTypes, Oid languageOid,
-                                               Oid *parameterTypes)
-{
-       int                     parameterCount = 0;
-       List       *x;
-
-       MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid));
-       foreach(x, argTypes)
-       {
-               TypeName   *t = (TypeName *) lfirst(x);
-               Oid                     toid;
-
-               if (parameterCount >= FUNC_MAX_ARGS)
-                       elog(ERROR, "functions cannot have more than %d arguments",
-                                FUNC_MAX_ARGS);
-
-               toid = LookupTypeName(t);
-               if (OidIsValid(toid))
-               {
-                       if (!get_typisdefined(toid))
-                               elog(WARNING, "Argument type \"%s\" is only a shell",
-                                        TypeNameToString(t));
-               }
-               else
-               {
-                       char      *typnam = TypeNameToString(t);
-
-                       if (strcmp(typnam, "opaque") == 0)
-                       {
-                               if (languageOid == SQLlanguageId)
-                                       elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
-                               toid = InvalidOid;
-                       }
-                       else
-                               elog(ERROR, "Type \"%s\" does not exist", typnam);
-               }
-
-               if (t->setof)
-                       elog(ERROR, "functions cannot accept set arguments");
-
-               parameterTypes[parameterCount++] = toid;
-       }
-
-       return parameterCount;
-}
-
-/*-------------
- *      Interpret the parameters *parameters and return their contents as
- *      *byte_pct_p, etc.
- *
- *     These parameters supply optional information about a function.
- *     All have defaults if not specified.
- *
- *     Note: currently, only three of these parameters actually do anything:
- *
- *      * isImplicit means the function may be used as an implicit type
- *        coercion.
- *
- *      * isStrict means the function should not be called when any NULL
- *        inputs are present; instead a NULL result value should be assumed.
- *
- *      * volatility tells the optimizer whether the function's result can
- *        be assumed to be repeatable over multiple evaluations.
- *
- *     The other four parameters are not used anywhere.        They used to be
- *     used in the "expensive functions" optimizer, but that's been dead code
- *     for a long time.
- *------------
- */
-static void
-compute_full_attributes(List *parameters,
-                                               int32 *byte_pct_p, int32 *perbyte_cpu_p,
-                                               int32 *percall_cpu_p, int32 *outin_ratio_p,
-                                               bool *isImplicit_p, bool *isStrict_p,
-                                               char *volatility_p)
-{
-       List       *pl;
-
-       /* the defaults */
-       *byte_pct_p = BYTE_PCT;
-       *perbyte_cpu_p = PERBYTE_CPU;
-       *percall_cpu_p = PERCALL_CPU;
-       *outin_ratio_p = OUTIN_RATIO;
-       *isImplicit_p = false;
-       *isStrict_p = false;
-       *volatility_p = PROVOLATILE_VOLATILE;
-
-       foreach(pl, parameters)
-       {
-               DefElem    *param = (DefElem *) lfirst(pl);
-
-               if (strcasecmp(param->defname, "implicitcoercion") == 0)
-                       *isImplicit_p = true;
-               else if (strcasecmp(param->defname, "isstrict") == 0)
-                       *isStrict_p = true;
-               else if (strcasecmp(param->defname, "isimmutable") == 0)
-                       *volatility_p = PROVOLATILE_IMMUTABLE;
-               else if (strcasecmp(param->defname, "isstable") == 0)
-                       *volatility_p = PROVOLATILE_STABLE;
-               else if (strcasecmp(param->defname, "isvolatile") == 0)
-                       *volatility_p = PROVOLATILE_VOLATILE;
-               else if (strcasecmp(param->defname, "iscachable") == 0)
-               {
-                       /* obsolete spelling of isImmutable */
-                       *volatility_p = PROVOLATILE_IMMUTABLE;
-               }
-               else if (strcasecmp(param->defname, "trusted") == 0)
-               {
-                       /*
-                        * we don't have untrusted functions any more. The 4.2
-                        * implementation is lousy anyway so I took it out. -ay 10/94
-                        */
-                       elog(ERROR, "untrusted function has been decommissioned.");
-               }
-               else if (strcasecmp(param->defname, "byte_pct") == 0)
-                       *byte_pct_p = (int) defGetNumeric(param);
-               else if (strcasecmp(param->defname, "perbyte_cpu") == 0)
-                       *perbyte_cpu_p = (int) defGetNumeric(param);
-               else if (strcasecmp(param->defname, "percall_cpu") == 0)
-                       *percall_cpu_p = (int) defGetNumeric(param);
-               else if (strcasecmp(param->defname, "outin_ratio") == 0)
-                       *outin_ratio_p = (int) defGetNumeric(param);
-               else
-                       elog(WARNING, "Unrecognized function attribute '%s' ignored",
-                                param->defname);
-       }
-}
-
-
-/*
- * For a dynamically linked C language object, the form of the clause is
- *
- *        AS <object file name> [, <link symbol name> ]
- *
- * In all other cases
- *
- *        AS <object reference, or sql code>
- *
- */
-
-static void
-interpret_AS_clause(Oid languageOid, const char *languageName, const List *as,
-                                       char **prosrc_str_p, char **probin_str_p)
-{
-       Assert(as != NIL);
-
-       if (languageOid == ClanguageId)
-       {
-               /*
-                * For "C" language, store the file name in probin and, when
-                * given, the link symbol name in prosrc.
-                */
-               *probin_str_p = strVal(lfirst(as));
-               if (lnext(as) == NULL)
-                       *prosrc_str_p = "-";
-               else
-                       *prosrc_str_p = strVal(lsecond(as));
-       }
-       else
-       {
-               /* Everything else wants the given string in prosrc. */
-               *prosrc_str_p = strVal(lfirst(as));
-               *probin_str_p = "-";
-
-               if (lnext(as) != NIL)
-                       elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language",
-                                languageName);
-       }
-}
-
-
-
-/*
- * CreateFunction
- *      Execute a CREATE FUNCTION utility statement.
- */
-void
-CreateFunction(ProcedureStmt *stmt)
-{
-       char       *probin_str;
-       char       *prosrc_str;
-       Oid                     prorettype;
-       bool            returnsSet;
-       char            languageName[NAMEDATALEN];
-       Oid                     languageOid;
-       char       *funcname;
-       Oid                     namespaceId;
-       int                     parameterCount;
-       Oid                     parameterTypes[FUNC_MAX_ARGS];
-       int32           byte_pct,
-                               perbyte_cpu,
-                               percall_cpu,
-                               outin_ratio;
-       bool            isImplicit,
-                               isStrict;
-       char            volatility;
-       HeapTuple       languageTuple;
-       Form_pg_language languageStruct;
-
-       /* Convert list of names to a name and namespace */
-       namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
-                                                                                                       &funcname);
-
-       /* Convert language name to canonical case */
-       case_translate_language_name(stmt->language, languageName);
-
-       /* Look up the language and validate permissions */
-       languageTuple = SearchSysCache(LANGNAME,
-                                                                  PointerGetDatum(languageName),
-                                                                  0, 0, 0);
-       if (!HeapTupleIsValid(languageTuple))
-               elog(ERROR, "language \"%s\" does not exist", languageName);
-
-       languageOid = languageTuple->t_data->t_oid;
-       languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
-
-       if (!((languageStruct->lanpltrusted
-                  && pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK)
-                 || superuser()))
-               elog(ERROR, "permission denied");
-
-       ReleaseSysCache(languageTuple);
-
-       /*
-        * Convert remaining parameters of CREATE to form wanted by
-        * ProcedureCreate.
-        */
-       compute_return_type(stmt->returnType, languageOid,
-                                               &prorettype, &returnsSet);
-
-       parameterCount = compute_parameter_types(stmt->argTypes, languageOid,
-                                                                                        parameterTypes);
-
-       compute_full_attributes(stmt->withClause,
-                                                       &byte_pct, &perbyte_cpu, &percall_cpu,
-                                                       &outin_ratio, &isImplicit, &isStrict,
-                                                       &volatility);
-
-       interpret_AS_clause(languageOid, languageName, stmt->as,
-                                               &prosrc_str, &probin_str);
-
-       /*
-        * And now that we have all the parameters, and know we're permitted
-        * to do so, go ahead and create the function.
-        */
-       ProcedureCreate(funcname,
-                                       namespaceId,
-                                       stmt->replace,
-                                       returnsSet,
-                                       prorettype,
-                                       languageOid,
-                                       prosrc_str, /* converted to text later */
-                                       probin_str, /* converted to text later */
-                                       false,          /* not an aggregate */
-                                       true,           /* (obsolete "trusted") */
-                                       isImplicit,
-                                       isStrict,
-                                       volatility,
-                                       byte_pct,
-                                       perbyte_cpu,
-                                       percall_cpu,
-                                       outin_ratio,
-                                       parameterCount,
-                                       parameterTypes);
-}
-
-
-/*
- * DefineOperator
- *             this function extracts all the information from the
- *             parameter list generated by the parser and then has
- *             OperatorCreate() do all the actual work.
- *
- * 'parameters' is a list of DefElem
- */
-void
-DefineOperator(List *names, List *parameters)
-{
-       char       *oprName;
-       Oid                     oprNamespace;
-       uint16          precedence = 0; /* operator precedence */
-       bool            canHash = false;        /* operator hashes */
-       bool            isLeftAssociative = true;               /* operator is left
-                                                                                                * associative */
-       char       *functionName = NULL;        /* function for operator */
-       TypeName   *typeName1 = NULL;           /* first type name */
-       TypeName   *typeName2 = NULL;           /* second type name */
-       Oid                     typeId1 = InvalidOid;   /* types converted to OID */
-       Oid                     typeId2 = InvalidOid;
-       char       *commutatorName = NULL;      /* optional commutator operator
-                                                                                * name */
-       char       *negatorName = NULL;         /* optional negator operator name */
-       char       *restrictionName = NULL; /* optional restrict. sel.
-                                                                                * procedure */
-       char       *joinName = NULL;    /* optional join sel. procedure name */
-       char       *sortName1 = NULL;           /* optional first sort operator */
-       char       *sortName2 = NULL;           /* optional second sort operator */
-       List       *pl;
-
-       /* Convert list of names to a name and namespace */
-       oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
-
-       /*
-        * loop over the definition list and extract the information we need.
-        */
-       foreach(pl, parameters)
-       {
-               DefElem    *defel = (DefElem *) lfirst(pl);
-
-               if (strcasecmp(defel->defname, "leftarg") == 0)
-               {
-                       typeName1 = defGetTypeName(defel);
-                       if (typeName1->setof)
-                               elog(ERROR, "setof type not implemented for leftarg");
-               }
-               else if (strcasecmp(defel->defname, "rightarg") == 0)
-               {
-                       typeName2 = defGetTypeName(defel);
-                       if (typeName2->setof)
-                               elog(ERROR, "setof type not implemented for rightarg");
-               }
-               else if (strcasecmp(defel->defname, "procedure") == 0)
-                       functionName = defGetString(defel);
-               else if (strcasecmp(defel->defname, "precedence") == 0)
-               {
-                       /* NOT IMPLEMENTED (never worked in v4.2) */
-                       elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
-               }
-               else if (strcasecmp(defel->defname, "associativity") == 0)
-               {
-                       /* NOT IMPLEMENTED (never worked in v4.2) */
-                       elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
-               }
-               else if (strcasecmp(defel->defname, "commutator") == 0)
-                       commutatorName = defGetString(defel);
-               else if (strcasecmp(defel->defname, "negator") == 0)
-                       negatorName = defGetString(defel);
-               else if (strcasecmp(defel->defname, "restrict") == 0)
-                       restrictionName = defGetString(defel);
-               else if (strcasecmp(defel->defname, "join") == 0)
-                       joinName = defGetString(defel);
-               else if (strcasecmp(defel->defname, "hashes") == 0)
-                       canHash = TRUE;
-               else if (strcasecmp(defel->defname, "sort1") == 0)
-                       sortName1 = defGetString(defel);
-               else if (strcasecmp(defel->defname, "sort2") == 0)
-                       sortName2 = defGetString(defel);
-               else
-               {
-                       elog(WARNING, "DefineOperator: attribute \"%s\" not recognized",
-                                defel->defname);
-               }
-       }
-
-       /*
-        * make sure we have our required definitions
-        */
-       if (functionName == NULL)
-               elog(ERROR, "Define: \"procedure\" unspecified");
-
-       /* Transform type names to type OIDs */
-       if (typeName1)
-               typeId1 = typenameTypeId(typeName1);
-       if (typeName2)
-               typeId2 = typenameTypeId(typeName2);
-
-       /*
-        * now have OperatorCreate do all the work..
-        */
-       OperatorCreate(oprName,         /* operator name */
-                                  typeId1,             /* left type id */
-                                  typeId2,             /* right type id */
-                                  functionName,        /* function for operator */
-                                  precedence,  /* operator precedence */
-                                  isLeftAssociative,   /* operator is left associative */
-                                  commutatorName,              /* optional commutator operator
-                                                                                * name */
-                                  negatorName, /* optional negator operator name */
-                                  restrictionName,             /* optional restrict. sel.
-                                                                                * procedure */
-                                  joinName,    /* optional join sel. procedure name */
-                                  canHash,             /* operator hashes */
-                                  sortName1,   /* optional first sort operator */
-                                  sortName2);  /* optional second sort operator */
-
-}
-
-/*
- *     DefineAggregate
- */
-void
-DefineAggregate(List *names, List *parameters)
-{
-       char       *aggName;
-       Oid                     aggNamespace;
-       List       *transfuncName = NIL;
-       List       *finalfuncName = NIL;
-       TypeName   *baseType = NULL;
-       TypeName   *transType = NULL;
-       char       *initval = NULL;
-       Oid                     baseTypeId;
-       Oid                     transTypeId;
-       List       *pl;
-
-       /* Convert list of names to a name and namespace */
-       aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName);
-
-       foreach(pl, parameters)
-       {
-               DefElem    *defel = (DefElem *) lfirst(pl);
-
-               /*
-                * sfunc1, stype1, and initcond1 are accepted as obsolete
-                * spellings for sfunc, stype, initcond.
-                */
-               if (strcasecmp(defel->defname, "sfunc") == 0)
-                       transfuncName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "sfunc1") == 0)
-                       transfuncName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "finalfunc") == 0)
-                       finalfuncName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "basetype") == 0)
-                       baseType = defGetTypeName(defel);
-               else if (strcasecmp(defel->defname, "stype") == 0)
-                       transType = defGetTypeName(defel);
-               else if (strcasecmp(defel->defname, "stype1") == 0)
-                       transType = defGetTypeName(defel);
-               else if (strcasecmp(defel->defname, "initcond") == 0)
-                       initval = defGetString(defel);
-               else if (strcasecmp(defel->defname, "initcond1") == 0)
-                       initval = defGetString(defel);
-               else
-                       elog(WARNING, "DefineAggregate: attribute \"%s\" not recognized",
-                                defel->defname);
-       }
-
-       /*
-        * make sure we have our required definitions
-        */
-       if (baseType == NULL)
-               elog(ERROR, "Define: \"basetype\" unspecified");
-       if (transType == NULL)
-               elog(ERROR, "Define: \"stype\" unspecified");
-       if (transfuncName == NIL)
-               elog(ERROR, "Define: \"sfunc\" unspecified");
-
-       /*
-        * Handle the aggregate's base type (input data type).  This can be
-        * specified as 'ANY' for a data-independent transition function, such
-        * as COUNT(*).
-        */
-       baseTypeId = LookupTypeName(baseType);
-       if (OidIsValid(baseTypeId))
-       {
-               /* no need to allow aggregates on as-yet-undefined types */
-               if (!get_typisdefined(baseTypeId))
-                       elog(ERROR, "Type \"%s\" is only a shell",
-                                TypeNameToString(baseType));
-       }
-       else
-       {
-               char      *typnam = TypeNameToString(baseType);
-
-               if (strcasecmp(typnam, "ANY") != 0)
-                       elog(ERROR, "Type \"%s\" does not exist", typnam);
-               baseTypeId = InvalidOid;
-       }
-
-       /* handle transtype --- no special cases here */
-       transTypeId = typenameTypeId(transType);
-
-       /*
-        * Most of the argument-checking is done inside of AggregateCreate
-        */
-       AggregateCreate(aggName,        /* aggregate name */
-                                       aggNamespace,   /* namespace */
-                                       transfuncName,          /* step function name */
-                                       finalfuncName,          /* final function name */
-                                       baseTypeId,     /* type of data being aggregated */
-                                       transTypeId,    /* transition data type */
-                                       initval);       /* initial condition */
-}
-
-/*
- * DefineDomain
- *             Registers a new domain.
- */
-void
-DefineDomain(CreateDomainStmt *stmt)
-{
-       char       *domainName;
-       Oid                     domainNamespace;
-       int16           internalLength;
-       int16           externalLength;
-       Oid                     inputProcedure;
-       Oid                     outputProcedure;
-       Oid                     receiveProcedure;
-       Oid                     sendProcedure;
-       bool            byValue;
-       char            delimiter;
-       char            alignment;
-       char            storage;
-       char            typtype;
-       Datum           datum;
-       bool            isnull;
-       char       *defaultValue = NULL;
-       char       *defaultValueBin = NULL;
-       bool            typNotNull = false;
-       Oid                     basetypelem;
-       int32           typNDims = length(stmt->typename->arrayBounds);
-       HeapTuple       typeTup;
-       List       *schema = stmt->constraints;
-       List       *listptr;
-
-       /* Convert list of names to a name and namespace */
-       domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
-                                                                                                               &domainName);
-
-       /*
-        * Domainnames, unlike typenames don't need to account for the '_'
-        * prefix.  So they can be one character longer.
-        */
-       if (strlen(domainName) > (NAMEDATALEN - 1))
-               elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
-                        NAMEDATALEN - 1);
-
-       /*
-        * Look up the base type.
-        */
-       typeTup = typenameType(stmt->typename);
-
-       /*
-        * What we really don't want is domains of domains.  This could cause all sorts
-        * of neat issues if we allow that.
-        *
-        * With testing, we may determine complex types should be allowed
-        */
-       typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
-       if (typtype != 'b')
-               elog(ERROR, "DefineDomain: %s is not a basetype",
-                        TypeNameToString(stmt->typename));
-
-       /* passed by value */
-       byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
-
-       /* Required Alignment */
-       alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
-
-       /* TOAST Strategy */
-       storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
-
-       /* Storage Length */
-       internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
-
-       /* External Length (unused) */
-       externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
-
-       /* Array element Delimiter */
-       delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
-
-       /* I/O Functions */
-       inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
-       outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
-       receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
-       sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
-
-       /* Inherited default value */
-       datum = SysCacheGetAttr(TYPEOID, typeTup,
-                                                       Anum_pg_type_typdefault, &isnull);
-       if (!isnull)
-               defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
-
-       /* Inherited default binary value */
-       datum = SysCacheGetAttr(TYPEOID, typeTup,
-                                                       Anum_pg_type_typdefaultbin, &isnull);
-       if (!isnull)
-               defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
-
-       /*
-        * Pull out the typelem name of the parent OID.
-        *
-        * This is what enables us to make a domain of an array
-        */
-       basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
-
-       /*
-        * Run through constraints manually to avoid the additional
-        * processing conducted by DefineRelation() and friends.
-        *
-        * Besides, we don't want any constraints to be cooked.  We'll
-        * do that when the table is created via MergeDomainAttributes().
-        */
-       foreach(listptr, schema)
-       {
-               Constraint *colDef = lfirst(listptr);
-               bool nullDefined = false;
-               Node       *expr;
-               ParseState *pstate;
-
-               switch (colDef->contype)
-               {
-                       /*
-                        * The inherited default value may be overridden by the user
-                        * with the DEFAULT <expr> statement.
-                        *
-                        * We have to search the entire constraint tree returned as we
-                        * don't want to cook or fiddle too much.
-                        */
-                       case CONSTR_DEFAULT:
-                               /* Create a dummy ParseState for transformExpr */
-                               pstate = make_parsestate(NULL);
-                               /*
-                                * Cook the colDef->raw_expr into an expression.
-                                * Note: Name is strictly for error message
-                                */
-                               expr = cookDefault(pstate, colDef->raw_expr,
-                                                                  typeTup->t_data->t_oid,
-                                                                  stmt->typename->typmod,
-                                                                  domainName);
-                               /*
-                                * Expression must be stored as a nodeToString result,
-                                * but we also require a valid textual representation
-                                * (mainly to make life easier for pg_dump).
-                                */
-                               defaultValue = deparse_expression(expr,
-                                                               deparse_context_for(domainName,
-                                                                                                       InvalidOid),
-                                                                                                  false);
-                               defaultValueBin = nodeToString(expr);
-                               break;
-
-                       /*
-                        * Find the NULL constraint.
-                        */
-                       case CONSTR_NOTNULL:
-                               if (nullDefined) {
-                                       elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
-                               } else {
-                                       typNotNull = true;
-                                       nullDefined = true;
-                               }
-                               break;
-
-                       case CONSTR_NULL:
-                               if (nullDefined) {
-                                       elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
-                               } else {
-                                       typNotNull = false;
-                                       nullDefined = true;
-                               }
-                               break;
-
-                       case CONSTR_UNIQUE:
-                               elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
-                               break;
-
-                       case CONSTR_PRIMARY:
-                               elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
-                               break;
-
-                       case CONSTR_CHECK:
-                               elog(ERROR, "DefineDomain: CHECK Constraints not supported");
-                               break;
-
-                       case CONSTR_ATTR_DEFERRABLE:
-                       case CONSTR_ATTR_NOT_DEFERRABLE:
-                       case CONSTR_ATTR_DEFERRED:
-                       case CONSTR_ATTR_IMMEDIATE:
-                               elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
-                               break;
-
-                       default:
-                               elog(ERROR, "DefineDomain: unrecognized constraint node type");
-                               break;
-               }
-       }
-
-       /*
-        * Have TypeCreate do all the real work.
-        */
-       TypeCreate(domainName,                  /* type name */
-                          domainNamespace,             /* namespace */
-                          InvalidOid,                  /* preassigned type oid (not done here) */
-                          InvalidOid,                  /* relation oid (n/a here) */
-                          internalLength,              /* internal size */
-                          externalLength,              /* external size */
-                          'd',                                 /* type-type (domain type) */
-                          delimiter,                   /* array element delimiter */
-                          inputProcedure,              /* input procedure */
-                          outputProcedure,             /* output procedure */
-                          receiveProcedure,    /* receive procedure */
-                          sendProcedure,               /* send procedure */
-                          basetypelem,                 /* element type ID */
-                          typeTup->t_data->t_oid,      /* base type ID */
-                          defaultValue,                /* default type value (text) */
-                          defaultValueBin,             /* default type value (binary) */
-                          byValue,                             /* passed by value */
-                          alignment,                   /* required alignment */
-                          storage,                             /* TOAST strategy */
-                          stmt->typename->typmod, /* typeMod value */
-                          typNDims,                    /* Array dimensions for base type */
-                          typNotNull);                 /* Type NOT NULL */
-
-       /*
-        * Now we can clean up.
-        */
-       ReleaseSysCache(typeTup);
-}
-
-/*
- * DefineType
- *             Registers a new type.
+ * Extract a string value (otherwise uninterpreted) from a DefElem.
  */
-void
-DefineType(List *names, List *parameters)
-{
-       char       *typeName;
-       Oid                     typeNamespace;
-       int16           internalLength = -1;    /* int2 */
-       int16           externalLength = -1;    /* int2 */
-       Oid                     elemType = InvalidOid;
-       List       *inputName = NIL;
-       List       *outputName = NIL;
-       List       *sendName = NIL;
-       List       *receiveName = NIL;
-       char       *defaultValue = NULL;
-       bool            byValue = false;
-       char            delimiter = DEFAULT_TYPDELIM;
-       char            alignment = 'i';        /* default alignment */
-       char            storage = 'p';  /* default TOAST storage method */
-       Oid                     inputOid;
-       Oid                     outputOid;
-       Oid                     sendOid;
-       Oid                     receiveOid;
-       char       *shadow_type;
-       List       *pl;
-       Oid                     typoid;
-
-       /* Convert list of names to a name and namespace */
-       typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
-
-       /*
-        * Type names must be one character shorter than other names, allowing
-        * room to create the corresponding array type name with prepended
-        * "_".
-        */
-       if (strlen(typeName) > (NAMEDATALEN - 2))
-               elog(ERROR, "DefineType: type names must be %d characters or less",
-                        NAMEDATALEN - 2);
-
-       foreach(pl, parameters)
-       {
-               DefElem    *defel = (DefElem *) lfirst(pl);
-
-               if (strcasecmp(defel->defname, "internallength") == 0)
-                       internalLength = defGetTypeLength(defel);
-               else if (strcasecmp(defel->defname, "externallength") == 0)
-                       externalLength = defGetTypeLength(defel);
-               else if (strcasecmp(defel->defname, "input") == 0)
-                       inputName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "output") == 0)
-                       outputName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "send") == 0)
-                       sendName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "receive") == 0)
-                       receiveName = defGetQualifiedName(defel);
-               else if (strcasecmp(defel->defname, "delimiter") == 0)
-               {
-                       char       *p = defGetString(defel);
-
-                       delimiter = p[0];
-               }
-               else if (strcasecmp(defel->defname, "element") == 0)
-                       elemType = typenameTypeId(defGetTypeName(defel));
-               else if (strcasecmp(defel->defname, "default") == 0)
-                       defaultValue = defGetString(defel);
-               else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
-                       byValue = true;
-               else if (strcasecmp(defel->defname, "alignment") == 0)
-               {
-                       char       *a = defGetString(defel);
-
-                       /*
-                        * Note: if argument was an unquoted identifier, parser will
-                        * have applied xlateSqlType() to it, so be prepared to
-                        * recognize translated type names as well as the nominal
-                        * form.
-                        */
-                       if (strcasecmp(a, "double") == 0)
-                               alignment = 'd';
-                       else if (strcasecmp(a, "float8") == 0)
-                               alignment = 'd';
-                       else if (strcasecmp(a, "int4") == 0)
-                               alignment = 'i';
-                       else if (strcasecmp(a, "int2") == 0)
-                               alignment = 's';
-                       else if (strcasecmp(a, "char") == 0)
-                               alignment = 'c';
-                       else if (strcasecmp(a, "bpchar") == 0)
-                               alignment = 'c';
-                       else
-                               elog(ERROR, "DefineType: \"%s\" alignment not recognized",
-                                        a);
-               }
-               else if (strcasecmp(defel->defname, "storage") == 0)
-               {
-                       char       *a = defGetString(defel);
-
-                       if (strcasecmp(a, "plain") == 0)
-                               storage = 'p';
-                       else if (strcasecmp(a, "external") == 0)
-                               storage = 'e';
-                       else if (strcasecmp(a, "extended") == 0)
-                               storage = 'x';
-                       else if (strcasecmp(a, "main") == 0)
-                               storage = 'm';
-                       else
-                               elog(ERROR, "DefineType: \"%s\" storage not recognized",
-                                        a);
-               }
-               else
-               {
-                       elog(WARNING, "DefineType: attribute \"%s\" not recognized",
-                                defel->defname);
-               }
-       }
-
-       /*
-        * make sure we have our required definitions
-        */
-       if (inputName == NIL)
-               elog(ERROR, "Define: \"input\" unspecified");
-       if (outputName == NIL)
-               elog(ERROR, "Define: \"output\" unspecified");
-
-       /* Convert I/O proc names to OIDs */
-       inputOid = findTypeIOFunction(inputName, false);
-       outputOid = findTypeIOFunction(outputName, true);
-       if (sendName)
-               sendOid = findTypeIOFunction(sendName, true);
-       else
-               sendOid = outputOid;
-       if (receiveName)
-               receiveOid = findTypeIOFunction(receiveName, false);
-       else
-               receiveOid = inputOid;
-
-       /*
-        * now have TypeCreate do all the real work.
-        */
-       typoid =
-               TypeCreate(typeName,            /* type name */
-                                  typeNamespace,       /* namespace */
-                                  InvalidOid,          /* preassigned type oid (not done here) */
-                                  InvalidOid,          /* relation oid (n/a here) */
-                                  internalLength,      /* internal size */
-                                  externalLength,      /* external size */
-                                  'b',                         /* type-type (base type) */
-                                  delimiter,           /* array element delimiter */
-                                  inputOid,            /* input procedure */
-                                  outputOid,           /* output procedure */
-                                  receiveOid,          /* receive procedure */
-                                  sendOid,                     /* send procedure */
-                                  elemType,            /* element type ID */
-                                  InvalidOid,          /* base type ID (only for domains) */
-                                  defaultValue,        /* default type value */
-                                  NULL,                        /* no binary form available */
-                                  byValue,                     /* passed by value */
-                                  alignment,           /* required alignment */
-                                  storage,                     /* TOAST strategy */
-                                  -1,                          /* typMod (Domains only) */
-                                  0,                           /* Array Dimensions of typbasetype */
-                                  false);                      /* Type NOT NULL */
-
-       /*
-        * When we create a base type (as opposed to a complex type) we need
-        * to have an array entry for it in pg_type as well.
-        */
-       shadow_type = makeArrayTypeName(typeName);
-
-       /* alignment must be 'i' or 'd' for arrays */
-       alignment = (alignment == 'd') ? 'd' : 'i';
-
-       TypeCreate(shadow_type,         /* type name */
-                          typeNamespace,       /* namespace */
-                          InvalidOid,          /* preassigned type oid (not done here) */
-                          InvalidOid,          /* relation oid (n/a here) */
-                          -1,                          /* internal size */
-                          -1,                          /* external size */
-                          'b',                         /* type-type (base type) */
-                          DEFAULT_TYPDELIM,    /* array element delimiter */
-                          F_ARRAY_IN,          /* input procedure */
-                          F_ARRAY_OUT,         /* output procedure */
-                          F_ARRAY_IN,          /* receive procedure */
-                          F_ARRAY_OUT,         /* send procedure */
-                          typoid,                      /* element type ID */
-                          InvalidOid,          /* base type ID */
-                          NULL,                        /* never a default type value */
-                          NULL,                        /* binary default isn't sent either */
-                          false,                       /* never passed by value */
-                          alignment,           /* see above */
-                          'x',                         /* ARRAY is always toastable */
-                          -1,                          /* typMod (Domains only) */
-                          0,                           /* Array dimensions of typbasetype */
-                          false);                      /* Type NOT NULL */
-
-       pfree(shadow_type);
-}
-
-static Oid
-findTypeIOFunction(List *procname, bool isOutput)
-{
-       Oid                     argList[FUNC_MAX_ARGS];
-       int                     nargs;
-       Oid                     procOid;
-
-       /*
-        * First look for a 1-argument func with all argtypes 0. This is
-        * valid for all kinds of procedure.
-        */
-       MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
-
-       procOid = LookupFuncName(procname, 1, argList);
-
-       if (!OidIsValid(procOid))
-       {
-               /*
-                * Alternatively, input procedures may take 3 args (data
-                * value, element OID, atttypmod); the pg_proc argtype
-                * signature is 0,OIDOID,INT4OID.  Output procedures may
-                * take 2 args (data value, element OID).
-                */
-               if (isOutput)
-               {
-                       /* output proc */
-                       nargs = 2;
-                       argList[1] = OIDOID;
-               }
-               else
-               {
-                       /* input proc */
-                       nargs = 3;
-                       argList[1] = OIDOID;
-                       argList[2] = INT4OID;
-               }
-               procOid = LookupFuncName(procname, nargs, argList);
-
-               if (!OidIsValid(procOid))
-                       func_error("TypeCreate", procname, 1, argList, NULL);
-       }
-
-       return procOid;
-}
-
-
-static char *
+char *
 defGetString(DefElem *def)
 {
        if (def->arg == NULL)
@@ -1133,7 +92,10 @@ defGetString(DefElem *def)
        return NULL;                            /* keep compiler quiet */
 }
 
-static double
+/*
+ * Extract a numeric value (actually double) from a DefElem.
+ */
+double
 defGetNumeric(DefElem *def)
 {
        if (def->arg == NULL)
@@ -1152,7 +114,10 @@ defGetNumeric(DefElem *def)
        return 0;                                       /* keep compiler quiet */
 }
 
-static List *
+/*
+ * Extract a possibly-qualified name (as a List of Strings) from a DefElem.
+ */
+List *
 defGetQualifiedName(DefElem *def)
 {
        if (def->arg == NULL)
@@ -1172,7 +137,10 @@ defGetQualifiedName(DefElem *def)
        return NIL;                                     /* keep compiler quiet */
 }
 
-static TypeName *
+/*
+ * Extract a TypeName from a DefElem.
+ */
+TypeName *
 defGetTypeName(DefElem *def)
 {
        if (def->arg == NULL)
@@ -1198,7 +166,11 @@ defGetTypeName(DefElem *def)
        return NULL;                            /* keep compiler quiet */
 }
 
-static int
+/*
+ * Extract a type length indicator (either absolute bytes, or
+ * -1 for "variable") from a DefElem.
+ */
+int
 defGetTypeLength(DefElem *def)
 {
        if (def->arg == NULL)
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
new file mode 100644 (file)
index 0000000..0bee7cd
--- /dev/null
@@ -0,0 +1,431 @@
+/*-------------------------------------------------------------------------
+ *
+ * functioncmds.c
+ *
+ *       Routines for CREATE and DROP FUNCTION commands
+ *
+ * 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/functioncmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ * DESCRIPTION
+ *       These routines take the parse tree and pick out the
+ *       appropriate arguments/flags, and pass the results to the
+ *       corresponding "FooDefine" routines (in src/catalog) that do
+ *       the actual catalog-munging.  These routines also verify permission
+ *       of the user to execute the command.
+ *
+ * NOTES
+ *       These things must be defined and committed in the following order:
+ *             "create function":
+ *                             input/output, recv/send procedures
+ *             "create type":
+ *                             type
+ *             "create operator":
+ *                             operators
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/comment.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "optimizer/cost.h"
+#include "parser/parse_func.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+/*
+ *      Examine the "returns" clause returnType of the CREATE FUNCTION statement
+ *      and return information about it as *prorettype_p and *returnsSet.
+ *
+ * This is more complex than the average typename lookup because we want to
+ * allow a shell type to be used, or even created if the specified return type
+ * doesn't exist yet.  (Without this, there's no way to define the I/O procs
+ * for a new type.)  But SQL function creation won't cope, so error out if
+ * the target language is SQL.
+ */
+static void
+compute_return_type(TypeName *returnType, Oid languageOid,
+                                       Oid *prorettype_p, bool *returnsSet_p)
+{
+       Oid             rettype;
+
+       rettype = LookupTypeName(returnType);
+
+       if (OidIsValid(rettype))
+       {
+               if (!get_typisdefined(rettype))
+               {
+                       if (languageOid == SQLlanguageId)
+                               elog(ERROR, "SQL functions cannot return shell types");
+                       else
+                               elog(WARNING, "Return type \"%s\" is only a shell",
+                                        TypeNameToString(returnType));
+               }
+       }
+       else
+       {
+               char      *typnam = TypeNameToString(returnType);
+
+               if (strcmp(typnam, "opaque") == 0)
+                       rettype = InvalidOid;
+               else
+               {
+                       Oid                     namespaceId;
+                       char       *typname;
+
+                       if (languageOid == SQLlanguageId)
+                               elog(ERROR, "Type \"%s\" does not exist", typnam);
+                       elog(WARNING, "ProcedureCreate: type %s is not yet defined",
+                                typnam);
+                       namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
+                                                                                                                       &typname);
+                       rettype = TypeShellMake(typname, namespaceId);
+                       if (!OidIsValid(rettype))
+                               elog(ERROR, "could not create type %s", typnam);
+               }
+       }
+
+       *prorettype_p = rettype;
+       *returnsSet_p = returnType->setof;
+}
+
+/*
+ * Interpret the argument-types list of the CREATE FUNCTION statement.
+ */
+static int
+compute_parameter_types(List *argTypes, Oid languageOid,
+                                               Oid *parameterTypes)
+{
+       int                     parameterCount = 0;
+       List       *x;
+
+       MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid));
+       foreach(x, argTypes)
+       {
+               TypeName   *t = (TypeName *) lfirst(x);
+               Oid                     toid;
+
+               if (parameterCount >= FUNC_MAX_ARGS)
+                       elog(ERROR, "functions cannot have more than %d arguments",
+                                FUNC_MAX_ARGS);
+
+               toid = LookupTypeName(t);
+               if (OidIsValid(toid))
+               {
+                       if (!get_typisdefined(toid))
+                               elog(WARNING, "Argument type \"%s\" is only a shell",
+                                        TypeNameToString(t));
+               }
+               else
+               {
+                       char      *typnam = TypeNameToString(t);
+
+                       if (strcmp(typnam, "opaque") == 0)
+                       {
+                               if (languageOid == SQLlanguageId)
+                                       elog(ERROR, "SQL functions cannot have arguments of type \"opaque\"");
+                               toid = InvalidOid;
+                       }
+                       else
+                               elog(ERROR, "Type \"%s\" does not exist", typnam);
+               }
+
+               if (t->setof)
+                       elog(ERROR, "functions cannot accept set arguments");
+
+               parameterTypes[parameterCount++] = toid;
+       }
+
+       return parameterCount;
+}
+
+/*-------------
+ *      Interpret the parameters *parameters and return their contents as
+ *      *byte_pct_p, etc.
+ *
+ *     These parameters supply optional information about a function.
+ *     All have defaults if not specified.
+ *
+ *     Note: currently, only three of these parameters actually do anything:
+ *
+ *      * isImplicit means the function may be used as an implicit type
+ *        coercion.
+ *
+ *      * isStrict means the function should not be called when any NULL
+ *        inputs are present; instead a NULL result value should be assumed.
+ *
+ *      * volatility tells the optimizer whether the function's result can
+ *        be assumed to be repeatable over multiple evaluations.
+ *
+ *     The other four parameters are not used anywhere.        They used to be
+ *     used in the "expensive functions" optimizer, but that's been dead code
+ *     for a long time.
+ *------------
+ */
+static void
+compute_full_attributes(List *parameters,
+                                               int32 *byte_pct_p, int32 *perbyte_cpu_p,
+                                               int32 *percall_cpu_p, int32 *outin_ratio_p,
+                                               bool *isImplicit_p, bool *isStrict_p,
+                                               char *volatility_p)
+{
+       List       *pl;
+
+       /* the defaults */
+       *byte_pct_p = BYTE_PCT;
+       *perbyte_cpu_p = PERBYTE_CPU;
+       *percall_cpu_p = PERCALL_CPU;
+       *outin_ratio_p = OUTIN_RATIO;
+       *isImplicit_p = false;
+       *isStrict_p = false;
+       *volatility_p = PROVOLATILE_VOLATILE;
+
+       foreach(pl, parameters)
+       {
+               DefElem    *param = (DefElem *) lfirst(pl);
+
+               if (strcasecmp(param->defname, "implicitcoercion") == 0)
+                       *isImplicit_p = true;
+               else if (strcasecmp(param->defname, "isstrict") == 0)
+                       *isStrict_p = true;
+               else if (strcasecmp(param->defname, "isimmutable") == 0)
+                       *volatility_p = PROVOLATILE_IMMUTABLE;
+               else if (strcasecmp(param->defname, "isstable") == 0)
+                       *volatility_p = PROVOLATILE_STABLE;
+               else if (strcasecmp(param->defname, "isvolatile") == 0)
+                       *volatility_p = PROVOLATILE_VOLATILE;
+               else if (strcasecmp(param->defname, "iscachable") == 0)
+               {
+                       /* obsolete spelling of isImmutable */
+                       *volatility_p = PROVOLATILE_IMMUTABLE;
+               }
+               else if (strcasecmp(param->defname, "trusted") == 0)
+               {
+                       /*
+                        * we don't have untrusted functions any more. The 4.2
+                        * implementation is lousy anyway so I took it out. -ay 10/94
+                        */
+                       elog(ERROR, "untrusted function has been decommissioned.");
+               }
+               else if (strcasecmp(param->defname, "byte_pct") == 0)
+                       *byte_pct_p = (int) defGetNumeric(param);
+               else if (strcasecmp(param->defname, "perbyte_cpu") == 0)
+                       *perbyte_cpu_p = (int) defGetNumeric(param);
+               else if (strcasecmp(param->defname, "percall_cpu") == 0)
+                       *percall_cpu_p = (int) defGetNumeric(param);
+               else if (strcasecmp(param->defname, "outin_ratio") == 0)
+                       *outin_ratio_p = (int) defGetNumeric(param);
+               else
+                       elog(WARNING, "Unrecognized function attribute '%s' ignored",
+                                param->defname);
+       }
+}
+
+
+/*
+ * For a dynamically linked C language object, the form of the clause is
+ *
+ *        AS <object file name> [, <link symbol name> ]
+ *
+ * In all other cases
+ *
+ *        AS <object reference, or sql code>
+ *
+ */
+
+static void
+interpret_AS_clause(Oid languageOid, const char *languageName, const List *as,
+                                       char **prosrc_str_p, char **probin_str_p)
+{
+       Assert(as != NIL);
+
+       if (languageOid == ClanguageId)
+       {
+               /*
+                * For "C" language, store the file name in probin and, when
+                * given, the link symbol name in prosrc.
+                */
+               *probin_str_p = strVal(lfirst(as));
+               if (lnext(as) == NULL)
+                       *prosrc_str_p = "-";
+               else
+                       *prosrc_str_p = strVal(lsecond(as));
+       }
+       else
+       {
+               /* Everything else wants the given string in prosrc. */
+               *prosrc_str_p = strVal(lfirst(as));
+               *probin_str_p = "-";
+
+               if (lnext(as) != NIL)
+                       elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language",
+                                languageName);
+       }
+}
+
+
+
+/*
+ * CreateFunction
+ *      Execute a CREATE FUNCTION utility statement.
+ */
+void
+CreateFunction(ProcedureStmt *stmt)
+{
+       char       *probin_str;
+       char       *prosrc_str;
+       Oid                     prorettype;
+       bool            returnsSet;
+       char            languageName[NAMEDATALEN];
+       Oid                     languageOid;
+       char       *funcname;
+       Oid                     namespaceId;
+       int                     parameterCount;
+       Oid                     parameterTypes[FUNC_MAX_ARGS];
+       int32           byte_pct,
+                               perbyte_cpu,
+                               percall_cpu,
+                               outin_ratio;
+       bool            isImplicit,
+                               isStrict;
+       char            volatility;
+       HeapTuple       languageTuple;
+       Form_pg_language languageStruct;
+
+       /* Convert list of names to a name and namespace */
+       namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
+                                                                                                       &funcname);
+
+       /* Convert language name to canonical case */
+       case_translate_language_name(stmt->language, languageName);
+
+       /* Look up the language and validate permissions */
+       languageTuple = SearchSysCache(LANGNAME,
+                                                                  PointerGetDatum(languageName),
+                                                                  0, 0, 0);
+       if (!HeapTupleIsValid(languageTuple))
+               elog(ERROR, "language \"%s\" does not exist", languageName);
+
+       languageOid = languageTuple->t_data->t_oid;
+       languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
+
+       if (!((languageStruct->lanpltrusted
+                  && pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK)
+                 || superuser()))
+               elog(ERROR, "permission denied");
+
+       ReleaseSysCache(languageTuple);
+
+       /*
+        * Convert remaining parameters of CREATE to form wanted by
+        * ProcedureCreate.
+        */
+       compute_return_type(stmt->returnType, languageOid,
+                                               &prorettype, &returnsSet);
+
+       parameterCount = compute_parameter_types(stmt->argTypes, languageOid,
+                                                                                        parameterTypes);
+
+       compute_full_attributes(stmt->withClause,
+                                                       &byte_pct, &perbyte_cpu, &percall_cpu,
+                                                       &outin_ratio, &isImplicit, &isStrict,
+                                                       &volatility);
+
+       interpret_AS_clause(languageOid, languageName, stmt->as,
+                                               &prosrc_str, &probin_str);
+
+       /*
+        * And now that we have all the parameters, and know we're permitted
+        * to do so, go ahead and create the function.
+        */
+       ProcedureCreate(funcname,
+                                       namespaceId,
+                                       stmt->replace,
+                                       returnsSet,
+                                       prorettype,
+                                       languageOid,
+                                       prosrc_str, /* converted to text later */
+                                       probin_str, /* converted to text later */
+                                       false,          /* not an aggregate */
+                                       true,           /* (obsolete "trusted") */
+                                       isImplicit,
+                                       isStrict,
+                                       volatility,
+                                       byte_pct,
+                                       perbyte_cpu,
+                                       percall_cpu,
+                                       outin_ratio,
+                                       parameterCount,
+                                       parameterTypes);
+}
+
+
+/*
+ * RemoveFunction
+ *             Deletes a function.
+ *
+ * Exceptions:
+ *             BadArg if name is invalid.
+ *             "ERROR" if function nonexistent.
+ *             ...
+ */
+void
+RemoveFunction(List *functionName,             /* function name to be removed */
+                          List *argTypes)      /* list of TypeName nodes */
+{
+       Oid                     funcOid;
+       Relation        relation;
+       HeapTuple       tup;
+
+       funcOid = LookupFuncNameTypeNames(functionName, argTypes, 
+                                                                         true, "RemoveFunction");
+
+       relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
+
+       tup = SearchSysCache(PROCOID,
+                                                ObjectIdGetDatum(funcOid),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup))     /* should not happen */
+               elog(ERROR, "RemoveFunction: couldn't find tuple for function %s",
+                        NameListToString(functionName));
+
+       if (!pg_proc_ownercheck(funcOid, GetUserId()))
+               elog(ERROR, "RemoveFunction: function '%s': permission denied",
+                        NameListToString(functionName));
+
+       if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
+               elog(ERROR, "RemoveFunction: function '%s' is an aggregate"
+                        "\n\tUse DROP AGGREGATE to remove it",
+                        NameListToString(functionName));
+
+       if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
+       {
+               /* "Helpful" WARNING when removing a builtin function ... */
+               elog(WARNING, "Removing built-in function \"%s\"",
+                        NameListToString(functionName));
+       }
+
+       /* Delete any comments associated with this function */
+       DeleteComments(funcOid, RelationGetRelid(relation));
+
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       heap_close(relation, RowExclusiveLock);
+}
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
new file mode 100644 (file)
index 0000000..c916695
--- /dev/null
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ *
+ * lockcmds.c
+ *       Lock command 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/lockcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/namespace.h"
+#include "commands/lockcmds.h"
+#include "miscadmin.h"
+#include "utils/acl.h"
+
+
+/*
+ * 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 */
+       }
+}
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
new file mode 100644 (file)
index 0000000..54f4892
--- /dev/null
@@ -0,0 +1,247 @@
+/*-------------------------------------------------------------------------
+ *
+ * operatorcmds.c
+ *
+ *       Routines for operator manipulation commands
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ * DESCRIPTION
+ *       The "DefineFoo" routines take the parse tree and pick out the
+ *       appropriate arguments/flags, passing the results to the
+ *       corresponding "FooDefine" routines (in src/catalog) that do
+ *       the actual catalog-munging.  These routines also verify permission
+ *       of the user to execute the command.
+ *
+ * NOTES
+ *       These things must be defined and committed in the following order:
+ *             "create function":
+ *                             input/output, recv/send procedures
+ *             "create type":
+ *                             type
+ *             "create operator":
+ *                             operators
+ *
+ *             Most of the parse-tree manipulation routines are defined in
+ *             commands/manip.c.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_operator.h"
+#include "commands/comment.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/syscache.h"
+
+
+/*
+ * DefineOperator
+ *             this function extracts all the information from the
+ *             parameter list generated by the parser and then has
+ *             OperatorCreate() do all the actual work.
+ *
+ * 'parameters' is a list of DefElem
+ */
+void
+DefineOperator(List *names, List *parameters)
+{
+       char       *oprName;
+       Oid                     oprNamespace;
+       uint16          precedence = 0; /* operator precedence */
+       bool            canHash = false;        /* operator hashes */
+       bool            isLeftAssociative = true;               /* operator is left
+                                                                                                * associative */
+       char       *functionName = NULL;        /* function for operator */
+       TypeName   *typeName1 = NULL;           /* first type name */
+       TypeName   *typeName2 = NULL;           /* second type name */
+       Oid                     typeId1 = InvalidOid;   /* types converted to OID */
+       Oid                     typeId2 = InvalidOid;
+       char       *commutatorName = NULL;      /* optional commutator operator
+                                                                                * name */
+       char       *negatorName = NULL;         /* optional negator operator name */
+       char       *restrictionName = NULL; /* optional restrict. sel.
+                                                                                * procedure */
+       char       *joinName = NULL;    /* optional join sel. procedure name */
+       char       *sortName1 = NULL;           /* optional first sort operator */
+       char       *sortName2 = NULL;           /* optional second sort operator */
+       List       *pl;
+
+       /* Convert list of names to a name and namespace */
+       oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
+
+       /*
+        * loop over the definition list and extract the information we need.
+        */
+       foreach(pl, parameters)
+       {
+               DefElem    *defel = (DefElem *) lfirst(pl);
+
+               if (strcasecmp(defel->defname, "leftarg") == 0)
+               {
+                       typeName1 = defGetTypeName(defel);
+                       if (typeName1->setof)
+                               elog(ERROR, "setof type not implemented for leftarg");
+               }
+               else if (strcasecmp(defel->defname, "rightarg") == 0)
+               {
+                       typeName2 = defGetTypeName(defel);
+                       if (typeName2->setof)
+                               elog(ERROR, "setof type not implemented for rightarg");
+               }
+               else if (strcasecmp(defel->defname, "procedure") == 0)
+                       functionName = defGetString(defel);
+               else if (strcasecmp(defel->defname, "precedence") == 0)
+               {
+                       /* NOT IMPLEMENTED (never worked in v4.2) */
+                       elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
+               }
+               else if (strcasecmp(defel->defname, "associativity") == 0)
+               {
+                       /* NOT IMPLEMENTED (never worked in v4.2) */
+                       elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
+               }
+               else if (strcasecmp(defel->defname, "commutator") == 0)
+                       commutatorName = defGetString(defel);
+               else if (strcasecmp(defel->defname, "negator") == 0)
+                       negatorName = defGetString(defel);
+               else if (strcasecmp(defel->defname, "restrict") == 0)
+                       restrictionName = defGetString(defel);
+               else if (strcasecmp(defel->defname, "join") == 0)
+                       joinName = defGetString(defel);
+               else if (strcasecmp(defel->defname, "hashes") == 0)
+                       canHash = TRUE;
+               else if (strcasecmp(defel->defname, "sort1") == 0)
+                       sortName1 = defGetString(defel);
+               else if (strcasecmp(defel->defname, "sort2") == 0)
+                       sortName2 = defGetString(defel);
+               else
+               {
+                       elog(WARNING, "DefineOperator: attribute \"%s\" not recognized",
+                                defel->defname);
+               }
+       }
+
+       /*
+        * make sure we have our required definitions
+        */
+       if (functionName == NULL)
+               elog(ERROR, "Define: \"procedure\" unspecified");
+
+       /* Transform type names to type OIDs */
+       if (typeName1)
+               typeId1 = typenameTypeId(typeName1);
+       if (typeName2)
+               typeId2 = typenameTypeId(typeName2);
+
+       /*
+        * now have OperatorCreate do all the work..
+        */
+       OperatorCreate(oprName,         /* operator name */
+                                  typeId1,             /* left type id */
+                                  typeId2,             /* right type id */
+                                  functionName,        /* function for operator */
+                                  precedence,  /* operator precedence */
+                                  isLeftAssociative,   /* operator is left associative */
+                                  commutatorName,              /* optional commutator operator
+                                                                                * name */
+                                  negatorName, /* optional negator operator name */
+                                  restrictionName,             /* optional restrict. sel.
+                                                                                * procedure */
+                                  joinName,    /* optional join sel. procedure name */
+                                  canHash,             /* operator hashes */
+                                  sortName1,   /* optional first sort operator */
+                                  sortName2);  /* optional second sort operator */
+
+}
+
+
+/*
+ * RemoveOperator
+ *             Deletes an operator.
+ *
+ * Exceptions:
+ *             BadArg if name is invalid.
+ *             BadArg if type1 is invalid.
+ *             "ERROR" if operator nonexistent.
+ *             ...
+ */
+void
+RemoveOperator(char *operatorName,             /* operator name */
+                          TypeName *typeName1, /* left argument type name */
+                          TypeName *typeName2) /* right argument type name */
+{
+       Relation        relation;
+       HeapTuple       tup;
+       Oid                     typeId1 = InvalidOid;
+       Oid                     typeId2 = InvalidOid;
+       char            oprtype;
+
+       if (typeName1)
+               typeId1 = typenameTypeId(typeName1);
+
+       if (typeName2)
+               typeId2 = typenameTypeId(typeName2);
+
+       if (OidIsValid(typeId1) && OidIsValid(typeId2))
+               oprtype = 'b';
+       else if (OidIsValid(typeId1))
+               oprtype = 'r';
+       else
+               oprtype = 'l';
+
+       relation = heap_openr(OperatorRelationName, RowExclusiveLock);
+
+       tup = SearchSysCacheCopy(OPERNAME,
+                                                        PointerGetDatum(operatorName),
+                                                        ObjectIdGetDatum(typeId1),
+                                                        ObjectIdGetDatum(typeId2),
+                                                        CharGetDatum(oprtype));
+
+       if (HeapTupleIsValid(tup))
+       {
+               if (!pg_oper_ownercheck(tup->t_data->t_oid, GetUserId()))
+                       elog(ERROR, "RemoveOperator: operator '%s': permission denied",
+                                operatorName);
+
+               /* Delete any comments associated with this operator */
+               DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
+
+               simple_heap_delete(relation, &tup->t_self);
+       }
+       else
+       {
+               if (OidIsValid(typeId1) && OidIsValid(typeId2))
+               {
+                       elog(ERROR, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist",
+                                operatorName,
+                                TypeNameToString(typeName1),
+                                TypeNameToString(typeName2));
+               }
+               else if (OidIsValid(typeId1))
+               {
+                       elog(ERROR, "RemoveOperator: right unary operator '%s' taking '%s' does not exist",
+                                operatorName,
+                                TypeNameToString(typeName1));
+               }
+               else
+               {
+                       elog(ERROR, "RemoveOperator: left unary operator '%s' taking '%s' does not exist",
+                                operatorName,
+                                TypeNameToString(typeName2));
+               }
+       }
+       heap_freetuple(tup);
+       heap_close(relation, RowExclusiveLock);
+}
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
new file mode 100644 (file)
index 0000000..6f690c0
--- /dev/null
@@ -0,0 +1,234 @@
+/*-------------------------------------------------------------------------
+ *
+ * portalcmds.c
+ *       portal support code
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "commands/portalcmds.h"
+#include "executor/executor.h"
+
+
+/*
+ * 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);
+}
index 4ef8d8f72a0bee3b3a69cab019cd467181aa6bd2..2ad25fdbd4f2cf977f9cd78175e99cd70b7a40b9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.30 2002/04/09 20:35:48 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.31 2002/04/15 05:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "commands/proclang.h"
+#include "commands/defrem.h"
 #include "fmgr.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
 #include "utils/syscache.h"
 
 
-/*
- * Translate the input language name to lower case.
- */
-static void
-case_translate_language_name(const char *input, char *output)
-{
-       int                     i;
-
-       for (i = 0; i < NAMEDATALEN && input[i]; ++i)
-               output[i] = tolower((unsigned char) input[i]);
-
-       output[i] = '\0';
-}
-
-
 /* ---------------------------------------------------------------------
  * CREATE PROCEDURAL LANGUAGE
  * ---------------------------------------------------------------------
diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c
deleted file mode 100644 (file)
index c32d2b2..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * remove.c
- *       POSTGRES remove (domain | function | type | operator ) utilty 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/remove.c,v 1.74 2002/04/11 19:59:58 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/namespace.h"
-#include "catalog/pg_language.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-#include "commands/comment.h"
-#include "commands/defrem.h"
-#include "miscadmin.h"
-#include "parser/parse.h"
-#include "parser/parse_func.h"
-#include "parser/parse_type.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/syscache.h"
-
-
-/*
- * RemoveOperator
- *             Deletes an operator.
- *
- * Exceptions:
- *             BadArg if name is invalid.
- *             BadArg if type1 is invalid.
- *             "ERROR" if operator nonexistent.
- *             ...
- */
-void
-RemoveOperator(char *operatorName,             /* operator name */
-                          TypeName *typeName1, /* left argument type name */
-                          TypeName *typeName2) /* right argument type name */
-{
-       Relation        relation;
-       HeapTuple       tup;
-       Oid                     typeId1 = InvalidOid;
-       Oid                     typeId2 = InvalidOid;
-       char            oprtype;
-
-       if (typeName1)
-               typeId1 = typenameTypeId(typeName1);
-
-       if (typeName2)
-               typeId2 = typenameTypeId(typeName2);
-
-       if (OidIsValid(typeId1) && OidIsValid(typeId2))
-               oprtype = 'b';
-       else if (OidIsValid(typeId1))
-               oprtype = 'r';
-       else
-               oprtype = 'l';
-
-       relation = heap_openr(OperatorRelationName, RowExclusiveLock);
-
-       tup = SearchSysCacheCopy(OPERNAME,
-                                                        PointerGetDatum(operatorName),
-                                                        ObjectIdGetDatum(typeId1),
-                                                        ObjectIdGetDatum(typeId2),
-                                                        CharGetDatum(oprtype));
-
-       if (HeapTupleIsValid(tup))
-       {
-               if (!pg_oper_ownercheck(tup->t_data->t_oid, GetUserId()))
-                       elog(ERROR, "RemoveOperator: operator '%s': permission denied",
-                                operatorName);
-
-               /* Delete any comments associated with this operator */
-               DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
-
-               simple_heap_delete(relation, &tup->t_self);
-       }
-       else
-       {
-               if (OidIsValid(typeId1) && OidIsValid(typeId2))
-               {
-                       elog(ERROR, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist",
-                                operatorName,
-                                TypeNameToString(typeName1),
-                                TypeNameToString(typeName2));
-               }
-               else if (OidIsValid(typeId1))
-               {
-                       elog(ERROR, "RemoveOperator: right unary operator '%s' taking '%s' does not exist",
-                                operatorName,
-                                TypeNameToString(typeName1));
-               }
-               else
-               {
-                       elog(ERROR, "RemoveOperator: left unary operator '%s' taking '%s' does not exist",
-                                operatorName,
-                                TypeNameToString(typeName2));
-               }
-       }
-       heap_freetuple(tup);
-       heap_close(relation, RowExclusiveLock);
-}
-
-#ifdef NOTYET
-/*
- * this stuff is to support removing all reference to a type
- * don't use it  - pma 2/1/94
- */
-/*
- *     SingleOpOperatorRemove
- *             Removes all operators that have operands or a result of type 'typeOid'.
- */
-static void
-SingleOpOperatorRemove(Oid typeOid)
-{
-       Relation        rel;
-       ScanKeyData key[3];
-       HeapScanDesc scan;
-       HeapTuple       tup;
-       static          attnums[3] = {7, 8, 9}; /* left, right, return */
-       int                     i;
-
-       ScanKeyEntryInitialize(&key[0],
-                                                  0, 0, F_OIDEQ, (Datum) typeOid);
-       rel = heap_openr(OperatorRelationName, RowExclusiveLock);
-       for (i = 0; i < 3; ++i)
-       {
-               key[0].sk_attno = attnums[i];
-               scan = heap_beginscan(rel, 0, SnapshotNow, 1, key);
-               while (HeapTupleIsValid(tup = heap_getnext(scan, 0)))
-               {
-                       /* Delete any comments associated with this operator */
-                       DeleteComments(tup->t_data->t_oid, RelationGetRelid(rel));
-
-                       simple_heap_delete(rel, &tup->t_self);
-               }
-
-               heap_endscan(scan);
-       }
-       heap_close(rel, RowExclusiveLock);
-}
-
-/*
- *     AttributeAndRelationRemove
- *             Removes all entries in the attribute and relation relations
- *             that contain entries of type 'typeOid'.
- *             Currently nothing calls this code, it is untested.
- */
-static void
-AttributeAndRelationRemove(Oid typeOid)
-{
-       struct oidlist
-       {
-               Oid                     reloid;
-               struct oidlist *next;
-       };
-       struct oidlist *oidptr,
-                          *optr;
-       Relation        rel;
-       ScanKeyData key[1];
-       HeapScanDesc scan;
-       HeapTuple       tup;
-
-       /*
-        * Get the oid's of the relations to be removed by scanning the entire
-        * attribute relation. We don't need to remove the attributes here,
-        * because amdestroy will remove all attributes of the relation. XXX
-        * should check for duplicate relations
-        */
-
-       ScanKeyEntryInitialize(&key[0],
-                                                  0, 3, F_OIDEQ, (Datum) typeOid);
-
-       oidptr = (struct oidlist *) palloc(sizeof(*oidptr));
-       oidptr->next = NULL;
-       optr = oidptr;
-       rel = heap_openr(AttributeRelationName, AccessShareLock);
-       scan = heap_beginscan(rel, 0, SnapshotNow, 1, key);
-       while (HeapTupleIsValid(tup = heap_getnext(scan, 0)))
-       {
-               optr->reloid = ((Form_pg_attribute) GETSTRUCT(tup))->attrelid;
-               optr->next = (struct oidlist *) palloc(sizeof(*oidptr));
-               optr = optr->next;
-       }
-       optr->next = NULL;
-       heap_endscan(scan);
-       heap_close(rel, AccessShareLock);
-
-       optr = oidptr;
-
-       ScanKeyEntryInitialize(&key[0], 0,
-                                                  ObjectIdAttributeNumber,
-                                                  F_OIDEQ, (Datum) 0);
-       /* get RowExclusiveLock because heap_destroy will need it */
-       rel = heap_openr(RelationRelationName, RowExclusiveLock);
-       while (PointerIsValid((char *) optr->next))
-       {
-               Oid             relOid = (optr++)->reloid;
-
-               key[0].sk_argument = ObjectIdGetDatum(relOid);
-               scan = heap_beginscan(rel, 0, SnapshotNow, 1, key);
-               tup = heap_getnext(scan, 0);
-               if (HeapTupleIsValid(tup))
-                       heap_drop_with_catalog(relOid, allowSystemTableMods);
-               heap_endscan(scan);
-       }
-       heap_close(rel, RowExclusiveLock);
-}
-#endif   /* NOTYET */
-
-/*
- *     TypeRemove
- *             Removes a datatype.
- *
- * NOTE: since this tries to remove the associated array type too, it'll
- * only work on scalar types.
- */
-void
-RemoveType(List *names)
-{
-       TypeName   *typename;
-       Relation        relation;
-       Oid                     typeoid;
-       HeapTuple       tup;
-
-       /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
-
-       relation = heap_openr(TypeRelationName, RowExclusiveLock);
-
-       /* Use LookupTypeName here so that shell types can be removed. */
-       typeoid = LookupTypeName(typename);
-       if (!OidIsValid(typeoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
-
-       tup = SearchSysCache(TYPEOID,
-                                                ObjectIdGetDatum(typeoid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
-
-       if (!pg_type_ownercheck(typeoid, GetUserId()))
-               elog(ERROR, "RemoveType: type '%s': permission denied",
-                        TypeNameToString(typename));
-
-       /* Delete any comments associated with this type */
-       DeleteComments(typeoid, RelationGetRelid(relation));
-
-       /* Remove the type tuple from pg_type */
-       simple_heap_delete(relation, &tup->t_self);
-
-       ReleaseSysCache(tup);
-
-       /* Now, delete the "array of" that type */
-       typename->arrayBounds = makeList1(makeInteger(1));
-
-       typeoid = LookupTypeName(typename);
-       if (!OidIsValid(typeoid))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
-
-       tup = SearchSysCache(TYPEOID,
-                                                ObjectIdGetDatum(typeoid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))
-               elog(ERROR, "Type \"%s\" does not exist",
-                        TypeNameToString(typename));
-
-       DeleteComments(typeoid, RelationGetRelid(relation));
-
-       simple_heap_delete(relation, &tup->t_self);
-
-       ReleaseSysCache(tup);
-
-       heap_close(relation, RowExclusiveLock);
-}
-
-/*
- *     RemoveDomain
- *             Removes a domain.
- */
-void
-RemoveDomain(List *names, int behavior)
-{
-       TypeName   *typename;
-       Relation        relation;
-       Oid                     typeoid;
-       HeapTuple       tup;
-       char            typtype;
-
-       /* CASCADE unsupported */
-       if (behavior == CASCADE)
-               elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
-
-       /* Make a TypeName so we can use standard type lookup machinery */
-       typename = makeNode(TypeName);
-       typename->names = names;
-       typename->typmod = -1;
-       typename->arrayBounds = NIL;
-
-       relation = heap_openr(TypeRelationName, RowExclusiveLock);
-
-       typeoid = typenameTypeId(typename);
-
-       tup = SearchSysCache(TYPEOID,
-                                                ObjectIdGetDatum(typeoid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))
-               elog(ERROR, "RemoveDomain: type '%s' does not exist",
-                        TypeNameToString(typename));
-
-       if (!pg_type_ownercheck(typeoid, GetUserId()))
-               elog(ERROR, "RemoveDomain: type '%s': permission denied",
-                        TypeNameToString(typename));
-
-       /* Check that this is actually a domain */
-       typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
-
-       if (typtype != 'd')
-               elog(ERROR, "%s is not a domain",
-                        TypeNameToString(typename));
-
-       /* Delete any comments associated with this type */
-       DeleteComments(typeoid, RelationGetRelid(relation));
-
-       /* Remove the type tuple from pg_type */
-       simple_heap_delete(relation, &tup->t_self);
-
-       ReleaseSysCache(tup);
-
-       /* At present, domains don't have associated array types */
-
-       heap_close(relation, RowExclusiveLock);
-}
-
-/*
- * RemoveFunction
- *             Deletes a function.
- *
- * Exceptions:
- *             BadArg if name is invalid.
- *             "ERROR" if function nonexistent.
- *             ...
- */
-void
-RemoveFunction(List *functionName,             /* function name to be removed */
-                          List *argTypes)      /* list of TypeName nodes */
-{
-       Oid                     funcOid;
-       Relation        relation;
-       HeapTuple       tup;
-
-       funcOid = LookupFuncNameTypeNames(functionName, argTypes, 
-                                                                         true, "RemoveFunction");
-
-       relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
-       tup = SearchSysCache(PROCOID,
-                                                ObjectIdGetDatum(funcOid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))     /* should not happen */
-               elog(ERROR, "RemoveFunction: couldn't find tuple for function %s",
-                        NameListToString(functionName));
-
-       if (!pg_proc_ownercheck(funcOid, GetUserId()))
-               elog(ERROR, "RemoveFunction: function '%s': permission denied",
-                        NameListToString(functionName));
-
-       if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
-               elog(ERROR, "RemoveFunction: function '%s' is an aggregate"
-                        "\n\tUse DROP AGGREGATE to remove it",
-                        NameListToString(functionName));
-
-       if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
-       {
-               /* "Helpful" WARNING when removing a builtin function ... */
-               elog(WARNING, "Removing built-in function \"%s\"",
-                        NameListToString(functionName));
-       }
-
-       /* Delete any comments associated with this function */
-       DeleteComments(funcOid, RelationGetRelid(relation));
-
-       simple_heap_delete(relation, &tup->t_self);
-
-       ReleaseSysCache(tup);
-
-       heap_close(relation, RowExclusiveLock);
-}
-
-void
-RemoveAggregate(List *aggName, TypeName *aggType)
-{
-       Relation        relation;
-       HeapTuple       tup;
-       Oid                     basetypeID;
-       Oid                     procOid;
-
-       /*
-        * if a basetype is passed in, then attempt to find an aggregate for
-        * that specific type.
-        *
-        * else if the basetype is blank, then attempt to find an aggregate with
-        * a basetype of zero.  This is valid. It means that the aggregate is
-        * to apply to all basetypes (eg, COUNT).
-        */
-       if (aggType)
-               basetypeID = typenameTypeId(aggType);
-       else
-               basetypeID = InvalidOid;
-
-       procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
-
-       /* Permission check */
-
-       if (!pg_proc_ownercheck(procOid, GetUserId()))
-       {
-               if (basetypeID == InvalidOid)
-                       elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied",
-                                NameListToString(aggName));
-               else
-                       elog(ERROR, "RemoveAggregate: aggregate %s for type %s: permission denied",
-                                NameListToString(aggName), format_type_be(basetypeID));
-       }
-
-       /* Remove the pg_proc tuple */
-
-       relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
-
-       tup = SearchSysCache(PROCOID,
-                                                ObjectIdGetDatum(procOid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))     /* should not happen */
-               elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s",
-                        NameListToString(aggName));
-
-       /* Delete any comments associated with this function */
-       DeleteComments(procOid, RelationGetRelid(relation));
-
-       simple_heap_delete(relation, &tup->t_self);
-
-       ReleaseSysCache(tup);
-
-       heap_close(relation, RowExclusiveLock);
-
-       /* Remove the pg_aggregate tuple */
-
-       relation = heap_openr(AggregateRelationName, RowExclusiveLock);
-
-       tup = SearchSysCache(AGGFNOID,
-                                                ObjectIdGetDatum(procOid),
-                                                0, 0, 0);
-       if (!HeapTupleIsValid(tup))     /* should not happen */
-               elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s",
-                        NameListToString(aggName));
-
-       simple_heap_delete(relation, &tup->t_self);
-
-       ReleaseSysCache(tup);
-
-       heap_close(relation, RowExclusiveLock);
-}
diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c
deleted file mode 100644 (file)
index 21db59b..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * rename.c
- *       renameatt() and renamerel() reside here.
- *
- * 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/rename.c,v 1.70 2002/04/12 20:38:24 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include <errno.h>
-
-#include "access/genam.h"
-#include "access/heapam.h"
-#include "access/itup.h"
-#include "catalog/catname.h"
-#include "catalog/pg_index.h"
-#include "catalog/pg_trigger.h"
-#include "catalog/pg_type.h"
-#include "catalog/heap.h"
-#include "catalog/indexing.h"
-#include "catalog/catalog.h"
-#include "commands/rename.h"
-#include "commands/trigger.h"
-#include "miscadmin.h"
-#include "storage/smgr.h"
-#include "optimizer/prep.h"
-#include "rewrite/rewriteDefine.h"
-#include "rewrite/rewriteSupport.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/lsyscache.h"
-#include "utils/relcache.h"
-#include "utils/syscache.h"
-
-
-#define RI_TRIGGER_PK  1               /* is a trigger on the PK relation */
-#define RI_TRIGGER_FK  2               /* is a trigger on the FK relation */
-#define RI_TRIGGER_NONE 0              /* is not an RI trigger function */
-
-static int     ri_trigger_type(Oid tgfoid);
-static void update_ri_trigger_args(Oid relid,
-                                          const char *oldname,
-                                          const char *newname,
-                                          bool fk_scan,
-                                          bool update_relname);
-
-
-/*
- *             renameatt               - changes the name of a attribute in a relation
- *
- *             Attname attribute is changed in attribute catalog.
- *             No record of the previous attname is kept (correct?).
- *
- *             get proper relrelation from relation catalog (if not arg)
- *             scan attribute catalog
- *                             for name conflict (within rel)
- *                             for original attribute (if not arg)
- *             modify attname in attribute tuple
- *             insert modified attribute in attribute catalog
- *             delete original attribute from attribute catalog
- */
-void
-renameatt(Oid relid,
-                 const char *oldattname,
-                 const char *newattname,
-                 bool recurse)
-{
-       Relation        targetrelation;
-       Relation        attrelation;
-       HeapTuple       atttup;
-       List       *indexoidlist;
-       List       *indexoidscan;
-
-       /*
-        * Grab an exclusive lock on the target table, which we will NOT
-        * release until end of transaction.
-        */
-       targetrelation = heap_open(relid, AccessExclusiveLock);
-
-       /*
-        * 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(targetrelation))
-               elog(ERROR, "renameatt: class \"%s\" is a system catalog",
-                        RelationGetRelationName(targetrelation));
-       if (!pg_class_ownercheck(relid, GetUserId()))
-               elog(ERROR, "renameatt: you do not own class \"%s\"",
-                        RelationGetRelationName(targetrelation));
-
-       /*
-        * if the 'recurse' flag is set then we are supposed to rename this
-        * attribute in all classes that inherit from 'relname' (as well as in
-        * 'relname').
-        *
-        * any permissions or problems with duplicate attributes will cause the
-        * whole transaction to abort, which is what we want -- all or
-        * nothing.
-        */
-       if (recurse)
-       {
-               List       *child,
-                                  *children;
-
-               /* this routine is actually in the planner */
-               children = find_all_inheritors(relid);
-
-               /*
-                * 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 == relid)
-                               continue;
-                       /* note we need not recurse again! */
-                       renameatt(childrelid, oldattname, newattname, false);
-               }
-       }
-
-       attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
-
-       atttup = SearchSysCacheCopy(ATTNAME,
-                                                               ObjectIdGetDatum(relid),
-                                                               PointerGetDatum(oldattname),
-                                                               0, 0);
-       if (!HeapTupleIsValid(atttup))
-               elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname);
-
-       if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0)
-               elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname);
-
-       /* should not already exist */
-       if (SearchSysCacheExists(ATTNAME,
-                                                        ObjectIdGetDatum(relid),
-                                                        PointerGetDatum(newattname),
-                                                        0, 0))
-               elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
-
-       StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
-                       newattname, NAMEDATALEN);
-
-       simple_heap_update(attrelation, &atttup->t_self, atttup);
-
-       /* 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, atttup);
-               CatalogCloseIndices(Num_pg_attr_indices, irelations);
-       }
-
-       heap_freetuple(atttup);
-
-       /*
-        * Update column names of indexes that refer to the column being
-        * renamed.
-        */
-       indexoidlist = RelationGetIndexList(targetrelation);
-
-       foreach(indexoidscan, indexoidlist)
-       {
-               Oid                     indexoid = lfirsti(indexoidscan);
-               HeapTuple       indextup;
-
-               /*
-                * First check to see if index is a functional index. If so, its
-                * column name is a function name and shouldn't be renamed here.
-                */
-               indextup = SearchSysCache(INDEXRELID,
-                                                                 ObjectIdGetDatum(indexoid),
-                                                                 0, 0, 0);
-               if (!HeapTupleIsValid(indextup))
-                       elog(ERROR, "renameatt: can't find index id %u", indexoid);
-               if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc))
-               {
-                       ReleaseSysCache(indextup);
-                       continue;
-               }
-               ReleaseSysCache(indextup);
-
-               /*
-                * Okay, look to see if any column name of the index matches the
-                * old attribute name.
-                */
-               atttup = SearchSysCacheCopy(ATTNAME,
-                                                                       ObjectIdGetDatum(indexoid),
-                                                                       PointerGetDatum(oldattname),
-                                                                       0, 0);
-               if (!HeapTupleIsValid(atttup))
-                       continue;                       /* Nope, so ignore it */
-
-               /*
-                * Update the (copied) attribute tuple.
-                */
-               StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
-                               newattname, NAMEDATALEN);
-
-               simple_heap_update(attrelation, &atttup->t_self, atttup);
-
-               /* 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, atttup);
-                       CatalogCloseIndices(Num_pg_attr_indices, irelations);
-               }
-               heap_freetuple(atttup);
-       }
-
-       freeList(indexoidlist);
-
-       heap_close(attrelation, RowExclusiveLock);
-
-       /*
-        * Update att name in any RI triggers associated with the relation.
-        */
-       if (targetrelation->rd_rel->reltriggers > 0)
-       {
-               /* update tgargs column reference where att is primary key */
-               update_ri_trigger_args(RelationGetRelid(targetrelation),
-                                                          oldattname, newattname,
-                                                          false, false);
-               /* update tgargs column reference where att is foreign key */
-               update_ri_trigger_args(RelationGetRelid(targetrelation),
-                                                          oldattname, newattname,
-                                                          true, false);
-       }
-
-       heap_close(targetrelation, NoLock); /* close rel but keep lock! */
-}
-
-/*
- *             renamerel               - change the name of a relation
- *
- *             XXX - When renaming sequences, we don't bother to modify the
- *                       sequence name that is stored within the sequence itself
- *                       (this would cause problems with MVCC). In the future,
- *                       the sequence name should probably be removed from the
- *                       sequence, AFAIK there's no need for it to be there.
- */
-void
-renamerel(Oid relid, const char *newrelname)
-{
-       Relation        targetrelation;
-       Relation        relrelation;    /* for RELATION relation */
-       HeapTuple       reltup;
-       Oid                     namespaceId;
-       char            relkind;
-       bool            relhastriggers;
-       Relation        irelations[Num_pg_class_indices];
-
-       /*
-        * Grab an exclusive lock on the target table or index, which we will
-        * NOT release until end of transaction.
-        */
-       targetrelation = relation_open(relid, AccessExclusiveLock);
-
-       namespaceId = RelationGetNamespace(targetrelation);
-
-       /* Validity checks */
-       if (!allowSystemTableMods &&
-               IsSystemRelation(targetrelation))
-               elog(ERROR, "renamerel: system relation \"%s\" may not be renamed",
-                        RelationGetRelationName(targetrelation));
-
-       relkind = targetrelation->rd_rel->relkind;
-       relhastriggers = (targetrelation->rd_rel->reltriggers > 0);
-
-       /*
-        * Find relation's pg_class tuple, and make sure newrelname isn't in
-        * use.
-        */
-       relrelation = heap_openr(RelationRelationName, RowExclusiveLock);
-
-       reltup = SearchSysCacheCopy(RELOID,
-                                                               PointerGetDatum(relid),
-                                                               0, 0, 0);
-       if (!HeapTupleIsValid(reltup))
-               elog(ERROR, "renamerel: relation \"%s\" does not exist",
-                        RelationGetRelationName(targetrelation));
-
-       if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
-               elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
-
-       /*
-        * Update pg_class tuple with new relname.      (Scribbling on reltup is
-        * OK because it's a copy...)
-        */
-       StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname),
-                       newrelname, NAMEDATALEN);
-
-       simple_heap_update(relrelation, &reltup->t_self, reltup);
-
-       /* keep the system catalog indices current */
-       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations);
-       CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, reltup);
-       CatalogCloseIndices(Num_pg_class_indices, irelations);
-
-       heap_close(relrelation, NoLock);
-       heap_freetuple(reltup);
-
-       /*
-        * Also rename the associated type, if any.
-        */
-       if (relkind != RELKIND_INDEX)
-               TypeRename(RelationGetRelationName(targetrelation), namespaceId,
-                                  newrelname);
-
-       /*
-        * If it's a view, must also rename the associated ON SELECT rule.
-        */
-       if (relkind == RELKIND_VIEW)
-       {
-               char       *oldrulename,
-                                  *newrulename;
-
-               oldrulename = MakeRetrieveViewRuleName(RelationGetRelationName(targetrelation));
-               newrulename = MakeRetrieveViewRuleName(newrelname);
-               RenameRewriteRule(oldrulename, newrulename);
-       }
-
-       /*
-        * Update rel name in any RI triggers associated with the relation.
-        */
-       if (relhastriggers)
-       {
-               /* update tgargs where relname is primary key */
-               update_ri_trigger_args(relid,
-                                                          RelationGetRelationName(targetrelation),
-                                                          newrelname,
-                                                          false, true);
-               /* update tgargs where relname is foreign key */
-               update_ri_trigger_args(relid,
-                                                          RelationGetRelationName(targetrelation),
-                                                          newrelname,
-                                                          true, true);
-       }
-
-       /*
-        * Close rel, but keep exclusive lock!
-        */
-       relation_close(targetrelation, NoLock);
-}
-
-/*
- * Given a trigger function OID, determine whether it is an RI trigger,
- * and if so whether it is attached to PK or FK relation.
- *
- * XXX this probably doesn't belong here; should be exported by
- * ri_triggers.c
- */
-static int
-ri_trigger_type(Oid tgfoid)
-{
-       switch (tgfoid)
-       {
-               case F_RI_FKEY_CASCADE_DEL:
-               case F_RI_FKEY_CASCADE_UPD:
-               case F_RI_FKEY_RESTRICT_DEL:
-               case F_RI_FKEY_RESTRICT_UPD:
-               case F_RI_FKEY_SETNULL_DEL:
-               case F_RI_FKEY_SETNULL_UPD:
-               case F_RI_FKEY_SETDEFAULT_DEL:
-               case F_RI_FKEY_SETDEFAULT_UPD:
-               case F_RI_FKEY_NOACTION_DEL:
-               case F_RI_FKEY_NOACTION_UPD:
-                       return RI_TRIGGER_PK;
-
-               case F_RI_FKEY_CHECK_INS:
-               case F_RI_FKEY_CHECK_UPD:
-                       return RI_TRIGGER_FK;
-       }
-
-       return RI_TRIGGER_NONE;
-}
-
-/*
- * Scan pg_trigger for RI triggers that are on the specified relation
- * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
- * is true).  Update RI trigger args fields matching oldname to contain
- * newname instead.  If update_relname is true, examine the relname
- * fields; otherwise examine the attname fields.
- */
-static void
-update_ri_trigger_args(Oid relid,
-                                          const char *oldname,
-                                          const char *newname,
-                                          bool fk_scan,
-                                          bool update_relname)
-{
-       Relation        tgrel;
-       Relation        irel;
-       ScanKeyData skey[1];
-       IndexScanDesc idxtgscan;
-       RetrieveIndexResult idxres;
-       Datum           values[Natts_pg_trigger];
-       char            nulls[Natts_pg_trigger];
-       char            replaces[Natts_pg_trigger];
-
-       tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
-       if (fk_scan)
-               irel = index_openr(TriggerConstrRelidIndex);
-       else
-               irel = index_openr(TriggerRelidIndex);
-
-       ScanKeyEntryInitialize(&skey[0], 0x0,
-                                                  1,   /* always column 1 of index */
-                                                  F_OIDEQ,
-                                                  ObjectIdGetDatum(relid));
-       idxtgscan = index_beginscan(irel, false, 1, skey);
-
-       while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL)
-       {
-               HeapTupleData tupledata;
-               Buffer          buffer;
-               HeapTuple       tuple;
-               Form_pg_trigger pg_trigger;
-               bytea      *val;
-               bytea      *newtgargs;
-               bool            isnull;
-               int                     tg_type;
-               bool            examine_pk;
-               bool            changed;
-               int                     tgnargs;
-               int                     i;
-               int                     newlen;
-               const char *arga[RI_MAX_ARGUMENTS];
-               const char *argp;
-
-               tupledata.t_self = idxres->heap_iptr;
-               heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan);
-               pfree(idxres);
-               if (!tupledata.t_data)
-                       continue;
-               tuple = &tupledata;
-               pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
-               tg_type = ri_trigger_type(pg_trigger->tgfoid);
-               if (tg_type == RI_TRIGGER_NONE)
-               {
-                       /* Not an RI trigger, forget it */
-                       ReleaseBuffer(buffer);
-                       continue;
-               }
-
-               /*
-                * It is an RI trigger, so parse the tgargs bytea.
-                *
-                * NB: we assume the field will never be compressed or moved out of
-                * line; so does trigger.c ...
-                */
-               tgnargs = pg_trigger->tgnargs;
-               val = (bytea *) fastgetattr(tuple,
-                                                                       Anum_pg_trigger_tgargs,
-                                                                       tgrel->rd_att, &isnull);
-               if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
-                       tgnargs > RI_MAX_ARGUMENTS)
-               {
-                       /* This probably shouldn't happen, but ignore busted triggers */
-                       ReleaseBuffer(buffer);
-                       continue;
-               }
-               argp = (const char *) VARDATA(val);
-               for (i = 0; i < tgnargs; i++)
-               {
-                       arga[i] = argp;
-                       argp += strlen(argp) + 1;
-               }
-
-               /*
-                * Figure out which item(s) to look at.  If the trigger is
-                * primary-key type and attached to my rel, I should look at the
-                * PK fields; if it is foreign-key type and attached to my rel, I
-                * should look at the FK fields.  But the opposite rule holds when
-                * examining triggers found by tgconstrrel search.
-                */
-               examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
-
-               changed = false;
-               if (update_relname)
-               {
-                       /* Change the relname if needed */
-                       i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
-                       if (strcmp(arga[i], oldname) == 0)
-                       {
-                               arga[i] = newname;
-                               changed = true;
-                       }
-               }
-               else
-               {
-                       /* Change attname(s) if needed */
-                       i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
-                               RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
-                       for (; i < tgnargs; i += 2)
-                       {
-                               if (strcmp(arga[i], oldname) == 0)
-                               {
-                                       arga[i] = newname;
-                                       changed = true;
-                               }
-                       }
-               }
-
-               if (!changed)
-               {
-                       /* Don't need to update this tuple */
-                       ReleaseBuffer(buffer);
-                       continue;
-               }
-
-               /*
-                * Construct modified tgargs bytea.
-                */
-               newlen = VARHDRSZ;
-               for (i = 0; i < tgnargs; i++)
-                       newlen += strlen(arga[i]) + 1;
-               newtgargs = (bytea *) palloc(newlen);
-               VARATT_SIZEP(newtgargs) = newlen;
-               newlen = VARHDRSZ;
-               for (i = 0; i < tgnargs; i++)
-               {
-                       strcpy(((char *) newtgargs) + newlen, arga[i]);
-                       newlen += strlen(arga[i]) + 1;
-               }
-
-               /*
-                * Build modified tuple.
-                */
-               for (i = 0; i < Natts_pg_trigger; i++)
-               {
-                       values[i] = (Datum) 0;
-                       replaces[i] = ' ';
-                       nulls[i] = ' ';
-               }
-               values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
-               replaces[Anum_pg_trigger_tgargs - 1] = 'r';
-
-               tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces);
-
-               /*
-                * Now we can release hold on original tuple.
-                */
-               ReleaseBuffer(buffer);
-
-               /*
-                * Update pg_trigger and its indexes
-                */
-               simple_heap_update(tgrel, &tuple->t_self, tuple);
-
-               {
-                       Relation        irelations[Num_pg_attr_indices];
-
-                       CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations);
-                       CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple);
-                       CatalogCloseIndices(Num_pg_trigger_indices, irelations);
-               }
-
-               /* free up our scratch memory */
-               pfree(newtgargs);
-               heap_freetuple(tuple);
-       }
-
-       index_endscan(idxtgscan);
-       index_close(irel);
-
-       heap_close(tgrel, RowExclusiveLock);
-
-       /*
-        * Increment cmd counter to make updates visible; this is needed in
-        * case the same tuple has to be updated again by next pass (can
-        * happen in case of a self-referential FK relationship).
-        */
-       CommandCounterIncrement();
-}
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
new file mode 100644 (file)
index 0000000..191d5e3
--- /dev/null
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemacmds.c
+ *       schema creation command support code
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/catalog.h"
+#include "catalog/pg_namespace.h"
+#include "commands/schemacmds.h"
+#include "miscadmin.h"
+#include "parser/analyze.h"
+#include "tcop/utility.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * 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);
+}
index a7678c5ce38484567e7ff38c25a27bf3f463947c..0fa56fcfb7db7fdba605fade2ae2ecc978069076 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.76 2002/03/30 01:02:41 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.77 2002/04/15 05:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,7 @@
 #include "access/heapam.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_type.h"
-#include "commands/creatinh.h"
+#include "commands/tablecmds.h"
 #include "commands/sequence.h"
 #include "miscadmin.h"
 #include "utils/acl.h"
similarity index 52%
rename from src/backend/commands/command.c
rename to src/backend/commands/tablecmds.c
index 9a20d8329ad9a60e7680c32ac936f10320f745a7..18cea28df12f79028abd87bb87f8631b3483a40e 100644 (file)
@@ -1,19 +1,14 @@
 /*-------------------------------------------------------------------------
  *
- * command.c
- *       random postgres portal and utility support code
+ * tablecmds.c
+ *       Commands for altering table structures and settings
  *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2002, 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.
+ *       $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_attrdef.h"
-#include "catalog/pg_index.h"
+#include "catalog/pg_inherits.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
-#include "catalog/pg_relcheck.h"
+#include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
-#include "commands/command.h"
+#include "commands/tablecmds.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 "rewrite/rewriteDefine.h"
+#include "rewrite/rewriteSupport.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -61,220 +54,27 @@ static void drop_default(Oid relid, int16 attnum);
 static bool needs_toast_table(Relation rel);
 static void CheckTupleType(Form_pg_class tuple_class);
 
+static List *MergeAttributes(List *schema, List *supers, bool istemp,
+                               List **supOids, List **supconstr, bool *supHasOids);
+static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
+static void StoreCatalogInheritance(Oid relationId, List *supers);
+static int     findAttrByName(const char *attributeName, List *schema);
+static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
+static List *MergeDomainAttributes(List *schema);
 
-/*
- *             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;
+/* Used by attribute and relation renaming routines: */
 
-       /*
-        * sanity checks
-        */
-       if (name == NULL)
-       {
-               elog(WARNING, "PerformPortalClose: missing portal name");
-               return;
-       }
+#define RI_TRIGGER_PK  1               /* is a trigger on the PK relation */
+#define RI_TRIGGER_FK  2               /* is a trigger on the FK relation */
+#define RI_TRIGGER_NONE 0              /* is not an RI trigger function */
 
-       /*
-        * get the portal from the portal name
-        */
-       portal = GetPortalByName(name);
-       if (!PortalIsValid(portal))
-       {
-               elog(WARNING, "PerformPortalClose: portal \"%s\" not found",
-                        name);
-               return;
-       }
+static int     ri_trigger_type(Oid tgfoid);
+static void update_ri_trigger_args(Oid relid,
+                                          const char *oldname,
+                                          const char *newname,
+                                          bool fk_scan,
+                                          bool update_relname);
 
-       /*
-        * Note: PortalCleanup is called as a side-effect
-        */
-       PortalDrop(portal);
-}
 
 /* ----------------
  *             AlterTableAddColumn
@@ -1856,142 +1656,1427 @@ needs_toast_table(Relation rel)
        return (tuple_length > TOAST_TUPLE_THRESHOLD);
 }
 
-/*
- * LOCK TABLE
+
+/* ----------------------------------------------------------------
+ *             DefineRelation
+ *                             Creates a new relation.
+ *
+ * If successful, returns the OID of the new relation.
+ * ----------------------------------------------------------------
  */
-void
-LockTableCommand(LockStmt *lockstmt)
+Oid
+DefineRelation(CreateStmt *stmt, char relkind)
 {
-       List       *p;
+       char       *relname = palloc(NAMEDATALEN);
+       Oid                     namespaceId;
+       List       *schema = stmt->tableElts;
+       int                     numberOfAttributes;
+       Oid                     relationId;
+       Relation        rel;
+       TupleDesc       descriptor;
+       List       *inheritOids;
+       List       *old_constraints;
+       bool            parentHasOids;
+       List       *rawDefaults;
+       List       *listptr;
+       int                     i;
+       AttrNumber      attnum;
+
+       /*
+        * Truncate relname to appropriate length (probably a waste of time,
+        * as parser should have done this already).
+        */
+       StrNCpy(relname, (stmt->relation)->relname, NAMEDATALEN);
+
+       /*
+        * Look up the namespace in which we are supposed to create the
+        * relation.
+        */
+       namespaceId = RangeVarGetCreationNamespace(stmt->relation);
+
+       /*
+        * Merge domain attributes into the known columns before processing table
+        * inheritance.  Otherwise we risk adding double constraints to a
+        * domain-type column that's inherited.
+        */
+       schema = MergeDomainAttributes(schema);
+
+       /*
+        * Look up inheritance ancestors and generate relation schema,
+        * including inherited attributes.
+        */
+       schema = MergeAttributes(schema, stmt->inhRelations,
+                                                        stmt->relation->istemp,
+                                                        &inheritOids, &old_constraints, &parentHasOids);
+
+       numberOfAttributes = length(schema);
+       if (numberOfAttributes <= 0)
+               elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute");
 
        /*
-        * Iterate over the list and open, lock, and close the relations one
-        * at a time
+        * Create a relation descriptor from the relation schema and create
+        * the relation.  Note that in this stage only inherited (pre-cooked)
+        * defaults and constraints will be included into the new relation.
+        * (BuildDescForRelation takes care of the inherited defaults, but we
+        * have to copy inherited constraints here.)
         */
+       descriptor = BuildDescForRelation(schema);
 
-       foreach(p, lockstmt->relations)
+       if (old_constraints != NIL)
        {
-               RangeVar   *relation = lfirst(p);
-               Oid                     reloid;
-               int32           aclresult;
-               Relation        rel;
+               ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
+                                                                                                       sizeof(ConstrCheck));
+               int                     ncheck = 0;
 
-               /*
-                * We don't want to open the relation until we've checked privilege.
-                * So, manually get the relation OID.
-                */
-               reloid = RangeVarGetRelid(relation, false);
+               foreach(listptr, old_constraints)
+               {
+                       Constraint *cdef = (Constraint *) lfirst(listptr);
 
-               if (lockstmt->mode == AccessShareLock)
-                       aclresult = pg_class_aclcheck(reloid, GetUserId(),
-                                                                                 ACL_SELECT);
-               else
-                       aclresult = pg_class_aclcheck(reloid, GetUserId(),
-                                                                                 ACL_UPDATE | ACL_DELETE);
+                       if (cdef->contype != CONSTR_CHECK)
+                               continue;
+
+                       if (cdef->name != NULL)
+                       {
+                               for (i = 0; i < ncheck; i++)
+                               {
+                                       if (strcmp(check[i].ccname, cdef->name) == 0)
+                                               elog(ERROR, "Duplicate CHECK constraint name: '%s'",
+                                                        cdef->name);
+                               }
+                               check[ncheck].ccname = cdef->name;
+                       }
+                       else
+                       {
+                               check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
+                               snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
+                       }
+                       Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
+                       check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
+                       ncheck++;
+               }
+               if (ncheck > 0)
+               {
+                       if (descriptor->constr == NULL)
+                       {
+                               descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
+                               descriptor->constr->defval = NULL;
+                               descriptor->constr->num_defval = 0;
+                               descriptor->constr->has_not_null = false;
+                       }
+                       descriptor->constr->num_check = ncheck;
+                       descriptor->constr->check = check;
+               }
+       }
+
+       relationId = heap_create_with_catalog(relname,
+                                                                                 namespaceId,
+                                                                                 descriptor,
+                                                                                 relkind,
+                                                                                 stmt->hasoids || parentHasOids,
+                                                                                 allowSystemTableMods);
+
+       StoreCatalogInheritance(relationId, inheritOids);
+
+       /*
+        * We must bump the command counter to make the newly-created relation
+        * tuple visible for opening.
+        */
+       CommandCounterIncrement();
+
+       /*
+        * Open the new relation and acquire exclusive lock on it.      This isn't
+        * really necessary for locking out other backends (since they can't
+        * see the new rel anyway until we commit), but it keeps the lock
+        * manager from complaining about deadlock risks.
+        */
+       rel = heap_open(relationId, AccessExclusiveLock);
+
+       /*
+        * Now add any newly specified column default values and CHECK
+        * constraints to the new relation.  These are passed to us in the
+        * form of raw parsetrees; we need to transform them to executable
+        * expression trees before they can be added. The most convenient way
+        * to do that is to apply the parser's transformExpr routine, but
+        * transformExpr doesn't work unless we have a pre-existing relation.
+        * So, the transformation has to be postponed to this final step of
+        * CREATE TABLE.
+        *
+        * First, scan schema to find new column defaults.
+        */
+       rawDefaults = NIL;
+       attnum = 0;
 
-               if (aclresult != ACLCHECK_OK)
-                       elog(ERROR, "LOCK TABLE: permission denied");
+       foreach(listptr, schema)
+       {
+               ColumnDef  *colDef = lfirst(listptr);
+               RawColumnDefault *rawEnt;
 
-               rel = relation_open(reloid, lockstmt->mode);
+               attnum++;
 
-               /* 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);
+               if (colDef->raw_default == NULL)
+                       continue;
+               Assert(colDef->cooked_default == NULL);
 
-               relation_close(rel, NoLock);    /* close rel, keep lock */
+               rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
+               rawEnt->attnum = attnum;
+               rawEnt->raw_default = colDef->raw_default;
+               rawDefaults = lappend(rawDefaults, rawEnt);
        }
+
+       /*
+        * Parse and add the defaults/constraints, if any.
+        */
+       if (rawDefaults || stmt->constraints)
+               AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
+
+       /*
+        * Clean up.  We keep lock on new relation (although it shouldn't be
+        * visible to anyone else anyway, until commit).
+        */
+       heap_close(rel, NoLock);
+
+       return relationId;
 }
 
+/*
+ * RemoveRelation
+ *             Deletes a relation.
+ *
+ * Exceptions:
+ *             BadArg if name is invalid.
+ *
+ * Note:
+ *             If the relation has indices defined on it, then the index relations
+ * themselves will be destroyed, too.
+ */
+void
+RemoveRelation(const RangeVar *relation)
+{
+       Oid                     relOid;
+
+       relOid = RangeVarGetRelid(relation, false);
+       heap_drop_with_catalog(relOid, allowSystemTableMods);
+}
 
 /*
- * CREATE SCHEMA
+ * TruncateRelation
+ *                               Removes all the rows from a relation
+ *
+ * Exceptions:
+ *                               BadArg if name is invalid
+ *
+ * Note:
+ *                               Rows are removed, indices are truncated and reconstructed.
  */
 void
-CreateSchemaCommand(CreateSchemaStmt *stmt)
+TruncateRelation(const RangeVar *relation)
 {
-       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;
+       Relation        rel;
+       Oid                     relid;
 
-       saved_userid = GetUserId();
+       /* Grab exclusive lock in preparation for truncate */
+       rel = heap_openrv(relation, AccessExclusiveLock);
+       relid = RelationGetRelid(rel);
 
-       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 (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+               elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
+                        RelationGetRelationName(rel));
+
+       if (rel->rd_rel->relkind == RELKIND_VIEW)
+               elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
+                        RelationGetRelationName(rel));
 
-       if (!allowSystemTableMods && IsReservedName(schemaName))
-               elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas",
-                        schemaName);
+       if (!allowSystemTableMods && IsSystemRelation(rel))
+               elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
+                        RelationGetRelationName(rel));
 
-       /* Create the schema's namespace */
-       NamespaceCreate(schemaName, owner_userid);
+       if (!pg_class_ownercheck(relid, GetUserId()))
+               elog(ERROR, "you do not own relation \"%s\"",
+                        RelationGetRelationName(rel));
 
-       /* Let commands in the schema-element-list know about the schema */
-       CommandCounterIncrement();
+       /* Keep the lock until transaction commit */
+       heap_close(rel, NoLock);
+
+       heap_truncate(relid);
+}
+
+
+/*
+ * MergeDomainAttributes
+ *      Returns a new table schema with the constraints, types, and other
+ *      attributes of domains resolved for fields using a domain as
+ *      their type.
+ */
+static List *
+MergeDomainAttributes(List *schema)
+{
+       List       *entry;
 
        /*
-        * 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.
+        * Loop through the table elements supplied. These should
+        * never include inherited domains else they'll be
+        * double (or more) processed.
         */
-       parsetree_list = analyzeCreateSchemaStmt(stmt);
+       foreach(entry, schema)
+       {
+               ColumnDef  *coldef = lfirst(entry);
+               HeapTuple  tuple;
+               Form_pg_type typeTup;
+
+               tuple = typenameType(coldef->typename);
+               typeTup = (Form_pg_type) GETSTRUCT(tuple);
+
+               if (typeTup->typtype == 'd')
+               {
+                       /* Force the column to have the correct typmod. */
+                       coldef->typename->typmod = typeTup->typtypmod;
+                       /* XXX more to do here? */
+               }
+
+               /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
+               /* Currently only used for domains, but could be valid for all */
+               coldef->is_not_null |= typeTup->typnotnull;
+
+               ReleaseSysCache(tuple);
+       }
+
+       return schema;
+}
+
+/*----------
+ * MergeAttributes
+ *             Returns new schema given initial schema and superclasses.
+ *
+ * Input arguments:
+ * 'schema' is the column/attribute definition for the table. (It's a list
+ *             of ColumnDef's.) It is destructively changed.
+ * 'supers' is a list of names (as RangeVar nodes) of parent relations.
+ * 'istemp' is TRUE if we are creating a temp relation.
+ *
+ * Output arguments:
+ * 'supOids' receives an integer list of the OIDs of the parent relations.
+ * 'supconstr' receives a list of constraints belonging to the parents,
+ *             updated as necessary to be valid for the child.
+ * 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE.
+ *
+ * Return value:
+ * Completed schema list.
+ *
+ * Notes:
+ *       The order in which the attributes are inherited is very important.
+ *       Intuitively, the inherited attributes should come first. If a table
+ *       inherits from multiple parents, the order of those attributes are
+ *       according to the order of the parents specified in CREATE TABLE.
+ *
+ *       Here's an example:
+ *
+ *             create table person (name text, age int4, location point);
+ *             create table emp (salary int4, manager text) inherits(person);
+ *             create table student (gpa float8) inherits (person);
+ *             create table stud_emp (percent int4) inherits (emp, student);
+ *
+ *       The order of the attributes of stud_emp is:
+ *
+ *                                                     person {1:name, 2:age, 3:location}
+ *                                                     /        \
+ *                        {6:gpa}      student   emp {4:salary, 5:manager}
+ *                                                     \        /
+ *                                                stud_emp {7:percent}
+ *
+ *        If the same attribute name appears multiple times, then it appears
+ *        in the result table in the proper location for its first appearance.
+ *
+ *        Constraints (including NOT NULL constraints) for the child table
+ *        are the union of all relevant constraints, from both the child schema
+ *        and parent tables.
+ *
+ *        The default value for a child column is defined as:
+ *             (1) If the child schema specifies a default, that value is used.
+ *             (2) If neither the child nor any parent specifies a default, then
+ *                     the column will not have a default.
+ *             (3) If conflicting defaults are inherited from different parents
+ *                     (and not overridden by the child), an error is raised.
+ *             (4) Otherwise the inherited default is used.
+ *             Rule (3) is new in Postgres 7.1; in earlier releases you got a
+ *             rather arbitrary choice of which parent default to use.
+ *----------
+ */
+static List *
+MergeAttributes(List *schema, List *supers, bool istemp,
+                               List **supOids, List **supconstr, bool *supHasOids)
+{
+       List       *entry;
+       List       *inhSchema = NIL;
+       List       *parentOids = NIL;
+       List       *constraints = NIL;
+       bool            parentHasOids = false;
+       bool            have_bogus_defaults = false;
+       char       *bogus_marker = "Bogus!";            /* marks conflicting
+                                                                                                * defaults */
+       int                     child_attno;
 
        /*
-        * Analyze and execute each command contained in the CREATE SCHEMA
+        * Check for duplicate names in the explicit list of attributes.
+        *
+        * Although we might consider merging such entries in the same way that
+        * we handle name conflicts for inherited attributes, it seems to make
+        * more sense to assume such conflicts are errors.
         */
-       foreach(parsetree_item, parsetree_list)
+       foreach(entry, schema)
        {
-               Node       *parsetree = (Node *) lfirst(parsetree_item);
-               List       *querytree_list,
-                                  *querytree_item;
+               ColumnDef  *coldef = lfirst(entry);
+               List       *rest;
 
-               querytree_list = parse_analyze(parsetree, NULL);
-
-               foreach(querytree_item, querytree_list)
+               foreach(rest, lnext(entry))
                {
-                       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();
+                       ColumnDef  *restdef = lfirst(rest);
+
+                       if (strcmp(coldef->colname, restdef->colname) == 0)
+                               elog(ERROR, "CREATE TABLE: attribute \"%s\" duplicated",
+                                        coldef->colname);
                }
        }
 
-       /* Reset current user */
-       SetUserId(saved_userid);
+       /*
+        * Scan the parents left-to-right, and merge their attributes to form
+        * a list of inherited attributes (inhSchema).  Also check to see if
+        * we need to inherit an OID column.
+        */
+       child_attno = 0;
+       foreach(entry, supers)
+       {
+               RangeVar   *parent = (RangeVar *) lfirst(entry);
+               Relation        relation;
+               TupleDesc       tupleDesc;
+               TupleConstr *constr;
+               AttrNumber *newattno;
+               AttrNumber      parent_attno;
+
+               relation = heap_openrv(parent, AccessShareLock);
+
+               if (relation->rd_rel->relkind != RELKIND_RELATION)
+                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" is not a table",
+                                parent->relname);
+               /* Permanent rels cannot inherit from temporary ones */
+               if (!istemp && isTempNamespace(RelationGetNamespace(relation)))
+                       elog(ERROR, "CREATE TABLE: cannot inherit from temp relation \"%s\"",
+                                parent->relname);
+
+               /*
+                * We should have an UNDER permission flag for this, but for now,
+                * demand that creator of a child table own the parent.
+                */
+               if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+                       elog(ERROR, "you do not own table \"%s\"",
+                                parent->relname);
+
+               /*
+                * Reject duplications in the list of parents.
+                */
+               if (intMember(RelationGetRelid(relation), parentOids))
+                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated",
+                                parent->relname);
+
+               parentOids = lappendi(parentOids, RelationGetRelid(relation));
+               setRelhassubclassInRelation(RelationGetRelid(relation), true);
+
+               parentHasOids |= relation->rd_rel->relhasoids;
+
+               tupleDesc = RelationGetDescr(relation);
+               constr = tupleDesc->constr;
+
+               /*
+                * newattno[] will contain the child-table attribute numbers for
+                * the attributes of this parent table.  (They are not the same
+                * for parents after the first one.)
+                */
+               newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber));
+
+               for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+                        parent_attno++)
+               {
+                       Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
+                       char       *attributeName = NameStr(attribute->attname);
+                       int                     exist_attno;
+                       ColumnDef  *def;
+                       TypeName   *typename;
+
+                       /*
+                        * Does it conflict with some previously inherited column?
+                        */
+                       exist_attno = findAttrByName(attributeName, inhSchema);
+                       if (exist_attno > 0)
+                       {
+                               /*
+                                * Yes, try to merge the two column definitions. They must
+                                * have the same type and typmod.
+                                */
+                               elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"",
+                                        attributeName);
+                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
+                               if (typenameTypeId(def->typename) != attribute->atttypid ||
+                                       def->typename->typmod != attribute->atttypmod)
+                                       elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)",
+                                                attributeName,
+                                                TypeNameToString(def->typename),
+                                                typeidTypeName(attribute->atttypid));
+                               /* Merge of NOT NULL constraints = OR 'em together */
+                               def->is_not_null |= attribute->attnotnull;
+                               /* Default and other constraints are handled below */
+                               newattno[parent_attno - 1] = exist_attno;
+                       }
+                       else
+                       {
+                               /*
+                                * No, create a new inherited column
+                                */
+                               def = makeNode(ColumnDef);
+                               def->colname = pstrdup(attributeName);
+                               typename = makeNode(TypeName);
+                               typename->typeid = attribute->atttypid;
+                               typename->typmod = attribute->atttypmod;
+                               def->typename = typename;
+                               def->is_not_null = attribute->attnotnull;
+                               def->raw_default = NULL;
+                               def->cooked_default = NULL;
+                               def->constraints = NIL;
+                               inhSchema = lappend(inhSchema, def);
+                               newattno[parent_attno - 1] = ++child_attno;
+                       }
+
+                       /*
+                        * Copy default if any
+                        */
+                       if (attribute->atthasdef)
+                       {
+                               char       *this_default = NULL;
+                               AttrDefault *attrdef;
+                               int                     i;
+
+                               /* Find default in constraint structure */
+                               Assert(constr != NULL);
+                               attrdef = constr->defval;
+                               for (i = 0; i < constr->num_defval; i++)
+                               {
+                                       if (attrdef[i].adnum == parent_attno)
+                                       {
+                                               this_default = attrdef[i].adbin;
+                                               break;
+                                       }
+                               }
+                               Assert(this_default != NULL);
+
+                               /*
+                                * If default expr could contain any vars, we'd need to
+                                * fix 'em, but it can't; so default is ready to apply to
+                                * child.
+                                *
+                                * If we already had a default from some prior parent, check
+                                * to see if they are the same.  If so, no problem; if
+                                * not, mark the column as having a bogus default. Below,
+                                * we will complain if the bogus default isn't overridden
+                                * by the child schema.
+                                */
+                               Assert(def->raw_default == NULL);
+                               if (def->cooked_default == NULL)
+                                       def->cooked_default = pstrdup(this_default);
+                               else if (strcmp(def->cooked_default, this_default) != 0)
+                               {
+                                       def->cooked_default = bogus_marker;
+                                       have_bogus_defaults = true;
+                               }
+                       }
+               }
+
+               /*
+                * Now copy the constraints of this parent, adjusting attnos using
+                * the completed newattno[] map
+                */
+               if (constr && constr->num_check > 0)
+               {
+                       ConstrCheck *check = constr->check;
+                       int                     i;
+
+                       for (i = 0; i < constr->num_check; i++)
+                       {
+                               Constraint *cdef = makeNode(Constraint);
+                               Node       *expr;
+
+                               cdef->contype = CONSTR_CHECK;
+                               if (check[i].ccname[0] == '$')
+                                       cdef->name = NULL;
+                               else
+                                       cdef->name = pstrdup(check[i].ccname);
+                               cdef->raw_expr = NULL;
+                               /* adjust varattnos of ccbin here */
+                               expr = stringToNode(check[i].ccbin);
+                               change_varattnos_of_a_node(expr, newattno);
+                               cdef->cooked_expr = nodeToString(expr);
+                               constraints = lappend(constraints, cdef);
+                       }
+               }
+
+               pfree(newattno);
+
+               /*
+                * Close the parent rel, but keep our AccessShareLock on it until
+                * xact commit.  That will prevent someone else from deleting or
+                * ALTERing the parent before the child is committed.
+                */
+               heap_close(relation, NoLock);
+       }
+
+       /*
+        * If we had no inherited attributes, the result schema is just the
+        * explicitly declared columns.  Otherwise, we need to merge the
+        * declared columns into the inherited schema list.
+        */
+       if (inhSchema != NIL)
+       {
+               foreach(entry, schema)
+               {
+                       ColumnDef  *newdef = lfirst(entry);
+                       char       *attributeName = newdef->colname;
+                       int                     exist_attno;
+
+                       /*
+                        * Does it conflict with some previously inherited column?
+                        */
+                       exist_attno = findAttrByName(attributeName, inhSchema);
+                       if (exist_attno > 0)
+                       {
+                               ColumnDef  *def;
+
+                               /*
+                                * Yes, try to merge the two column definitions. They must
+                                * have the same type and typmod.
+                                */
+                               elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition",
+                                        attributeName);
+                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
+                               if (typenameTypeId(def->typename) != typenameTypeId(newdef->typename) ||
+                                       def->typename->typmod != newdef->typename->typmod)
+                                       elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)",
+                                                attributeName,
+                                                TypeNameToString(def->typename),
+                                                TypeNameToString(newdef->typename));
+                               /* Merge of NOT NULL constraints = OR 'em together */
+                               def->is_not_null |= newdef->is_not_null;
+                               /* If new def has a default, override previous default */
+                               if (newdef->raw_default != NULL)
+                               {
+                                       def->raw_default = newdef->raw_default;
+                                       def->cooked_default = newdef->cooked_default;
+                               }
+                       }
+                       else
+                       {
+                               /*
+                                * No, attach new column to result schema
+                                */
+                               inhSchema = lappend(inhSchema, newdef);
+                       }
+               }
+
+               schema = inhSchema;
+       }
+
+       /*
+        * If we found any conflicting parent default values, check to make
+        * sure they were overridden by the child.
+        */
+       if (have_bogus_defaults)
+       {
+               foreach(entry, schema)
+               {
+                       ColumnDef  *def = lfirst(entry);
+
+                       if (def->cooked_default == bogus_marker)
+                               elog(ERROR, "CREATE TABLE: attribute \"%s\" inherits conflicting default values"
+                                        "\n\tTo resolve the conflict, specify a default explicitly",
+                                        def->colname);
+               }
+       }
+
+       *supOids = parentOids;
+       *supconstr = constraints;
+       *supHasOids = parentHasOids;
+       return schema;
+}
+
+/*
+ * complementary static functions for MergeAttributes().
+ *
+ * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
+ * constraints from parent classes, since the inherited attributes could
+ * be given different column numbers in multiple-inheritance cases.
+ *
+ * Note that the passed node tree is modified in place!
+ */
+static bool
+change_varattnos_walker(Node *node, const AttrNumber *newattno)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
+       {
+               Var                *var = (Var *) node;
+
+               if (var->varlevelsup == 0 && var->varno == 1 &&
+                       var->varattno > 0)
+               {
+                       /*
+                        * ??? the following may be a problem when the node is
+                        * multiply referenced though stringToNode() doesn't create
+                        * such a node currently.
+                        */
+                       Assert(newattno[var->varattno - 1] > 0);
+                       var->varattno = newattno[var->varattno - 1];
+               }
+               return false;
+       }
+       return expression_tree_walker(node, change_varattnos_walker,
+                                                                 (void *) newattno);
+}
+
+static bool
+change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
+{
+       return change_varattnos_walker(node, newattno);
+}
+
+/*
+ * StoreCatalogInheritance
+ *             Updates the system catalogs with proper inheritance information.
+ *
+ * supers is an integer list of the OIDs of the new relation's direct
+ * ancestors.  NB: it is destructively changed to include indirect ancestors.
+ */
+static void
+StoreCatalogInheritance(Oid relationId, List *supers)
+{
+       Relation        relation;
+       TupleDesc       desc;
+       int16           seqNumber;
+       List       *entry;
+       HeapTuple       tuple;
+
+       /*
+        * sanity checks
+        */
+       AssertArg(OidIsValid(relationId));
+
+       if (supers == NIL)
+               return;
+
+       /*
+        * Catalog INHERITS information using direct ancestors only.
+        */
+       relation = heap_openr(InheritsRelationName, RowExclusiveLock);
+       desc = RelationGetDescr(relation);
+
+       seqNumber = 1;
+       foreach(entry, supers)
+       {
+               Oid                     entryOid = lfirsti(entry);
+               Datum           datum[Natts_pg_inherits];
+               char            nullarr[Natts_pg_inherits];
+
+               datum[0] = ObjectIdGetDatum(relationId);                /* inhrel */
+               datum[1] = ObjectIdGetDatum(entryOid);  /* inhparent */
+               datum[2] = Int16GetDatum(seqNumber);    /* inhseqno */
+
+               nullarr[0] = ' ';
+               nullarr[1] = ' ';
+               nullarr[2] = ' ';
+
+               tuple = heap_formtuple(desc, datum, nullarr);
+
+               heap_insert(relation, tuple);
+
+               if (RelationGetForm(relation)->relhasindex)
+               {
+                       Relation        idescs[Num_pg_inherits_indices];
+
+                       CatalogOpenIndices(Num_pg_inherits_indices, Name_pg_inherits_indices, idescs);
+                       CatalogIndexInsert(idescs, Num_pg_inherits_indices, relation, tuple);
+                       CatalogCloseIndices(Num_pg_inherits_indices, idescs);
+               }
+
+               heap_freetuple(tuple);
+
+               seqNumber += 1;
+       }
+
+       heap_close(relation, RowExclusiveLock);
+
+       /* ----------------
+        * Expand supers list to include indirect ancestors as well.
+        *
+        * Algorithm:
+        *      0. begin with list of direct superclasses.
+        *      1. append after each relationId, its superclasses, recursively.
+        *      2. remove all but last of duplicates.
+        * ----------------
+        */
+
+       /*
+        * 1. append after each relationId, its superclasses, recursively.
+        */
+       foreach(entry, supers)
+       {
+               HeapTuple       tuple;
+               Oid                     id;
+               int16           number;
+               List       *next;
+               List       *current;
+
+               id = (Oid) lfirsti(entry);
+               current = entry;
+               next = lnext(entry);
+
+               for (number = 1;; number += 1)
+               {
+                       tuple = SearchSysCache(INHRELID,
+                                                                  ObjectIdGetDatum(id),
+                                                                  Int16GetDatum(number),
+                                                                  0, 0);
+                       if (!HeapTupleIsValid(tuple))
+                               break;
+
+                       lnext(current) = lconsi(((Form_pg_inherits)
+                                                                        GETSTRUCT(tuple))->inhparent,
+                                                                       NIL);
+
+                       ReleaseSysCache(tuple);
+
+                       current = lnext(current);
+               }
+               lnext(current) = next;
+       }
+
+       /*
+        * 2. remove all but last of duplicates.
+        */
+       foreach(entry, supers)
+       {
+               Oid                     thisone;
+               bool            found;
+               List       *rest;
+
+again:
+               thisone = lfirsti(entry);
+               found = false;
+               foreach(rest, lnext(entry))
+               {
+                       if (thisone == lfirsti(rest))
+                       {
+                               found = true;
+                               break;
+                       }
+               }
+               if (found)
+               {
+                       /*
+                        * found a later duplicate, so remove this entry.
+                        */
+                       lfirsti(entry) = lfirsti(lnext(entry));
+                       lnext(entry) = lnext(lnext(entry));
+
+                       goto again;
+               }
+       }
+}
+
+/*
+ * Look for an existing schema entry with the given name.
+ *
+ * Returns the index (starting with 1) if attribute already exists in schema,
+ * 0 if it doesn't.
+ */
+static int
+findAttrByName(const char *attributeName, List *schema)
+{
+       List       *s;
+       int                     i = 0;
+
+       foreach(s, schema)
+       {
+               ColumnDef  *def = lfirst(s);
+
+               ++i;
+               if (strcmp(attributeName, def->colname) == 0)
+                       return i;
+       }
+       return 0;
+}
+
+/*
+ * Update a relation's pg_class.relhassubclass entry to the given value
+ */
+static void
+setRelhassubclassInRelation(Oid relationId, bool relhassubclass)
+{
+       Relation        relationRelation;
+       HeapTuple       tuple;
+       Relation        idescs[Num_pg_class_indices];
+
+       /*
+        * Fetch a modifiable copy of the tuple, modify it, update pg_class.
+        */
+       relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
+       tuple = SearchSysCacheCopy(RELOID,
+                                                          ObjectIdGetDatum(relationId),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "setRelhassubclassInRelation: cache lookup failed for relation %u", relationId);
+
+       ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass = relhassubclass;
+       simple_heap_update(relationRelation, &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, relationRelation, tuple);
+       CatalogCloseIndices(Num_pg_class_indices, idescs);
+
+       heap_freetuple(tuple);
+       heap_close(relationRelation, RowExclusiveLock);
+}
+
+
+/*
+ *             renameatt               - changes the name of a attribute in a relation
+ *
+ *             Attname attribute is changed in attribute catalog.
+ *             No record of the previous attname is kept (correct?).
+ *
+ *             get proper relrelation from relation catalog (if not arg)
+ *             scan attribute catalog
+ *                             for name conflict (within rel)
+ *                             for original attribute (if not arg)
+ *             modify attname in attribute tuple
+ *             insert modified attribute in attribute catalog
+ *             delete original attribute from attribute catalog
+ */
+void
+renameatt(Oid relid,
+                 const char *oldattname,
+                 const char *newattname,
+                 bool recurse)
+{
+       Relation        targetrelation;
+       Relation        attrelation;
+       HeapTuple       atttup;
+       List       *indexoidlist;
+       List       *indexoidscan;
+
+       /*
+        * Grab an exclusive lock on the target table, which we will NOT
+        * release until end of transaction.
+        */
+       targetrelation = heap_open(relid, AccessExclusiveLock);
+
+       /*
+        * 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(targetrelation))
+               elog(ERROR, "renameatt: class \"%s\" is a system catalog",
+                        RelationGetRelationName(targetrelation));
+       if (!pg_class_ownercheck(relid, GetUserId()))
+               elog(ERROR, "renameatt: you do not own class \"%s\"",
+                        RelationGetRelationName(targetrelation));
+
+       /*
+        * if the 'recurse' flag is set then we are supposed to rename this
+        * attribute in all classes that inherit from 'relname' (as well as in
+        * 'relname').
+        *
+        * any permissions or problems with duplicate attributes will cause the
+        * whole transaction to abort, which is what we want -- all or
+        * nothing.
+        */
+       if (recurse)
+       {
+               List       *child,
+                                  *children;
+
+               /* this routine is actually in the planner */
+               children = find_all_inheritors(relid);
+
+               /*
+                * 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 == relid)
+                               continue;
+                       /* note we need not recurse again! */
+                       renameatt(childrelid, oldattname, newattname, false);
+               }
+       }
+
+       attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
+
+       atttup = SearchSysCacheCopy(ATTNAME,
+                                                               ObjectIdGetDatum(relid),
+                                                               PointerGetDatum(oldattname),
+                                                               0, 0);
+       if (!HeapTupleIsValid(atttup))
+               elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname);
+
+       if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0)
+               elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname);
+
+       /* should not already exist */
+       if (SearchSysCacheExists(ATTNAME,
+                                                        ObjectIdGetDatum(relid),
+                                                        PointerGetDatum(newattname),
+                                                        0, 0))
+               elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
+
+       StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
+                       newattname, NAMEDATALEN);
+
+       simple_heap_update(attrelation, &atttup->t_self, atttup);
+
+       /* 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, atttup);
+               CatalogCloseIndices(Num_pg_attr_indices, irelations);
+       }
+
+       heap_freetuple(atttup);
+
+       /*
+        * Update column names of indexes that refer to the column being
+        * renamed.
+        */
+       indexoidlist = RelationGetIndexList(targetrelation);
+
+       foreach(indexoidscan, indexoidlist)
+       {
+               Oid                     indexoid = lfirsti(indexoidscan);
+               HeapTuple       indextup;
+
+               /*
+                * First check to see if index is a functional index. If so, its
+                * column name is a function name and shouldn't be renamed here.
+                */
+               indextup = SearchSysCache(INDEXRELID,
+                                                                 ObjectIdGetDatum(indexoid),
+                                                                 0, 0, 0);
+               if (!HeapTupleIsValid(indextup))
+                       elog(ERROR, "renameatt: can't find index id %u", indexoid);
+               if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc))
+               {
+                       ReleaseSysCache(indextup);
+                       continue;
+               }
+               ReleaseSysCache(indextup);
+
+               /*
+                * Okay, look to see if any column name of the index matches the
+                * old attribute name.
+                */
+               atttup = SearchSysCacheCopy(ATTNAME,
+                                                                       ObjectIdGetDatum(indexoid),
+                                                                       PointerGetDatum(oldattname),
+                                                                       0, 0);
+               if (!HeapTupleIsValid(atttup))
+                       continue;                       /* Nope, so ignore it */
+
+               /*
+                * Update the (copied) attribute tuple.
+                */
+               StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
+                               newattname, NAMEDATALEN);
+
+               simple_heap_update(attrelation, &atttup->t_self, atttup);
+
+               /* 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, atttup);
+                       CatalogCloseIndices(Num_pg_attr_indices, irelations);
+               }
+               heap_freetuple(atttup);
+       }
+
+       freeList(indexoidlist);
+
+       heap_close(attrelation, RowExclusiveLock);
+
+       /*
+        * Update att name in any RI triggers associated with the relation.
+        */
+       if (targetrelation->rd_rel->reltriggers > 0)
+       {
+               /* update tgargs column reference where att is primary key */
+               update_ri_trigger_args(RelationGetRelid(targetrelation),
+                                                          oldattname, newattname,
+                                                          false, false);
+               /* update tgargs column reference where att is foreign key */
+               update_ri_trigger_args(RelationGetRelid(targetrelation),
+                                                          oldattname, newattname,
+                                                          true, false);
+       }
+
+       heap_close(targetrelation, NoLock); /* close rel but keep lock! */
+}
+
+/*
+ *             renamerel               - change the name of a relation
+ *
+ *             XXX - When renaming sequences, we don't bother to modify the
+ *                       sequence name that is stored within the sequence itself
+ *                       (this would cause problems with MVCC). In the future,
+ *                       the sequence name should probably be removed from the
+ *                       sequence, AFAIK there's no need for it to be there.
+ */
+void
+renamerel(Oid relid, const char *newrelname)
+{
+       Relation        targetrelation;
+       Relation        relrelation;    /* for RELATION relation */
+       HeapTuple       reltup;
+       Oid                     namespaceId;
+       char            relkind;
+       bool            relhastriggers;
+       Relation        irelations[Num_pg_class_indices];
+
+       /*
+        * Grab an exclusive lock on the target table or index, which we will
+        * NOT release until end of transaction.
+        */
+       targetrelation = relation_open(relid, AccessExclusiveLock);
+
+       namespaceId = RelationGetNamespace(targetrelation);
+
+       /* Validity checks */
+       if (!allowSystemTableMods &&
+               IsSystemRelation(targetrelation))
+               elog(ERROR, "renamerel: system relation \"%s\" may not be renamed",
+                        RelationGetRelationName(targetrelation));
+
+       relkind = targetrelation->rd_rel->relkind;
+       relhastriggers = (targetrelation->rd_rel->reltriggers > 0);
+
+       /*
+        * Find relation's pg_class tuple, and make sure newrelname isn't in
+        * use.
+        */
+       relrelation = heap_openr(RelationRelationName, RowExclusiveLock);
+
+       reltup = SearchSysCacheCopy(RELOID,
+                                                               PointerGetDatum(relid),
+                                                               0, 0, 0);
+       if (!HeapTupleIsValid(reltup))
+               elog(ERROR, "renamerel: relation \"%s\" does not exist",
+                        RelationGetRelationName(targetrelation));
+
+       if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
+               elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
+
+       /*
+        * Update pg_class tuple with new relname.      (Scribbling on reltup is
+        * OK because it's a copy...)
+        */
+       StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname),
+                       newrelname, NAMEDATALEN);
+
+       simple_heap_update(relrelation, &reltup->t_self, reltup);
+
+       /* keep the system catalog indices current */
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations);
+       CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, reltup);
+       CatalogCloseIndices(Num_pg_class_indices, irelations);
+
+       heap_close(relrelation, NoLock);
+       heap_freetuple(reltup);
+
+       /*
+        * Also rename the associated type, if any.
+        */
+       if (relkind != RELKIND_INDEX)
+               TypeRename(RelationGetRelationName(targetrelation), namespaceId,
+                                  newrelname);
+
+       /*
+        * If it's a view, must also rename the associated ON SELECT rule.
+        */
+       if (relkind == RELKIND_VIEW)
+       {
+               char       *oldrulename,
+                                  *newrulename;
+
+               oldrulename = MakeRetrieveViewRuleName(RelationGetRelationName(targetrelation));
+               newrulename = MakeRetrieveViewRuleName(newrelname);
+               RenameRewriteRule(oldrulename, newrulename);
+       }
+
+       /*
+        * Update rel name in any RI triggers associated with the relation.
+        */
+       if (relhastriggers)
+       {
+               /* update tgargs where relname is primary key */
+               update_ri_trigger_args(relid,
+                                                          RelationGetRelationName(targetrelation),
+                                                          newrelname,
+                                                          false, true);
+               /* update tgargs where relname is foreign key */
+               update_ri_trigger_args(relid,
+                                                          RelationGetRelationName(targetrelation),
+                                                          newrelname,
+                                                          true, true);
+       }
+
+       /*
+        * Close rel, but keep exclusive lock!
+        */
+       relation_close(targetrelation, NoLock);
+}
+
+/*
+ * Given a trigger function OID, determine whether it is an RI trigger,
+ * and if so whether it is attached to PK or FK relation.
+ *
+ * XXX this probably doesn't belong here; should be exported by
+ * ri_triggers.c
+ */
+static int
+ri_trigger_type(Oid tgfoid)
+{
+       switch (tgfoid)
+       {
+               case F_RI_FKEY_CASCADE_DEL:
+               case F_RI_FKEY_CASCADE_UPD:
+               case F_RI_FKEY_RESTRICT_DEL:
+               case F_RI_FKEY_RESTRICT_UPD:
+               case F_RI_FKEY_SETNULL_DEL:
+               case F_RI_FKEY_SETNULL_UPD:
+               case F_RI_FKEY_SETDEFAULT_DEL:
+               case F_RI_FKEY_SETDEFAULT_UPD:
+               case F_RI_FKEY_NOACTION_DEL:
+               case F_RI_FKEY_NOACTION_UPD:
+                       return RI_TRIGGER_PK;
+
+               case F_RI_FKEY_CHECK_INS:
+               case F_RI_FKEY_CHECK_UPD:
+                       return RI_TRIGGER_FK;
+       }
+
+       return RI_TRIGGER_NONE;
+}
+
+/*
+ * Scan pg_trigger for RI triggers that are on the specified relation
+ * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
+ * is true).  Update RI trigger args fields matching oldname to contain
+ * newname instead.  If update_relname is true, examine the relname
+ * fields; otherwise examine the attname fields.
+ */
+static void
+update_ri_trigger_args(Oid relid,
+                                          const char *oldname,
+                                          const char *newname,
+                                          bool fk_scan,
+                                          bool update_relname)
+{
+       Relation        tgrel;
+       Relation        irel;
+       ScanKeyData skey[1];
+       IndexScanDesc idxtgscan;
+       RetrieveIndexResult idxres;
+       Datum           values[Natts_pg_trigger];
+       char            nulls[Natts_pg_trigger];
+       char            replaces[Natts_pg_trigger];
+
+       tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+       if (fk_scan)
+               irel = index_openr(TriggerConstrRelidIndex);
+       else
+               irel = index_openr(TriggerRelidIndex);
+
+       ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                  1,   /* always column 1 of index */
+                                                  F_OIDEQ,
+                                                  ObjectIdGetDatum(relid));
+       idxtgscan = index_beginscan(irel, false, 1, skey);
+
+       while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL)
+       {
+               HeapTupleData tupledata;
+               Buffer          buffer;
+               HeapTuple       tuple;
+               Form_pg_trigger pg_trigger;
+               bytea      *val;
+               bytea      *newtgargs;
+               bool            isnull;
+               int                     tg_type;
+               bool            examine_pk;
+               bool            changed;
+               int                     tgnargs;
+               int                     i;
+               int                     newlen;
+               const char *arga[RI_MAX_ARGUMENTS];
+               const char *argp;
+
+               tupledata.t_self = idxres->heap_iptr;
+               heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan);
+               pfree(idxres);
+               if (!tupledata.t_data)
+                       continue;
+               tuple = &tupledata;
+               pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+               tg_type = ri_trigger_type(pg_trigger->tgfoid);
+               if (tg_type == RI_TRIGGER_NONE)
+               {
+                       /* Not an RI trigger, forget it */
+                       ReleaseBuffer(buffer);
+                       continue;
+               }
+
+               /*
+                * It is an RI trigger, so parse the tgargs bytea.
+                *
+                * NB: we assume the field will never be compressed or moved out of
+                * line; so does trigger.c ...
+                */
+               tgnargs = pg_trigger->tgnargs;
+               val = (bytea *) fastgetattr(tuple,
+                                                                       Anum_pg_trigger_tgargs,
+                                                                       tgrel->rd_att, &isnull);
+               if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
+                       tgnargs > RI_MAX_ARGUMENTS)
+               {
+                       /* This probably shouldn't happen, but ignore busted triggers */
+                       ReleaseBuffer(buffer);
+                       continue;
+               }
+               argp = (const char *) VARDATA(val);
+               for (i = 0; i < tgnargs; i++)
+               {
+                       arga[i] = argp;
+                       argp += strlen(argp) + 1;
+               }
+
+               /*
+                * Figure out which item(s) to look at.  If the trigger is
+                * primary-key type and attached to my rel, I should look at the
+                * PK fields; if it is foreign-key type and attached to my rel, I
+                * should look at the FK fields.  But the opposite rule holds when
+                * examining triggers found by tgconstrrel search.
+                */
+               examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
+
+               changed = false;
+               if (update_relname)
+               {
+                       /* Change the relname if needed */
+                       i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
+                       if (strcmp(arga[i], oldname) == 0)
+                       {
+                               arga[i] = newname;
+                               changed = true;
+                       }
+               }
+               else
+               {
+                       /* Change attname(s) if needed */
+                       i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
+                               RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
+                       for (; i < tgnargs; i += 2)
+                       {
+                               if (strcmp(arga[i], oldname) == 0)
+                               {
+                                       arga[i] = newname;
+                                       changed = true;
+                               }
+                       }
+               }
+
+               if (!changed)
+               {
+                       /* Don't need to update this tuple */
+                       ReleaseBuffer(buffer);
+                       continue;
+               }
+
+               /*
+                * Construct modified tgargs bytea.
+                */
+               newlen = VARHDRSZ;
+               for (i = 0; i < tgnargs; i++)
+                       newlen += strlen(arga[i]) + 1;
+               newtgargs = (bytea *) palloc(newlen);
+               VARATT_SIZEP(newtgargs) = newlen;
+               newlen = VARHDRSZ;
+               for (i = 0; i < tgnargs; i++)
+               {
+                       strcpy(((char *) newtgargs) + newlen, arga[i]);
+                       newlen += strlen(arga[i]) + 1;
+               }
+
+               /*
+                * Build modified tuple.
+                */
+               for (i = 0; i < Natts_pg_trigger; i++)
+               {
+                       values[i] = (Datum) 0;
+                       replaces[i] = ' ';
+                       nulls[i] = ' ';
+               }
+               values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
+               replaces[Anum_pg_trigger_tgargs - 1] = 'r';
+
+               tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces);
+
+               /*
+                * Now we can release hold on original tuple.
+                */
+               ReleaseBuffer(buffer);
+
+               /*
+                * Update pg_trigger and its indexes
+                */
+               simple_heap_update(tgrel, &tuple->t_self, tuple);
+
+               {
+                       Relation        irelations[Num_pg_attr_indices];
+
+                       CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations);
+                       CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple);
+                       CatalogCloseIndices(Num_pg_trigger_indices, irelations);
+               }
+
+               /* free up our scratch memory */
+               pfree(newtgargs);
+               heap_freetuple(tuple);
+       }
+
+       index_endscan(idxtgscan);
+       index_close(irel);
+
+       heap_close(tgrel, RowExclusiveLock);
+
+       /*
+        * Increment cmd counter to make updates visible; this is needed in
+        * case the same tuple has to be updated again by next pass (can
+        * happen in case of a self-referential FK relationship).
+        */
+       CommandCounterIncrement();
 }
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
new file mode 100644 (file)
index 0000000..13dbf04
--- /dev/null
@@ -0,0 +1,660 @@
+/*-------------------------------------------------------------------------
+ *
+ * typecmds.c
+ *       Routines for SQL commands that manipulate types (and domains).
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ * DESCRIPTION
+ *       The "DefineFoo" routines take the parse tree and pick out the
+ *       appropriate arguments/flags, passing the results to the
+ *       corresponding "FooDefine" routines (in src/catalog) that do
+ *       the actual catalog-munging.  These routines also verify permission
+ *       of the user to execute the command.
+ *
+ * NOTES
+ *       These things must be defined and committed in the following order:
+ *             "create function":
+ *                             input/output, recv/send procedures
+ *             "create type":
+ *                             type
+ *             "create operator":
+ *                             operators
+ *
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/heap.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "commands/comment.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse.h"
+#include "parser/parse_func.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/syscache.h"
+
+
+static Oid findTypeIOFunction(List *procname, bool isOutput);
+
+
+/*
+ * DefineType
+ *             Registers a new type.
+ */
+void
+DefineType(List *names, List *parameters)
+{
+       char       *typeName;
+       Oid                     typeNamespace;
+       int16           internalLength = -1;    /* int2 */
+       int16           externalLength = -1;    /* int2 */
+       Oid                     elemType = InvalidOid;
+       List       *inputName = NIL;
+       List       *outputName = NIL;
+       List       *sendName = NIL;
+       List       *receiveName = NIL;
+       char       *defaultValue = NULL;
+       bool            byValue = false;
+       char            delimiter = DEFAULT_TYPDELIM;
+       char            alignment = 'i';        /* default alignment */
+       char            storage = 'p';  /* default TOAST storage method */
+       Oid                     inputOid;
+       Oid                     outputOid;
+       Oid                     sendOid;
+       Oid                     receiveOid;
+       char       *shadow_type;
+       List       *pl;
+       Oid                     typoid;
+
+       /* Convert list of names to a name and namespace */
+       typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
+
+       /*
+        * Type names must be one character shorter than other names, allowing
+        * room to create the corresponding array type name with prepended
+        * "_".
+        */
+       if (strlen(typeName) > (NAMEDATALEN - 2))
+               elog(ERROR, "DefineType: type names must be %d characters or less",
+                        NAMEDATALEN - 2);
+
+       foreach(pl, parameters)
+       {
+               DefElem    *defel = (DefElem *) lfirst(pl);
+
+               if (strcasecmp(defel->defname, "internallength") == 0)
+                       internalLength = defGetTypeLength(defel);
+               else if (strcasecmp(defel->defname, "externallength") == 0)
+                       externalLength = defGetTypeLength(defel);
+               else if (strcasecmp(defel->defname, "input") == 0)
+                       inputName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "output") == 0)
+                       outputName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "send") == 0)
+                       sendName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "receive") == 0)
+                       receiveName = defGetQualifiedName(defel);
+               else if (strcasecmp(defel->defname, "delimiter") == 0)
+               {
+                       char       *p = defGetString(defel);
+
+                       delimiter = p[0];
+               }
+               else if (strcasecmp(defel->defname, "element") == 0)
+                       elemType = typenameTypeId(defGetTypeName(defel));
+               else if (strcasecmp(defel->defname, "default") == 0)
+                       defaultValue = defGetString(defel);
+               else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
+                       byValue = true;
+               else if (strcasecmp(defel->defname, "alignment") == 0)
+               {
+                       char       *a = defGetString(defel);
+
+                       /*
+                        * Note: if argument was an unquoted identifier, parser will
+                        * have applied xlateSqlType() to it, so be prepared to
+                        * recognize translated type names as well as the nominal
+                        * form.
+                        */
+                       if (strcasecmp(a, "double") == 0)
+                               alignment = 'd';
+                       else if (strcasecmp(a, "float8") == 0)
+                               alignment = 'd';
+                       else if (strcasecmp(a, "int4") == 0)
+                               alignment = 'i';
+                       else if (strcasecmp(a, "int2") == 0)
+                               alignment = 's';
+                       else if (strcasecmp(a, "char") == 0)
+                               alignment = 'c';
+                       else if (strcasecmp(a, "bpchar") == 0)
+                               alignment = 'c';
+                       else
+                               elog(ERROR, "DefineType: \"%s\" alignment not recognized",
+                                        a);
+               }
+               else if (strcasecmp(defel->defname, "storage") == 0)
+               {
+                       char       *a = defGetString(defel);
+
+                       if (strcasecmp(a, "plain") == 0)
+                               storage = 'p';
+                       else if (strcasecmp(a, "external") == 0)
+                               storage = 'e';
+                       else if (strcasecmp(a, "extended") == 0)
+                               storage = 'x';
+                       else if (strcasecmp(a, "main") == 0)
+                               storage = 'm';
+                       else
+                               elog(ERROR, "DefineType: \"%s\" storage not recognized",
+                                        a);
+               }
+               else
+               {
+                       elog(WARNING, "DefineType: attribute \"%s\" not recognized",
+                                defel->defname);
+               }
+       }
+
+       /*
+        * make sure we have our required definitions
+        */
+       if (inputName == NIL)
+               elog(ERROR, "Define: \"input\" unspecified");
+       if (outputName == NIL)
+               elog(ERROR, "Define: \"output\" unspecified");
+
+       /* Convert I/O proc names to OIDs */
+       inputOid = findTypeIOFunction(inputName, false);
+       outputOid = findTypeIOFunction(outputName, true);
+       if (sendName)
+               sendOid = findTypeIOFunction(sendName, true);
+       else
+               sendOid = outputOid;
+       if (receiveName)
+               receiveOid = findTypeIOFunction(receiveName, false);
+       else
+               receiveOid = inputOid;
+
+       /*
+        * now have TypeCreate do all the real work.
+        */
+       typoid =
+               TypeCreate(typeName,            /* type name */
+                                  typeNamespace,       /* namespace */
+                                  InvalidOid,          /* preassigned type oid (not done here) */
+                                  InvalidOid,          /* relation oid (n/a here) */
+                                  internalLength,      /* internal size */
+                                  externalLength,      /* external size */
+                                  'b',                         /* type-type (base type) */
+                                  delimiter,           /* array element delimiter */
+                                  inputOid,            /* input procedure */
+                                  outputOid,           /* output procedure */
+                                  receiveOid,          /* receive procedure */
+                                  sendOid,                     /* send procedure */
+                                  elemType,            /* element type ID */
+                                  InvalidOid,          /* base type ID (only for domains) */
+                                  defaultValue,        /* default type value */
+                                  NULL,                        /* no binary form available */
+                                  byValue,                     /* passed by value */
+                                  alignment,           /* required alignment */
+                                  storage,                     /* TOAST strategy */
+                                  -1,                          /* typMod (Domains only) */
+                                  0,                           /* Array Dimensions of typbasetype */
+                                  false);                      /* Type NOT NULL */
+
+       /*
+        * When we create a base type (as opposed to a complex type) we need
+        * to have an array entry for it in pg_type as well.
+        */
+       shadow_type = makeArrayTypeName(typeName);
+
+       /* alignment must be 'i' or 'd' for arrays */
+       alignment = (alignment == 'd') ? 'd' : 'i';
+
+       TypeCreate(shadow_type,         /* type name */
+                          typeNamespace,       /* namespace */
+                          InvalidOid,          /* preassigned type oid (not done here) */
+                          InvalidOid,          /* relation oid (n/a here) */
+                          -1,                          /* internal size */
+                          -1,                          /* external size */
+                          'b',                         /* type-type (base type) */
+                          DEFAULT_TYPDELIM,    /* array element delimiter */
+                          F_ARRAY_IN,          /* input procedure */
+                          F_ARRAY_OUT,         /* output procedure */
+                          F_ARRAY_IN,          /* receive procedure */
+                          F_ARRAY_OUT,         /* send procedure */
+                          typoid,                      /* element type ID */
+                          InvalidOid,          /* base type ID */
+                          NULL,                        /* never a default type value */
+                          NULL,                        /* binary default isn't sent either */
+                          false,                       /* never passed by value */
+                          alignment,           /* see above */
+                          'x',                         /* ARRAY is always toastable */
+                          -1,                          /* typMod (Domains only) */
+                          0,                           /* Array dimensions of typbasetype */
+                          false);                      /* Type NOT NULL */
+
+       pfree(shadow_type);
+}
+
+
+/*
+ *     RemoveType
+ *             Removes a datatype.
+ *
+ * NOTE: since this tries to remove the associated array type too, it'll
+ * only work on scalar types.
+ */
+void
+RemoveType(List *names)
+{
+       TypeName   *typename;
+       Relation        relation;
+       Oid                     typeoid;
+       HeapTuple       tup;
+
+       /* Make a TypeName so we can use standard type lookup machinery */
+       typename = makeNode(TypeName);
+       typename->names = names;
+       typename->typmod = -1;
+       typename->arrayBounds = NIL;
+
+       relation = heap_openr(TypeRelationName, RowExclusiveLock);
+
+       /* Use LookupTypeName here so that shell types can be removed. */
+       typeoid = LookupTypeName(typename);
+       if (!OidIsValid(typeoid))
+               elog(ERROR, "Type \"%s\" does not exist",
+                        TypeNameToString(typename));
+
+       tup = SearchSysCache(TYPEOID,
+                                                ObjectIdGetDatum(typeoid),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup))
+               elog(ERROR, "Type \"%s\" does not exist",
+                        TypeNameToString(typename));
+
+       if (!pg_type_ownercheck(typeoid, GetUserId()))
+               elog(ERROR, "RemoveType: type '%s': permission denied",
+                        TypeNameToString(typename));
+
+       /* Delete any comments associated with this type */
+       DeleteComments(typeoid, RelationGetRelid(relation));
+
+       /* Remove the type tuple from pg_type */
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       /* Now, delete the "array of" that type */
+       typename->arrayBounds = makeList1(makeInteger(1));
+
+       typeoid = LookupTypeName(typename);
+       if (!OidIsValid(typeoid))
+               elog(ERROR, "Type \"%s\" does not exist",
+                        TypeNameToString(typename));
+
+       tup = SearchSysCache(TYPEOID,
+                                                ObjectIdGetDatum(typeoid),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup))
+               elog(ERROR, "Type \"%s\" does not exist",
+                        TypeNameToString(typename));
+
+       DeleteComments(typeoid, RelationGetRelid(relation));
+
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       heap_close(relation, RowExclusiveLock);
+}
+
+
+/*
+ * DefineDomain
+ *             Registers a new domain.
+ */
+void
+DefineDomain(CreateDomainStmt *stmt)
+{
+       char       *domainName;
+       Oid                     domainNamespace;
+       int16           internalLength;
+       int16           externalLength;
+       Oid                     inputProcedure;
+       Oid                     outputProcedure;
+       Oid                     receiveProcedure;
+       Oid                     sendProcedure;
+       bool            byValue;
+       char            delimiter;
+       char            alignment;
+       char            storage;
+       char            typtype;
+       Datum           datum;
+       bool            isnull;
+       char       *defaultValue = NULL;
+       char       *defaultValueBin = NULL;
+       bool            typNotNull = false;
+       Oid                     basetypelem;
+       int32           typNDims = length(stmt->typename->arrayBounds);
+       HeapTuple       typeTup;
+       List       *schema = stmt->constraints;
+       List       *listptr;
+
+       /* Convert list of names to a name and namespace */
+       domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
+                                                                                                               &domainName);
+
+       /*
+        * Domainnames, unlike typenames don't need to account for the '_'
+        * prefix.  So they can be one character longer.
+        */
+       if (strlen(domainName) > (NAMEDATALEN - 1))
+               elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
+                        NAMEDATALEN - 1);
+
+       /*
+        * Look up the base type.
+        */
+       typeTup = typenameType(stmt->typename);
+
+       /*
+        * What we really don't want is domains of domains.  This could cause all sorts
+        * of neat issues if we allow that.
+        *
+        * With testing, we may determine complex types should be allowed
+        */
+       typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
+       if (typtype != 'b')
+               elog(ERROR, "DefineDomain: %s is not a basetype",
+                        TypeNameToString(stmt->typename));
+
+       /* passed by value */
+       byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
+
+       /* Required Alignment */
+       alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
+
+       /* TOAST Strategy */
+       storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
+
+       /* Storage Length */
+       internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
+
+       /* External Length (unused) */
+       externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
+
+       /* Array element Delimiter */
+       delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
+
+       /* I/O Functions */
+       inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
+       outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
+       receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
+       sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
+
+       /* Inherited default value */
+       datum = SysCacheGetAttr(TYPEOID, typeTup,
+                                                       Anum_pg_type_typdefault, &isnull);
+       if (!isnull)
+               defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
+
+       /* Inherited default binary value */
+       datum = SysCacheGetAttr(TYPEOID, typeTup,
+                                                       Anum_pg_type_typdefaultbin, &isnull);
+       if (!isnull)
+               defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
+
+       /*
+        * Pull out the typelem name of the parent OID.
+        *
+        * This is what enables us to make a domain of an array
+        */
+       basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
+
+       /*
+        * Run through constraints manually to avoid the additional
+        * processing conducted by DefineRelation() and friends.
+        *
+        * Besides, we don't want any constraints to be cooked.  We'll
+        * do that when the table is created via MergeDomainAttributes().
+        */
+       foreach(listptr, schema)
+       {
+               Constraint *colDef = lfirst(listptr);
+               bool nullDefined = false;
+               Node       *expr;
+               ParseState *pstate;
+
+               switch (colDef->contype)
+               {
+                       /*
+                        * The inherited default value may be overridden by the user
+                        * with the DEFAULT <expr> statement.
+                        *
+                        * We have to search the entire constraint tree returned as we
+                        * don't want to cook or fiddle too much.
+                        */
+                       case CONSTR_DEFAULT:
+                               /* Create a dummy ParseState for transformExpr */
+                               pstate = make_parsestate(NULL);
+                               /*
+                                * Cook the colDef->raw_expr into an expression.
+                                * Note: Name is strictly for error message
+                                */
+                               expr = cookDefault(pstate, colDef->raw_expr,
+                                                                  typeTup->t_data->t_oid,
+                                                                  stmt->typename->typmod,
+                                                                  domainName);
+                               /*
+                                * Expression must be stored as a nodeToString result,
+                                * but we also require a valid textual representation
+                                * (mainly to make life easier for pg_dump).
+                                */
+                               defaultValue = deparse_expression(expr,
+                                                               deparse_context_for(domainName,
+                                                                                                       InvalidOid),
+                                                                                                  false);
+                               defaultValueBin = nodeToString(expr);
+                               break;
+
+                       /*
+                        * Find the NULL constraint.
+                        */
+                       case CONSTR_NOTNULL:
+                               if (nullDefined) {
+                                       elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
+                               } else {
+                                       typNotNull = true;
+                                       nullDefined = true;
+                               }
+                               break;
+
+                       case CONSTR_NULL:
+                               if (nullDefined) {
+                                       elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
+                               } else {
+                                       typNotNull = false;
+                                       nullDefined = true;
+                               }
+                               break;
+
+                       case CONSTR_UNIQUE:
+                               elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
+                               break;
+
+                       case CONSTR_PRIMARY:
+                               elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
+                               break;
+
+                       case CONSTR_CHECK:
+                               elog(ERROR, "DefineDomain: CHECK Constraints not supported");
+                               break;
+
+                       case CONSTR_ATTR_DEFERRABLE:
+                       case CONSTR_ATTR_NOT_DEFERRABLE:
+                       case CONSTR_ATTR_DEFERRED:
+                       case CONSTR_ATTR_IMMEDIATE:
+                               elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
+                               break;
+
+                       default:
+                               elog(ERROR, "DefineDomain: unrecognized constraint node type");
+                               break;
+               }
+       }
+
+       /*
+        * Have TypeCreate do all the real work.
+        */
+       TypeCreate(domainName,                  /* type name */
+                          domainNamespace,             /* namespace */
+                          InvalidOid,                  /* preassigned type oid (not done here) */
+                          InvalidOid,                  /* relation oid (n/a here) */
+                          internalLength,              /* internal size */
+                          externalLength,              /* external size */
+                          'd',                                 /* type-type (domain type) */
+                          delimiter,                   /* array element delimiter */
+                          inputProcedure,              /* input procedure */
+                          outputProcedure,             /* output procedure */
+                          receiveProcedure,    /* receive procedure */
+                          sendProcedure,               /* send procedure */
+                          basetypelem,                 /* element type ID */
+                          typeTup->t_data->t_oid,      /* base type ID */
+                          defaultValue,                /* default type value (text) */
+                          defaultValueBin,             /* default type value (binary) */
+                          byValue,                             /* passed by value */
+                          alignment,                   /* required alignment */
+                          storage,                             /* TOAST strategy */
+                          stmt->typename->typmod, /* typeMod value */
+                          typNDims,                    /* Array dimensions for base type */
+                          typNotNull);                 /* Type NOT NULL */
+
+       /*
+        * Now we can clean up.
+        */
+       ReleaseSysCache(typeTup);
+}
+
+
+/*
+ *     RemoveDomain
+ *             Removes a domain.
+ */
+void
+RemoveDomain(List *names, int behavior)
+{
+       TypeName   *typename;
+       Relation        relation;
+       Oid                     typeoid;
+       HeapTuple       tup;
+       char            typtype;
+
+       /* CASCADE unsupported */
+       if (behavior == CASCADE)
+               elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
+
+       /* Make a TypeName so we can use standard type lookup machinery */
+       typename = makeNode(TypeName);
+       typename->names = names;
+       typename->typmod = -1;
+       typename->arrayBounds = NIL;
+
+       relation = heap_openr(TypeRelationName, RowExclusiveLock);
+
+       typeoid = typenameTypeId(typename);
+
+       tup = SearchSysCache(TYPEOID,
+                                                ObjectIdGetDatum(typeoid),
+                                                0, 0, 0);
+       if (!HeapTupleIsValid(tup))
+               elog(ERROR, "RemoveDomain: type '%s' does not exist",
+                        TypeNameToString(typename));
+
+       if (!pg_type_ownercheck(typeoid, GetUserId()))
+               elog(ERROR, "RemoveDomain: type '%s': permission denied",
+                        TypeNameToString(typename));
+
+       /* Check that this is actually a domain */
+       typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
+
+       if (typtype != 'd')
+               elog(ERROR, "%s is not a domain",
+                        TypeNameToString(typename));
+
+       /* Delete any comments associated with this type */
+       DeleteComments(typeoid, RelationGetRelid(relation));
+
+       /* Remove the type tuple from pg_type */
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       /* At present, domains don't have associated array types */
+
+       heap_close(relation, RowExclusiveLock);
+}
+
+
+/*
+ * Find a suitable I/O function for a type.
+ */
+static Oid
+findTypeIOFunction(List *procname, bool isOutput)
+{
+       Oid                     argList[FUNC_MAX_ARGS];
+       int                     nargs;
+       Oid                     procOid;
+
+       /*
+        * First look for a 1-argument func with all argtypes 0. This is
+        * valid for all kinds of procedure.
+        */
+       MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
+
+       procOid = LookupFuncName(procname, 1, argList);
+
+       if (!OidIsValid(procOid))
+       {
+               /*
+                * Alternatively, input procedures may take 3 args (data
+                * value, element OID, atttypmod); the pg_proc argtype
+                * signature is 0,OIDOID,INT4OID.  Output procedures may
+                * take 2 args (data value, element OID).
+                */
+               if (isOutput)
+               {
+                       /* output proc */
+                       nargs = 2;
+                       argList[1] = OIDOID;
+               }
+               else
+               {
+                       /* input proc */
+                       nargs = 3;
+                       argList[1] = OIDOID;
+                       argList[2] = INT4OID;
+               }
+               procOid = LookupFuncName(procname, nargs, argList);
+
+               if (!OidIsValid(procOid))
+                       func_error("TypeCreate", procname, 1, argList, NULL);
+       }
+
+       return procOid;
+}
index 6b8652db5b72e5c4adba96046a00dad33917600c..bb4f2185b08baac403fedd2e693812864714a18c 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: view.c,v 1.61 2002/03/29 19:06:08 tgl Exp $
+ *     $Id: view.c,v 1.62 2002/04/15 05:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,7 @@
 #include "access/xact.h"
 #include "catalog/heap.h"
 #include "catalog/namespace.h"
-#include "commands/creatinh.h"
+#include "commands/tablecmds.h"
 #include "commands/view.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
index 659ab63f5536dfd4ff2f65b6b95e50123ba6bfa4..a2045a9eb80e2dc808020df999865d9932defdd5 100644 (file)
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.157 2002/04/08 22:42:18 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.158 2002/04/15 05:22:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@
 #include "access/heapam.h"
 #include "catalog/heap.h"
 #include "catalog/namespace.h"
-#include "commands/command.h"
+#include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "executor/execdebug.h"
 #include "executor/execdefs.h"
index f09f90744e8fcae68cf416368dccadc3295d56af..b9bcce30e29acab992940a0f59ff650b7d638349 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.68 2002/03/21 16:00:38 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.69 2002/04/15 05:22:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,7 +16,7 @@
 
 #include "access/printtup.h"
 #include "catalog/heap.h"
-#include "commands/command.h"
+#include "commands/portalcmds.h"
 #include "executor/spi_priv.h"
 #include "tcop/tcopprot.h"
 #include "utils/lsyscache.h"
index 4599a27a6428512fd8c4b634270373e8c621299b..e8df96a53defeb4bc6e26b6a6674f36f0745a2a3 100644 (file)
@@ -8,14 +8,14 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.51 2002/03/21 16:01:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.52 2002/04/15 05:22:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "commands/command.h"
+#include "commands/portalcmds.h"
 #include "executor/execdefs.h"
 #include "executor/executor.h"
 #include "tcop/pquery.h"
index 77dfe4a23651b9124ea60ce1bfb3673ae81dc3af..63e6b5dd9b2184ccc8a453b0d2c6bade4644696b 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.148 2002/04/12 20:38:27 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.149 2002/04/15 05:22:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "catalog/pg_shadow.h"
 #include "commands/async.h"
 #include "commands/cluster.h"
-#include "commands/command.h"
 #include "commands/comment.h"
 #include "commands/copy.h"
-#include "commands/creatinh.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/explain.h"
+#include "commands/lockcmds.h"
+#include "commands/portalcmds.h"
 #include "commands/proclang.h"
-#include "commands/rename.h"
+#include "commands/schemacmds.h"
 #include "commands/sequence.h"
+#include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "commands/user.h"
 #include "commands/vacuum.h"
diff --git a/src/include/commands/command.h b/src/include/commands/command.h
deleted file mode 100644 (file)
index cfb8f20..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * command.h
- *       prototypes for command.c.
- *
- *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: command.h,v 1.37 2002/04/01 04:35:39 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef COMMAND_H
-#define COMMAND_H
-
-#include "utils/portal.h"
-
-
-/*
- * PerformPortalFetch
- *             Performs the POSTQUEL function FETCH.  Fetches count (or all if 0)
- * tuples in portal with name in the forward direction iff goForward.
- *
- * Exceptions:
- *             BadArg if forward invalid.
- *             "ERROR" if portal not found.
- */
-extern void PerformPortalFetch(char *name, bool forward, int count,
-                                                          CommandDest dest, char *completionTag);
-
-/*
- * PerformPortalClose
- *             Performs the POSTQUEL function CLOSE.
- */
-extern void PerformPortalClose(char *name, CommandDest dest);
-
-extern void PortalCleanup(Portal portal);
-
-/*
- * ALTER TABLE variants
- */
-extern void AlterTableAddColumn(Oid myrelid, bool inherits, ColumnDef *colDef);
-
-extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh,
-                                                                                const char *colName, Node *newDefault);
-
-extern void AlterTableAlterColumnDropNotNull(Oid myrelid,
-                                                        bool inh, const char *colName);
-
-extern void AlterTableAlterColumnSetNotNull(Oid myrelid,
-                                                        bool inh, const char *colName);
-
-extern void AlterTableAlterColumnFlags(Oid myrelid,
-                                                                          bool inh, const char *colName,
-                                                                          Node *flagValue, const char *flagType);
-
-extern void AlterTableDropColumn(Oid myrelid, bool inh,
-                                                                const char *colName, int behavior);
-
-extern void AlterTableAddConstraint(Oid myrelid,
-                                                                       bool inh, List *newConstraints);
-
-extern void AlterTableDropConstraint(Oid myrelid,
-                                                                        bool inh, const char *constrName, int behavior);
-
-extern void AlterTableCreateToastTable(Oid relOid, bool silent);
-
-extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId);
-
-/*
- * LOCK
- */
-extern void LockTableCommand(LockStmt *lockstmt);
-
-/*
- * SCHEMA
- */
-extern void CreateSchemaCommand(CreateSchemaStmt *parsetree);
-
-#endif   /* COMMAND_H */
diff --git a/src/include/commands/creatinh.h b/src/include/commands/creatinh.h
deleted file mode 100644 (file)
index fe0f2bc..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * creatinh.h
- *       prototypes for creatinh.c.
- *
- *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: creatinh.h,v 1.20 2002/03/29 19:06:22 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef CREATINH_H
-#define CREATINH_H
-
-#include "nodes/parsenodes.h"
-
-extern Oid     DefineRelation(CreateStmt *stmt, char relkind);
-extern void RemoveRelation(const RangeVar *relation);
-extern void TruncateRelation(const RangeVar *relation);
-
-#endif   /* CREATINH_H */
index 83b8fec6d77cb05cc6e06f9561186fcf7231181d..711bc91f57993f3a37e29795635e466e698bc35b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: defrem.h,v 1.34 2002/04/09 20:35:54 tgl Exp $
+ * $Id: defrem.h,v 1.35 2002/04/15 05:22:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,8 @@
 
 #include "nodes/parsenodes.h"
 
+#define DEFAULT_TYPDELIM               ','
+
 /*
  * prototypes in indexcmds.c
  */
@@ -33,22 +35,33 @@ extern void ReindexTable(RangeVar *relation, bool force);
 extern void ReindexDatabase(const char *databaseName, bool force, bool all);
 
 /*
- * prototypes in define.c
+ * DefineFoo and RemoveFoo are now both in foocmds.c
  */
+
 extern void CreateFunction(ProcedureStmt *stmt);
+extern void RemoveFunction(List *functionName, List *argTypes);
+
 extern void DefineOperator(List *names, List *parameters);
+extern void RemoveOperator(char *operatorName,
+                                                  TypeName *typeName1, TypeName *typeName2);
+
 extern void DefineAggregate(List *names, List *parameters);
+extern void RemoveAggregate(List *aggName, TypeName *aggType);
+
 extern void DefineType(List *names, List *parameters);
+extern void RemoveType(List *names);
 extern void DefineDomain(CreateDomainStmt *stmt);
-
-/*
- * prototypes in remove.c
- */
 extern void RemoveDomain(List *names, int behavior);
-extern void RemoveFunction(List *functionName, List *argTypes);
-extern void RemoveOperator(char *operatorName,
-                          TypeName *typeName1, TypeName *typeName2);
-extern void RemoveType(List *names);
-extern void RemoveAggregate(List *aggName, TypeName *aggType);
+
+
+/* support routines in define.c */
+
+extern void case_translate_language_name(const char *input, char *output);
+
+extern char *defGetString(DefElem *def);
+extern double defGetNumeric(DefElem *def);
+extern List *defGetQualifiedName(DefElem *def);
+extern TypeName *defGetTypeName(DefElem *def);
+extern int     defGetTypeLength(DefElem *def);
 
 #endif   /* DEFREM_H */
diff --git a/src/include/commands/lockcmds.h b/src/include/commands/lockcmds.h
new file mode 100644 (file)
index 0000000..04335c3
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * lockcmds.h
+ *       prototypes for lockcmds.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: lockcmds.h,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef LOCKCMDS_H
+#define LOCKCMDS_H
+
+#include "nodes/parsenodes.h"
+
+/*
+ * LOCK
+ */
+extern void LockTableCommand(LockStmt *lockstmt);
+
+#endif  /* LOCKCMDS_H */
diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h
new file mode 100644 (file)
index 0000000..b62e363
--- /dev/null
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * portalcmds.h
+ *       prototypes for portalcmds.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: portalcmds.h,v 1.1 2002/04/15 05:22:03 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORTALCMDS_H
+#define PORTALCMDS_H
+
+#include "utils/portal.h"
+
+/*
+ * PerformPortalFetch
+ *             Performs the POSTQUEL function FETCH.  Fetches count (or all if 0)
+ * tuples in portal with name in the forward direction iff goForward.
+ *
+ * Exceptions:
+ *             BadArg if forward invalid.
+ *             "ERROR" if portal not found.
+ */
+extern void PerformPortalFetch(char *name, bool forward, int count,
+                                                          CommandDest dest, char *completionTag);
+
+/*
+ * PerformPortalClose
+ *             Performs the POSTQUEL function CLOSE.
+ */
+extern void PerformPortalClose(char *name, CommandDest dest);
+
+extern void PortalCleanup(Portal portal);
+
+#endif  /* PORTALCMDS_H */
diff --git a/src/include/commands/rename.h b/src/include/commands/rename.h
deleted file mode 100644 (file)
index 48e1a1c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * rename.h
- *
- *
- *
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: rename.h,v 1.16 2002/03/31 07:49:30 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef RENAME_H
-#define RENAME_H
-
-extern void renameatt(Oid relid,
-                 const char *oldattname,
-                 const char *newattname,
-                 bool recurse);
-
-extern void renamerel(Oid relid,
-                 const char *newrelname);
-
-#endif   /* RENAME_H */
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
new file mode 100644 (file)
index 0000000..6adf9a4
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * schemacmds.h
+ *       prototypes for schemacmds.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: schemacmds.h,v 1.1 2002/04/15 05:22:04 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SCHEMACMDS_H
+#define SCHEMACMDS_H
+
+#include "nodes/parsenodes.h"
+
+extern void CreateSchemaCommand(CreateSchemaStmt *parsetree);
+
+#endif  /* SCHEMACMDS_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
new file mode 100644 (file)
index 0000000..5d89597
--- /dev/null
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * tablecmds.h
+ *       prototypes for tablecmds.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: tablecmds.h,v 1.1 2002/04/15 05:22:04 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TABLECMDS_H
+#define TABLECMDS_H
+
+#include "nodes/parsenodes.h"
+
+extern void AlterTableAddColumn(Oid myrelid, bool inherits,
+                                                               ColumnDef *colDef);
+
+extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh,
+                                                                                const char *colName,
+                                                                                Node *newDefault);
+
+extern void AlterTableAlterColumnDropNotNull(Oid myrelid, bool inh,
+                                                                                        const char *colName);
+
+extern void AlterTableAlterColumnSetNotNull(Oid myrelid, bool inh,
+                                                                                       const char *colName);
+
+extern void AlterTableAlterColumnFlags(Oid myrelid, bool inh,
+                                                                          const char *colName,
+                                                                          Node *flagValue, const char *flagType);
+
+extern void AlterTableDropColumn(Oid myrelid, bool inh,
+                                                                const char *colName, int behavior);
+
+extern void AlterTableAddConstraint(Oid myrelid, bool inh,
+                                                                       List *newConstraints);
+
+extern void AlterTableDropConstraint(Oid myrelid, bool inh,
+                                                                        const char *constrName, int behavior);
+
+extern void AlterTableCreateToastTable(Oid relOid, bool silent);
+
+extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId);
+
+extern Oid     DefineRelation(CreateStmt *stmt, char relkind);
+
+extern void RemoveRelation(const RangeVar *relation);
+
+extern void TruncateRelation(const RangeVar *relation);
+
+extern void renameatt(Oid relid,
+                 const char *oldattname,
+                 const char *newattname,
+                 bool recurse);
+
+extern void renamerel(Oid relid,
+                 const char *newrelname);
+
+#endif   /* TABLECMDS_H */