aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/Makefile.inc25
-rw-r--r--src/backend/commands/_deadcode/version.c336
-rw-r--r--src/backend/commands/async.c605
-rw-r--r--src/backend/commands/async.h33
-rw-r--r--src/backend/commands/cluster.c370
-rw-r--r--src/backend/commands/cluster.h30
-rw-r--r--src/backend/commands/command.c511
-rw-r--r--src/backend/commands/command.h56
-rw-r--r--src/backend/commands/copy.c782
-rw-r--r--src/backend/commands/copy.h21
-rw-r--r--src/backend/commands/creatinh.c564
-rw-r--r--src/backend/commands/creatinh.h20
-rw-r--r--src/backend/commands/defind.c505
-rw-r--r--src/backend/commands/define.c564
-rw-r--r--src/backend/commands/defrem.h53
-rw-r--r--src/backend/commands/explain.c219
-rw-r--r--src/backend/commands/explain.h17
-rw-r--r--src/backend/commands/purge.c168
-rw-r--r--src/backend/commands/purge.h20
-rw-r--r--src/backend/commands/recipe.c1181
-rw-r--r--src/backend/commands/recipe.h17
-rw-r--r--src/backend/commands/remove.c435
-rw-r--r--src/backend/commands/rename.c275
-rw-r--r--src/backend/commands/rename.h24
-rw-r--r--src/backend/commands/vacuum.c853
-rw-r--r--src/backend/commands/vacuum.h48
-rw-r--r--src/backend/commands/version.h26
-rw-r--r--src/backend/commands/view.c325
-rw-r--r--src/backend/commands/view.h20
29 files changed, 8103 insertions, 0 deletions
diff --git a/src/backend/commands/Makefile.inc b/src/backend/commands/Makefile.inc
new file mode 100644
index 00000000000..d05052dfccd
--- /dev/null
+++ b/src/backend/commands/Makefile.inc
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+# Makefile for the commands module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/backend/commands/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+VPATH:=$(VPATH):$(CURDIR)/commands
+
+
+SRCS_COMMANDS= async.c creatinh.c command.c copy.c defind.c define.c \
+ purge.c remove.c rename.c vacuum.c version.c view.c cluster.c \
+ recipe.c explain.c
+
+HEADERS+= async.h command.h copy.h creatinh.h defrem.h purge.h \
+ rename.h vacuum.h version.h view.h cluster.h \
+ recipe.h
+
+
diff --git a/src/backend/commands/_deadcode/version.c b/src/backend/commands/_deadcode/version.c
new file mode 100644
index 00000000000..6dd311cee7e
--- /dev/null
+++ b/src/backend/commands/_deadcode/version.c
@@ -0,0 +1,336 @@
+/*-------------------------------------------------------------------------
+ *
+ * version.c--
+ * This file contains all the rules that govern all version semantics.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * The version stuff has not been tested under postgres95 and probably doesn't
+ * work! - jolly 8/19/95
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $
+ *
+ * NOTES
+ * At the point the version is defined, 2 physical relations are created
+ * <vname>_added and <vname>_deleted.
+ *
+ * In addition, 4 rules are defined which govern the semantics of versions
+ * w.r.t retrieves, appends, replaces and deletes.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+
+#include "postgres.h"
+
+#include "utils/rel.h"
+#include "access/heapam.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "nodes/pg_list.h"
+#include "commands/version.h"
+#include "access/xact.h" /* for GetCurrentXactStartTime */
+#include "tcop/tcopprot.h"
+
+#define MAX_QUERY_LEN 1024
+
+char rule_buf[MAX_QUERY_LEN];
+static char attr_list[MAX_QUERY_LEN];
+
+static void setAttrList(char *bname);
+
+/*
+ * problem: the version system assumes that the rules it declares will
+ * be fired in the order of declaration, it also assumes
+ * goh's silly instead semantics. Unfortunately, it is a pain
+ * to make the version system work with the new semantics.
+ * However the whole problem can be solved, and some nice
+ * functionality can be achieved if we get multiple action rules
+ * to work. So thats what I did -- glass
+ *
+ * Well, at least they've been working for about 20 minutes.
+ *
+ * So any comments in this code about 1 rule per transction are false...:)
+ *
+ */
+
+/*
+ * This is needed because the rule system only allows
+ * *1* rule to be defined per transaction.
+ *
+ * NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ * DONT DO THAT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ * If you commit the current Xact all the palloced memory GOES AWAY
+ * and could be re-palloced in the new Xact and the whole hell breaks
+ * loose and poor people like me spend 2 hours of their live chassing
+ * a strange memory bug instead of watching the "Get Smart" marathon
+ * in NICK !
+ * DO NOT COMMIT THE XACT, just increase the Cid counter!
+ * _sp.
+ */
+static void
+eval_as_new_xact(char *query)
+{
+ /* WARNING! do not uncomment the following lines WARNING!
+ * CommitTransactionCommand();
+ * StartTransactionCommand();
+ */
+ CommandCounterIncrement();
+ pg_eval(query, (char **) NULL, (Oid *) NULL, 0);
+}
+
+/*
+ * Define a version.
+ */
+void
+DefineVersion(char *name, char *fromRelname, char *date)
+{
+ char *bname;
+ static char saved_basename[512];
+ static char saved_snapshot[512];
+
+ if (date == NULL) {
+ /* no time ranges */
+ bname = fromRelname;
+ (void) strcpy(saved_basename, (char *) bname);
+ *saved_snapshot = (char)NULL;
+ } else {
+ /* version is a snapshot */
+ bname = fromRelname;
+ (void) strcpy(saved_basename, (char *) bname);
+ sprintf(saved_snapshot, "['%s']", date);
+ }
+
+
+ /*
+ * Calls the routine ``GetAttrList'' get the list of attributes
+ * from the base relation.
+ * Code is put here so that we only need to look up the attribute once for
+ * both appends and replaces.
+ */
+ setAttrList(bname);
+
+ VersionCreate (name, saved_basename);
+ VersionAppend (name, saved_basename);
+ VersionDelete (name, saved_basename,saved_snapshot);
+ VersionReplace (name, saved_basename,saved_snapshot);
+ VersionRetrieve (name, saved_basename, saved_snapshot);
+}
+
+
+/*
+ * Creates the deltas.
+ */
+void
+VersionCreate(char *vname, char *bname)
+{
+ static char query_buf [MAX_QUERY_LEN];
+
+ /*
+ * Creating the dummy version relation for triggering rules.
+ */
+ sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2",
+ vname, bname);
+
+ pg_eval (query_buf, (char **) NULL, (Oid *) NULL, 0);
+
+ /*
+ * Creating the ``v_added'' relation
+ */
+ sprintf (query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2",
+ vname, bname);
+ eval_as_new_xact (query_buf);
+
+ /*
+ * Creating the ``v_deleted'' relation.
+ */
+ sprintf (query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
+ eval_as_new_xact (query_buf);
+}
+
+
+/*
+ * Given the relation name, does a catalog lookup for that relation and
+ * sets the global variable 'attr_list' with the list of attributes (names)
+ * for that relation.
+ */
+static void
+setAttrList(char *bname)
+{
+ Relation rdesc;
+ int i = 0;
+ int maxattrs = 0;
+ char *attrname;
+ char temp_buf[512];
+ int notfirst = 0;
+
+ rdesc = heap_openr(bname);
+ if (rdesc == NULL ) {
+ elog(WARN,"Unable to expand all -- amopenr failed ");
+ return;
+ }
+ maxattrs = RelationGetNumberOfAttributes(rdesc);
+
+ attr_list[0] = '\0';
+
+ for ( i = maxattrs-1 ; i > -1 ; --i ) {
+ attrname = (rdesc->rd_att->attrs[i]->attname).data;
+
+ if (notfirst == 1) {
+ sprintf(temp_buf, ", %s = new.%s", attrname, attrname);
+ } else {
+ sprintf(temp_buf, "%s = new.%s", attrname, attrname);
+ notfirst = 1;
+ }
+ strcat(attr_list, temp_buf);
+ }
+
+ heap_close(rdesc);
+
+ return;
+}
+
+/*
+ * This routine defines the rule governing the append semantics of
+ * versions. All tuples appended to a version gets appended to the
+ * <vname>_added relation.
+ */
+void
+VersionAppend(char *vname, char *bname)
+{
+ sprintf(rule_buf,
+ "define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)",
+ vname, vname, vname, attr_list);
+
+ eval_as_new_xact(rule_buf);
+}
+
+
+/*
+ * This routine defines the rule governing the retrieval semantics of
+ * versions. To retrieve tuples from a version , we need to:
+ *
+ * 1. Retrieve all tuples in the <vname>_added relation.
+ * 2. Retrieve all tuples in the base relation which are not in
+ * the <vname>_del relation.
+ */
+void
+VersionRetrieve(char *vname, char *bname, char *snapshot)
+{
+
+ sprintf(rule_buf,
+ "define rewrite rule %s_retrieve is on SELECT to %s do instead\n\
+SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \
+where _%s.oid !!= '%s_del.DOID'",
+ vname, vname, vname, vname, bname,
+ bname, snapshot,
+ vname, vname, bname, bname, vname);
+
+ eval_as_new_xact(rule_buf);
+
+ /* printf("%s\n",rule_buf); */
+
+}
+
+/*
+ * This routine defines the rules that govern the delete semantics of
+ * versions. Two things happens when we delete a tuple from a version:
+ *
+ * 1. If the tuple to be deleted was added to the version *after*
+ * the version was created, then we simply delete the tuple
+ * from the <vname>_added relation.
+ * 2. If the tuple to be deleted is actually in the base relation,
+ * then we have to mark that tuple as being deleted by adding
+ * it to the <vname>_del relation.
+ */
+void
+VersionDelete(char *vname, char *bname, char *snapshot)
+{
+
+ sprintf(rule_buf,
+ "define rewrite rule %s_delete1 is on delete to %s do instead\n \
+[delete %s_added where current.oid = %s_added.oid\n \
+ append %s_del(DOID = current.oid) from _%s in %s%s \
+ where current.oid = _%s.oid] \n",
+ vname,vname,vname,vname,vname,
+bname,bname,snapshot,bname);
+
+ eval_as_new_xact(rule_buf);
+#ifdef OLD_REWRITE
+ sprintf(rule_buf,
+ "define rewrite rule %s_delete2 is on delete to %s do instead \n \
+ append %s_del(DOID = current.oid) from _%s in %s%s \
+ where current.oid = _%s.oid \n",
+ vname,vname,vname,bname,bname,snapshot,bname);
+
+ eval_as_new_xact(rule_buf);
+#endif /* OLD_REWRITE */
+}
+
+/*
+ * This routine defines the rules that govern the update semantics
+ * of versions. To update a tuple in a version:
+ *
+ * 1. If the tuple is in <vname>_added, we simply ``replace''
+ * the tuple (as per postgres style).
+ * 2. if the tuple is in the base relation, then two things have to
+ * happen:
+ * 2.1 The tuple is marked ``deleted'' from the base relation by
+ * adding the tuple to the <vname>_del relation.
+ * 2.2 A copy of the tuple is appended to the <vname>_added relation
+ */
+void
+VersionReplace(char *vname, char *bname, char *snapshot)
+{
+ sprintf(rule_buf,
+ "define rewrite rule %s_replace1 is on replace to %s do instead \n\
+[replace %s_added(%s) where current.oid = %s_added.oid \n\
+ append %s_del(DOID = current.oid) from _%s in %s%s \
+ where current.oid = _%s.oid\n\
+ append %s_added(%s) from _%s in %s%s \
+ where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n",
+ vname,vname,vname,attr_list,vname,
+ vname,bname,bname,snapshot,bname,
+vname,attr_list,bname,bname,snapshot,vname,bname);
+
+ eval_as_new_xact(rule_buf);
+
+/* printf("%s\n",rule_buf); */
+#ifdef OLD_REWRITE
+ sprintf(rule_buf,
+ "define rewrite rule %s_replace2 is on replace to %s do \n\
+ append %s_del(DOID = current.oid) from _%s in %s%s \
+ where current.oid = _%s.oid\n",
+ vname,vname,vname,bname,bname,snapshot,bname);
+
+ eval_as_new_xact(rule_buf);
+
+ sprintf(rule_buf,
+ "define rewrite rule %s_replace3 is on replace to %s do instead\n\
+ append %s_added(%s) from _%s in %s%s \
+ where current.oid !!= '%s_added.oid' and current.oid = \
+ _%s.oid\n",
+ vname,vname, vname,attr_list,bname,bname,snapshot,vname,bname);
+
+ eval_as_new_xact(rule_buf);
+#endif /* OLD_REWRITE */
+/* printf("%s\n",rule_buf); */
+
+}
+
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
new file mode 100644
index 00000000000..2d3064fa472
--- /dev/null
+++ b/src/backend/commands/async.c
@@ -0,0 +1,605 @@
+/*-------------------------------------------------------------------------
+ *
+ * async.c--
+ * Asynchronous notification
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/* New Async Notification Model:
+ * 1. Multiple backends on same machine. Multiple backends listening on
+ * one relation.
+ *
+ * 2. One of the backend does a 'notify <relname>'. For all backends that
+ * are listening to this relation (all notifications take place at the
+ * end of commit),
+ * 2.a If the process is the same as the backend process that issued
+ * notification (we are notifying something that we are listening),
+ * signal the corresponding frontend over the comm channel using the
+ * out-of-band channel.
+ * 2.b For all other listening processes, we send kill(2) to wake up
+ * the listening backend.
+ * 3. Upon receiving a kill(2) signal from another backend process notifying
+ * that one of the relation that we are listening is being notified,
+ * we can be in either of two following states:
+ * 3.a We are sleeping, wake up and signal our frontend.
+ * 3.b We are in middle of another transaction, wait until the end of
+ * of the current transaction and signal our frontend.
+ * 4. Each frontend receives this notification and prcesses accordingly.
+ *
+ * -- jw, 12/28/93
+ *
+ */
+/*
+ * The following is the old model which does not work.
+ */
+/*
+ * Model is:
+ * 1. Multiple backends on same machine.
+ *
+ * 2. Query on one backend sends stuff over an asynchronous portal by
+ * appending to a relation, and then doing an async. notification
+ * (which takes place after commit) to all listeners on this relation.
+ *
+ * 3. Async. notification results in all backends listening on relation
+ * to be woken up, by a process signal kill(2), with name of relation
+ * passed in shared memory.
+ *
+ * 4. Each backend notifies its respective frontend over the comm
+ * channel using the out-of-band channel.
+ *
+ * 5. Each frontend receives this notification and processes accordingly.
+ *
+ * #4,#5 are changing soon with pending rewrite of portal/protocol.
+ *
+ */
+
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+#include "access/xact.h"
+
+#include "commands/async.h"
+#include "commands/copy.h"
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "nodes/pg_list.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_listener.h"
+
+#include "executor/execdefs.h"
+/* #include "executor/execdesc.h"*/
+
+#include "storage/bufmgr.h"
+#include "lib/dllist.h"
+#include "libpq/libpq.h"
+
+
+static int notifyFrontEndPending = 0;
+static int notifyIssued = 0;
+static Dllist *pendingNotifies = NULL;
+
+
+static int AsyncExistsPendingNotify(char *);
+static void ClearPendingNotify(void);
+
+/*
+ *--------------------------------------------------------------
+ * Async_NotifyHandler --
+ *
+ * This is the signal handler for SIGUSR2. When the backend
+ * is signaled, the backend can be in two states.
+ * 1. If the backend is in the middle of another transaction,
+ * we set the flag, notifyFrontEndPending, and wait until
+ * the end of the transaction to notify the front end.
+ * 2. If the backend is not in the middle of another transaction,
+ * we notify the front end immediately.
+ *
+ * -- jw, 12/28/93
+ * Results:
+ * none
+ *
+ * Side effects:
+ * none
+ */
+void
+#if defined(PORTNAME_linux)
+Async_NotifyHandler(int i)
+#else
+Async_NotifyHandler()
+#endif
+{
+ extern TransactionState CurrentTransactionState;
+
+ if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
+ (CurrentTransactionState->blockState == TRANS_DEFAULT)) {
+
+ elog(DEBUG, "Waking up sleeping backend process");
+ Async_NotifyFrontEnd();
+
+ }else {
+ elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d",
+ CurrentTransactionState->state,
+ CurrentTransactionState->blockState);
+ notifyFrontEndPending = 1;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_Notify --
+ *
+ * Adds the relation to the list of pending notifies.
+ * All notification happens at end of commit.
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ * All notification of backend processes happens here,
+ * then each backend notifies its corresponding front end at
+ * the end of commit.
+ *
+ * This correspond to 'notify <relname>' command
+ * -- jw, 12/28/93
+ *
+ * Results:
+ * XXX
+ *
+ * Side effects:
+ * All tuples for relname in pg_listener are updated.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_Notify(char *relname)
+{
+
+ HeapTuple lTuple, rTuple;
+ Relation lRel;
+ HeapScanDesc sRel;
+ TupleDesc tdesc;
+ ScanKeyData key;
+ Buffer b;
+ Datum d, value[3];
+ bool isnull;
+ char repl[3], nulls[3];
+
+ char *notifyName;
+
+ elog(DEBUG,"Async_Notify: %s",relname);
+
+ if (!pendingNotifies)
+ pendingNotifies = DLNewList();
+
+ notifyName = pstrdup(relname);
+ DLAddHead(pendingNotifies, DLNewElem(notifyName));
+
+ ScanKeyEntryInitialize(&key, 0,
+ Anum_pg_listener_relname,
+ NameEqualRegProcedure,
+ PointerGetDatum(notifyName));
+
+ lRel = heap_openr(ListenerRelationName);
+ tdesc = RelationGetTupleDescriptor(lRel);
+ sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key);
+
+ nulls[0] = nulls[1] = nulls[2] = ' ';
+ repl[0] = repl[1] = repl[2] = ' ';
+ repl[Anum_pg_listener_notify - 1] = 'r';
+ value[0] = value[1] = value[2] = (Datum) 0;
+ value[Anum_pg_listener_notify - 1] = Int32GetDatum(1);
+
+ while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) {
+ d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_notify,
+ tdesc, &isnull);
+ if (!DatumGetInt32(d)) {
+ rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl);
+ (void) heap_replace(lRel, &lTuple->t_ctid, rTuple);
+ }
+ ReleaseBuffer(b);
+ }
+ heap_endscan(sRel);
+ heap_close(lRel);
+ notifyIssued = 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_NotifyAtCommit --
+ *
+ * Signal our corresponding frontend process on relations that
+ * were notified. Signal all other backend process that
+ * are listening also.
+ *
+ * -- jw, 12/28/93
+ *
+ * Results:
+ * XXX
+ *
+ * Side effects:
+ * Tuples in pg_listener that has our listenerpid are updated so
+ * that the notification is 0. We do not want to notify frontend
+ * more than once.
+ *
+ * -- jw, 12/28/93
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_NotifyAtCommit()
+{
+ HeapTuple lTuple;
+ Relation lRel;
+ HeapScanDesc sRel;
+ TupleDesc tdesc;
+ ScanKeyData key;
+ Datum d;
+ int ourpid;
+ bool isnull;
+ Buffer b;
+ extern TransactionState CurrentTransactionState;
+
+ if (!pendingNotifies)
+ pendingNotifies = DLNewList();
+
+ if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
+ (CurrentTransactionState->blockState == TRANS_DEFAULT)) {
+
+ if (notifyIssued) { /* 'notify <relname>' issued by us */
+ notifyIssued = 0;
+ StartTransactionCommand();
+ elog(DEBUG, "Async_NotifyAtCommit.");
+ ScanKeyEntryInitialize(&key, 0,
+ Anum_pg_listener_notify,
+ Integer32EqualRegProcedure,
+ Int32GetDatum(1));
+ lRel = heap_openr(ListenerRelationName);
+ sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key);
+ tdesc = RelationGetTupleDescriptor(lRel);
+ ourpid = getpid();
+
+ while (HeapTupleIsValid(lTuple = heap_getnext(sRel,0, &b))) {
+ d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname,
+ tdesc, &isnull);
+
+ if (AsyncExistsPendingNotify((char *) DatumGetPointer(d))) {
+ d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_pid,
+ tdesc, &isnull);
+
+ if (ourpid == DatumGetInt32(d)) {
+ elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1");
+ notifyFrontEndPending = 1;
+ } else {
+ elog(DEBUG, "Notifying others");
+#ifndef WIN32
+ if (kill(DatumGetInt32(d), SIGUSR2) < 0) {
+ if (errno == ESRCH) {
+ heap_delete(lRel, &lTuple->t_ctid);
+ }
+ }
+#endif /* WIN32 */
+ }
+ }
+ ReleaseBuffer(b);
+ }
+ CommitTransactionCommand();
+ ClearPendingNotify();
+ }
+
+ if (notifyFrontEndPending) { /* we need to notify the frontend of
+ all pending notifies. */
+ notifyFrontEndPending = 1;
+ Async_NotifyFrontEnd();
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_NotifyAtAbort --
+ *
+ * Gets rid of pending notifies. List elements are automatically
+ * freed through memory context.
+ *
+ *
+ * Results:
+ * XXX
+ *
+ * Side effects:
+ * XXX
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_NotifyAtAbort()
+{
+ extern TransactionState CurrentTransactionState;
+
+ if (notifyIssued) {
+ ClearPendingNotify();
+ }
+ notifyIssued = 0;
+ if (pendingNotifies)
+ DLFreeList(pendingNotifies);
+ pendingNotifies = DLNewList();
+
+ if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
+ (CurrentTransactionState->blockState == TRANS_DEFAULT)) {
+ if (notifyFrontEndPending) { /* don't forget to notify front end */
+ Async_NotifyFrontEnd();
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_Listen --
+ *
+ * Register a backend (identified by its Unix PID) as listening
+ * on the specified relation.
+ *
+ * This corresponds to the 'listen <relation>' command in SQL
+ *
+ * One listener per relation, pg_listener relation is keyed
+ * on (relname,pid) to provide multiple listeners in future.
+ *
+ * Results:
+ * pg_listeners is updated.
+ *
+ * Side effects:
+ * XXX
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_Listen(char *relname, int pid)
+{
+ Datum values[Natts_pg_listener];
+ char nulls[Natts_pg_listener];
+ TupleDesc tdesc;
+ HeapScanDesc s;
+ HeapTuple htup,tup;
+ Relation lDesc;
+ Buffer b;
+ Datum d;
+ int i;
+ bool isnull;
+ int alreadyListener = 0;
+ int ourPid = getpid();
+ char *relnamei;
+ TupleDesc tupDesc;
+
+ elog(DEBUG,"Async_Listen: %s",relname);
+ for (i = 0 ; i < Natts_pg_listener; i++) {
+ nulls[i] = ' ';
+ values[i] = PointerGetDatum(NULL);
+ }
+
+ i = 0;
+ values[i++] = (Datum) relname;
+ values[i++] = (Datum) pid;
+ values[i++] = (Datum) 0; /* no notifies pending */
+
+ lDesc = heap_openr(ListenerRelationName);
+
+ /* is someone already listening. One listener per relation */
+ tdesc = RelationGetTupleDescriptor(lDesc);
+ s = heap_beginscan(lDesc,0,NowTimeQual,0,(ScanKey)NULL);
+ while (HeapTupleIsValid(htup = heap_getnext(s,0,&b))) {
+ d = (Datum) heap_getattr(htup,b,Anum_pg_listener_relname,tdesc,
+ &isnull);
+ relnamei = DatumGetPointer(d);
+ if (!strncmp(relnamei,relname, NAMEDATALEN)) {
+ d = (Datum) heap_getattr(htup,b,Anum_pg_listener_pid,tdesc,&isnull);
+ pid = DatumGetInt32(d);
+ if (pid == ourPid) {
+ alreadyListener = 1;
+ }
+ }
+ ReleaseBuffer(b);
+ }
+ heap_endscan(s);
+
+ if (alreadyListener) {
+ elog(NOTICE, "Async_Listen: We are already listening on %s",
+ relname);
+ return;
+ }
+
+ tupDesc = lDesc->rd_att;
+ tup = heap_formtuple(tupDesc,
+ values,
+ nulls);
+ heap_insert(lDesc, tup);
+
+ pfree(tup);
+ /* if (alreadyListener) {
+ elog(NOTICE,"Async_Listen: already one listener on %s (possibly dead)",relname);
+ }*/
+ heap_close(lDesc);
+
+ /*
+ * now that we are listening, we should make a note to ourselves
+ * to unlisten prior to dying.
+ */
+ relnamei = malloc(NAMEDATALEN); /* persists to process exit */
+ memset (relnamei, 0, NAMEDATALEN);
+ strncpy(relnamei, relname, NAMEDATALEN);
+ on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei);
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_Unlisten --
+ *
+ * Remove the backend from the list of listening backends
+ * for the specified relation.
+ *
+ * This would correspond to the 'unlisten <relation>'
+ * command, but there isn't one yet.
+ *
+ * Results:
+ * pg_listeners is updated.
+ *
+ * Side effects:
+ * XXX
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_Unlisten(char *relname, int pid)
+{
+ Relation lDesc;
+ HeapTuple lTuple;
+
+ lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname),
+ Int32GetDatum(pid),
+ 0,0);
+ lDesc = heap_openr(ListenerRelationName);
+ if (lTuple != NULL) {
+ heap_delete(lDesc,&lTuple->t_ctid);
+ }
+ heap_close(lDesc);
+}
+
+void
+Async_UnlistenOnExit(int code, /* from exitpg */
+ char *relname)
+{
+ Async_Unlisten((char *) relname, getpid());
+}
+
+/*
+ * --------------------------------------------------------------
+ * Async_NotifyFrontEnd --
+ *
+ * Perform an asynchronous notification to front end over
+ * portal comm channel. The name of the relation which contains the
+ * data is sent to the front end.
+ *
+ * We remove the notification flag from the pg_listener tuple
+ * associated with our process.
+ *
+ * Results:
+ * XXX
+ *
+ * Side effects:
+ *
+ * We make use of the out-of-band channel to transmit the
+ * notification to the front end. The actual data transfer takes
+ * place at the front end's request.
+ *
+ * --------------------------------------------------------------
+ */
+GlobalMemory notifyContext = NULL;
+
+void
+Async_NotifyFrontEnd()
+{
+ extern CommandDest whereToSendOutput;
+ HeapTuple lTuple, rTuple;
+ Relation lRel;
+ HeapScanDesc sRel;
+ TupleDesc tdesc;
+ ScanKeyData key[2];
+ Datum d, value[3];
+ char repl[3], nulls[3];
+ Buffer b;
+ int ourpid;
+ bool isnull;
+
+ notifyFrontEndPending = 0;
+
+ elog(DEBUG, "Async_NotifyFrontEnd: notifying front end.");
+
+ StartTransactionCommand();
+ ourpid = getpid();
+ ScanKeyEntryInitialize(&key[0], 0,
+ Anum_pg_listener_notify,
+ Integer32EqualRegProcedure,
+ Int32GetDatum(1));
+ ScanKeyEntryInitialize(&key[1], 0,
+ Anum_pg_listener_pid,
+ Integer32EqualRegProcedure,
+ Int32GetDatum(ourpid));
+ lRel = heap_openr(ListenerRelationName);
+ tdesc = RelationGetTupleDescriptor(lRel);
+ sRel = heap_beginscan(lRel, 0, NowTimeQual, 2, key);
+
+ nulls[0] = nulls[1] = nulls[2] = ' ';
+ repl[0] = repl[1] = repl[2] = ' ';
+ repl[Anum_pg_listener_notify - 1] = 'r';
+ value[0] = value[1] = value[2] = (Datum) 0;
+ value[Anum_pg_listener_notify - 1] = Int32GetDatum(0);
+
+ while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0,&b))) {
+ d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname,
+ tdesc, &isnull);
+ rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl);
+ (void) heap_replace(lRel, &lTuple->t_ctid, rTuple);
+
+ /* notifying the front end */
+
+ if (whereToSendOutput == Remote) {
+ pq_putnchar("A", 1);
+ pq_putint(ourpid, 4);
+ pq_putstr(DatumGetName(d)->data);
+ pq_flush();
+ } else {
+ elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions");
+ }
+ ReleaseBuffer(b);
+ }
+ CommitTransactionCommand();
+}
+
+static int
+AsyncExistsPendingNotify(char *relname)
+{
+ Dlelem* p;
+ for (p = DLGetHead(pendingNotifies);
+ p != NULL;
+ p = DLGetSucc(p)) {
+ if (!strcmp(DLE_VAL(p), relname))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+ClearPendingNotify()
+{
+ Dlelem* p;
+ while ( (p = DLRemHead(pendingNotifies)) != NULL)
+ free(DLE_VAL(p));
+}
+
diff --git a/src/backend/commands/async.h b/src/backend/commands/async.h
new file mode 100644
index 00000000000..65e4bd69d55
--- /dev/null
+++ b/src/backend/commands/async.h
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * async.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: async.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ASYNC_H
+#define ASYNC_H
+
+#include "nodes/memnodes.h"
+
+#if defined(PORTNAME_linux)
+extern void Async_NotifyHandler(int);
+#else
+extern void Async_NotifyHandler(void);
+#endif
+extern void Async_Notify(char *relname);
+extern void Async_NotifyAtCommit(void);
+extern void Async_NotifyAtAbort(void);
+extern void Async_Listen(char *relname, int pid);
+extern void Async_Unlisten(char *relname, int pid);
+extern void Async_UnlistenOnExit(int code, char *relname);
+
+extern GlobalMemory notifyContext;
+extern void Async_NotifyFrontEnd(void);
+
+#endif /* ASYNC_H */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
new file mode 100644
index 00000000000..8400832d6ac
--- /dev/null
+++ b/src/backend/commands/cluster.c
@@ -0,0 +1,370 @@
+/*-------------------------------------------------------------------------
+ *
+ * cluster.c--
+ * Paul Brown's implementation of cluster index.
+ *
+ * I am going to use the rename function as a model for this in the
+ * parser and executor, and the vacuum code as an example in this
+ * file. As I go - in contrast to the rest of postgres - there will
+ * be BUCKETS of comments. This is to allow reviewers to understand
+ * my (probably bogus) assumptions about the way this works.
+ * [pbrown '94]
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <stdio.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/htup.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "access/xact.h"
+#include "utils/tqual.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/index.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_type.h"
+
+#include "commands/copy.h"
+#include "commands/cluster.h"
+#include "commands/rename.h"
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/itemptr.h"
+
+#include "miscadmin.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "utils/builtins.h"
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+
+#include "optimizer/internal.h"
+
+#ifndef NO_SECURITY
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif /* !NO_SECURITY */
+
+/*
+ * cluster
+ *
+ * Check that the relation is a relation in the appropriate user
+ * ACL. I will use the same security that limits users on the
+ * renamerel() function.
+ *
+ * Check that the index specified is appropriate for the task
+ * ( ie it's an index over this relation ). This is trickier.
+ *
+ * Create a list of all the other indicies on this relation. Because
+ * the cluster will wreck all the tids, I'll need to destroy bogus
+ * indicies. The user will have to re-create them. Not nice, but
+ * I'm not a nice guy. The alternative is to try some kind of post
+ * destroy re-build. This may be possible. I'll check out what the
+ * index create functiond want in the way of paramaters. On the other
+ * hand, re-creating n indicies may blow out the space.
+ *
+ * Create new (temporary) relations for the base heap and the new
+ * index.
+ *
+ * Exclusively lock the relations.
+ *
+ * Create new clustered index and base heap relation.
+ *
+ */
+void
+cluster(char oldrelname[], char oldindexname[])
+{
+ Oid OIDOldHeap, OIDOldIndex, OIDNewHeap;
+
+ Relation OldHeap, OldIndex;
+ Relation NewHeap;
+
+ char *NewIndexName;
+ char *szNewHeapName;
+
+ /*
+ *
+ * I'm going to force all checking back into the commands.c function.
+ *
+ * Get the list if indicies for this relation. If the index we want
+ * is among them, do not add it to the 'kill' list, as it will be
+ * handled by the 'clean up' code which commits this transaction.
+ *
+ * I'm not using the SysCache, because this will happen but
+ * once, and the slow way is the sure way in this case.
+ *
+ */
+ /*
+ * Like vacuum, cluster spans transactions, so I'm going to handle it in
+ * the same way.
+ */
+
+ /* matches the StartTransaction in PostgresMain() */
+
+ OldHeap = heap_openr(oldrelname);
+ if (!RelationIsValid(OldHeap)) {
+ elog(WARN, "cluster: unknown relation: \"%-.*s\"",
+ NAMEDATALEN, oldrelname);
+ }
+ OIDOldHeap = OldHeap->rd_id; /* Get OID for the index scan */
+
+ OldIndex=index_openr(oldindexname);/* Open old index relation */
+ if (!RelationIsValid(OldIndex)) {
+ elog(WARN, "cluster: unknown index: \"%-.*s\"",
+ NAMEDATALEN, oldindexname);
+ }
+ OIDOldIndex = OldIndex->rd_id; /* OID for the index scan */
+
+ heap_close(OldHeap);
+ index_close(OldIndex);
+
+ /*
+ * I need to build the copies of the heap and the index. The Commit()
+ * between here is *very* bogus. If someone is appending stuff, they will
+ * get the lock after being blocked and add rows which won't be present in
+ * the new table. Bleagh! I'd be best to try and ensure that no-one's
+ * in the tables for the entire duration of this process with a pg_vlock.
+ */
+ NewHeap = copy_heap(OIDOldHeap);
+ OIDNewHeap = NewHeap->rd_id;
+ szNewHeapName = pstrdup(NewHeap->rd_rel->relname.data);
+
+ /* Need to do this to make the new heap visible. */
+ CommandCounterIncrement();
+
+ rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
+
+ /* Need to do this to make the new heap visible. */
+ CommandCounterIncrement();
+
+ /* can't be found in the SysCache. */
+ copy_index(OIDOldIndex, OIDNewHeap); /* No contention with the old */
+
+ /*
+ * make this really happen. Flush all the buffers.
+ */
+ CommitTransactionCommand();
+ StartTransactionCommand();
+
+ /*
+ * Questionable bit here. Because the renamerel destroys all trace of the
+ * pre-existing relation, I'm going to Destroy old, and then rename new
+ * to old. If this fails, it fails, and you lose your old. Tough - say
+ * I. Have good backups!
+ */
+
+ /*
+ Here lies the bogosity. The RelationNameGetRelation returns a bad
+ list of TupleDescriptors. Damn. Can't work out why this is.
+ */
+
+ heap_destroy(oldrelname); /* AAAAAAAAGH!! */
+
+ CommandCounterIncrement();
+
+ /*
+ * The Commit flushes all palloced memory, so I have to grab the
+ * New stuff again. This is annoying, but oh heck!
+ */
+/*
+ renamerel(szNewHeapName.data, oldrelname);
+ TypeRename(&szNewHeapName, &szOldRelName);
+
+ sprintf(NewIndexName.data, "temp_%x", OIDOldIndex);
+ renamerel(NewIndexName.data, szOldIndexName.data);
+*/
+ NewIndexName = palloc(NAMEDATALEN+1); /* XXX */
+ sprintf(NewIndexName, "temp_%x", OIDOldIndex);
+ renamerel(NewIndexName, oldindexname);
+}
+
+Relation
+copy_heap(Oid OIDOldHeap)
+{
+ char NewName[NAMEDATALEN];
+ TupleDesc OldHeapDesc, tupdesc;
+ Oid OIDNewHeap;
+ Relation NewHeap, OldHeap;
+
+ /*
+ * Create a new heap relation with a temporary name, which has the
+ * same tuple description as the old one.
+ */
+ sprintf(NewName,"temp_%x", OIDOldHeap);
+
+ OldHeap= heap_open(OIDOldHeap);
+ OldHeapDesc= RelationGetTupleDescriptor(OldHeap);
+
+ /*
+ * Need to make a copy of the tuple descriptor, heap_create modifies
+ * it.
+ */
+
+ tupdesc = CreateTupleDescCopy(OldHeapDesc);
+
+ OIDNewHeap=heap_create(NewName,
+ NULL,
+ OldHeap->rd_rel->relarch,
+ OldHeap->rd_rel->relsmgr,
+ tupdesc);
+
+ if (!OidIsValid(OIDNewHeap))
+ elog(WARN,"clusterheap: cannot create temporary heap relation\n");
+
+ NewHeap=heap_open(OIDNewHeap);
+
+ heap_close(NewHeap);
+ heap_close(OldHeap);
+
+ return NewHeap;
+}
+
+void
+copy_index(Oid OIDOldIndex, Oid OIDNewHeap)
+{
+ Relation OldIndex, NewHeap;
+ HeapTuple Old_pg_index_Tuple, Old_pg_index_relation_Tuple, pg_proc_Tuple;
+ IndexTupleForm Old_pg_index_Form;
+ Form_pg_class Old_pg_index_relation_Form;
+ Form_pg_proc pg_proc_Form;
+ char *NewIndexName;
+ AttrNumber *attnumP;
+ int natts;
+ FuncIndexInfo * finfo;
+
+ NewHeap = heap_open(OIDNewHeap);
+ OldIndex = index_open(OIDOldIndex);
+
+ /*
+ * OK. Create a new (temporary) index for the one that's already
+ * here. To do this I get the info from pg_index, re-build the
+ * FunctInfo if I have to, and add a new index with a temporary
+ * name.
+ */
+ Old_pg_index_Tuple =
+ SearchSysCacheTuple(INDEXRELID,
+ ObjectIdGetDatum(OldIndex->rd_id),
+ 0,0,0);
+
+ Assert(Old_pg_index_Tuple);
+ Old_pg_index_Form = (IndexTupleForm)GETSTRUCT(Old_pg_index_Tuple);
+
+ Old_pg_index_relation_Tuple =
+ SearchSysCacheTuple(RELOID,
+ ObjectIdGetDatum(OldIndex->rd_id),
+ 0,0,0);
+
+ Assert(Old_pg_index_relation_Tuple);
+ Old_pg_index_relation_Form =
+ (Form_pg_class)GETSTRUCT(Old_pg_index_relation_Tuple);
+
+ NewIndexName = palloc(NAMEDATALEN+1); /* XXX */
+ sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */
+
+ /*
+ * Ugly as it is, the only way I have of working out the number of
+ * attribues is to count them. Mostly there'll be just one but
+ * I've got to be sure.
+ */
+ for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0;
+ *attnumP != InvalidAttrNumber;
+ attnumP++, natts++);
+
+ /*
+ * If this is a functional index, I need to rebuild the functional
+ * component to pass it to the defining procedure.
+ */
+ if (Old_pg_index_Form->indproc != InvalidOid) {
+ FIgetnArgs(finfo) = natts;
+ FIgetProcOid(finfo) = Old_pg_index_Form->indproc;
+
+ pg_proc_Tuple =
+ SearchSysCacheTuple(PROOID,
+ ObjectIdGetDatum(Old_pg_index_Form->indproc),
+ 0,0,0);
+
+ Assert(pg_proc_Tuple);
+ pg_proc_Form = (Form_pg_proc)GETSTRUCT(pg_proc_Tuple);
+ namecpy(&(finfo->funcName), &(pg_proc_Form->proname));
+ } else {
+ finfo = (FuncIndexInfo *) NULL;
+ natts = 1;
+ }
+
+ index_create((NewHeap->rd_rel->relname).data,
+ NewIndexName,
+ finfo,
+ Old_pg_index_relation_Form->relam,
+ natts,
+ Old_pg_index_Form->indkey,
+ Old_pg_index_Form->indclass,
+ (uint16)0, (Datum) NULL, NULL);
+
+ heap_close(OldIndex);
+ heap_close(NewHeap);
+}
+
+
+void
+rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
+{
+ Relation LocalNewHeap, LocalOldHeap, LocalOldIndex;
+ IndexScanDesc ScanDesc;
+ RetrieveIndexResult ScanResult;
+ ItemPointer HeapTid;
+ HeapTuple LocalHeapTuple;
+ Buffer LocalBuffer;
+ Oid OIDNewHeapInsert;
+
+ /*
+ * Open the relations I need. Scan through the OldHeap on the OldIndex and
+ * insert each tuple into the NewHeap.
+ */
+ LocalNewHeap=(Relation)heap_open(OIDNewHeap);
+ LocalOldHeap=(Relation)heap_open(OIDOldHeap);
+ LocalOldIndex=(Relation)index_open(OIDOldIndex);
+
+ ScanDesc=index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
+
+ while ((ScanResult =
+ index_getnext(ScanDesc, ForwardScanDirection)) != NULL) {
+
+ HeapTid = &ScanResult->heap_iptr;
+ LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer);
+ OIDNewHeapInsert =
+ heap_insert(LocalNewHeap, LocalHeapTuple);
+ pfree(ScanResult);
+ ReleaseBuffer(LocalBuffer);
+ }
+
+ index_close(LocalOldIndex);
+ heap_close(LocalOldHeap);
+ heap_close(LocalNewHeap);
+}
+
diff --git a/src/backend/commands/cluster.h b/src/backend/commands/cluster.h
new file mode 100644
index 00000000000..2194e13f9a8
--- /dev/null
+++ b/src/backend/commands/cluster.h
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * cluster.h--
+ * header file for postgres cluster command stuff
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ * $Id: cluster.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CLUSTER_H
+#define CLUSTER_H
+
+/*
+ * defines for contant stuff
+ */
+#define _TEMP_RELATION_KEY_ "clXXXXXXXX"
+#define _SIZE_OF_TEMP_RELATION_KEY_ 11
+
+
+/*
+ * functions
+ */
+extern void cluster(char oldrelname[], char oldindexname[]);
+extern Relation copy_heap(Oid OIDOldHeap);
+extern void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
+extern void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
+
+#endif /* CLUSTER_H */
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
new file mode 100644
index 00000000000..4283b594d59
--- /dev/null
+++ b/src/backend/commands/command.c
@@ -0,0 +1,511 @@
+/*-------------------------------------------------------------------------
+ *
+ * command.c--
+ * random postgres portal and utility support code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ * NOTES
+ * The PortalExecutorHeapMemory crap needs to be eliminated
+ * by designing a better executor / portal processing memory
+ * interface.
+ *
+ * The PerformAddAttribute() code, like most of the relation
+ * manipulating code in the commands/ directory, should go
+ * someplace closer to the lib/catalog code.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+
+#include "commands/copy.h"
+
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+
+#include "miscadmin.h"
+
+#include "utils/portal.h"
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "catalog/indexing.h"
+
+#include "executor/executor.h"
+#include "executor/execdefs.h"
+#include "executor/execdesc.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/prep.h" /* for find_all_inheritors */
+
+
+#ifndef NO_SECURITY
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif /* !NO_SECURITY */
+
+/* ----------------
+ * PortalExecutorHeapMemory stuff
+ *
+ * This is where the XXXSuperDuperHacky code was. -cim 3/15/90
+ * ----------------
+ */
+MemoryContext PortalExecutorHeapMemory = NULL;
+
+/* --------------------------------
+ * PortalCleanup
+ * --------------------------------
+ */
+void
+PortalCleanup(Portal portal)
+{
+ MemoryContext context;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ AssertArg(PortalIsValid(portal));
+ AssertArg(portal->cleanup == PortalCleanup);
+
+ /* ----------------
+ * set proper portal-executor context before calling ExecMain.
+ * ----------------
+ */
+ context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
+ PortalExecutorHeapMemory = (MemoryContext)
+ PortalGetHeapMemory(portal);
+
+ /* ----------------
+ * tell the executor to shutdown the query
+ * ----------------
+ */
+ ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
+
+ /* ----------------
+ * switch back to previous context
+ * ----------------
+ */
+ (void) MemoryContextSwitchTo(context);
+ PortalExecutorHeapMemory = (MemoryContext) NULL;
+}
+
+/* --------------------------------
+ * PerformPortalFetch
+ * --------------------------------
+ */
+void
+PerformPortalFetch(char *name,
+ bool forward,
+ int count,
+ char *tag,
+ CommandDest dest)
+{
+ Portal portal;
+ int feature;
+ QueryDesc *queryDesc;
+ MemoryContext context;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ if (name == NULL) {
+ elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
+ return;
+ }
+
+ /* ----------------
+ * get the portal from the portal name
+ * ----------------
+ */
+ portal = GetPortalByName(name);
+ if (! PortalIsValid(portal)) {
+ elog(NOTICE, "PerformPortalFetch: portal \"%-.*s\" not found",
+ NAMEDATALEN, name);
+ return;
+ }
+
+ /* ----------------
+ * switch into the portal context
+ * ----------------
+ */
+ context= MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal));
+
+ AssertState(context ==
+ (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
+
+ /* ----------------
+ * setup "feature" to tell the executor what direction and
+ * how many tuples to fetch.
+ * ----------------
+ */
+ if (forward)
+ feature = EXEC_FOR;
+ else
+ feature = EXEC_BACK;
+
+ /* ----------------
+ * tell the destination to prepare to recieve some tuples
+ * ----------------
+ */
+ queryDesc = PortalGetQueryDesc(portal);
+ BeginCommand(name,
+ queryDesc->operation,
+ portal->attinfo,/* QueryDescGetTypeInfo(queryDesc), */
+ false, /* portal fetches don't end up in relations */
+ false, /* this is a portal fetch, not a "retrieve portal" */
+ tag,
+ dest);
+
+ /* ----------------
+ * execute the portal fetch operation
+ * ----------------
+ */
+ PortalExecutorHeapMemory = (MemoryContext)
+ PortalGetHeapMemory(portal);
+
+ ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
+
+ /* ----------------
+ * Note: the "end-of-command" tag is returned by higher-level
+ * utility code
+ *
+ * Return blank portal for now.
+ * Otherwise, this named portal will be cleaned.
+ * Note: portals will only be supported within a BEGIN...END
+ * block in the near future. Later, someone will fix it to
+ * do what is possible across transaction boundries.
+ * ----------------
+ */
+ (void) MemoryContextSwitchTo(
+ (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
+}
+
+/* --------------------------------
+ * PerformPortalClose
+ * --------------------------------
+ */
+void
+PerformPortalClose(char *name, CommandDest dest)
+{
+ Portal portal;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ if (name == NULL) {
+ elog(NOTICE, "PerformPortalClose: blank portal unsupported");
+ return;
+ }
+
+ /* ----------------
+ * get the portal from the portal name
+ * ----------------
+ */
+ portal = GetPortalByName(name);
+ if (! PortalIsValid(portal)) {
+ elog(NOTICE, "PerformPortalClose: portal \"%-.*s\" not found",
+ NAMEDATALEN, name);
+ return;
+ }
+
+ /* ----------------
+ * Note: PortalCleanup is called as a side-effect
+ * ----------------
+ */
+ PortalDestroy(&portal);
+}
+
+/* ----------------
+ * PerformAddAttribute
+ *
+ * adds an additional attribute to a relation
+ *
+ * Adds attribute field(s) to a relation. Each new attribute
+ * is given attnums in sequential order and is added to the
+ * ATTRIBUTE relation. If the AMI fails, defunct tuples will
+ * remain in the ATTRIBUTE relation for later vacuuming.
+ * Later, there may be some reserved attribute names???
+ *
+ * (If needed, can instead use elog to handle exceptions.)
+ *
+ * Note:
+ * Initial idea of ordering the tuple attributes so that all
+ * the variable length domains occured last was scratched. Doing
+ * so would not speed access too much (in general) and would create
+ * many complications in formtuple, amgetattr, and addattribute.
+ *
+ * scan attribute catalog for name conflict (within rel)
+ * scan type catalog for absence of data type (if not arg)
+ * create attnum magically???
+ * create attribute tuple
+ * insert attribute in attribute catalog
+ * modify reldesc
+ * create new relation tuple
+ * insert new relation in relation catalog
+ * delete original relation from relation catalog
+ * ----------------
+ */
+void
+PerformAddAttribute(char *relationName,
+ char *userName,
+ bool inherits,
+ ColumnDef *colDef)
+{
+ Relation relrdesc, attrdesc;
+ HeapScanDesc attsdesc;
+ HeapTuple reltup;
+ HeapTuple attributeTuple;
+ AttributeTupleForm attribute;
+ FormData_pg_attribute attributeD;
+ int i;
+ int minattnum, maxatts;
+ HeapTuple tup;
+ ScanKeyData key[2];
+ ItemPointerData oldTID;
+ Relation idescs[Num_pg_attr_indices];
+ Relation ridescs[Num_pg_class_indices];
+ bool hasindex;
+
+ /*
+ * 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 (IsSystemRelationName(relationName))
+ elog(WARN, "PerformAddAttribute: class \"%-.*s\" is a system catalog",
+ NAMEDATALEN, relationName);
+#ifndef NO_SECURITY
+ if (!pg_ownercheck(userName, relationName, RELNAME))
+ elog(WARN, "PerformAddAttribute: you do not own class \"%s\"",
+ relationName);
+#endif
+
+ /*
+ * if the first element in the 'schema' list is a "*" then we are
+ * supposed to add this attribute to all classes that inherit from
+ * 'relationName' (as well as to 'relationName').
+ *
+ * any permissions or problems with duplicate attributes will cause
+ * the whole transaction to abort, which is what we want -- all or
+ * nothing.
+ */
+ if (colDef != NULL) {
+ if (inherits) {
+ Oid myrelid, childrelid;
+ List *child, *children;
+
+ relrdesc = heap_openr(relationName);
+ if (!RelationIsValid(relrdesc)) {
+ elog(WARN, "PerformAddAttribute: unknown relation: \"%-.*s\"",
+ NAMEDATALEN, relationName);
+ }
+ myrelid = relrdesc->rd_id;
+ heap_close(relrdesc);
+
+ /* this routine is actually in the planner */
+ children = find_all_inheritors(lconsi(myrelid,NIL), NIL);
+
+ /*
+ * 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) {
+ childrelid = lfirsti(child);
+ if (childrelid == myrelid)
+ continue;
+ relrdesc = heap_open(childrelid);
+ if (!RelationIsValid(relrdesc)) {
+ elog(WARN, "PerformAddAttribute: can't find catalog entry for inheriting class with oid %d",
+ childrelid);
+ }
+ PerformAddAttribute((relrdesc->rd_rel->relname).data,
+ userName, false, colDef);
+ heap_close(relrdesc);
+ }
+ }
+ }
+
+ relrdesc = heap_openr(RelationRelationName);
+ reltup = ClassNameIndexScan(relrdesc, relationName);
+
+ if (!PointerIsValid(reltup)) {
+ heap_close(relrdesc);
+ elog(WARN, "PerformAddAttribute: relation \"%s\" not found",
+ relationName);
+ }
+ /*
+ * XXX is the following check sufficient?
+ */
+ if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX) {
+ elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed",
+ relationName);
+ return;
+ }
+
+ minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
+ maxatts = minattnum + 1;
+ if (maxatts > MaxHeapAttributeNumber) {
+ pfree(reltup); /* XXX temp */
+ heap_close(relrdesc); /* XXX temp */
+ elog(WARN, "PerformAddAttribute: relations limited to %d attributes",
+ MaxHeapAttributeNumber);
+ return;
+ }
+
+ attrdesc = heap_openr(AttributeRelationName);
+
+ Assert(attrdesc);
+ Assert(RelationGetRelationTupleForm(attrdesc));
+
+ /*
+ * Open all (if any) pg_attribute indices
+ */
+ hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex;
+ if (hasindex)
+ CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+
+ ScanKeyEntryInitialize(&key[0],
+ (bits16) NULL,
+ (AttrNumber) Anum_pg_attribute_attrelid,
+ (RegProcedure)ObjectIdEqualRegProcedure,
+ (Datum) reltup->t_oid);
+
+ ScanKeyEntryInitialize(&key[1],
+ (bits16) NULL,
+ (AttrNumber) Anum_pg_attribute_attname,
+ (RegProcedure)NameEqualRegProcedure,
+ (Datum) NULL);
+
+ attributeD.attrelid = reltup->t_oid;
+ attributeD.attdefrel = InvalidOid; /* XXX temporary */
+ attributeD.attnvals = 0; /* XXX temporary */
+ attributeD.atttyparg = InvalidOid; /* XXX temporary */
+ attributeD.attbound = 0; /* XXX temporary */
+ attributeD.attcanindex = 0; /* XXX need this info */
+ attributeD.attproc = InvalidOid; /* XXX tempoirary */
+ attributeD.attcacheoff = -1;
+
+ attributeTuple = heap_addheader(Natts_pg_attribute,
+ sizeof attributeD,
+ (char *)&attributeD);
+
+ attribute = (AttributeTupleForm)GETSTRUCT(attributeTuple);
+
+ i = 1 + minattnum;
+
+ {
+ HeapTuple typeTuple;
+ TypeTupleForm form;
+ char *p;
+ int attnelems;
+
+ /*
+ * XXX use syscache here as an optimization
+ */
+ key[1].sk_argument = (Datum)colDef->colname;
+ attsdesc = heap_beginscan(attrdesc, 0, NowTimeQual, 2, key);
+
+
+ tup = heap_getnext(attsdesc, 0, (Buffer *) NULL);
+ if (HeapTupleIsValid(tup)) {
+ pfree(reltup); /* XXX temp */
+ heap_endscan(attsdesc); /* XXX temp */
+ heap_close(attrdesc); /* XXX temp */
+ heap_close(relrdesc); /* XXX temp */
+ elog(WARN, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"",
+ key[1].sk_argument,
+ relationName);
+ return;
+ }
+ heap_endscan(attsdesc);
+
+ /*
+ * check to see if it is an array attribute.
+ */
+
+ p = colDef->typename->name;
+
+ if (colDef->typename->arrayBounds)
+ {
+ attnelems = length(colDef->typename->arrayBounds);
+ p = makeArrayTypeName(colDef->typename->name);
+ }
+ else
+ attnelems = 0;
+
+ typeTuple = SearchSysCacheTuple(TYPNAME,
+ PointerGetDatum(p),
+ 0,0,0);
+ form = (TypeTupleForm)GETSTRUCT(typeTuple);
+
+ if (!HeapTupleIsValid(typeTuple)) {
+ elog(WARN, "Add: type \"%s\" nonexistant", p);
+ }
+ namestrcpy(&(attribute->attname), (char*) key[1].sk_argument);
+ attribute->atttypid = typeTuple->t_oid;
+ attribute->attlen = form->typlen;
+ attribute->attnum = i;
+ attribute->attbyval = form->typbyval;
+ attribute->attnelems = attnelems;
+ attribute->attcacheoff = -1;
+ attribute->attisset = (bool) (form->typtype == 'c');
+ attribute->attalign = form->typalign;
+
+ heap_insert(attrdesc, attributeTuple);
+ if (hasindex)
+ CatalogIndexInsert(idescs,
+ Num_pg_attr_indices,
+ attrdesc,
+ attributeTuple);
+ }
+
+ if (hasindex)
+ CatalogCloseIndices(Num_pg_attr_indices, idescs);
+ heap_close(attrdesc);
+
+ ((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
+ oldTID = reltup->t_ctid;
+ (void) heap_replace(relrdesc, &oldTID, reltup);
+
+ /* keep catalog indices current */
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+ CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup);
+ CatalogCloseIndices(Num_pg_class_indices, ridescs);
+
+ pfree(reltup);
+ heap_close(relrdesc);
+}
diff --git a/src/backend/commands/command.h b/src/backend/commands/command.h
new file mode 100644
index 00000000000..266c6b4be14
--- /dev/null
+++ b/src/backend/commands/command.h
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * command.h--
+ * prototypes for command.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: command.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include "utils/portal.h"
+#include "tcop/dest.h"
+
+extern MemoryContext PortalExecutorHeapMemory;
+
+/*
+ * PortalCleanup --
+ * Cleans up the query state of the portal.
+ *
+ * Exceptions:
+ * BadArg if portal invalid.
+ */
+extern void PortalCleanup(Portal portal);
+
+
+/*
+ * 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.
+ * "WARN" if portal not found.
+ */
+extern void PerformPortalFetch(char *name, bool forward, int count,
+ char *tag, CommandDest dest);
+
+/*
+ * PerformPortalClose --
+ * Performs the POSTQUEL function CLOSE.
+ */
+extern void PerformPortalClose(char *name, CommandDest dest);
+
+/*
+ * PerformAddAttribute --
+ * Performs the POSTQUEL function ADD.
+ */
+extern void PerformAddAttribute(char *relationName, char *userName,
+ bool inh, ColumnDef *colDef);
+
+#endif /* COMMAND_H */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
new file mode 100644
index 00000000000..7e10818abfc
--- /dev/null
+++ b/src/backend/commands/copy.c
@@ -0,0 +1,782 @@
+/*-------------------------------------------------------------------------
+ *
+ * copy.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <sys/types.h> /* for mode_t */
+#include <sys/stat.h> /* for umask(2) prototype */
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_index.h"
+#include "catalog/index.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/funcindex.h"
+#include "access/tupdesc.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/pg_list.h"
+#include "executor/tuptable.h"
+#include "executor/executor.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "machine.h"
+
+/*
+ * New copy code.
+ *
+ * This code "knows" the following about tuples:
+ *
+ */
+
+static bool reading_from_input = false;
+
+/* non-export function prototypes */
+static void CopyTo(Relation rel, bool binary, FILE *fp, char *delim);
+static void CopyFrom(Relation rel, bool binary, FILE *fp, char *delim);
+static Oid GetOutputFunction(Oid type);
+static Oid GetTypeElement(Oid type);
+static Oid GetInputFunction(Oid type);
+static Oid IsTypeByVal(Oid type);
+static void GetIndexRelations(Oid main_relation_oid,
+ int *n_indices,
+ Relation **index_rels);
+static char *CopyReadAttribute(int attno, FILE *fp, bool *isnull, char *delim);
+static void CopyAttributeOut(FILE *fp, char *string, char *delim);
+static int CountTuples(Relation relation);
+
+extern FILE *Pfout, *Pfin;
+
+void
+DoCopy(char *relname, bool binary, bool from, bool pipe, char *filename,
+ char *delim)
+{
+ FILE *fp;
+ Relation rel;
+ reading_from_input = pipe;
+
+ rel = heap_openr(relname);
+ if (rel == NULL) elog(WARN, "Copy: class %s does not exist.", relname);
+
+ if (from) {
+ if (pipe && IsUnderPostmaster) ReceiveCopyBegin();
+ if (IsUnderPostmaster) {
+ fp = pipe ? Pfin : fopen(filename, "r");
+ }else {
+ fp = pipe ? stdin : fopen(filename, "r");
+ }
+ if (fp == NULL) {
+ elog(WARN, "COPY: file %s could not be open for reading", filename);
+ }
+ CopyFrom(rel, binary, fp, delim);
+ }else {
+
+ mode_t oumask = umask((mode_t) 0);
+
+ if (pipe && IsUnderPostmaster) SendCopyBegin();
+ if (IsUnderPostmaster) {
+ fp = pipe ? Pfout : fopen(filename, "w");
+
+ }else {
+ fp = pipe ? stdout : fopen(filename, "w");
+ }
+ (void) umask(oumask);
+ if (fp == NULL) {
+ elog(WARN, "COPY: file %s could not be open for writing", filename);
+ }
+ CopyTo(rel, binary, fp, delim);
+ }
+ if (!pipe) {
+ fclose(fp);
+ }else if (!from && !binary) {
+ fputs(".\n", fp);
+ if (IsUnderPostmaster) fflush(Pfout);
+ }
+}
+
+static void
+CopyTo(Relation rel, bool binary, FILE *fp, char *delim)
+{
+ HeapTuple tuple;
+ HeapScanDesc scandesc;
+
+ int32 attr_count, i;
+ AttributeTupleForm *attr;
+ func_ptr *out_functions;
+ int dummy;
+ Oid out_func_oid;
+ Oid *elements;
+ Datum value;
+ bool isnull = (bool) true;
+ char *nulls;
+ char *string;
+ int32 ntuples;
+ TupleDesc tupDesc;
+
+ scandesc = heap_beginscan(rel, 0, NULL, 0, NULL);
+
+ attr_count = rel->rd_att->natts;
+ attr = rel->rd_att->attrs;
+ tupDesc = rel->rd_att;
+
+ if (!binary) {
+ out_functions = (func_ptr *)
+ palloc(attr_count * sizeof(func_ptr));
+ elements = (Oid *) palloc(attr_count * sizeof(Oid));
+ for (i = 0; i < attr_count; i++) {
+ out_func_oid = (Oid) GetOutputFunction(attr[i]->atttypid);
+ fmgr_info(out_func_oid, &out_functions[i], &dummy);
+ elements[i] = GetTypeElement(attr[i]->atttypid);
+ }
+ }else {
+ nulls = (char *) palloc(attr_count);
+ for (i = 0; i < attr_count; i++) nulls[i] = ' ';
+
+ /* XXX expensive */
+
+ ntuples = CountTuples(rel);
+ fwrite(&ntuples, sizeof(int32), 1, fp);
+ }
+
+ for (tuple = heap_getnext(scandesc, 0, NULL);
+ tuple != NULL;
+ tuple = heap_getnext(scandesc, 0, NULL)) {
+
+ for (i = 0; i < attr_count; i++) {
+ value = (Datum)
+ heap_getattr(tuple, InvalidBuffer, i+1, tupDesc, &isnull);
+ if (!binary) {
+ if (!isnull) {
+ string = (char *) (out_functions[i]) (value, elements[i]);
+ CopyAttributeOut(fp, string, delim);
+ pfree(string);
+ }
+ if (i == attr_count - 1) {
+ fputc('\n', fp);
+ }else {
+ /* when copying out, only use the first char of the delim
+ string */
+ fputc(delim[0], fp);
+ }
+ }else {
+ /*
+ * only interesting thing heap_getattr tells us in this case
+ * is if we have a null attribute or not.
+ */
+ if (isnull) nulls[i] = 'n';
+ }
+ }
+
+ if (binary) {
+ int32 null_ct = 0, length;
+
+ for (i = 0; i < attr_count; i++) {
+ if (nulls[i] == 'n') null_ct++;
+ }
+
+ length = tuple->t_len - tuple->t_hoff;
+ fwrite(&length, sizeof(int32), 1, fp);
+ fwrite(&null_ct, sizeof(int32), 1, fp);
+ if (null_ct > 0) {
+ for (i = 0; i < attr_count; i++) {
+ if (nulls[i] == 'n') {
+ fwrite(&i, sizeof(int32), 1, fp);
+ nulls[i] = ' ';
+ }
+ }
+ }
+ fwrite((char *) tuple + tuple->t_hoff, length, 1, fp);
+ }
+ }
+
+ heap_endscan(scandesc);
+ if (binary) {
+ pfree(nulls);
+ }else {
+ pfree(out_functions);
+ pfree(elements);
+ }
+
+ heap_close(rel);
+}
+
+static void
+CopyFrom(Relation rel, bool binary, FILE *fp, char *delim)
+{
+ HeapTuple tuple;
+ IndexTuple ituple;
+ AttrNumber attr_count;
+ AttributeTupleForm *attr;
+ func_ptr *in_functions;
+ int i, dummy;
+ Oid in_func_oid;
+ Datum *values;
+ char *nulls, *index_nulls;
+ bool *byval;
+ bool isnull;
+ bool has_index;
+ int done = 0;
+ char *string, *ptr;
+ Relation *index_rels;
+ int32 len, null_ct, null_id;
+ int32 ntuples, tuples_read = 0;
+ bool reading_to_eof = true;
+ Oid *elements;
+ FuncIndexInfo *finfo, **finfoP;
+ TupleDesc *itupdescArr;
+ HeapTuple pgIndexTup;
+ IndexTupleForm *pgIndexP;
+ int *indexNatts;
+ char *predString;
+ Node **indexPred;
+ TupleDesc rtupdesc;
+ ExprContext *econtext;
+ TupleTable tupleTable;
+ TupleTableSlot *slot;
+ int natts;
+ AttrNumber *attnumP;
+ Datum idatum;
+ int n_indices;
+ InsertIndexResult indexRes;
+ TupleDesc tupDesc;
+
+ tupDesc = RelationGetTupleDescriptor(rel);
+ attr = tupDesc->attrs;
+ attr_count = tupDesc->natts;
+
+ has_index = false;
+
+ /*
+ * This may be a scalar or a functional index. We initialize all
+ * kinds of arrays here to avoid doing extra work at every tuple
+ * copy.
+ */
+
+ if (rel->rd_rel->relhasindex) {
+ GetIndexRelations(rel->rd_id, &n_indices, &index_rels);
+ if (n_indices > 0) {
+ has_index = true;
+ itupdescArr =
+ (TupleDesc *)palloc(n_indices * sizeof(TupleDesc));
+ pgIndexP =
+ (IndexTupleForm *)palloc(n_indices * sizeof(IndexTupleForm));
+ indexNatts = (int *) palloc(n_indices * sizeof(int));
+ finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo));
+ finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *));
+ indexPred = (Node **) palloc(n_indices * sizeof(Node*));
+ econtext = NULL;
+ for (i = 0; i < n_indices; i++) {
+ itupdescArr[i] = RelationGetTupleDescriptor(index_rels[i]);
+ pgIndexTup =
+ SearchSysCacheTuple(INDEXRELID,
+ ObjectIdGetDatum(index_rels[i]->rd_id),
+ 0,0,0);
+ Assert(pgIndexTup);
+ pgIndexP[i] = (IndexTupleForm)GETSTRUCT(pgIndexTup);
+ for (attnumP = &(pgIndexP[i]->indkey[0]), natts = 0;
+ *attnumP != InvalidAttrNumber;
+ attnumP++, natts++);
+ if (pgIndexP[i]->indproc != InvalidOid) {
+ FIgetnArgs(&finfo[i]) = natts;
+ natts = 1;
+ FIgetProcOid(&finfo[i]) = pgIndexP[i]->indproc;
+ *(FIgetname(&finfo[i])) = '\0';
+ finfoP[i] = &finfo[i];
+ } else
+ finfoP[i] = (FuncIndexInfo *) NULL;
+ indexNatts[i] = natts;
+ if (VARSIZE(&pgIndexP[i]->indpred) != 0) {
+ predString = fmgr(F_TEXTOUT, &pgIndexP[i]->indpred);
+ indexPred[i] = stringToNode(predString);
+ pfree(predString);
+ /* make dummy ExprContext for use by ExecQual */
+ if (econtext == NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+ tupleTable = ExecCreateTupleTable(1);
+ slot = ExecAllocTableSlot(tupleTable);
+ econtext = makeNode(ExprContext);
+ econtext->ecxt_scantuple = slot;
+ rtupdesc = RelationGetTupleDescriptor(rel);
+ slot->ttc_tupleDescriptor = rtupdesc;
+ /*
+ * There's no buffer associated with heap tuples here,
+ * so I set the slot's buffer to NULL. Currently, it
+ * appears that the only way a buffer could be needed
+ * would be if the partial index predicate referred to
+ * the "lock" system attribute. If it did, then
+ * heap_getattr would call HeapTupleGetRuleLock, which
+ * uses the buffer's descriptor to get the relation id.
+ * Rather than try to fix this, I'll just disallow
+ * partial indexes on "lock", which wouldn't be useful
+ * anyway. --Nels, Nov '92
+ */
+ /* SetSlotBuffer(slot, (Buffer) NULL); */
+ /* SetSlotShouldFree(slot, false); */
+ slot->ttc_buffer = (Buffer)NULL;
+ slot->ttc_shouldFree = false;
+#endif /* OMIT_PARTIAL_INDEX */
+ }
+ } else {
+ indexPred[i] = NULL;
+ }
+ }
+ }
+ }
+
+ if (!binary)
+ {
+ in_functions = (func_ptr *) palloc(attr_count * sizeof(func_ptr));
+ elements = (Oid *) palloc(attr_count * sizeof(Oid));
+ for (i = 0; i < attr_count; i++)
+ {
+ in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
+ fmgr_info(in_func_oid, &in_functions[i], &dummy);
+ elements[i] = GetTypeElement(attr[i]->atttypid);
+ }
+ }
+ else
+ {
+ fread(&ntuples, sizeof(int32), 1, fp);
+ if (ntuples != 0) reading_to_eof = false;
+ }
+
+ values = (Datum *) palloc(sizeof(Datum) * attr_count);
+ nulls = (char *) palloc(attr_count);
+ index_nulls = (char *) palloc(attr_count);
+ byval = (bool *) palloc(attr_count * sizeof(bool));
+
+ for (i = 0; i < attr_count; i++) {
+ nulls[i] = ' ';
+ index_nulls[i] = ' ';
+ byval[i] = (bool) IsTypeByVal(attr[i]->atttypid);
+ }
+
+ while (!done) {
+ if (!binary) {
+ for (i = 0; i < attr_count && !done; i++) {
+ string = CopyReadAttribute(i, fp, &isnull, delim);
+ if (isnull) {
+ values[i] = PointerGetDatum(NULL);
+ nulls[i] = 'n';
+ }else if (string == NULL) {
+ done = 1;
+ }else {
+ values[i] =
+ (Datum)(in_functions[i])(string,
+ elements[i],
+ attr[i]->attlen);
+ /*
+ * Sanity check - by reference attributes cannot return
+ * NULL
+ */
+ if (!PointerIsValid(values[i]) &&
+ !(rel->rd_att->attrs[i]->attbyval)) {
+ elog(WARN, "copy from: Bad file format");
+ }
+ }
+ }
+ }else { /* binary */
+ fread(&len, sizeof(int32), 1, fp);
+ if (feof(fp)) {
+ done = 1;
+ }else {
+ fread(&null_ct, sizeof(int32), 1, fp);
+ if (null_ct > 0) {
+ for (i = 0; i < null_ct; i++) {
+ fread(&null_id, sizeof(int32), 1, fp);
+ nulls[null_id] = 'n';
+ }
+ }
+
+ string = (char *) palloc(len);
+ fread(string, len, 1, fp);
+
+ ptr = string;
+
+ for (i = 0; i < attr_count; i++) {
+ if (byval[i] && nulls[i] != 'n') {
+
+ switch(attr[i]->attlen) {
+ case sizeof(char):
+ values[i] = (Datum) *(unsigned char *) ptr;
+ ptr += sizeof(char);
+ break;
+ case sizeof(short):
+ ptr = (char *) SHORTALIGN(ptr);
+ values[i] = (Datum) *(unsigned short *) ptr;
+ ptr += sizeof(short);
+ break;
+ case sizeof(int32):
+ ptr = (char *) INTALIGN(ptr);
+ values[i] = (Datum) *(uint32 *) ptr;
+ ptr += sizeof(int32);
+ break;
+ default:
+ elog(WARN, "COPY BINARY: impossible size!");
+ break;
+ }
+ }else if (nulls[i] != 'n') {
+ switch (attr[i]->attlen) {
+ case -1:
+ if (attr[i]->attalign == 'd')
+ ptr = (char *)DOUBLEALIGN(ptr);
+ else
+ ptr = (char *)INTALIGN(ptr);
+ values[i] = (Datum) ptr;
+ ptr += * (uint32 *) ptr;
+ break;
+ case sizeof(char):
+ values[i] = (Datum)ptr;
+ ptr += attr[i]->attlen;
+ break;
+ case sizeof(short):
+ ptr = (char*)SHORTALIGN(ptr);
+ values[i] = (Datum)ptr;
+ ptr += attr[i]->attlen;
+ break;
+ case sizeof(int32):
+ ptr = (char*)INTALIGN(ptr);
+ values[i] = (Datum)ptr;
+ ptr += attr[i]->attlen;
+ break;
+ default:
+ if (attr[i]->attalign == 'd')
+ ptr = (char *)DOUBLEALIGN(ptr);
+ else
+ ptr = (char *)LONGALIGN(ptr);
+ values[i] = (Datum) ptr;
+ ptr += attr[i]->attlen;
+ }
+ }
+ }
+ }
+ }
+ if (done) continue;
+
+ tupDesc = CreateTupleDesc(attr_count, attr);
+ tuple = heap_formtuple(tupDesc, values, nulls);
+ heap_insert(rel, tuple);
+
+ if (has_index) {
+ for (i = 0; i < n_indices; i++) {
+ if (indexPred[i] != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+ /* if tuple doesn't satisfy predicate,
+ * don't update index
+ */
+ slot->val = tuple;
+ /*SetSlotContents(slot, tuple); */
+ if (ExecQual((List*)indexPred[i], econtext) == false)
+ continue;
+#endif /* OMIT_PARTIAL_INDEX */
+ }
+ FormIndexDatum(indexNatts[i],
+ (AttrNumber *)&(pgIndexP[i]->indkey[0]),
+ tuple,
+ tupDesc,
+ InvalidBuffer,
+ &idatum,
+ index_nulls,
+ finfoP[i]);
+ ituple = index_formtuple(itupdescArr[i], &idatum, index_nulls);
+ ituple->t_tid = tuple->t_ctid;
+ indexRes = index_insert(index_rels[i], ituple);
+ if (indexRes) pfree(indexRes);
+ pfree(ituple);
+ }
+ }
+
+ if (binary) pfree(string);
+
+ for (i = 0; i < attr_count; i++) {
+ if (!byval[i] && nulls[i] != 'n') {
+ if (!binary) pfree((void*)values[i]);
+ }else if (nulls[i] == 'n') {
+ nulls[i] = ' ';
+ }
+ }
+
+ pfree(tuple);
+ tuples_read++;
+
+ if (!reading_to_eof && ntuples == tuples_read) done = true;
+ }
+ pfree(values);
+ if (!binary) pfree(in_functions);
+ pfree(nulls);
+ pfree(byval);
+ heap_close(rel);
+}
+
+static Oid
+GetOutputFunction(Oid type)
+{
+ HeapTuple typeTuple;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type),
+ 0,0,0);
+
+ if (HeapTupleIsValid(typeTuple))
+ return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
+
+ elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type);
+ return(InvalidOid);
+}
+
+static Oid
+GetTypeElement(Oid type)
+{
+ HeapTuple typeTuple;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type),
+ 0,0,0);
+
+
+ if (HeapTupleIsValid(typeTuple))
+ return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
+
+ elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type);
+ return(InvalidOid);
+}
+
+static Oid
+GetInputFunction(Oid type)
+{
+ HeapTuple typeTuple;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type),
+ 0,0,0);
+
+ if (HeapTupleIsValid(typeTuple))
+ return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typinput);
+
+ elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type);
+ return(InvalidOid);
+}
+
+static Oid
+IsTypeByVal(Oid type)
+{
+ HeapTuple typeTuple;
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(type),
+ 0,0,0);
+
+ if (HeapTupleIsValid(typeTuple))
+ return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typbyval);
+
+ elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type);
+
+ return(InvalidOid);
+}
+
+/*
+ * Given the OID of a relation, return an array of index relation descriptors
+ * and the number of index relations. These relation descriptors are open
+ * using heap_open().
+ *
+ * Space for the array itself is palloc'ed.
+ */
+
+typedef struct rel_list {
+ Oid index_rel_oid;
+ struct rel_list *next;
+} RelationList;
+
+static void
+GetIndexRelations(Oid main_relation_oid,
+ int *n_indices,
+ Relation **index_rels)
+{
+ RelationList *head, *scan;
+ Relation pg_index_rel;
+ HeapScanDesc scandesc;
+ Oid index_relation_oid;
+ HeapTuple tuple;
+ TupleDesc tupDesc;
+ int i;
+ bool isnull;
+
+ pg_index_rel = heap_openr(IndexRelationName);
+ scandesc = heap_beginscan(pg_index_rel, 0, NULL, 0, NULL);
+ tupDesc = RelationGetTupleDescriptor(pg_index_rel);
+
+ *n_indices = 0;
+
+ head = (RelationList *) palloc(sizeof(RelationList));
+ scan = head;
+ head->next = NULL;
+
+ for (tuple = heap_getnext(scandesc, 0, NULL);
+ tuple != NULL;
+ tuple = heap_getnext(scandesc, 0, NULL)) {
+
+ index_relation_oid =
+ (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer, 2,
+ tupDesc, &isnull));
+ if (index_relation_oid == main_relation_oid) {
+ scan->index_rel_oid =
+ (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer,
+ Anum_pg_index_indexrelid,
+ tupDesc, &isnull));
+ (*n_indices)++;
+ scan->next = (RelationList *) palloc(sizeof(RelationList));
+ scan = scan->next;
+ }
+ }
+
+ heap_endscan(scandesc);
+ heap_close(pg_index_rel);
+
+ *index_rels = (Relation *) palloc(*n_indices * sizeof(Relation));
+
+ for (i = 0, scan = head; i < *n_indices; i++, scan = scan->next) {
+ (*index_rels)[i] = index_open(scan->index_rel_oid);
+ }
+
+ for (i = 0, scan = head; i < *n_indices + 1; i++) {
+ scan = head->next;
+ pfree(head);
+ head = scan;
+ }
+}
+
+#define EXT_ATTLEN 5*8192
+
+/*
+ returns 1 is c is in s
+*/
+static bool
+inString(char c, char* s)
+{
+ int i;
+
+ if (s) {
+ i = 0;
+ while (s[i] != '\0') {
+ if (s[i] == c)
+ return 1;
+ i++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Reads input from fp until eof is seen. If we are reading from standard
+ * input, AND we see a dot on a line by itself (a dot followed immediately
+ * by a newline), we exit as if we saw eof. This is so that copy pipelines
+ * can be used as standard input.
+ */
+
+static char *
+CopyReadAttribute(int attno, FILE *fp, bool *isnull, char *delim)
+{
+ static char attribute[EXT_ATTLEN];
+ char c;
+ int done = 0;
+ int i = 0;
+
+ if (feof(fp)) {
+ *isnull = (bool) false;
+ return(NULL);
+ }
+
+ while (!done) {
+ c = getc(fp);
+
+ if (feof(fp)) {
+ *isnull = (bool) false;
+ return(NULL);
+ }else if (reading_from_input && attno == 0 && i == 0 && c == '.') {
+ attribute[0] = c;
+ c = getc(fp);
+ if (c == '\n') {
+ *isnull = (bool) false;
+ return(NULL);
+ }else if (inString(c,delim)) {
+ attribute[1] = 0;
+ *isnull = (bool) false;
+ return(&attribute[0]);
+ }else {
+ attribute[1] = c;
+ i = 2;
+ }
+ }else if (c == '\\') {
+ c = getc(fp);
+ }else if (inString(c,delim) || c == '\n') {
+ done = 1;
+ }
+ if (!done) attribute[i++] = c;
+ if (i == EXT_ATTLEN - 1)
+ elog(WARN, "CopyReadAttribute - attribute length too long");
+ }
+ attribute[i] = '\0';
+ if (i == 0) {
+ *isnull = (bool) true;
+ return(NULL);
+ }else {
+ *isnull = (bool) false;
+ return(&attribute[0]);
+ }
+}
+
+static void
+CopyAttributeOut(FILE *fp, char *string, char *delim)
+{
+ int i;
+ int len = strlen(string);
+
+ for (i = 0; i < len; i++) {
+ if (string[i] == delim[0] || string[i] == '\n' || string[i] == '\\') {
+ fputc('\\', fp);
+ }
+ fputc(string[i], fp);
+ }
+}
+
+/*
+ * Returns the number of tuples in a relation. Unfortunately, currently
+ * must do a scan of the entire relation to determine this.
+ *
+ * relation is expected to be an open relation descriptor.
+ */
+static int
+CountTuples(Relation relation)
+{
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+
+ int i;
+
+ scandesc = heap_beginscan(relation, 0, NULL, 0, NULL);
+
+ for (tuple = heap_getnext(scandesc, 0, NULL), i = 0;
+ tuple != NULL;
+ tuple = heap_getnext(scandesc, 0, NULL), i++)
+ ;
+ heap_endscan(scandesc);
+ return(i);
+}
diff --git a/src/backend/commands/copy.h b/src/backend/commands/copy.h
new file mode 100644
index 00000000000..ccd29555626
--- /dev/null
+++ b/src/backend/commands/copy.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * copy.h--
+ * Definitions for using the POSTGRES copy command.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: copy.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COPY_H
+#define COPY_H
+
+#include "postgres.h"
+
+void DoCopy(char *relname, bool binary, bool from, bool pipe, char *filename,
+ char *delim);
+
+#endif /* COPY_H */
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c
new file mode 100644
index 00000000000..a0e3a9f682b
--- /dev/null
+++ b/src/backend/commands/creatinh.c
@@ -0,0 +1,564 @@
+/*-------------------------------------------------------------------------
+ *
+ * creatinh.c--
+ * POSTGRES create/destroy relation with inheritance utility code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h> /* for sprintf() */
+#include <string.h>
+#include "postgres.h"
+
+#include "tcop/tcopdebug.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/execnodes.h"
+
+#include "utils/syscache.h"
+#include "utils/relcache.h"
+#include "catalog/catname.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_ipl.h"
+#include "parser/catalog_utils.h"
+
+#include "commands/creatinh.h"
+
+#include "access/tupdesc.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+
+/* ----------------
+ * local stuff
+ * ----------------
+ */
+
+static int checkAttrExists(char *attributeName,
+ char *attributeType, List *schema);
+static List *MergeAttributes(List *schema, List *supers);
+static void StoreCatalogInheritance(Oid relationId, List *supers);
+
+/* ----------------------------------------------------------------
+ * DefineRelation --
+ * Creates a new relation.
+ * ----------------------------------------------------------------
+ */
+void
+DefineRelation(CreateStmt *stmt)
+{
+ char *relname = stmt->relname;
+ List *schema = stmt->tableElts;
+ int numberOfAttributes;
+ Oid relationId;
+ char archChar;
+ List *inheritList = NULL;
+ char *archiveName = NULL;
+ TupleDesc descriptor;
+ int heaploc, archloc;
+
+ char* typename = NULL; /* the typename of this relation. not useod for now */
+
+ if ( strlen(relname) > NAMEDATALEN)
+ elog(WARN, "the relation name %s is > %d characters long", relname,
+ NAMEDATALEN);
+
+ /* ----------------
+ * Handle parameters
+ * XXX parameter handling missing below.
+ * ----------------
+ */
+ inheritList = stmt->inhRelnames;
+
+ /* ----------------
+ * determine archive mode
+ * XXX use symbolic constants...
+ * ----------------
+ */
+ archChar = 'n';
+
+ switch (stmt->archiveType) {
+ case ARCH_NONE:
+ archChar = 'n';
+ break;
+ case ARCH_LIGHT:
+ archChar = 'l';
+ break;
+ case ARCH_HEAVY:
+ archChar = 'h';
+ break;
+ default:
+ elog(WARN, "Botched archive mode %d, ignoring",
+ stmt->archiveType);
+ break;
+ }
+
+ if (stmt->location == -1)
+ heaploc = 0;
+ else
+ heaploc = stmt->location;
+
+ /*
+ * For now, any user-defined relation defaults to the magnetic
+ * disk storgage manager. --mao 2 july 91
+ */
+ if (stmt->archiveLoc == -1) {
+ archloc = 0;
+ } else {
+ if (archChar == 'n') {
+ elog(WARN, "Set archive location, but not mode, for %s",
+ relname);
+ }
+ archloc = stmt->archiveLoc;
+ }
+
+ /* ----------------
+ * generate relation schema, including inherited attributes.
+ * ----------------
+ */
+ schema = MergeAttributes(schema, inheritList);
+
+ numberOfAttributes = length(schema);
+ if (numberOfAttributes <= 0) {
+ elog(WARN, "DefineRelation: %s",
+ "please inherit from a relation or define an attribute");
+ }
+
+ /* ----------------
+ * create a relation descriptor from the relation schema
+ * and create the relation.
+ * ----------------
+ */
+ descriptor = BuildDescForRelation(schema, relname);
+ relationId = heap_create(relname,
+ typename,
+ archChar,
+ heaploc,
+ descriptor);
+
+ StoreCatalogInheritance(relationId, inheritList);
+
+ /* ----------------
+ * create an archive relation if necessary
+ * ----------------
+ */
+ if (archChar != 'n') {
+ /*
+ * Need to create an archive relation for this heap relation.
+ * We cobble up the command by hand, and increment the command
+ * counter ourselves.
+ */
+
+ CommandCounterIncrement();
+ archiveName = MakeArchiveName(relationId);
+
+ relationId = heap_create(archiveName,
+ typename,
+ 'n', /* archive isn't archived */
+ archloc,
+ descriptor);
+
+ pfree(archiveName);
+ }
+}
+
+/*
+ * RemoveRelation --
+ * Deletes a new 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(char *name)
+{
+ AssertArg(name);
+ heap_destroy(name);
+}
+
+
+/*
+ * MergeAttributes --
+ * Returns new schema given initial schema and supers.
+ *
+ *
+ * 'schema' is the column/attribute definition for the table. (It's a list
+ * of ColumnDef's.) It is destructively changed.
+ * 'inheritList' is the list of inherited relations (a list of Value(str)'s).
+ *
+ * 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 char16) 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 as follow:
+ *
+ *
+ * person {1:name, 2:age, 3:location}
+ * / \
+ * {6:gpa} student emp {4:salary, 5:manager}
+ * \ /
+ * stud_emp {7:percent}
+ */
+static List *
+MergeAttributes(List *schema, List *supers)
+{
+ List *entry;
+ List *inhSchema = NIL;
+
+ /*
+ * Validates that there are no duplications.
+ * Validity checking of types occurs later.
+ */
+ foreach (entry, schema) {
+ List *rest;
+ ColumnDef *coldef = lfirst(entry);
+
+ foreach (rest, lnext(entry)) {
+ /*
+ * check for duplicated relation names
+ */
+ ColumnDef *restdef = lfirst(rest);
+
+ if (!strcmp(coldef->colname, restdef->colname)) {
+ elog(WARN, "attribute \"%s\" duplicated",
+ coldef->colname);
+ }
+ }
+ }
+ foreach (entry, supers) {
+ List *rest;
+
+ foreach (rest, lnext(entry)) {
+ if (!strcmp(strVal(lfirst(entry)), strVal(lfirst(rest)))) {
+ elog(WARN, "relation \"%s\" duplicated",
+ strVal(lfirst(entry)));
+ }
+ }
+ }
+
+ /*
+ * merge the inherited attributes into the schema
+ */
+ foreach (entry, supers) {
+ char *name = strVal(lfirst(entry));
+ Relation relation;
+ List *partialResult = NIL;
+ AttrNumber attrno;
+ TupleDesc tupleDesc;
+
+ relation = heap_openr(name);
+ if (relation==NULL) {
+ elog(WARN,
+ "MergeAttr: Can't inherit from non-existent superclass '%s'",
+ name);
+ }
+ tupleDesc = RelationGetTupleDescriptor(relation);
+
+ for (attrno = relation->rd_rel->relnatts - 1; attrno >= 0; attrno--) {
+ AttributeTupleForm attribute = tupleDesc->attrs[attrno];
+ char *attributeName;
+ char *attributeType;
+ HeapTuple tuple;
+ ColumnDef *def;
+ TypeName *typename;
+
+ /*
+ * form name and type
+ */
+ attributeName = (attribute->attname).data;
+ tuple =
+ SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(attribute->atttypid),
+ 0,0,0);
+ AssertState(HeapTupleIsValid(tuple));
+ attributeType =
+ (((TypeTupleForm)GETSTRUCT(tuple))->typname).data;
+ /*
+ * check validity
+ *
+ */
+ if (checkAttrExists(attributeName, attributeType, inhSchema) ||
+ checkAttrExists(attributeName, attributeType, schema)) {
+ /*
+ * this entry already exists
+ */
+ continue;
+ }
+
+ /*
+ * add an entry to the schema
+ */
+ def = makeNode(ColumnDef);
+ typename = makeNode(TypeName);
+ def->colname = pstrdup(attributeName);
+ typename->name = pstrdup(attributeType);
+ def->typename = typename;
+ partialResult = lcons(def, partialResult);
+ }
+
+ /*
+ * iteration cleanup and result collection
+ */
+ heap_close(relation);
+
+ /*
+ * wants the inherited schema to appear in the order they are
+ * specified in CREATE TABLE
+ */
+ inhSchema = nconc(inhSchema, partialResult);
+ }
+
+ /*
+ * put the inherited schema before our the schema for this table
+ */
+ schema = nconc(inhSchema, schema);
+
+ return (schema);
+}
+
+/*
+ * StoreCatalogInheritance --
+ * Updates the system catalogs with proper inheritance information.
+ */
+static void
+StoreCatalogInheritance(Oid relationId, List *supers)
+{
+ Relation relation;
+ TupleDesc desc;
+ int16 seqNumber;
+ List *entry;
+ List *idList;
+ HeapTuple tuple;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ AssertArg(OidIsValid(relationId));
+
+ if (supers==NIL)
+ return;
+
+ /* ----------------
+ * Catalog INHERITS information.
+ * ----------------
+ */
+ relation = heap_openr( InheritsRelationName );
+ desc = RelationGetTupleDescriptor(relation);
+
+ seqNumber = 1;
+ idList = NIL;
+ foreach (entry, supers) {
+ Datum datum[ Natts_pg_inherits ];
+ char nullarr[ Natts_pg_inherits ];
+
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(strVal(lfirst(entry))),
+ 0,0,0);
+ AssertArg(HeapTupleIsValid(tuple));
+
+ /*
+ * build idList for use below
+ */
+ idList = lappendi(idList, tuple->t_oid);
+
+ datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
+ datum[1] = ObjectIdGetDatum(tuple->t_oid); /* inhparent */
+ datum[2] = Int16GetDatum(seqNumber); /* inhseqno */
+
+ nullarr[0] = ' ';
+ nullarr[1] = ' ';
+ nullarr[2] = ' ';
+
+ tuple = heap_formtuple(desc,datum, nullarr);
+
+ (void) heap_insert(relation, tuple);
+ pfree(tuple);
+
+ seqNumber += 1;
+ }
+
+ heap_close(relation);
+
+ /* ----------------
+ * Catalog IPL information.
+ *
+ * Algorithm:
+ * 0. list superclasses (by Oid) in order given (see idList).
+ * 1. append after each relationId, its superclasses, recursively.
+ * 3. remove all but last of duplicates.
+ * 4. store result.
+ * ----------------
+ */
+
+ /* ----------------
+ * 1.
+ * ----------------
+ */
+ foreach (entry, idList) {
+ 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 = SearchSysCacheTuple(INHRELID,
+ ObjectIdGetDatum(id),
+ Int16GetDatum(number),
+ 0,0);
+
+ if (! HeapTupleIsValid(tuple))
+ break;
+
+ lnext(current) =
+ lconsi(((InheritsTupleForm)
+ GETSTRUCT(tuple))->inhparent,
+ NIL);
+
+ current = lnext(current);
+ }
+ lnext(current) = next;
+ }
+
+ /* ----------------
+ * 2.
+ * ----------------
+ */
+ foreach (entry, idList) {
+ Oid name;
+ List *rest;
+ bool found = false;
+
+ again:
+ name = lfirsti(entry);
+ foreach (rest, lnext(entry)) {
+ if (name == lfirsti(rest)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ /*
+ * entry list must be of length >= 2 or else no match
+ *
+ * so, remove this entry.
+ */
+ lfirst(entry) = lfirst(lnext(entry));
+ lnext(entry) = lnext(lnext(entry));
+
+ found = false;
+ goto again;
+ }
+ }
+
+ /* ----------------
+ * 3.
+ * ----------------
+ */
+ relation = heap_openr( InheritancePrecidenceListRelationName );
+ desc = RelationGetTupleDescriptor(relation);
+
+ seqNumber = 1;
+
+ foreach (entry, idList) {
+ Datum datum[ Natts_pg_ipl ];
+ char nullarr[ Natts_pg_ipl ];
+
+ datum[0] = ObjectIdGetDatum(relationId); /* iplrel */
+ datum[1] = ObjectIdGetDatum(lfirsti(entry));
+ /*iplinherits*/
+ datum[2] = Int16GetDatum(seqNumber); /* iplseqno */
+
+ nullarr[0] = ' ';
+ nullarr[1] = ' ';
+ nullarr[2] = ' ';
+
+ tuple = heap_formtuple( desc, datum, nullarr);
+
+ (void) heap_insert(relation, tuple);
+ pfree(tuple);
+
+ seqNumber += 1;
+ }
+
+ heap_close(relation);
+}
+
+/*
+ * returns 1 if attribute already exists in schema, 0 otherwise.
+ */
+static int
+checkAttrExists(char *attributeName, char *attributeType, List *schema)
+{
+ List *s;
+
+ foreach (s, schema) {
+ ColumnDef *def = lfirst(s);
+
+ if (!strcmp(attributeName, def->colname)) {
+ /*
+ * attribute exists. Make sure the types are the same.
+ */
+ if (strcmp(attributeType, def->typename->name) != 0) {
+ elog(WARN, "%s and %s conflict for %s",
+ attributeType, def->typename->name, attributeName);
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * MakeArchiveName
+ * make an archive rel name out of a regular rel name
+ *
+* the CALLER is responsible for freeing the memory allocated
+ */
+
+char*
+MakeArchiveName(Oid relationId)
+{
+ char *arch;
+
+ /*
+ * Archive relations are named a,XXXXX where XXXXX == the OID
+ * of the relation they archive. Create a string containing
+ * this name and find the reldesc for the archive relation.
+ */
+ arch = palloc(NAMEDATALEN);
+ sprintf(arch, "a,%d",relationId);
+
+ return arch;
+}
+
diff --git a/src/backend/commands/creatinh.h b/src/backend/commands/creatinh.h
new file mode 100644
index 00000000000..a86fd4ed82b
--- /dev/null
+++ b/src/backend/commands/creatinh.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * creatinh.h--
+ * prototypes for creatinh.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: creatinh.h,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CREATINH_H
+#define CREATINH_H
+
+extern void DefineRelation(CreateStmt *stmt);
+extern void RemoveRelation(char *name);
+extern char* MakeArchiveName(Oid relid);
+
+#endif /* CREATINH_H */
diff --git a/src/backend/commands/defind.c b/src/backend/commands/defind.c
new file mode 100644
index 00000000000..da797e23cbb
--- /dev/null
+++ b/src/backend/commands/defind.c
@@ -0,0 +1,505 @@
+/*-------------------------------------------------------------------------
+ *
+ * defind.c--
+ * POSTGRES define, extend and remove index code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/funcindex.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "catalog/index.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/relcache.h"
+#include "utils/lsyscache.h"
+
+#include "commands/defrem.h"
+#include "parser/parsetree.h" /* for getrelid() */
+
+#include "optimizer/prep.h"
+#include "optimizer/clauses.h"
+#include "storage/lmgr.h"
+
+#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL)
+
+/* non-export function prototypes */
+static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
+static void CheckPredExpr(Node *predicate, List *rangeTable,
+ Oid baseRelOid);
+static void
+CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
+static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP,
+ Oid *argTypes, Oid *opOidP, Oid relId);
+static void NormIndexAttrs(List *attList, AttrNumber *attNumP,
+ Oid *opOidP, Oid relId);
+
+/*
+ * DefineIndex --
+ * Creates a new index.
+ *
+ * 'attributeList' is a list of IndexElem specifying either a functional
+ * index or a list of attributes to index on.
+ * 'parameterList' is a list of ParamString specified in the with clause.
+ * 'predicate' is the qual specified in the where clause.
+ * 'rangetable' is for the predicate
+ *
+ * Exceptions:
+ * XXX
+ */
+void
+DefineIndex(char *heapRelationName,
+ char *indexRelationName,
+ char *accessMethodName,
+ List *attributeList,
+ List *parameterList,
+ Expr *predicate,
+ List *rangetable)
+{
+ Oid *classObjectId;
+ Oid accessMethodId;
+ Oid relationId;
+ int numberOfAttributes;
+ AttrNumber *attributeNumberA;
+ HeapTuple tuple;
+ uint16 parameterCount = 0;
+ Datum *parameterA = NULL;
+ FuncIndexInfo fInfo;
+ List *cnfPred = NULL;
+
+
+ /*
+ * Handle attributes
+ */
+ numberOfAttributes = length(attributeList);
+ if (numberOfAttributes <= 0) {
+ elog(WARN, "DefineIndex: must specify at least one attribute");
+ }
+
+ /*
+ * compute heap relation id
+ */
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(heapRelationName),
+ 0,0,0);
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN, "DefineIndex: %s relation not found",
+ heapRelationName);
+ }
+ relationId = tuple->t_oid;
+
+ /*
+ * compute access method id
+ */
+ tuple = SearchSysCacheTuple(AMNAME, PointerGetDatum(accessMethodName),
+ 0,0,0);
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN, "DefineIndex: %s access method not found",
+ accessMethodName);
+ }
+ accessMethodId = tuple->t_oid;
+
+
+ /*
+ * Handle parameters
+ * [param list is now different (NOT USED, really) - ay 10/94]
+ */
+
+
+ /*
+ * Convert the partial-index predicate from parsetree form to plan
+ * form, so it can be readily evaluated during index creation.
+ * Note: "predicate" comes in as a list containing (1) the predicate
+ * itself (a where_clause), and (2) a corresponding range table.
+ *
+ * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
+ */
+ if (predicate != NULL && rangetable != NIL) {
+ cnfPred = cnfify((Expr*)copyObject(predicate), true);
+ fix_opids(cnfPred);
+ CheckPredicate(cnfPred, rangetable, relationId);
+ }
+
+ if (IsFuncIndex(attributeList)) {
+ IndexElem *funcIndex= lfirst(attributeList);
+ int nargs;
+
+ nargs = length(funcIndex->args);
+ if (nargs > INDEX_MAX_KEYS) {
+ elog(WARN,
+ "Too many args to function, limit of %d",
+ INDEX_MAX_KEYS);
+ }
+
+ FIsetnArgs(&fInfo,nargs);
+
+ strcpy(FIgetname(&fInfo), funcIndex->name);
+
+ attributeNumberA =
+ (AttrNumber *)palloc(nargs * sizeof attributeNumberA[0]);
+
+ classObjectId = (Oid *)palloc(sizeof classObjectId[0]);
+
+
+ FuncIndexArgs(funcIndex, attributeNumberA,
+ &(FIgetArg(&fInfo, 0)),
+ classObjectId, relationId);
+
+ index_create(heapRelationName,
+ indexRelationName,
+ &fInfo, accessMethodId,
+ numberOfAttributes, attributeNumberA,
+ classObjectId, parameterCount, parameterA, (Node*)cnfPred);
+ }else {
+ attributeNumberA =
+ (AttrNumber *)palloc(numberOfAttributes *
+ sizeof attributeNumberA[0]);
+
+ classObjectId =
+ (Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]);
+
+ NormIndexAttrs(attributeList, attributeNumberA,
+ classObjectId, relationId);
+
+ index_create(heapRelationName, indexRelationName, NULL,
+ accessMethodId, numberOfAttributes, attributeNumberA,
+ classObjectId, parameterCount, parameterA, (Node*)cnfPred);
+ }
+}
+
+
+/*
+ * ExtendIndex --
+ * Extends a partial index.
+ *
+ * Exceptions:
+ * XXX
+ */
+void
+ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
+{
+ Oid *classObjectId;
+ Oid accessMethodId;
+ Oid indexId, relationId;
+ Oid indproc;
+ int numberOfAttributes;
+ AttrNumber *attributeNumberA;
+ HeapTuple tuple;
+ FuncIndexInfo fInfo;
+ FuncIndexInfo *funcInfo = NULL;
+ IndexTupleForm index;
+ Node *oldPred = NULL;
+ List *cnfPred = NULL;
+ PredInfo *predInfo;
+ Relation heapRelation;
+ Relation indexRelation;
+ int i;
+
+ /*
+ * compute index relation id and access method id
+ */
+ tuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(indexRelationName),
+ 0,0,0);
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN, "ExtendIndex: %s index not found",
+ indexRelationName);
+ }
+ indexId = tuple->t_oid;
+ accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
+
+ /*
+ * find pg_index tuple
+ */
+ tuple = SearchSysCacheTuple(INDEXRELID,
+ ObjectIdGetDatum(indexId),
+ 0,0,0);
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN, "ExtendIndex: %s is not an index",
+ indexRelationName);
+ }
+
+ /*
+ * Extract info from the pg_index tuple
+ */
+ index = (IndexTupleForm)GETSTRUCT(tuple);
+ Assert(index->indexrelid == indexId);
+ relationId = index->indrelid;
+ indproc = index->indproc;
+
+ for (i=0; i<INDEX_MAX_KEYS; i++)
+ if (index->indkey[i] == 0) break;
+ numberOfAttributes = i;
+
+ if (VARSIZE(&index->indpred) != 0) {
+ char *predString;
+
+ predString = fmgr(F_TEXTOUT, &index->indpred);
+ oldPred = stringToNode(predString);
+ pfree(predString);
+ }
+ if (oldPred == NULL)
+ elog(WARN, "ExtendIndex: %s is not a partial index",
+ indexRelationName);
+
+ /*
+ * Convert the extension predicate from parsetree form to plan
+ * form, so it can be readily evaluated during index creation.
+ * Note: "predicate" comes in as a list containing (1) the predicate
+ * itself (a where_clause), and (2) a corresponding range table.
+ */
+ if (rangetable != NIL) {
+ cnfPred = cnfify((Expr*)copyObject(predicate), true);
+ fix_opids(cnfPred);
+ CheckPredicate(cnfPred, rangetable, relationId);
+ }
+
+ /* make predInfo list to pass to index_build */
+ predInfo = (PredInfo*)palloc(sizeof(PredInfo));
+ predInfo->pred = (Node*)cnfPred;
+ predInfo->oldPred = oldPred;
+
+ attributeNumberA =
+ (AttrNumber *)palloc(numberOfAttributes*
+ sizeof attributeNumberA[0]);
+ classObjectId =
+ (Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]);
+
+
+ for (i=0; i<numberOfAttributes; i++) {
+ attributeNumberA[i] = index->indkey[i];
+ classObjectId[i] = index->indclass[i];
+ }
+
+ if (indproc != InvalidOid) {
+ funcInfo = &fInfo;
+/* FIgetnArgs(funcInfo) = numberOfAttributes; */
+ FIsetnArgs(funcInfo,numberOfAttributes);
+
+ tuple = SearchSysCacheTuple(PROOID,
+ ObjectIdGetDatum(indproc),
+ 0,0,0);
+ if (!HeapTupleIsValid(tuple))
+ elog(WARN, "ExtendIndex: index procedure not found");
+
+ namecpy(&(funcInfo->funcName),
+ &(((Form_pg_proc) GETSTRUCT(tuple))->proname));
+
+ FIsetProcOid(funcInfo,tuple->t_oid);
+ }
+
+ heapRelation = heap_open(relationId);
+ indexRelation = index_open(indexId);
+
+ RelationSetLockForWrite(heapRelation);
+
+ InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId);
+
+ index_build(heapRelation, indexRelation, numberOfAttributes,
+ attributeNumberA, 0, NULL, funcInfo, predInfo);
+}
+
+
+/*
+ * CheckPredicate
+ * Checks that the given list of partial-index predicates refer
+ * (via the given range table) only to the given base relation oid,
+ * and that they're in a form the planner can handle, i.e.,
+ * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
+ * has to be on the left).
+ */
+
+static void
+CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
+{
+ List *item;
+
+ foreach (item, predList) {
+ CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
+ }
+}
+
+static void
+CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
+{
+ List *clauses = NIL, *clause;
+
+ if (is_opclause(predicate)) {
+ CheckPredClause((Expr*)predicate, rangeTable, baseRelOid);
+ return;
+ } else if (or_clause(predicate))
+ clauses = ((Expr*)predicate)->args;
+ else if (and_clause(predicate))
+ clauses = ((Expr*)predicate)->args;
+ else
+ elog(WARN, "Unsupported partial-index predicate expression type");
+
+ foreach (clause, clauses) {
+ CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
+ }
+}
+
+static void
+CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
+{
+ Var *pred_var;
+ Const *pred_const;
+
+ pred_var = (Var *)get_leftop(predicate);
+ pred_const = (Const *)get_rightop(predicate);
+
+ if (!IsA(predicate->oper,Oper) ||
+ !IsA(pred_var,Var) ||
+ !IsA(pred_const,Const)) {
+ elog(WARN, "Unsupported partial-index predicate clause type");
+ }
+
+ if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
+ elog(WARN,
+ "Partial-index predicates may refer only to the base relation");
+}
+
+
+static void
+FuncIndexArgs(IndexElem *funcIndex,
+ AttrNumber *attNumP,
+ Oid *argTypes,
+ Oid *opOidP,
+ Oid relId)
+{
+ List *rest;
+ HeapTuple tuple;
+ AttributeTupleForm att;
+
+ tuple = SearchSysCacheTuple(CLANAME,
+ PointerGetDatum(funcIndex->class),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(tuple))
+ {
+ elog(WARN, "DefineIndex: %s class not found",
+ funcIndex->class);
+ }
+ *opOidP = tuple->t_oid;
+
+ memset(argTypes, 0, 8 * sizeof(Oid));
+
+ /*
+ * process the function arguments
+ */
+ for (rest=funcIndex->args; rest != NIL; rest = lnext(rest)) {
+ char *arg;
+
+ arg = strVal(lfirst(rest));
+
+ tuple = SearchSysCacheTuple(ATTNAME,
+ ObjectIdGetDatum(relId),
+ PointerGetDatum(arg),0,0);
+
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN,
+ "DefineIndex: attribute \"%s\" not found",
+ arg);
+ }
+ att = (AttributeTupleForm)GETSTRUCT(tuple);
+ *attNumP++ = att->attnum;
+ *argTypes++ = att->atttypid;
+ }
+}
+
+static void
+NormIndexAttrs(List *attList, /* list of IndexElem's */
+ AttrNumber *attNumP,
+ Oid *opOidP,
+ Oid relId)
+{
+ List *rest;
+ HeapTuple tuple;
+
+ /*
+ * process attributeList
+ */
+
+ for (rest=attList; rest != NIL; rest = lnext(rest)) {
+ IndexElem *attribute;
+
+ attribute = lfirst(rest);
+
+ if (attribute->class == NULL) {
+ elog(WARN,
+ "DefineIndex: default index class unsupported");
+ }
+
+ if (attribute->name == NULL)
+ elog(WARN, "missing attribute for define index");
+
+ tuple = SearchSysCacheTuple(ATTNAME,
+ ObjectIdGetDatum(relId),
+ PointerGetDatum(attribute->name),
+ 0,0);
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN,
+ "DefineIndex: attribute \"%s\" not found",
+ attribute->name);
+ }
+ *attNumP++ = ((AttributeTupleForm)GETSTRUCT(tuple))->attnum;
+
+ tuple = SearchSysCacheTuple(CLANAME,
+ PointerGetDatum(attribute->class),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN, "DefineIndex: %s class not found",
+ attribute->class);
+ }
+ *opOidP++ = tuple->t_oid;
+ }
+}
+
+/*
+ * RemoveIndex --
+ * Deletes an index.
+ *
+ * Exceptions:
+ * BadArg if name is invalid.
+ * "WARN" if index nonexistant.
+ * ...
+ */
+void
+RemoveIndex(char *name)
+{
+ HeapTuple tuple;
+
+ tuple = SearchSysCacheTuple(RELNAME,
+ PointerGetDatum(name),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(tuple)) {
+ elog(WARN, "index \"%s\" nonexistant", name);
+ }
+
+ if (((Form_pg_class)GETSTRUCT(tuple))->relkind != RELKIND_INDEX) {
+ elog(WARN, "relation \"%s\" is of type \"%c\"",
+ name,
+ ((Form_pg_class)GETSTRUCT(tuple))->relkind);
+ }
+
+ index_destroy(tuple->t_oid);
+}
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
new file mode 100644
index 00000000000..4ba38c793c7
--- /dev/null
+++ b/src/backend/commands/define.c
@@ -0,0 +1,564 @@
+/*-------------------------------------------------------------------------
+ *
+ * define.c--
+ * POSTGRES "define" utility code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.1.1.1 1996/07/09 06:21:20 scrappy 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.
+ *
+ * NOTES
+ * These things must be defined and committed in the following order:
+ * "define function":
+ * input/output, recv/send procedures
+ * "define type":
+ * type
+ * "define operator":
+ * operators
+ *
+ * Most of the parse-tree manipulation routines are defined in
+ * commands/manip.c.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "utils/tqual.h"
+#include "catalog/catname.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "fmgr.h" /* for fmgr */
+
+#include "utils/builtins.h" /* prototype for textin() */
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "commands/defrem.h"
+#include "optimizer/xfunc.h"
+#include "tcop/dest.h"
+
+static char *defGetString(DefElem *def);
+static int defGetTypeLength(DefElem *def);
+
+#define DEFAULT_TYPDELIM ','
+
+/*
+ * DefineFunction --
+ * Registers a new function.
+ *
+ */
+void
+DefineFunction(ProcedureStmt *stmt, CommandDest dest)
+{
+ List *parameters = stmt->withClause;
+ char *proname = stmt->funcname;
+ char* probin_str;
+ char* prosrc_str;
+ char *prorettype;
+ char *languageName;
+ bool canCache;
+ bool trusted = TRUE;
+ List *argList;
+ int32 byte_pct, perbyte_cpu, percall_cpu, outin_ratio;
+ bool returnsSet;
+ int i;
+
+ /* ----------------
+ * figure out the language and convert it to lowercase.
+ * ----------------
+ */
+ languageName = stmt->language;
+ for (i = 0; i < NAMEDATALEN && languageName[i]; ++i) {
+ languageName[i] = tolower(languageName[i]);
+ }
+
+ /* ----------------
+ * handle "returntype = X". The function could return a singleton
+ * value or a set of values. Figure out which.
+ * ----------------
+ */
+ if (nodeTag(stmt->returnType)==T_TypeName) {
+ TypeName *setType = (TypeName *)stmt->returnType;
+ /* a set of values */
+ prorettype = setType->name,
+ returnsSet = true;
+ }else {
+ /* singleton */
+ prorettype = strVal(stmt->returnType);
+ returnsSet = false;
+ }
+
+ /* Next attributes are only defined for C functions */
+ if ( strcmp(languageName, "c") == 0 ||
+ strcmp(languageName, "internal") == 0 ) {
+ List *pl;
+
+ /* the defaults */
+ canCache = FALSE;
+ byte_pct = BYTE_PCT;
+ perbyte_cpu = PERBYTE_CPU;
+ percall_cpu = PERCALL_CPU;
+ outin_ratio = OUTIN_RATIO;
+
+ foreach(pl, parameters) {
+ int count;
+ char *ptr;
+ ParamString *param = (ParamString*)lfirst(pl);
+
+ if (!strcasecmp(param->name, "isacachable")) {
+ /* ----------------
+ * handle "[ iscachable ]": figure out if Postquel functions
+ * are cacheable automagically?
+ * ----------------
+ */
+ canCache = TRUE;
+ }else if (!strcasecmp(param->name, "trusted")) {
+ /*
+ * we don't have untrusted functions any more. The 4.2
+ * implementation is lousy anyway so I took it out.
+ * -ay 10/94
+ */
+ elog(WARN, "untrusted function has been decommissioned.");
+ }else if (!strcasecmp(param->name, "byte_pct")) {
+ /*
+ ** handle expensive function parameters
+ */
+ byte_pct = atoi(param->val);
+ }else if (!strcasecmp(param->name, "perbyte_cpu")) {
+ if (!sscanf(param->val, "%d", &perbyte_cpu)) {
+ for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) {
+ if (*ptr == '!') {
+ count++;
+ }
+ }
+ perbyte_cpu = (int) pow(10.0, (double) count);
+ }
+ }else if (!strcasecmp(param->name, "percall_cpu")) {
+ if (!sscanf(param->val, "%d", &percall_cpu)) {
+ for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) {
+ if (*ptr == '!') {
+ count++;
+ }
+ }
+ percall_cpu = (int) pow(10.0, (double) count);
+ }
+ }else if (!strcasecmp(param->name, "outin_ratio")) {
+ outin_ratio = atoi(param->val);
+ }
+ }
+ } else if (!strcmp(languageName, "sql")) {
+ canCache = false;
+ trusted = true;
+
+ /* query optimizer groks sql, these are meaningless */
+ perbyte_cpu = percall_cpu = 0;
+ byte_pct = outin_ratio = 100;
+ } else {
+ elog(WARN, "DefineFunction: language '%s' is not supported",
+ languageName);
+ }
+
+ /* ----------------
+ * handle "[ arg is (...) ]"
+ * XXX fix optional arg handling below
+ * ----------------
+ */
+ argList = stmt->defArgs;
+
+ if ( strcmp(languageName, "c") == 0 ||
+ strcmp(languageName, "internal") == 0 ) {
+ prosrc_str = "-";
+ probin_str = stmt->as;
+ } else {
+ prosrc_str = stmt->as;
+ probin_str = "-";
+ }
+
+ /* C is stored uppercase in pg_language */
+ if (!strcmp(languageName, "c")) {
+ languageName[0] = 'C';
+ }
+
+ /* ----------------
+ * now have ProcedureDefine do all the work..
+ * ----------------
+ */
+ ProcedureCreate(proname,
+ returnsSet,
+ prorettype,
+ languageName,
+ prosrc_str, /* converted to text later */
+ probin_str, /* converted to text later */
+ canCache,
+ trusted,
+ byte_pct,
+ perbyte_cpu,
+ percall_cpu,
+ outin_ratio,
+ argList,
+ dest);
+
+}
+
+/* --------------------------------
+ * 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(char *oprName,
+ List *parameters)
+{
+ uint16 precedence=0; /* operator precedence */
+ bool canHash=false; /* operator hashes */
+ bool isLeftAssociative=true; /* operator is left associative */
+ char *functionName=NULL; /* function for operator */
+ char *typeName1=NULL; /* first type name */
+ char *typeName2=NULL; /* second type name */
+ 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;
+
+ /*
+ * loop over the definition list and extract the information we need.
+ */
+ foreach (pl, parameters) {
+ DefElem *defel = (DefElem *)lfirst(pl);
+
+ if (!strcasecmp(defel->defname, "leftarg")) {
+ /* see gram.y, must be setof */
+ if (nodeTag(defel->arg)==T_TypeName)
+ elog(WARN, "setof type not implemented for leftarg");
+
+ if (nodeTag(defel->arg)==T_String) {
+ typeName1 = defGetString(defel);
+ }else {
+ elog(WARN, "type for leftarg is malformed.");
+ }
+ } else if (!strcasecmp(defel->defname, "rightarg")) {
+ /* see gram.y, must be setof */
+ if (nodeTag(defel->arg)==T_TypeName)
+ elog(WARN, "setof type not implemented for rightarg");
+
+ if (nodeTag(defel->arg)==T_String) {
+ typeName2 = defGetString(defel);
+ }else {
+ elog(WARN, "type for rightarg is malformed.");
+ }
+ } else if (!strcasecmp(defel->defname, "procedure")) {
+ functionName = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "precedence")) {
+ /* NOT IMPLEMENTED (never worked in v4.2) */
+ elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
+ } else if (!strcasecmp(defel->defname, "associativity")) {
+ /* NOT IMPLEMENTED (never worked in v4.2) */
+ elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
+ } else if (!strcasecmp(defel->defname, "commutator")) {
+ commutatorName = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "negator")) {
+ negatorName = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "restrict")) {
+ restrictionName = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "join")) {
+ joinName = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "hashes")) {
+ canHash = TRUE;
+ } else if (!strcasecmp(defel->defname, "sort1")) {
+ /* ----------------
+ * XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... )
+ * XXX is undocumented in the reference manual source as of
+ * 89/8/22.
+ * ----------------
+ */
+ sortName1 = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "sort2")) {
+ sortName2 = defGetString(defel);
+ } else {
+ elog(NOTICE, "DefineOperator: attribute \"%s\" not recognized",
+ defel->defname);
+ }
+ }
+
+ /*
+ * make sure we have our required definitions
+ */
+ if (functionName==NULL) {
+ elog(WARN, "Define: \"procedure\" unspecified");
+ }
+
+ /* ----------------
+ * now have OperatorCreate do all the work..
+ * ----------------
+ */
+ OperatorCreate(oprName, /* operator name */
+ typeName1, /* first type name */
+ typeName2, /* second type name */
+ 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(char *aggName, List *parameters)
+
+{
+ char *stepfunc1Name = NULL;
+ char *stepfunc2Name = NULL;
+ char *finalfuncName = NULL;
+ char *baseType = NULL;
+ char *stepfunc1Type = NULL;
+ char *stepfunc2Type = NULL;
+ char *init1 = NULL;
+ char *init2 = NULL;
+ List *pl;
+
+ foreach (pl, parameters) {
+ DefElem *defel = (DefElem *)lfirst(pl);
+
+ /*
+ * sfunc1
+ */
+ if (!strcasecmp(defel->defname, "sfunc1")) {
+ stepfunc1Name = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "basetype")) {
+ baseType = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "stype1")) {
+ stepfunc1Type = defGetString(defel);
+
+ /*
+ * sfunc2
+ */
+ } else if (!strcasecmp(defel->defname, "sfunc2")) {
+ stepfunc2Name = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "stype2")) {
+ stepfunc2Type = defGetString(defel);
+ /*
+ * final
+ */
+ } else if (!strcasecmp(defel->defname, "finalfunc")) {
+ finalfuncName = defGetString(defel);
+ /*
+ * initial conditions
+ */
+ } else if (!strcasecmp(defel->defname, "initcond1")) {
+ init1 = defGetString(defel);
+ } else if (!strcasecmp(defel->defname, "initcond2")) {
+ init2 = defGetString(defel);
+ } else {
+ elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized",
+ defel->defname);
+ }
+ }
+
+ /*
+ * make sure we have our required definitions
+ */
+ if (baseType==NULL)
+ elog(WARN, "Define: \"basetype\" unspecified");
+ if (stepfunc1Name!=NULL) {
+ if (stepfunc1Type==NULL)
+ elog(WARN, "Define: \"stype1\" unspecified");
+ }
+ if (stepfunc2Name!=NULL) {
+ if (stepfunc2Type==NULL)
+ elog(WARN, "Define: \"stype2\" unspecified");
+ }
+
+ /*
+ * Most of the argument-checking is done inside of AggregateCreate
+ */
+ AggregateCreate(aggName, /* aggregate name */
+ stepfunc1Name, /* first step function name */
+ stepfunc2Name, /* second step function name */
+ finalfuncName, /* final function name */
+ baseType, /* type of object being aggregated */
+ stepfunc1Type, /* return type of first function */
+ stepfunc2Type, /* return type of second function */
+ init1, /* first initial condition */
+ init2); /* second initial condition */
+
+ /* XXX free palloc'd memory */
+}
+
+/*
+ * DefineType --
+ * Registers a new type.
+ *
+ */
+void
+DefineType(char *typeName, List *parameters)
+{
+ int16 internalLength= 0; /* int2 */
+ int16 externalLength= 0; /* int2 */
+ char *elemName = NULL;
+ char *inputName = NULL;
+ char *outputName = NULL;
+ char *sendName = NULL;
+ char *receiveName = NULL;
+ char *defaultValue = NULL; /* Datum */
+ bool byValue = false;
+ char delimiter = DEFAULT_TYPDELIM;
+ char *shadow_type;
+ List *pl;
+ char alignment = 'i'; /* default alignment */
+
+ /*
+ * Type names can only be 15 characters long, so that the shadow type
+ * can be created using the 16th character as necessary.
+ */
+ if (strlen(typeName) >= (NAMEDATALEN - 1)) {
+ elog(WARN, "DefineType: type names must be %d characters or less",
+ NAMEDATALEN - 1);
+ }
+
+ foreach(pl, parameters) {
+ DefElem *defel = (DefElem*)lfirst(pl);
+
+ if (!strcasecmp(defel->defname, "internallength")) {
+ internalLength = defGetTypeLength(defel);
+ }else if (!strcasecmp(defel->defname, "externallength")) {
+ externalLength = defGetTypeLength(defel);
+ }else if (!strcasecmp(defel->defname, "input")) {
+ inputName = defGetString(defel);
+ }else if (!strcasecmp(defel->defname, "output")) {
+ outputName = defGetString(defel);
+ }else if (!strcasecmp(defel->defname, "send")) {
+ sendName = defGetString(defel);
+ }else if (!strcasecmp(defel->defname, "delimiter")) {
+ char *p = defGetString(defel);
+ delimiter = p[0];
+ }else if (!strcasecmp(defel->defname, "receive")) {
+ receiveName = defGetString(defel);
+ }else if (!strcasecmp(defel->defname, "element")) {
+ elemName = defGetString(defel);
+ }else if (!strcasecmp(defel->defname, "default")) {
+ defaultValue = defGetString(defel);
+ }else if (!strcasecmp(defel->defname, "passedbyvalue")) {
+ byValue = true;
+ }else if (!strcasecmp(defel->defname, "alignment")) {
+ char *a = defGetString(defel);
+ if (!strcasecmp(a, "double")) {
+ alignment = 'd';
+ } else if (!strcasecmp(a, "int")) {
+ alignment = 'i';
+ } else {
+ elog(WARN, "DefineType: \"%s\" alignment not recognized",
+ a);
+ }
+ }else {
+ elog(NOTICE, "DefineType: attribute \"%s\" not recognized",
+ defel->defname);
+ }
+ }
+
+ /*
+ * make sure we have our required definitions
+ */
+ if (inputName==NULL)
+ elog(WARN, "Define: \"input\" unspecified");
+ if (outputName==NULL)
+ elog(WARN, "Define: \"output\" unspecified");
+
+ /* ----------------
+ * now have TypeCreate do all the real work.
+ * ----------------
+ */
+ (void) TypeCreate(typeName, /* type name */
+ InvalidOid, /* relation oid (n/a here) */
+ internalLength, /* internal size */
+ externalLength, /* external size */
+ 'b', /* type-type (base type) */
+ delimiter, /* array element delimiter */
+ inputName, /* input procedure */
+ outputName, /* output procedure */
+ sendName, /* send procedure */
+ receiveName, /* receive procedure */
+ elemName, /* element type name */
+ defaultValue, /* default type value */
+ byValue, /* passed by value */
+ alignment);
+
+ /* ----------------
+ * When we create a true type (as opposed to a complex type)
+ * we need to have an shadow array entry for it in pg_type as well.
+ * ----------------
+ */
+ shadow_type = makeArrayTypeName(typeName);
+
+ (void) TypeCreate(shadow_type, /* type name */
+ InvalidOid, /* relation oid (n/a here) */
+ -1, /* internal size */
+ -1, /* external size */
+ 'b', /* type-type (base type) */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ "array_in", /* input procedure */
+ "array_out", /* output procedure */
+ "array_out", /* send procedure */
+ "array_in", /* receive procedure */
+ typeName, /* element type name */
+ defaultValue, /* default type value */
+ false, /* never passed by value */
+ alignment);
+
+ pfree(shadow_type);
+}
+
+static char *
+defGetString(DefElem *def)
+{
+ if (nodeTag(def->arg)!=T_String)
+ elog(WARN, "Define: \"%s\" = what?", def->defname);
+ return (strVal(def->arg));
+}
+
+static int
+defGetTypeLength(DefElem *def)
+{
+ if (nodeTag(def->arg)==T_Integer)
+ return (intVal(def->arg));
+ else if (nodeTag(def->arg)==T_String &&
+ !strcasecmp(strVal(def->arg),"variable"))
+ return -1; /* variable length */
+
+ elog(WARN, "Define: \"%s\" = what?", def->defname);
+ return -1;
+}
diff --git a/src/backend/commands/defrem.h b/src/backend/commands/defrem.h
new file mode 100644
index 00000000000..3658dc50085
--- /dev/null
+++ b/src/backend/commands/defrem.h
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * defrem.h--
+ * POSTGRES define and remove utility definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: defrem.h,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DEFREM_H
+#define DEFREM_H
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "tcop/dest.h"
+
+/*
+ * prototypes in defind.c
+ */
+extern void DefineIndex(char *heapRelationName,
+ char *indexRelationName,
+ char *accessMethodName,
+ List *attributeList,
+ List *parameterList, Expr *predicate,
+ List *rangetable);
+extern void ExtendIndex(char *indexRelationName,
+ Expr *predicate,
+ List *rangetable);
+extern void RemoveIndex(char *name);
+
+/*
+ * prototypes in define.c
+ */
+extern void DefineFunction(ProcedureStmt *nameargsexe, CommandDest dest);
+extern void DefineOperator(char *name, List *parameters);
+extern void DefineAggregate(char *name, List *parameters);
+extern void DefineType(char *name, List *parameters);
+
+/*
+ * prototypes in remove.c
+ */
+extern void RemoveFunction(char *functionName, int nargs, List *argNameList);
+extern void RemoveOperator(char *operatorName,
+ char *typeName1, char *typeName2);
+extern void RemoveType(char *typeName);
+extern void RemoveAggregate(char *aggName);
+
+#endif /* DEFREM_H */
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
new file mode 100644
index 00000000000..a37f0f9cf4b
--- /dev/null
+++ b/src/backend/commands/explain.c
@@ -0,0 +1,219 @@
+/*-------------------------------------------------------------------------
+ *
+ * explain.c--
+ * Explain the query execution plan
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h" /* for MakeTimeRange() */
+#include "nodes/plannodes.h"
+#include "tcop/tcopprot.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "lib/stringinfo.h"
+#include "commands/explain.h"
+#include "optimizer/planner.h"
+#include "access/xact.h"
+
+typedef struct ExplainState {
+ /* options */
+ int printCost; /* print cost */
+ int printNodes; /* do nodeToString() instead */
+ /* other states */
+ List *rtable; /* range table */
+} ExplainState;
+
+static char *Explain_PlanToString(Plan *plan, ExplainState *es);
+
+/*
+ * ExplainQuery -
+ * print out the execution plan for a given query
+ *
+ */
+void
+ExplainQuery(Query *query, List *options, CommandDest dest)
+{
+ char *s;
+ Plan *plan;
+ ExplainState *es;
+ int len;
+
+ if (IsAbortedTransactionBlockState()) {
+ char *tag = "*ABORT STATE*";
+ EndCommand(tag, dest);
+
+ elog(NOTICE, "(transaction aborted): %s",
+ "queries ignored until END");
+
+ return;
+ }
+
+ /* plan the queries (XXX we've ignored rewrite!!) */
+ plan = planner(query);
+
+ /* pg_plan could have failed */
+ if (plan == NULL)
+ return;
+
+ es = (ExplainState*)malloc(sizeof(ExplainState));
+ memset(es, 0, sizeof(ExplainState));
+
+ /* parse options */
+ while (options) {
+ char *ostr = strVal(lfirst(options));
+ if (!strcasecmp(ostr, "cost"))
+ es->printCost = 1;
+ else if (!strcasecmp(ostr, "full_plan"))
+ es->printNodes = 1;
+
+ options = lnext(options);
+ }
+ es->rtable = query->rtable;
+
+ if (es->printNodes) {
+ s = nodeToString(plan);
+ } else {
+ s = Explain_PlanToString(plan, es);
+ }
+
+ /* output the plan */
+ len = strlen(s);
+ elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN-64, s);
+ len -= ELOG_MAXLEN-64;
+ while (len > 0) {
+ s += ELOG_MAXLEN-64;
+ elog(NOTICE, "%.*s", ELOG_MAXLEN-64, s);
+ len -= ELOG_MAXLEN-64;
+ }
+ free(es);
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * explain_outNode -
+ * converts a Node into ascii string and append it to 'str'
+ */
+static void
+explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
+{
+ char *pname;
+ char buf[1000];
+ int i;
+
+ if (plan==NULL) {
+ appendStringInfo(str, "\n");
+ return;
+ }
+
+ switch(nodeTag(plan)) {
+ case T_Result:
+ pname = "Result";
+ break;
+ case T_Append:
+ pname = "Append";
+ break;
+ case T_NestLoop:
+ pname = "Nested Loop";
+ break;
+ case T_MergeJoin:
+ pname = "Merge Join";
+ break;
+ case T_HashJoin:
+ pname = "Hash Join";
+ break;
+ case T_SeqScan:
+ pname = "Seq Scan";
+ break;
+ case T_IndexScan:
+ pname = "Index Scan";
+ break;
+ case T_Temp:
+ pname = "Temp Scan";
+ break;
+ case T_Sort:
+ pname = "Sort";
+ break;
+ case T_Agg:
+ pname = "Aggregate";
+ break;
+ case T_Unique:
+ pname = "Unique";
+ break;
+ case T_Hash:
+ pname = "Hash";
+ break;
+ case T_Tee:
+ pname = "Tee";
+ break;
+ default:
+ break;
+ }
+
+ for(i=0; i < indent; i++)
+ appendStringInfo(str, " ");
+
+ appendStringInfo(str, pname);
+ switch(nodeTag(plan)) {
+ case T_SeqScan:
+ case T_IndexScan:
+ if (((Scan*)plan)->scanrelid > 0) {
+ RangeTblEntry *rte = nth(((Scan*)plan)->scanrelid-1, es->rtable);
+ sprintf(buf, " on %.*s", NAMEDATALEN, rte->refname);
+ appendStringInfo(str, buf);
+ }
+ break;
+ default:
+ break;
+ }
+ if (es->printCost) {
+ sprintf(buf, " (cost=%.2f size=%d width=%d)",
+ plan->cost, plan->plan_size, plan->plan_width);
+ appendStringInfo(str, buf);
+ }
+ appendStringInfo(str, "\n");
+
+ /* lefttree */
+ if (outerPlan(plan)) {
+ for(i=0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " -> ");
+ explain_outNode(str, outerPlan(plan), indent+1, es);
+ }
+
+ /* righttree */
+ if (innerPlan(plan)) {
+ for(i=0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " -> ");
+ explain_outNode(str, innerPlan(plan), indent+1, es);
+ }
+ return;
+}
+
+static char *
+Explain_PlanToString(Plan *plan, ExplainState *es)
+{
+ StringInfo str;
+ char *s;
+
+ if (plan==NULL)
+ return "";
+ Assert(plan!=NULL);
+ str = makeStringInfo();
+ explain_outNode(str, plan, 0, es);
+ s = str->data;
+ pfree(str);
+
+ return s;
+}
diff --git a/src/backend/commands/explain.h b/src/backend/commands/explain.h
new file mode 100644
index 00000000000..e0848bb7711
--- /dev/null
+++ b/src/backend/commands/explain.h
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * explain.h--
+ * prototypes for explain.c
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ * $Id: explain.h,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXPLAIN_H
+#define EXPLAIN_H
+
+extern void ExplainQuery(Query *query, List *options, CommandDest dest);
+
+#endif /* EXPLAIN_H*/
diff --git a/src/backend/commands/purge.c b/src/backend/commands/purge.c
new file mode 100644
index 00000000000..b8b8317ab96
--- /dev/null
+++ b/src/backend/commands/purge.c
@@ -0,0 +1,168 @@
+/*-------------------------------------------------------------------------
+ *
+ * purge.c--
+ * the POSTGRES purge command.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $
+ *
+ * Note:
+ * XXX There are many instances of int32 instead of ...Time. These
+ * should be changed once it is decided the signed'ness will be.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "utils/tqual.h" /* for NowTimeQual */
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/nabstime.h"
+
+#include "catalog/pg_class.h"
+#include "commands/purge.h"
+#include "utils/builtins.h" /* for isreltime() */
+
+static char cmdname[] = "RelationPurge";
+
+#define RELATIVE 01
+#define ABSOLUTE 02
+
+int32
+RelationPurge(char *relationName,
+ char *absoluteTimeString,
+ char *relativeTimeString)
+{
+ register i;
+ AbsoluteTime absoluteTime = INVALID_ABSTIME;
+ RelativeTime relativeTime = INVALID_RELTIME;
+ bits8 dateTag;
+ Relation relation;
+ HeapScanDesc scan;
+ static ScanKeyData key[1] = {
+ { 0, Anum_pg_class_relname, F_NAMEEQ }
+ };
+ Buffer buffer;
+ HeapTuple newTuple, oldTuple;
+ AbsoluteTime currentTime;
+ char *values[Natts_pg_class];
+ char nulls[Natts_pg_class];
+ char replace[Natts_pg_class];
+ Relation idescs[Num_pg_class_indices];
+
+ /*
+ * XXX for some reason getmyrelids (in inval.c) barfs when
+ * you heap_replace tuples from these classes. i thought
+ * setheapoverride would fix it but it didn't. for now,
+ * just disallow purge on these classes.
+ */
+ if (strcmp(RelationRelationName, relationName) == 0 ||
+ strcmp(AttributeRelationName, relationName) == 0 ||
+ strcmp(AccessMethodRelationName, relationName) == 0 ||
+ strcmp(AccessMethodOperatorRelationName, relationName) == 0) {
+ elog(WARN, "%s: cannot purge catalog \"%s\"",
+ cmdname, relationName);
+ }
+
+ if (PointerIsValid(absoluteTimeString)) {
+ absoluteTime = (int32) nabstimein(absoluteTimeString);
+ absoluteTimeString[0] = '\0';
+ if (absoluteTime == INVALID_ABSTIME) {
+ elog(NOTICE, "%s: bad absolute time string \"%s\"",
+ cmdname, absoluteTimeString);
+ elog(WARN, "purge not executed");
+ }
+ }
+
+#ifdef PURGEDEBUG
+ elog(DEBUG, "%s: absolute time `%s' is %d.",
+ cmdname, absoluteTimeString, absoluteTime);
+#endif /* defined(PURGEDEBUG) */
+
+ if (PointerIsValid(relativeTimeString)) {
+ if (isreltime(relativeTimeString, NULL, NULL, NULL) != 1) {
+ elog(WARN, "%s: bad relative time string \"%s\"",
+ cmdname, relativeTimeString);
+ }
+ relativeTime = reltimein(relativeTimeString);
+
+#ifdef PURGEDEBUG
+ elog(DEBUG, "%s: relative time `%s' is %d.",
+ cmdname, relativeTimeString, relativeTime);
+#endif /* defined(PURGEDEBUG) */
+ }
+
+ /*
+ * Find the RELATION relation tuple for the given relation.
+ */
+ relation = heap_openr(RelationRelationName);
+ key[0].sk_argument = PointerGetDatum(relationName);
+ fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
+
+ scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+ oldTuple = heap_getnext(scan, 0, &buffer);
+ if (!HeapTupleIsValid(oldTuple)) {
+ heap_endscan(scan);
+ heap_close(relation);
+ elog(WARN, "%s: no such relation: %s", cmdname, relationName);
+ return(0);
+ }
+
+ /*
+ * Dig around in the tuple.
+ */
+ currentTime = GetCurrentTransactionStartTime();
+ if (!RelativeTimeIsValid(relativeTime)) {
+ dateTag = ABSOLUTE;
+ if (!AbsoluteTimeIsValid(absoluteTime))
+ absoluteTime = currentTime;
+ } else if (!AbsoluteTimeIsValid(absoluteTime))
+ dateTag = RELATIVE;
+ else
+ dateTag = ABSOLUTE | RELATIVE;
+
+ for (i = 0; i < Natts_pg_class; ++i) {
+ nulls[i] = heap_attisnull(oldTuple, i+1) ? 'n' : ' ';
+ values[i] = NULL;
+ replace[i] = ' ';
+ }
+ if (dateTag & ABSOLUTE) {
+ values[Anum_pg_class_relexpires-1] =
+ (char *) UInt32GetDatum(absoluteTime);
+ replace[Anum_pg_class_relexpires-1] = 'r';
+ }
+ if (dateTag & RELATIVE) {
+ values[Anum_pg_class_relpreserved-1] =
+ (char *) UInt32GetDatum(relativeTime);
+ replace[Anum_pg_class_relpreserved-1] = 'r';
+ }
+
+ /*
+ * Change the RELATION relation tuple for the given relation.
+ */
+ newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum*)values,
+ nulls, replace);
+
+ /* XXX How do you detect an insertion error?? */
+ (void) heap_replace(relation, &newTuple->t_ctid, newTuple);
+
+ /* keep the system catalog indices current */
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+ CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple);
+ CatalogCloseIndices(Num_pg_class_indices, idescs);
+
+ pfree(newTuple);
+
+ heap_endscan(scan);
+ heap_close(relation);
+ return(1);
+}
+
diff --git a/src/backend/commands/purge.h b/src/backend/commands/purge.h
new file mode 100644
index 00000000000..20174182880
--- /dev/null
+++ b/src/backend/commands/purge.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * purge.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: purge.h,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PURGE_H
+#define PURGE_H
+
+extern int32 RelationPurge(char *relationName,
+ char *absoluteTimeString,
+ char *relativeTimeString);
+
+#endif /* PURGE_H */
diff --git a/src/backend/commands/recipe.c b/src/backend/commands/recipe.c
new file mode 100644
index 00000000000..97d0df6d379
--- /dev/null
+++ b/src/backend/commands/recipe.c
@@ -0,0 +1,1181 @@
+/*-------------------------------------------------------------------------
+ *
+ * recipe.c--
+ * routines for handling execution of Tioga recipes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include "include/postgres.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/makefuncs.h"
+#include "catalog/pg_type.h"
+#include "commands/recipe.h"
+#include "libpq/libpq-be.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/geo-decls.h"
+#include "utils/relcache.h" /* for RelationNameGetRelation*/
+#include "parser/parse_query.h"
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
+#include "tcop/pquery.h"
+#include "tcop/dest.h"
+#include "optimizer/planner.h"
+#include "executor/executor.h"
+
+/* from tcop/postgres.c */
+extern CommandDest whereToSendOutput;
+
+#ifndef TIOGA
+
+void beginRecipe(RecipeStmt *stmt) {
+ elog(NOTICE,"You must compile with TIOGA defined in order to use recipes\n");
+}
+#else
+
+#include "tioga/tgRecipe.h"
+
+#define DEBUG_RECIPE 1
+
+/* structure to keep track of the tee node plans */
+typedef struct _teePlanInfo {
+ char* tpi_relName;
+ Query* tpi_parsetree;
+ Plan* tpi_plan;
+} TeePlanInfo;
+
+typedef struct _teeInfo {
+ int num;
+ TeePlanInfo *val;
+} TeeInfo;
+
+QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2);
+void OffsetVarAttno(Node* node, int varno, int offset);
+
+static void appendTeeQuery(TeeInfo *teeInfo,
+ QueryTreeList *q,
+ char* teeNodeName);
+
+static Plan* replaceTeeScans(Plan* plan,
+ Query* parsetree,
+ TeeInfo *teeInfo);
+static void replaceSeqScan(Plan* plan,
+ Plan* parent,
+ int rt_ind,
+ Plan* tplan);
+
+static void tg_rewriteQuery(TgRecipe* r, TgNode *n,
+ QueryTreeList *q,
+ QueryTreeList *inputQlist);
+static Node *tg_replaceNumberedParam(Node* expression,
+ int pnum,
+ int rt_ind,
+ char *teeRelName);
+static Node *tg_rewriteParamsInExpr(Node *expression,
+ QueryTreeList *inputQlist);
+static QueryTreeList *tg_parseSubQuery(TgRecipe* r,
+ TgNode* n,
+ TeeInfo* teeInfo);
+static QueryTreeList* tg_parseTeeNode(TgRecipe *r,
+ TgNode *n,
+ int i,
+ QueryTreeList *qList,
+ TeeInfo* teeInfo);
+
+
+/*
+ The Tioga recipe rewrite algorithm:
+
+ To parse a Tioga recipe, we start from an eye node and go backwards through
+ its input nodes. To rewrite a Tioga node, we do the following:
+
+ 1) parse the node we're at in the standard way (calling parser() )
+ 2) rewrite its input nodes recursively using Tioga rewrite
+ 3) now, with the rewritten input parse trees and the original parse tree
+ of the node, we rewrite the the node.
+ To do the rewrite, we use the target lists, range tables, and
+ qualifications of the input parse trees
+*/
+
+/*
+ * beginRecipe:
+ * this is the main function to recipe execution
+ * this function is invoked for EXECUTE RECIPE ... statements
+ *
+ * takes in a RecipeStmt structure from the parser
+ * and returns a list of cursor names
+ */
+
+void
+beginRecipe(RecipeStmt* stmt)
+{
+ TgRecipe* r;
+ int i;
+ QueryTreeList *qList;
+ char portalName[1024];
+
+ Plan *plan;
+ TupleDesc attinfo;
+ QueryDesc *queryDesc;
+ Query *parsetree;
+
+ int numTees;
+ TeeInfo* teeInfo;
+
+ /* retrieveRecipe() reads the recipe from the database
+ and returns a TgRecipe* structure we can work with */
+
+ r = retrieveRecipe(stmt->recipeName);
+
+ if (r == NULL) return;
+
+ /* find the number of tees in the recipe */
+ numTees = r->tees->num;
+
+ if (numTees > 0) {
+ /* allocate a teePlan structure */
+ teeInfo = (TeeInfo*)malloc(sizeof(TeeInfo));
+ teeInfo->num = numTees;
+ teeInfo->val = (TeePlanInfo*)malloc(numTees * sizeof(TeePlanInfo));
+ for (i=0;i<numTees;i++) {
+ teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName;
+ teeInfo->val[i].tpi_parsetree = NULL;
+ teeInfo->val[i].tpi_plan = NULL;
+ }
+ } else
+ teeInfo = NULL;
+
+ /*
+ * for each viewer in the recipe, go backwards from each viewer input
+ * and generate a plan. Attach the plan to cursors.
+ **/
+ for (i=0;i<r->eyes->num;i++) {
+ TgNodePtr e;
+
+ e = r->eyes->val[i];
+ if (e->inNodes->num > 1) {
+ elog(NOTICE,
+ "beginRecipe: Currently eyes cannot have more than one input");
+ }
+ if (e->inNodes->num == 0) {
+ /* no input to this eye, skip it */
+ continue;
+ }
+
+#ifdef DEBUG_RECIPE
+ elog(NOTICE,"beginRecipe: eyes[%d] = %s\n", i, e->nodeName);
+#endif /* DEBUG_RECIPE */
+
+ qList = tg_parseSubQuery(r,e->inNodes->val[0], teeInfo);
+
+ if (qList == NULL) {
+ /* eye is directly connected to a tee node */
+ /* XXX TODO: handle this case */
+ }
+
+ /* now, plan the queries */
+ /* should really do everything pg_plan() does, but for now,
+ we skip the rule rewrite and time qual stuff */
+
+ /* ----------------------------------------------------------
+ * 1) plan the main query, everything from an eye node back to
+ a Tee
+ * ---------------------------------------------------------- */
+ parsetree = qList->qtrees[0];
+
+ /* before we plan, we want to see all the changes
+ we did, during the rewrite phase, such as
+ creating the tee tables,
+ setheapoverride() allows us to see the changes */
+ setheapoverride(true);
+ plan = planner(parsetree);
+
+ /* ----------------------------------------------------------
+ * 2) plan the tee queries, (subgraphs rooted from a Tee)
+ by the time the eye is processed, all tees that contribute
+ to that eye will have been included in the teeInfo list
+ * ---------------------------------------------------------- */
+ if (teeInfo) {
+ int t;
+ Plan* tplan;
+ Tee* newplan;
+
+ for (t=0; t<teeInfo->num;t++) {
+ if (teeInfo->val[t].tpi_plan == NULL) {
+ /* plan it in the usual fashion */
+ tplan = planner(teeInfo->val[t].tpi_parsetree);
+
+ /* now add a tee node to the root of the plan */
+elog(NOTICE, "adding tee plan node to the root of the %s\n",
+ teeInfo->val[t].tpi_relName);
+ newplan = (Tee*)makeNode(Tee);
+ newplan->plan.targetlist = tplan->targetlist;
+ newplan->plan.qual = NULL; /* tplan->qual; */
+ newplan->plan.lefttree = tplan;
+ newplan->plan.righttree = NULL;
+ newplan->leftParent = NULL;
+ newplan->rightParent = NULL;
+ /* the range table of the tee is the range table
+ of the tplan */
+ newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable;
+ strcpy(newplan->teeTableName,
+ teeInfo->val[t].tpi_relName);
+ teeInfo->val[t].tpi_plan = (Plan*)newplan;
+ }
+ }
+
+ /* ----------------------------------------------------------
+ * 3) replace the tee table scans in the main plan with
+ actual tee plannodes
+ * ---------------------------------------------------------- */
+
+ plan = replaceTeeScans(plan, parsetree, teeInfo);
+
+ } /* if (teeInfo) */
+
+ setheapoverride(false);
+
+ /* define a portal for this viewer input */
+ /* for now, eyes can only have one input */
+ sprintf(portalName, "%s%d",e->nodeName,0);
+
+ queryDesc = CreateQueryDesc(parsetree,
+ plan,
+ whereToSendOutput);
+ /* ----------------
+ * call ExecStart to prepare the plan for execution
+ * ----------------
+ */
+ attinfo = ExecutorStart(queryDesc,NULL);
+
+ ProcessPortal(portalName,
+ parsetree,
+ plan,
+ attinfo,
+ whereToSendOutput);
+elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName);
+ }
+
+}
+
+
+
+/*
+ * tg_rewriteQuery -
+ * r - the recipe being rewritten
+ * n - the node that we're current at
+ * q - a QueryTree List containing the parse tree of the node
+ * inputQlist - the parsetrees of its input nodes,
+ * the size of inputQlist must be the same as the
+ * number of input nodes. Some elements in the inpuQlist
+ * may be null if the inputs to those nodes are unconnected
+ *
+ * this is the main routine for rewriting the recipe queries
+ * the original query tree 'q' is modified
+ */
+
+static void
+tg_rewriteQuery(TgRecipe* r,
+ TgNode *n,
+ QueryTreeList *q,
+ QueryTreeList *inputQlist)
+{
+ Query* orig;
+ Query* inputQ;
+ int i;
+ List *rtable;
+ List *input_rtable;
+ int rt_length;
+
+ /* orig is the original parse tree of the node */
+ orig = q->qtrees[0];
+
+
+ /*-------------------------------------------------------------------
+ step 1:
+
+ form a combined range table from all the range tables in the original
+ query as well as the input nodes
+
+ form a combined qualification from the qual in the original plus
+ the quals of the input nodes
+ -------------------------------------------------------------------
+ */
+
+ /* start with the original range table */
+ rtable = orig->rtable;
+ rt_length = length(rtable);
+
+ for (i=0;i<n->inNodes->num;i++) {
+ if (n->inNodes->val[i] != NULL &&
+ n->inNodes->val[i]->nodeType != TG_TEE_NODE) {
+ inputQ = inputQlist->qtrees[i];
+ input_rtable = inputQ->rtable;
+
+ /* need to offset the var nodes in the qual and targetlist
+ because they are indexed off the original rtable */
+ OffsetVarNodes((Node*)inputQ->qual, rt_length);
+ OffsetVarNodes((Node*)inputQ->targetList, rt_length);
+
+ /* append the range tables from the children nodes */
+ rtable = nconc (rtable, input_rtable);
+
+ /* append the qualifications of the child node into the
+ original qual list */
+ AddQual(orig, inputQ->qual);
+ }
+ }
+ orig->rtable = rtable;
+
+ /* step 2:
+ rewrite the target list of the original parse tree
+ if there are any references to params, replace them with
+ the appropriate target list entry of the children node
+ */
+ if (orig->targetList != NIL) {
+ List *tl;
+ TargetEntry *tle;
+
+ foreach (tl, orig->targetList) {
+ tle = lfirst(tl);
+ if (tle->resdom != NULL) {
+ tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist);
+ }
+ }
+ }
+
+ /* step 3:
+ rewrite the qual of the original parse tree
+ if there are any references to params, replace them with
+ the appropriate target list entry of the children node
+ */
+ if (orig->qual) {
+ if (nodeTag(orig->qual) == T_List) {
+ elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???");
+ }
+ orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist);
+ }
+
+ /* at this point, we're done with the rewrite, the querytreelist q
+ has been modified */
+
+}
+
+
+/* tg_replaceNumberedParam:
+
+ this procedure replaces the specified numbered param with a
+ reference to a range table
+
+ this procedure recursively calls itself
+
+ it returns a (possibly modified) Node*.
+
+*/
+static Node*
+tg_replaceNumberedParam(Node *expression,
+ int pnum, /* the number of the parameter */
+ int rt_ind, /* the range table index */
+ char *teeRelName) /* the relname of the tee table */
+{
+ TargetEntry *param_tle;
+ Param* p;
+ Var *newVar,*oldVar;
+
+ if (expression == NULL) return NULL;
+
+ switch (nodeTag(expression)) {
+ case T_Param:
+ {
+ /* the node is a parameter,
+ substitute the entry from the target list of the child that
+ corresponds to the parameter number*/
+ p = (Param*)expression;
+
+ /* we only deal with the case of numbered parameters */
+ if (p->paramkind == PARAM_NUM && p->paramid == pnum) {
+
+ if (p->param_tlist) {
+ /* we have a parameter with an attribute like $N.foo
+ so replace it with a new var node */
+
+ /* param tlist can only have one entry in them! */
+ param_tle = (TargetEntry*)(lfirst(p->param_tlist));
+ oldVar = (Var*)param_tle->expr;
+ oldVar->varno = rt_ind;
+ oldVar->varnoold = rt_ind;
+ return (Node*)oldVar;
+ } else {
+ /* we have $N without the .foo */
+ bool defined;
+ bool isRel;
+ /* TODO here, we need to check to see whether the type of the
+ tee is a complex type (relation) or a simple type */
+ /* if it is a simple type, then we need to get the "result"
+ attribute from the tee relation */
+
+ isRel = (typeid_get_relid(p->paramtype) != 0);
+ if (isRel) {
+ newVar = makeVar(rt_ind,
+ 0, /* the whole tuple */
+ TypeGet(teeRelName,&defined),
+ rt_ind,
+ 0);
+ return (Node*)newVar;
+ } else
+ newVar = makeVar(rt_ind,
+ 1, /* just the first field, which is 'result' */
+ TypeGet(teeRelName,&defined),
+ rt_ind,
+ 0);
+ return (Node*)newVar;
+
+ }
+ }
+ else {
+ elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind);
+ }
+ }
+ break;
+ case T_Expr:
+ {
+ /* the node is an expression, we need to recursively
+ call ourselves until we find parameter nodes */
+ List *l;
+ Expr *expr = (Expr*)expression;
+ List *newArgs;
+
+ /* we have to make a new args lists because Params
+ can be replaced by Var nodes in tg_replaceNumberedParam()*/
+ newArgs = NIL;
+
+ /* we only care about argument to expressions,
+ it doesn't matter when the opType is */
+ /* recursively rewrite the arguments of this expression */
+ foreach (l, expr->args) {
+ newArgs = lappend(newArgs,
+ tg_replaceNumberedParam(lfirst(l),
+ pnum,
+ rt_ind,
+ teeRelName));
+ }
+ /* change the arguments of the expression */
+ expr->args = newArgs;
+ }
+ break;
+ default:
+ {
+ /* ignore other expr types */
+ }
+ }
+
+ return expression;
+}
+
+
+
+
+
+/* tg_rewriteParamsInExpr:
+
+ rewrite the params in expressions by using the targetlist entries
+ from the input parsetrees
+
+ this procedure recursively calls itself
+
+ it returns a (possibly modified) Node*.
+
+*/
+static Node*
+tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist)
+{
+ List *tl;
+ TargetEntry *param_tle, *tle;
+ Param* p;
+ int childno;
+ char *resname;
+
+ if (expression == NULL) return NULL;
+
+ switch (nodeTag(expression)) {
+ case T_Param:
+ {
+ /* the node is a parameter,
+ substitute the entry from the target list of the child that
+ corresponds to the parameter number*/
+ p = (Param*)expression;
+
+ /* we only deal with the case of numbered parameters */
+ if (p->paramkind == PARAM_NUM) {
+ /* paramid's start from 1*/
+ childno = p->paramid - 1;
+
+ if (p->param_tlist) {
+ /* we have a parameter with an attribute like $N.foo
+ so match the resname "foo" against the target list
+ of the (N-1)th inputQlist */
+
+ /* param tlist can only have one entry in them! */
+ param_tle = (TargetEntry*)(lfirst(p->param_tlist));
+ resname = param_tle->resdom->resname;
+
+ if (inputQlist->qtrees[childno]) {
+ foreach (tl, inputQlist->qtrees[childno]->targetList) {
+ tle = lfirst(tl);
+ if (strcmp(resname, tle->resdom->resname) == 0) {
+ return tle->expr;
+ }
+ }
+ }
+ else {
+ elog(WARN,"tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid);
+ }
+
+ } else {
+ /* we have $N without the .foo */
+ /* use the first resdom in the targetlist of the */
+ /* appropriate child query */
+ tl = inputQlist->qtrees[childno]->targetList;
+ tle = lfirst(tl);
+ return tle->expr;
+ }
+ }
+ else {
+ elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind);
+ }
+ }
+ break;
+ case T_Expr:
+ {
+ /* the node is an expression, we need to recursively
+ call ourselves until we find parameter nodes */
+ List *l;
+ Expr *expr = (Expr*)expression;
+ List *newArgs;
+
+ /* we have to make a new args lists because Params
+ can be replaced by Var nodes in tg_rewriteParamsInExpr()*/
+ newArgs = NIL;
+
+ /* we only care about argument to expressions,
+ it doesn't matter when the opType is */
+ /* recursively rewrite the arguments of this expression */
+ foreach (l, expr->args) {
+ newArgs = lappend(newArgs,
+ tg_rewriteParamsInExpr(lfirst(l), inputQlist));
+ }
+ /* change the arguments of the expression */
+ expr->args = newArgs;
+ }
+ break;
+ default:
+ {
+ /* ignore other expr types */
+ }
+ }
+
+ return expression;
+}
+
+
+
+/*
+ getParamTypes:
+ given an element, finds its parameter types.
+ the typev array argument is set to the parameter types.
+ the parameterCount is returned
+
+ this code is very similar to ProcedureDefine() in pg_proc.c
+*/
+static int
+getParamTypes (TgElement *elem, Oid typev[])
+{
+ /* this code is similar to ProcedureDefine() */
+ int16 parameterCount;
+ bool defined;
+ Oid toid;
+ char *t;
+ int i,j;
+
+ parameterCount = 0;
+ for (i=0;i<8;i++) {
+ typev[i] = 0;
+ }
+ for (j=0;j<elem->inTypes->num;j++) {
+ if (parameterCount == 8) {
+ elog(WARN,
+ "getParamTypes: Ingredients cannot take > 8 arguments");
+ }
+ t = elem->inTypes->val[j];
+ if (strcmp(t,"opaque") == 0) {
+ elog(WARN,
+ "getParamTypes: Ingredient functions cannot take type 'opaque'");
+ } else {
+ toid = TypeGet(elem->inTypes->val[j], &defined);
+ if (!OidIsValid(toid)) {
+ elog(WARN, "getParamTypes: arg type '%s' is not defined",t);
+ }
+ if (!defined) {
+ elog(NOTICE, "getParamTypes: arg type '%s' is only a shell",t);
+ }
+ }
+ typev[parameterCount++] = toid;
+ }
+
+ return parameterCount;
+}
+
+
+/*
+ * tg_parseTeeNode
+ *
+ * handles the parsing of the tee node
+ *
+ *
+ */
+
+static QueryTreeList*
+tg_parseTeeNode(TgRecipe *r,
+ TgNode *n, /* the tee node */
+ int i, /* which input this node is to its parent */
+ QueryTreeList *qList,
+ TeeInfo* teeInfo)
+
+{
+ QueryTreeList *q;
+ char* tt;
+ int rt_ind;
+ Query* orig;
+
+ /* the input Node is a tee node, so we need to do the following:
+ * we need to parse the child of the tee node,
+ we add that to our query tree list
+ * we need the name of the tee node table
+ the tee node table is the table into which the tee node
+ may materialize results. Call it TT
+ * we add a range table to our existing query with TT in it
+ * we need to replace the parameter $i with TT
+ (otherwise the optimizer won't know to use the table
+ on expression containining $i)
+ After that rewrite, the optimizer will generate
+ sequential scans of TT
+
+ Later, in the glue phase, we replace all instances of TT
+ sequential scans with the actual Tee node
+ */
+ q = tg_parseSubQuery(r,n, teeInfo);
+
+ /* tt is the name of the tee node table */
+ tt = n->nodeName;
+
+ if (q)
+ appendTeeQuery(teeInfo,q,tt);
+
+ orig = qList->qtrees[0];
+ rt_ind = RangeTablePosn(orig->rtable,tt);
+ /* check to see that this table is not part of
+ the range table already. This usually only
+ happens if multiple inputs are connected to the
+ same Tee. */
+ if (rt_ind == 0) {
+ orig->rtable = lappend(orig->rtable,
+ makeRangeTableEntry(tt,
+ FALSE,
+ NULL,
+ tt));
+ rt_ind = length(orig->rtable);
+ }
+
+ orig->qual = tg_replaceNumberedParam(orig->qual,
+ i+1, /* params start at 1*/
+ rt_ind,
+ tt);
+ return qList;
+}
+
+
+/*
+ * tg_parseSubQuery:
+ * go backwards from a node and parse the query
+ *
+ * the result parse tree is passed back
+ *
+ * could return NULL if trying to parse a teeNode
+ * that's already been processed by another parent
+ *
+ */
+
+static QueryTreeList*
+tg_parseSubQuery(TgRecipe* r, TgNode* n, TeeInfo* teeInfo)
+{
+ TgElement *elem;
+ char* funcName;
+ Oid typev[8]; /* eight arguments maximum */
+ int i;
+ int parameterCount;
+
+ QueryTreeList *qList; /* the parse tree of the nodeElement */
+ QueryTreeList *inputQlist; /* the list of parse trees for the
+ inputs to this node */
+ QueryTreeList *q;
+ Oid relid;
+ TgNode* child;
+ Relation rel;
+ unsigned int len;
+ TupleDesc tupdesc;
+
+ qList = NULL;
+
+ if (n->nodeType == TG_INGRED_NODE) {
+ /* parse each ingredient node in turn */
+
+ elem = n->nodeElem;
+ switch (elem->srcLang) {
+ case TG_SQL:
+ {
+ /* for SQL ingredients, the SQL query is contained in the
+ 'src' field */
+
+#ifdef DEBUG_RECIPE
+elog(NOTICE,"calling parser with %s",elem->src);
+#endif /* DEBUG_RECIPE */
+
+ parameterCount = getParamTypes(elem,typev);
+
+ qList = parser(elem->src,typev,parameterCount);
+
+ if (qList->len > 1) {
+ elog(NOTICE,
+ "tg_parseSubQuery: parser produced > 1 query tree");
+ }
+ }
+ break;
+ case TG_C:
+ {
+ /* C ingredients are registered functions in postgres */
+ /* we create a new query string by using the function name
+ (found in the 'src' field) and adding parameters to it
+ so if the function was FOOBAR and took in two arguments,
+ we would create a string
+ select FOOBAR($1,$2)
+ */
+ char newquery[1000];
+
+ funcName = elem->src;
+ parameterCount = getParamTypes(elem,typev);
+
+ if (parameterCount > 0) {
+ int i;
+ sprintf(newquery,"select %s($1",funcName);
+ for (i=1;i<parameterCount;i++) {
+ sprintf(newquery,"%s,$%d",newquery,i);
+ }
+ sprintf(newquery,"%s)",newquery);
+ } else
+ sprintf(newquery,"select %s()",funcName);
+
+#ifdef DEBUG_RECIPE
+elog(NOTICE,"calling parser with %s", newquery);
+#endif /* DEBUG_RECIPE */
+
+ qList = parser(newquery,typev,parameterCount);
+ if (qList->len > 1) {
+ elog(NOTICE,
+ "tg_parseSubQuery: parser produced > 1 query tree");
+ }
+ }
+ break;
+ case TG_RECIPE_GRAPH:
+ elog(NOTICE,"tg_parseSubQuery: can't parse recipe graph ingredients yet!");
+ break;
+ case TG_COMPILED:
+ elog(NOTICE,"tg_parseSubQuery: can't parse compiled ingredients yet!");
+ break;
+ default:
+ elog(NOTICE,"tg_parseSubQuery: unknown srcLang: %d",elem->srcLang);
+ }
+
+ /* parse each of the subrecipes that are input to this node*/
+
+ if (n->inNodes->num > 0) {
+ inputQlist = malloc(sizeof(QueryTreeList));
+ inputQlist->len = n->inNodes->num + 1 ;
+ inputQlist->qtrees = (Query**)malloc(inputQlist->len * sizeof(Query*));
+ for (i=0;i<n->inNodes->num;i++) {
+
+ inputQlist->qtrees[i] = NULL;
+ if (n->inNodes->val[i]) {
+ if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) {
+ qList = tg_parseTeeNode(r,n->inNodes->val[i],
+ i,qList,teeInfo);
+ }
+ else
+ { /* input node is not a Tee */
+ q = tg_parseSubQuery(r,n->inNodes->val[i],
+ teeInfo);
+ Assert (q->len == 1);
+ inputQlist->qtrees[i] = q->qtrees[0];
+ }
+ }
+ }
+
+ /* now, we have all the query trees from our input nodes */
+ /* transform the original parse tree appropriately */
+ tg_rewriteQuery(r,n,qList,inputQlist);
+ }
+ }
+ else if (n->nodeType == TG_EYE_NODE) {
+ /* if we hit an eye, we need to stop and make what we have
+ into a subrecipe query block*/
+ elog(NOTICE,"tg_parseSubQuery: can't handle eye nodes yet");
+ }
+ else if (n->nodeType == TG_TEE_NODE) {
+ /* if we hit a tee, check to see if the parsing has been done
+ for this tee already by the other parent */
+
+ rel = RelationNameGetRelation(n->nodeName);
+ if (RelationIsValid(rel)) {
+ /* this tee has already been visited,
+ no need to do any further processing */
+ return NULL;
+ } else {
+ /* we need to process the child of the tee first, */
+ child = n->inNodes->val[0];
+
+ if (child->nodeType == TG_TEE_NODE) {
+ /* nested Tee nodes */
+ qList = tg_parseTeeNode(r,child,0,qList,teeInfo);
+ return qList;
+ }
+
+ Assert (child != NULL);
+
+ /* parse the input node */
+ q = tg_parseSubQuery(r,child, teeInfo);
+ Assert (q->len == 1);
+
+ /* add the parsed query to the main list of queries */
+ qList = appendQlist(qList,q);
+
+ /* need to create the tee table here */
+ /* the tee table created is used both for materializing the values
+ at the tee node, and for parsing and optimization.
+ The optimization needs to have a real table before it will
+ consider scans on it */
+
+ /* first, find the type of the tuples being produced by the
+ tee. The type is the same as the output type of
+ the child node.
+
+ NOTE: we are assuming that the child node only has a single
+ output here! */
+ getParamTypes(child->nodeElem,typev);
+
+ /* the output type is either a complex type,
+ (and is thus a relation) or is a simple type */
+
+ rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]);
+
+ if (RelationIsValid(rel)) {
+ /* for complex types, create new relation with the same
+ tuple descriptor as the output table type*/
+ len = length(q->qtrees[0]->targetList);
+ tupdesc = rel->rd_att;
+
+ relid = heap_create(child->nodeElem->outTypes->val[0],
+ NULL, /* XXX */
+ 'n',
+ DEFAULT_SMGR,
+ tupdesc);
+ }
+ else {
+ /* we have to create a relation with one attribute of
+ the simple base type. That attribute will have
+ an attr name of "result" */
+ /*NOTE: ignore array types for the time being */
+
+ len = 1;
+ tupdesc = CreateTemplateTupleDesc(len);
+
+ if ( !TupleDescInitEntry(tupdesc,1,
+ "result",
+ NULL,
+ 0, false)) {
+ elog(NOTICE,"tg_parseSubQuery: unexpected result from TupleDescInitEntry");
+ } else {
+ relid = heap_create(child->nodeElem->outTypes->val[0],
+ NULL, /* XXX */
+ 'n',
+ DEFAULT_SMGR,
+ tupdesc);
+ }
+ }
+ }
+ }
+ else if (n->nodeType == TG_RECIPE_NODE) {
+ elog(NOTICE,"tg_parseSubQuery: can't handle embedded recipes yet!");
+ } else
+ elog (NOTICE, "unknown nodeType: %d", n->nodeType);
+
+ return qList;
+}
+
+/*
+ * OffsetVarAttno -
+ * recursively find all the var nodes with the specified varno
+ * and offset their varattno with the offset
+ *
+ * code is similar to OffsetVarNodes in rewriteManip.c
+ */
+
+void
+OffsetVarAttno(Node* node, int varno, int offset)
+{
+ if (node == NULL) return;
+ switch (nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+ OffsetVarAttno(tle->expr, varno, offset);
+ }
+ break;
+ case T_Expr:
+ {
+ Expr *expr = (Expr*)node;
+ OffsetVarAttno((Node*)expr->args, varno, offset);
+ }
+ break;
+ case T_Var:
+ {
+ Var *var = (Var*)node;
+ if (var->varno == varno)
+ var->varattno += offset;
+ }
+ break;
+ case T_List:
+ {
+ List *l;
+
+ foreach(l, (List*)node) {
+ OffsetVarAttno(lfirst(l), varno, offset);
+ }
+ }
+ break;
+ default:
+ /* ignore the others */
+ break;
+ }
+}
+
+/*
+ * appendQlist
+ * add the contents of a QueryTreeList q2 to the end of the QueryTreeList
+ * q1
+ *
+ * returns a new querytree list
+ */
+
+QueryTreeList*
+appendQlist(QueryTreeList *q1, QueryTreeList *q2)
+{
+ QueryTreeList* newq;
+ int i,j;
+ int newlen;
+
+ if (q1 == NULL)
+ return q2;
+
+ if (q2 == NULL)
+ return q1;
+
+ newlen = q1->len + q2->len;
+ newq = (QueryTreeList*)malloc(sizeof(QueryTreeList));
+ newq->len = newlen;
+ newq->qtrees = (Query**)malloc(newlen * sizeof(Query*));
+ for (i=0;i<q1->len;i++)
+ newq->qtrees[i] = q1->qtrees[i];
+ for (j=0;j<q2->len;j++) {
+ newq->qtrees[i + j] = q2->qtrees[j];
+ }
+ return newq;
+}
+
+/*
+ * appendTeeQuery
+ *
+ * modify the query field of the teeInfo list of the particular tee node
+ */
+static void
+appendTeeQuery(TeeInfo *teeInfo, QueryTreeList *q, char* teeNodeName)
+{
+ int i;
+
+ Assert(teeInfo);
+
+ for (i=0;i<teeInfo->num;i++) {
+ if ( strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) {
+
+ Assert(q->len == 1);
+ teeInfo->val[i].tpi_parsetree = q->qtrees[0];
+ return;
+ }
+ }
+ elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo");
+}
+
+
+
+/*
+ * replaceSeqScan
+ * replaces sequential scans of a specified relation with the tee plan
+ * the relation is specified by its index in the range table, rt_ind
+ *
+ * returns the modified plan
+ * the offset_attno is the offset that needs to be added to the parent's
+ * qual or targetlist because the child plan has been replaced with a tee node
+ */
+static void
+replaceSeqScan(Plan* plan, Plan* parent,
+ int rt_ind, Plan* tplan)
+{
+ Scan* snode;
+ Tee* teePlan;
+ Result* newPlan;
+
+ if (plan == NULL) {
+ return;
+ }
+
+ if (plan->type == T_SeqScan) {
+ snode = (Scan*)plan;
+ if (snode->scanrelid == rt_ind) {
+ /* found the sequential scan that should be replaced
+ with the tplan. */
+ /* we replace the plan, but we also need to modify its parent*/
+
+ /* replace the sequential scan with a Result node
+ the reason we use a result node is so that we get the proper
+ projection behavior. The Result node is simply (ab)used as
+ a projection node */
+
+ newPlan = makeNode(Result);
+ newPlan->plan.cost = 0.0;
+ newPlan->plan.state = (EState*)NULL;
+ newPlan->plan.targetlist = plan->targetlist;
+ newPlan->plan.lefttree = tplan;
+ newPlan->plan.righttree = NULL;
+ newPlan->resconstantqual = NULL;
+ newPlan->resstate = NULL;
+
+ /* change all the varno's to 1*/
+ ChangeVarNodes((Node*)newPlan->plan.targetlist,
+ snode->scanrelid, 1);
+
+ if (parent) {
+ teePlan = (Tee*)tplan;
+
+ if (parent->lefttree == plan)
+ parent->lefttree = (Plan*)newPlan;
+ else
+ parent->righttree = (Plan*)newPlan;
+
+
+ if (teePlan->leftParent == NULL)
+ teePlan->leftParent = (Plan*)newPlan;
+ else
+ teePlan->rightParent = (Plan*)newPlan;
+
+/* comment for now to test out executor-stuff
+ if (parent->state) {
+ ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
+ }
+*/
+ }
+ }
+
+ } else {
+ if (plan->lefttree) {
+ replaceSeqScan(plan->lefttree, plan, rt_ind, tplan);
+ }
+ if (plan->righttree) {
+ replaceSeqScan(plan->righttree, plan, rt_ind, tplan);
+ }
+ }
+}
+
+/*
+ * replaceTeeScans
+ * places the sequential scans of the Tee table with
+ * a connection to the actual tee plan node
+ */
+static Plan*
+replaceTeeScans(Plan* plan, Query* parsetree, TeeInfo *teeInfo)
+{
+
+ int i;
+ List* rtable;
+ RangeTblEntry *rte;
+ char prefix[5];
+ int rt_ind;
+ Plan* tplan;
+
+ rtable = parsetree->rtable;
+ if (rtable == NULL)
+ return plan;
+
+ /* look through the range table for the tee relation entry,
+ that will give use the varno we need to detect which
+ sequential scans need to be replaced with tee nodes*/
+
+ rt_ind = 0;
+ while (rtable != NIL) {
+ rte = lfirst(rtable);
+ rtable = lnext(rtable);
+ rt_ind++; /* range table references in varno fields start w/ 1 */
+
+ /* look for the "tee_" prefix in the refname,
+ also check to see that the relname and the refname are the same
+ this should eliminate any user-specified table and leave
+ us with the tee table entries only*/
+ if ((strlen(rte->refname) < 4) ||
+ (strcmp (rte->relname, rte->refname) != 0))
+ continue;
+ strncpy(prefix,rte->refname,4);
+ prefix[4] = '\0';
+ if (strcmp(prefix,"tee_") == 0) {
+ /* okay, we found a tee node entry in the range table */
+
+ /* find the appropriate plan in the teeInfo list */
+ tplan = NULL;
+ for (i=0;i<teeInfo->num;i++) {
+ if (strcmp(teeInfo->val[i].tpi_relName,
+ rte->refname) == 0) {
+ tplan = teeInfo->val[i].tpi_plan;
+ }
+ }
+ if (tplan == NULL) {
+ elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); }
+
+ /* replace the sequential scan node with that var number
+ with the tee plan node */
+ replaceSeqScan(plan, NULL, rt_ind, tplan);
+ }
+ }
+
+ return plan;
+}
+
+
+
+#endif /* TIOGA */
diff --git a/src/backend/commands/recipe.h b/src/backend/commands/recipe.h
new file mode 100644
index 00000000000..62fcc314a34
--- /dev/null
+++ b/src/backend/commands/recipe.h
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * recipe.h--
+ * recipe handling routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: recipe.h,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RECIPE_H
+#define RECIPE_H
+
+extern void beginRecipe(RecipeStmt* stmt);
+
+#endif /* RECIPE_H */
diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c
new file mode 100644
index 00000000000..95830c6cc08
--- /dev/null
+++ b/src/backend/commands/remove.c
@@ -0,0 +1,435 @@
+/*-------------------------------------------------------------------------
+ *
+ * remove.c--
+ * POSTGRES remove (function | type | operator ) utilty code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "c.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h" /* for NowTimeQual */
+#include "catalog/catname.h"
+#include "commands/defrem.h"
+#include "utils/elog.h"
+
+#include "miscadmin.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 "utils/syscache.h"
+#include "parser/catalog_utils.h"
+#include "storage/bufmgr.h"
+#include "fmgr.h"
+
+/*
+ * RemoveOperator --
+ * Deletes an operator.
+ *
+ * Exceptions:
+ * BadArg if name is invalid.
+ * BadArg if type1 is invalid.
+ * "WARN" if operator nonexistant.
+ * ...
+ */
+void
+RemoveOperator(char *operatorName, /* operator name */
+ char *typeName1, /* first type name */
+ char *typeName2) /* optional second type name */
+{
+ Relation relation;
+ HeapScanDesc scan;
+ HeapTuple tup;
+ Oid typeId1 = InvalidOid;
+ Oid typeId2 = InvalidOid;
+ bool defined;
+ ItemPointerData itemPointerData;
+ Buffer buffer;
+ ScanKeyData operatorKey[3];
+ char *userName;
+
+ if (typeName1) {
+ typeId1 = TypeGet(typeName1, &defined);
+ if (!OidIsValid(typeId1)) {
+ elog(WARN, "RemoveOperator: type '%s' does not exist", typeName1);
+ return;
+ }
+ }
+
+ if (typeName2) {
+ typeId2 = TypeGet(typeName2, &defined);
+ if (!OidIsValid(typeId2)) {
+ elog(WARN, "RemoveOperator: type '%s' does not exist", typeName2);
+ return;
+ }
+ }
+
+ ScanKeyEntryInitialize(&operatorKey[0], 0x0,
+ Anum_pg_operator_oprname,
+ NameEqualRegProcedure,
+ PointerGetDatum(operatorName));
+
+ ScanKeyEntryInitialize(&operatorKey[1], 0x0,
+ Anum_pg_operator_oprleft,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(typeId1));
+
+ ScanKeyEntryInitialize(&operatorKey[2], 0x0,
+ Anum_pg_operator_oprright,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(typeId2));
+
+ relation = heap_openr(OperatorRelationName);
+ scan = heap_beginscan(relation, 0, NowTimeQual, 3, operatorKey);
+ tup = heap_getnext(scan, 0, &buffer);
+ if (HeapTupleIsValid(tup)) {
+#ifndef NO_SECURITY
+ userName = GetPgUserName();
+ if (!pg_ownercheck(userName,
+ (char *) ObjectIdGetDatum(tup->t_oid),
+ OPROID))
+ elog(WARN, "RemoveOperator: operator '%s': permission denied",
+ operatorName);
+#endif
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ heap_delete(relation, &itemPointerData);
+ } else {
+ if (OidIsValid(typeId1) && OidIsValid(typeId2)) {
+ elog(WARN, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist",
+ operatorName,
+ typeName1,
+ typeName2);
+ } else if (OidIsValid(typeId1)) {
+ elog(WARN, "RemoveOperator: right unary operator '%s' taking '%s' does not exist",
+ operatorName,
+ typeName1);
+ } else {
+ elog(WARN, "RemoveOperator: left unary operator '%s' taking '%s' does not exist",
+ operatorName,
+ typeName2);
+ }
+ }
+ heap_endscan(scan);
+ heap_close(relation);
+}
+
+#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 rdesc;
+ ScanKeyData key[3];
+ HeapScanDesc sdesc;
+ HeapTuple tup;
+ ItemPointerData itemPointerData;
+ Buffer buffer;
+ static attnums[3] = { 7, 8, 9 }; /* left, right, return */
+ register i;
+
+ ScanKeyEntryInitialize(&key[0],
+ 0, 0, ObjectIdEqualRegProcedure, (Datum)typeOid);
+ rdesc = heap_openr(OperatorRelationName);
+ for (i = 0; i < 3; ++i) {
+ key[0].sk_attno = attnums[i];
+ sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
+ while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) {
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ /* XXX LOCK not being passed */
+ heap_delete(rdesc, &itemPointerData);
+ }
+ heap_endscan(sdesc);
+ }
+ heap_close(rdesc);
+}
+
+/*
+ * 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 rdesc;
+ ScanKeyData key[1];
+ HeapScanDesc sdesc;
+ HeapTuple tup;
+ ItemPointerData itemPointerData;
+ Buffer buffer;
+
+ /*
+ * 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, ObjectIdEqualRegProcedure, (Datum)typeOid);
+
+ oidptr = (struct oidlist *) palloc(sizeof(*oidptr));
+ oidptr->next = NULL;
+ optr = oidptr;
+ rdesc = heap_openr(AttributeRelationName);
+ sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
+ while (PointerIsValid(tup = heap_getnext(sdesc, 0, &buffer))) {
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ optr->reloid = ((AttributeTupleForm)GETSTRUCT(tup))->attrelid;
+ optr->next = (struct oidlist *) palloc(sizeof(*oidptr));
+ optr = optr->next;
+ }
+ optr->next = NULL;
+ heap_endscan(sdesc);
+ heap_close(rdesc);
+
+
+ ScanKeyEntryInitialize(&key[0], 0,
+ ObjectIdAttributeNumber,
+ ObjectIdEqualRegProcedure, (Datum)0);
+ optr = oidptr;
+ rdesc = heap_openr(RelationRelationName);
+ while (PointerIsValid((char *) optr->next)) {
+ key[0].sk_argument = (Datum) (optr++)->reloid;
+ sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
+ tup = heap_getnext(sdesc, 0, &buffer);
+ if (PointerIsValid(tup)) {
+ char *name;
+
+ name = (((Form_pg_class)GETSTRUCT(tup))->relname).data;
+ heap_destroy(name);
+ }
+ }
+ heap_endscan(sdesc);
+ heap_close(rdesc);
+}
+#endif /* NOTYET */
+
+/*
+ * TypeRemove
+ * Removes the type 'typeName' and all attributes and relations that
+ * use it.
+ */
+void
+RemoveType(char *typeName) /* type name to be removed */
+{
+ Relation relation;
+ HeapScanDesc scan;
+ HeapTuple tup;
+ Oid typeOid;
+ ItemPointerData itemPointerData;
+ static ScanKeyData typeKey[1] = {
+ { 0, Anum_pg_type_typname, NameEqualRegProcedure }
+ };
+ char *shadow_type;
+ char *userName;
+
+#ifndef NO_SECURITY
+ userName = GetPgUserName();
+ if (!pg_ownercheck(userName, typeName, TYPNAME))
+ elog(WARN, "RemoveType: type '%s': permission denied",
+ typeName);
+#endif
+
+ relation = heap_openr(TypeRelationName);
+ fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func,
+ &typeKey[0].sk_nargs);
+
+ /* Delete the primary type */
+
+ typeKey[0].sk_argument = PointerGetDatum(typeName);
+
+ scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey);
+ tup = heap_getnext(scan, 0, (Buffer *) 0);
+ if (!HeapTupleIsValid(tup)) {
+ heap_endscan(scan);
+ heap_close(relation);
+ elog(WARN, "RemoveType: type '%s' does not exist",
+ typeName);
+ }
+ typeOid = tup->t_oid;
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ heap_delete(relation, &itemPointerData);
+ heap_endscan(scan);
+
+ /* Now, Delete the "array of" that type */
+ shadow_type = makeArrayTypeName(typeName);
+ typeKey[0].sk_argument = NameGetDatum(shadow_type);
+
+ scan = heap_beginscan(relation, 0, NowTimeQual,
+ 1, (ScanKey) typeKey);
+ tup = heap_getnext(scan, 0, (Buffer *) 0);
+
+ if (!HeapTupleIsValid(tup))
+ {
+ elog(WARN, "RemoveType: type '%s': array stub not found",
+ typeName);
+ }
+ typeOid = tup->t_oid;
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ heap_delete(relation, &itemPointerData);
+ heap_endscan(scan);
+
+ heap_close(relation);
+}
+
+/*
+ * RemoveFunction --
+ * Deletes a function.
+ *
+ * Exceptions:
+ * BadArg if name is invalid.
+ * "WARN" if function nonexistant.
+ * ...
+ */
+void
+RemoveFunction(char *functionName, /* function name to be removed */
+ int nargs,
+ List *argNameList /* list of TypeNames */)
+{
+ Relation relation;
+ HeapScanDesc scan;
+ HeapTuple tup;
+ Buffer buffer = InvalidBuffer;
+ bool bufferUsed = FALSE;
+ Oid argList[8];
+ Form_pg_proc the_proc;
+ ItemPointerData itemPointerData;
+ static ScanKeyData key[3] = {
+ { 0, Anum_pg_proc_proname, NameEqualRegProcedure }
+ };
+ char *userName;
+ char *typename;
+ int i;
+
+ memset(argList, 0, 8 * sizeof(Oid));
+ for (i=0; i<nargs; i++) {
+/* typename = ((TypeName*)(lfirst(argNameList)))->name; */
+ typename = strVal(lfirst(argNameList));
+ argNameList = lnext(argNameList);
+
+ if (strcmp(typename, "opaque") == 0)
+ argList[i] = 0;
+ else {
+ tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typename),
+ 0,0,0);
+
+ if (!HeapTupleIsValid(tup)) {
+ elog(WARN, "RemoveFunction: type '%s' not found",typename);
+ }
+ argList[i] = tup->t_oid;
+ }
+ }
+
+ tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName),
+ Int32GetDatum(nargs),
+ PointerGetDatum(argList),0);
+ if (!HeapTupleIsValid(tup))
+ func_error("RemoveFunction", functionName, nargs, (int*)argList);
+
+#ifndef NO_SECURITY
+ userName = GetPgUserName();
+ if (!pg_func_ownercheck(userName, functionName, nargs, argList)) {
+ elog(WARN, "RemoveFunction: function '%s': permission denied",
+ functionName);
+ }
+#endif
+
+ key[0].sk_argument = PointerGetDatum(functionName);
+
+ fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
+
+ relation = heap_openr(ProcedureRelationName);
+ scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+
+ do { /* hope this is ok because it's indexed */
+ if (bufferUsed) {
+ ReleaseBuffer(buffer);
+ bufferUsed = FALSE;
+ }
+ tup = heap_getnext(scan, 0, (Buffer *) &buffer);
+ if (!HeapTupleIsValid(tup))
+ break;
+ bufferUsed = TRUE;
+ the_proc = (Form_pg_proc) GETSTRUCT(tup);
+ } while ( (namestrcmp(&(the_proc->proname), functionName) == 0) &&
+ (the_proc->pronargs != nargs ||
+ !oid8eq(&(the_proc->proargtypes[0]), &argList[0])));
+
+
+ if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname),
+ functionName) != 0)
+ {
+ heap_endscan(scan);
+ heap_close(relation);
+ func_error("RemoveFunction", functionName,nargs, (int*)argList);
+ }
+
+ /* ok, function has been found */
+
+ if (the_proc->prolang == INTERNALlanguageId)
+ elog(WARN, "RemoveFunction: function \"%-.*s\" is built-in",
+ NAMEDATALEN, functionName);
+
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ heap_delete(relation, &itemPointerData);
+ heap_endscan(scan);
+ heap_close(relation);
+}
+
+void
+RemoveAggregate(char *aggName)
+{
+ Relation relation;
+ HeapScanDesc scan;
+ HeapTuple tup;
+ ItemPointerData itemPointerData;
+ static ScanKeyData key[3] = {
+ { 0, Anum_pg_aggregate_aggname, NameEqualRegProcedure }
+ };
+
+ key[0].sk_argument = PointerGetDatum(aggName);
+
+ fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
+ relation = heap_openr(AggregateRelationName);
+ scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+ tup = heap_getnext(scan, 0, (Buffer *) 0);
+ if (!HeapTupleIsValid(tup)) {
+ heap_endscan(scan);
+ heap_close(relation);
+ elog(WARN, "RemoveAggregate: aggregate '%s' does not exist",
+ aggName);
+ }
+ ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+ heap_delete(relation, &itemPointerData);
+ heap_endscan(scan);
+ heap_close(relation);
+}
diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c
new file mode 100644
index 00000000000..83dc8944eac
--- /dev/null
+++ b/src/backend/commands/rename.c
@@ -0,0 +1,275 @@
+/*-------------------------------------------------------------------------
+ *
+ * rename.c--
+ * renameatt() and renamerel() reside here.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/indexing.h"
+#include "catalog/catalog.h"
+
+#include "commands/copy.h"
+
+#include "executor/execdefs.h" /* for EXEC_{FOR,BACK,FDEBUG,BDEBUG} */
+
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/prep.h" /* for find_all_inheritors */
+
+#ifndef NO_SECURITY
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif /* !NO_SECURITY */
+
+/*
+ * 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 reldesc 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
+ *
+ * XXX Renaming an indexed attribute must (eventually) also change
+ * the attribute name in the associated indexes.
+ */
+void
+renameatt(char *relname,
+ char *oldattname,
+ char *newattname,
+ char *userName,
+ int recurse)
+{
+ Relation relrdesc, attrdesc;
+ HeapTuple reltup, oldatttup, newatttup;
+ ItemPointerData oldTID;
+ Relation idescs[Num_pg_attr_indices];
+
+ /*
+ * 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 (IsSystemRelationName(relname))
+ elog(WARN, "renameatt: class \"%-.*s\" is a system catalog",
+ NAMEDATALEN, relname);
+#ifndef NO_SECURITY
+ if (!IsBootstrapProcessingMode() &&
+ !pg_ownercheck(userName, relname, RELNAME))
+ elog(WARN, "renameatt: you do not own class \"%-.*s\"",
+ NAMEDATALEN, relname);
+#endif
+
+ /*
+ * 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) {
+ Oid myrelid, childrelid;
+ List *child, *children;
+
+ relrdesc = heap_openr(relname);
+ if (!RelationIsValid(relrdesc)) {
+ elog(WARN, "renameatt: unknown relation: \"%-.*s\"",
+ NAMEDATALEN, relname);
+ }
+ myrelid = relrdesc->rd_id;
+ heap_close(relrdesc);
+
+ /* this routine is actually in the planner */
+ children = find_all_inheritors(lconsi(myrelid, NIL), NIL);
+
+
+ /*
+ * 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) {
+ char *childname;
+
+ childrelid = lfirsti(child);
+ if (childrelid == myrelid)
+ continue;
+ relrdesc = heap_open(childrelid);
+ if (!RelationIsValid(relrdesc)) {
+ elog(WARN, "renameatt: can't find catalog entry for inheriting class with oid %d",
+ childrelid);
+ }
+ childname = (relrdesc->rd_rel->relname).data;
+ heap_close(relrdesc);
+ renameatt(childname, oldattname, newattname,
+ userName, 0); /* no more recursion! */
+ }
+ }
+
+ relrdesc = heap_openr(RelationRelationName);
+ reltup = ClassNameIndexScan(relrdesc, relname);
+ if (!PointerIsValid(reltup)) {
+ heap_close(relrdesc);
+ elog(WARN, "renameatt: relation \"%-.*s\" nonexistent",
+ NAMEDATALEN, relname);
+ return;
+ }
+ heap_close(relrdesc);
+
+ attrdesc = heap_openr(AttributeRelationName);
+ oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname);
+ if (!PointerIsValid(oldatttup)) {
+ heap_close(attrdesc);
+ elog(WARN, "renameatt: attribute \"%-.*s\" nonexistent",
+ NAMEDATALEN, oldattname);
+ }
+ if (((AttributeTupleForm ) GETSTRUCT(oldatttup))->attnum < 0) {
+ elog(WARN, "renameatt: system attribute \"%-.*s\" not renamed",
+ NAMEDATALEN, oldattname);
+ }
+
+ newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname);
+ if (PointerIsValid(newatttup)) {
+ pfree(oldatttup);
+ heap_close(attrdesc);
+ elog(WARN, "renameatt: attribute \"%-.*s\" exists",
+ NAMEDATALEN, newattname);
+ }
+
+ namestrcpy(&(((AttributeTupleForm)(GETSTRUCT(oldatttup)))->attname),
+ newattname);
+ oldTID = oldatttup->t_ctid;
+
+ /* insert "fixed" tuple */
+ (void) heap_replace(attrdesc, &oldTID, oldatttup);
+
+ /* keep system catalog indices current */
+ CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+ CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, oldatttup);
+ CatalogCloseIndices(Num_pg_attr_indices, idescs);
+
+ heap_close(attrdesc);
+ pfree(oldatttup);
+}
+
+/*
+ * renamerel - change the name of a relation
+ *
+ * Relname attribute is changed in relation catalog.
+ * No record of the previous relname is kept (correct?).
+ *
+ * scan relation catalog
+ * for name conflict
+ * for original relation (if not arg)
+ * modify relname in relation tuple
+ * insert modified relation in relation catalog
+ * delete original relation from relation catalog
+ *
+ * XXX Will currently lose track of a relation if it is unable to
+ * properly replace the new relation tuple.
+ */
+void
+renamerel(char oldrelname[], char newrelname[])
+{
+ Relation relrdesc; /* for RELATION relation */
+ HeapTuple oldreltup, newreltup;
+ ItemPointerData oldTID;
+ char oldpath[MAXPGPATH], newpath[MAXPGPATH];
+ Relation idescs[Num_pg_class_indices];
+
+ if (IsSystemRelationName(oldrelname)) {
+ elog(WARN, "renamerel: system relation \"%-.*s\" not renamed",
+ NAMEDATALEN, oldrelname);
+ return;
+ }
+ if (IsSystemRelationName(newrelname)) {
+ elog(WARN, "renamerel: Illegal class name: \"%-.*s\" -- pg_ is reserved for system catalogs",
+ NAMEDATALEN, newrelname);
+ return;
+ }
+
+ relrdesc = heap_openr(RelationRelationName);
+ oldreltup = ClassNameIndexScan(relrdesc, oldrelname);
+
+ if (!PointerIsValid(oldreltup)) {
+ heap_close(relrdesc);
+ elog(WARN, "renamerel: relation \"%-.*s\" does not exist",
+ NAMEDATALEN, oldrelname);
+ }
+
+ newreltup = ClassNameIndexScan(relrdesc, newrelname);
+ if (PointerIsValid(newreltup)) {
+ pfree(oldreltup);
+ heap_close(relrdesc);
+ elog(WARN, "renamerel: relation \"%-.*s\" exists",
+ NAMEDATALEN, newrelname);
+ }
+
+ /* rename the directory first, so if this fails the rename's not done */
+ (void) strcpy(oldpath, relpath(oldrelname));
+ (void) strcpy(newpath, relpath(newrelname));
+ if (rename(oldpath, newpath) < 0)
+ elog(WARN, "renamerel: unable to rename file: %m");
+
+ memmove((char *) (((Form_pg_class) GETSTRUCT(oldreltup))->relname.data),
+ newrelname,
+ NAMEDATALEN);
+ oldTID = oldreltup->t_ctid;
+
+ /* insert fixed rel tuple */
+ (void) heap_replace(relrdesc, &oldTID, oldreltup);
+
+ /* keep the system catalog indices current */
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+ CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup);
+ CatalogCloseIndices(Num_pg_class_indices, idescs);
+
+ pfree(oldreltup);
+ heap_close(relrdesc);
+}
diff --git a/src/backend/commands/rename.h b/src/backend/commands/rename.h
new file mode 100644
index 00000000000..c3889e12f89
--- /dev/null
+++ b/src/backend/commands/rename.h
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * rename.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: rename.h,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RENAME_H
+#define RENAME_H
+
+extern void renameatt(char *relname,
+ char *oldattname,
+ char *newattname,
+ char *userName, int recurse);
+
+extern void renamerel(char *oldrelname,
+ char *newrelname);
+
+#endif /* RENAME_H */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
new file mode 100644
index 00000000000..7e1514cd2a3
--- /dev/null
+++ b/src/backend/commands/vacuum.c
@@ -0,0 +1,853 @@
+/*-------------------------------------------------------------------------
+ *
+ * vacuum.c--
+ * the postgres vacuum cleaner
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+
+#include "postgres.h"
+#include "utils/portal.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"
+#include "utils/tqual.h"
+#include "access/htup.h"
+
+#include "catalog/pg_index.h"
+#include "catalog/catname.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_proc.h"
+
+#include "storage/fd.h" /* for O_ */
+#include "storage/itemid.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/smgr.h"
+
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+
+#include "commands/vacuum.h"
+
+bool VacuumRunning = false;
+
+/* non-export function prototypes */
+static void _vc_init(char *vacrel);
+static void _vc_shutdown(char *vacrel);
+static void _vc_vacuum(char *vacrel);
+static VRelList _vc_getrels(Portal p, char *vacrel);
+static void _vc_vacone(Portal p, VRelList curvrl);
+static void _vc_vacheap(Portal p, VRelList curvrl, Relation onerel);
+static void _vc_vacindices(VRelList curvrl, Relation onerel);
+static void _vc_vaconeind(VRelList curvrl, Relation indrel);
+static void _vc_updstats(Oid relid, int npages, int ntuples, bool hasindex);
+static void _vc_setpagelock(Relation rel, BlockNumber blkno);
+static bool _vc_ontidlist(ItemPointer itemptr, VTidList tidlist);
+static void _vc_reaptid(Portal p, VRelList curvrl, BlockNumber blkno,
+ OffsetNumber offnum);
+static void _vc_free(Portal p, VRelList vrl);
+static Relation _vc_getarchrel(Relation heaprel);
+static void _vc_archive(Relation archrel, HeapTuple htup);
+static bool _vc_isarchrel(char *rname);
+
+void
+vacuum(char *vacrel)
+{
+ /* initialize vacuum cleaner */
+ _vc_init(vacrel);
+
+ /* vacuum the database */
+ _vc_vacuum(vacrel);
+
+ /* clean up */
+ _vc_shutdown(vacrel);
+}
+
+/*
+ * _vc_init(), _vc_shutdown() -- start up and shut down the vacuum cleaner.
+ *
+ * We run exactly one vacuum cleaner at a time. We use the file system
+ * to guarantee an exclusive lock on vacuuming, since a single vacuum
+ * cleaner instantiation crosses transaction boundaries, and we'd lose
+ * postgres-style locks at the end of every transaction.
+ *
+ * The strangeness with committing and starting transactions in the
+ * init and shutdown routines is due to the fact that the vacuum cleaner
+ * is invoked via a sql command, and so is already executing inside
+ * a transaction. We need to leave ourselves in a predictable state
+ * on entry and exit to the vacuum cleaner. We commit the transaction
+ * started in PostgresMain() inside _vc_init(), and start one in
+ * _vc_shutdown() to match the commit waiting for us back in
+ * PostgresMain().
+ */
+static void
+_vc_init(char *vacrel)
+{
+ int fd;
+
+ if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0)
+ elog(WARN, "can't create lock file -- another vacuum cleaner running?");
+
+ close(fd);
+
+ /*
+ * By here, exclusive open on the lock file succeeded. If we abort
+ * for any reason during vacuuming, we need to remove the lock file.
+ * This global variable is checked in the transaction manager on xact
+ * abort, and the routine vc_abort() is called if necessary.
+ */
+
+ VacuumRunning = true;
+
+ /* matches the StartTransaction in PostgresMain() */
+ CommitTransactionCommand();
+}
+
+static void
+_vc_shutdown(char *vacrel)
+{
+ /* on entry, not in a transaction */
+ if (unlink("pg_vlock") < 0)
+ elog(WARN, "vacuum: can't destroy lock file!");
+
+ /* okay, we're done */
+ VacuumRunning = false;
+
+ /* matches the CommitTransaction in PostgresMain() */
+ StartTransactionCommand();
+}
+
+void
+vc_abort()
+{
+ /* on abort, remove the vacuum cleaner lock file */
+ (void) unlink("pg_vlock");
+
+ VacuumRunning = false;
+}
+
+/*
+ * _vc_vacuum() -- vacuum the database.
+ *
+ * This routine builds a list of relations to vacuum, and then calls
+ * code that vacuums them one at a time. We are careful to vacuum each
+ * relation in a separate transaction in order to avoid holding too many
+ * locks at one time.
+ */
+static void
+_vc_vacuum(char *vacrel)
+{
+ VRelList vrl, cur;
+ char *pname;
+ Portal p;
+
+ /*
+ * Create a portal for safe memory across transctions. We need to
+ * palloc the name space for it because our hash function expects
+ * the name to be on a longword boundary. CreatePortal copies the
+ * name to safe storage for us.
+ */
+
+ pname = (char *) palloc(strlen(VACPNAME) + 1);
+ strcpy(pname, VACPNAME);
+ p = CreatePortal(pname);
+ pfree(pname);
+
+ /* get list of relations */
+ vrl = _vc_getrels(p, vacrel);
+
+ /* vacuum each heap relation */
+ for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
+ _vc_vacone(p, cur);
+
+ _vc_free(p, vrl);
+
+ PortalDestroy(&p);
+}
+
+static VRelList
+_vc_getrels(Portal p, char *vacrel)
+{
+ Relation pgclass;
+ TupleDesc pgcdesc;
+ HeapScanDesc pgcscan;
+ HeapTuple pgctup;
+ Buffer buf;
+ PortalVariableMemory portalmem;
+ MemoryContext old;
+ VRelList vrl, cur;
+ Datum d;
+ char *rname;
+ int16 smgrno;
+ bool n;
+ ScanKeyData pgckey;
+
+ StartTransactionCommand();
+
+ if (vacrel) {
+ ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname,
+ NameEqualRegProcedure,
+ PointerGetDatum(vacrel));
+ } else {
+ ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind,
+ CharacterEqualRegProcedure, CharGetDatum('r'));
+ }
+
+ portalmem = PortalGetVariableMemory(p);
+ vrl = (VRelList) NULL;
+
+ pgclass = heap_openr(RelationRelationName);
+ pgcdesc = RelationGetTupleDescriptor(pgclass);
+
+ pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey);
+
+ while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) {
+
+ /*
+ * We have to be careful not to vacuum the archive (since it
+ * already contains vacuumed tuples), and not to vacuum
+ * relations on write-once storage managers like the Sony
+ * jukebox at Berkeley.
+ */
+
+ d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname,
+ pgcdesc, &n);
+ rname = (char*)d;
+
+ /* skip archive relations */
+ if (_vc_isarchrel(rname)) {
+ ReleaseBuffer(buf);
+ continue;
+ }
+
+ d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr,
+ pgcdesc, &n);
+ smgrno = DatumGetInt16(d);
+
+ /* skip write-once storage managers */
+ if (smgriswo(smgrno)) {
+ ReleaseBuffer(buf);
+ continue;
+ }
+
+ /* get a relation list entry for this guy */
+ old = MemoryContextSwitchTo((MemoryContext)portalmem);
+ if (vrl == (VRelList) NULL) {
+ vrl = cur = (VRelList) palloc(sizeof(VRelListData));
+ } else {
+ cur->vrl_next = (VRelList) palloc(sizeof(VRelListData));
+ cur = cur->vrl_next;
+ }
+ (void) MemoryContextSwitchTo(old);
+
+ cur->vrl_relid = pgctup->t_oid;
+ cur->vrl_attlist = (VAttList) NULL;
+ cur->vrl_tidlist = (VTidList) NULL;
+ cur->vrl_npages = cur->vrl_ntups = 0;
+ cur->vrl_hasindex = false;
+ cur->vrl_next = (VRelList) NULL;
+
+ /* wei hates it if you forget to do this */
+ ReleaseBuffer(buf);
+ }
+
+ heap_close(pgclass);
+ heap_endscan(pgcscan);
+
+ CommitTransactionCommand();
+
+ return (vrl);
+}
+
+/*
+ * _vc_vacone() -- vacuum one heap relation
+ *
+ * This routine vacuums a single heap, cleans out its indices, and
+ * updates its statistics npages and ntuples statistics.
+ *
+ * Doing one heap at a time incurs extra overhead, since we need to
+ * check that the heap exists again just before we vacuum it. The
+ * reason that we do this is so that vacuuming can be spread across
+ * many small transactions. Otherwise, two-phase locking would require
+ * us to lock the entire database during one pass of the vacuum cleaner.
+ */
+static void
+_vc_vacone(Portal p, VRelList curvrl)
+{
+ Relation pgclass;
+ TupleDesc pgcdesc;
+ HeapTuple pgctup;
+ Buffer pgcbuf;
+ HeapScanDesc pgcscan;
+ Relation onerel;
+ ScanKeyData pgckey;
+
+ StartTransactionCommand();
+
+ ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(curvrl->vrl_relid));
+
+ pgclass = heap_openr(RelationRelationName);
+ pgcdesc = RelationGetTupleDescriptor(pgclass);
+ pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey);
+
+ /*
+ * Race condition -- if the pg_class tuple has gone away since the
+ * last time we saw it, we don't need to vacuum it.
+ */
+
+ if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &pgcbuf))) {
+ heap_endscan(pgcscan);
+ heap_close(pgclass);
+ CommitTransactionCommand();
+ return;
+ }
+
+ /* now open the class and vacuum it */
+ onerel = heap_open(curvrl->vrl_relid);
+
+ /* we require the relation to be locked until the indices are cleaned */
+ RelationSetLockForWrite(onerel);
+
+ /* vacuum it */
+ _vc_vacheap(p, curvrl, onerel);
+
+ /* if we vacuumed any heap tuples, vacuum the indices too */
+ if (curvrl->vrl_tidlist != (VTidList) NULL)
+ _vc_vacindices(curvrl, onerel);
+ else
+ curvrl->vrl_hasindex = onerel->rd_rel->relhasindex;
+
+ /* all done with this class */
+ heap_close(onerel);
+ heap_endscan(pgcscan);
+ heap_close(pgclass);
+
+ /* update statistics in pg_class */
+ _vc_updstats(curvrl->vrl_relid, curvrl->vrl_npages, curvrl->vrl_ntups,
+ curvrl->vrl_hasindex);
+
+ CommitTransactionCommand();
+}
+
+/*
+ * _vc_vacheap() -- vacuum an open heap relation
+ *
+ * This routine sets commit times, vacuums dead tuples, cleans up
+ * wasted space on the page, and maintains statistics on the number
+ * of live tuples in a heap. In addition, it records the tids of
+ * all tuples removed from the heap for any reason. These tids are
+ * used in a scan of indices on the relation to get rid of dead
+ * index tuples.
+ */
+static void
+_vc_vacheap(Portal p, VRelList curvrl, Relation onerel)
+{
+ int nblocks, blkno;
+ ItemId itemid;
+ HeapTuple htup;
+ Buffer buf;
+ Page page;
+ OffsetNumber offnum, maxoff;
+ Relation archrel;
+ bool isarchived;
+ int nvac;
+ int ntups;
+ bool pgchanged, tupgone;
+ AbsoluteTime purgetime, expiretime;
+ RelativeTime preservetime;
+
+ nvac = 0;
+ ntups = 0;
+ nblocks = RelationGetNumberOfBlocks(onerel);
+
+ {
+ char *relname;
+ relname = (RelationGetRelationName(onerel))->data;
+
+ if ( (strlen(relname) > 4) &&
+ relname[0] == 'X' &&
+ relname[1] == 'i' &&
+ relname[2] == 'n' &&
+ (relname[3] == 'v' || relname[3] == 'x'))
+ return;
+ }
+
+
+ /* if the relation has an archive, open it */
+ if (onerel->rd_rel->relarch != 'n') {
+ isarchived = true;
+ archrel = _vc_getarchrel(onerel);
+ } else
+ isarchived = false;
+
+ /* don't vacuum large objects for now.
+ something breaks when we do*/
+ {
+ char *relname;
+ relname = (RelationGetRelationName(onerel))->data;
+
+ if ( (strlen(relname) > 4) &&
+ relname[0] == 'X' &&
+ relname[1] == 'i' &&
+ relname[2] == 'n' &&
+ (relname[3] == 'v' || relname[3] == 'x'))
+ return;
+ }
+
+ /* calculate the purge time: tuples that expired before this time
+ will be archived or deleted */
+ purgetime = GetCurrentTransactionStartTime();
+ expiretime = (AbsoluteTime)onerel->rd_rel->relexpires;
+ preservetime = (RelativeTime)onerel->rd_rel->relpreserved;
+
+ if (RelativeTimeIsValid(preservetime) && (preservetime)) {
+ purgetime -= preservetime;
+ if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime) &&
+ expiretime > purgetime)
+ purgetime = expiretime;
+ }
+
+ else if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime))
+ purgetime = expiretime;
+
+ for (blkno = 0; blkno < nblocks; blkno++) {
+ buf = ReadBuffer(onerel, blkno);
+ page = BufferGetPage(buf);
+
+ if (PageIsEmpty(page)) {
+ ReleaseBuffer(buf);
+ continue;
+ }
+
+ pgchanged = false;
+ maxoff = PageGetMaxOffsetNumber(page);
+ for (offnum = FirstOffsetNumber;
+ offnum <= maxoff;
+ offnum = OffsetNumberNext(offnum)) {
+ itemid = PageGetItemId(page, offnum);
+
+ if (!ItemIdIsUsed(itemid))
+ continue;
+
+ htup = (HeapTuple) PageGetItem(page, itemid);
+ tupgone = false;
+
+ if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) &&
+ TransactionIdIsValid((TransactionId)htup->t_xmin)) {
+
+ if (TransactionIdDidAbort(htup->t_xmin)) {
+ _vc_reaptid(p, curvrl, blkno, offnum);
+ pgchanged = true;
+ tupgone = true;
+ } else if (TransactionIdDidCommit(htup->t_xmin)) {
+ htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin);
+ pgchanged = true;
+ }
+ }
+
+ if (TransactionIdIsValid((TransactionId)htup->t_xmax)) {
+ if (TransactionIdDidAbort(htup->t_xmax)) {
+ StoreInvalidTransactionId(&(htup->t_xmax));
+ pgchanged = true;
+ } else if (TransactionIdDidCommit(htup->t_xmax)) {
+ if (!AbsoluteTimeIsBackwardCompatiblyReal(htup->t_tmax)) {
+
+ htup->t_tmax = TransactionIdGetCommitTime(htup->t_xmax);
+ pgchanged = true;
+ }
+
+ /*
+ * Reap the dead tuple if its expiration time is
+ * before purgetime.
+ */
+
+ if (!tupgone && htup->t_tmax < purgetime) {
+ _vc_reaptid(p, curvrl, blkno, offnum);
+ tupgone = true;
+ pgchanged = true;
+ }
+ }
+ }
+
+ if (tupgone) {
+ ItemId lpp = &(((PageHeader) page)->pd_linp[offnum - 1]);
+
+ /* write the tuple to the archive, if necessary */
+ if (isarchived)
+ _vc_archive(archrel, htup);
+
+ /* mark it unused */
+ lpp->lp_flags &= ~LP_USED;
+
+ ++nvac;
+ } else {
+ ntups++;
+ }
+ }
+
+ if (pgchanged) {
+ PageRepairFragmentation(page);
+ WriteBuffer(buf);
+ } else {
+ ReleaseBuffer(buf);
+ }
+ }
+
+ if (isarchived)
+ heap_close(archrel);
+
+ /* save stats in the rel list for use later */
+ curvrl->vrl_ntups = ntups;
+ curvrl->vrl_npages = nblocks;
+}
+
+/*
+ * _vc_vacindices() -- vacuum all the indices for a particular heap relation.
+ *
+ * On entry, curvrl points at the relation currently being vacuumed.
+ * We already have a write lock on the relation, so we don't need to
+ * worry about anyone building an index on it while we're doing the
+ * vacuuming. The tid list for curvrl is sorted in reverse tid order:
+ * that is, tids on higher page numbers are before those on lower page
+ * numbers, and tids high on the page are before those low on the page.
+ * We use this ordering to cut down the search cost when we look at an
+ * index entry.
+ *
+ * We're executing inside the transaction that vacuumed the heap.
+ */
+static void
+_vc_vacindices(VRelList curvrl, Relation onerel)
+{
+ Relation pgindex;
+ TupleDesc pgidesc;
+ HeapTuple pgitup;
+ HeapScanDesc pgiscan;
+ Buffer buf;
+ Relation indrel;
+ Oid indoid;
+ Datum d;
+ bool n;
+ int nindices;
+ ScanKeyData pgikey;
+
+ /* see if we can dodge doing any work at all */
+ if (!(onerel->rd_rel->relhasindex))
+ return;
+
+ nindices = 0;
+
+ /* prepare a heap scan on the pg_index relation */
+ pgindex = heap_openr(IndexRelationName);
+ pgidesc = RelationGetTupleDescriptor(pgindex);
+
+ ScanKeyEntryInitialize(&pgikey, 0x0, Anum_pg_index_indrelid,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(curvrl->vrl_relid));
+
+ pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey);
+
+ /* vacuum all the indices */
+ while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, &buf))) {
+ d = (Datum) heap_getattr(pgitup, buf, Anum_pg_index_indexrelid,
+ pgidesc, &n);
+ indoid = DatumGetObjectId(d);
+ indrel = index_open(indoid);
+ _vc_vaconeind(curvrl, indrel);
+ heap_close(indrel);
+ nindices++;
+ }
+
+ heap_endscan(pgiscan);
+ heap_close(pgindex);
+
+ if (nindices > 0)
+ curvrl->vrl_hasindex = true;
+ else
+ curvrl->vrl_hasindex = false;
+}
+
+/*
+ * _vc_vaconeind() -- vacuum one index relation.
+ *
+ * Curvrl is the VRelList entry for the heap we're currently vacuuming.
+ * It's locked. The vrl_tidlist entry in curvrl is the list of deleted
+ * heap tids, sorted in reverse (page, offset) order. Onerel is an
+ * index relation on the vacuumed heap. We don't set locks on the index
+ * relation here, since the indexed access methods support locking at
+ * different granularities. We let them handle it.
+ *
+ * Finally, we arrange to update the index relation's statistics in
+ * pg_class.
+ */
+static void
+_vc_vaconeind(VRelList curvrl, Relation indrel)
+{
+ RetrieveIndexResult res;
+ IndexScanDesc iscan;
+ ItemPointer heapptr;
+ int nvac;
+ int nitups;
+ int nipages;
+
+ /* walk through the entire index */
+ iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
+ nvac = 0;
+ nitups = 0;
+
+ while ((res = index_getnext(iscan, ForwardScanDirection))
+ != (RetrieveIndexResult) NULL) {
+ heapptr = &res->heap_iptr;
+
+ if (_vc_ontidlist(heapptr, curvrl->vrl_tidlist)) {
+#if 0
+ elog(DEBUG, "<%x,%x> -> <%x,%x>",
+ ItemPointerGetBlockNumber(&(res->index_iptr)),
+ ItemPointerGetOffsetNumber(&(res->index_iptr)),
+ ItemPointerGetBlockNumber(&(res->heap_iptr)),
+ ItemPointerGetOffsetNumber(&(res->heap_iptr)));
+#endif
+ ++nvac;
+ index_delete(indrel, &res->index_iptr);
+ } else {
+ nitups++;
+ }
+
+ /* be tidy */
+ pfree(res);
+ }
+
+ index_endscan(iscan);
+
+ /* now update statistics in pg_class */
+ nipages = RelationGetNumberOfBlocks(indrel);
+ _vc_updstats(indrel->rd_id, nipages, nitups, false);
+}
+
+/*
+ * _vc_updstats() -- update pg_class statistics for one relation
+ *
+ * This routine works for both index and heap relation entries in
+ * pg_class. We violate no-overwrite semantics here by storing new
+ * values for ntuples, npages, and hasindex directly in the pg_class
+ * tuple that's already on the page. The reason for this is that if
+ * we updated these tuples in the usual way, then every tuple in pg_class
+ * would be replaced every day. This would make planning and executing
+ * historical queries very expensive.
+ */
+static void
+_vc_updstats(Oid relid, int npages, int ntuples, bool hasindex)
+{
+ Relation rd;
+ HeapScanDesc sdesc;
+ HeapTuple tup;
+ Buffer buf;
+ Form_pg_class pgcform;
+ ScanKeyData skey;
+
+ /*
+ * update number of tuples and number of pages in pg_class
+ */
+ ScanKeyEntryInitialize(&skey, 0x0, ObjectIdAttributeNumber,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(relid));
+
+ rd = heap_openr(RelationRelationName);
+ sdesc = heap_beginscan(rd, false, NowTimeQual, 1, &skey);
+
+ if (!HeapTupleIsValid(tup = heap_getnext(sdesc, 0, &buf)))
+ elog(WARN, "pg_class entry for relid %d vanished during vacuuming",
+ relid);
+
+ /* overwrite the existing statistics in the tuple */
+ _vc_setpagelock(rd, BufferGetBlockNumber(buf));
+ pgcform = (Form_pg_class) GETSTRUCT(tup);
+ pgcform->reltuples = ntuples;
+ pgcform->relpages = npages;
+ pgcform->relhasindex = hasindex;
+
+ /* XXX -- after write, should invalidate relcache in other backends */
+ WriteNoReleaseBuffer(buf);
+
+ /* that's all, folks */
+ heap_endscan(sdesc);
+ heap_close(rd);
+
+}
+
+static void _vc_setpagelock(Relation rel, BlockNumber blkno)
+{
+ ItemPointerData itm;
+
+ ItemPointerSet(&itm, blkno, 1);
+
+ RelationSetLockForWritePage(rel, &itm);
+}
+
+/*
+ * _vc_ontidlist() -- is a particular tid on the supplied tid list?
+ *
+ * Tidlist is sorted in reverse (page, offset) order.
+ */
+static bool
+_vc_ontidlist(ItemPointer itemptr, VTidList tidlist)
+{
+ BlockNumber ibkno;
+ OffsetNumber ioffno;
+ ItemPointer check;
+ BlockNumber ckbkno;
+ OffsetNumber ckoffno;
+
+ ibkno = ItemPointerGetBlockNumber(itemptr);
+ ioffno = ItemPointerGetOffsetNumber(itemptr);
+
+ while (tidlist != (VTidList) NULL) {
+ check = &(tidlist->vtl_tid);
+ ckbkno = ItemPointerGetBlockNumber(check);
+ ckoffno = ItemPointerGetOffsetNumber(check);
+
+ /* see if we've looked far enough down the list */
+ if ((ckbkno < ibkno) || (ckbkno == ibkno && ckoffno < ioffno))
+ return (false);
+
+ /* see if we have a match */
+ if (ckbkno == ibkno && ckoffno == ioffno)
+ return (true);
+
+ /* check next */
+ tidlist = tidlist->vtl_next;
+ }
+
+ /* ran off the end of the list without finding a match */
+ return (false);
+}
+
+/*
+ * _vc_reaptid() -- save a tid on the list of reaped tids for the current
+ * entry on the vacuum relation list.
+ *
+ * As a side effect of the way that the vacuuming loop for a given
+ * relation works, the tids of vacuumed tuples wind up in reverse
+ * order in the list -- highest tid on a page is first, and higher
+ * pages come before lower pages. This is important later when we
+ * vacuum the indices, as it gives us a way of stopping the search
+ * for a tid if we notice we've passed the page it would be on.
+ */
+static void
+_vc_reaptid(Portal p,
+ VRelList curvrl,
+ BlockNumber blkno,
+ OffsetNumber offnum)
+{
+ PortalVariableMemory pmem;
+ MemoryContext old;
+ VTidList newvtl;
+
+ /* allocate a VTidListData entry in the portal memory context */
+ pmem = PortalGetVariableMemory(p);
+ old = MemoryContextSwitchTo((MemoryContext) pmem);
+ newvtl = (VTidList) palloc(sizeof(VTidListData));
+ MemoryContextSwitchTo(old);
+
+ /* fill it in */
+ ItemPointerSet(&(newvtl->vtl_tid), blkno, offnum);
+ newvtl->vtl_next = curvrl->vrl_tidlist;
+ curvrl->vrl_tidlist = newvtl;
+}
+
+static void
+_vc_free(Portal p, VRelList vrl)
+{
+ VRelList p_vrl;
+ VAttList p_val, val;
+ VTidList p_vtl, vtl;
+ MemoryContext old;
+ PortalVariableMemory pmem;
+
+ pmem = PortalGetVariableMemory(p);
+ old = MemoryContextSwitchTo((MemoryContext)pmem);
+
+ while (vrl != (VRelList) NULL) {
+
+ /* free attribute list */
+ val = vrl->vrl_attlist;
+ while (val != (VAttList) NULL) {
+ p_val = val;
+ val = val->val_next;
+ pfree(p_val);
+ }
+
+ /* free tid list */
+ vtl = vrl->vrl_tidlist;
+ while (vtl != (VTidList) NULL) {
+ p_vtl = vtl;
+ vtl = vtl->vtl_next;
+ pfree(p_vtl);
+ }
+
+ /* free rel list entry */
+ p_vrl = vrl;
+ vrl = vrl->vrl_next;
+ pfree(p_vrl);
+ }
+
+ (void) MemoryContextSwitchTo(old);
+}
+
+/*
+ * _vc_getarchrel() -- open the archive relation for a heap relation
+ *
+ * The archive relation is named 'a,XXXXX' for the heap relation
+ * whose relid is XXXXX.
+ */
+
+#define ARCHIVE_PREFIX "a,"
+
+static Relation
+_vc_getarchrel(Relation heaprel)
+{
+ Relation archrel;
+ char *archrelname;
+
+ archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */
+ sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id);
+
+ archrel = heap_openr(archrelname);
+
+ pfree(archrelname);
+ return (archrel);
+}
+
+/*
+ * _vc_archive() -- write a tuple to an archive relation
+ *
+ * In the future, this will invoke the archived accessd method. For
+ * now, archive relations are on mag disk.
+ */
+static void
+_vc_archive(Relation archrel, HeapTuple htup)
+{
+ doinsert(archrel, htup);
+}
+
+static bool
+_vc_isarchrel(char *rname)
+{
+ if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0)
+ return (true);
+
+ return (false);
+}
diff --git a/src/backend/commands/vacuum.h b/src/backend/commands/vacuum.h
new file mode 100644
index 00000000000..f5994d7d6d5
--- /dev/null
+++ b/src/backend/commands/vacuum.h
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * vacuum.h--
+ * header file for postgres vacuum cleaner
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: vacuum.h,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef VACUUM_H
+#define VACUUM_H
+
+typedef struct VAttListData {
+ int val_dummy;
+ struct VAttListData *val_next;
+} VAttListData;
+
+typedef VAttListData *VAttList;
+
+typedef struct VTidListData {
+ ItemPointerData vtl_tid;
+ struct VTidListData *vtl_next;
+} VTidListData;
+
+typedef VTidListData *VTidList;
+
+typedef struct VRelListData {
+ Oid vrl_relid;
+ VAttList vrl_attlist;
+ VTidList vrl_tidlist;
+ int vrl_ntups;
+ int vrl_npages;
+ bool vrl_hasindex;
+ struct VRelListData *vrl_next;
+} VRelListData;
+
+typedef VRelListData *VRelList;
+
+extern bool VacuumRunning;
+
+extern void vc_abort(void);
+extern void vacuum(char *vacrel);
+
+
+#endif /* VACUUM_H */
diff --git a/src/backend/commands/version.h b/src/backend/commands/version.h
new file mode 100644
index 00000000000..20d49d2c0c7
--- /dev/null
+++ b/src/backend/commands/version.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * version.h--
+ * Header file for versions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: version.h,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef VERSION_H
+#define VERSION_H
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+
+extern void DefineVersion(char *name, char *fromRelname, char *date);
+extern void VersionCreate(char *vname, char *bname);
+extern void VersionAppend(char *vname, char *bname);
+extern void VersionRetrieve(char *vname, char *bname, char *snapshot);
+extern void VersionDelete(char *vname, char *bname, char *snapshot);
+extern void VersionReplace(char *vname, char *bname, char *snapshot);
+
+#endif /* VERSION_H */
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
new file mode 100644
index 00000000000..f6023ca08de
--- /dev/null
+++ b/src/backend/commands/view.c
@@ -0,0 +1,325 @@
+/*-------------------------------------------------------------------------
+ *
+ * view.c--
+ * use rewrite rules to construct views
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h> /* for sprintf() */
+#include "postgres.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"
+#include "rewrite/rewriteDefine.h"
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
+#include "rewrite/rewriteRemove.h"
+#include "commands/creatinh.h"
+
+/*---------------------------------------------------------------------
+ * DefineVirtualRelation
+ *
+ * Create the "view" relation.
+ * `DefineRelation' does all the work, we just provide the correct
+ * arguments!
+ *
+ * If the relation already exists, then 'DefineRelation' will abort
+ * the xact...
+ *---------------------------------------------------------------------
+ */
+static void
+DefineVirtualRelation(char *relname, List *tlist)
+{
+ CreateStmt createStmt;
+ List *attrList, *t;
+ TargetEntry *entry;
+ Resdom *res;
+ char *resname;
+ char *restypename;
+
+ /*
+ * create a list with one entry per attribute of this relation.
+ * Each entry is a two element list. The first element is the
+ * name of the attribute (a string) and the second the name of the type
+ * (NOTE: a string, not a type id!).
+ */
+ attrList = NIL;
+ if (tlist!=NIL) {
+ foreach (t, tlist ) {
+ ColumnDef *def = makeNode(ColumnDef);
+ TypeName *typename;
+
+ /*
+ * find the names of the attribute & its type
+ */
+ entry = lfirst(t);
+ res = entry->resdom;
+ resname = res->resname;
+ restypename = tname(get_id_type((long)res->restype));
+
+ typename = makeNode(TypeName);
+
+ typename->name = pstrdup(restypename);
+ def->colname = pstrdup(resname);
+
+ def->typename = typename;
+
+ attrList = lappend(attrList, def);
+ }
+ } else {
+ elog ( WARN, "attempted to define virtual relation with no attrs");
+ }
+
+ /*
+ * now create the parametesr for keys/inheritance etc.
+ * All of them are nil...
+ */
+ createStmt.relname = relname;
+ createStmt.tableElts = attrList;
+/* createStmt.tableType = NULL;*/
+ createStmt.inhRelnames = NIL;
+ createStmt.archiveType = ARCH_NONE;
+ createStmt.location = -1;
+ createStmt.archiveLoc = -1;
+
+ /*
+ * finally create the relation...
+ */
+ DefineRelation(&createStmt);
+}
+
+/*------------------------------------------------------------------
+ * makeViewRetrieveRuleName
+ *
+ * Given a view name, returns the name for the 'on retrieve to "view"'
+ * rule.
+ * This routine is called when defining/removing a view.
+ *
+ * NOTE: it quarantees that the name is at most 15 chars long
+ *
+ * XXX it also means viewName cannot be 16 chars long! - ay 11/94
+ *------------------------------------------------------------------
+ */
+char *
+MakeRetrieveViewRuleName(char *viewName)
+{
+/*
+ char buf[100];
+
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data);
+ buf[15] = '\0';
+ namestrcpy(rule_name, buf);
+*/
+
+ char *buf;
+ buf = palloc(strlen(viewName) + 5);
+ sprintf(buf, "_RET%s",viewName);
+ return buf;
+}
+
+static RuleStmt *
+FormViewRetrieveRule(char *viewName, Query *viewParse)
+{
+ RuleStmt *rule;
+ char *rname;
+ Attr *attr;
+
+ /*
+ * Create a RuleStmt that corresponds to the suitable
+ * rewrite rule args for DefineQueryRewrite();
+ */
+ rule = makeNode(RuleStmt);
+ rname = MakeRetrieveViewRuleName(viewName);
+
+ attr = makeNode(Attr);
+ attr->relname = pstrdup(viewName);
+/* attr->refname = pstrdup(viewName);*/
+ rule->rulename = pstrdup(rname);
+ rule->whereClause = NULL;
+ rule->event = CMD_SELECT;
+ rule->object = attr;
+ rule->instead = true;
+ rule->actions = lcons(viewParse, NIL);
+
+ return rule;
+}
+
+static void
+DefineViewRules(char *viewName, Query *viewParse)
+{
+ RuleStmt *retrieve_rule = NULL;
+#ifdef NOTYET
+ RuleStmt *replace_rule = NULL;
+ RuleStmt *append_rule = NULL;
+ RuleStmt *delete_rule = NULL;
+#endif
+
+ retrieve_rule =
+ FormViewRetrieveRule(viewName, viewParse);
+
+#ifdef NOTYET
+
+ replace_rule =
+ FormViewReplaceRule(viewName, viewParse);
+ append_rule =
+ FormViewAppendRule(viewName, viewParse);
+ delete_rule =
+ FormViewDeleteRule(viewName, viewParse);
+
+#endif
+
+ DefineQueryRewrite(retrieve_rule);
+
+#ifdef NOTYET
+ DefineQueryRewrite(replace_rule);
+ DefineQueryRewrite(append_rule);
+ DefineQueryRewrite(delete_rule);
+#endif
+
+}
+
+/*---------------------------------------------------------------
+ * UpdateRangeTableOfViewParse
+ *
+ * Update the range table of the given parsetree.
+ * This update consists of adding two new entries IN THE BEGINNING
+ * of the range table (otherwise the rule system will die a slow,
+ * horrible and painful death, and we do not want that now, do we?)
+ * one for the CURRENT relation and one for the NEW one (both of
+ * them refer in fact to the "view" relation).
+ *
+ * Of course we must also increase the 'varnos' of all the Var nodes
+ * by 2...
+ *
+ * NOTE: these are destructive changes. It would be difficult to
+ * make a complete copy of the parse tree and make the changes
+ * in the copy.
+ *---------------------------------------------------------------
+ */
+static void
+UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
+{
+ List *old_rt;
+ List *new_rt;
+ RangeTblEntry *rt_entry1, *rt_entry2;
+
+ /*
+ * first offset all var nodes by 2
+ */
+ OffsetVarNodes((Node*)viewParse->targetList, 2);
+ OffsetVarNodes(viewParse->qual, 2);
+
+ /*
+ * find the old range table...
+ */
+ old_rt = viewParse->rtable;
+
+ /*
+ * create the 2 new range table entries and form the new
+ * range table...
+ * CURRENT first, then NEW....
+ */
+ rt_entry1 =
+ makeRangeTableEntry((char*)viewName, FALSE, NULL, "*CURRENT*");
+ rt_entry2 =
+ makeRangeTableEntry((char*)viewName, FALSE, NULL, "*NEW*");
+ new_rt = lcons(rt_entry2, old_rt);
+ new_rt = lcons(rt_entry1, new_rt);
+
+ /*
+ * Now the tricky part....
+ * Update the range table in place... Be careful here, or
+ * hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
+ */
+ viewParse->rtable = new_rt;
+}
+
+/*-------------------------------------------------------------------
+ * DefineView
+ *
+ * - takes a "viewname", "parsetree" pair and then
+ * 1) construct the "virtual" relation
+ * 2) commit the command but NOT the transaction,
+ * so that the relation exists
+ * before the rules are defined.
+ * 2) define the "n" rules specified in the PRS2 paper
+ * over the "virtual" relation
+ *-------------------------------------------------------------------
+ */
+void
+DefineView(char *viewName, Query *viewParse)
+{
+ List *viewTlist;
+
+ viewTlist = viewParse->targetList;
+
+ /*
+ * Create the "view" relation
+ * NOTE: if it already exists, the xaxt will be aborted.
+ */
+ DefineVirtualRelation(viewName, viewTlist);
+
+ /*
+ * The relation we have just created is not visible
+ * to any other commands running with the same transaction &
+ * command id.
+ * So, increment the command id counter (but do NOT pfree any
+ * memory!!!!)
+ */
+ CommandCounterIncrement();
+
+ /*
+ * The range table of 'viewParse' does not contain entries
+ * for the "CURRENT" and "NEW" relations.
+ * So... add them!
+ * NOTE: we make the update in place! After this call 'viewParse'
+ * will never be what it used to be...
+ */
+ UpdateRangeTableOfViewParse(viewName, viewParse);
+ DefineViewRules(viewName, viewParse);
+}
+
+/*------------------------------------------------------------------
+ * RemoveView
+ *
+ * Remove a view given its name
+ *------------------------------------------------------------------
+ */
+void
+RemoveView(char *viewName)
+{
+ char* rname;
+
+ /*
+ * first remove all the "view" rules...
+ * Currently we only have one!
+ */
+ rname = MakeRetrieveViewRuleName(viewName);
+ RemoveRewriteRule(rname);
+
+ /*
+ * we don't really need that, but just in case...
+ */
+ CommandCounterIncrement();
+
+ /*
+ * now remove the relation.
+ */
+ heap_destroy(viewName);
+ pfree(rname);
+}
diff --git a/src/backend/commands/view.h b/src/backend/commands/view.h
new file mode 100644
index 00000000000..15151237715
--- /dev/null
+++ b/src/backend/commands/view.h
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * view.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: view.h,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef VIEW_H
+#define VIEW_H
+
+extern char *MakeRetrieveViewRuleName(char *view_name);
+extern void DefineView(char *view_name, Query *view_parse);
+extern void RemoveView(char *view_name);
+
+#endif /* VIEW_H */