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:
25
src/backend/commands/Makefile.inc
Normal file
25
src/backend/commands/Makefile.inc
Normal 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
|
||||
|
||||
|
336
src/backend/commands/_deadcode/version.c
Normal file
336
src/backend/commands/_deadcode/version.c
Normal 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); */
|
||||
|
||||
}
|
||||
|
605
src/backend/commands/async.c
Normal file
605
src/backend/commands/async.c
Normal 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));
|
||||
}
|
||||
|
33
src/backend/commands/async.h
Normal file
33
src/backend/commands/async.h
Normal 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 */
|
370
src/backend/commands/cluster.c
Normal file
370
src/backend/commands/cluster.c
Normal 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);
|
||||
}
|
||||
|
30
src/backend/commands/cluster.h
Normal file
30
src/backend/commands/cluster.h
Normal 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 */
|
511
src/backend/commands/command.c
Normal file
511
src/backend/commands/command.c
Normal 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);
|
||||
}
|
56
src/backend/commands/command.h
Normal file
56
src/backend/commands/command.h
Normal 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
782
src/backend/commands/copy.c
Normal 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);
|
||||
}
|
21
src/backend/commands/copy.h
Normal file
21
src/backend/commands/copy.h
Normal 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 */
|
564
src/backend/commands/creatinh.c
Normal file
564
src/backend/commands/creatinh.c
Normal 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;
|
||||
}
|
||||
|
20
src/backend/commands/creatinh.h
Normal file
20
src/backend/commands/creatinh.h
Normal 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 */
|
505
src/backend/commands/defind.c
Normal file
505
src/backend/commands/defind.c
Normal 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);
|
||||
}
|
564
src/backend/commands/define.c
Normal file
564
src/backend/commands/define.c
Normal 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;
|
||||
}
|
53
src/backend/commands/defrem.h
Normal file
53
src/backend/commands/defrem.h
Normal 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 */
|
219
src/backend/commands/explain.c
Normal file
219
src/backend/commands/explain.c
Normal 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;
|
||||
}
|
17
src/backend/commands/explain.h
Normal file
17
src/backend/commands/explain.h
Normal 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*/
|
168
src/backend/commands/purge.c
Normal file
168
src/backend/commands/purge.c
Normal 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);
|
||||
}
|
||||
|
20
src/backend/commands/purge.h
Normal file
20
src/backend/commands/purge.h
Normal 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 */
|
1181
src/backend/commands/recipe.c
Normal file
1181
src/backend/commands/recipe.c
Normal file
File diff suppressed because it is too large
Load Diff
17
src/backend/commands/recipe.h
Normal file
17
src/backend/commands/recipe.h
Normal 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 */
|
435
src/backend/commands/remove.c
Normal file
435
src/backend/commands/remove.c
Normal 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);
|
||||
}
|
275
src/backend/commands/rename.c
Normal file
275
src/backend/commands/rename.c
Normal 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);
|
||||
}
|
24
src/backend/commands/rename.h
Normal file
24
src/backend/commands/rename.h
Normal 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 */
|
853
src/backend/commands/vacuum.c
Normal file
853
src/backend/commands/vacuum.c
Normal 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);
|
||||
}
|
48
src/backend/commands/vacuum.h
Normal file
48
src/backend/commands/vacuum.h
Normal 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 */
|
26
src/backend/commands/version.h
Normal file
26
src/backend/commands/version.h
Normal 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
325
src/backend/commands/view.c
Normal 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);
|
||||
}
|
20
src/backend/commands/view.h
Normal file
20
src/backend/commands/view.h
Normal 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 */
|
Reference in New Issue
Block a user