1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Postgres95 1.01 Distribution - Virgin Sources

This commit is contained in:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View File

@@ -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

View File

@@ -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); */
}

View File

@@ -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));
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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 */

782
src/backend/commands/copy.c Normal file
View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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*/

View File

@@ -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);
}

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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 */

325
src/backend/commands/view.c Normal file
View File

@@ -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);
}

View File

@@ -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 */