aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-04-15 05:22:04 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-04-15 05:22:04 +0000
commit71dc300a375f4932693b064b161a400448c9bc2e (patch)
treeab20c095125a6eab33644befd187f936cf4fd093
parentab1ead6b9761531787c78e0c80daf98b421ac06f (diff)
downloadpostgresql-71dc300a375f4932693b064b161a400448c9bc2e.tar.gz
postgresql-71dc300a375f4932693b064b161a400448c9bc2e.zip
The contents of command.c, creatinh.c, define.c, remove.c and rename.c
have been divided according to the type of object manipulated - so ALTER TABLE code is in tablecmds.c, aggregate commands in aggregatecmds.c and so on. A few common support routines remain in define.c (prototypes in src/include/commands/defrem.h). No code has been changed except for includes to reflect the new files. The prototypes for aggregatecmds.c, functioncmds.c, operatorcmds.c, and typecmds.c remain in src/include/commands/defrem.h. From John Gray <jgray@azuli.co.uk>
-rw-r--r--src/backend/commands/Makefile13
-rw-r--r--src/backend/commands/aggregatecmds.c208
-rw-r--r--src/backend/commands/cluster.c5
-rw-r--r--src/backend/commands/creatinh.c932
-rw-r--r--src/backend/commands/define.c1076
-rw-r--r--src/backend/commands/functioncmds.c431
-rw-r--r--src/backend/commands/lockcmds.c69
-rw-r--r--src/backend/commands/operatorcmds.c247
-rw-r--r--src/backend/commands/portalcmds.c234
-rw-r--r--src/backend/commands/proclang.c18
-rw-r--r--src/backend/commands/remove.c476
-rw-r--r--src/backend/commands/rename.c591
-rw-r--r--src/backend/commands/schemacmds.c116
-rw-r--r--src/backend/commands/sequence.c4
-rw-r--r--src/backend/commands/tablecmds.c (renamed from src/backend/commands/command.c)1711
-rw-r--r--src/backend/commands/typecmds.c660
-rw-r--r--src/backend/commands/view.c4
-rw-r--r--src/backend/executor/execMain.c4
-rw-r--r--src/backend/executor/spi.c4
-rw-r--r--src/backend/tcop/pquery.c4
-rw-r--r--src/backend/tcop/utility.c9
-rw-r--r--src/include/commands/command.h81
-rw-r--r--src/include/commands/creatinh.h23
-rw-r--r--src/include/commands/defrem.h35
-rw-r--r--src/include/commands/lockcmds.h24
-rw-r--r--src/include/commands/portalcmds.h39
-rw-r--r--src/include/commands/rename.h25
-rw-r--r--src/include/commands/schemacmds.h22
-rw-r--r--src/include/commands/tablecmds.h63
29 files changed, 3585 insertions, 3543 deletions
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index b27e6d77b2a..190b0fd64f6 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -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
index 00000000000..7ee0c8f0784
--- /dev/null
+++ b/src/backend/commands/aggregatecmds.c
@@ -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);
+}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index aad5361b6b8..8883eddc49f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -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
index 6a0cf811473..00000000000
--- a/src/backend/commands/creatinh.c
+++ /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);
-}
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
index 692fc9f957c..889ddd0f440 100644
--- a/src/backend/commands/define.c
+++ b/src/backend/commands/define.c
@@ -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.
*
*-------------------------------------------------------------------------
*/
@@ -38,42 +35,16 @@
#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
index 00000000000..0bee7cdced1
--- /dev/null
+++ b/src/backend/commands/functioncmds.c
@@ -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
index 00000000000..c91669572ef
--- /dev/null
+++ b/src/backend/commands/lockcmds.c
@@ -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
index 00000000000..54f48928b41
--- /dev/null
+++ b/src/backend/commands/operatorcmds.c
@@ -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
index 00000000000..6f690c0927c
--- /dev/null
+++ b/src/backend/commands/portalcmds.c
@@ -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);
+}
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 4ef8d8f72a0..2ad25fdbd4f 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -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"
@@ -30,21 +31,6 @@
#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
index c32d2b215c5..00000000000
--- a/src/backend/commands/remove.c
+++ /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
index 21db59b7f6b..00000000000
--- a/src/backend/commands/rename.c
+++ /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
index 00000000000..191d5e329b8
--- /dev/null
+++ b/src/backend/commands/schemacmds.c
@@ -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);
+}
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index a7678c5ce38..0fa56fcfb7d 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -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"
diff --git a/src/backend/commands/command.c b/src/backend/commands/tablecmds.c
index 9a20d8329ad..18cea28df12 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/tablecmds.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -28,27 +23,25 @@
#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;
+/* Used by attribute and relation renaming routines: */
- ExecutorRun(queryDesc, estate, direction, (long) count);
+#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 */
- 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 */
- }
+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);
- /* Return command status if wanted */
- if (completionTag)
- snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u",
- (dest == None) ? "MOVE" : "FETCH",
- estate->es_processed);
-
- /*
- * Restore outer command ID.
- */
- SetScanCommandId(savedId);
-
- /*
- * Clean up and switch back to old context.
- */
- if (temp_desc)
- pfree(queryDesc);
-
- MemoryContextSwitchTo(oldcontext);
-}
-
-/*
- * PerformPortalClose
- */
-void
-PerformPortalClose(char *name, CommandDest dest)
-{
- Portal portal;
-
- /*
- * sanity checks
- */
- if (name == NULL)
- {
- elog(WARNING, "PerformPortalClose: missing portal name");
- return;
- }
-
- /*
- * get the portal from the portal name
- */
- portal = GetPortalByName(name);
- if (!PortalIsValid(portal))
- {
- elog(WARNING, "PerformPortalClose: portal \"%s\" not found",
- name);
- return;
- }
-
- /*
- * Note: PortalCleanup is called as a side-effect
- */
- PortalDrop(portal);
-}
/* ----------------
* AlterTableAddColumn
@@ -1856,142 +1656,1427 @@ needs_toast_table(Relation rel)
return (tuple_length > TOAST_TUPLE_THRESHOLD);
}
+
+/* ----------------------------------------------------------------
+ * 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;
+}
+
/*
- * LOCK TABLE
+ * 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
-LockTableCommand(LockStmt *lockstmt)
+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 *p;
+ List *entry;
/*
- * Iterate over the list and open, lock, and close the relations one
- * at a time
+ * 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;
- foreach(p, lockstmt->relations)
+ /*
+ * 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)
{
- RangeVar *relation = lfirst(p);
- Oid reloid;
- int32 aclresult;
- Relation rel;
+ 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 don't want to open the relation until we've checked privilege.
- * So, manually get the relation OID.
+ * We should have an UNDER permission flag for this, but for now,
+ * demand that creator of a child table own the parent.
*/
- reloid = RangeVarGetRelid(relation, false);
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ elog(ERROR, "you do not own table \"%s\"",
+ parent->relname);
- if (lockstmt->mode == AccessShareLock)
- aclresult = pg_class_aclcheck(reloid, GetUserId(),
- ACL_SELECT);
- else
- aclresult = pg_class_aclcheck(reloid, GetUserId(),
- ACL_UPDATE | ACL_DELETE);
+ /*
+ * Reject duplications in the list of parents.
+ */
+ if (intMember(RelationGetRelid(relation), parentOids))
+ elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated",
+ parent->relname);
- if (aclresult != ACLCHECK_OK)
- elog(ERROR, "LOCK TABLE: permission denied");
+ 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));
- rel = relation_open(reloid, lockstmt->mode);
+ 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;
- /* 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);
+ /*
+ * 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;
+ }
- relation_close(rel, NoLock); /* close rel, keep lock */
+ /*
+ * 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);
+}
/*
- * CREATE SCHEMA
+ * 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
-CreateSchemaCommand(CreateSchemaStmt *stmt)
+renameatt(Oid relid,
+ const char *oldattname,
+ const char *newattname,
+ bool recurse)
{
- 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 targetrelation;
+ Relation attrelation;
+ HeapTuple atttup;
+ List *indexoidlist;
+ List *indexoidscan;
- saved_userid = GetUserId();
+ /*
+ * Grab an exclusive lock on the target table, which we will NOT
+ * release until end of transaction.
+ */
+ targetrelation = heap_open(relid, AccessExclusiveLock);
- if (!authId)
+ /*
+ * 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)
{
- owner_userid = saved_userid;
- owner_name = GetUserName(owner_userid);
+ 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);
+ }
}
- else if (superuser())
+
+ 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)
{
- owner_name = authId;
- /* The following will error out if user does not exist */
- owner_userid = get_usesysid(owner_name);
+ 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);
+
/*
- * 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.)
+ * Okay, look to see if any column name of the index matches the
+ * old attribute name.
*/
- SetUserId(owner_userid);
+ 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);
}
- else /* not superuser */
+
+ freeList(indexoidlist);
+
+ heap_close(attrelation, RowExclusiveLock);
+
+ /*
+ * Update att name in any RI triggers associated with the relation.
+ */
+ if (targetrelation->rd_rel->reltriggers > 0)
{
- 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);
+ /* 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);
}
- if (!allowSystemTableMods && IsReservedName(schemaName))
- elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas",
- schemaName);
+ heap_close(targetrelation, NoLock); /* close rel but keep lock! */
+}
- /* Create the schema's namespace */
- NamespaceCreate(schemaName, owner_userid);
+/*
+ * 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];
- /* Let commands in the schema-element-list know about the schema */
- CommandCounterIncrement();
+ /*
+ * 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);
+ }
/*
- * 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.
+ * Update rel name in any RI triggers associated with the relation.
*/
- parsetree_list = analyzeCreateSchemaStmt(stmt);
+ 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);
+ }
/*
- * Analyze and execute each command contained in the CREATE SCHEMA
+ * Close rel, but keep exclusive lock!
*/
- foreach(parsetree_item, parsetree_list)
+ 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)
{
- Node *parsetree = (Node *) lfirst(parsetree_item);
- List *querytree_list,
- *querytree_item;
+ 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;
+ }
- querytree_list = parse_analyze(parsetree, NULL);
+ /*
+ * 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;
+ }
- foreach(querytree_item, querytree_list)
+ /*
+ * 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)
{
- 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();
+ /* 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);
}
- /* Reset current user */
- SetUserId(saved_userid);
+ 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
index 00000000000..13dbf04aaac
--- /dev/null
+++ b/src/backend/commands/typecmds.c
@@ -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;
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 6b8652db5b7..bb4f2185b08 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -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"
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 659ab63f553..a2045a9eb80 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -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"
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index f09f90744e8..b9bcce30e29 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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"
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 4599a27a642..e8df96a53de 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -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"
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 77dfe4a2365..63e6b5dd9b2 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -22,16 +22,17 @@
#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
index cfb8f20890e..00000000000
--- a/src/include/commands/command.h
+++ /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
index fe0f2bc3512..00000000000
--- a/src/include/commands/creatinh.h
+++ /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 */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 83b8fec6d77..711bc91f579 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -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
index 00000000000..04335c31c4e
--- /dev/null
+++ b/src/include/commands/lockcmds.h
@@ -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
index 00000000000..b62e3638a91
--- /dev/null
+++ b/src/include/commands/portalcmds.h
@@ -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
index 48e1a1cbe0d..00000000000
--- a/src/include/commands/rename.h
+++ /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
index 00000000000..6adf9a4517e
--- /dev/null
+++ b/src/include/commands/schemacmds.h
@@ -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
index 00000000000..5d895972f5b
--- /dev/null
+++ b/src/include/commands/tablecmds.h
@@ -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 */