diff options
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/_deadcode/version.c | 369 | ||||
-rw-r--r-- | src/backend/commands/async.c | 831 | ||||
-rw-r--r-- | src/backend/commands/cluster.c | 580 | ||||
-rw-r--r-- | src/backend/commands/command.c | 827 | ||||
-rw-r--r-- | src/backend/commands/copy.c | 1884 | ||||
-rw-r--r-- | src/backend/commands/creatinh.c | 1112 | ||||
-rw-r--r-- | src/backend/commands/defind.c | 899 | ||||
-rw-r--r-- | src/backend/commands/define.c | 1113 | ||||
-rw-r--r-- | src/backend/commands/explain.c | 353 | ||||
-rw-r--r-- | src/backend/commands/purge.c | 271 | ||||
-rw-r--r-- | src/backend/commands/recipe.c | 2130 | ||||
-rw-r--r-- | src/backend/commands/remove.c | 796 | ||||
-rw-r--r-- | src/backend/commands/rename.c | 401 | ||||
-rw-r--r-- | src/backend/commands/sequence.c | 924 | ||||
-rw-r--r-- | src/backend/commands/trigger.c | 1050 | ||||
-rw-r--r-- | src/backend/commands/vacuum.c | 3920 | ||||
-rw-r--r-- | src/backend/commands/view.c | 427 |
17 files changed, 9402 insertions, 8485 deletions
diff --git a/src/backend/commands/_deadcode/version.c b/src/backend/commands/_deadcode/version.c index bac35cd4f87..c3eb6f47797 100644 --- a/src/backend/commands/_deadcode/version.c +++ b/src/backend/commands/_deadcode/version.c @@ -1,23 +1,23 @@ /*------------------------------------------------------------------------- * * version.c-- - * This file contains all the rules that govern all version semantics. + * 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 - * + * 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.5 1997/08/19 21:30:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.6 1997/09/07 04:41:04 momjian Exp $ * * NOTES - * At the point the version is defined, 2 physical relations are created - * <vname>_added and <vname>_deleted. + * 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. + * In addition, 4 rules are defined which govern the semantics of versions + * w.r.t retrieves, appends, replaces and deletes. * *------------------------------------------------------------------------- */ @@ -34,29 +34,31 @@ #define MAX_QUERY_LEN 1024 -char rule_buf[MAX_QUERY_LEN]; +char rule_buf[MAX_QUERY_LEN]; + #ifdef NOT_USED -static char attr_list[MAX_QUERY_LEN]; +static char attr_list[MAX_QUERY_LEN]; + #endif /* * 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 + * 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. + * This is needed because the rule system only allows + * *1* rule to be defined per transaction. * * NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO @@ -80,267 +82,282 @@ static char attr_list[MAX_QUERY_LEN]; * a strange memory bug instead of watching the "Get Smart" marathon * in NICK ! * DO NOT COMMIT THE XACT, just increase the Cid counter! - * _sp. + * _sp. */ #ifdef NOT_USED 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); + + /* + * WARNING! do not uncomment the following lines WARNING! + * CommitTransactionCommand(); StartTransactionCommand(); + */ + CommandCounterIncrement(); + pg_eval(query, (char **) NULL, (Oid *) NULL, 0); } + #endif /* - * Define a version. + * Define a version. */ #ifdef NOT_USED 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; - strcpy(saved_basename, (char *) bname); - *saved_snapshot = (char)NULL; - } else { - /* version is a snapshot */ - bname = fromRelname; - 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); + char *bname; + static char saved_basename[512]; + static char saved_snapshot[512]; + + if (date == NULL) + { + /* no time ranges */ + bname = fromRelname; + strcpy(saved_basename, (char *) bname); + *saved_snapshot = (char) NULL; + } + else + { + /* version is a snapshot */ + bname = fromRelname; + 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); } + #endif /* - * Creates the deltas. + * Creates the deltas. */ #ifdef NOT_USED 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); + 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); } + #endif /* * 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. + * for that relation. */ #ifdef NOT_USED 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; + 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); } - strcat(attr_list, temp_buf); - } - - heap_close(rdesc); - - return; + + heap_close(rdesc); + + return; } + #endif /* * This routine defines the rule governing the append semantics of - * versions. All tuples appended to a version gets appended to the + * versions. All tuples appended to a version gets appended to the * <vname>_added relation. */ #ifdef NOT_USED static 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); + 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); } + #endif /* * 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. + * 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. */ #ifdef NOT_USED void VersionRetrieve(char *vname, char *bname, char *snapshot) { - - sprintf(rule_buf, - "define rewrite rule %s_retrieve is on SELECT to %s do instead\n\ + + 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); */ - + vname, vname, vname, vname, bname, + bname, snapshot, + vname, vname, bname, bname, vname); + + eval_as_new_xact(rule_buf); + + /* printf("%s\n",rule_buf); */ + } + #endif /* - * This routine defines the rules that govern the delete semantics of + * 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. + * 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. */ #ifdef NOT_USED void VersionDelete(char *vname, char *bname, char *snapshot) { - - sprintf(rule_buf, - "define rewrite rule %s_delete1 is on delete to %s do instead\n \ + + 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); + vname, vname, vname, vname, vname, + bname, bname, snapshot, bname); - eval_as_new_xact(rule_buf); + 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 \ + 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); + vname, vname, vname, bname, bname, snapshot, bname); - eval_as_new_xact(rule_buf); -#endif /* OLD_REWRITE */ + eval_as_new_xact(rule_buf); +#endif /* OLD_REWRITE */ } + #endif /* - * This routine defines the rules that govern the update semantics - * of versions. To update a tuple in a version: + * 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 + * 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 */ #ifdef NOT_USED void VersionReplace(char *vname, char *bname, char *snapshot) { - sprintf(rule_buf, - "define rewrite rule %s_replace1 is on replace to %s do instead \n\ + 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); + 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); + eval_as_new_xact(rule_buf); -/* printf("%s\n",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\ + 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); + vname, vname, vname, bname, bname, snapshot, bname); - eval_as_new_xact(rule_buf); + eval_as_new_xact(rule_buf); - sprintf(rule_buf, - "define rewrite rule %s_replace3 is on replace to %s do instead\n\ + 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); + vname, vname, vname, attr_list, bname, bname, snapshot, vname, bname); - eval_as_new_xact(rule_buf); -#endif /* OLD_REWRITE */ -/* printf("%s\n",rule_buf); */ + eval_as_new_xact(rule_buf); +#endif /* OLD_REWRITE */ +/* printf("%s\n",rule_buf); */ } diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 8a1e6d59b57..42d440a8676 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -1,35 +1,35 @@ /*------------------------------------------------------------------------- * * async.c-- - * Asynchronous notification + * Asynchronous notification * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.17 1997/08/19 21:30:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.18 1997/09/07 04:40:35 momjian Exp $ * *------------------------------------------------------------------------- */ /* New Async Notification Model: * 1. Multiple backends on same machine. Multiple backends listening on - * one relation. + * 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. + * 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. + * 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 @@ -42,16 +42,16 @@ * 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. + * 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. + * 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. + * channel using the out-of-band channel. * * 5. Each frontend receives this notification and processes accordingly. * @@ -62,7 +62,7 @@ #include <signal.h> #include <string.h> #include <errno.h> -#include <sys/types.h> /* Needed by in.h on Ultrix */ +#include <sys/types.h> /* Needed by in.h on Ultrix */ #include <netinet/in.h> #include <postgres.h> @@ -75,546 +75,585 @@ #include <catalog/pg_proc.h> #include <catalog/catname.h> #include <catalog/pg_listener.h> -#include <access/heapam.h> +#include <access/heapam.h> #include <storage/bufmgr.h> #include <nodes/memnodes.h> #include <utils/mcxt.h> #include <commands/async.h> #include <libpq/libpq.h> -#include <port-protos.h> /* for strdup() */ +#include <port-protos.h> /* for strdup() */ #include <storage/lmgr.h> -static int notifyFrontEndPending = 0; -static int notifyIssued = 0; -static Dllist *pendingNotifies = NULL; +static int notifyFrontEndPending = 0; +static int notifyIssued = 0; +static Dllist *pendingNotifies = NULL; -static int AsyncExistsPendingNotify(char *); -static void ClearPendingNotify(void); -static void Async_NotifyFrontEnd(void); -static void Async_Unlisten(char *relname, int pid); -static void Async_UnlistenOnExit(int code, char *relname); - +static int AsyncExistsPendingNotify(char *); +static void ClearPendingNotify(void); +static void Async_NotifyFrontEnd(void); +static void Async_Unlisten(char *relname, int pid); +static void Async_UnlistenOnExit(int code, char *relname); + /* *-------------------------------------------------------------- * 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. + * 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 + * -- jw, 12/28/93 * Results: - * none + * none * * Side effects: - * none + * none */ void Async_NotifyHandler(SIGNAL_ARGS) { - extern TransactionState CurrentTransactionState; - - if ((CurrentTransactionState->state == TRANS_DEFAULT) && - (CurrentTransactionState->blockState == TRANS_DEFAULT)) { + extern TransactionState CurrentTransactionState; + + if ((CurrentTransactionState->state == TRANS_DEFAULT) && + (CurrentTransactionState->blockState == TRANS_DEFAULT)) + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Waking up sleeping backend process"); + elog(DEBUG, "Waking up sleeping backend process"); #endif - Async_NotifyFrontEnd(); + Async_NotifyFrontEnd(); - }else { + } + else + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d", - CurrentTransactionState->state, - CurrentTransactionState->blockState); + elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d", + CurrentTransactionState->state, + CurrentTransactionState->blockState); #endif - notifyFrontEndPending = 1; - } + notifyFrontEndPending = 1; + } } /* *-------------------------------------------------------------- * Async_Notify -- * - * Adds the relation to the list of pending notifies. - * All notification happens at end of commit. - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + * 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. + * 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 + * This correspond to 'notify <relname>' command + * -- jw, 12/28/93 * * Results: - * XXX + * XXX * * Side effects: - * All tuples for relname in pg_listener are updated. + * 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; - + + 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; + #ifdef ASYNC_DEBUG - elog(DEBUG,"Async_Notify: %s",relname); + elog(DEBUG, "Async_Notify: %s", relname); #endif - - if (!pendingNotifies) - pendingNotifies = DLNewList(); - - /* - * Allocate memory from the global malloc pool because it needs to be - * referenced also when the transaction is finished. DZ - 26-08-1996 - */ - notifyName = strdup(relname); - DLAddHead(pendingNotifies, DLNewElem(notifyName)); - - ScanKeyEntryInitialize(&key, 0, - Anum_pg_listener_relname, - NameEqualRegProcedure, - PointerGetDatum(notifyName)); - - lRel = heap_openr(ListenerRelationName); - tdesc = RelationGetTupleDescriptor(lRel); - RelationSetLockForWrite(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); - heap_replace(lRel, &lTuple->t_ctid, rTuple); + + if (!pendingNotifies) + pendingNotifies = DLNewList(); + + /* + * Allocate memory from the global malloc pool because it needs to be + * referenced also when the transaction is finished. DZ - 26-08-1996 + */ + notifyName = strdup(relname); + DLAddHead(pendingNotifies, DLNewElem(notifyName)); + + ScanKeyEntryInitialize(&key, 0, + Anum_pg_listener_relname, + NameEqualRegProcedure, + PointerGetDatum(notifyName)); + + lRel = heap_openr(ListenerRelationName); + tdesc = RelationGetTupleDescriptor(lRel); + RelationSetLockForWrite(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); + heap_replace(lRel, &lTuple->t_ctid, rTuple); + } + ReleaseBuffer(b); } - ReleaseBuffer(b); - } - heap_endscan(sRel); - RelationUnsetLockForWrite(lRel); - heap_close(lRel); - notifyIssued = 1; + heap_endscan(sRel); + RelationUnsetLockForWrite(lRel); + 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. + * Signal our corresponding frontend process on relations that + * were notified. Signal all other backend process that + * are listening also. * - * -- jw, 12/28/93 + * -- jw, 12/28/93 * * Results: - * XXX + * 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. + * 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 + * -- 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(); + 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(); #ifdef ASYNC_DEBUG - elog(DEBUG, "Async_NotifyAtCommit."); + elog(DEBUG, "Async_NotifyAtCommit."); #endif - ScanKeyEntryInitialize(&key, 0, - Anum_pg_listener_notify, - Integer32EqualRegProcedure, - Int32GetDatum(1)); - lRel = heap_openr(ListenerRelationName); - RelationSetLockForWrite(lRel); - 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)) { + ScanKeyEntryInitialize(&key, 0, + Anum_pg_listener_notify, + Integer32EqualRegProcedure, + Int32GetDatum(1)); + lRel = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lRel); + 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)) + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1"); + elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1"); #endif - notifyFrontEndPending = 1; - } else { + notifyFrontEndPending = 1; + } + else + { #ifdef ASYNC_DEBUG - elog(DEBUG, "Notifying others"); + elog(DEBUG, "Notifying others"); #endif #ifdef HAVE_KILL - if (kill(DatumGetInt32(d), SIGUSR2) < 0) { - if (errno == ESRCH) { - heap_delete(lRel, &lTuple->t_ctid); - } + if (kill(DatumGetInt32(d), SIGUSR2) < 0) + { + if (errno == ESRCH) + { + heap_delete(lRel, &lTuple->t_ctid); + } + } +#endif + } + } + ReleaseBuffer(b); } -#endif - } + heap_endscan(sRel); + RelationUnsetLockForWrite(lRel); + heap_close(lRel); + + CommitTransactionCommand(); + ClearPendingNotify(); } - ReleaseBuffer(b); - } - heap_endscan(sRel); - RelationUnsetLockForWrite(lRel); - heap_close(lRel); - CommitTransactionCommand(); - ClearPendingNotify(); - } - - if (notifyFrontEndPending) { /* we need to notify the frontend of - all pending notifies. */ - notifyFrontEndPending = 1; - Async_NotifyFrontEnd(); + 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. - * + * Gets rid of pending notifies. List elements are automatically + * freed through memory context. + * * * Results: - * XXX + * XXX * * Side effects: - * XXX + * 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(); + 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. + * Register a backend (identified by its Unix PID) as listening + * on the specified relation. * - * This corresponds to the 'listen <relation>' command in SQL + * 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. + * One listener per relation, pg_listener relation is keyed + * on (relname,pid) to provide multiple listeners in future. * * Results: - * pg_listeners is updated. + * pg_listeners is updated. * * Side effects: - * XXX + * 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; - + 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; + #ifdef ASYNC_DEBUG - elog(DEBUG,"Async_Listen: %s",relname); + elog(DEBUG, "Async_Listen: %s", relname); #endif - 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); - RelationSetLockForWrite(lDesc); - - /* 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; - } + for (i = 0; i < Natts_pg_listener; i++) + { + nulls[i] = ' '; + values[i] = PointerGetDatum(NULL); } - 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); - }*/ - - RelationUnsetLockForWrite(lDesc); - 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 */ - strNcpy(relnamei, relname, NAMEDATALEN-1); - on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei); + + i = 0; + values[i++] = (Datum) relname; + values[i++] = (Datum) pid; + values[i++] = (Datum) 0; /* no notifies pending */ + + lDesc = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lDesc); + + /* 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); } + */ + + RelationUnsetLockForWrite(lDesc); + 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 */ + strNcpy(relnamei, relname, NAMEDATALEN - 1); + 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. + * 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. + * pg_listeners is updated. * * Side effects: - * XXX + * XXX * *-------------------------------------------------------------- */ static void Async_Unlisten(char *relname, int pid) { - Relation lDesc; - HeapTuple lTuple; - - lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname), - Int32GetDatum(pid), - 0,0); - lDesc = heap_openr(ListenerRelationName); - RelationSetLockForWrite(lDesc); - - if (lTuple != NULL) { - heap_delete(lDesc,&lTuple->t_ctid); - } - - RelationUnsetLockForWrite(lDesc); - heap_close(lDesc); + Relation lDesc; + HeapTuple lTuple; + + lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname), + Int32GetDatum(pid), + 0, 0); + lDesc = heap_openr(ListenerRelationName); + RelationSetLockForWrite(lDesc); + + if (lTuple != NULL) + { + heap_delete(lDesc, &lTuple->t_ctid); + } + + RelationUnsetLockForWrite(lDesc); + heap_close(lDesc); } static void Async_UnlistenOnExit(int code, /* from exitpg */ - char *relname) + char *relname) { - Async_Unlisten((char *) relname, getpid()); + 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. + * 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. + * We remove the notification flag from the pg_listener tuple + * associated with our process. * * Results: - * XXX + * 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. + * 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; +GlobalMemory notifyContext = NULL; static 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; - + 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; + #ifdef ASYNC_DEBUG - elog(DEBUG, "Async_NotifyFrontEnd: notifying front end."); + elog(DEBUG, "Async_NotifyFrontEnd: notifying front end."); #endif - - 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); - RelationSetLockForWrite(lRel); - 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); - 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"); + + 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); + RelationSetLockForWrite(lRel); + 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); + 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); } - ReleaseBuffer(b); - } - CommitTransactionCommand(); + CommitTransactionCommand(); } static int AsyncExistsPendingNotify(char *relname) { - Dlelem* p; - for (p = DLGetHead(pendingNotifies); - p != NULL; - p = DLGetSucc(p)) { - /* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */ - if (!strncmp(DLE_VAL(p), relname, NAMEDATALEN)) - return 1; - } - - return 0; + Dlelem *p; + + for (p = DLGetHead(pendingNotifies); + p != NULL; + p = DLGetSucc(p)) + { + /* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */ + if (!strncmp(DLE_VAL(p), relname, NAMEDATALEN)) + return 1; + } + + return 0; } static void ClearPendingNotify() { - Dlelem* p; - while ( (p = DLRemHead(pendingNotifies)) != NULL) - free(DLE_VAL(p)); -} + Dlelem *p; + while ((p = DLRemHead(pendingNotifies)) != NULL) + free(DLE_VAL(p)); +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 65a9c041643..2b18cb46df0 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1,20 +1,20 @@ /*------------------------------------------------------------------------- * * cluster.c-- - * Paul Brown's implementation of cluster index. + * 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] + * 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.13 1997/08/19 21:30:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.14 1997/09/07 04:40:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -47,307 +47,323 @@ #include <optimizer/internal.h> #ifndef NO_SECURITY #include <utils/acl.h> -#endif /* !NO_SECURITY */ +#endif /* !NO_SECURITY */ static Relation copy_heap(Oid OIDOldHeap); -static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap); -static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); +static void copy_index(Oid OIDOldIndex, Oid OIDNewHeap); +static void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex); /* * 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 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. + * 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 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. + * 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[NAMEDATALEN]; - char NewHeapName[NAMEDATALEN]; - char saveoldrelname[NAMEDATALEN]; - char saveoldindexname[NAMEDATALEN]; - - - /* Save the old names because they will get lost when the old relations - * are destroyed. - */ - strcpy(saveoldrelname, oldrelname); - strcpy(saveoldindexname, oldindexname); - - /* - * - * 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\"", - 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\"", - 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; - strcpy(NewHeapName,NewHeap->rd_rel->relname.data); - - - /* To make the new heap visible (which is until now empty). */ - CommandCounterIncrement(); - - rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex); - - /* To flush the filled new heap (and the statistics about it). */ - CommandCounterIncrement(); - - /* Create new index over the tuples of the new heap. */ - copy_index(OIDOldIndex, OIDNewHeap); - sprintf(NewIndexName, "temp_%x", OIDOldIndex); - - /* - * make this really happen. Flush all the buffers. - * (Believe me, it is necessary ... ended up in a mess without it.) - */ - CommitTransactionCommand(); - StartTransactionCommand(); - - - /* Destroy old heap (along with its index) and rename new. */ - heap_destroy(oldrelname); - - renamerel(NewHeapName, saveoldrelname); - TypeRename(NewHeapName, saveoldrelname); - - renamerel(NewIndexName, saveoldindexname); - - /* - * Again flush all the buffers. - */ - CommitTransactionCommand(); - StartTransactionCommand(); + Oid OIDOldHeap, + OIDOldIndex, + OIDNewHeap; + + Relation OldHeap, + OldIndex; + Relation NewHeap; + + char NewIndexName[NAMEDATALEN]; + char NewHeapName[NAMEDATALEN]; + char saveoldrelname[NAMEDATALEN]; + char saveoldindexname[NAMEDATALEN]; + + + /* + * Save the old names because they will get lost when the old + * relations are destroyed. + */ + strcpy(saveoldrelname, oldrelname); + strcpy(saveoldindexname, oldindexname); + + /* + * 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\"", + 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\"", + 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; + strcpy(NewHeapName, NewHeap->rd_rel->relname.data); + + + /* To make the new heap visible (which is until now empty). */ + CommandCounterIncrement(); + + rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex); + + /* To flush the filled new heap (and the statistics about it). */ + CommandCounterIncrement(); + + /* Create new index over the tuples of the new heap. */ + copy_index(OIDOldIndex, OIDNewHeap); + sprintf(NewIndexName, "temp_%x", OIDOldIndex); + + /* + * make this really happen. Flush all the buffers. (Believe me, it is + * necessary ... ended up in a mess without it.) + */ + CommitTransactionCommand(); + StartTransactionCommand(); + + + /* Destroy old heap (along with its index) and rename new. */ + heap_destroy(oldrelname); + + renamerel(NewHeapName, saveoldrelname); + TypeRename(NewHeapName, saveoldrelname); + + renamerel(NewIndexName, saveoldindexname); + + /* + * Again flush all the buffers. + */ + CommitTransactionCommand(); + StartTransactionCommand(); } -static Relation +static 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; + 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; } static 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); /* 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) { - finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); - 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, - NULL, /* type info is in the old index */ - Old_pg_index_relation_Form->relam, - natts, - Old_pg_index_Form->indkey, - Old_pg_index_Form->indclass, - (uint16)0, (Datum) NULL, NULL, - Old_pg_index_Form->indislossy, - Old_pg_index_Form->indisunique); - - heap_close(OldIndex); - heap_close(NewHeap); + 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); /* 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) + { + finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo)); + 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, + NULL, /* type info is in the old index */ + Old_pg_index_relation_Form->relam, + natts, + Old_pg_index_Form->indkey, + Old_pg_index_Form->indclass, + (uint16) 0, (Datum) NULL, NULL, + Old_pg_index_Form->indislossy, + Old_pg_index_Form->indisunique); + + heap_close(OldIndex); + heap_close(NewHeap); } static 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_endscan(ScanDesc); - - index_close(LocalOldIndex); - heap_close(LocalOldHeap); - heap_close(LocalNewHeap); + 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_endscan(ScanDesc); + + index_close(LocalOldIndex); + heap_close(LocalOldHeap); + heap_close(LocalNewHeap); } - diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 376bd3ae5fa..7af9b37c072 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -1,29 +1,29 @@ /*------------------------------------------------------------------------- * * command.c-- - * random postgres portal and utility support code + * 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.13 1997/08/22 14:22:07 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.14 1997/09/07 04:40:38 momjian 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. - * + * 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 <postgres.h> #include <access/relscan.h> -#include <utils/portal.h> +#include <utils/portal.h> #include <commands/command.h> #include <utils/mcxt.h> #include <executor/executor.h> @@ -31,7 +31,7 @@ #include <catalog/indexing.h> #include <utils/syscache.h> #include <catalog/catalog.h> -#include <access/heapam.h> +#include <access/heapam.h> #include <utils/array.h> #include <utils/acl.h> #include <optimizer/prep.h> @@ -41,443 +41,468 @@ #include <utils/builtins.h> /* ---------------- - * PortalExecutorHeapMemory stuff + * PortalExecutorHeapMemory stuff * - * This is where the XXXSuperDuperHacky code was. -cim 3/15/90 + * This is where the XXXSuperDuperHacky code was. -cim 3/15/90 * ---------------- */ -MemoryContext PortalExecutorHeapMemory = NULL; +MemoryContext PortalExecutorHeapMemory = NULL; /* -------------------------------- - * PortalCleanup + * 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 - * ---------------- - */ - MemoryContextSwitchTo(context); - PortalExecutorHeapMemory = (MemoryContext) NULL; + 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 + * ---------------- + */ + MemoryContextSwitchTo(context); + PortalExecutorHeapMemory = (MemoryContext) NULL; } /* -------------------------------- - * PerformPortalFetch + * PerformPortalFetch * -------------------------------- */ void PerformPortalFetch(char *name, - bool forward, - int count, - char *tag, - CommandDest dest) + 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", - 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. - * ---------------- - */ - MemoryContextSwitchTo( - (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL))); + 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", + 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. + * ---------------- + */ + MemoryContextSwitchTo( + (MemoryContext) PortalGetHeapMemory(GetPortalByName(NULL))); } /* -------------------------------- - * PerformPortalClose + * 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", - name); - return; - } - - /* ---------------- - * Note: PortalCleanup is called as a side-effect - * ---------------- - */ - PortalDestroy(&portal); + 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", + name); + return; + } + + /* ---------------- + * Note: PortalCleanup is called as a side-effect + * ---------------- + */ + PortalDestroy(&portal); } /* ---------------- - * PerformAddAttribute + * PerformAddAttribute * - * adds an additional attribute to a relation + * 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??? + * 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.) + * (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. + * 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 + * 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", - 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", + relationName); #ifndef NO_SECURITY - if (!pg_ownercheck(userName, relationName, RELNAME)) - elog(WARN, "PerformAddAttribute: you do not own class \"%s\"", - relationName); + if (!pg_ownercheck(userName, relationName, RELNAME)) + elog(WARN, "PerformAddAttribute: you do not own class \"%s\"", + relationName); #endif - /* - * we can't add a not null attribute - */ - if (colDef->is_not_null) - elog(WARN,"Can't add a not null attribute to a existent relation"); - if (colDef->defval) - elog(WARN,"ADD ATTRIBUTE: DEFAULT is not implemented, yet"); - /* - * 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\"", - 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); + + /* + * we can't add a not null attribute + */ + if (colDef->is_not_null) + elog(WARN, "Can't add a not null attribute to a existent relation"); + if (colDef->defval) + elog(WARN, "ADD ATTRIBUTE: DEFAULT is not implemented, yet"); + + /* + * 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\"", + 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); + } } - PerformAddAttribute((relrdesc->rd_rel->relname).data, - userName, false, colDef); + } + + relrdesc = heap_openr(RelationRelationName); + reltup = ClassNameIndexScan(relrdesc, relationName); + + if (!PointerIsValid(reltup)) + { heap_close(relrdesc); - } + elog(WARN, "PerformAddAttribute: relation \"%s\" not found", + relationName); } - } - - 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.attdisbursion = 0; /* XXX temporary */ - 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 + * XXX is the following check sufficient? */ - 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; + 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; } - heap_endscan(attsdesc); - + + attrdesc = heap_openr(AttributeRelationName); + + Assert(attrdesc); + Assert(RelationGetRelationTupleForm(attrdesc)); + /* - * check to see if it is an array attribute. + * Open all (if any) pg_attribute indices */ - - 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\" nonexistent", p); + 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.attdisbursion = 0; /* XXX temporary */ + 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\" nonexistent", p); + } + namestrcpy(&(attribute->attname), (char *) key[1].sk_argument); + attribute->atttypid = typeTuple->t_oid; + if (colDef->typename->typlen > 0) + attribute->attlen = colDef->typename->typlen; + else +/* bpchar, varchar, text */ + 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; + attribute->attnotnull = false; + + heap_insert(attrdesc, attributeTuple); + if (hasindex) + CatalogIndexInsert(idescs, + Num_pg_attr_indices, + attrdesc, + attributeTuple); } - namestrcpy(&(attribute->attname), (char*) key[1].sk_argument); - attribute->atttypid = typeTuple->t_oid; - if (colDef->typename->typlen > 0) - attribute->attlen = colDef->typename->typlen; - else /* bpchar, varchar, text */ - 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; - attribute->attnotnull = false; - - 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; - 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); + CatalogCloseIndices(Num_pg_attr_indices, idescs); + heap_close(attrdesc); + + ((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts; + oldTID = reltup->t_ctid; + heap_replace(relrdesc, &oldTID, reltup); + + /* keep catalog indices current */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); + CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup); + CatalogCloseIndices(Num_pg_class_indices, ridescs); + + pfree(reltup); + heap_close(relrdesc); } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 687cd1eb12e..795e9f5584f 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.29 1997/09/04 13:18:59 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.30 1997/09/07 04:40:40 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -42,736 +42,857 @@ /* non-export function prototypes */ -static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim); -static void CopyFrom(Relation rel, bool binary, bool oids, 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 void CopyTo(Relation rel, bool binary, bool oids, FILE * fp, char *delim); +static void CopyFrom(Relation rel, bool binary, bool oids, 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); + #ifdef COPY_PATCH -static void CopyReadNewline(FILE *fp, int *newline); -static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline); +static void CopyReadNewline(FILE * fp, int *newline); +static char *CopyReadAttribute(FILE * fp, bool * isnull, char *delim, int *newline); + #else -static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim); +static char *CopyReadAttribute(FILE * fp, bool * isnull, char *delim); + #endif -static void CopyAttributeOut(FILE *fp, char *string, char *delim); -static int CountTuples(Relation relation); +static void CopyAttributeOut(FILE * fp, char *string, char *delim); +static int CountTuples(Relation relation); -extern FILE *Pfout, *Pfin; +extern FILE *Pfout, + *Pfin; #ifdef COPY_DEBUG -static int lineno; +static int lineno; + #endif - + /* - * DoCopy executes a the SQL COPY statement. + * DoCopy executes a the SQL COPY statement. */ void -DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, - char *filename, char *delim) { +DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, + char *filename, char *delim) +{ /*---------------------------------------------------------------------------- Either unload or reload contents of class <relname>, depending on <from>. If <pipe> is false, transfer is between the class and the file named <filename>. Otherwise, transfer is between the class and our regular - input/output stream. The latter could be either stdin/stdout or a + input/output stream. The latter could be either stdin/stdout or a socket, depending on whether we're running under Postmaster control. Iff <binary>, unload or reload in the binary format, as opposed to the - more wasteful but more robust and portable text format. + more wasteful but more robust and portable text format. - If in the text format, delimit columns with delimiter <delim>. + If in the text format, delimit columns with delimiter <delim>. When loading in the text format from an input stream (as opposed to - a file), recognize a "." on a line by itself as EOF. Also recognize + a file), recognize a "." on a line by itself as EOF. Also recognize a stream EOF. When unloading in the text format to an output stream, write a "." on a line by itself at the end of the data. Iff <oids>, unload or reload the format that includes OID information. Do not allow a Postgres user without superuser privilege to read from - or write to a file. + or write to a file. Do not allow the copy if user doesn't have proper permission to access the class. ----------------------------------------------------------------------------*/ - FILE *fp; - Relation rel; - extern char *UserName; /* defined in global.c */ - const AclMode required_access = from ? ACL_WR : ACL_RD; - int result; - - rel = heap_openr(relname); - if (rel == NULL) elog(WARN, "COPY command failed. Class %s " - "does not exist.", relname); - - result = pg_aclcheck(relname, UserName, required_access); - if(result != ACLCHECK_OK) - elog(WARN, "%s: %s", relname, aclcheck_error_strings[result]); - /* Above should not return */ - else if (!superuser() && !pipe) - elog(WARN, "You must have Postgres superuser privilege to do a COPY " - "directly to or from a file. Anyone can COPY to stdout or " - "from stdin. Psql's \\copy command also works for anyone."); - /* Above should not return. */ - else { - if (from) { /* copy from file to database */ - if ( rel->rd_rel->relkind == RELKIND_SEQUENCE ) - elog (WARN, "You can't change sequence relation %s", relname); - if (pipe) { - if (IsUnderPostmaster) { - ReceiveCopyBegin(); - fp = Pfin; - } else fp = stdin; - } else { - fp = AllocateFile(filename, "r"); - if (fp == NULL) - elog(WARN, "COPY command, running in backend with " - "effective uid %d, could not open file '%s' for " - "reading. Errno = %s (%d).", - geteuid(), filename, strerror(errno), errno); - /* Above should not return */ - } - CopyFrom(rel, binary, oids, fp, delim); - } else { /* copy from database to file */ - if (pipe) { - if (IsUnderPostmaster) { - SendCopyBegin(); - fp = Pfout; - } else fp = stdout; - } else { - mode_t oumask; /* Pre-existing umask value */ - oumask = umask((mode_t) 0); - fp = AllocateFile(filename, "w"); - umask(oumask); - if (fp == NULL) - elog(WARN, "COPY command, running in backend with " - "effective uid %d, could not open file '%s' for " - "writing. Errno = %s (%d).", - geteuid(), filename, strerror(errno), errno); - /* Above should not return */ - } - CopyTo(rel, binary, oids, fp, delim); - } - if (!pipe) - FreeFile(fp); - else if (!from && !binary) { - fputs("\\.\n", fp); - if (IsUnderPostmaster) fflush(Pfout); - } - } + FILE *fp; + Relation rel; + extern char *UserName; /* defined in global.c */ + const AclMode required_access = from ? ACL_WR : ACL_RD; + int result; + + rel = heap_openr(relname); + if (rel == NULL) + elog(WARN, "COPY command failed. Class %s " + "does not exist.", relname); + + result = pg_aclcheck(relname, UserName, required_access); + if (result != ACLCHECK_OK) + elog(WARN, "%s: %s", relname, aclcheck_error_strings[result]); + /* Above should not return */ + else if (!superuser() && !pipe) + elog(WARN, "You must have Postgres superuser privilege to do a COPY " + "directly to or from a file. Anyone can COPY to stdout or " + "from stdin. Psql's \\copy command also works for anyone."); + /* Above should not return. */ + else + { + if (from) + { /* copy from file to database */ + if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + elog(WARN, "You can't change sequence relation %s", relname); + if (pipe) + { + if (IsUnderPostmaster) + { + ReceiveCopyBegin(); + fp = Pfin; + } + else + fp = stdin; + } + else + { + fp = AllocateFile(filename, "r"); + if (fp == NULL) + elog(WARN, "COPY command, running in backend with " + "effective uid %d, could not open file '%s' for " + "reading. Errno = %s (%d).", + geteuid(), filename, strerror(errno), errno); + /* Above should not return */ + } + CopyFrom(rel, binary, oids, fp, delim); + } + else + { /* copy from database to file */ + if (pipe) + { + if (IsUnderPostmaster) + { + SendCopyBegin(); + fp = Pfout; + } + else + fp = stdout; + } + else + { + mode_t oumask; /* Pre-existing umask value */ + + oumask = umask((mode_t) 0); + fp = AllocateFile(filename, "w"); + umask(oumask); + if (fp == NULL) + elog(WARN, "COPY command, running in backend with " + "effective uid %d, could not open file '%s' for " + "writing. Errno = %s (%d).", + geteuid(), filename, strerror(errno), errno); + /* Above should not return */ + } + CopyTo(rel, binary, oids, fp, delim); + } + if (!pipe) + FreeFile(fp); + else if (!from && !binary) + { + fputs("\\.\n", fp); + if (IsUnderPostmaster) + fflush(Pfout); + } + } } static void -CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim) +CopyTo(Relation rel, bool binary, bool oids, 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; /* The attribute we are copying is null */ - char *nulls; - /* <nulls> is a (dynamically allocated) array with one character - per attribute in the instance being copied. nulls[I-1] is - 'n' if Attribute Number I is null, and ' ' otherwise. - - <nulls> is meaningful only if we are doing a binary copy. - */ - 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); - } - nulls = NULL; /* meaningless, but compiler doesn't know that */ - }else { - elements = NULL; - out_functions = NULL; - 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)) { - - if (oids && !binary) { - fputs(oidout(tuple->t_oid),fp); - fputc(delim[0], fp); - } - - 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); - } - else - fputs("\\N", fp); /* null indicator */ - - 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); - if (oids) - fwrite((char *) &tuple->t_oid, 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); + 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; /* The attribute we are copying is null */ + char *nulls; + + /* + * <nulls> is a (dynamically allocated) array with one character per + * attribute in the instance being copied. nulls[I-1] is 'n' if + * Attribute Number I is null, and ' ' otherwise. + * + * <nulls> is meaningful only if we are doing a binary copy. + */ + 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); + } + nulls = NULL; /* meaningless, but compiler doesn't know + * that */ + } + else + { + elements = NULL; + out_functions = NULL; + 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)) + { + + if (oids && !binary) + { + fputs(oidout(tuple->t_oid), fp); + fputc(delim[0], fp); + } + + 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); + } + else + fputs("\\N", fp); /* null indicator */ + + 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); + if (oids) + fwrite((char *) &tuple->t_oid, 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, bool oids, FILE *fp, char *delim) +CopyFrom(Relation rel, bool binary, bool oids, FILE * fp, char *delim) { - HeapTuple tuple; - 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 = NULL, *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 = NULL; - TupleDesc *itupdescArr; - HeapTuple pgIndexTup; - IndexTupleForm *pgIndexP = NULL; - int *indexNatts = NULL; - char *predString; - Node **indexPred = NULL; - TupleDesc rtupdesc; - ExprContext *econtext = NULL; + HeapTuple tuple; + 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 = NULL, + *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 = NULL; + TupleDesc *itupdescArr; + HeapTuple pgIndexTup; + IndexTupleForm *pgIndexP = NULL; + int *indexNatts = NULL; + char *predString; + Node **indexPred = NULL; + TupleDesc rtupdesc; + ExprContext *econtext = NULL; + #ifndef OMIT_PARTIAL_INDEX - TupleTable tupleTable; - TupleTableSlot *slot = NULL; + TupleTable tupleTable; + TupleTableSlot *slot = NULL; + #endif - int natts; - AttrNumber *attnumP; - Datum *idatum; - int n_indices; - InsertIndexResult indexRes; - TupleDesc tupDesc; - Oid loaded_oid; - bool skip_tuple = false; - - 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) { + int natts; + AttrNumber *attnumP; + Datum *idatum; + int n_indices; + InsertIndexResult indexRes; + TupleDesc tupDesc; + Oid loaded_oid; + bool skip_tuple = false; + + 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 - { - in_functions = NULL; - elements = NULL; - 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); - idatum = (Datum *) palloc(sizeof(Datum) * 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); - } - + 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 + { + in_functions = NULL; + elements = NULL; + 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); + idatum = (Datum *) palloc(sizeof(Datum) * 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); + } + #ifdef COPY_DEBUG - lineno = 0; + lineno = 0; #endif - while (!done) { - if (!binary) { + while (!done) + { + if (!binary) + { #ifdef COPY_PATCH - int newline = 0; + int newline = 0; + #endif #ifdef COPY_DEBUG - lineno++; - elog(DEBUG, "line %d", lineno); + lineno++; + elog(DEBUG, "line %d", lineno); #endif - if (oids) { + if (oids) + { #ifdef COPY_PATCH - string = CopyReadAttribute(fp, &isnull, delim, &newline); + string = CopyReadAttribute(fp, &isnull, delim, &newline); #else - string = CopyReadAttribute(fp, &isnull, delim); + string = CopyReadAttribute(fp, &isnull, delim); #endif - if (string == NULL) - done = 1; - else { - loaded_oid = oidin(string); - if (loaded_oid < BootstrapObjectIdData) - elog(WARN, "COPY TEXT: Invalid Oid"); - } - } - for (i = 0; i < attr_count && !done; i++) { + if (string == NULL) + done = 1; + else + { + loaded_oid = oidin(string); + if (loaded_oid < BootstrapObjectIdData) + elog(WARN, "COPY TEXT: Invalid Oid"); + } + } + for (i = 0; i < attr_count && !done; i++) + { #ifdef COPY_PATCH - string = CopyReadAttribute(fp, &isnull, delim, &newline); + string = CopyReadAttribute(fp, &isnull, delim, &newline); #else - string = CopyReadAttribute(fp, &isnull, delim); + string = CopyReadAttribute(fp, &isnull, delim); #endif - 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)) { + 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)) + { #ifdef COPY_DEBUG - elog(WARN, - "copy from: line %d - Bad file format", lineno); + elog(WARN, + "copy from: line %d - Bad file format", lineno); #else - elog(WARN, "copy from: Bad file format"); + elog(WARN, "copy from: Bad file format"); #endif - } - } - } + } + } + } #ifdef COPY_PATCH - if (!done) { - CopyReadNewline(fp, &newline); - } + if (!done) + { + CopyReadNewline(fp, &newline); + } #endif - }else { /* binary */ - fread(&len, sizeof(int32), 1, fp); - if (feof(fp)) { - done = 1; - }else { - if (oids) { - fread(&loaded_oid, sizeof(int32), 1, fp); - if (loaded_oid < BootstrapObjectIdData) - elog(WARN, "COPY BINARY: Invalid Oid"); - } - 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; + } + else + { /* binary */ + fread(&len, sizeof(int32), 1, fp); + if (feof(fp)) + { + done = 1; + } + else + { + if (oids) + { + fread(&loaded_oid, sizeof(int32), 1, fp); + if (loaded_oid < BootstrapObjectIdData) + elog(WARN, "COPY BINARY: Invalid Oid"); + } + 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'; + } + } - /* - * Does it have any sence ? - vadim 12/14/96 - * - tupDesc = CreateTupleDesc(attr_count, attr); - */ - tuple = heap_formtuple(tupDesc, values, nulls); - if (oids) - tuple->t_oid = loaded_oid; - - skip_tuple = false; - /* BEFORE ROW INSERT Triggers */ - if ( rel->trigdesc && - rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0 ) - { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers (rel, tuple); - - if ( newtuple == NULL ) /* "do nothing" */ - skip_tuple = true; - else if ( newtuple != tuple ) /* modified by Trigger(s) */ - { - pfree (tuple); - tuple = newtuple; - } - } - - if ( !skip_tuple ) - { - /* ---------------- - * Check the constraints of a tuple - * ---------------- - */ - - if ( rel->rd_att->constr ) - { - HeapTuple newtuple; - - newtuple = ExecConstraints ("CopyFrom", rel, tuple); - - if ( newtuple != tuple ) - { - pfree (tuple); - tuple = newtuple; - } - } - - heap_insert(rel, tuple); - - if (has_index) - { - for (i = 0; i < n_indices; i++) - { - if (indexPred[i] != NULL) - { + 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; + + /* + * Does it have any sence ? - vadim 12/14/96 + * + * tupDesc = CreateTupleDesc(attr_count, attr); + */ + tuple = heap_formtuple(tupDesc, values, nulls); + if (oids) + tuple->t_oid = loaded_oid; + + skip_tuple = false; + /* BEFORE ROW INSERT Triggers */ + if (rel->trigdesc && + rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + { + HeapTuple newtuple; + + newtuple = ExecBRInsertTriggers(rel, tuple); + + if (newtuple == NULL) /* "do nothing" */ + skip_tuple = true; + else if (newtuple != tuple) /* modified by Trigger(s) */ + { + pfree(tuple); + tuple = newtuple; + } + } + + if (!skip_tuple) + { + /* ---------------- + * Check the constraints of a tuple + * ---------------- + */ + + if (rel->rd_att->constr) + { + HeapTuple newtuple; + + newtuple = ExecConstraints("CopyFrom", rel, tuple); + + if (newtuple != tuple) + { + pfree(tuple); + tuple = newtuple; + } + } + + 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]); - indexRes = index_insert(index_rels[i], idatum, index_nulls, - &(tuple->t_ctid), rel); - if (indexRes) pfree(indexRes); - } - } - /* AFTER ROW INSERT Triggers */ - if ( rel->trigdesc && - rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 ) - ExecARInsertTriggers (rel, tuple); - } - - 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); + + /* + * 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]); + indexRes = index_insert(index_rels[i], idatum, index_nulls, + &(tuple->t_ctid), rel); + if (indexRes) + pfree(indexRes); + } + } + /* AFTER ROW INSERT Triggers */ + if (rel->trigdesc && + rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) + ExecARInsertTriggers(rel, tuple); + } + + 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 +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); + 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 +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); + 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 +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); + 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 +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); + 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(). @@ -779,71 +900,77 @@ IsTypeByVal(Oid type) * Space for the array itself is palloc'ed. */ -typedef struct rel_list { - Oid index_rel_oid; - struct rel_list *next; -} RelationList; +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) + 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); - - /* We cannot trust to relhasindex of the main_relation now, so... */ - if ( *n_indices == 0 ) - return; - - *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; - } + 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); + + /* We cannot trust to relhasindex of the main_relation now, so... */ + if (*n_indices == 0) + return; + + *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 @@ -851,20 +978,22 @@ GetIndexRelations(Oid main_relation_oid, /* returns 1 is c is in s */ -static bool -inString(char c, char* 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; + int i; + + if (s) + { + i = 0; + while (s[i] != '\0') + { + if (s[i] == c) + return 1; + i++; + } + } + return 0; } #ifdef COPY_PATCH @@ -873,19 +1002,21 @@ inString(char c, char* s) */ void -CopyReadNewline(FILE *fp, int *newline) +CopyReadNewline(FILE * fp, int *newline) { - if (!*newline) { + if (!*newline) + { #ifdef COPY_DEBUG - elog(NOTICE, "CopyReadNewline: line %d - extra fields ignored", - lineno); + elog(NOTICE, "CopyReadNewline: line %d - extra fields ignored", + lineno); #else - elog(NOTICE, "CopyReadNewline: line - extra fields ignored"); + elog(NOTICE, "CopyReadNewline: line - extra fields ignored"); #endif - while (!feof(fp) && (getc(fp) != '\n')); - } - *newline = 0; + while (!feof(fp) && (getc(fp) != '\n')); + } + *newline = 0; } + #endif /* @@ -895,148 +1026,167 @@ CopyReadNewline(FILE *fp, int *newline) * can be used as standard input. */ -static char * +static char * #ifdef COPY_PATCH -CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline) +CopyReadAttribute(FILE * fp, bool * isnull, char *delim, int *newline) #else -CopyReadAttribute(FILE *fp, bool *isnull, char *delim) +CopyReadAttribute(FILE * fp, bool * isnull, char *delim) #endif { - static char attribute[EXT_ATTLEN]; - char c; - int done = 0; - int i = 0; - + static char attribute[EXT_ATTLEN]; + char c; + int done = 0; + int i = 0; + #ifdef COPY_PATCH - /* if last delimiter was a newline return a NULL attribute */ - if (*newline) { - *isnull = (bool) true; - return(NULL); - } + /* if last delimiter was a newline return a NULL attribute */ + if (*newline) + { + *isnull = (bool) true; + return (NULL); + } #endif - *isnull = (bool) false; /* set default */ - if (feof(fp)) - return(NULL); - - while (!done) { - c = getc(fp); - - if (feof(fp)) - return(NULL); - else if (c == '\\') { - c = getc(fp); - if (feof(fp)) - return(NULL); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': { - int val; - val = VALUE(c); - c = getc(fp); - if (ISOCTAL(c)) { - val = (val<<3) + VALUE(c); - c = getc(fp); - if (ISOCTAL(c)) { - val = (val<<3) + VALUE(c); - } else { - if (feof(fp)) - return(NULL); - ungetc(c, fp); - } - } else { - if (feof(fp)) - return(NULL); - ungetc(c, fp); - } - c = val & 0377; - } - break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; - case 'n': - c = '\n'; - break; - case 'r': - c = '\r'; - break; - case 't': - c = '\t'; - break; - case 'v': - c = '\v'; - break; - case 'N': - attribute[0] = '\0'; /* just to be safe */ - *isnull = (bool) true; - break; - case '.': - c = getc(fp); - if (c != '\n') - elog(WARN, "CopyReadAttribute - end of record marker corrupted"); - return(NULL); - break; - } - }else if (inString(c,delim) || c == '\n') { + *isnull = (bool) false; /* set default */ + if (feof(fp)) + return (NULL); + + while (!done) + { + c = getc(fp); + + if (feof(fp)) + return (NULL); + else if (c == '\\') + { + c = getc(fp); + if (feof(fp)) + return (NULL); + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int val; + + val = VALUE(c); + c = getc(fp); + if (ISOCTAL(c)) + { + val = (val << 3) + VALUE(c); + c = getc(fp); + if (ISOCTAL(c)) + { + val = (val << 3) + VALUE(c); + } + else + { + if (feof(fp)) + return (NULL); + ungetc(c, fp); + } + } + else + { + if (feof(fp)) + return (NULL); + ungetc(c, fp); + } + c = val & 0377; + } + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'N': + attribute[0] = '\0'; /* just to be safe */ + *isnull = (bool) true; + break; + case '.': + c = getc(fp); + if (c != '\n') + elog(WARN, "CopyReadAttribute - end of record marker corrupted"); + return (NULL); + break; + } + } + else if (inString(c, delim) || c == '\n') + { #ifdef COPY_PATCH - if (c == '\n') { - *newline = 1; - } + if (c == '\n') + { + *newline = 1; + } #endif - done = 1; - } - if (!done) attribute[i++] = c; - if (i == EXT_ATTLEN - 1) - elog(WARN, "CopyReadAttribute - attribute length too long"); - } - attribute[i] = '\0'; - return(&attribute[0]); + done = 1; + } + if (!done) + attribute[i++] = c; + if (i == EXT_ATTLEN - 1) + elog(WARN, "CopyReadAttribute - attribute length too long"); + } + attribute[i] = '\0'; + return (&attribute[0]); } static void -CopyAttributeOut(FILE *fp, char *string, char *delim) +CopyAttributeOut(FILE * fp, char *string, char *delim) { - char c; - int is_array = false; - int len = strlen(string); - - /* XXX - This is a kludge, we should check the data type */ - if (len && (string[0] == '{') && (string[len-1] == '}')) - is_array = true; - - for ( ; (c = *string) != '\0'; string++) { - if (c == delim[0] || c == '\n' || - (c == '\\' && !is_array)) - fputc('\\', fp); - else - if (c == '\\' && is_array) - if (*(string+1) == '\\') { - /* translate \\ to \\\\ */ - fputc('\\', fp); - fputc('\\', fp); - fputc('\\', fp); - string++; - } else if (*(string+1) == '"') { - /* translate \" to \\\" */ - fputc('\\', fp); - fputc('\\', fp); - } - fputc(*string, fp); - } + char c; + int is_array = false; + int len = strlen(string); + + /* XXX - This is a kludge, we should check the data type */ + if (len && (string[0] == '{') && (string[len - 1] == '}')) + is_array = true; + + for (; (c = *string) != '\0'; string++) + { + if (c == delim[0] || c == '\n' || + (c == '\\' && !is_array)) + fputc('\\', fp); + else if (c == '\\' && is_array) + if (*(string + 1) == '\\') + { + /* translate \\ to \\\\ */ + fputc('\\', fp); + fputc('\\', fp); + fputc('\\', fp); + string++; + } + else if (*(string + 1) == '"') + { + /* translate \" to \\\" */ + fputc('\\', fp); + fputc('\\', fp); + } + fputc(*string, fp); + } } /* - * Returns the number of tuples in a relation. Unfortunately, currently + * 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. @@ -1044,17 +1194,17 @@ CopyAttributeOut(FILE *fp, char *string, char *delim) 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); + HeapScanDesc scandesc; + HeapTuple tuple; + + int i; + + scandesc = heap_beginscan(relation, 0, NULL, 0, NULL); + + for (tuple = heap_getnext(scandesc, 0, NULL), i = 0; + tuple != NULL; + tuple = heap_getnext(scandesc, 0, NULL), i++) + ; + heap_endscan(scandesc); + return (i); } diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c index 248aaa3e768..92641ca70d6 100644 --- a/src/backend/commands/creatinh.c +++ b/src/backend/commands/creatinh.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * creatinh.c-- - * POSTGRES create/destroy relation with inheritance utility code. + * 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.14 1997/08/22 03:03:56 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.15 1997/09/07 04:40:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -29,623 +29,661 @@ #include <catalog/pg_ipl.h> /* ---------------- - * local stuff + * local stuff * ---------------- */ -static int checkAttrExists(char *attributeName, - char *attributeType, List *schema); -static List *MergeAttributes(List *schema, List *supers, List **supconstr); -static void StoreCatalogInheritance(Oid relationId, List *supers); +static int +checkAttrExists(char *attributeName, + char *attributeType, List * schema); +static List *MergeAttributes(List * schema, List * supers, List ** supconstr); +static void StoreCatalogInheritance(Oid relationId, List * supers); /* ---------------------------------------------------------------- - * DefineRelation -- - * Creates a new relation. + * DefineRelation -- + * Creates a new relation. * ---------------------------------------------------------------- */ void -DefineRelation(CreateStmt *stmt) +DefineRelation(CreateStmt * stmt) { - char *relname = palloc(NAMEDATALEN); - List *schema = stmt->tableElts; - int numberOfAttributes; - Oid relationId; - char archChar; - List *inheritList = NULL; - char *archiveName = NULL; - TupleDesc descriptor; - List *constraints; - int heaploc, archloc; - - char* typename = NULL; /* the typename of this relation. not useod for now */ - - if ( strlen(stmt->relname) >= NAMEDATALEN) - elog(WARN, "the relation name %s is >= %d characters long", stmt->relname, - NAMEDATALEN); - strNcpy(relname,stmt->relname,NAMEDATALEN-1); /* make full length for copy */ - - /* ---------------- - * 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: + char *relname = palloc(NAMEDATALEN); + List *schema = stmt->tableElts; + int numberOfAttributes; + Oid relationId; + char archChar; + List *inheritList = NULL; + char *archiveName = NULL; + TupleDesc descriptor; + List *constraints; + int heaploc, + archloc; + + char *typename = NULL; /* the typename of this relation. + * not useod for now */ + + if (strlen(stmt->relname) >= NAMEDATALEN) + elog(WARN, "the relation name %s is >= %d characters long", stmt->relname, + NAMEDATALEN); + strNcpy(relname, stmt->relname, NAMEDATALEN - 1); /* make full length for + * copy */ + + /* ---------------- + * Handle parameters + * XXX parameter handling missing below. + * ---------------- + */ + inheritList = stmt->inhRelnames; + + /* ---------------- + * determine archive mode + * XXX use symbolic constants... + * ---------------- + */ 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); + + 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, &constraints); + constraints = nconc(constraints, stmt->constraints); + + numberOfAttributes = length(schema); + if (numberOfAttributes <= 0) + { + elog(WARN, "DefineRelation: %s", + "please inherit from a relation or define an attribute"); } - archloc = stmt->archiveLoc; - } - - /* ---------------- - * generate relation schema, including inherited attributes. - * ---------------- - */ - schema = MergeAttributes(schema, inheritList, &constraints); - constraints = nconc (constraints, stmt->constraints); - - 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); - - if ( constraints != NIL ) - { - List *entry; - int nconstr = length (constraints); - ConstrCheck *check = (ConstrCheck *) palloc (nconstr * sizeof (ConstrCheck)); - int ncheck = 0; - int i; - - foreach (entry, constraints) - { - ConstraintDef *cdef = (ConstraintDef *) lfirst (entry); - - if ( cdef->type == CONSTR_CHECK ) - { - if ( cdef->name != NULL ) - { - for (i = 0; i < ncheck; i++) - { - if ( strcmp (check[i].ccname, cdef->name) == 0 ) - elog (WARN, "DefineRelation: name (%s) of CHECK constraint duplicated", cdef->name); - } - check[ncheck].ccname = cdef->name; - } - else - { - check[ncheck].ccname = (char*) palloc (NAMEDATALEN); - sprintf (check[ncheck].ccname, "$%d", ncheck + 1); - } - check[ncheck].ccbin = NULL; - check[ncheck].ccsrc = (char*) cdef->def; - ncheck++; - } - } - if ( ncheck > 0 ) - { - if ( ncheck < nconstr ) - check = (ConstrCheck *) repalloc (check, ncheck * sizeof (ConstrCheck)); - if ( descriptor->constr == NULL ) - { - descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); - descriptor->constr->num_defval = 0; - descriptor->constr->has_not_null = false; - } - descriptor->constr->num_check = ncheck; - descriptor->constr->check = check; - } - } - - relationId = heap_create(relname, - typename, - archChar, - heaploc, - descriptor); - - StoreCatalogInheritance(relationId, inheritList); - - /* - * create an archive relation if necessary - */ - if (archChar != 'n') - { - TupleDesc tupdesc; + + /* ---------------- + * create a relation descriptor from the relation schema + * and create the relation. + * ---------------- + */ + descriptor = BuildDescForRelation(schema, relname); + + if (constraints != NIL) + { + List *entry; + int nconstr = length(constraints); + ConstrCheck *check = (ConstrCheck *) palloc(nconstr * sizeof(ConstrCheck)); + int ncheck = 0; + int i; + + foreach(entry, constraints) + { + ConstraintDef *cdef = (ConstraintDef *) lfirst(entry); + + if (cdef->type == CONSTR_CHECK) + { + if (cdef->name != NULL) + { + for (i = 0; i < ncheck; i++) + { + if (strcmp(check[i].ccname, cdef->name) == 0) + elog(WARN, "DefineRelation: name (%s) of CHECK constraint duplicated", cdef->name); + } + check[ncheck].ccname = cdef->name; + } + else + { + check[ncheck].ccname = (char *) palloc(NAMEDATALEN); + sprintf(check[ncheck].ccname, "$%d", ncheck + 1); + } + check[ncheck].ccbin = NULL; + check[ncheck].ccsrc = (char *) cdef->def; + ncheck++; + } + } + if (ncheck > 0) + { + if (ncheck < nconstr) + check = (ConstrCheck *) repalloc(check, ncheck * sizeof(ConstrCheck)); + if (descriptor->constr == NULL) + { + descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); + descriptor->constr->num_defval = 0; + descriptor->constr->has_not_null = false; + } + descriptor->constr->num_check = ncheck; + descriptor->constr->check = check; + } + } + + relationId = heap_create(relname, + typename, + archChar, + heaploc, + descriptor); + + StoreCatalogInheritance(relationId, inheritList); + /* - * Need to create an archive relation for this heap relation. - * We cobble up the command by hand, and increment the command - * counter ourselves. + * create an archive relation if necessary */ - - CommandCounterIncrement(); - archiveName = MakeArchiveName(relationId); - - tupdesc = CreateTupleDescCopy (descriptor); /* get rid of constraints */ - (void) heap_create(archiveName, - typename, - 'n', /* archive isn't archived */ - archloc, - tupdesc); - - FreeTupleDesc (tupdesc); - FreeTupleDesc (descriptor); - pfree(archiveName); - } + if (archChar != 'n') + { + TupleDesc tupdesc; + + /* + * 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); + + tupdesc = CreateTupleDescCopy(descriptor); /* get rid of + * constraints */ + (void) heap_create(archiveName, + typename, + 'n', /* archive isn't archived */ + archloc, + tupdesc); + + FreeTupleDesc(tupdesc); + FreeTupleDesc(descriptor); + pfree(archiveName); + } } /* * RemoveRelation -- - * Deletes a new relation. + * Deletes a new relation. * * Exceptions: - * BadArg if name is invalid. + * BadArg if name is invalid. * * Note: - * If the relation has indices defined on it, then the index relations + * 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); + AssertArg(name); + heap_destroy(name); } /* * MergeAttributes -- - * Returns new schema given initial schema and supers. + * 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. + * 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. + * 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: + * 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); + * 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: + * 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} + * 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 **supconstr) +static List * +MergeAttributes(List * schema, List * supers, List ** supconstr) { - List *entry; - List *inhSchema = NIL; - List *constraints = 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; - TupleConstr *constr; - - relation = heap_openr(name); - if (relation==NULL) { - elog(WARN, - "MergeAttr: Can't inherit from non-existent superclass '%s'", - name); - } - if ( relation->rd_rel->relkind == 'S' ) + List *entry; + List *inhSchema = NIL; + List *constraints = NIL; + + /* + * Validates that there are no duplications. Validity checking of + * types occurs later. + */ + foreach(entry, schema) { - elog(WARN, "MergeAttr: Can't inherit from sequence superclass '%s'", - name); - } - tupleDesc = RelationGetTupleDescriptor(relation); - constr = tupleDesc->constr; - - 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, type and constraints - */ - 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; - def->is_not_null = attribute->attnotnull; - def->defval = NULL; - if ( attribute->atthasdef ) - { - AttrDefault *attrdef = constr->defval; - int i; - - Assert ( constr != NULL && constr->num_defval > 0 ); - - for (i = 0; i < constr->num_defval; i++) - { - if ( attrdef[i].adnum != attrno + 1 ) - continue; - def->defval = pstrdup (attrdef[i].adsrc); - break; - } - Assert ( def->defval != NULL ); - } - partialResult = lcons(def, partialResult); + 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); + } + } } - - if ( constr && constr->num_check > 0 ) + foreach(entry, supers) { - ConstrCheck *check = constr->check; - int i; - - for (i = 0; i < constr->num_check; i++) - { - ConstraintDef *cdef = (ConstraintDef *) palloc (sizeof (ConstraintDef)); - - cdef->type = CONSTR_CHECK; - if ( check[i].ccname[0] == '$' ) - cdef->name = NULL; - else - cdef->name = pstrdup (check[i].ccname); - cdef->def = (void*) pstrdup (check[i].ccsrc); - constraints = lappend (constraints, cdef); - } + List *rest; + + foreach(rest, lnext(entry)) + { + if (!strcmp(strVal(lfirst(entry)), strVal(lfirst(rest)))) + { + elog(WARN, "relation \"%s\" duplicated", + strVal(lfirst(entry))); + } + } } - + /* - * iteration cleanup and result collection + * merge the inherited attributes into the schema */ - heap_close(relation); + foreach(entry, supers) + { + char *name = strVal(lfirst(entry)); + Relation relation; + List *partialResult = NIL; + AttrNumber attrno; + TupleDesc tupleDesc; + TupleConstr *constr; + + relation = heap_openr(name); + if (relation == NULL) + { + elog(WARN, + "MergeAttr: Can't inherit from non-existent superclass '%s'", + name); + } + if (relation->rd_rel->relkind == 'S') + { + elog(WARN, "MergeAttr: Can't inherit from sequence superclass '%s'", + name); + } + tupleDesc = RelationGetTupleDescriptor(relation); + constr = tupleDesc->constr; + + 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, type and constraints + */ + 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; + def->is_not_null = attribute->attnotnull; + def->defval = NULL; + if (attribute->atthasdef) + { + AttrDefault *attrdef = constr->defval; + int i; + + Assert(constr != NULL && constr->num_defval > 0); + + for (i = 0; i < constr->num_defval; i++) + { + if (attrdef[i].adnum != attrno + 1) + continue; + def->defval = pstrdup(attrdef[i].adsrc); + break; + } + Assert(def->defval != NULL); + } + partialResult = lcons(def, partialResult); + } + + if (constr && constr->num_check > 0) + { + ConstrCheck *check = constr->check; + int i; + + for (i = 0; i < constr->num_check; i++) + { + ConstraintDef *cdef = (ConstraintDef *) palloc(sizeof(ConstraintDef)); + + cdef->type = CONSTR_CHECK; + if (check[i].ccname[0] == '$') + cdef->name = NULL; + else + cdef->name = pstrdup(check[i].ccname); + cdef->def = (void *) pstrdup(check[i].ccsrc); + constraints = lappend(constraints, cdef); + } + } + + /* + * 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); + } /* - * wants the inherited schema to appear in the order they are - * specified in CREATE TABLE + * put the inherited schema before our the schema for this table */ - inhSchema = nconc(inhSchema, partialResult); - } - - /* - * put the inherited schema before our the schema for this table - */ - schema = nconc(inhSchema, schema); - *supconstr = constraints; - return (schema); + schema = nconc(inhSchema, schema); + *supconstr = constraints; + return (schema); } /* * StoreCatalogInheritance -- - * Updates the system catalogs with proper inheritance information. + * Updates the system catalogs with proper inheritance information. */ static void -StoreCatalogInheritance(Oid relationId, List *supers) +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); - - 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) { + Relation relation; + TupleDesc desc; + int16 seqNumber; + List *entry; + List *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); + + /* ---------------- + * 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); + + heap_insert(relation, tuple); + pfree(tuple); + + seqNumber += 1; } - 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; - } + + 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; } - 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; + + /* ---------------- + * 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); - - heap_insert(relation, tuple); - pfree(tuple); - - seqNumber += 1; - } - - heap_close(relation); + + /* ---------------- + * 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); + + 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) +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; + 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; + return 0; } /* * MakeArchiveName - * make an archive rel name out of a regular rel name + * make an archive rel name out of a regular rel name * * the CALLER is responsible for freeing the memory allocated */ -char* +char * MakeArchiveName(Oid relationId) { - char *arch; + 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); + /* + * 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; + return arch; } diff --git a/src/backend/commands/defind.c b/src/backend/commands/defind.c index c6b293fec68..9b8c5a0218a 100644 --- a/src/backend/commands/defind.c +++ b/src/backend/commands/defind.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * defind.c-- - * POSTGRES define, extend and remove index code. + * 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.12 1997/03/26 03:05:28 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.13 1997/09/07 04:40:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,7 @@ #include <utils/relcache.h> #include <utils/lsyscache.h> #include <commands/defrem.h> -#include <parser/parsetree.h> /* for getrelid() */ +#include <parser/parsetree.h> /* for getrelid() */ #include <optimizer/prep.h> #include <optimizer/clauses.h> #include <storage/lmgr.h> @@ -39,508 +39,543 @@ #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, +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); -static char *GetDefaultOpClass(Oid atttypid); + 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); +static char *GetDefaultOpClass(Oid atttypid); /* * DefineIndex -- - * Creates a new index. + * Creates a new index. * * 'attributeList' is a list of IndexElem specifying either a functional - * index or a list of attributes to index on. + * 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 + * XXX */ void DefineIndex(char *heapRelationName, - char *indexRelationName, - char *accessMethodName, - List *attributeList, - List *parameterList, - bool unique, - Expr *predicate, - List *rangetable) + char *indexRelationName, + char *accessMethodName, + List * attributeList, + List * parameterList, + bool unique, + 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; - bool lossy = FALSE; - List *pl; - - /* - * 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; - - if (unique && strcmp(accessMethodName,"btree") != 0) - elog(WARN, "DefineIndex: unique indices are only available with the btree access method"); - - if (numberOfAttributes > 1 && strcmp(accessMethodName,"btree") != 0) - elog(WARN, "DefineIndex: multi-column indices are only available with the btree access method"); - - /* - * 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] - * - * WITH clause reinstated to handle lossy indices. - * -- JMH, 7/22/96 - */ - foreach(pl, parameterList) { - ParamString *param = (ParamString*)lfirst(pl); - - if (!strcasecmp(param->name, "islossy")) - lossy = TRUE; - } - - - /* - * 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); + Oid *classObjectId; + Oid accessMethodId; + Oid relationId; + int numberOfAttributes; + AttrNumber *attributeNumberA; + HeapTuple tuple; + uint16 parameterCount = 0; + Datum *parameterA = NULL; + FuncIndexInfo fInfo; + List *cnfPred = NULL; + bool lossy = FALSE; + List *pl; + + /* + * 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); } - - FIsetnArgs(&fInfo,nargs); + relationId = tuple->t_oid; - strcpy(FIgetname(&fInfo), funcIndex->name); + if (unique && strcmp(accessMethodName, "btree") != 0) + elog(WARN, "DefineIndex: unique indices are only available with the btree access method"); - 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, NULL, accessMethodId, - numberOfAttributes, attributeNumberA, - classObjectId, parameterCount, parameterA, (Node*)cnfPred, - lossy, unique); - }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, - attributeList, - accessMethodId, numberOfAttributes, attributeNumberA, - classObjectId, parameterCount, parameterA, (Node*)cnfPred, - lossy, unique); - } + if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0) + elog(WARN, "DefineIndex: multi-column indices are only available with the btree access method"); + + /* + * 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] + * + * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96 + */ + foreach(pl, parameterList) + { + ParamString *param = (ParamString *) lfirst(pl); + + if (!strcasecmp(param->name, "islossy")) + lossy = TRUE; + } + + + /* + * 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, NULL, accessMethodId, + numberOfAttributes, attributeNumberA, + classObjectId, parameterCount, parameterA, (Node *) cnfPred, + lossy, unique); + } + 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, + attributeList, + accessMethodId, numberOfAttributes, attributeNumberA, + classObjectId, parameterCount, parameterA, (Node *) cnfPred, + lossy, unique); + } } /* * ExtendIndex -- - * Extends a partial index. + * Extends a partial index. * * Exceptions: - * XXX + * XXX */ void -ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable) +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); + 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: 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); + { + 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). + * 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) +CheckPredicate(List * predList, List * rangeTable, Oid baseRelOid) { - List *item; - - foreach (item, predList) { - CheckPredExpr(lfirst(item), rangeTable, baseRelOid); - } + List *item; + + foreach(item, predList) + { + CheckPredExpr(lfirst(item), rangeTable, baseRelOid); + } } static void -CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid) +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); - } + 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) +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"); + 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) +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)) + 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); + 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); + *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; } - 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) +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; - AttributeTupleForm attform; - - attribute = lfirst(rest); - - 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); - } + List *rest; + HeapTuple tuple; - attform = (AttributeTupleForm)GETSTRUCT(tuple); - *attNumP++ = attform->attnum; - - if (attribute->class == NULL) { - /* no operator class specified, so find the default */ - attribute->class = GetDefaultOpClass(attform->atttypid); - if(attribute->class == NULL) { - elog(WARN, - "Can't find a default operator class for type %d.", - attform->atttypid); - } - } - - tuple = SearchSysCacheTuple(CLANAME, - PointerGetDatum(attribute->class), - 0,0,0); - - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "DefineIndex: %s class not found", - attribute->class); + /* + * process attributeList + */ + + for (rest = attList; rest != NIL; rest = lnext(rest)) + { + IndexElem *attribute; + AttributeTupleForm attform; + + attribute = lfirst(rest); + + 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); + } + + attform = (AttributeTupleForm) GETSTRUCT(tuple); + *attNumP++ = attform->attnum; + + if (attribute->class == NULL) + { + /* no operator class specified, so find the default */ + attribute->class = GetDefaultOpClass(attform->atttypid); + if (attribute->class == NULL) + { + elog(WARN, + "Can't find a default operator class for type %d.", + attform->atttypid); + } + } + + 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; } - *opOidP++ = tuple->t_oid; - } } -static char * +static char * GetDefaultOpClass(Oid atttypid) { - HeapTuple tuple; + HeapTuple tuple; - tuple = SearchSysCacheTuple(CLADEFTYPE, - ObjectIdGetDatum(atttypid), - 0, 0, 0); - if(!HeapTupleIsValid(tuple)) { - return 0; - } + tuple = SearchSysCacheTuple(CLADEFTYPE, + ObjectIdGetDatum(atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + return 0; + } - return nameout(&(((Form_pg_opclass)GETSTRUCT(tuple))->opcname)); + return nameout(&(((Form_pg_opclass) GETSTRUCT(tuple))->opcname)); } /* * RemoveIndex -- - * Deletes an index. + * Deletes an index. * * Exceptions: - * BadArg if name is invalid. - * "WARN" if index nonexistent. - * ... + * BadArg if name is invalid. + * "WARN" if index nonexistent. + * ... */ void RemoveIndex(char *name) { - HeapTuple tuple; - - tuple = SearchSysCacheTuple(RELNAME, - PointerGetDatum(name), - 0,0,0); - - if (!HeapTupleIsValid(tuple)) { - elog(WARN, "index \"%s\" nonexistent", 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); + HeapTuple tuple; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(name), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(WARN, "index \"%s\" nonexistent", name); + } + + if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX) + { + elog(WARN, "relation \"%s\" is of type \"%c\"", + name, + ((Form_pg_class) GETSTRUCT(tuple))->relkind); + } + + index_destroy(tuple->t_oid); } diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 800d85a48b8..fb1df213cec 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -2,33 +2,33 @@ * * define.c-- * - * These routines execute some of the CREATE statements. In an earlier - * version of Postgres, these were "define" statements. + * These routines execute some of the CREATE statements. In an earlier + * version of Postgres, these were "define" statements. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.13 1997/08/12 22:52:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.14 1997/09/07 04:40:46 momjian Exp $ * * DESCRIPTION - * The "DefineFoo" routines take the parse tree and pick out the - * appropriate arguments/flags, passing the results to the - * corresponding "FooDefine" routines (in src/catalog) that do - * the actual catalog-munging. These routines also verify permission - * of the user to execute the command. + * The "DefineFoo" routines take the parse tree and pick out the + * appropriate arguments/flags, passing the results to the + * corresponding "FooDefine" routines (in src/catalog) that do + * the actual catalog-munging. These routines also verify permission + * of the user to execute the command. * * NOTES - * These things must be defined and committed in the following order: - * "create function": - * input/output, recv/send procedures - * "create type": - * type - * "create operator": - * operators + * These things must be defined and committed in the following order: + * "create function": + * input/output, recv/send procedures + * "create type": + * type + * "create operator": + * operators * - * Most of the parse-tree manipulation routines are defined in - * commands/manip.c. + * Most of the parse-tree manipulation routines are defined in + * commands/manip.c. * *------------------------------------------------------------------------- */ @@ -46,209 +46,259 @@ #include <catalog/pg_proc.h> #include <catalog/pg_type.h> #include <utils/syscache.h> -#include <fmgr.h> /* for fmgr */ -#include <utils/builtins.h> /* prototype for textin() */ +#include <fmgr.h> /* for fmgr */ +#include <utils/builtins.h> /* prototype for textin() */ #include <commands/defrem.h> #include <optimizer/xfunc.h> #include <tcop/dest.h> #include <catalog/pg_user.h> -static char *defGetString(DefElem *def); -static int defGetTypeLength(DefElem *def); +static char *defGetString(DefElem * def); +static int defGetTypeLength(DefElem * def); -#define DEFAULT_TYPDELIM ',' +#define DEFAULT_TYPDELIM ',' static void -case_translate_language_name(const char *input, char *output) { +case_translate_language_name(const char *input, char *output) +{ /*------------------------------------------------------------------------- Translate the input language name to lower case, except if it's C, - translate to upper case. + translate to upper case. --------------------------------------------------------------------------*/ - int i; + int i; - for (i = 0; i < NAMEDATALEN && input[i] != '\0'; ++i) - output[i] = tolower(input[i]); + for (i = 0; i < NAMEDATALEN && input[i] != '\0'; ++i) + output[i] = tolower(input[i]); - output[i] = '\0'; + output[i] = '\0'; - if (strcmp(output, "c") == 0) output[0] = 'C'; -} + if (strcmp(output, "c") == 0) + output[0] = 'C'; +} static void -compute_return_type(const Node *returnType, - char **prorettype_p, bool *returnsSet_p) { +compute_return_type(const Node * returnType, + char **prorettype_p, bool * returnsSet_p) +{ /*--------------------------------------------------------------------------- - Examine the "returns" clause returnType of the CREATE FUNCTION statement + Examine the "returns" clause returnType of the CREATE FUNCTION statement and return information about it as **prorettype_p and **returnsSet. ----------------------------------------------------------------------------*/ - if (nodeTag(returnType) == T_TypeName) { - /* a set of values */ - TypeName *setType = (TypeName *)returnType; - *prorettype_p = setType->name; - *returnsSet_p = true; - }else { - /* singleton */ - *prorettype_p = strVal(returnType); - *returnsSet_p = false; - } + if (nodeTag(returnType) == T_TypeName) + { + /* a set of values */ + TypeName *setType = (TypeName *) returnType; + + *prorettype_p = setType->name; + *returnsSet_p = true; + } + else + { + /* singleton */ + *prorettype_p = strVal(returnType); + *returnsSet_p = false; + } } - -static void -compute_full_attributes(const List *parameters, int32 *byte_pct_p, - int32 *perbyte_cpu_p, int32 *percall_cpu_p, - int32 *outin_ratio_p, bool *canCache_p) { + +static void +compute_full_attributes(const List * parameters, int32 * byte_pct_p, + int32 * perbyte_cpu_p, int32 * percall_cpu_p, + int32 * outin_ratio_p, bool * canCache_p) +{ /*-------------------------------------------------------------------------- Interpret the parameters *parameters and return their contents as *byte_pct_p, etc. These are the full parameters of a C or internal function. ---------------------------------------------------------------------------*/ - List *pl; - - /* the defaults */ - *byte_pct_p = BYTE_PCT; - *perbyte_cpu_p = PERBYTE_CPU; - *percall_cpu_p = PERCALL_CPU; - *outin_ratio_p = OUTIN_RATIO; - - foreach(pl, (List *)parameters) { - ParamString *param = (ParamString*)lfirst(pl); - - if (strcasecmp(param->name, "iscachable") == 0) { - *canCache_p = true; - } else if (strcasecmp(param->name, "trusted") == 0) { - /* - * we don't have untrusted functions any more. The 4.2 - * implementation is lousy anyway so I took it out. - * -ay 10/94 - */ - elog(WARN, "untrusted function has been decommissioned."); - } else if (strcasecmp(param->name, "byte_pct") == 0) { - /* - ** handle expensive function parameters - */ - *byte_pct_p = atoi(param->val); - } else if (strcasecmp(param->name, "perbyte_cpu") == 0) { - if (sscanf(param->val, "%d", perbyte_cpu_p) == 0) { - int count; - char *ptr; - for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) - if (*ptr == '!') count++; - *perbyte_cpu_p = (int) pow(10.0, (double) count); - } - } else if (strcasecmp(param->name, "percall_cpu") == 0) { - if (sscanf(param->val, "%d", percall_cpu_p) == 0) { - int count; - char *ptr; - for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) - if (*ptr == '!') count++; - *percall_cpu_p = (int) pow(10.0, (double) count); - } - } else if (strcasecmp(param->name, "outin_ratio") == 0) { - *outin_ratio_p = atoi(param->val); - } - } + List *pl; + + /* the defaults */ + *byte_pct_p = BYTE_PCT; + *perbyte_cpu_p = PERBYTE_CPU; + *percall_cpu_p = PERCALL_CPU; + *outin_ratio_p = OUTIN_RATIO; + + foreach(pl, (List *) parameters) + { + ParamString *param = (ParamString *) lfirst(pl); + + if (strcasecmp(param->name, "iscachable") == 0) + { + *canCache_p = true; + } + else if (strcasecmp(param->name, "trusted") == 0) + { + + /* + * we don't have untrusted functions any more. The 4.2 + * implementation is lousy anyway so I took it out. -ay 10/94 + */ + elog(WARN, "untrusted function has been decommissioned."); + } + else if (strcasecmp(param->name, "byte_pct") == 0) + { + + /* + * * handle expensive function parameters + */ + *byte_pct_p = atoi(param->val); + } + else if (strcasecmp(param->name, "perbyte_cpu") == 0) + { + if (sscanf(param->val, "%d", perbyte_cpu_p) == 0) + { + int count; + char *ptr; + + for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) + if (*ptr == '!') + count++; + *perbyte_cpu_p = (int) pow(10.0, (double) count); + } + } + else if (strcasecmp(param->name, "percall_cpu") == 0) + { + if (sscanf(param->val, "%d", percall_cpu_p) == 0) + { + int count; + char *ptr; + + for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) + if (*ptr == '!') + count++; + *percall_cpu_p = (int) pow(10.0, (double) count); + } + } + else if (strcasecmp(param->name, "outin_ratio") == 0) + { + *outin_ratio_p = atoi(param->val); + } + } } static void interpret_AS_clause(const char languageName[], const char as[], - char **prosrc_str_p, char **probin_str_p) { - - if ( strcmp(languageName, "C") == 0 || - strcmp(languageName, "internal") == 0 ) { - *prosrc_str_p = "-"; - *probin_str_p = (char *)as; - } else { - *prosrc_str_p = (char *)as; - *probin_str_p = "-"; - } + char **prosrc_str_p, char **probin_str_p) +{ + + if (strcmp(languageName, "C") == 0 || + strcmp(languageName, "internal") == 0) + { + *prosrc_str_p = "-"; + *probin_str_p = (char *) as; + } + else + { + *prosrc_str_p = (char *) as; + *probin_str_p = "-"; + } } /* * CreateFunction -- - * Execute a CREATE FUNCTION utility statement. + * Execute a CREATE FUNCTION utility statement. * */ void -CreateFunction(ProcedureStmt *stmt, CommandDest dest) +CreateFunction(ProcedureStmt * stmt, CommandDest dest) { - char *probin_str; - /* pathname of executable file that executes this function, if any */ - char *prosrc_str; - /* SQL that executes this function, if any */ - char *prorettype; - /* Type of return value (or member of set of values) from function */ - char languageName[NAMEDATALEN]; - /* name of language of function, with case adjusted: - "C", "internal", or "SQL" - */ - /* The following are attributes of the function, as expressed in the - CREATE FUNCTION statement, where applicable. - */ - int32 byte_pct, perbyte_cpu, percall_cpu, outin_ratio; - bool canCache; - bool returnsSet; - /* The function returns a set of values, as opposed to a singleton. */ - - - case_translate_language_name(stmt->language, languageName); - - compute_return_type(stmt->returnType, &prorettype, &returnsSet); - - if ( strcmp(languageName, "C") == 0 || - strcmp(languageName, "internal") == 0 ) { - compute_full_attributes(stmt->withClause, - &byte_pct, &perbyte_cpu, &percall_cpu, - &outin_ratio, &canCache); - } else if (strcmp(languageName, "sql") == 0) { - /* query optimizer groks sql, these are meaningless */ - perbyte_cpu = percall_cpu = 0; - byte_pct = outin_ratio = 100; - canCache = false; - } else { - elog(WARN, - "Unrecognized language specified in a CREATE FUNCTION: " - "'%s'. Recognized languages are sql, C, and internal.", - languageName); - } - - interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str); - - if (strcmp(languageName, "sql") != 0 && !superuser()) - elog(WARN, - "Only users with Postgres superuser privilege are permitted " - "to create a function " - "in the '%s' language. Others may use the 'sql' language.", - languageName); - /* Above does not return. */ - else { - /* And now that we have all the parameters, and know we're permitted - to do so, go ahead and create the function. - */ - ProcedureCreate(stmt->funcname, - returnsSet, - prorettype, - languageName, - prosrc_str, /* converted to text later */ - probin_str, /* converted to text later */ - canCache, - true, /* (obsolete "trusted") */ - byte_pct, - perbyte_cpu, - percall_cpu, - outin_ratio, - stmt->defArgs, - dest); - } + char *probin_str; + + /* pathname of executable file that executes this function, if any */ + char *prosrc_str; + + /* SQL that executes this function, if any */ + char *prorettype; + + /* Type of return value (or member of set of values) from function */ + char languageName[NAMEDATALEN]; + + /* + * name of language of function, with case adjusted: "C", "internal", + * or "SQL" + */ + + /* + * The following are attributes of the function, as expressed in the + * CREATE FUNCTION statement, where applicable. + */ + int32 byte_pct, + perbyte_cpu, + percall_cpu, + outin_ratio; + bool canCache; + bool returnsSet; + + /* The function returns a set of values, as opposed to a singleton. */ + + + case_translate_language_name(stmt->language, languageName); + + compute_return_type(stmt->returnType, &prorettype, &returnsSet); + + if (strcmp(languageName, "C") == 0 || + strcmp(languageName, "internal") == 0) + { + compute_full_attributes(stmt->withClause, + &byte_pct, &perbyte_cpu, &percall_cpu, + &outin_ratio, &canCache); + } + else if (strcmp(languageName, "sql") == 0) + { + /* query optimizer groks sql, these are meaningless */ + perbyte_cpu = percall_cpu = 0; + byte_pct = outin_ratio = 100; + canCache = false; + } + else + { + elog(WARN, + "Unrecognized language specified in a CREATE FUNCTION: " + "'%s'. Recognized languages are sql, C, and internal.", + languageName); + } + + interpret_AS_clause(languageName, stmt->as, &prosrc_str, &probin_str); + + if (strcmp(languageName, "sql") != 0 && !superuser()) + elog(WARN, + "Only users with Postgres superuser privilege are permitted " + "to create a function " + "in the '%s' language. Others may use the 'sql' language.", + languageName); + /* Above does not return. */ + else + { + + /* + * And now that we have all the parameters, and know we're + * permitted to do so, go ahead and create the function. + */ + ProcedureCreate(stmt->funcname, + returnsSet, + prorettype, + languageName, + prosrc_str, /* converted to text later */ + probin_str, /* converted to text later */ + canCache, + true, /* (obsolete "trusted") */ + byte_pct, + perbyte_cpu, + percall_cpu, + outin_ratio, + stmt->defArgs, + dest); + } } @@ -256,344 +306,437 @@ CreateFunction(ProcedureStmt *stmt, CommandDest 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. + * 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) +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 */ - + 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 + * DefineAggregate * ------------------ */ void -DefineAggregate(char *aggName, List *parameters) +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 */ + 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. + * Registers a new type. * */ void -DefineType(char *typeName, List *parameters) +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. - * ---------------- - */ - 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); - - 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); + 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. + * ---------------- + */ + 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); + + 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) +static char * +defGetString(DefElem * def) { - if (nodeTag(def->arg)!=T_String) - elog(WARN, "Define: \"%s\" = what?", def->defname); - return (strVal(def->arg)); + if (nodeTag(def->arg) != T_String) + elog(WARN, "Define: \"%s\" = what?", def->defname); + return (strVal(def->arg)); } -static int -defGetTypeLength(DefElem *def) +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; + if (nodeTag(def->arg) == T_Integer) + return (intVal(def->arg)); + else if (nodeTag(def->arg) == T_String && + !strcasecmp(strVal(def->arg), "variable")) + return -1; /* variable length */ + + elog(WARN, "Define: \"%s\" = what?", def->defname); + return -1; } diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 7d1f34d0327..192076e3911 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * explain.c-- - * Explain the query execution plan + * 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.10 1997/08/18 20:52:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.11 1997/09/07 04:40:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ #include <postgres.h> #include <parser/catalog_utils.h> -#include <parser/parse_query.h> /* for MakeTimeRange() */ +#include <parser/parse_query.h> /* for MakeTimeRange() */ #include <nodes/plannodes.h> #include <tcop/tcopprot.h> #include <lib/stringinfo.h> @@ -25,79 +25,86 @@ #include <optimizer/planner.h> #include <access/xact.h> -typedef struct ExplainState { - /* options */ - bool printCost; /* print cost */ - bool printNodes; /* do nodeToString() instead */ - /* other states */ - List *rtable; /* range table */ -} ExplainState; +typedef struct ExplainState +{ + /* options */ + bool printCost; /* print cost */ + bool printNodes; /* do nodeToString() instead */ + /* other states */ + List *rtable; /* range table */ +} ExplainState; -static char *Explain_PlanToString(Plan *plan, ExplainState *es); +static char *Explain_PlanToString(Plan * plan, ExplainState * es); /* * ExplainQuery - - * print out the execution plan for a given query + * print out the execution plan for a given query * */ void -ExplainQuery(Query *query, bool verbose, CommandDest dest) +ExplainQuery(Query * query, bool verbose, CommandDest dest) { - char *s = NULL, *s2; - 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; - } + char *s = NULL, + *s2; + Plan *plan; + ExplainState *es; + int len; - /* plan the queries (XXX we've ignored rewrite!!) */ - plan = planner(query); + if (IsAbortedTransactionBlockState()) + { + char *tag = "*ABORT STATE*"; - /* pg_plan could have failed */ - if (plan == NULL) - return; + EndCommand(tag, dest); + + elog(NOTICE, "(transaction aborted): %s", + "queries ignored until END"); + + return; + } - es = (ExplainState*)malloc(sizeof(ExplainState)); - memset(es, 0, sizeof(ExplainState)); + /* plan the queries (XXX we've ignored rewrite!!) */ + plan = planner(query); - es->printCost = true; /* default */ + /* pg_plan could have failed */ + if (plan == NULL) + return; - if (verbose) - es->printNodes = true; + es = (ExplainState *) malloc(sizeof(ExplainState)); + memset(es, 0, sizeof(ExplainState)); - es->rtable = query->rtable; + es->printCost = true; /* default */ - if (es->printNodes) - s = nodeToString(plan); + if (verbose) + es->printNodes = true; - if (es->printCost) { - s2 = Explain_PlanToString(plan, es); - if (s == NULL) - s = s2; - else { - strcat(s, "\n\n"); - strcat(s, s2); + es->rtable = query->rtable; + + if (es->printNodes) + s = nodeToString(plan); + + if (es->printCost) + { + s2 = Explain_PlanToString(plan, es); + if (s == NULL) + s = s2; + else + { + strcat(s, "\n\n"); + strcat(s, s2); + } } - } - - /* 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); + + /* 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); } /***************************************************************************** @@ -106,122 +113,130 @@ ExplainQuery(Query *query, bool verbose, CommandDest dest) /* * explain_outNode - - * converts a Node into ascii string and append it to 'str' + * converts a Node into ascii string and append it to 'str' */ static void -explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) +explain_outNode(StringInfo str, Plan * plan, int indent, ExplainState * es) { - char *pname; - char buf[1000]; - int i; - - if (plan==NULL) { + 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_Group: + pname = "Group"; + 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: + pname = ""; + 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", 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"); - 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_Group: - pname = "Group"; - 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: - pname = ""; - 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", rte->refname); - appendStringInfo(str, buf); + + /* lefttree */ + if (outerPlan(plan)) + { + for (i = 0; i < indent; i++) + appendStringInfo(str, " "); + appendStringInfo(str, " -> "); + explain_outNode(str, outerPlan(plan), indent + 1, es); } - 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; + + /* 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) +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; + StringInfo str; + char *s; + + if (plan == NULL) + return ""; + Assert(plan != NULL); + str = makeStringInfo(); + explain_outNode(str, plan, 0, es); + s = str->data; + pfree(str); + + return s; } diff --git a/src/backend/commands/purge.c b/src/backend/commands/purge.c index 5c514fc8675..8000bbc7352 100644 --- a/src/backend/commands/purge.c +++ b/src/backend/commands/purge.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * purge.c-- - * the POSTGRES purge command. + * 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.6 1997/08/12 22:52:25 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.7 1997/09/07 04:40:51 momjian 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. + * XXX There are many instances of int32 instead of ...Time. These + * should be changed once it is decided the signed'ness will be. * *------------------------------------------------------------------------- */ @@ -21,145 +21,156 @@ #include <access/heapam.h> #include <access/xact.h> -#include <utils/tqual.h> /* for NowTimeQual */ +#include <utils/tqual.h> /* for NowTimeQual */ #include <catalog/catname.h> #include <catalog/indexing.h> #include <fmgr.h> #include <commands/purge.h> -#include <utils/builtins.h> /* for isreltime() */ +#include <utils/builtins.h> /* for isreltime() */ -static char cmdname[] = "RelationPurge"; +static char cmdname[] = "RelationPurge"; -#define RELATIVE 01 -#define ABSOLUTE 02 +#define RELATIVE 01 +#define ABSOLUTE 02 int32 RelationPurge(char *relationName, - char *absoluteTimeString, - char *relativeTimeString) + 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"); + 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); } - } - -#ifdef PURGEDEBUG - elog(DEBUG, "%s: absolute time `%s' is %d.", - cmdname, absoluteTimeString, absoluteTime); -#endif /* defined(PURGEDEBUG) */ - - if (PointerIsValid(relativeTimeString)) { - if (isreltime(relativeTimeString) != 1) { - elog(WARN, "%s: bad relative time string \"%s\"", - cmdname, relativeTimeString); + + 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"); + } } - relativeTime = reltimein(relativeTimeString); - + +#ifdef PURGEDEBUG + elog(DEBUG, "%s: absolute time `%s' is %d.", + cmdname, absoluteTimeString, absoluteTime); +#endif /* defined(PURGEDEBUG) */ + + if (PointerIsValid(relativeTimeString)) + { + if (isreltime(relativeTimeString) != 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)) { + 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?? */ + 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); - 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?? */ - 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); + return (1); } - diff --git a/src/backend/commands/recipe.c b/src/backend/commands/recipe.c index e6aa009bd33..bf05c293d13 100644 --- a/src/backend/commands/recipe.c +++ b/src/backend/commands/recipe.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * recipe.c-- - * routines for handling execution of Tioga recipes + * routines for handling execution of Tioga recipes * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.6 1997/08/12 20:15:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.7 1997/09/07 04:40:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,7 @@ #include <commands/recipe.h> #include <libpq/libpq-be.h> #include <utils/builtins.h> -#include <utils/relcache.h> /* for RelationNameGetRelation*/ +#include <utils/relcache.h> /* for RelationNameGetRelation */ #include <parser/parse_query.h> #include <rewrite/rewriteHandler.h> #include <rewrite/rewriteManip.h> @@ -35,9 +35,12 @@ extern CommandDest whereToSendOutput; #ifndef TIOGA -void beginRecipe(RecipeStmt *stmt) { - elog(NOTICE,"You must compile with TIOGA defined in order to use recipes\n"); +void +beginRecipe(RecipeStmt * stmt) +{ + elog(NOTICE, "You must compile with TIOGA defined in order to use recipes\n"); } + #else #include <tioga/tgRecipe.h> @@ -45,49 +48,59 @@ void beginRecipe(RecipeStmt *stmt) { #define DEBUG_RECIPE 1 /* structure to keep track of the tee node plans */ -typedef struct _teePlanInfo { - char* tpi_relName; - Query* tpi_parsetree; - Plan* tpi_plan; -} TeePlanInfo; - -typedef struct _teeInfo { - int num; - TeePlanInfo *val; -} TeeInfo; - -QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2); -void OffsetVarAttno(Node* node, int varno, int offset); - -static void appendTeeQuery(TeeInfo *teeInfo, - QueryTreeList *q, - char* teeNodeName); - -static Plan* replaceTeeScans(Plan* plan, - Query* parsetree, - TeeInfo *teeInfo); -static void replaceSeqScan(Plan* plan, - Plan* parent, - int rt_ind, - Plan* tplan); - -static void tg_rewriteQuery(TgRecipe* r, TgNode *n, - QueryTreeList *q, - QueryTreeList *inputQlist); -static Node *tg_replaceNumberedParam(Node* expression, - int pnum, - int rt_ind, - char *teeRelName); -static Node *tg_rewriteParamsInExpr(Node *expression, - QueryTreeList *inputQlist); -static QueryTreeList *tg_parseSubQuery(TgRecipe* r, - TgNode* n, - TeeInfo* teeInfo); -static QueryTreeList* tg_parseTeeNode(TgRecipe *r, - TgNode *n, - int i, - QueryTreeList *qList, - TeeInfo* teeInfo); +typedef struct _teePlanInfo +{ + char *tpi_relName; + Query *tpi_parsetree; + Plan *tpi_plan; +} TeePlanInfo; + +typedef struct _teeInfo +{ + int num; + TeePlanInfo *val; +} TeeInfo; + +QueryTreeList *appendQlist(QueryTreeList * q1, QueryTreeList * q2); +void OffsetVarAttno(Node * node, int varno, int offset); + +static void +appendTeeQuery(TeeInfo * teeInfo, + QueryTreeList * q, + char *teeNodeName); + +static Plan * +replaceTeeScans(Plan * plan, + Query * parsetree, + TeeInfo * teeInfo); +static void +replaceSeqScan(Plan * plan, + Plan * parent, + int rt_ind, + Plan * tplan); + +static void +tg_rewriteQuery(TgRecipe * r, TgNode * n, + QueryTreeList * q, + QueryTreeList * inputQlist); +static Node * +tg_replaceNumberedParam(Node * expression, + int pnum, + int rt_ind, + char *teeRelName); +static Node * +tg_rewriteParamsInExpr(Node * expression, + QueryTreeList * inputQlist); +static QueryTreeList * +tg_parseSubQuery(TgRecipe * r, + TgNode * n, + TeeInfo * teeInfo); +static QueryTreeList * +tg_parseTeeNode(TgRecipe * r, + TgNode * n, + int i, + QueryTreeList * qList, + TeeInfo * teeInfo); /* @@ -96,172 +109,192 @@ static QueryTreeList* tg_parseTeeNode(TgRecipe *r, To parse a Tioga recipe, we start from an eye node and go backwards through its input nodes. To rewrite a Tioga node, we do the following: - 1) parse the node we're at in the standard way (calling parser() ) - 2) rewrite its input nodes recursively using Tioga rewrite - 3) now, with the rewritten input parse trees and the original parse tree - of the node, we rewrite the the node. - To do the rewrite, we use the target lists, range tables, and - qualifications of the input parse trees + 1) parse the node we're at in the standard way (calling parser() ) + 2) rewrite its input nodes recursively using Tioga rewrite + 3) now, with the rewritten input parse trees and the original parse tree + of the node, we rewrite the the node. + To do the rewrite, we use the target lists, range tables, and + qualifications of the input parse trees */ /* * beginRecipe: - * this is the main function to recipe execution - * this function is invoked for EXECUTE RECIPE ... statements - * - * takes in a RecipeStmt structure from the parser + * this is the main function to recipe execution + * this function is invoked for EXECUTE RECIPE ... statements + * + * takes in a RecipeStmt structure from the parser * and returns a list of cursor names */ void -beginRecipe(RecipeStmt* stmt) +beginRecipe(RecipeStmt * stmt) { - TgRecipe* r; - int i; - QueryTreeList *qList; - char portalName[1024]; - - Plan *plan; - TupleDesc attinfo; - QueryDesc *queryDesc; - Query *parsetree; - - int numTees; - TeeInfo* teeInfo; - - /* retrieveRecipe() reads the recipe from the database - and returns a TgRecipe* structure we can work with */ - - r = retrieveRecipe(stmt->recipeName); - - if (r == NULL) return; - - /* find the number of tees in the recipe */ - numTees = r->tees->num; - - if (numTees > 0) { - /* allocate a teePlan structure */ - teeInfo = (TeeInfo*)malloc(sizeof(TeeInfo)); - teeInfo->num = numTees; - teeInfo->val = (TeePlanInfo*)malloc(numTees * sizeof(TeePlanInfo)); - for (i=0;i<numTees;i++) { - teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName; - teeInfo->val[i].tpi_parsetree = NULL; - teeInfo->val[i].tpi_plan = NULL; - } - } else - teeInfo = NULL; - - /* - * for each viewer in the recipe, go backwards from each viewer input - * and generate a plan. Attach the plan to cursors. - **/ - for (i=0;i<r->eyes->num;i++) { - TgNodePtr e; - - e = r->eyes->val[i]; - if (e->inNodes->num > 1) { - elog(NOTICE, - "beginRecipe: Currently eyes cannot have more than one input"); - } - if (e->inNodes->num == 0) { - /* no input to this eye, skip it */ - continue; - } + TgRecipe *r; + int i; + QueryTreeList *qList; + char portalName[1024]; + + Plan *plan; + TupleDesc attinfo; + QueryDesc *queryDesc; + Query *parsetree; + + int numTees; + TeeInfo *teeInfo; + + /* + * retrieveRecipe() reads the recipe from the database and returns a + * TgRecipe* structure we can work with + */ + + r = retrieveRecipe(stmt->recipeName); + + if (r == NULL) + return; + + /* find the number of tees in the recipe */ + numTees = r->tees->num; + + if (numTees > 0) + { + /* allocate a teePlan structure */ + teeInfo = (TeeInfo *) malloc(sizeof(TeeInfo)); + teeInfo->num = numTees; + teeInfo->val = (TeePlanInfo *) malloc(numTees * sizeof(TeePlanInfo)); + for (i = 0; i < numTees; i++) + { + teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName; + teeInfo->val[i].tpi_parsetree = NULL; + teeInfo->val[i].tpi_plan = NULL; + } + } + else + teeInfo = NULL; + + /* + * for each viewer in the recipe, go backwards from each viewer input + * and generate a plan. Attach the plan to cursors. + */ + for (i = 0; i < r->eyes->num; i++) + { + TgNodePtr e; + + e = r->eyes->val[i]; + if (e->inNodes->num > 1) + { + elog(NOTICE, + "beginRecipe: Currently eyes cannot have more than one input"); + } + if (e->inNodes->num == 0) + { + /* no input to this eye, skip it */ + continue; + } #ifdef DEBUG_RECIPE - elog(NOTICE,"beginRecipe: eyes[%d] = %s\n", i, e->nodeName); -#endif /* DEBUG_RECIPE */ - - qList = tg_parseSubQuery(r,e->inNodes->val[0], teeInfo); - - if (qList == NULL) { - /* eye is directly connected to a tee node */ - /* XXX TODO: handle this case */ - } - - /* now, plan the queries */ - /* should really do everything pg_plan() does, but for now, - we skip the rule rewrite and time qual stuff */ - - /* ---------------------------------------------------------- - * 1) plan the main query, everything from an eye node back to - a Tee - * ---------------------------------------------------------- */ - parsetree = qList->qtrees[0]; - - /* before we plan, we want to see all the changes - we did, during the rewrite phase, such as - creating the tee tables, - setheapoverride() allows us to see the changes */ - setheapoverride(true); - plan = planner(parsetree); - - /* ---------------------------------------------------------- - * 2) plan the tee queries, (subgraphs rooted from a Tee) - by the time the eye is processed, all tees that contribute - to that eye will have been included in the teeInfo list - * ---------------------------------------------------------- */ - if (teeInfo) { - int t; - Plan* tplan; - Tee* newplan; - - for (t=0; t<teeInfo->num;t++) { - if (teeInfo->val[t].tpi_plan == NULL) { - /* plan it in the usual fashion */ - tplan = planner(teeInfo->val[t].tpi_parsetree); - - /* now add a tee node to the root of the plan */ -elog(NOTICE, "adding tee plan node to the root of the %s\n", - teeInfo->val[t].tpi_relName); - newplan = (Tee*)makeNode(Tee); - newplan->plan.targetlist = tplan->targetlist; - newplan->plan.qual = NULL; /* tplan->qual; */ - newplan->plan.lefttree = tplan; - newplan->plan.righttree = NULL; - newplan->leftParent = NULL; - newplan->rightParent = NULL; - /* the range table of the tee is the range table - of the tplan */ - newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable; - strcpy(newplan->teeTableName, - teeInfo->val[t].tpi_relName); - teeInfo->val[t].tpi_plan = (Plan*)newplan; - } - } - - /* ---------------------------------------------------------- - * 3) replace the tee table scans in the main plan with - actual tee plannodes - * ---------------------------------------------------------- */ - - plan = replaceTeeScans(plan, parsetree, teeInfo); - - } /* if (teeInfo) */ - - setheapoverride(false); - - /* define a portal for this viewer input */ - /* for now, eyes can only have one input */ - sprintf(portalName, "%s%d",e->nodeName,0); - - queryDesc = CreateQueryDesc(parsetree, - plan, - whereToSendOutput); - /* ---------------- - * call ExecStart to prepare the plan for execution - * ---------------- - */ - attinfo = ExecutorStart(queryDesc,NULL); - - ProcessPortal(portalName, - parsetree, - plan, - attinfo, - whereToSendOutput); -elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName); - } + elog(NOTICE, "beginRecipe: eyes[%d] = %s\n", i, e->nodeName); +#endif /* DEBUG_RECIPE */ + + qList = tg_parseSubQuery(r, e->inNodes->val[0], teeInfo); + + if (qList == NULL) + { + /* eye is directly connected to a tee node */ + /* XXX TODO: handle this case */ + } + + /* now, plan the queries */ + + /* + * should really do everything pg_plan() does, but for now, we + * skip the rule rewrite and time qual stuff + */ + + /* ---------------------------------------------------------- + * 1) plan the main query, everything from an eye node back to + a Tee + * ---------------------------------------------------------- */ + parsetree = qList->qtrees[0]; + + /* + * before we plan, we want to see all the changes we did, during + * the rewrite phase, such as creating the tee tables, + * setheapoverride() allows us to see the changes + */ + setheapoverride(true); + plan = planner(parsetree); + + /* ---------------------------------------------------------- + * 2) plan the tee queries, (subgraphs rooted from a Tee) + by the time the eye is processed, all tees that contribute + to that eye will have been included in the teeInfo list + * ---------------------------------------------------------- */ + if (teeInfo) + { + int t; + Plan *tplan; + Tee *newplan; + + for (t = 0; t < teeInfo->num; t++) + { + if (teeInfo->val[t].tpi_plan == NULL) + { + /* plan it in the usual fashion */ + tplan = planner(teeInfo->val[t].tpi_parsetree); + + /* now add a tee node to the root of the plan */ + elog(NOTICE, "adding tee plan node to the root of the %s\n", + teeInfo->val[t].tpi_relName); + newplan = (Tee *) makeNode(Tee); + newplan->plan.targetlist = tplan->targetlist; + newplan->plan.qual = NULL; /* tplan->qual; */ + newplan->plan.lefttree = tplan; + newplan->plan.righttree = NULL; + newplan->leftParent = NULL; + newplan->rightParent = NULL; + + /* + * the range table of the tee is the range table of + * the tplan + */ + newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable; + strcpy(newplan->teeTableName, + teeInfo->val[t].tpi_relName); + teeInfo->val[t].tpi_plan = (Plan *) newplan; + } + } + + /* ---------------------------------------------------------- + * 3) replace the tee table scans in the main plan with + actual tee plannodes + * ---------------------------------------------------------- */ + + plan = replaceTeeScans(plan, parsetree, teeInfo); + + } /* if (teeInfo) */ + + setheapoverride(false); + + /* define a portal for this viewer input */ + /* for now, eyes can only have one input */ + sprintf(portalName, "%s%d", e->nodeName, 0); + + queryDesc = CreateQueryDesc(parsetree, + plan, + whereToSendOutput); + /* ---------------- + * call ExecStart to prepare the plan for execution + * ---------------- + */ + attinfo = ExecutorStart(queryDesc, NULL); + + ProcessPortal(portalName, + parsetree, + plan, + attinfo, + whereToSendOutput); + elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName); + } } @@ -269,109 +302,122 @@ elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName); /* * tg_rewriteQuery - - * r - the recipe being rewritten - * n - the node that we're current at - * q - a QueryTree List containing the parse tree of the node - * inputQlist - the parsetrees of its input nodes, - * the size of inputQlist must be the same as the - * number of input nodes. Some elements in the inpuQlist - * may be null if the inputs to those nodes are unconnected + * r - the recipe being rewritten + * n - the node that we're current at + * q - a QueryTree List containing the parse tree of the node + * inputQlist - the parsetrees of its input nodes, + * the size of inputQlist must be the same as the + * number of input nodes. Some elements in the inpuQlist + * may be null if the inputs to those nodes are unconnected * - * this is the main routine for rewriting the recipe queries - * the original query tree 'q' is modified + * this is the main routine for rewriting the recipe queries + * the original query tree 'q' is modified */ -static void -tg_rewriteQuery(TgRecipe* r, - TgNode *n, - QueryTreeList *q, - QueryTreeList *inputQlist) +static void +tg_rewriteQuery(TgRecipe * r, + TgNode * n, + QueryTreeList * q, + QueryTreeList * inputQlist) { - Query* orig; - Query* inputQ; - int i; - List *rtable; - List *input_rtable; - int rt_length; - - /* orig is the original parse tree of the node */ - orig = q->qtrees[0]; - - - /*------------------------------------------------------------------- - step 1: - - form a combined range table from all the range tables in the original - query as well as the input nodes - - form a combined qualification from the qual in the original plus - the quals of the input nodes - ------------------------------------------------------------------- - */ - - /* start with the original range table */ - rtable = orig->rtable; - rt_length = length(rtable); - - for (i=0;i<n->inNodes->num;i++) { - if (n->inNodes->val[i] != NULL && - n->inNodes->val[i]->nodeType != TG_TEE_NODE) { - inputQ = inputQlist->qtrees[i]; - input_rtable = inputQ->rtable; - - /* need to offset the var nodes in the qual and targetlist - because they are indexed off the original rtable */ - OffsetVarNodes((Node*)inputQ->qual, rt_length); - OffsetVarNodes((Node*)inputQ->targetList, rt_length); - - /* append the range tables from the children nodes */ - rtable = nconc (rtable, input_rtable); - - /* append the qualifications of the child node into the - original qual list */ - AddQual(orig, inputQ->qual); + Query *orig; + Query *inputQ; + int i; + List *rtable; + List *input_rtable; + int rt_length; + + /* orig is the original parse tree of the node */ + orig = q->qtrees[0]; + + + /*------------------------------------------------------------------- + step 1: + + form a combined range table from all the range tables in the original + query as well as the input nodes + + form a combined qualification from the qual in the original plus + the quals of the input nodes + ------------------------------------------------------------------- + */ + + /* start with the original range table */ + rtable = orig->rtable; + rt_length = length(rtable); + + for (i = 0; i < n->inNodes->num; i++) + { + if (n->inNodes->val[i] != NULL && + n->inNodes->val[i]->nodeType != TG_TEE_NODE) + { + inputQ = inputQlist->qtrees[i]; + input_rtable = inputQ->rtable; + + /* + * need to offset the var nodes in the qual and targetlist + * because they are indexed off the original rtable + */ + OffsetVarNodes((Node *) inputQ->qual, rt_length); + OffsetVarNodes((Node *) inputQ->targetList, rt_length); + + /* append the range tables from the children nodes */ + rtable = nconc(rtable, input_rtable); + + /* + * append the qualifications of the child node into the + * original qual list + */ + AddQual(orig, inputQ->qual); + } } - } - orig->rtable = rtable; - - /* step 2: - rewrite the target list of the original parse tree - if there are any references to params, replace them with - the appropriate target list entry of the children node - */ - if (orig->targetList != NIL) { - List *tl; - TargetEntry *tle; - - foreach (tl, orig->targetList) { - tle = lfirst(tl); - if (tle->resdom != NULL) { - tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist); - } - } - } - - /* step 3: - rewrite the qual of the original parse tree - if there are any references to params, replace them with - the appropriate target list entry of the children node - */ - if (orig->qual) { - if (nodeTag(orig->qual) == T_List) { - elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???"); + orig->rtable = rtable; + + /* + * step 2: rewrite the target list of the original parse tree if there + * are any references to params, replace them with the appropriate + * target list entry of the children node + */ + if (orig->targetList != NIL) + { + List *tl; + TargetEntry *tle; + + foreach(tl, orig->targetList) + { + tle = lfirst(tl); + if (tle->resdom != NULL) + { + tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist); + } + } + } + + /* + * step 3: rewrite the qual of the original parse tree if there are + * any references to params, replace them with the appropriate target + * list entry of the children node + */ + if (orig->qual) + { + if (nodeTag(orig->qual) == T_List) + { + elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???"); + } + orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist); } - orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist); - } - /* at this point, we're done with the rewrite, the querytreelist q - has been modified */ + /* + * at this point, we're done with the rewrite, the querytreelist q has + * been modified + */ } /* tg_replaceNumberedParam: - this procedure replaces the specified numbered param with a + this procedure replaces the specified numbered param with a reference to a range table this procedure recursively calls itself @@ -379,104 +425,137 @@ tg_rewriteQuery(TgRecipe* r, it returns a (possibly modified) Node*. */ -static Node* -tg_replaceNumberedParam(Node *expression, - int pnum, /* the number of the parameter */ - int rt_ind, /* the range table index */ - char *teeRelName) /* the relname of the tee table */ +static Node * +tg_replaceNumberedParam(Node * expression, + int pnum, /* the number of the parameter */ + int rt_ind, /* the range table index */ + char *teeRelName) /* the relname of the tee + * table */ { - TargetEntry *param_tle; - Param* p; - Var *newVar,*oldVar; - - if (expression == NULL) return NULL; - - switch (nodeTag(expression)) { - case T_Param: - { - /* the node is a parameter, - substitute the entry from the target list of the child that - corresponds to the parameter number*/ - p = (Param*)expression; - - /* we only deal with the case of numbered parameters */ - if (p->paramkind == PARAM_NUM && p->paramid == pnum) { - - if (p->param_tlist) { - /* we have a parameter with an attribute like $N.foo - so replace it with a new var node */ - - /* param tlist can only have one entry in them! */ - param_tle = (TargetEntry*)(lfirst(p->param_tlist)); - oldVar = (Var*)param_tle->expr; - oldVar->varno = rt_ind; - oldVar->varnoold = rt_ind; - return (Node*)oldVar; - } else { - /* we have $N without the .foo */ - bool defined; - bool isRel; - /* TODO here, we need to check to see whether the type of the - tee is a complex type (relation) or a simple type */ - /* if it is a simple type, then we need to get the "result" - attribute from the tee relation */ - - isRel = (typeid_get_relid(p->paramtype) != 0); - if (isRel) { - newVar = makeVar(rt_ind, - 0, /* the whole tuple */ - TypeGet(teeRelName,&defined), - rt_ind, - 0); - return (Node*)newVar; - } else - newVar = makeVar(rt_ind, - 1, /* just the first field, which is 'result' */ - TypeGet(teeRelName,&defined), - rt_ind, - 0); - return (Node*)newVar; - - } - } - else { - elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind); - } - } - break; - case T_Expr: - { - /* the node is an expression, we need to recursively - call ourselves until we find parameter nodes */ - List *l; - Expr *expr = (Expr*)expression; - List *newArgs; - - /* we have to make a new args lists because Params - can be replaced by Var nodes in tg_replaceNumberedParam()*/ - newArgs = NIL; - - /* we only care about argument to expressions, - it doesn't matter when the opType is */ - /* recursively rewrite the arguments of this expression */ - foreach (l, expr->args) { - newArgs = lappend(newArgs, - tg_replaceNumberedParam(lfirst(l), - pnum, - rt_ind, - teeRelName)); - } - /* change the arguments of the expression */ - expr->args = newArgs; - } - break; - default: - { - /* ignore other expr types */ - } - } - - return expression; + TargetEntry *param_tle; + Param *p; + Var *newVar, + *oldVar; + + if (expression == NULL) + return NULL; + + switch (nodeTag(expression)) + { + case T_Param: + { + + /* + * the node is a parameter, substitute the entry from the + * target list of the child that corresponds to the parameter + * number + */ + p = (Param *) expression; + + /* we only deal with the case of numbered parameters */ + if (p->paramkind == PARAM_NUM && p->paramid == pnum) + { + + if (p->param_tlist) + { + + /* + * we have a parameter with an attribute like $N.foo + * so replace it with a new var node + */ + + /* param tlist can only have one entry in them! */ + param_tle = (TargetEntry *) (lfirst(p->param_tlist)); + oldVar = (Var *) param_tle->expr; + oldVar->varno = rt_ind; + oldVar->varnoold = rt_ind; + return (Node *) oldVar; + } + else + { + /* we have $N without the .foo */ + bool defined; + bool isRel; + + /* + * TODO here, we need to check to see whether the type + * of the tee is a complex type (relation) or a simple + * type + */ + + /* + * if it is a simple type, then we need to get the + * "result" attribute from the tee relation + */ + + isRel = (typeid_get_relid(p->paramtype) != 0); + if (isRel) + { + newVar = makeVar(rt_ind, + 0, /* the whole tuple */ + TypeGet(teeRelName, &defined), + rt_ind, + 0); + return (Node *) newVar; + } + else + newVar = makeVar(rt_ind, + 1, /* just the first field, + * which is 'result' */ + TypeGet(teeRelName, &defined), + rt_ind, + 0); + return (Node *) newVar; + + } + } + else + { + elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind); + } + } + break; + case T_Expr: + { + + /* + * the node is an expression, we need to recursively call + * ourselves until we find parameter nodes + */ + List *l; + Expr *expr = (Expr *) expression; + List *newArgs; + + /* + * we have to make a new args lists because Params can be + * replaced by Var nodes in tg_replaceNumberedParam() + */ + newArgs = NIL; + + /* + * we only care about argument to expressions, it doesn't + * matter when the opType is + */ + /* recursively rewrite the arguments of this expression */ + foreach(l, expr->args) + { + newArgs = lappend(newArgs, + tg_replaceNumberedParam(lfirst(l), + pnum, + rt_ind, + teeRelName)); + } + /* change the arguments of the expression */ + expr->args = newArgs; + } + break; + default: + { + /* ignore other expr types */ + } + } + + return expression; } @@ -485,694 +564,817 @@ tg_replaceNumberedParam(Node *expression, /* tg_rewriteParamsInExpr: - rewrite the params in expressions by using the targetlist entries - from the input parsetrees + rewrite the params in expressions by using the targetlist entries + from the input parsetrees this procedure recursively calls itself it returns a (possibly modified) Node*. */ -static Node* -tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist) +static Node * +tg_rewriteParamsInExpr(Node * expression, QueryTreeList * inputQlist) { - List *tl; - TargetEntry *param_tle, *tle; - Param* p; - int childno; - char *resname; - - if (expression == NULL) return NULL; - - switch (nodeTag(expression)) { - case T_Param: - { - /* the node is a parameter, - substitute the entry from the target list of the child that - corresponds to the parameter number*/ - p = (Param*)expression; - - /* we only deal with the case of numbered parameters */ - if (p->paramkind == PARAM_NUM) { - /* paramid's start from 1*/ - childno = p->paramid - 1; - - if (p->param_tlist) { - /* we have a parameter with an attribute like $N.foo - so match the resname "foo" against the target list - of the (N-1)th inputQlist */ - - /* param tlist can only have one entry in them! */ - param_tle = (TargetEntry*)(lfirst(p->param_tlist)); - resname = param_tle->resdom->resname; - - if (inputQlist->qtrees[childno]) { - foreach (tl, inputQlist->qtrees[childno]->targetList) { - tle = lfirst(tl); - if (strcmp(resname, tle->resdom->resname) == 0) { - return tle->expr; - } - } - } - else { - elog(WARN,"tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid); - } - - } else { - /* we have $N without the .foo */ - /* use the first resdom in the targetlist of the */ - /* appropriate child query */ - tl = inputQlist->qtrees[childno]->targetList; - tle = lfirst(tl); - return tle->expr; - } - } - else { - elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind); - } - } - break; - case T_Expr: - { - /* the node is an expression, we need to recursively - call ourselves until we find parameter nodes */ - List *l; - Expr *expr = (Expr*)expression; - List *newArgs; - - /* we have to make a new args lists because Params - can be replaced by Var nodes in tg_rewriteParamsInExpr()*/ - newArgs = NIL; - - /* we only care about argument to expressions, - it doesn't matter when the opType is */ - /* recursively rewrite the arguments of this expression */ - foreach (l, expr->args) { - newArgs = lappend(newArgs, - tg_rewriteParamsInExpr(lfirst(l), inputQlist)); - } - /* change the arguments of the expression */ - expr->args = newArgs; - } - break; - default: - { - /* ignore other expr types */ - } - } - - return expression; + List *tl; + TargetEntry *param_tle, + *tle; + Param *p; + int childno; + char *resname; + + if (expression == NULL) + return NULL; + + switch (nodeTag(expression)) + { + case T_Param: + { + + /* + * the node is a parameter, substitute the entry from the + * target list of the child that corresponds to the parameter + * number + */ + p = (Param *) expression; + + /* we only deal with the case of numbered parameters */ + if (p->paramkind == PARAM_NUM) + { + /* paramid's start from 1 */ + childno = p->paramid - 1; + + if (p->param_tlist) + { + + /* + * we have a parameter with an attribute like $N.foo + * so match the resname "foo" against the target list + * of the (N-1)th inputQlist + */ + + /* param tlist can only have one entry in them! */ + param_tle = (TargetEntry *) (lfirst(p->param_tlist)); + resname = param_tle->resdom->resname; + + if (inputQlist->qtrees[childno]) + { + foreach(tl, inputQlist->qtrees[childno]->targetList) + { + tle = lfirst(tl); + if (strcmp(resname, tle->resdom->resname) == 0) + { + return tle->expr; + } + } + } + else + { + elog(WARN, "tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid); + } + + } + else + { + /* we have $N without the .foo */ + /* use the first resdom in the targetlist of the */ + /* appropriate child query */ + tl = inputQlist->qtrees[childno]->targetList; + tle = lfirst(tl); + return tle->expr; + } + } + else + { + elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind); + } + } + break; + case T_Expr: + { + + /* + * the node is an expression, we need to recursively call + * ourselves until we find parameter nodes + */ + List *l; + Expr *expr = (Expr *) expression; + List *newArgs; + + /* + * we have to make a new args lists because Params can be + * replaced by Var nodes in tg_rewriteParamsInExpr() + */ + newArgs = NIL; + + /* + * we only care about argument to expressions, it doesn't + * matter when the opType is + */ + /* recursively rewrite the arguments of this expression */ + foreach(l, expr->args) + { + newArgs = lappend(newArgs, + tg_rewriteParamsInExpr(lfirst(l), inputQlist)); + } + /* change the arguments of the expression */ + expr->args = newArgs; + } + break; + default: + { + /* ignore other expr types */ + } + } + + return expression; } /* getParamTypes: - given an element, finds its parameter types. - the typev array argument is set to the parameter types. - the parameterCount is returned - - this code is very similar to ProcedureDefine() in pg_proc.c + given an element, finds its parameter types. + the typev array argument is set to the parameter types. + the parameterCount is returned + + this code is very similar to ProcedureDefine() in pg_proc.c */ static int -getParamTypes (TgElement *elem, Oid typev[]) +getParamTypes(TgElement * elem, Oid typev[]) { - /* this code is similar to ProcedureDefine() */ - int16 parameterCount; - bool defined; - Oid toid; - char *t; - int i,j; - - parameterCount = 0; - for (i=0;i<8;i++) { - typev[i] = 0; - } - for (j=0;j<elem->inTypes->num;j++) { - if (parameterCount == 8) { - elog(WARN, - "getParamTypes: Ingredients cannot take > 8 arguments"); + /* this code is similar to ProcedureDefine() */ + int16 parameterCount; + bool defined; + Oid toid; + char *t; + int i, + j; + + parameterCount = 0; + for (i = 0; i < 8; i++) + { + typev[i] = 0; } - t = elem->inTypes->val[j]; - if (strcmp(t,"opaque") == 0) { - elog(WARN, - "getParamTypes: Ingredient functions cannot take type 'opaque'"); - } else { - toid = TypeGet(elem->inTypes->val[j], &defined); - if (!OidIsValid(toid)) { - elog(WARN, "getParamTypes: arg type '%s' is not defined",t); - } - if (!defined) { - elog(NOTICE, "getParamTypes: arg type '%s' is only a shell",t); - } + for (j = 0; j < elem->inTypes->num; j++) + { + if (parameterCount == 8) + { + elog(WARN, + "getParamTypes: Ingredients cannot take > 8 arguments"); + } + t = elem->inTypes->val[j]; + if (strcmp(t, "opaque") == 0) + { + elog(WARN, + "getParamTypes: Ingredient functions cannot take type 'opaque'"); + } + else + { + toid = TypeGet(elem->inTypes->val[j], &defined); + if (!OidIsValid(toid)) + { + elog(WARN, "getParamTypes: arg type '%s' is not defined", t); + } + if (!defined) + { + elog(NOTICE, "getParamTypes: arg type '%s' is only a shell", t); + } + } + typev[parameterCount++] = toid; } - typev[parameterCount++] = toid; - } - return parameterCount; + return parameterCount; } /* * tg_parseTeeNode - * - * handles the parsing of the tee node - * + * + * handles the parsing of the tee node + * * */ -static QueryTreeList* -tg_parseTeeNode(TgRecipe *r, - TgNode *n, /* the tee node */ - int i, /* which input this node is to its parent */ - QueryTreeList *qList, - TeeInfo* teeInfo) +static QueryTreeList * +tg_parseTeeNode(TgRecipe * r, + TgNode * n, /* the tee node */ + int i, /* which input this node is to its parent */ + QueryTreeList * qList, + TeeInfo * teeInfo) { - QueryTreeList *q; - char* tt; - int rt_ind; - Query* orig; - - /* the input Node is a tee node, so we need to do the following: - * we need to parse the child of the tee node, - we add that to our query tree list - * we need the name of the tee node table - the tee node table is the table into which the tee node - may materialize results. Call it TT - * we add a range table to our existing query with TT in it - * we need to replace the parameter $i with TT - (otherwise the optimizer won't know to use the table - on expression containining $i) - After that rewrite, the optimizer will generate - sequential scans of TT - - Later, in the glue phase, we replace all instances of TT - sequential scans with the actual Tee node - */ - q = tg_parseSubQuery(r,n, teeInfo); - - /* tt is the name of the tee node table */ - tt = n->nodeName; - - if (q) - appendTeeQuery(teeInfo,q,tt); - - orig = qList->qtrees[0]; - rt_ind = RangeTablePosn(orig->rtable,tt); - /* check to see that this table is not part of - the range table already. This usually only - happens if multiple inputs are connected to the - same Tee. */ - if (rt_ind == 0) { - orig->rtable = lappend(orig->rtable, - addRangeTableEntry(NULL, - tt, - tt, - FALSE, - FALSE, - NULL)); - rt_ind = length(orig->rtable); - } - - orig->qual = tg_replaceNumberedParam(orig->qual, - i+1, /* params start at 1*/ - rt_ind, - tt); - return qList; + QueryTreeList *q; + char *tt; + int rt_ind; + Query *orig; + + /* + * the input Node is a tee node, so we need to do the following: we + * need to parse the child of the tee node, we add that to our query + * tree list we need the name of the tee node table the tee node table + * is the table into which the tee node may materialize results. Call + * it TT we add a range table to our existing query with TT in it we + * need to replace the parameter $i with TT (otherwise the optimizer + * won't know to use the table on expression containining $i) After + * that rewrite, the optimizer will generate sequential scans of TT + * + * Later, in the glue phase, we replace all instances of TT sequential + * scans with the actual Tee node + */ + q = tg_parseSubQuery(r, n, teeInfo); + + /* tt is the name of the tee node table */ + tt = n->nodeName; + + if (q) + appendTeeQuery(teeInfo, q, tt); + + orig = qList->qtrees[0]; + rt_ind = RangeTablePosn(orig->rtable, tt); + + /* + * check to see that this table is not part of the range table + * already. This usually only happens if multiple inputs are + * connected to the same Tee. + */ + if (rt_ind == 0) + { + orig->rtable = lappend(orig->rtable, + addRangeTableEntry(NULL, + tt, + tt, + FALSE, + FALSE, + NULL)); + rt_ind = length(orig->rtable); + } + + orig->qual = tg_replaceNumberedParam(orig->qual, + i + 1, /* params start at 1 */ + rt_ind, + tt); + return qList; } /* * tg_parseSubQuery: - * go backwards from a node and parse the query + * go backwards from a node and parse the query * - * the result parse tree is passed back - * - * could return NULL if trying to parse a teeNode + * the result parse tree is passed back + * + * could return NULL if trying to parse a teeNode * that's already been processed by another parent - * + * */ -static QueryTreeList* -tg_parseSubQuery(TgRecipe* r, TgNode* n, TeeInfo* teeInfo) +static QueryTreeList * +tg_parseSubQuery(TgRecipe * r, TgNode * n, TeeInfo * teeInfo) { - TgElement *elem; - char* funcName; - Oid typev[8]; /* eight arguments maximum */ - int i; - int parameterCount; - - QueryTreeList *qList; /* the parse tree of the nodeElement */ - QueryTreeList *inputQlist; /* the list of parse trees for the - inputs to this node */ - QueryTreeList *q; - Oid relid; - TgNode* child; - Relation rel; - unsigned int len; - TupleDesc tupdesc; - - qList = NULL; - - if (n->nodeType == TG_INGRED_NODE) { - /* parse each ingredient node in turn */ - - elem = n->nodeElem; - switch (elem->srcLang) { - case TG_SQL: - { - /* for SQL ingredients, the SQL query is contained in the - 'src' field */ + TgElement *elem; + char *funcName; + Oid typev[8]; /* eight arguments maximum */ + int i; + int parameterCount; + + QueryTreeList *qList; /* the parse tree of the nodeElement */ + QueryTreeList *inputQlist; /* the list of parse trees for the inputs + * to this node */ + QueryTreeList *q; + Oid relid; + TgNode *child; + Relation rel; + unsigned int len; + TupleDesc tupdesc; + + qList = NULL; + + if (n->nodeType == TG_INGRED_NODE) + { + /* parse each ingredient node in turn */ + + elem = n->nodeElem; + switch (elem->srcLang) + { + case TG_SQL: + { + + /* + * for SQL ingredients, the SQL query is contained in the + * 'src' field + */ #ifdef DEBUG_RECIPE -elog(NOTICE,"calling parser with %s",elem->src); -#endif /* DEBUG_RECIPE */ + elog(NOTICE, "calling parser with %s", elem->src); +#endif /* DEBUG_RECIPE */ - parameterCount = getParamTypes(elem,typev); + parameterCount = getParamTypes(elem, typev); - qList = parser(elem->src,typev,parameterCount); + qList = parser(elem->src, typev, parameterCount); - if (qList->len > 1) { - elog(NOTICE, - "tg_parseSubQuery: parser produced > 1 query tree"); - } - } - break; - case TG_C: - { - /* C ingredients are registered functions in postgres */ - /* we create a new query string by using the function name - (found in the 'src' field) and adding parameters to it - so if the function was FOOBAR and took in two arguments, - we would create a string - select FOOBAR($1,$2) - */ - char newquery[1000]; - - funcName = elem->src; - parameterCount = getParamTypes(elem,typev); - - if (parameterCount > 0) { - int i; - sprintf(newquery,"select %s($1",funcName); - for (i=1;i<parameterCount;i++) { - sprintf(newquery,"%s,$%d",newquery,i); - } - sprintf(newquery,"%s)",newquery); - } else - sprintf(newquery,"select %s()",funcName); + if (qList->len > 1) + { + elog(NOTICE, + "tg_parseSubQuery: parser produced > 1 query tree"); + } + } + break; + case TG_C: + { + /* C ingredients are registered functions in postgres */ + + /* + * we create a new query string by using the function name + * (found in the 'src' field) and adding parameters to it + * so if the function was FOOBAR and took in two + * arguments, we would create a string select + * FOOBAR($1,$2) + */ + char newquery[1000]; + + funcName = elem->src; + parameterCount = getParamTypes(elem, typev); + + if (parameterCount > 0) + { + int i; + + sprintf(newquery, "select %s($1", funcName); + for (i = 1; i < parameterCount; i++) + { + sprintf(newquery, "%s,$%d", newquery, i); + } + sprintf(newquery, "%s)", newquery); + } + else + sprintf(newquery, "select %s()", funcName); #ifdef DEBUG_RECIPE -elog(NOTICE,"calling parser with %s", newquery); -#endif /* DEBUG_RECIPE */ + elog(NOTICE, "calling parser with %s", newquery); +#endif /* DEBUG_RECIPE */ + + qList = parser(newquery, typev, parameterCount); + if (qList->len > 1) + { + elog(NOTICE, + "tg_parseSubQuery: parser produced > 1 query tree"); + } + } + break; + case TG_RECIPE_GRAPH: + elog(NOTICE, "tg_parseSubQuery: can't parse recipe graph ingredients yet!"); + break; + case TG_COMPILED: + elog(NOTICE, "tg_parseSubQuery: can't parse compiled ingredients yet!"); + break; + default: + elog(NOTICE, "tg_parseSubQuery: unknown srcLang: %d", elem->srcLang); + } + + /* parse each of the subrecipes that are input to this node */ + + if (n->inNodes->num > 0) + { + inputQlist = malloc(sizeof(QueryTreeList)); + inputQlist->len = n->inNodes->num + 1; + inputQlist->qtrees = (Query **) malloc(inputQlist->len * sizeof(Query *)); + for (i = 0; i < n->inNodes->num; i++) + { + + inputQlist->qtrees[i] = NULL; + if (n->inNodes->val[i]) + { + if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) + { + qList = tg_parseTeeNode(r, n->inNodes->val[i], + i, qList, teeInfo); + } + else + { /* input node is not a Tee */ + q = tg_parseSubQuery(r, n->inNodes->val[i], + teeInfo); + Assert(q->len == 1); + inputQlist->qtrees[i] = q->qtrees[0]; + } + } + } - qList = parser(newquery,typev,parameterCount); - if (qList->len > 1) { - elog(NOTICE, - "tg_parseSubQuery: parser produced > 1 query tree"); + /* now, we have all the query trees from our input nodes */ + /* transform the original parse tree appropriately */ + tg_rewriteQuery(r, n, qList, inputQlist); } - } - break; - case TG_RECIPE_GRAPH: - elog(NOTICE,"tg_parseSubQuery: can't parse recipe graph ingredients yet!"); - break; - case TG_COMPILED: - elog(NOTICE,"tg_parseSubQuery: can't parse compiled ingredients yet!"); - break; - default: - elog(NOTICE,"tg_parseSubQuery: unknown srcLang: %d",elem->srcLang); } + else if (n->nodeType == TG_EYE_NODE) + { - /* parse each of the subrecipes that are input to this node*/ - - if (n->inNodes->num > 0) { - inputQlist = malloc(sizeof(QueryTreeList)); - inputQlist->len = n->inNodes->num + 1 ; - inputQlist->qtrees = (Query**)malloc(inputQlist->len * sizeof(Query*)); - for (i=0;i<n->inNodes->num;i++) { - - inputQlist->qtrees[i] = NULL; - if (n->inNodes->val[i]) { - if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) { - qList = tg_parseTeeNode(r,n->inNodes->val[i], - i,qList,teeInfo); - } - else - { /* input node is not a Tee */ - q = tg_parseSubQuery(r,n->inNodes->val[i], - teeInfo); - Assert (q->len == 1); - inputQlist->qtrees[i] = q->qtrees[0]; - } + /* + * if we hit an eye, we need to stop and make what we have into a + * subrecipe query block + */ + elog(NOTICE, "tg_parseSubQuery: can't handle eye nodes yet"); + } + else if (n->nodeType == TG_TEE_NODE) + { + + /* + * if we hit a tee, check to see if the parsing has been done for + * this tee already by the other parent + */ + + rel = RelationNameGetRelation(n->nodeName); + if (RelationIsValid(rel)) + { + + /* + * this tee has already been visited, no need to do any + * further processing + */ + return NULL; } - } + else + { + /* we need to process the child of the tee first, */ + child = n->inNodes->val[0]; + + if (child->nodeType == TG_TEE_NODE) + { + /* nested Tee nodes */ + qList = tg_parseTeeNode(r, child, 0, qList, teeInfo); + return qList; + } - /* now, we have all the query trees from our input nodes */ - /* transform the original parse tree appropriately */ - tg_rewriteQuery(r,n,qList,inputQlist); + Assert(child != NULL); + + /* parse the input node */ + q = tg_parseSubQuery(r, child, teeInfo); + Assert(q->len == 1); + + /* add the parsed query to the main list of queries */ + qList = appendQlist(qList, q); + + /* need to create the tee table here */ + + /* + * the tee table created is used both for materializing the + * values at the tee node, and for parsing and optimization. + * The optimization needs to have a real table before it will + * consider scans on it + */ + + /* + * first, find the type of the tuples being produced by the + * tee. The type is the same as the output type of the child + * node. + * + * NOTE: we are assuming that the child node only has a single + * output here! + */ + getParamTypes(child->nodeElem, typev); + + /* + * the output type is either a complex type, (and is thus a + * relation) or is a simple type + */ + + rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]); + + if (RelationIsValid(rel)) + { + + /* + * for complex types, create new relation with the same + * tuple descriptor as the output table type + */ + len = length(q->qtrees[0]->targetList); + tupdesc = rel->rd_att; + + relid = heap_create(child->nodeElem->outTypes->val[0], + NULL, /* XXX */ + 'n', + DEFAULT_SMGR, + tupdesc); + } + else + { + + /* + * we have to create a relation with one attribute of the + * simple base type. That attribute will have an attr + * name of "result" + */ + /* NOTE: ignore array types for the time being */ + + len = 1; + tupdesc = CreateTemplateTupleDesc(len); + + if (!TupleDescInitEntry(tupdesc, 1, + "result", + NULL, + 0, false)) + { + elog(NOTICE, "tg_parseSubQuery: unexpected result from TupleDescInitEntry"); + } + else + { + relid = heap_create(child->nodeElem->outTypes->val[0], + NULL, /* XXX */ + 'n', + DEFAULT_SMGR, + tupdesc); + } + } + } } - } - else if (n->nodeType == TG_EYE_NODE) { - /* if we hit an eye, we need to stop and make what we have - into a subrecipe query block*/ - elog(NOTICE,"tg_parseSubQuery: can't handle eye nodes yet"); - } - else if (n->nodeType == TG_TEE_NODE) { - /* if we hit a tee, check to see if the parsing has been done - for this tee already by the other parent */ - - rel = RelationNameGetRelation(n->nodeName); - if (RelationIsValid(rel)) { - /* this tee has already been visited, - no need to do any further processing */ - return NULL; - } else { - /* we need to process the child of the tee first, */ - child = n->inNodes->val[0]; - - if (child->nodeType == TG_TEE_NODE) { - /* nested Tee nodes */ - qList = tg_parseTeeNode(r,child,0,qList,teeInfo); - return qList; - } - - Assert (child != NULL); - - /* parse the input node */ - q = tg_parseSubQuery(r,child, teeInfo); - Assert (q->len == 1); - - /* add the parsed query to the main list of queries */ - qList = appendQlist(qList,q); - - /* need to create the tee table here */ - /* the tee table created is used both for materializing the values - at the tee node, and for parsing and optimization. - The optimization needs to have a real table before it will - consider scans on it */ - - /* first, find the type of the tuples being produced by the - tee. The type is the same as the output type of - the child node. - - NOTE: we are assuming that the child node only has a single - output here! */ - getParamTypes(child->nodeElem,typev); - - /* the output type is either a complex type, - (and is thus a relation) or is a simple type */ - - rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]); - - if (RelationIsValid(rel)) { - /* for complex types, create new relation with the same - tuple descriptor as the output table type*/ - len = length(q->qtrees[0]->targetList); - tupdesc = rel->rd_att; - - relid = heap_create(child->nodeElem->outTypes->val[0], - NULL, /* XXX */ - 'n', - DEFAULT_SMGR, - tupdesc); - } - else { - /* we have to create a relation with one attribute of - the simple base type. That attribute will have - an attr name of "result" */ - /*NOTE: ignore array types for the time being */ - - len = 1; - tupdesc = CreateTemplateTupleDesc(len); - - if ( !TupleDescInitEntry(tupdesc,1, - "result", - NULL, - 0, false)) { - elog(NOTICE,"tg_parseSubQuery: unexpected result from TupleDescInitEntry"); - } else { - relid = heap_create(child->nodeElem->outTypes->val[0], - NULL, /* XXX */ - 'n', - DEFAULT_SMGR, - tupdesc); - } - } + else if (n->nodeType == TG_RECIPE_NODE) + { + elog(NOTICE, "tg_parseSubQuery: can't handle embedded recipes yet!"); } - } - else if (n->nodeType == TG_RECIPE_NODE) { - elog(NOTICE,"tg_parseSubQuery: can't handle embedded recipes yet!"); - } else - elog (NOTICE, "unknown nodeType: %d", n->nodeType); + else + elog(NOTICE, "unknown nodeType: %d", n->nodeType); - return qList; + return qList; } /* * OffsetVarAttno - - * recursively find all the var nodes with the specified varno + * recursively find all the var nodes with the specified varno * and offset their varattno with the offset - * - * code is similar to OffsetVarNodes in rewriteManip.c + * + * code is similar to OffsetVarNodes in rewriteManip.c */ void -OffsetVarAttno(Node* node, int varno, int offset) +OffsetVarAttno(Node * node, int varno, int offset) { - if (node == NULL) return; - switch (nodeTag(node)) { - case T_TargetEntry: - { - TargetEntry *tle = (TargetEntry *)node; - OffsetVarAttno(tle->expr, varno, offset); - } - break; - case T_Expr: - { - Expr *expr = (Expr*)node; - OffsetVarAttno((Node*)expr->args, varno, offset); - } - break; - case T_Var: + if (node == NULL) + return; + switch (nodeTag(node)) { - Var *var = (Var*)node; - if (var->varno == varno) - var->varattno += offset; - } - break; - case T_List: - { - List *l; + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *) node; + + OffsetVarAttno(tle->expr, varno, offset); + } + break; + case T_Expr: + { + Expr *expr = (Expr *) node; + + OffsetVarAttno((Node *) expr->args, varno, offset); + } + break; + case T_Var: + { + Var *var = (Var *) node; - foreach(l, (List*)node) { - OffsetVarAttno(lfirst(l), varno, offset); - } + if (var->varno == varno) + var->varattno += offset; + } + break; + case T_List: + { + List *l; + + foreach(l, (List *) node) + { + OffsetVarAttno(lfirst(l), varno, offset); + } + } + break; + default: + /* ignore the others */ + break; } - break; - default: - /* ignore the others */ - break; - } } /* - * appendQlist - * add the contents of a QueryTreeList q2 to the end of the QueryTreeList - * q1 + * appendQlist + * add the contents of a QueryTreeList q2 to the end of the QueryTreeList + * q1 * - * returns a new querytree list + * returns a new querytree list */ -QueryTreeList* -appendQlist(QueryTreeList *q1, QueryTreeList *q2) +QueryTreeList * +appendQlist(QueryTreeList * q1, QueryTreeList * q2) { - QueryTreeList* newq; - int i,j; - int newlen; - - if (q1 == NULL) - return q2; - - if (q2 == NULL) - return q1; - - newlen = q1->len + q2->len; - newq = (QueryTreeList*)malloc(sizeof(QueryTreeList)); - newq->len = newlen; - newq->qtrees = (Query**)malloc(newlen * sizeof(Query*)); - for (i=0;i<q1->len;i++) - newq->qtrees[i] = q1->qtrees[i]; - for (j=0;j<q2->len;j++) { - newq->qtrees[i + j] = q2->qtrees[j]; - } - return newq; + QueryTreeList *newq; + int i, + j; + int newlen; + + if (q1 == NULL) + return q2; + + if (q2 == NULL) + return q1; + + newlen = q1->len + q2->len; + newq = (QueryTreeList *) malloc(sizeof(QueryTreeList)); + newq->len = newlen; + newq->qtrees = (Query **) malloc(newlen * sizeof(Query *)); + for (i = 0; i < q1->len; i++) + newq->qtrees[i] = q1->qtrees[i]; + for (j = 0; j < q2->len; j++) + { + newq->qtrees[i + j] = q2->qtrees[j]; + } + return newq; } /* - * appendTeeQuery - * - * modify the query field of the teeInfo list of the particular tee node + * appendTeeQuery + * + * modify the query field of the teeInfo list of the particular tee node */ static void -appendTeeQuery(TeeInfo *teeInfo, QueryTreeList *q, char* teeNodeName) +appendTeeQuery(TeeInfo * teeInfo, QueryTreeList * q, char *teeNodeName) { - int i; - - Assert(teeInfo); + int i; - for (i=0;i<teeInfo->num;i++) { - if ( strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) { + Assert(teeInfo); + + for (i = 0; i < teeInfo->num; i++) + { + if (strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) + { - Assert(q->len == 1); - teeInfo->val[i].tpi_parsetree = q->qtrees[0]; - return; + Assert(q->len == 1); + teeInfo->val[i].tpi_parsetree = q->qtrees[0]; + return; + } } - } - elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo"); + elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo"); } /* - * replaceSeqScan - * replaces sequential scans of a specified relation with the tee plan - * the relation is specified by its index in the range table, rt_ind + * replaceSeqScan + * replaces sequential scans of a specified relation with the tee plan + * the relation is specified by its index in the range table, rt_ind * * returns the modified plan * the offset_attno is the offset that needs to be added to the parent's * qual or targetlist because the child plan has been replaced with a tee node */ static void -replaceSeqScan(Plan* plan, Plan* parent, - int rt_ind, Plan* tplan) +replaceSeqScan(Plan * plan, Plan * parent, + int rt_ind, Plan * tplan) { - Scan* snode; - Tee* teePlan; - Result* newPlan; - - if (plan == NULL) { - return; - } - - if (plan->type == T_SeqScan) { - snode = (Scan*)plan; - if (snode->scanrelid == rt_ind) { - /* found the sequential scan that should be replaced - with the tplan. */ - /* we replace the plan, but we also need to modify its parent*/ - - /* replace the sequential scan with a Result node - the reason we use a result node is so that we get the proper - projection behavior. The Result node is simply (ab)used as - a projection node */ - - newPlan = makeNode(Result); - newPlan->plan.cost = 0.0; - newPlan->plan.state = (EState*)NULL; - newPlan->plan.targetlist = plan->targetlist; - newPlan->plan.lefttree = tplan; - newPlan->plan.righttree = NULL; - newPlan->resconstantqual = NULL; - newPlan->resstate = NULL; - - /* change all the varno's to 1*/ - ChangeVarNodes((Node*)newPlan->plan.targetlist, - snode->scanrelid, 1); - - if (parent) { - teePlan = (Tee*)tplan; - - if (parent->lefttree == plan) - parent->lefttree = (Plan*)newPlan; - else - parent->righttree = (Plan*)newPlan; - + Scan *snode; + Tee *teePlan; + Result *newPlan; - if (teePlan->leftParent == NULL) - teePlan->leftParent = (Plan*)newPlan; - else - teePlan->rightParent = (Plan*)newPlan; + if (plan == NULL) + { + return; + } + + if (plan->type == T_SeqScan) + { + snode = (Scan *) plan; + if (snode->scanrelid == rt_ind) + { + + /* + * found the sequential scan that should be replaced with the + * tplan. + */ + /* we replace the plan, but we also need to modify its parent */ + + /* + * replace the sequential scan with a Result node the reason + * we use a result node is so that we get the proper + * projection behavior. The Result node is simply (ab)used as + * a projection node + */ + + newPlan = makeNode(Result); + newPlan->plan.cost = 0.0; + newPlan->plan.state = (EState *) NULL; + newPlan->plan.targetlist = plan->targetlist; + newPlan->plan.lefttree = tplan; + newPlan->plan.righttree = NULL; + newPlan->resconstantqual = NULL; + newPlan->resstate = NULL; + + /* change all the varno's to 1 */ + ChangeVarNodes((Node *) newPlan->plan.targetlist, + snode->scanrelid, 1); + + if (parent) + { + teePlan = (Tee *) tplan; + + if (parent->lefttree == plan) + parent->lefttree = (Plan *) newPlan; + else + parent->righttree = (Plan *) newPlan; + + + if (teePlan->leftParent == NULL) + teePlan->leftParent = (Plan *) newPlan; + else + teePlan->rightParent = (Plan *) newPlan; /* comment for now to test out executor-stuff - if (parent->state) { - ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan); - } + if (parent->state) { + ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan); + } */ - } - } + } + } - } else { - if (plan->lefttree) { - replaceSeqScan(plan->lefttree, plan, rt_ind, tplan); } - if (plan->righttree) { - replaceSeqScan(plan->righttree, plan, rt_ind, tplan); + else + { + if (plan->lefttree) + { + replaceSeqScan(plan->lefttree, plan, rt_ind, tplan); + } + if (plan->righttree) + { + replaceSeqScan(plan->righttree, plan, rt_ind, tplan); + } } - } } /* - * replaceTeeScans - * places the sequential scans of the Tee table with + * replaceTeeScans + * places the sequential scans of the Tee table with * a connection to the actual tee plan node */ -static Plan* -replaceTeeScans(Plan* plan, Query* parsetree, TeeInfo *teeInfo) +static Plan * +replaceTeeScans(Plan * plan, Query * parsetree, TeeInfo * teeInfo) { - int i; - List* rtable; - RangeTblEntry *rte; - char prefix[5]; - int rt_ind; - Plan* tplan; - - rtable = parsetree->rtable; - if (rtable == NULL) - return plan; + int i; + List *rtable; + RangeTblEntry *rte; + char prefix[5]; + int rt_ind; + Plan *tplan; + + rtable = parsetree->rtable; + if (rtable == NULL) + return plan; + + /* + * look through the range table for the tee relation entry, that will + * give use the varno we need to detect which sequential scans need to + * be replaced with tee nodes + */ + + rt_ind = 0; + while (rtable != NIL) + { + rte = lfirst(rtable); + rtable = lnext(rtable); + rt_ind++; /* range table references in varno fields + * start w/ 1 */ + + /* + * look for the "tee_" prefix in the refname, also check to see + * that the relname and the refname are the same this should + * eliminate any user-specified table and leave us with the tee + * table entries only + */ + if ((strlen(rte->refname) < 4) || + (strcmp(rte->relname, rte->refname) != 0)) + continue; + strNcpy(prefix, rte->refname, 4); + if (strcmp(prefix, "tee_") == 0) + { + /* okay, we found a tee node entry in the range table */ + + /* find the appropriate plan in the teeInfo list */ + tplan = NULL; + for (i = 0; i < teeInfo->num; i++) + { + if (strcmp(teeInfo->val[i].tpi_relName, + rte->refname) == 0) + { + tplan = teeInfo->val[i].tpi_plan; + } + } + if (tplan == NULL) + { + elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); + } - /* look through the range table for the tee relation entry, - that will give use the varno we need to detect which - sequential scans need to be replaced with tee nodes*/ - - rt_ind = 0; - while (rtable != NIL) { - rte = lfirst(rtable); - rtable = lnext(rtable); - rt_ind++; /* range table references in varno fields start w/ 1 */ - - /* look for the "tee_" prefix in the refname, - also check to see that the relname and the refname are the same - this should eliminate any user-specified table and leave - us with the tee table entries only*/ - if ((strlen(rte->refname) < 4) || - (strcmp (rte->relname, rte->refname) != 0)) - continue; - strNcpy(prefix,rte->refname,4); - if (strcmp(prefix,"tee_") == 0) { - /* okay, we found a tee node entry in the range table */ - - /* find the appropriate plan in the teeInfo list */ - tplan = NULL; - for (i=0;i<teeInfo->num;i++) { - if (strcmp(teeInfo->val[i].tpi_relName, - rte->refname) == 0) { - tplan = teeInfo->val[i].tpi_plan; + /* + * replace the sequential scan node with that var number with + * the tee plan node + */ + replaceSeqScan(plan, NULL, rt_ind, tplan); } - } - if (tplan == NULL) { - elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); } - - /* replace the sequential scan node with that var number - with the tee plan node */ - replaceSeqScan(plan, NULL, rt_ind, tplan); } - } - return plan; + return plan; } -#endif /* TIOGA */ +#endif /* TIOGA */ diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c index 7f0198a10b7..cafe4d09710 100644 --- a/src/backend/commands/remove.c +++ b/src/backend/commands/remove.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * remove.c-- - * POSTGRES remove (function | type | operator ) utilty code. + * 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.10 1997/08/18 20:52:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.11 1997/09/07 04:40:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,100 +28,112 @@ #include <storage/bufmgr.h> #include <fmgr.h> #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* * RemoveOperator -- - * Deletes an operator. + * Deletes an operator. * * Exceptions: - * BadArg if name is invalid. - * BadArg if type1 is invalid. - * "WARN" if operator nonexistent. - * ... + * BadArg if name is invalid. + * BadArg if type1 is invalid. + * "WARN" if operator nonexistent. + * ... */ void -RemoveOperator(char *operatorName, /* operator name */ - char *typeName1, /* first type name */ - char *typeName2) /* optional second type name */ +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; + 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; + + 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)) { + + 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); + 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); + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); } - } - heap_endscan(scan); - heap_close(relation); + 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 @@ -130,353 +142,379 @@ RemoveOperator(char *operatorName, /* operator name */ * don't use it - pma 2/1/94 */ /* - * SingleOpOperatorRemove - * Removes all operators that have operands or a result of type 'typeOid'. + * 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); + 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_endscan(sdesc); - } - heap_close(rdesc); + 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. + * 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; + 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); - tup = heap_getnext(sdesc, 0, &buffer); - if (PointerIsValid(tup)) { - char *name; - - name = (((Form_pg_class)GETSTRUCT(tup))->relname).data; - heap_destroy(name); + 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; } - } - heap_endscan(sdesc); - heap_close(rdesc); + 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 */ + +#endif /* NOTYET */ /* - * TypeRemove - * Removes the type 'typeName' and all attributes and relations that - * use it. + * TypeRemove + * Removes the type 'typeName' and all attributes and relations that + * use it. */ void -RemoveType(char *typeName) /* type name to be removed */ +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; - + 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); + 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)) { + + 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); - 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)) + + /* 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); + 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); + typeOid = tup->t_oid; + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); + heap_endscan(scan); + + heap_close(relation); } /* * RemoveFunction -- - * Deletes a function. + * Deletes a function. * * Exceptions: - * BadArg if name is invalid. - * "WARN" if function nonexistent. - * ... + * BadArg if name is invalid. + * "WARN" if function nonexistent. + * ... */ void -RemoveFunction(char *functionName, /* function name to be removed */ - int nargs, - List *argNameList /* list of TypeNames */) +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 = NULL; - 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; + Relation relation; + HeapScanDesc scan; + HeapTuple tup; + Buffer buffer = InvalidBuffer; + bool bufferUsed = FALSE; + Oid argList[8]; + Form_pg_proc the_proc = NULL; + 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, argList); - + + tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName), + Int32GetDatum(nargs), + PointerGetDatum(argList), 0); + if (!HeapTupleIsValid(tup)) + func_error("RemoveFunction", functionName, nargs, 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; + userName = GetPgUserName(); + if (!pg_func_ownercheck(userName, functionName, nargs, argList)) + { + elog(WARN, "RemoveFunction: function '%s': permission denied", + functionName); } - 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, argList); +#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, argList); } - - /* ok, function has been found */ - - if (the_proc->prolang == INTERNALlanguageId) - elog(WARN, "RemoveFunction: function \"%s\" is built-in", - functionName); - - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - heap_endscan(scan); - heap_close(relation); + + /* ok, function has been found */ + + if (the_proc->prolang == INTERNALlanguageId) + elog(WARN, "RemoveFunction: function \"%s\" is built-in", + functionName); + + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); + heap_endscan(scan); + heap_close(relation); } void RemoveAggregate(char *aggName, char *aggType) { - Relation relation; - HeapScanDesc scan; - HeapTuple tup; - ItemPointerData itemPointerData; - char *userName; - Oid basetypeID = InvalidOid; - bool defined; - ScanKeyData aggregateKey[3]; - - - /* - * if a basetype is passed in, then attempt to find an aggregate for that - * specific type. - * - * else if the basetype is blank, then attempt to find an aggregate with a - * basetype of zero. This is valid. It means that the aggregate is to apply - * to all basetypes. ie, a counter of some sort. - * - */ - - if (aggType) { - basetypeID = TypeGet(aggType, &defined); - if (!OidIsValid(basetypeID)) { - elog(WARN, "RemoveAggregate: type '%s' does not exist", aggType); - } - } else { - basetypeID = 0; - } + Relation relation; + HeapScanDesc scan; + HeapTuple tup; + ItemPointerData itemPointerData; + char *userName; + Oid basetypeID = InvalidOid; + bool defined; + ScanKeyData aggregateKey[3]; + + + /* + * if a basetype is passed in, then attempt to find an aggregate for + * that specific type. + * + * else if the basetype is blank, then attempt to find an aggregate with + * a basetype of zero. This is valid. It means that the aggregate is + * to apply to all basetypes. ie, a counter of some sort. + * + */ + + if (aggType) + { + basetypeID = TypeGet(aggType, &defined); + if (!OidIsValid(basetypeID)) + { + elog(WARN, "RemoveAggregate: type '%s' does not exist", aggType); + } + } + else + { + basetypeID = 0; + } /* #ifndef NO_SECURITY */ - userName = GetPgUserName(); - if (!pg_aggr_ownercheck(userName, aggName, basetypeID)) { - if (aggType) { - elog(WARN, "RemoveAggregate: aggregate '%s' on type '%s': permission denied", - aggName, aggType); - } else { - elog(WARN, "RemoveAggregate: aggregate '%s': permission denied", - aggName); - } - } + userName = GetPgUserName(); + if (!pg_aggr_ownercheck(userName, aggName, basetypeID)) + { + if (aggType) + { + elog(WARN, "RemoveAggregate: aggregate '%s' on type '%s': permission denied", + aggName, aggType); + } + else + { + elog(WARN, "RemoveAggregate: aggregate '%s': permission denied", + aggName); + } + } /* #endif */ - ScanKeyEntryInitialize(&aggregateKey[0], 0x0, - Anum_pg_aggregate_aggname, - NameEqualRegProcedure, - PointerGetDatum(aggName)); - - ScanKeyEntryInitialize(&aggregateKey[1], 0x0, - Anum_pg_aggregate_aggbasetype, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(basetypeID)); - - relation = heap_openr(AggregateRelationName); - scan = heap_beginscan(relation, 0, NowTimeQual, 2, aggregateKey); - tup = heap_getnext(scan, 0, (Buffer *) 0); - if (!HeapTupleIsValid(tup)) { - heap_endscan(scan); - heap_close(relation); - if (aggType) { - elog(WARN, "RemoveAggregate: aggregate '%s' for '%s' does not exist", - aggName, aggType); - } else { - elog(WARN, "RemoveAggregate: aggregate '%s' for all types does not exist", - aggName); - } - } - ItemPointerCopy(&tup->t_ctid, &itemPointerData); - heap_delete(relation, &itemPointerData); - heap_endscan(scan); - heap_close(relation); + ScanKeyEntryInitialize(&aggregateKey[0], 0x0, + Anum_pg_aggregate_aggname, + NameEqualRegProcedure, + PointerGetDatum(aggName)); + + ScanKeyEntryInitialize(&aggregateKey[1], 0x0, + Anum_pg_aggregate_aggbasetype, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(basetypeID)); + + relation = heap_openr(AggregateRelationName); + scan = heap_beginscan(relation, 0, NowTimeQual, 2, aggregateKey); + tup = heap_getnext(scan, 0, (Buffer *) 0); + if (!HeapTupleIsValid(tup)) + { + heap_endscan(scan); + heap_close(relation); + if (aggType) + { + elog(WARN, "RemoveAggregate: aggregate '%s' for '%s' does not exist", + aggName, aggType); + } + else + { + elog(WARN, "RemoveAggregate: aggregate '%s' for all types does not exist", + aggName); + } + } + ItemPointerCopy(&tup->t_ctid, &itemPointerData); + heap_delete(relation, &itemPointerData); + heap_endscan(scan); + heap_close(relation); } diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c index 5d4e4ab2bb8..9b8df698346 100644 --- a/src/backend/commands/rename.c +++ b/src/backend/commands/rename.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * rename.c-- - * renameatt() and renamerel() reside here. + * 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.7 1997/08/18 20:52:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.8 1997/09/07 04:40:55 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -32,227 +32,246 @@ #include <catalog/pg_proc.h> #include <catalog/pg_class.h> #include <optimizer/internal.h> -#include <optimizer/prep.h> /* for find_all_inheritors */ +#include <optimizer/prep.h> /* for find_all_inheritors */ #ifndef NO_SECURITY -# include <utils/acl.h> -#endif /* !NO_SECURITY */ +#include <utils/acl.h> +#endif /* !NO_SECURITY */ #ifndef HAVE_MEMMOVE -# include <regex/utils.h> +#include <regex/utils.h> #else -# include <string.h> +#include <string.h> #endif /* - * renameatt - changes the name of a attribute in a relation + * 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?). + * 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 + * 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. + * 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) + 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", - relname); + 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", + relname); #ifndef NO_SECURITY - if (!IsBootstrapProcessingMode() && - !pg_ownercheck(userName, relname, RELNAME)) - elog(WARN, "renameatt: you do not own class \"%s\"", - relname); + if (!IsBootstrapProcessingMode() && + !pg_ownercheck(userName, relname, RELNAME)) + elog(WARN, "renameatt: you do not own class \"%s\"", + 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\"", - 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. + * 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. */ - 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! */ + if (recurse) + { + Oid myrelid, + childrelid; + List *child, + *children; + + relrdesc = heap_openr(relname); + if (!RelationIsValid(relrdesc)) + { + elog(WARN, "renameatt: unknown relation: \"%s\"", + 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", + relname); + return; } - } - - relrdesc = heap_openr(RelationRelationName); - reltup = ClassNameIndexScan(relrdesc, relname); - if (!PointerIsValid(reltup)) { heap_close(relrdesc); - elog(WARN, "renameatt: relation \"%s\" nonexistent", - relname); - return; - } - heap_close(relrdesc); - - attrdesc = heap_openr(AttributeRelationName); - oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname); - if (!PointerIsValid(oldatttup)) { + + attrdesc = heap_openr(AttributeRelationName); + oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname); + if (!PointerIsValid(oldatttup)) + { + heap_close(attrdesc); + elog(WARN, "renameatt: attribute \"%s\" nonexistent", + oldattname); + } + if (((AttributeTupleForm) GETSTRUCT(oldatttup))->attnum < 0) + { + elog(WARN, "renameatt: system attribute \"%s\" not renamed", + oldattname); + } + + newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname); + if (PointerIsValid(newatttup)) + { + pfree(oldatttup); + heap_close(attrdesc); + elog(WARN, "renameatt: attribute \"%s\" exists", + newattname); + } + + namestrcpy(&(((AttributeTupleForm) (GETSTRUCT(oldatttup)))->attname), + newattname); + oldTID = oldatttup->t_ctid; + + /* insert "fixed" tuple */ + 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); - elog(WARN, "renameatt: attribute \"%s\" nonexistent", - oldattname); - } - if (((AttributeTupleForm ) GETSTRUCT(oldatttup))->attnum < 0) { - elog(WARN, "renameatt: system attribute \"%s\" not renamed", - oldattname); - } - - newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname); - if (PointerIsValid(newatttup)) { pfree(oldatttup); - heap_close(attrdesc); - elog(WARN, "renameatt: attribute \"%s\" exists", - newattname); - } - - namestrcpy(&(((AttributeTupleForm)(GETSTRUCT(oldatttup)))->attname), - newattname); - oldTID = oldatttup->t_ctid; - - /* insert "fixed" tuple */ - 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 + * renamerel - change the name of a relation * - * Relname attribute is changed in relation catalog. - * No record of the previous relname is kept (correct?). + * 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 + * 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. + * 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", - oldrelname); - return; - } - if (IsSystemRelationName(newrelname)) { - elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs", - newrelname); - return; - } - - relrdesc = heap_openr(RelationRelationName); - oldreltup = ClassNameIndexScan(relrdesc, oldrelname); - - if (!PointerIsValid(oldreltup)) { - heap_close(relrdesc); - elog(WARN, "renamerel: relation \"%s\" does not exist", - oldrelname); - } - - newreltup = ClassNameIndexScan(relrdesc, newrelname); - if (PointerIsValid(newreltup)) { + 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", + oldrelname); + return; + } + if (IsSystemRelationName(newrelname)) + { + elog(WARN, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs", + newrelname); + return; + } + + relrdesc = heap_openr(RelationRelationName); + oldreltup = ClassNameIndexScan(relrdesc, oldrelname); + + if (!PointerIsValid(oldreltup)) + { + heap_close(relrdesc); + elog(WARN, "renamerel: relation \"%s\" does not exist", + oldrelname); + } + + newreltup = ClassNameIndexScan(relrdesc, newrelname); + if (PointerIsValid(newreltup)) + { + pfree(oldreltup); + heap_close(relrdesc); + elog(WARN, "renamerel: relation \"%s\" exists", + newrelname); + } + + /* rename the directory first, so if this fails the rename's not done */ + strcpy(oldpath, relpath(oldrelname)); + 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 */ + 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); - elog(WARN, "renamerel: relation \"%s\" exists", - newrelname); - } - - /* rename the directory first, so if this fails the rename's not done */ - strcpy(oldpath, relpath(oldrelname)); - 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 */ - heap_replace(relrdesc, &oldTID, oldreltup); - - /* keep the system catalog indices current */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup); - CatalogCloseIndices(Num_pg_class_indices, idescs); - - pfree(oldreltup); - heap_close(relrdesc); } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 5d35f7b60f5..c4bd8c40dcf 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * sequence.c-- - * PostgreSQL sequences support code. + * PostgreSQL sequences support code. * *------------------------------------------------------------------------- */ @@ -19,523 +19,539 @@ #include <commands/sequence.h> #include <utils/builtins.h> -#define SEQ_MAGIC 0x1717 +#define SEQ_MAGIC 0x1717 #define SEQ_MAXVALUE ((int4)0x7FFFFFFF) #define SEQ_MINVALUE -(SEQ_MAXVALUE) -bool ItsSequenceCreation = false; +bool ItsSequenceCreation = false; -typedef struct FormData_pg_sequence { - NameData sequence_name; - int4 last_value; - int4 increment_by; - int4 max_value; - int4 min_value; - int4 cache_value; - char is_cycled; - char is_called; -} FormData_pg_sequence; - -typedef FormData_pg_sequence *SequenceTupleForm; - -typedef struct sequence_magic { - uint32 magic; -} sequence_magic; +typedef struct FormData_pg_sequence +{ + NameData sequence_name; + int4 last_value; + int4 increment_by; + int4 max_value; + int4 min_value; + int4 cache_value; + char is_cycled; + char is_called; +} FormData_pg_sequence; + +typedef FormData_pg_sequence *SequenceTupleForm; + +typedef struct sequence_magic +{ + uint32 magic; +} sequence_magic; -typedef struct SeqTableData { - char *name; - Oid relid; +typedef struct SeqTableData +{ + char *name; + Oid relid; Relation rel; int4 cached; int4 last; int4 increment; - struct SeqTableData *next; -} SeqTableData; + struct SeqTableData *next; +} SeqTableData; typedef SeqTableData *SeqTable; static SeqTable seqtab = NULL; -static SeqTable init_sequence (char *caller, char *name); -static SequenceTupleForm read_info (char * caller, SeqTable elm, Buffer * buf); -static void init_params (CreateSeqStmt *seq, SequenceTupleForm new); -static int get_param (DefElem *def); +static SeqTable init_sequence(char *caller, char *name); +static SequenceTupleForm read_info(char *caller, SeqTable elm, Buffer * buf); +static void init_params(CreateSeqStmt * seq, SequenceTupleForm new); +static int get_param(DefElem * def); /* * DefineSequence -- - * Creates a new sequence relation + * Creates a new sequence relation */ void -DefineSequence (CreateSeqStmt *seq) +DefineSequence(CreateSeqStmt * seq) { - FormData_pg_sequence new; - CreateStmt *stmt = makeNode (CreateStmt); - ColumnDef *coldef; - TypeName *typnam; - Relation rel; - Buffer buf; - PageHeader page; - sequence_magic *sm; - HeapTuple tuple; - TupleDesc tupDesc; - Datum value[SEQ_COL_LASTCOL]; - char null[SEQ_COL_LASTCOL]; - int i; - - /* Check and set values */ - init_params (seq, &new); - - /* - * Create relation (and fill null[] & value[]) - */ - stmt->tableElts = NIL; - for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) - { - typnam = makeNode(TypeName); - typnam->setof = FALSE; - typnam->arrayBounds = NULL; - coldef = makeNode(ColumnDef); - coldef->typename = typnam; - coldef->defval = NULL; - coldef->is_not_null = false; - null[i-1] = ' '; - - switch (i) + FormData_pg_sequence new; + CreateStmt *stmt = makeNode(CreateStmt); + ColumnDef *coldef; + TypeName *typnam; + Relation rel; + Buffer buf; + PageHeader page; + sequence_magic *sm; + HeapTuple tuple; + TupleDesc tupDesc; + Datum value[SEQ_COL_LASTCOL]; + char null[SEQ_COL_LASTCOL]; + int i; + + /* Check and set values */ + init_params(seq, &new); + + /* + * Create relation (and fill null[] & value[]) + */ + stmt->tableElts = NIL; + for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) { - case SEQ_COL_NAME: - typnam->name = "name"; - coldef->colname = "sequence_name"; - value[i-1] = PointerGetDatum (seq->seqname); - break; - case SEQ_COL_LASTVAL: - typnam->name = "int4"; - coldef->colname = "last_value"; - value[i-1] = Int32GetDatum (new.last_value); - break; - case SEQ_COL_INCBY: - typnam->name = "int4"; - coldef->colname = "increment_by"; - value[i-1] = Int32GetDatum (new.increment_by); - break; - case SEQ_COL_MAXVALUE: - typnam->name = "int4"; - coldef->colname = "max_value"; - value[i-1] = Int32GetDatum (new.max_value); - break; - case SEQ_COL_MINVALUE: - typnam->name = "int4"; - coldef->colname = "min_value"; - value[i-1] = Int32GetDatum (new.min_value); - break; - case SEQ_COL_CACHE: - typnam->name = "int4"; - coldef->colname = "cache_value"; - value[i-1] = Int32GetDatum (new.cache_value); - break; - case SEQ_COL_CYCLE: - typnam->name = "char"; - coldef->colname = "is_cycled"; - value[i-1] = CharGetDatum (new.is_cycled); - break; - case SEQ_COL_CALLED: - typnam->name = "char"; - coldef->colname = "is_called"; - value[i-1] = CharGetDatum ('f'); - break; - } - stmt->tableElts = lappend (stmt->tableElts, coldef); - } - - stmt->relname = seq->seqname; - stmt->archiveLoc = -1; /* default */ - stmt->archiveType = ARCH_NONE; - stmt->inhRelnames = NIL; - stmt->constraints = NIL; - - ItsSequenceCreation = true; /* hack */ - - DefineRelation (stmt); - - /* Xact abort calls CloseSequences, which turns ItsSequenceCreation off */ - ItsSequenceCreation = false; /* hack */ - - rel = heap_openr (seq->seqname); - Assert ( RelationIsValid (rel) ); - - RelationSetLockForWrite (rel); - - tupDesc = RelationGetTupleDescriptor(rel); - - Assert ( RelationGetNumberOfBlocks (rel) == 0 ); - buf = ReadBuffer (rel, P_NEW); - - if ( !BufferIsValid (buf) ) - elog (WARN, "DefineSequence: ReadBuffer failed"); - - page = (PageHeader) BufferGetPage (buf); - - PageInit((Page)page, BufferGetPageSize(buf), sizeof(sequence_magic)); - sm = (sequence_magic *) PageGetSpecialPointer (page); - sm->magic = SEQ_MAGIC; - - /* Now - form & insert sequence tuple */ - tuple = heap_formtuple (tupDesc, value, null); - heap_insert (rel, tuple); - - if ( WriteBuffer (buf) == STATUS_ERROR ) - elog (WARN, "DefineSequence: WriteBuffer failed"); - - RelationUnsetLockForWrite (rel); - heap_close (rel); - - return; + typnam = makeNode(TypeName); + typnam->setof = FALSE; + typnam->arrayBounds = NULL; + coldef = makeNode(ColumnDef); + coldef->typename = typnam; + coldef->defval = NULL; + coldef->is_not_null = false; + null[i - 1] = ' '; + + switch (i) + { + case SEQ_COL_NAME: + typnam->name = "name"; + coldef->colname = "sequence_name"; + value[i - 1] = PointerGetDatum(seq->seqname); + break; + case SEQ_COL_LASTVAL: + typnam->name = "int4"; + coldef->colname = "last_value"; + value[i - 1] = Int32GetDatum(new.last_value); + break; + case SEQ_COL_INCBY: + typnam->name = "int4"; + coldef->colname = "increment_by"; + value[i - 1] = Int32GetDatum(new.increment_by); + break; + case SEQ_COL_MAXVALUE: + typnam->name = "int4"; + coldef->colname = "max_value"; + value[i - 1] = Int32GetDatum(new.max_value); + break; + case SEQ_COL_MINVALUE: + typnam->name = "int4"; + coldef->colname = "min_value"; + value[i - 1] = Int32GetDatum(new.min_value); + break; + case SEQ_COL_CACHE: + typnam->name = "int4"; + coldef->colname = "cache_value"; + value[i - 1] = Int32GetDatum(new.cache_value); + break; + case SEQ_COL_CYCLE: + typnam->name = "char"; + coldef->colname = "is_cycled"; + value[i - 1] = CharGetDatum(new.is_cycled); + break; + case SEQ_COL_CALLED: + typnam->name = "char"; + coldef->colname = "is_called"; + value[i - 1] = CharGetDatum('f'); + break; + } + stmt->tableElts = lappend(stmt->tableElts, coldef); + } + + stmt->relname = seq->seqname; + stmt->archiveLoc = -1; /* default */ + stmt->archiveType = ARCH_NONE; + stmt->inhRelnames = NIL; + stmt->constraints = NIL; + + ItsSequenceCreation = true; /* hack */ + + DefineRelation(stmt); + + /* + * Xact abort calls CloseSequences, which turns ItsSequenceCreation + * off + */ + ItsSequenceCreation = false;/* hack */ + + rel = heap_openr(seq->seqname); + Assert(RelationIsValid(rel)); + + RelationSetLockForWrite(rel); + + tupDesc = RelationGetTupleDescriptor(rel); + + Assert(RelationGetNumberOfBlocks(rel) == 0); + buf = ReadBuffer(rel, P_NEW); + + if (!BufferIsValid(buf)) + elog(WARN, "DefineSequence: ReadBuffer failed"); + + page = (PageHeader) BufferGetPage(buf); + + PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic)); + sm = (sequence_magic *) PageGetSpecialPointer(page); + sm->magic = SEQ_MAGIC; + + /* Now - form & insert sequence tuple */ + tuple = heap_formtuple(tupDesc, value, null); + heap_insert(rel, tuple); + + if (WriteBuffer(buf) == STATUS_ERROR) + elog(WARN, "DefineSequence: WriteBuffer failed"); + + RelationUnsetLockForWrite(rel); + heap_close(rel); + + return; } int4 -nextval (struct varlena * seqin) +nextval(struct varlena * seqin) { - char *seqname = textout(seqin); - SeqTable elm; - Buffer buf; - SequenceTupleForm seq; - ItemPointerData iptr; - int4 incby, maxv, minv, cache; - int4 result, next, rescnt = 0; - - /* open and WIntentLock sequence */ - elm = init_sequence ("nextval", seqname); - pfree (seqname); - - if ( elm->last != elm->cached ) /* some numbers were cached */ - { - elm->last += elm->increment; - return (elm->last); - } - - seq = read_info ("nextval", elm, &buf); /* lock page and read tuple */ - - next = result = seq->last_value; - incby = seq->increment_by; - maxv = seq->max_value; - minv = seq->min_value; - cache = seq->cache_value; - - if ( seq->is_called != 't' ) - rescnt++; /* last_value if not called */ - - while ( rescnt < cache ) /* try to fetch cache numbers */ - { - /* - * Check MAXVALUE for ascending sequences - * and MINVALUE for descending sequences - */ - if ( incby > 0 ) /* ascending sequence */ - { - if ( ( maxv >= 0 && next > maxv - incby) || - ( maxv < 0 && next + incby > maxv ) ) - { - if ( rescnt > 0 ) - break; /* stop caching */ - if ( seq->is_cycled != 't' ) - elog (WARN, "%s.nextval: got MAXVALUE (%d)", - elm->name, maxv); - next = minv; - } - else - next += incby; + char *seqname = textout(seqin); + SeqTable elm; + Buffer buf; + SequenceTupleForm seq; + ItemPointerData iptr; + int4 incby, + maxv, + minv, + cache; + int4 result, + next, + rescnt = 0; + + /* open and WIntentLock sequence */ + elm = init_sequence("nextval", seqname); + pfree(seqname); + + if (elm->last != elm->cached) /* some numbers were cached */ + { + elm->last += elm->increment; + return (elm->last); } - else /* descending sequence */ + + seq = read_info("nextval", elm, &buf); /* lock page and read + * tuple */ + + next = result = seq->last_value; + incby = seq->increment_by; + maxv = seq->max_value; + minv = seq->min_value; + cache = seq->cache_value; + + if (seq->is_called != 't') + rescnt++; /* last_value if not called */ + + while (rescnt < cache) /* try to fetch cache numbers */ { - if ( ( minv < 0 && next < minv - incby ) || - ( minv >= 0 && next + incby < minv ) ) - { - if ( rescnt > 0 ) - break; /* stop caching */ - if ( seq->is_cycled != 't' ) - elog (WARN, "%s.nextval: got MINVALUE (%d)", - elm->name, minv); - next = maxv; - } - else - next += incby; - } - rescnt++; /* got result */ - if ( rescnt == 1 ) /* if it's first one - */ - result = next; /* it's what to return */ - } - - /* save info in local cache */ - elm->last = result; /* last returned number */ - elm->cached = next; /* last cached number */ - - /* save info in sequence relation */ - seq->last_value = next; /* last fetched number */ - seq->is_called = 't'; - - if ( WriteBuffer (buf) == STATUS_ERROR ) - elog (WARN, "%s.nextval: WriteBuffer failed", elm->name); - - ItemPointerSet(&iptr, 0, FirstOffsetNumber); - RelationUnsetSingleWLockPage (elm->rel, &iptr); - - return (result); - + + /* + * Check MAXVALUE for ascending sequences and MINVALUE for + * descending sequences + */ + if (incby > 0) /* ascending sequence */ + { + if ((maxv >= 0 && next > maxv - incby) || + (maxv < 0 && next + incby > maxv)) + { + if (rescnt > 0) + break; /* stop caching */ + if (seq->is_cycled != 't') + elog(WARN, "%s.nextval: got MAXVALUE (%d)", + elm->name, maxv); + next = minv; + } + else + next += incby; + } + else +/* descending sequence */ + { + if ((minv < 0 && next < minv - incby) || + (minv >= 0 && next + incby < minv)) + { + if (rescnt > 0) + break; /* stop caching */ + if (seq->is_cycled != 't') + elog(WARN, "%s.nextval: got MINVALUE (%d)", + elm->name, minv); + next = maxv; + } + else + next += incby; + } + rescnt++; /* got result */ + if (rescnt == 1) /* if it's first one - */ + result = next; /* it's what to return */ + } + + /* save info in local cache */ + elm->last = result; /* last returned number */ + elm->cached = next; /* last cached number */ + + /* save info in sequence relation */ + seq->last_value = next; /* last fetched number */ + seq->is_called = 't'; + + if (WriteBuffer(buf) == STATUS_ERROR) + elog(WARN, "%s.nextval: WriteBuffer failed", elm->name); + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationUnsetSingleWLockPage(elm->rel, &iptr); + + return (result); + } int4 -currval (struct varlena * seqin) +currval(struct varlena * seqin) { - char *seqname = textout(seqin); - SeqTable elm; - int4 result; - - /* open and WIntentLock sequence */ - elm = init_sequence ("currval", seqname); - pfree (seqname); - - if ( elm->increment == 0 ) /* nextval/read_info were not called */ - { - elog (WARN, "%s.currval is not yet defined in this session", elm->name); - } - - result = elm->last; - - return (result); - + char *seqname = textout(seqin); + SeqTable elm; + int4 result; + + /* open and WIntentLock sequence */ + elm = init_sequence("currval", seqname); + pfree(seqname); + + if (elm->increment == 0) /* nextval/read_info were not called */ + { + elog(WARN, "%s.currval is not yet defined in this session", elm->name); + } + + result = elm->last; + + return (result); + } -static SequenceTupleForm -read_info (char * caller, SeqTable elm, Buffer * buf) +static SequenceTupleForm +read_info(char *caller, SeqTable elm, Buffer * buf) { - ItemPointerData iptr; - PageHeader page; - ItemId lp; - HeapTuple tuple; - sequence_magic *sm; - SequenceTupleForm seq; - - ItemPointerSet(&iptr, 0, FirstOffsetNumber); - RelationSetSingleWLockPage (elm->rel, &iptr); - - if ( RelationGetNumberOfBlocks (elm->rel) != 1 ) - elog (WARN, "%s.%s: invalid number of blocks in sequence", - elm->name, caller); - - *buf = ReadBuffer (elm->rel, 0); - if ( !BufferIsValid (*buf) ) - elog (WARN, "%s.%s: ReadBuffer failed", elm->name, caller); - - page = (PageHeader) BufferGetPage (*buf); - sm = (sequence_magic *) PageGetSpecialPointer (page); - - if ( sm->magic != SEQ_MAGIC ) - elog (WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); - - lp = PageGetItemId (page, FirstOffsetNumber); - Assert (ItemIdIsUsed (lp)); - tuple = (HeapTuple) PageGetItem ((Page) page, lp); - - seq = (SequenceTupleForm) GETSTRUCT(tuple); - - elm->increment = seq->increment_by; - - return (seq); + ItemPointerData iptr; + PageHeader page; + ItemId lp; + HeapTuple tuple; + sequence_magic *sm; + SequenceTupleForm seq; + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationSetSingleWLockPage(elm->rel, &iptr); + + if (RelationGetNumberOfBlocks(elm->rel) != 1) + elog(WARN, "%s.%s: invalid number of blocks in sequence", + elm->name, caller); + + *buf = ReadBuffer(elm->rel, 0); + if (!BufferIsValid(*buf)) + elog(WARN, "%s.%s: ReadBuffer failed", elm->name, caller); + + page = (PageHeader) BufferGetPage(*buf); + sm = (sequence_magic *) PageGetSpecialPointer(page); + + if (sm->magic != SEQ_MAGIC) + elog(WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); + + lp = PageGetItemId(page, FirstOffsetNumber); + Assert(ItemIdIsUsed(lp)); + tuple = (HeapTuple) PageGetItem((Page) page, lp); + + seq = (SequenceTupleForm) GETSTRUCT(tuple); + + elm->increment = seq->increment_by; + + return (seq); } -static SeqTable -init_sequence (char * caller, char * name) +static SeqTable +init_sequence(char *caller, char *name) { - SeqTable elm, priv = (SeqTable) NULL; - SeqTable temp; - - for (elm = seqtab; elm != (SeqTable) NULL; ) - { - if ( strcmp (elm->name, name) == 0 ) - break; - priv = elm; - elm = elm->next; - } - - if ( elm == (SeqTable) NULL ) /* not found */ - { - temp = (SeqTable) malloc (sizeof(SeqTableData)); - temp->name = malloc (strlen(name) + 1); - strcpy (temp->name, name); - temp->rel = (Relation) NULL; - temp->cached = temp->last = temp->increment = 0; - temp->next = (SeqTable) NULL; - } - else /* found */ - { - if ( elm->rel != (Relation) NULL) /* already opened */ - return (elm); - temp = elm; - } - - temp->rel = heap_openr (name); - - if ( ! RelationIsValid (temp->rel) ) - elog (WARN, "%s.%s: sequence does not exist", name, caller); - - RelationSetWIntentLock (temp->rel); - - if ( temp->rel->rd_rel->relkind != RELKIND_SEQUENCE ) - elog (WARN, "%s.%s: %s is not sequence !", name, caller, name); - - if ( elm != (SeqTable) NULL ) /* we opened sequence from our */ - { /* SeqTable - check relid ! */ - if ( RelationGetRelationId (elm->rel) != elm->relid ) - { - elog (NOTICE, "%s.%s: sequence was re-created", - name, caller, name); - elm->cached = elm->last = elm->increment = 0; - elm->relid = RelationGetRelationId (elm->rel); - } - } - else - { - elm = temp; - elm->relid = RelationGetRelationId (elm->rel); - if ( seqtab == (SeqTable) NULL ) - seqtab = elm; - else - priv->next = elm; - } - - return (elm); - + SeqTable elm, + priv = (SeqTable) NULL; + SeqTable temp; + + for (elm = seqtab; elm != (SeqTable) NULL;) + { + if (strcmp(elm->name, name) == 0) + break; + priv = elm; + elm = elm->next; + } + + if (elm == (SeqTable) NULL) /* not found */ + { + temp = (SeqTable) malloc(sizeof(SeqTableData)); + temp->name = malloc(strlen(name) + 1); + strcpy(temp->name, name); + temp->rel = (Relation) NULL; + temp->cached = temp->last = temp->increment = 0; + temp->next = (SeqTable) NULL; + } + else +/* found */ + { + if (elm->rel != (Relation) NULL) /* already opened */ + return (elm); + temp = elm; + } + + temp->rel = heap_openr(name); + + if (!RelationIsValid(temp->rel)) + elog(WARN, "%s.%s: sequence does not exist", name, caller); + + RelationSetWIntentLock(temp->rel); + + if (temp->rel->rd_rel->relkind != RELKIND_SEQUENCE) + elog(WARN, "%s.%s: %s is not sequence !", name, caller, name); + + if (elm != (SeqTable) NULL) /* we opened sequence from our */ + { /* SeqTable - check relid ! */ + if (RelationGetRelationId(elm->rel) != elm->relid) + { + elog(NOTICE, "%s.%s: sequence was re-created", + name, caller, name); + elm->cached = elm->last = elm->increment = 0; + elm->relid = RelationGetRelationId(elm->rel); + } + } + else + { + elm = temp; + elm->relid = RelationGetRelationId(elm->rel); + if (seqtab == (SeqTable) NULL) + seqtab = elm; + else + priv->next = elm; + } + + return (elm); + } /* * CloseSequences -- - * is calling by xact mgr at commit/abort. + * is calling by xact mgr at commit/abort. */ void -CloseSequences (void) +CloseSequences(void) { - SeqTable elm; - Relation rel; - - ItsSequenceCreation = false; - - for (elm = seqtab; elm != (SeqTable) NULL; ) - { - if ( elm->rel != (Relation) NULL ) /* opened in current xact */ + SeqTable elm; + Relation rel; + + ItsSequenceCreation = false; + + for (elm = seqtab; elm != (SeqTable) NULL;) { - rel = elm->rel; - elm->rel = (Relation) NULL; - RelationUnsetWIntentLock (rel); - heap_close (rel); - } - elm = elm->next; - } - - return; - + if (elm->rel != (Relation) NULL) /* opened in current xact */ + { + rel = elm->rel; + elm->rel = (Relation) NULL; + RelationUnsetWIntentLock(rel); + heap_close(rel); + } + elm = elm->next; + } + + return; + } -static void -init_params (CreateSeqStmt *seq, SequenceTupleForm new) +static void +init_params(CreateSeqStmt * seq, SequenceTupleForm new) { - DefElem *last_value = NULL; - DefElem *increment_by = NULL; - DefElem *max_value = NULL; - DefElem *min_value = NULL; - DefElem *cache_value = NULL; - List *option; - - new->is_cycled = 'f'; - foreach (option, seq->options) - { - DefElem *defel = (DefElem *)lfirst(option); - - if ( !strcasecmp(defel->defname, "increment") ) - increment_by = defel; - else if ( !strcasecmp(defel->defname, "start") ) - last_value = defel; - else if ( !strcasecmp(defel->defname, "maxvalue") ) - max_value = defel; - else if ( !strcasecmp(defel->defname, "minvalue") ) - min_value = defel; - else if ( !strcasecmp(defel->defname, "cache") ) - cache_value = defel; - else if ( !strcasecmp(defel->defname, "cycle") ) - { - if ( defel->arg != (Node*) NULL ) - elog (WARN, "DefineSequence: CYCLE ??"); - new->is_cycled = 't'; - } - else - elog (WARN, "DefineSequence: option \"%s\" not recognized", - defel->defname); - } - - if ( increment_by == (DefElem*) NULL ) /* INCREMENT BY */ - new->increment_by = 1; - else if ( ( new->increment_by = get_param (increment_by) ) == 0 ) - elog (WARN, "DefineSequence: can't INCREMENT by 0"); - - if ( max_value == (DefElem*) NULL ) /* MAXVALUE */ - if ( new->increment_by > 0 ) - new->max_value = SEQ_MAXVALUE; /* ascending seq */ + DefElem *last_value = NULL; + DefElem *increment_by = NULL; + DefElem *max_value = NULL; + DefElem *min_value = NULL; + DefElem *cache_value = NULL; + List *option; + + new->is_cycled = 'f'; + foreach(option, seq->options) + { + DefElem *defel = (DefElem *) lfirst(option); + + if (!strcasecmp(defel->defname, "increment")) + increment_by = defel; + else if (!strcasecmp(defel->defname, "start")) + last_value = defel; + else if (!strcasecmp(defel->defname, "maxvalue")) + max_value = defel; + else if (!strcasecmp(defel->defname, "minvalue")) + min_value = defel; + else if (!strcasecmp(defel->defname, "cache")) + cache_value = defel; + else if (!strcasecmp(defel->defname, "cycle")) + { + if (defel->arg != (Node *) NULL) + elog(WARN, "DefineSequence: CYCLE ??"); + new->is_cycled = 't'; + } + else + elog(WARN, "DefineSequence: option \"%s\" not recognized", + defel->defname); + } + + if (increment_by == (DefElem *) NULL) /* INCREMENT BY */ + new->increment_by = 1; + else if ((new->increment_by = get_param(increment_by)) == 0) + elog(WARN, "DefineSequence: can't INCREMENT by 0"); + + if (max_value == (DefElem *) NULL) /* MAXVALUE */ + if (new->increment_by > 0) + new->max_value = SEQ_MAXVALUE; /* ascending seq */ + else + new->max_value = -1;/* descending seq */ else - new->max_value = -1; /* descending seq */ - else - new->max_value = get_param (max_value); + new->max_value = get_param(max_value); - if ( min_value == (DefElem*) NULL ) /* MINVALUE */ - if ( new->increment_by > 0 ) - new->min_value = 1; /* ascending seq */ + if (min_value == (DefElem *) NULL) /* MINVALUE */ + if (new->increment_by > 0) + new->min_value = 1; /* ascending seq */ + else + new->min_value = SEQ_MINVALUE; /* descending seq */ else - new->min_value = SEQ_MINVALUE; /* descending seq */ - else - new->min_value = get_param (min_value); - - if ( new->min_value >= new->max_value ) - elog (WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)", - new->min_value, new->max_value); - - if ( last_value == (DefElem*) NULL ) /* START WITH */ - if ( new->increment_by > 0 ) - new->last_value = new->min_value; /* ascending seq */ + new->min_value = get_param(min_value); + + if (new->min_value >= new->max_value) + elog(WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)", + new->min_value, new->max_value); + + if (last_value == (DefElem *) NULL) /* START WITH */ + if (new->increment_by > 0) + new->last_value = new->min_value; /* ascending seq */ + else + new->last_value = new->max_value; /* descending seq */ else - new->last_value = new->max_value; /* descending seq */ - else - new->last_value = get_param (last_value); - - if ( new->last_value < new->min_value ) - elog (WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)", - new->last_value, new->min_value); - if ( new->last_value > new->max_value ) - elog (WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)", - new->last_value, new->max_value); - - if ( cache_value == (DefElem*) NULL ) /* CACHE */ - new->cache_value = 1; - else if ( ( new->cache_value = get_param (cache_value) ) <= 0 ) - elog (WARN, "DefineSequence: CACHE (%d) can't be <= 0", - new->cache_value); + new->last_value = get_param(last_value); + + if (new->last_value < new->min_value) + elog(WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)", + new->last_value, new->min_value); + if (new->last_value > new->max_value) + elog(WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)", + new->last_value, new->max_value); + + if (cache_value == (DefElem *) NULL) /* CACHE */ + new->cache_value = 1; + else if ((new->cache_value = get_param(cache_value)) <= 0) + elog(WARN, "DefineSequence: CACHE (%d) can't be <= 0", + new->cache_value); } static int -get_param (DefElem *def) +get_param(DefElem * def) { - if ( def->arg == (Node*) NULL ) - elog (WARN, "DefineSequence: \"%s\" value unspecified", def->defname); - - if ( nodeTag (def->arg) == T_Integer ) - return (intVal (def->arg)); - - elog (WARN, "DefineSequence: \"%s\" is to be integer", def->defname); - return (-1); + if (def->arg == (Node *) NULL) + elog(WARN, "DefineSequence: \"%s\" value unspecified", def->defname); + + if (nodeTag(def->arg) == T_Integer) + return (intVal(def->arg)); + + elog(WARN, "DefineSequence: \"%s\" is to be integer", def->defname); + return (-1); } diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2919df473fd..53ab1838cfe 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * trigger.c-- - * PostgreSQL TRIGGERs support code. + * PostgreSQL TRIGGERs support code. * *------------------------------------------------------------------------- */ @@ -32,581 +32,587 @@ #include "utils/syscache.h" #endif -TriggerData *CurrentTriggerData = NULL; +TriggerData *CurrentTriggerData = NULL; -void RelationBuildTriggers (Relation relation); -void FreeTriggerDesc (Relation relation); +void RelationBuildTriggers(Relation relation); +void FreeTriggerDesc(Relation relation); -static void DescribeTrigger (TriggerDesc *trigdesc, Trigger *trigger); +static void DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger); -extern void fmgr_info(Oid procedureId, func_ptr *function, int *nargs); +extern void fmgr_info(Oid procedureId, func_ptr * function, int *nargs); extern GlobalMemory CacheCxt; void -CreateTrigger (CreateTrigStmt *stmt) +CreateTrigger(CreateTrigStmt * stmt) { - int16 tgtype; - int16 tgattr[8] = {0}; - Datum values[Natts_pg_trigger]; - char nulls[Natts_pg_trigger]; - Relation rel; - Relation tgrel; - HeapScanDesc tgscan; - ScanKeyData key; - Relation relrdesc; - HeapTuple tuple; - ItemPointerData oldTID; - Relation idescs[Num_pg_trigger_indices]; - Relation ridescs[Num_pg_class_indices]; - MemoryContext oldcxt; - Oid fargtypes[8]; - int found = 0; - int i; - - if ( IsSystemRelationName (stmt->relname) ) - elog(WARN, "CreateTrigger: can't create trigger for system relation %s", stmt->relname); + int16 tgtype; + int16 tgattr[8] = {0}; + Datum values[Natts_pg_trigger]; + char nulls[Natts_pg_trigger]; + Relation rel; + Relation tgrel; + HeapScanDesc tgscan; + ScanKeyData key; + Relation relrdesc; + HeapTuple tuple; + ItemPointerData oldTID; + Relation idescs[Num_pg_trigger_indices]; + Relation ridescs[Num_pg_class_indices]; + MemoryContext oldcxt; + Oid fargtypes[8]; + int found = 0; + int i; + + if (IsSystemRelationName(stmt->relname)) + elog(WARN, "CreateTrigger: can't create trigger for system relation %s", stmt->relname); #ifndef NO_SECURITY - if ( !pg_ownercheck (GetPgUserName (), stmt->relname, RELNAME)) - elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); + if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME)) + elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); #endif - - rel = heap_openr (stmt->relname); - if ( !RelationIsValid (rel) ) - elog (WARN, "CreateTrigger: there is no relation %s", stmt->relname); - - RelationSetLockForWrite (rel); - - TRIGGER_CLEAR_TYPE (tgtype); - if ( stmt->before ) - TRIGGER_SETT_BEFORE (tgtype); - if ( stmt->row ) - TRIGGER_SETT_ROW (tgtype); - for (i = 0; i < 3 && stmt->actions[i]; i++) - { - switch ( stmt->actions[i] ) - { - case 'i': - if ( TRIGGER_FOR_INSERT (tgtype) ) - elog (WARN, "CreateTrigger: double INSERT event specified"); - TRIGGER_SETT_INSERT (tgtype); - break; - case 'd': - if ( TRIGGER_FOR_DELETE (tgtype) ) - elog (WARN, "CreateTrigger: double DELETE event specified"); - TRIGGER_SETT_DELETE (tgtype); - break; - case 'u': - if ( TRIGGER_FOR_UPDATE (tgtype) ) - elog (WARN, "CreateTrigger: double UPDATE event specified"); - TRIGGER_SETT_UPDATE (tgtype); - break; - default: - elog (WARN, "CreateTrigger: unknown event specified"); - break; - } - } - - /* Scan pg_trigger */ - tgrel = heap_openr (TriggerRelationName); - RelationSetLockForWrite (tgrel); - ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key); - while (tuple = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tuple)) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple); - if ( namestrcmp (&(pg_trigger->tgname), stmt->trigname) == 0 ) - elog (WARN, "CreateTrigger: trigger %s already defined on relation %s", - stmt->trigname, stmt->relname); - else - found++; - } - heap_endscan (tgscan); - - memset (fargtypes, 0, 8 * sizeof(Oid)); - tuple = SearchSysCacheTuple (PRONAME, - PointerGetDatum (stmt->funcname), - 0, PointerGetDatum (fargtypes), 0); - if ( !HeapTupleIsValid (tuple) || - ((Form_pg_proc)GETSTRUCT(tuple))->prorettype != 0 || - ((Form_pg_proc)GETSTRUCT(tuple))->pronargs != 0 ) - elog (WARN, "CreateTrigger: function %s () does not exist", stmt->funcname); - - if ( ((Form_pg_proc)GETSTRUCT(tuple))->prolang != ClanguageId ) - elog (WARN, "CreateTrigger: only C functions are supported"); - - memset (nulls, ' ', Natts_pg_trigger * sizeof (char)); - - values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum (rel->rd_id); - values[Anum_pg_trigger_tgname - 1] = NameGetDatum (namein (stmt->trigname)); - values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum (tuple->t_oid); - values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum (tgtype); - if ( stmt->args ) - { - List *le; - char *args; - int16 nargs = length (stmt->args); - int len = 0; - - foreach (le, stmt->args) - { - char *ar = (char *) lfirst (le); - len += strlen (ar) + 4; - } - args = (char *) palloc (len + 1); - args[0] = 0; - foreach (le, stmt->args) - sprintf (args + strlen (args), "%s\\000", (char *)lfirst (le)); - values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum (nargs); - values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum (byteain (args)); - } - else - { - values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum (0); - values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum (byteain ("")); - } - values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum (tgattr); - - tuple = heap_formtuple (tgrel->rd_att, values, nulls); - heap_insert (tgrel, tuple); - CatalogOpenIndices (Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); - CatalogIndexInsert (idescs, Num_pg_trigger_indices, tgrel, tuple); - CatalogCloseIndices (Num_pg_trigger_indices, idescs); - pfree (tuple); - RelationUnsetLockForWrite (tgrel); - heap_close (tgrel); - - pfree (DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); - pfree (DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); - - /* update pg_class */ - relrdesc = heap_openr (RelationRelationName); - tuple = ClassNameIndexScan (relrdesc, stmt->relname); - if ( !PointerIsValid (tuple) ) - { + + rel = heap_openr(stmt->relname); + if (!RelationIsValid(rel)) + elog(WARN, "CreateTrigger: there is no relation %s", stmt->relname); + + RelationSetLockForWrite(rel); + + TRIGGER_CLEAR_TYPE(tgtype); + if (stmt->before) + TRIGGER_SETT_BEFORE(tgtype); + if (stmt->row) + TRIGGER_SETT_ROW(tgtype); + for (i = 0; i < 3 && stmt->actions[i]; i++) + { + switch (stmt->actions[i]) + { + case 'i': + if (TRIGGER_FOR_INSERT(tgtype)) + elog(WARN, "CreateTrigger: double INSERT event specified"); + TRIGGER_SETT_INSERT(tgtype); + break; + case 'd': + if (TRIGGER_FOR_DELETE(tgtype)) + elog(WARN, "CreateTrigger: double DELETE event specified"); + TRIGGER_SETT_DELETE(tgtype); + break; + case 'u': + if (TRIGGER_FOR_UPDATE(tgtype)) + elog(WARN, "CreateTrigger: double UPDATE event specified"); + TRIGGER_SETT_UPDATE(tgtype); + break; + default: + elog(WARN, "CreateTrigger: unknown event specified"); + break; + } + } + + /* Scan pg_trigger */ + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForWrite(tgrel); + ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + tgscan = heap_beginscan(tgrel, 0, NowTimeQual, 1, &key); + while (tuple = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tuple)) + { + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + + if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) + elog(WARN, "CreateTrigger: trigger %s already defined on relation %s", + stmt->trigname, stmt->relname); + else + found++; + } + heap_endscan(tgscan); + + memset(fargtypes, 0, 8 * sizeof(Oid)); + tuple = SearchSysCacheTuple(PRONAME, + PointerGetDatum(stmt->funcname), + 0, PointerGetDatum(fargtypes), 0); + if (!HeapTupleIsValid(tuple) || + ((Form_pg_proc) GETSTRUCT(tuple))->prorettype != 0 || + ((Form_pg_proc) GETSTRUCT(tuple))->pronargs != 0) + elog(WARN, "CreateTrigger: function %s () does not exist", stmt->funcname); + + if (((Form_pg_proc) GETSTRUCT(tuple))->prolang != ClanguageId) + elog(WARN, "CreateTrigger: only C functions are supported"); + + memset(nulls, ' ', Natts_pg_trigger * sizeof(char)); + + values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(rel->rd_id); + values[Anum_pg_trigger_tgname - 1] = NameGetDatum(namein(stmt->trigname)); + values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(tuple->t_oid); + values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); + if (stmt->args) + { + List *le; + char *args; + int16 nargs = length(stmt->args); + int len = 0; + + foreach(le, stmt->args) + { + char *ar = (char *) lfirst(le); + + len += strlen(ar) + 4; + } + args = (char *) palloc(len + 1); + args[0] = 0; + foreach(le, stmt->args) + sprintf(args + strlen(args), "%s\\000", (char *) lfirst(le)); + values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs); + values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain(args)); + } + else + { + values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0); + values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(byteain("")); + } + values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr); + + tuple = heap_formtuple(tgrel->rd_att, values, nulls); + heap_insert(tgrel, tuple); + CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple); + CatalogCloseIndices(Num_pg_trigger_indices, idescs); + pfree(tuple); + RelationUnsetLockForWrite(tgrel); + heap_close(tgrel); + + pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); + pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); + + /* update pg_class */ + relrdesc = heap_openr(RelationRelationName); + tuple = ClassNameIndexScan(relrdesc, stmt->relname); + if (!PointerIsValid(tuple)) + { + heap_close(relrdesc); + elog(WARN, "CreateTrigger: relation %s not found in pg_class", stmt->relname); + } + ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; + RelationInvalidateHeapTuple(relrdesc, tuple); + oldTID = tuple->t_ctid; + heap_replace(relrdesc, &oldTID, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); + CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, tuple); + CatalogCloseIndices(Num_pg_class_indices, ridescs); + pfree(tuple); heap_close(relrdesc); - elog(WARN, "CreateTrigger: relation %s not found in pg_class", stmt->relname); - } - ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1; - RelationInvalidateHeapTuple (relrdesc, tuple); - oldTID = tuple->t_ctid; - heap_replace (relrdesc, &oldTID, tuple); - CatalogOpenIndices (Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert (ridescs, Num_pg_class_indices, relrdesc, tuple); - CatalogCloseIndices (Num_pg_class_indices, ridescs); - pfree(tuple); - heap_close(relrdesc); - - CommandCounterIncrement (); - oldcxt = MemoryContextSwitchTo ((MemoryContext)CacheCxt); - FreeTriggerDesc (rel); - rel->rd_rel->reltriggers = found + 1; - RelationBuildTriggers (rel); - MemoryContextSwitchTo (oldcxt); - heap_close (rel); - return; + + CommandCounterIncrement(); + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + FreeTriggerDesc(rel); + rel->rd_rel->reltriggers = found + 1; + RelationBuildTriggers(rel); + MemoryContextSwitchTo(oldcxt); + heap_close(rel); + return; } void -DropTrigger (DropTrigStmt *stmt) +DropTrigger(DropTrigStmt * stmt) { - Relation rel; - Relation tgrel; - HeapScanDesc tgscan; - ScanKeyData key; - Relation relrdesc; - HeapTuple tuple; - ItemPointerData oldTID; - Relation ridescs[Num_pg_class_indices]; - MemoryContext oldcxt; - int found = 0; - int tgfound = 0; - + Relation rel; + Relation tgrel; + HeapScanDesc tgscan; + ScanKeyData key; + Relation relrdesc; + HeapTuple tuple; + ItemPointerData oldTID; + Relation ridescs[Num_pg_class_indices]; + MemoryContext oldcxt; + int found = 0; + int tgfound = 0; + #ifndef NO_SECURITY - if ( !pg_ownercheck (GetPgUserName (), stmt->relname, RELNAME)) - elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); + if (!pg_ownercheck(GetPgUserName(), stmt->relname, RELNAME)) + elog(WARN, "%s: %s", stmt->relname, aclcheck_error_strings[ACLCHECK_NOT_OWNER]); #endif - - rel = heap_openr (stmt->relname); - if ( !RelationIsValid (rel) ) - elog (WARN, "DropTrigger: there is no relation %s", stmt->relname); - - RelationSetLockForWrite (rel); - - tgrel = heap_openr (TriggerRelationName); - RelationSetLockForWrite (tgrel); - ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key); - while (tuple = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tuple)) - { - Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple); - if ( namestrcmp (&(pg_trigger->tgname), stmt->trigname) == 0 ) - { - heap_delete (tgrel, &tuple->t_ctid); - tgfound++; - } - else - found++; - } - if ( tgfound == 0 ) - elog (WARN, "DropTrigger: there is no trigger %s on relation %s", - stmt->trigname, stmt->relname); - if ( tgfound > 1 ) - elog (NOTICE, "DropTrigger: found (and deleted) %d trigger %s on relation %s", - tgfound, stmt->trigname, stmt->relname); - heap_endscan (tgscan); - RelationUnsetLockForWrite (tgrel); - heap_close (tgrel); - - /* update pg_class */ - relrdesc = heap_openr (RelationRelationName); - tuple = ClassNameIndexScan (relrdesc, stmt->relname); - if ( !PointerIsValid (tuple) ) - { + + rel = heap_openr(stmt->relname); + if (!RelationIsValid(rel)) + elog(WARN, "DropTrigger: there is no relation %s", stmt->relname); + + RelationSetLockForWrite(rel); + + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForWrite(tgrel); + ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + tgscan = heap_beginscan(tgrel, 0, NowTimeQual, 1, &key); + while (tuple = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tuple)) + { + Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + + if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0) + { + heap_delete(tgrel, &tuple->t_ctid); + tgfound++; + } + else + found++; + } + if (tgfound == 0) + elog(WARN, "DropTrigger: there is no trigger %s on relation %s", + stmt->trigname, stmt->relname); + if (tgfound > 1) + elog(NOTICE, "DropTrigger: found (and deleted) %d trigger %s on relation %s", + tgfound, stmt->trigname, stmt->relname); + heap_endscan(tgscan); + RelationUnsetLockForWrite(tgrel); + heap_close(tgrel); + + /* update pg_class */ + relrdesc = heap_openr(RelationRelationName); + tuple = ClassNameIndexScan(relrdesc, stmt->relname); + if (!PointerIsValid(tuple)) + { + heap_close(relrdesc); + elog(WARN, "DropTrigger: relation %s not found in pg_class", stmt->relname); + } + ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found; + RelationInvalidateHeapTuple(relrdesc, tuple); + oldTID = tuple->t_ctid; + heap_replace(relrdesc, &oldTID, tuple); + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs); + CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, tuple); + CatalogCloseIndices(Num_pg_class_indices, ridescs); + pfree(tuple); heap_close(relrdesc); - elog(WARN, "DropTrigger: relation %s not found in pg_class", stmt->relname); - } - ((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found; - RelationInvalidateHeapTuple (relrdesc, tuple); - oldTID = tuple->t_ctid; - heap_replace (relrdesc, &oldTID, tuple); - CatalogOpenIndices (Num_pg_class_indices, Name_pg_class_indices, ridescs); - CatalogIndexInsert (ridescs, Num_pg_class_indices, relrdesc, tuple); - CatalogCloseIndices (Num_pg_class_indices, ridescs); - pfree(tuple); - heap_close(relrdesc); - - CommandCounterIncrement (); - oldcxt = MemoryContextSwitchTo ((MemoryContext)CacheCxt); - FreeTriggerDesc (rel); - rel->rd_rel->reltriggers = found; - if ( found > 0 ) - RelationBuildTriggers (rel); - MemoryContextSwitchTo (oldcxt); - heap_close (rel); - return; + + CommandCounterIncrement(); + oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); + FreeTriggerDesc(rel); + rel->rd_rel->reltriggers = found; + if (found > 0) + RelationBuildTriggers(rel); + MemoryContextSwitchTo(oldcxt); + heap_close(rel); + return; } -void -RelationRemoveTriggers (Relation rel) +void +RelationRemoveTriggers(Relation rel) { - Relation tgrel; - HeapScanDesc tgscan; - ScanKeyData key; - HeapTuple tup; - - tgrel = heap_openr (TriggerRelationName); - RelationSetLockForWrite (tgrel); - ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, - ObjectIdEqualRegProcedure, rel->rd_id); - - tgscan = heap_beginscan (tgrel, 0, NowTimeQual, 1, &key); - - while (tup = heap_getnext (tgscan, 0, (Buffer *)NULL), PointerIsValid(tup)) - heap_delete (tgrel, &tup->t_ctid); - - heap_endscan (tgscan); - RelationUnsetLockForWrite (tgrel); - heap_close (tgrel); + Relation tgrel; + HeapScanDesc tgscan; + ScanKeyData key; + HeapTuple tup; + + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForWrite(tgrel); + ScanKeyEntryInitialize(&key, 0, Anum_pg_trigger_tgrelid, + ObjectIdEqualRegProcedure, rel->rd_id); + + tgscan = heap_beginscan(tgrel, 0, NowTimeQual, 1, &key); + + while (tup = heap_getnext(tgscan, 0, (Buffer *) NULL), PointerIsValid(tup)) + heap_delete(tgrel, &tup->t_ctid); + + heap_endscan(tgscan); + RelationUnsetLockForWrite(tgrel); + heap_close(tgrel); } void -RelationBuildTriggers (Relation relation) +RelationBuildTriggers(Relation relation) { - TriggerDesc *trigdesc = (TriggerDesc *) palloc (sizeof (TriggerDesc)); - int ntrigs = relation->rd_rel->reltriggers; - Trigger *triggers = NULL; - Trigger *build; - Relation tgrel; - Form_pg_trigger pg_trigger; - Relation irel; - ScanKeyData skey; - HeapTuple tuple; - IndexScanDesc sd; - RetrieveIndexResult indexRes; - Buffer buffer; - ItemPointer iptr; - struct varlena *val; - bool isnull; - int found; - - memset (trigdesc, 0, sizeof (TriggerDesc)); - - ScanKeyEntryInitialize(&skey, - (bits16)0x0, - (AttrNumber)1, - (RegProcedure)ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relation->rd_id)); - - tgrel = heap_openr(TriggerRelationName); - RelationSetLockForRead (tgrel); - irel = index_openr(TriggerRelidIndex); - sd = index_beginscan(irel, false, 1, &skey); - - for (found = 0; ; ) - { - indexRes = index_getnext(sd, ForwardScanDirection); - if (!indexRes) - break; - - iptr = &indexRes->heap_iptr; - tuple = heap_fetch(tgrel, NowTimeQual, iptr, &buffer); - pfree(indexRes); - if (!HeapTupleIsValid(tuple)) - continue; - if ( found == ntrigs ) - elog (WARN, "RelationBuildTriggers: unexpected record found for rel %.*s", - NAMEDATALEN, relation->rd_rel->relname.data); - - pg_trigger = (Form_pg_trigger) GETSTRUCT (tuple); - - if ( triggers == NULL ) - triggers = (Trigger *) palloc (sizeof (Trigger)); - else - triggers = (Trigger *) repalloc (triggers, (found + 1) * sizeof (Trigger)); - build = &(triggers[found]); - - build->tgname = nameout (&(pg_trigger->tgname)); - build->tgfoid = pg_trigger->tgfoid; - build->tgfunc = NULL; - build->tgtype = pg_trigger->tgtype; - build->tgnargs = pg_trigger->tgnargs; - memcpy (build->tgattr, &(pg_trigger->tgattr), 8 * sizeof (int16)); - val = (struct varlena*) fastgetattr (tuple, - Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull); - if ( isnull ) - elog (WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", - NAMEDATALEN, relation->rd_rel->relname.data); - if ( build->tgnargs > 0 ) - { - char *p; - int i; - - val = (struct varlena*) fastgetattr (tuple, - Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull); - if ( isnull ) - elog (WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", - NAMEDATALEN, relation->rd_rel->relname.data); - p = (char *) VARDATA (val); - build->tgargs = (char**) palloc (build->tgnargs * sizeof (char*)); - for (i = 0; i < build->tgnargs; i++) - { - build->tgargs[i] = (char*) palloc (strlen (p) + 1); - strcpy (build->tgargs[i], p); - p += strlen (p) + 1; - } - } - else - build->tgargs = NULL; - - found++; - ReleaseBuffer(buffer); - } - - if ( found < ntrigs ) - elog (WARN, "RelationBuildTriggers: %d record not found for rel %.*s", - ntrigs - found, - NAMEDATALEN, relation->rd_rel->relname.data); - - index_endscan (sd); - pfree (sd); - index_close (irel); - RelationUnsetLockForRead (tgrel); - heap_close (tgrel); - - /* Build trigdesc */ - trigdesc->triggers = triggers; - for (found = 0; found < ntrigs; found++) - { - build = &(triggers[found]); - DescribeTrigger (trigdesc, build); - } - - relation->trigdesc = trigdesc; - + TriggerDesc *trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc)); + int ntrigs = relation->rd_rel->reltriggers; + Trigger *triggers = NULL; + Trigger *build; + Relation tgrel; + Form_pg_trigger pg_trigger; + Relation irel; + ScanKeyData skey; + HeapTuple tuple; + IndexScanDesc sd; + RetrieveIndexResult indexRes; + Buffer buffer; + ItemPointer iptr; + struct varlena *val; + bool isnull; + int found; + + memset(trigdesc, 0, sizeof(TriggerDesc)); + + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relation->rd_id)); + + tgrel = heap_openr(TriggerRelationName); + RelationSetLockForRead(tgrel); + irel = index_openr(TriggerRelidIndex); + sd = index_beginscan(irel, false, 1, &skey); + + for (found = 0;;) + { + indexRes = index_getnext(sd, ForwardScanDirection); + if (!indexRes) + break; + + iptr = &indexRes->heap_iptr; + tuple = heap_fetch(tgrel, NowTimeQual, iptr, &buffer); + pfree(indexRes); + if (!HeapTupleIsValid(tuple)) + continue; + if (found == ntrigs) + elog(WARN, "RelationBuildTriggers: unexpected record found for rel %.*s", + NAMEDATALEN, relation->rd_rel->relname.data); + + pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + + if (triggers == NULL) + triggers = (Trigger *) palloc(sizeof(Trigger)); + else + triggers = (Trigger *) repalloc(triggers, (found + 1) * sizeof(Trigger)); + build = &(triggers[found]); + + build->tgname = nameout(&(pg_trigger->tgname)); + build->tgfoid = pg_trigger->tgfoid; + build->tgfunc = NULL; + build->tgtype = pg_trigger->tgtype; + build->tgnargs = pg_trigger->tgnargs; + memcpy(build->tgattr, &(pg_trigger->tgattr), 8 * sizeof(int16)); + val = (struct varlena *) fastgetattr(tuple, + Anum_pg_trigger_tgargs, + tgrel->rd_att, &isnull); + if (isnull) + elog(WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", + NAMEDATALEN, relation->rd_rel->relname.data); + if (build->tgnargs > 0) + { + char *p; + int i; + + val = (struct varlena *) fastgetattr(tuple, + Anum_pg_trigger_tgargs, + tgrel->rd_att, &isnull); + if (isnull) + elog(WARN, "RelationBuildTriggers: tgargs IS NULL for rel %.*s", + NAMEDATALEN, relation->rd_rel->relname.data); + p = (char *) VARDATA(val); + build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *)); + for (i = 0; i < build->tgnargs; i++) + { + build->tgargs[i] = (char *) palloc(strlen(p) + 1); + strcpy(build->tgargs[i], p); + p += strlen(p) + 1; + } + } + else + build->tgargs = NULL; + + found++; + ReleaseBuffer(buffer); + } + + if (found < ntrigs) + elog(WARN, "RelationBuildTriggers: %d record not found for rel %.*s", + ntrigs - found, + NAMEDATALEN, relation->rd_rel->relname.data); + + index_endscan(sd); + pfree(sd); + index_close(irel); + RelationUnsetLockForRead(tgrel); + heap_close(tgrel); + + /* Build trigdesc */ + trigdesc->triggers = triggers; + for (found = 0; found < ntrigs; found++) + { + build = &(triggers[found]); + DescribeTrigger(trigdesc, build); + } + + relation->trigdesc = trigdesc; + } -void -FreeTriggerDesc (Relation relation) +void +FreeTriggerDesc(Relation relation) { - TriggerDesc *trigdesc = relation->trigdesc; - Trigger ***t; - Trigger *trigger; - int i; - - if ( trigdesc == NULL ) - return; - - t = trigdesc->tg_before_statement; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - t = trigdesc->tg_before_row; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - t = trigdesc->tg_after_row; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - t = trigdesc->tg_after_statement; - for (i = 0; i < 3; i++) - if ( t[i] != NULL ) - pfree (t[i]); - - trigger = trigdesc->triggers; - for (i = 0; i < relation->rd_rel->reltriggers; i++) - { - pfree (trigger->tgname); - if ( trigger->tgnargs > 0 ) - { - while ( --(trigger->tgnargs) >= 0 ) - pfree (trigger->tgargs[trigger->tgnargs]); - pfree (trigger->tgargs); - } - trigger++; - } - pfree (trigdesc->triggers); - pfree (trigdesc); - relation->trigdesc = NULL; - return; + TriggerDesc *trigdesc = relation->trigdesc; + Trigger ***t; + Trigger *trigger; + int i; + + if (trigdesc == NULL) + return; + + t = trigdesc->tg_before_statement; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + t = trigdesc->tg_before_row; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + t = trigdesc->tg_after_row; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + t = trigdesc->tg_after_statement; + for (i = 0; i < 3; i++) + if (t[i] != NULL) + pfree(t[i]); + + trigger = trigdesc->triggers; + for (i = 0; i < relation->rd_rel->reltriggers; i++) + { + pfree(trigger->tgname); + if (trigger->tgnargs > 0) + { + while (--(trigger->tgnargs) >= 0) + pfree(trigger->tgargs[trigger->tgnargs]); + pfree(trigger->tgargs); + } + trigger++; + } + pfree(trigdesc->triggers); + pfree(trigdesc); + relation->trigdesc = NULL; + return; } static void -DescribeTrigger (TriggerDesc *trigdesc, Trigger *trigger) +DescribeTrigger(TriggerDesc * trigdesc, Trigger * trigger) { - uint16 *n; - Trigger ***t, ***tp; - - if ( TRIGGER_FOR_ROW (trigger->tgtype) ) /* Is ROW/STATEMENT trigger */ - { - if ( TRIGGER_FOR_BEFORE (trigger->tgtype) ) - { - n = trigdesc->n_before_row; - t = trigdesc->tg_before_row; - } - else - { - n = trigdesc->n_after_row; - t = trigdesc->tg_after_row; - } - } - else /* STATEMENT (NI) */ - { - if ( TRIGGER_FOR_BEFORE (trigger->tgtype) ) - { - n = trigdesc->n_before_statement; - t = trigdesc->tg_before_statement; - } - else - { - n = trigdesc->n_after_statement; - t = trigdesc->tg_after_statement; - } - } - - if ( TRIGGER_FOR_INSERT (trigger->tgtype) ) - { - tp = &(t[TRIGGER_EVENT_INSERT]); - if ( *tp == NULL ) - *tp = (Trigger **) palloc (sizeof (Trigger *)); - else - *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_INSERT] + 1) * - sizeof (Trigger *)); - (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger; - (n[TRIGGER_EVENT_INSERT])++; - } - - if ( TRIGGER_FOR_DELETE (trigger->tgtype) ) - { - tp = &(t[TRIGGER_EVENT_DELETE]); - if ( *tp == NULL ) - *tp = (Trigger **) palloc (sizeof (Trigger *)); - else - *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_DELETE] + 1) * - sizeof (Trigger *)); - (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger; - (n[TRIGGER_EVENT_DELETE])++; - } - - if ( TRIGGER_FOR_UPDATE (trigger->tgtype) ) - { - tp = &(t[TRIGGER_EVENT_UPDATE]); - if ( *tp == NULL ) - *tp = (Trigger **) palloc (sizeof (Trigger *)); - else - *tp = (Trigger **) repalloc (*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * - sizeof (Trigger *)); - (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger; - (n[TRIGGER_EVENT_UPDATE])++; - } - + uint16 *n; + Trigger ***t, + ***tp; + + if (TRIGGER_FOR_ROW(trigger->tgtype)) /* Is ROW/STATEMENT + * trigger */ + { + if (TRIGGER_FOR_BEFORE(trigger->tgtype)) + { + n = trigdesc->n_before_row; + t = trigdesc->tg_before_row; + } + else + { + n = trigdesc->n_after_row; + t = trigdesc->tg_after_row; + } + } + else +/* STATEMENT (NI) */ + { + if (TRIGGER_FOR_BEFORE(trigger->tgtype)) + { + n = trigdesc->n_before_statement; + t = trigdesc->tg_before_statement; + } + else + { + n = trigdesc->n_after_statement; + t = trigdesc->tg_after_statement; + } + } + + if (TRIGGER_FOR_INSERT(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_INSERT]); + if (*tp == NULL) + *tp = (Trigger **) palloc(sizeof(Trigger *)); + else + *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) * + sizeof(Trigger *)); + (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger; + (n[TRIGGER_EVENT_INSERT])++; + } + + if (TRIGGER_FOR_DELETE(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_DELETE]); + if (*tp == NULL) + *tp = (Trigger **) palloc(sizeof(Trigger *)); + else + *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) * + sizeof(Trigger *)); + (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger; + (n[TRIGGER_EVENT_DELETE])++; + } + + if (TRIGGER_FOR_UPDATE(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_UPDATE]); + if (*tp == NULL) + *tp = (Trigger **) palloc(sizeof(Trigger *)); + else + *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * + sizeof(Trigger *)); + (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger; + (n[TRIGGER_EVENT_UPDATE])++; + } + } -HeapTuple -ExecBRInsertTriggers (Relation rel, HeapTuple tuple) +HeapTuple +ExecBRInsertTriggers(Relation rel, HeapTuple tuple) { - int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; - Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; - HeapTuple newtuple = tuple; - int nargs; - int i; - - CurrentTriggerData = (TriggerData *) palloc (sizeof (TriggerData)); - CurrentTriggerData->tg_event = TRIGGER_EVENT_INSERT|TRIGGER_EVENT_ROW; - CurrentTriggerData->tg_relation = rel; - CurrentTriggerData->tg_newtuple = NULL; - for (i = 0; i < ntrigs; i++) - { - CurrentTriggerData->tg_trigtuple = newtuple; - CurrentTriggerData->tg_trigger = trigger[i]; - if ( trigger[i]->tgfunc == NULL ) - fmgr_info (trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs); - newtuple = (HeapTuple) ( (*(trigger[i]->tgfunc)) () ); - if ( newtuple == NULL ) - break; - } - pfree (CurrentTriggerData); - CurrentTriggerData = NULL; - return (newtuple); + int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; + Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; + HeapTuple newtuple = tuple; + int nargs; + int i; + + CurrentTriggerData = (TriggerData *) palloc(sizeof(TriggerData)); + CurrentTriggerData->tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW; + CurrentTriggerData->tg_relation = rel; + CurrentTriggerData->tg_newtuple = NULL; + for (i = 0; i < ntrigs; i++) + { + CurrentTriggerData->tg_trigtuple = newtuple; + CurrentTriggerData->tg_trigger = trigger[i]; + if (trigger[i]->tgfunc == NULL) + fmgr_info(trigger[i]->tgfoid, &(trigger[i]->tgfunc), &nargs); + newtuple = (HeapTuple) ((*(trigger[i]->tgfunc)) ()); + if (newtuple == NULL) + break; + } + pfree(CurrentTriggerData); + CurrentTriggerData = NULL; + return (newtuple); } void -ExecARInsertTriggers (Relation rel, HeapTuple tuple) +ExecARInsertTriggers(Relation rel, HeapTuple tuple) { - - return; + + return; } bool -ExecBRDeleteTriggers (Relation rel, ItemPointer tupleid) +ExecBRDeleteTriggers(Relation rel, ItemPointer tupleid) { - - return (true); + + return (true); } void -ExecARDeleteTriggers (Relation rel, ItemPointer tupleid) +ExecARDeleteTriggers(Relation rel, ItemPointer tupleid) { - - return; + + return; } HeapTuple -ExecBRUpdateTriggers (Relation rel, ItemPointer tupleid, HeapTuple tuple) +ExecBRUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple tuple) { - - return (tuple); + + return (tuple); } void -ExecARUpdateTriggers (Relation rel, ItemPointer tupleid, HeapTuple tuple) +ExecARUpdateTriggers(Relation rel, ItemPointer tupleid, HeapTuple tuple) { - - return; + + return; } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 0c480581179..30690f0f32b 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * vacuum.c-- - * the postgres vacuum cleaner + * the postgres vacuum cleaner * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.42 1997/08/22 04:13:08 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.43 1997/09/07 04:41:02 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -48,2220 +48,2364 @@ #include <storage/bufpage.h> #include "storage/shmem.h" #ifndef HAVE_GETRUSAGE -# include <rusagestub.h> -#else -# include <sys/time.h> -# include <sys/resource.h> -#endif +#include <rusagestub.h> +#else +#include <sys/time.h> +#include <sys/resource.h> +#endif #include <port-protos.h> -bool VacuumRunning = false; +bool VacuumRunning = false; -static Portal vc_portal; +static Portal vc_portal; -static int MESSAGE_LEVEL; /* message level */ +static int MESSAGE_LEVEL; /* message level */ #define swapLong(a,b) {long tmp; tmp=a; a=b; b=tmp;} #define swapInt(a,b) {int tmp; tmp=a; a=b; b=tmp;} #define swapDatum(a,b) {Datum tmp; tmp=a; a=b; b=tmp;} #define VacAttrStatsEqValid(stats) ( stats->f_cmpeq != NULL ) #define VacAttrStatsLtGtValid(stats) ( stats->f_cmplt != NULL && \ - stats->f_cmpgt != NULL && \ - RegProcedureIsValid(stats->outfunc) ) - + stats->f_cmpgt != NULL && \ + RegProcedureIsValid(stats->outfunc) ) + /* non-export function prototypes */ -static void vc_init(void); -static void vc_shutdown(void); -static void vc_vacuum(NameData *VacRelP, bool analyze, List *va_cols); -static VRelList vc_getrels(NameData *VacRelP); -static void vc_vacone (Oid relid, bool analyze, List *va_cols); -static void vc_scanheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl); -static void vc_rpfheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel); -static void vc_vacheap (VRelStats *vacrelstats, Relation onerel, VPageList vpl); -static void vc_vacpage (Page page, VPageDescr vpd, Relation archrel); -static void vc_vaconeind (VPageList vpl, Relation indrel, int nhtups); -static void vc_scanoneind (Relation indrel, int nhtups); -static void vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple htup); -static void vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum *bucket, int16 *bucket_len); -static void vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats *vacrelstats); -static void vc_delhilowstats (Oid relid, int attcnt, int *attnums); -static void vc_setpagelock(Relation rel, BlockNumber blkno); -static VPageDescr vc_tidreapped (ItemPointer itemptr, VPageList vpl); -static void vc_reappage (VPageList vpl, VPageDescr vpc); -static void vc_vpinsert (VPageList vpl, VPageDescr vpnew); -static void vc_free(VRelList vrl); -static void vc_getindices (Oid relid, int *nindices, Relation **Irel); -static void vc_clsindices (int nindices, Relation *Irel); +static void vc_init(void); +static void vc_shutdown(void); +static void vc_vacuum(NameData * VacRelP, bool analyze, List * va_cols); +static VRelList vc_getrels(NameData * VacRelP); +static void vc_vacone(Oid relid, bool analyze, List * va_cols); +static void vc_scanheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl); +static void vc_rpfheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl, int nindices, Relation * Irel); +static void vc_vacheap(VRelStats * vacrelstats, Relation onerel, VPageList vpl); +static void vc_vacpage(Page page, VPageDescr vpd, Relation archrel); +static void vc_vaconeind(VPageList vpl, Relation indrel, int nhtups); +static void vc_scanoneind(Relation indrel, int nhtups); +static void vc_attrstats(Relation onerel, VRelStats * vacrelstats, HeapTuple htup); +static void vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum * bucket, int16 * bucket_len); +static void vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats * vacrelstats); +static void vc_delhilowstats(Oid relid, int attcnt, int *attnums); +static void vc_setpagelock(Relation rel, BlockNumber blkno); +static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl); +static void vc_reappage(VPageList vpl, VPageDescr vpc); +static void vc_vpinsert(VPageList vpl, VPageDescr vpnew); +static void vc_free(VRelList vrl); +static void vc_getindices(Oid relid, int *nindices, Relation ** Irel); +static void vc_clsindices(int nindices, Relation * Irel); static Relation vc_getarchrel(Relation heaprel); -static void vc_archive(Relation archrel, HeapTuple htup); -static bool vc_isarchrel(char *rname); -static void vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc); -static char * vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *)); -static int vc_cmp_blk (char *left, char *right); -static int vc_cmp_offno (char *left, char *right); -static bool vc_enough_space (VPageDescr vpd, Size len); +static void vc_archive(Relation archrel, HeapTuple htup); +static bool vc_isarchrel(char *rname); +static void vc_mkindesc(Relation onerel, int nindices, Relation * Irel, IndDesc ** Idesc); +static char *vc_find_eq(char *bot, int nelem, int size, char *elm, int (*compar) (char *, char *)); +static int vc_cmp_blk(char *left, char *right); +static int vc_cmp_offno(char *left, char *right); +static bool vc_enough_space(VPageDescr vpd, Size len); void -vacuum(char *vacrel, bool verbose, bool analyze, List *va_spec) +vacuum(char *vacrel, bool verbose, bool analyze, List * va_spec) { - char *pname; - MemoryContext old; - PortalVariableMemory pmem; - NameData VacRel; - List *le; - List *va_cols = NIL; - - /* - * 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); - vc_portal = CreatePortal(pname); - pfree(pname); - - if (verbose) - MESSAGE_LEVEL = NOTICE; - else - MESSAGE_LEVEL = DEBUG; - - /* vacrel gets de-allocated on transaction commit */ - if (vacrel) - strcpy(VacRel.data,vacrel); - - pmem = PortalGetVariableMemory(vc_portal); - old = MemoryContextSwitchTo((MemoryContext)pmem); - - Assert ( va_spec == NIL || analyze ); - foreach (le, va_spec) - { - char *col = (char*)lfirst(le); - char *dest; - - dest = (char*) palloc (strlen (col) + 1); - strcpy (dest, col); - va_cols = lappend (va_cols, dest); - } - MemoryContextSwitchTo(old); - - /* initialize vacuum cleaner */ - vc_init(); - - /* vacuum the database */ - if (vacrel) - vc_vacuum (&VacRel, analyze, va_cols); - else - vc_vacuum (NULL, analyze, NIL); - - PortalDestroy (&vc_portal); - - /* clean up */ - vc_shutdown(); + char *pname; + MemoryContext old; + PortalVariableMemory pmem; + NameData VacRel; + List *le; + List *va_cols = NIL; + + /* + * 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); + vc_portal = CreatePortal(pname); + pfree(pname); + + if (verbose) + MESSAGE_LEVEL = NOTICE; + else + MESSAGE_LEVEL = DEBUG; + + /* vacrel gets de-allocated on transaction commit */ + if (vacrel) + strcpy(VacRel.data, vacrel); + + pmem = PortalGetVariableMemory(vc_portal); + old = MemoryContextSwitchTo((MemoryContext) pmem); + + Assert(va_spec == NIL || analyze); + foreach(le, va_spec) + { + char *col = (char *) lfirst(le); + char *dest; + + dest = (char *) palloc(strlen(col) + 1); + strcpy(dest, col); + va_cols = lappend(va_cols, dest); + } + MemoryContextSwitchTo(old); + + /* initialize vacuum cleaner */ + vc_init(); + + /* vacuum the database */ + if (vacrel) + vc_vacuum(&VacRel, analyze, va_cols); + else + vc_vacuum(NULL, analyze, NIL); + + PortalDestroy(&vc_portal); + + /* clean up */ + vc_shutdown(); } /* - * vc_init(), vc_shutdown() -- start up and shut down the vacuum cleaner. + * 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. + * 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(). + * 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() { - int fd; + int fd; - if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0) - elog(WARN, "can't create lock file -- another vacuum cleaner running?"); + if ((fd = open("pg_vlock", O_CREAT | O_EXCL, 0600)) < 0) + elog(WARN, "can't create lock file -- another vacuum cleaner running?"); - close(fd); + 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. - */ + /* + * 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; + VacuumRunning = true; - /* matches the StartTransaction in PostgresMain() */ - CommitTransactionCommand(); + /* matches the StartTransaction in PostgresMain() */ + CommitTransactionCommand(); } static void vc_shutdown() { - /* on entry, not in a transaction */ - if (unlink("pg_vlock") < 0) - elog(WARN, "vacuum: can't destroy lock file!"); + /* 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; + /* okay, we're done */ + VacuumRunning = false; - /* matches the CommitTransaction in PostgresMain() */ - StartTransactionCommand(); + /* matches the CommitTransaction in PostgresMain() */ + StartTransactionCommand(); } void vc_abort() { - /* on abort, remove the vacuum cleaner lock file */ - unlink("pg_vlock"); + /* on abort, remove the vacuum cleaner lock file */ + unlink("pg_vlock"); - VacuumRunning = false; + VacuumRunning = false; } /* - * vc_vacuum() -- vacuum the database. + * 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. + * 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(NameData *VacRelP, bool analyze, List *va_cols) +vc_vacuum(NameData * VacRelP, bool analyze, List * va_cols) { - VRelList vrl, cur; + VRelList vrl, + cur; + + /* get list of relations */ + vrl = vc_getrels(VacRelP); - /* get list of relations */ - vrl = vc_getrels(VacRelP); + if (analyze && VacRelP == NULL && vrl != NULL) + vc_delhilowstats(InvalidOid, 0, NULL); - if ( analyze && VacRelP == NULL && vrl != NULL ) - vc_delhilowstats (InvalidOid, 0, NULL); - - /* vacuum each heap relation */ - for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) - vc_vacone (cur->vrl_relid, analyze, va_cols); + /* vacuum each heap relation */ + for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next) + vc_vacone(cur->vrl_relid, analyze, va_cols); - vc_free(vrl); + vc_free(vrl); } -static VRelList -vc_getrels(NameData *VacRelP) +static VRelList +vc_getrels(NameData * VacRelP) { - Relation pgclass; - TupleDesc pgcdesc; - HeapScanDesc pgcscan; - HeapTuple pgctup; - Buffer buf; - PortalVariableMemory portalmem; - MemoryContext old; - VRelList vrl, cur; - Datum d; - char *rname; - char rkind; - int16 smgrno; - bool n; - ScanKeyData pgckey; - bool found = false; - - StartTransactionCommand(); - - if (VacRelP->data) { - ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname, - NameEqualRegProcedure, - PointerGetDatum(VacRelP->data)); - } else { - ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind, - CharacterEqualRegProcedure, CharGetDatum('r')); - } - - portalmem = PortalGetVariableMemory(vc_portal); - vrl = cur = (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))) { - - found = true; - - /* - * 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. - */ + Relation pgclass; + TupleDesc pgcdesc; + HeapScanDesc pgcscan; + HeapTuple pgctup; + Buffer buf; + PortalVariableMemory portalmem; + MemoryContext old; + VRelList vrl, + cur; + Datum d; + char *rname; + char rkind; + int16 smgrno; + bool n; + ScanKeyData pgckey; + bool found = false; + + StartTransactionCommand(); + + if (VacRelP->data) + { + ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname, + NameEqualRegProcedure, + PointerGetDatum(VacRelP->data)); + } + else + { + ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind, + CharacterEqualRegProcedure, CharGetDatum('r')); + } - d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname, - pgcdesc, &n); - rname = (char*)d; + portalmem = PortalGetVariableMemory(vc_portal); + vrl = cur = (VRelList) NULL; - /* skip archive relations */ - if (vc_isarchrel(rname)) { - ReleaseBuffer(buf); - continue; - } + pgclass = heap_openr(RelationRelationName); + pgcdesc = RelationGetTupleDescriptor(pgclass); + + pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey); - /* don't vacuum large objects for now - something breaks when we do */ - if ( (strlen(rname) >= 5) && rname[0] == 'x' && - rname[1] == 'i' && rname[2] == 'n' && - (rname[3] == 'v' || rname[3] == 'x') && - rname[4] >= '0' && rname[4] <= '9') + while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) { - elog (NOTICE, "Rel %s: can't vacuum LargeObjects now", - rname); - ReleaseBuffer(buf); - continue; - } - d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr, - pgcdesc, &n); - smgrno = DatumGetInt16(d); + found = true; - /* skip write-once storage managers */ - if (smgriswo(smgrno)) { - ReleaseBuffer(buf); - continue; - } + /* + * 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_relkind, - pgcdesc, &n); + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname, + pgcdesc, &n); + rname = (char *) d; - rkind = DatumGetChar(d); + /* skip archive relations */ + if (vc_isarchrel(rname)) + { + ReleaseBuffer(buf); + continue; + } - /* skip system relations */ - if (rkind != 'r') { - ReleaseBuffer(buf); - elog(NOTICE, "Vacuum: can not process index and certain system tables" ); - 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; - } - MemoryContextSwitchTo(old); + /* + * don't vacuum large objects for now - something breaks when we + * do + */ + if ((strlen(rname) >= 5) && rname[0] == 'x' && + rname[1] == 'i' && rname[2] == 'n' && + (rname[3] == 'v' || rname[3] == 'x') && + rname[4] >= '0' && rname[4] <= '9') + { + elog(NOTICE, "Rel %s: can't vacuum LargeObjects now", + 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; + } + + d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relkind, + pgcdesc, &n); + + rkind = DatumGetChar(d); + + /* skip system relations */ + if (rkind != 'r') + { + ReleaseBuffer(buf); + elog(NOTICE, "Vacuum: can not process index and certain system tables"); + continue; + } - cur->vrl_relid = pgctup->t_oid; - cur->vrl_next = (VRelList) NULL; + /* 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; + } + MemoryContextSwitchTo(old); - /* wei hates it if you forget to do this */ - ReleaseBuffer(buf); - } - if (found == false) - elog(NOTICE, "Vacuum: table not found" ); + cur->vrl_relid = pgctup->t_oid; + cur->vrl_next = (VRelList) NULL; - - heap_endscan(pgcscan); - heap_close(pgclass); + /* wei hates it if you forget to do this */ + ReleaseBuffer(buf); + } + if (found == false) + elog(NOTICE, "Vacuum: table not found"); + + + heap_endscan(pgcscan); + heap_close(pgclass); - CommitTransactionCommand(); + CommitTransactionCommand(); - return (vrl); + return (vrl); } /* - * vc_vacone() -- vacuum one heap relation + * vc_vacone() -- vacuum one heap relation * - * This routine vacuums a single heap, cleans out its indices, and - * updates its statistics npages and ntups statistics. + * This routine vacuums a single heap, cleans out its indices, and + * updates its statistics npages and ntups 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. + * 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 (Oid relid, bool analyze, List *va_cols) +vc_vacone(Oid relid, bool analyze, List * va_cols) { - Relation pgclass; - TupleDesc pgcdesc; - HeapTuple pgctup, pgttup; - Buffer pgcbuf; - HeapScanDesc pgcscan; - Relation onerel; - ScanKeyData pgckey; - VPageListData Vvpl; /* List of pages to vacuum and/or clean indices */ - VPageListData Fvpl; /* List of pages with space enough for re-using */ - VPageDescr *vpp; - Relation *Irel; - int32 nindices, i; - VRelStats *vacrelstats; - - StartTransactionCommand(); - - ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(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))) { + Relation pgclass; + TupleDesc pgcdesc; + HeapTuple pgctup, + pgttup; + Buffer pgcbuf; + HeapScanDesc pgcscan; + Relation onerel; + ScanKeyData pgckey; + VPageListData Vvpl; /* List of pages to vacuum and/or clean + * indices */ + VPageListData Fvpl; /* List of pages with space enough for + * re-using */ + VPageDescr *vpp; + Relation *Irel; + int32 nindices, + i; + VRelStats *vacrelstats; + + StartTransactionCommand(); + + ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(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(relid); + + vacrelstats = (VRelStats *) palloc(sizeof(VRelStats)); + vacrelstats->relid = relid; + vacrelstats->npages = vacrelstats->ntups = 0; + vacrelstats->hasindex = false; + if (analyze && !IsSystemRelationName((RelationGetRelationName(onerel))->data)) + { + int attr_cnt, + *attnums = NULL; + AttributeTupleForm *attr; + + attr_cnt = onerel->rd_att->natts; + attr = onerel->rd_att->attrs; + + if (va_cols != NIL) + { + int tcnt = 0; + List *le; + + if (length(va_cols) > attr_cnt) + elog(WARN, "vacuum: too many attributes specified for relation %s", + (RelationGetRelationName(onerel))->data); + attnums = (int *) palloc(attr_cnt * sizeof(int)); + foreach(le, va_cols) + { + char *col = (char *) lfirst(le); + + for (i = 0; i < attr_cnt; i++) + { + if (namestrcmp(&(attr[i]->attname), col) == 0) + break; + } + if (i < attr_cnt) /* found */ + attnums[tcnt++] = i; + else + { + elog(WARN, "vacuum: there is no attribute %s in %s", + col, (RelationGetRelationName(onerel))->data); + } + } + attr_cnt = tcnt; + } + + vacrelstats->vacattrstats = + (VacAttrStats *) palloc(attr_cnt * sizeof(VacAttrStats)); + + for (i = 0; i < attr_cnt; i++) + { + Operator func_operator; + OperatorTupleForm pgopform; + VacAttrStats *stats; + + stats = &vacrelstats->vacattrstats[i]; + stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); + memmove(stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); + stats->best = stats->guess1 = stats->guess2 = 0; + stats->max = stats->min = 0; + stats->best_len = stats->guess1_len = stats->guess2_len = 0; + stats->max_len = stats->min_len = 0; + stats->initialized = false; + stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; + stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; + + func_operator = oper("=", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + int nargs; + + pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmpeq), &nargs); + } + else + stats->f_cmpeq = NULL; + + func_operator = oper("<", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + int nargs; + + pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmplt), &nargs); + } + else + stats->f_cmplt = NULL; + + func_operator = oper(">", stats->attr->atttypid, stats->attr->atttypid, true); + if (func_operator != NULL) + { + int nargs; + + pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); + fmgr_info(pgopform->oprcode, &(stats->f_cmpgt), &nargs); + } + else + stats->f_cmpgt = NULL; + + pgttup = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(stats->attr->atttypid), + 0, 0, 0); + if (HeapTupleIsValid(pgttup)) + stats->outfunc = ((TypeTupleForm) GETSTRUCT(pgttup))->typoutput; + else + stats->outfunc = InvalidOid; + } + vacrelstats->va_natts = attr_cnt; + vc_delhilowstats(relid, ((attnums) ? attr_cnt : 0), attnums); + if (attnums) + pfree(attnums); + } + else + { + vacrelstats->va_natts = 0; + vacrelstats->vacattrstats = (VacAttrStats *) NULL; + } + + /* we require the relation to be locked until the indices are cleaned */ + RelationSetLockForWrite(onerel); + + /* scan it */ + Vvpl.vpl_npages = Fvpl.vpl_npages = 0; + vc_scanheap(vacrelstats, onerel, &Vvpl, &Fvpl); + + /* Now open indices */ + Irel = (Relation *) NULL; + vc_getindices(vacrelstats->relid, &nindices, &Irel); + + if (nindices > 0) + vacrelstats->hasindex = true; + else + vacrelstats->hasindex = false; + + /* Clean/scan index relation(s) */ + if (Irel != (Relation *) NULL) + { + if (Vvpl.vpl_npages > 0) + { + for (i = 0; i < nindices; i++) + vc_vaconeind(&Vvpl, Irel[i], vacrelstats->ntups); + } + else +/* just scan indices to update statistic */ + { + for (i = 0; i < nindices; i++) + vc_scanoneind(Irel[i], vacrelstats->ntups); + } + } + + if (Fvpl.vpl_npages > 0) /* Try to shrink heap */ + vc_rpfheap(vacrelstats, onerel, &Vvpl, &Fvpl, nindices, Irel); + else + { + if (Irel != (Relation *) NULL) + vc_clsindices(nindices, Irel); + if (Vvpl.vpl_npages > 0)/* Clean pages from Vvpl list */ + vc_vacheap(vacrelstats, onerel, &Vvpl); + } + + /* ok - free Vvpl list of reapped pages */ + if (Vvpl.vpl_npages > 0) + { + vpp = Vvpl.vpl_pgdesc; + for (i = 0; i < Vvpl.vpl_npages; i++, vpp++) + pfree(*vpp); + pfree(Vvpl.vpl_pgdesc); + if (Fvpl.vpl_npages > 0) + pfree(Fvpl.vpl_pgdesc); + } + + /* all done with this class */ + heap_close(onerel); heap_endscan(pgcscan); heap_close(pgclass); - CommitTransactionCommand(); - return; - } - - /* now open the class and vacuum it */ - onerel = heap_open(relid); - - vacrelstats = (VRelStats *) palloc(sizeof(VRelStats)); - vacrelstats->relid = relid; - vacrelstats->npages = vacrelstats->ntups = 0; - vacrelstats->hasindex = false; - if ( analyze && !IsSystemRelationName ((RelationGetRelationName (onerel))->data) ) - { - int attr_cnt, *attnums = NULL; - AttributeTupleForm *attr; - - attr_cnt = onerel->rd_att->natts; - attr = onerel->rd_att->attrs; - - if ( va_cols != NIL ) - { - int tcnt = 0; - List *le; - - if ( length (va_cols) > attr_cnt ) - elog (WARN, "vacuum: too many attributes specified for relation %s", - (RelationGetRelationName(onerel))->data); - attnums = (int*) palloc (attr_cnt * sizeof (int)); - foreach (le, va_cols) - { - char *col = (char*) lfirst(le); - - for (i = 0; i < attr_cnt; i++) - { - if ( namestrcmp (&(attr[i]->attname), col) == 0 ) - break; - } - if ( i < attr_cnt ) /* found */ - attnums[tcnt++] = i; - else - { - elog (WARN, "vacuum: there is no attribute %s in %s", - col, (RelationGetRelationName(onerel))->data); - } - } - attr_cnt = tcnt; - } - - vacrelstats->vacattrstats = - (VacAttrStats *) palloc (attr_cnt * sizeof(VacAttrStats)); - - for (i = 0; i < attr_cnt; i++) - { - Operator func_operator; - OperatorTupleForm pgopform; - VacAttrStats *stats; - - stats = &vacrelstats->vacattrstats[i]; - stats->attr = palloc(ATTRIBUTE_TUPLE_SIZE); - memmove (stats->attr, attr[((attnums) ? attnums[i] : i)], ATTRIBUTE_TUPLE_SIZE); - stats->best = stats->guess1 = stats->guess2 = 0; - stats->max = stats->min = 0; - stats->best_len = stats->guess1_len = stats->guess2_len = 0; - stats->max_len = stats->min_len = 0; - stats->initialized = false; - stats->best_cnt = stats->guess1_cnt = stats->guess1_hits = stats->guess2_hits = 0; - stats->max_cnt = stats->min_cnt = stats->null_cnt = stats->nonnull_cnt = 0; - - func_operator = oper("=",stats->attr->atttypid,stats->attr->atttypid,true); - if (func_operator != NULL) - { - int nargs; - - pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); - fmgr_info (pgopform->oprcode, &(stats->f_cmpeq), &nargs); - } - else - stats->f_cmpeq = NULL; - - func_operator = oper("<",stats->attr->atttypid,stats->attr->atttypid,true); - if (func_operator != NULL) - { - int nargs; - - pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); - fmgr_info (pgopform->oprcode, &(stats->f_cmplt), &nargs); - } - else - stats->f_cmplt = NULL; - - func_operator = oper(">",stats->attr->atttypid,stats->attr->atttypid,true); - if (func_operator != NULL) - { - int nargs; - - pgopform = (OperatorTupleForm) GETSTRUCT(func_operator); - fmgr_info (pgopform->oprcode, &(stats->f_cmpgt), &nargs); - } - else - stats->f_cmpgt = NULL; - - pgttup = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(stats->attr->atttypid), - 0,0,0); - if (HeapTupleIsValid(pgttup)) - stats->outfunc = ((TypeTupleForm) GETSTRUCT(pgttup))->typoutput; - else - stats->outfunc = InvalidOid; - } - vacrelstats->va_natts = attr_cnt; - vc_delhilowstats (relid, ((attnums) ? attr_cnt : 0), attnums); - if ( attnums ) - pfree (attnums); - } - else - { - vacrelstats->va_natts = 0; - vacrelstats->vacattrstats = (VacAttrStats *) NULL; - } - - /* we require the relation to be locked until the indices are cleaned */ - RelationSetLockForWrite(onerel); - - /* scan it */ - Vvpl.vpl_npages = Fvpl.vpl_npages = 0; - vc_scanheap(vacrelstats, onerel, &Vvpl, &Fvpl); - - /* Now open indices */ - Irel = (Relation *) NULL; - vc_getindices(vacrelstats->relid, &nindices, &Irel); - - if ( nindices > 0 ) - vacrelstats->hasindex = true; - else - vacrelstats->hasindex = false; - /* Clean/scan index relation(s) */ - if ( Irel != (Relation*) NULL ) - { - if ( Vvpl.vpl_npages > 0 ) - { - for (i = 0; i < nindices; i++) - vc_vaconeind (&Vvpl, Irel[i], vacrelstats->ntups); - } - else /* just scan indices to update statistic */ - { - for (i = 0; i < nindices; i++) - vc_scanoneind (Irel[i], vacrelstats->ntups); - } - } - - if ( Fvpl.vpl_npages > 0 ) /* Try to shrink heap */ - vc_rpfheap (vacrelstats, onerel, &Vvpl, &Fvpl, nindices, Irel); - else - { - if ( Irel != (Relation*) NULL ) - vc_clsindices (nindices, Irel); - if ( Vvpl.vpl_npages > 0 ) /* Clean pages from Vvpl list */ - vc_vacheap (vacrelstats, onerel, &Vvpl); - } - - /* ok - free Vvpl list of reapped pages */ - if ( Vvpl.vpl_npages > 0 ) - { - vpp = Vvpl.vpl_pgdesc; - for (i = 0; i < Vvpl.vpl_npages; i++, vpp++) - pfree(*vpp); - pfree (Vvpl.vpl_pgdesc); - if ( Fvpl.vpl_npages > 0 ) - pfree (Fvpl.vpl_pgdesc); - } - - /* all done with this class */ - heap_close(onerel); - heap_endscan(pgcscan); - heap_close(pgclass); - - /* update statistics in pg_class */ - vc_updstats(vacrelstats->relid, vacrelstats->npages, vacrelstats->ntups, - vacrelstats->hasindex, vacrelstats); - - /* next command frees attribute stats */ - - CommitTransactionCommand(); + /* update statistics in pg_class */ + vc_updstats(vacrelstats->relid, vacrelstats->npages, vacrelstats->ntups, + vacrelstats->hasindex, vacrelstats); + + /* next command frees attribute stats */ + + CommitTransactionCommand(); } /* - * vc_scanheap() -- scan an open heap relation + * vc_scanheap() -- scan an open heap relation * - * This routine sets commit times, constructs Vvpl list of - * empty/uninitialized pages and pages with dead tuples and - * ~LP_USED line pointers, constructs Fvpl list of pages - * appropriate for purposes of shrinking and maintains statistics - * on the number of live tuples in a heap. + * This routine sets commit times, constructs Vvpl list of + * empty/uninitialized pages and pages with dead tuples and + * ~LP_USED line pointers, constructs Fvpl list of pages + * appropriate for purposes of shrinking and maintains statistics + * on the number of live tuples in a heap. */ static void -vc_scanheap (VRelStats *vacrelstats, Relation onerel, +vc_scanheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl, VPageList Fvpl) { - int nblocks, blkno; - ItemId itemid; - ItemPointer itemptr; - HeapTuple htup; - Buffer buf; - Page page, tempPage = NULL; - OffsetNumber offnum, maxoff; - bool pgchanged, tupgone, dobufrel, notup; - char *relname; - VPageDescr vpc, vp; - uint32 nvac, ntups, nunused, ncrash, nempg, nnepg, nchpg, nemend; - Size frsize, frsusf; - Size min_tlen = MAXTUPLEN; - Size max_tlen = 0; - int32 i/*, attr_cnt*/; - struct rusage ru0, ru1; - bool do_shrinking = true; - - getrusage(RUSAGE_SELF, &ru0); - - nvac = ntups = nunused = ncrash = nempg = nnepg = nchpg = nemend = 0; - frsize = frsusf = 0; - - relname = (RelationGetRelationName(onerel))->data; - - nblocks = RelationGetNumberOfBlocks(onerel); - - vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber)); - vpc->vpd_nusd = 0; - - for (blkno = 0; blkno < nblocks; blkno++) { - buf = ReadBuffer(onerel, blkno); - page = BufferGetPage(buf); - vpc->vpd_blkno = blkno; - vpc->vpd_noff = 0; - - if (PageIsNew(page)) { - elog (NOTICE, "Rel %s: Uninitialized page %u - fixing", - relname, blkno); - PageInit (page, BufferGetPageSize (buf), 0); - vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower; - frsize += (vpc->vpd_free - sizeof (ItemIdData)); - nnepg++; - nemend++; - vc_reappage (Vvpl, vpc); - WriteBuffer(buf); - continue; - } - - if (PageIsEmpty(page)) { - vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower; - frsize += (vpc->vpd_free - sizeof (ItemIdData)); - nempg++; - nemend++; - vc_reappage (Vvpl, vpc); - ReleaseBuffer(buf); - continue; - } + int nblocks, + blkno; + ItemId itemid; + ItemPointer itemptr; + HeapTuple htup; + Buffer buf; + Page page, + tempPage = NULL; + OffsetNumber offnum, + maxoff; + bool pgchanged, + tupgone, + dobufrel, + notup; + char *relname; + VPageDescr vpc, + vp; + uint32 nvac, + ntups, + nunused, + ncrash, + nempg, + nnepg, + nchpg, + nemend; + Size frsize, + frsusf; + Size min_tlen = MAXTUPLEN; + Size max_tlen = 0; + int32 i /* , attr_cnt */ ; + struct rusage ru0, + ru1; + bool do_shrinking = true; + + getrusage(RUSAGE_SELF, &ru0); + + nvac = ntups = nunused = ncrash = nempg = nnepg = nchpg = nemend = 0; + frsize = frsusf = 0; + + relname = (RelationGetRelationName(onerel))->data; + + nblocks = RelationGetNumberOfBlocks(onerel); + + vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber)); + vpc->vpd_nusd = 0; + + for (blkno = 0; blkno < nblocks; blkno++) + { + buf = ReadBuffer(onerel, blkno); + page = BufferGetPage(buf); + vpc->vpd_blkno = blkno; + vpc->vpd_noff = 0; - pgchanged = false; - notup = true; - maxoff = PageGetMaxOffsetNumber(page); - for (offnum = FirstOffsetNumber; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) { - itemid = PageGetItemId(page, offnum); - - /* - * Collect un-used items too - it's possible to have - * indices pointing here after crash. - */ - if (!ItemIdIsUsed(itemid)) { - vpc->vpd_voff[vpc->vpd_noff++] = offnum; - nunused++; - continue; - } - - htup = (HeapTuple) PageGetItem(page, itemid); - tupgone = false; - - if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) && - TransactionIdIsValid((TransactionId)htup->t_xmin)) { - - if (TransactionIdDidAbort(htup->t_xmin)) { - tupgone = true; - } else if (TransactionIdDidCommit(htup->t_xmin)) { - htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin); - pgchanged = true; - } else if ( !TransactionIdIsInProgress (htup->t_xmin) ) { - /* - * Not Aborted, Not Committed, Not in Progress - - * so it from crashed process. - vadim 11/26/96 - */ - ncrash++; - tupgone = true; + if (PageIsNew(page)) + { + elog(NOTICE, "Rel %s: Uninitialized page %u - fixing", + relname, blkno); + PageInit(page, BufferGetPageSize(buf), 0); + vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; + frsize += (vpc->vpd_free - sizeof(ItemIdData)); + nnepg++; + nemend++; + vc_reappage(Vvpl, vpc); + WriteBuffer(buf); + continue; } - else + + if (PageIsEmpty(page)) { - elog (NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation", - relname, blkno, offnum, htup->t_xmin); - do_shrinking = false; + vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; + frsize += (vpc->vpd_free - sizeof(ItemIdData)); + nempg++; + nemend++; + vc_reappage(Vvpl, vpc); + ReleaseBuffer(buf); + continue; } - } - if (TransactionIdIsValid((TransactionId)htup->t_xmax)) - { - if (TransactionIdDidAbort(htup->t_xmax)) + pgchanged = false; + notup = true; + maxoff = PageGetMaxOffsetNumber(page); + for (offnum = FirstOffsetNumber; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) { - StoreInvalidTransactionId(&(htup->t_xmax)); - pgchanged = true; + itemid = PageGetItemId(page, offnum); + + /* + * Collect un-used items too - it's possible to have indices + * pointing here after crash. + */ + if (!ItemIdIsUsed(itemid)) + { + vpc->vpd_voff[vpc->vpd_noff++] = offnum; + nunused++; + continue; + } + + htup = (HeapTuple) PageGetItem(page, itemid); + tupgone = false; + + if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) && + TransactionIdIsValid((TransactionId) htup->t_xmin)) + { + + if (TransactionIdDidAbort(htup->t_xmin)) + { + tupgone = true; + } + else if (TransactionIdDidCommit(htup->t_xmin)) + { + htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin); + pgchanged = true; + } + else if (!TransactionIdIsInProgress(htup->t_xmin)) + { + + /* + * Not Aborted, Not Committed, Not in Progress - so it + * from crashed process. - vadim 11/26/96 + */ + ncrash++; + tupgone = true; + } + else + { + elog(NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation", + relname, blkno, offnum, htup->t_xmin); + do_shrinking = false; + } + } + + if (TransactionIdIsValid((TransactionId) htup->t_xmax)) + { + if (TransactionIdDidAbort(htup->t_xmax)) + { + StoreInvalidTransactionId(&(htup->t_xmax)); + pgchanged = true; + } + else if (TransactionIdDidCommit(htup->t_xmax)) + tupgone = true; + else if (!TransactionIdIsInProgress(htup->t_xmax)) + { + + /* + * Not Aborted, Not Committed, Not in Progress - so it + * from crashed process. - vadim 06/02/97 + */ + StoreInvalidTransactionId(&(htup->t_xmax)); + pgchanged = true; + } + else + { + elog(NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation", + relname, blkno, offnum, htup->t_xmax); + do_shrinking = false; + } + } + + /* + * Is it possible at all ? - vadim 11/26/96 + */ + if (!TransactionIdIsValid((TransactionId) htup->t_xmin)) + { + elog(NOTICE, "Rel %s: TID %u/%u: INSERT_TRANSACTION_ID IS INVALID. \ +DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.", + relname, blkno, offnum, + TransactionIdIsValid((TransactionId) htup->t_xmax), + tupgone); + } + + /* + * It's possibly! But from where it comes ? And should we fix + * it ? - vadim 11/28/96 + */ + itemptr = &(htup->t_ctid); + if (!ItemPointerIsValid(itemptr) || + BlockIdGetBlockNumber(&(itemptr->ip_blkid)) != blkno) + { + elog(NOTICE, "Rel %s: TID %u/%u: TID IN TUPLEHEADER %u/%u IS NOT THE SAME. TUPGONE %d.", + relname, blkno, offnum, + BlockIdGetBlockNumber(&(itemptr->ip_blkid)), + itemptr->ip_posid, tupgone); + } + + /* + * Other checks... + */ + if (htup->t_len != itemid->lp_len) + { + elog(NOTICE, "Rel %s: TID %u/%u: TUPLE_LEN IN PAGEHEADER %u IS NOT THE SAME AS IN TUPLEHEADER %u. TUPGONE %d.", + relname, blkno, offnum, + itemid->lp_len, htup->t_len, tupgone); + } + if (!OidIsValid(htup->t_oid)) + { + elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.", + relname, blkno, offnum, tupgone); + } + + if (tupgone) + { + ItemId lpp; + + if (tempPage == (Page) NULL) + { + Size pageSize; + + pageSize = PageGetPageSize(page); + tempPage = (Page) palloc(pageSize); + memmove(tempPage, page, pageSize); + } + + lpp = &(((PageHeader) tempPage)->pd_linp[offnum - 1]); + + /* mark it unused */ + lpp->lp_flags &= ~LP_USED; + + vpc->vpd_voff[vpc->vpd_noff++] = offnum; + nvac++; + + } + else + { + ntups++; + notup = false; + if (htup->t_len < min_tlen) + min_tlen = htup->t_len; + if (htup->t_len > max_tlen) + max_tlen = htup->t_len; + vc_attrstats(onerel, vacrelstats, htup); + } } - else if (TransactionIdDidCommit(htup->t_xmax)) - tupgone = true; - else if ( !TransactionIdIsInProgress (htup->t_xmax) ) { - /* - * Not Aborted, Not Committed, Not in Progress - - * so it from crashed process. - vadim 06/02/97 - */ - StoreInvalidTransactionId(&(htup->t_xmax)); - pgchanged = true; + + if (pgchanged) + { + WriteBuffer(buf); + dobufrel = false; + nchpg++; } else - { - elog (NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation", - relname, blkno, offnum, htup->t_xmax); - do_shrinking = false; + dobufrel = true; + if (tempPage != (Page) NULL) + { /* Some tuples are gone */ + PageRepairFragmentation(tempPage); + vpc->vpd_free = ((PageHeader) tempPage)->pd_upper - ((PageHeader) tempPage)->pd_lower; + frsize += vpc->vpd_free; + vc_reappage(Vvpl, vpc); + pfree(tempPage); + tempPage = (Page) NULL; } - } - - /* - * Is it possible at all ? - vadim 11/26/96 - */ - if ( !TransactionIdIsValid((TransactionId)htup->t_xmin) ) - { - elog (NOTICE, "Rel %s: TID %u/%u: INSERT_TRANSACTION_ID IS INVALID. \ -DELETE_TRANSACTION_ID_VALID %d, TUPGONE %d.", - relname, blkno, offnum, - TransactionIdIsValid((TransactionId)htup->t_xmax), - tupgone); - } - - /* - * It's possibly! But from where it comes ? - * And should we fix it ? - vadim 11/28/96 - */ - itemptr = &(htup->t_ctid); - if ( !ItemPointerIsValid (itemptr) || - BlockIdGetBlockNumber(&(itemptr->ip_blkid)) != blkno ) - { - elog (NOTICE, "Rel %s: TID %u/%u: TID IN TUPLEHEADER %u/%u IS NOT THE SAME. TUPGONE %d.", - relname, blkno, offnum, - BlockIdGetBlockNumber(&(itemptr->ip_blkid)), - itemptr->ip_posid, tupgone); - } - - /* - * Other checks... - */ - if ( htup->t_len != itemid->lp_len ) - { - elog (NOTICE, "Rel %s: TID %u/%u: TUPLE_LEN IN PAGEHEADER %u IS NOT THE SAME AS IN TUPLEHEADER %u. TUPGONE %d.", - relname, blkno, offnum, - itemid->lp_len, htup->t_len, tupgone); - } - if ( !OidIsValid(htup->t_oid) ) - { - elog (NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.", - relname, blkno, offnum, tupgone); - } - - if (tupgone) { - ItemId lpp; - - if ( tempPage == (Page) NULL ) - { - Size pageSize; - - pageSize = PageGetPageSize(page); - tempPage = (Page) palloc(pageSize); - memmove (tempPage, page, pageSize); + else if (vpc->vpd_noff > 0) + { /* there are only ~LP_USED line pointers */ + vpc->vpd_free = ((PageHeader) page)->pd_upper - ((PageHeader) page)->pd_lower; + frsize += vpc->vpd_free; + vc_reappage(Vvpl, vpc); } - - lpp = &(((PageHeader) tempPage)->pd_linp[offnum - 1]); - - /* mark it unused */ - lpp->lp_flags &= ~LP_USED; - - vpc->vpd_voff[vpc->vpd_noff++] = offnum; - nvac++; - - } else { - ntups++; - notup = false; - if ( htup->t_len < min_tlen ) - min_tlen = htup->t_len; - if ( htup->t_len > max_tlen ) - max_tlen = htup->t_len; - vc_attrstats(onerel, vacrelstats, htup); - } + if (dobufrel) + ReleaseBuffer(buf); + if (notup) + nemend++; + else + nemend = 0; } - if (pgchanged) { - WriteBuffer(buf); - dobufrel = false; - nchpg++; - } - else - dobufrel = true; - if ( tempPage != (Page) NULL ) - { /* Some tuples are gone */ - PageRepairFragmentation(tempPage); - vpc->vpd_free = ((PageHeader)tempPage)->pd_upper - ((PageHeader)tempPage)->pd_lower; - frsize += vpc->vpd_free; - vc_reappage (Vvpl, vpc); - pfree (tempPage); - tempPage = (Page) NULL; - } - else if ( vpc->vpd_noff > 0 ) - { /* there are only ~LP_USED line pointers */ - vpc->vpd_free = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower; - frsize += vpc->vpd_free; - vc_reappage (Vvpl, vpc); - } - if ( dobufrel ) - ReleaseBuffer(buf); - if ( notup ) - nemend++; - else - nemend = 0; - } - - pfree (vpc); - - /* save stats in the rel list for use later */ - vacrelstats->ntups = ntups; - vacrelstats->npages = nblocks; -/* vacrelstats->natts = attr_cnt;*/ - if ( ntups == 0 ) - min_tlen = max_tlen = 0; - vacrelstats->min_tlen = min_tlen; - vacrelstats->max_tlen = max_tlen; - - Vvpl->vpl_nemend = nemend; - Fvpl->vpl_nemend = nemend; - - /* - * Try to make Fvpl keeping in mind that we can't use free space - * of "empty" end-pages and last page if it reapped. - */ - if ( do_shrinking && Vvpl->vpl_npages - nemend > 0 ) - { - int nusf; /* blocks usefull for re-using */ - - nusf = Vvpl->vpl_npages - nemend; - if ( (Vvpl->vpl_pgdesc[nusf-1])->vpd_blkno == nblocks - nemend - 1 ) - nusf--; - - for (i = 0; i < nusf; i++) - { - vp = Vvpl->vpl_pgdesc[i]; - if ( vc_enough_space (vp, min_tlen) ) - { - vc_vpinsert (Fvpl, vp); - frsusf += vp->vpd_free; - } + pfree(vpc); + + /* save stats in the rel list for use later */ + vacrelstats->ntups = ntups; + vacrelstats->npages = nblocks; +/* vacrelstats->natts = attr_cnt;*/ + if (ntups == 0) + min_tlen = max_tlen = 0; + vacrelstats->min_tlen = min_tlen; + vacrelstats->max_tlen = max_tlen; + + Vvpl->vpl_nemend = nemend; + Fvpl->vpl_nemend = nemend; + + /* + * Try to make Fvpl keeping in mind that we can't use free space of + * "empty" end-pages and last page if it reapped. + */ + if (do_shrinking && Vvpl->vpl_npages - nemend > 0) + { + int nusf; /* blocks usefull for re-using */ + + nusf = Vvpl->vpl_npages - nemend; + if ((Vvpl->vpl_pgdesc[nusf - 1])->vpd_blkno == nblocks - nemend - 1) + nusf--; + + for (i = 0; i < nusf; i++) + { + vp = Vvpl->vpl_pgdesc[i]; + if (vc_enough_space(vp, min_tlen)) + { + vc_vpinsert(Fvpl, vp); + frsusf += vp->vpd_free; + } + } } - } - getrusage(RUSAGE_SELF, &ru1); - - elog (MESSAGE_LEVEL, "Rel %s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \ + getrusage(RUSAGE_SELF, &ru1); + + elog(MESSAGE_LEVEL, "Rel %s: Pages %u: Changed %u, Reapped %u, Empty %u, New %u; \ Tup %u: Vac %u, Crash %u, UnUsed %u, MinLen %u, MaxLen %u; Re-using: Free/Avail. Space %u/%u; EndEmpty/Avail. Pages %u/%u. Elapsed %u/%u sec.", - relname, - nblocks, nchpg, Vvpl->vpl_npages, nempg, nnepg, - ntups, nvac, ncrash, nunused, min_tlen, max_tlen, - frsize, frsusf, nemend, Fvpl->vpl_npages, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + relname, + nblocks, nchpg, Vvpl->vpl_npages, nempg, nnepg, + ntups, nvac, ncrash, nunused, min_tlen, max_tlen, + frsize, frsusf, nemend, Fvpl->vpl_npages, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); -} /* vc_scanheap */ +} /* vc_scanheap */ /* - * vc_rpfheap() -- try to repaire relation' fragmentation + * vc_rpfheap() -- try to repaire relation' fragmentation * - * This routine marks dead tuples as unused and tries re-use dead space - * by moving tuples (and inserting indices if needed). It constructs - * Nvpl list of free-ed pages (moved tuples) and clean indices - * for them after committing (in hack-manner - without losing locks - * and freeing memory!) current transaction. It truncates relation - * if some end-blocks are gone away. + * This routine marks dead tuples as unused and tries re-use dead space + * by moving tuples (and inserting indices if needed). It constructs + * Nvpl list of free-ed pages (moved tuples) and clean indices + * for them after committing (in hack-manner - without losing locks + * and freeing memory!) current transaction. It truncates relation + * if some end-blocks are gone away. */ static void -vc_rpfheap (VRelStats *vacrelstats, Relation onerel, - VPageList Vvpl, VPageList Fvpl, int nindices, Relation *Irel) +vc_rpfheap(VRelStats * vacrelstats, Relation onerel, + VPageList Vvpl, VPageList Fvpl, int nindices, Relation * Irel) { - TransactionId myXID; - CommandId myCID; - AbsoluteTime myCTM = 0; - Buffer buf, ToBuf; - int nblocks, blkno; - Page page, ToPage = NULL; - OffsetNumber offnum = 0, maxoff = 0, newoff, moff; - ItemId itemid, newitemid; - HeapTuple htup, newtup; - TupleDesc tupdesc = NULL; - Datum *idatum = NULL; - char *inulls = NULL; - InsertIndexResult iresult; - VPageListData Nvpl; - VPageDescr ToVpd = NULL, Fvplast, Vvplast, vpc, *vpp; - int ToVpI = 0; - IndDesc *Idesc, *idcur; - int Fblklast, Vblklast, i; - Size tlen; - int nmoved, Fnpages, Vnpages; - int nchkmvd, ntups; - bool isempty, dowrite; - Relation archrel; - struct rusage ru0, ru1; - - getrusage(RUSAGE_SELF, &ru0); - - myXID = GetCurrentTransactionId(); - myCID = GetCurrentCommandId(); - - if ( Irel != (Relation*) NULL ) /* preparation for index' inserts */ - { - vc_mkindesc (onerel, nindices, Irel, &Idesc); - tupdesc = RelationGetTupleDescriptor(onerel); - idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof (*idatum)); - inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof (*inulls)); - } - - /* if the relation has an archive, open it */ - if (onerel->rd_rel->relarch != 'n') - { - archrel = vc_getarchrel(onerel); - /* Archive tuples from "empty" end-pages */ - for ( vpp = Vvpl->vpl_pgdesc + Vvpl->vpl_npages - 1, - i = Vvpl->vpl_nemend; i > 0; i--, vpp-- ) + TransactionId myXID; + CommandId myCID; + AbsoluteTime myCTM = 0; + Buffer buf, + ToBuf; + int nblocks, + blkno; + Page page, + ToPage = NULL; + OffsetNumber offnum = 0, + maxoff = 0, + newoff, + moff; + ItemId itemid, + newitemid; + HeapTuple htup, + newtup; + TupleDesc tupdesc = NULL; + Datum *idatum = NULL; + char *inulls = NULL; + InsertIndexResult iresult; + VPageListData Nvpl; + VPageDescr ToVpd = NULL, + Fvplast, + Vvplast, + vpc, + *vpp; + int ToVpI = 0; + IndDesc *Idesc, + *idcur; + int Fblklast, + Vblklast, + i; + Size tlen; + int nmoved, + Fnpages, + Vnpages; + int nchkmvd, + ntups; + bool isempty, + dowrite; + Relation archrel; + struct rusage ru0, + ru1; + + getrusage(RUSAGE_SELF, &ru0); + + myXID = GetCurrentTransactionId(); + myCID = GetCurrentCommandId(); + + if (Irel != (Relation *) NULL) /* preparation for index' inserts */ { - if ( (*vpp)->vpd_noff > 0 ) - { - buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); - page = BufferGetPage(buf); - Assert ( !PageIsEmpty(page) ); - vc_vacpage (page, *vpp, archrel); - WriteBuffer (buf); - } + vc_mkindesc(onerel, nindices, Irel, &Idesc); + tupdesc = RelationGetTupleDescriptor(onerel); + idatum = (Datum *) palloc(INDEX_MAX_KEYS * sizeof(*idatum)); + inulls = (char *) palloc(INDEX_MAX_KEYS * sizeof(*inulls)); } - } - else - archrel = (Relation) NULL; - - Nvpl.vpl_npages = 0; - Fnpages = Fvpl->vpl_npages; - Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; - Fblklast = Fvplast->vpd_blkno; - Assert ( Vvpl->vpl_npages > Vvpl->vpl_nemend ); - Vnpages = Vvpl->vpl_npages - Vvpl->vpl_nemend; - Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; - Vblklast = Vvplast->vpd_blkno; - Assert ( Vblklast >= Fblklast ); - ToBuf = InvalidBuffer; - nmoved = 0; - - vpc = (VPageDescr) palloc (sizeof(VPageDescrData) + MaxOffsetNumber*sizeof(OffsetNumber)); - vpc->vpd_nusd = vpc->vpd_noff = 0; - - nblocks = vacrelstats->npages; - for (blkno = nblocks - Vvpl->vpl_nemend - 1; ; blkno--) - { - /* if it's reapped page and it was used by me - quit */ - if ( blkno == Fblklast && Fvplast->vpd_nusd > 0 ) - break; - - buf = ReadBuffer(onerel, blkno); - page = BufferGetPage(buf); - - vpc->vpd_noff = 0; - - isempty = PageIsEmpty(page); - - dowrite = false; - if ( blkno == Vblklast ) /* it's reapped page */ + + /* if the relation has an archive, open it */ + if (onerel->rd_rel->relarch != 'n') { - if ( Vvplast->vpd_noff > 0 ) /* there are dead tuples */ - { /* on this page - clean */ - Assert ( ! isempty ); - vc_vacpage (page, Vvplast, archrel); - dowrite = true; - } - else - { - Assert ( isempty ); - } - --Vnpages; - Assert ( Vnpages > 0 ); - /* get prev reapped page from Vvpl */ - Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; - Vblklast = Vvplast->vpd_blkno; - if ( blkno == Fblklast ) /* this page in Fvpl too */ - { - --Fnpages; - Assert ( Fnpages > 0 ); - Assert ( Fvplast->vpd_nusd == 0 ); - /* get prev reapped page from Fvpl */ - Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; - Fblklast = Fvplast->vpd_blkno; - } - Assert ( Fblklast <= Vblklast ); - if ( isempty ) - { - ReleaseBuffer(buf); - continue; - } + archrel = vc_getarchrel(onerel); + /* Archive tuples from "empty" end-pages */ + for (vpp = Vvpl->vpl_pgdesc + Vvpl->vpl_npages - 1, + i = Vvpl->vpl_nemend; i > 0; i--, vpp--) + { + if ((*vpp)->vpd_noff > 0) + { + buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); + page = BufferGetPage(buf); + Assert(!PageIsEmpty(page)); + vc_vacpage(page, *vpp, archrel); + WriteBuffer(buf); + } + } } else + archrel = (Relation) NULL; + + Nvpl.vpl_npages = 0; + Fnpages = Fvpl->vpl_npages; + Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; + Fblklast = Fvplast->vpd_blkno; + Assert(Vvpl->vpl_npages > Vvpl->vpl_nemend); + Vnpages = Vvpl->vpl_npages - Vvpl->vpl_nemend; + Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; + Vblklast = Vvplast->vpd_blkno; + Assert(Vblklast >= Fblklast); + ToBuf = InvalidBuffer; + nmoved = 0; + + vpc = (VPageDescr) palloc(sizeof(VPageDescrData) + MaxOffsetNumber * sizeof(OffsetNumber)); + vpc->vpd_nusd = vpc->vpd_noff = 0; + + nblocks = vacrelstats->npages; + for (blkno = nblocks - Vvpl->vpl_nemend - 1;; blkno--) { - Assert ( ! isempty ); - } + /* if it's reapped page and it was used by me - quit */ + if (blkno == Fblklast && Fvplast->vpd_nusd > 0) + break; - vpc->vpd_blkno = blkno; - maxoff = PageGetMaxOffsetNumber(page); - for (offnum = FirstOffsetNumber; - offnum <= maxoff; - offnum = OffsetNumberNext(offnum)) - { - itemid = PageGetItemId(page, offnum); - - if (!ItemIdIsUsed(itemid)) - continue; - - htup = (HeapTuple) PageGetItem(page, itemid); - tlen = htup->t_len; - - /* try to find new page for this tuple */ - if ( ToBuf == InvalidBuffer || - ! vc_enough_space (ToVpd, tlen) ) - { - if ( ToBuf != InvalidBuffer ) + buf = ReadBuffer(onerel, blkno); + page = BufferGetPage(buf); + + vpc->vpd_noff = 0; + + isempty = PageIsEmpty(page); + + dowrite = false; + if (blkno == Vblklast) /* it's reapped page */ { - WriteBuffer(ToBuf); - ToBuf = InvalidBuffer; - /* - * If no one tuple can't be added to this page - - * remove page from Fvpl. - vadim 11/27/96 - */ - if ( !vc_enough_space (ToVpd, vacrelstats->min_tlen) ) - { - if ( ToVpd != Fvplast ) - { - Assert ( Fnpages > ToVpI + 1 ); - memmove (Fvpl->vpl_pgdesc + ToVpI, - Fvpl->vpl_pgdesc + ToVpI + 1, - sizeof (VPageDescr*) * (Fnpages - ToVpI - 1)); - } - Assert ( Fnpages >= 1 ); - Fnpages--; - if ( Fnpages == 0 ) - break; - /* get prev reapped page from Fvpl */ - Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; - Fblklast = Fvplast->vpd_blkno; - } - } - for (i=0; i < Fnpages; i++) + if (Vvplast->vpd_noff > 0) /* there are dead tuples */ + { /* on this page - clean */ + Assert(!isempty); + vc_vacpage(page, Vvplast, archrel); + dowrite = true; + } + else + { + Assert(isempty); + } + --Vnpages; + Assert(Vnpages > 0); + /* get prev reapped page from Vvpl */ + Vvplast = Vvpl->vpl_pgdesc[Vnpages - 1]; + Vblklast = Vvplast->vpd_blkno; + if (blkno == Fblklast) /* this page in Fvpl too */ + { + --Fnpages; + Assert(Fnpages > 0); + Assert(Fvplast->vpd_nusd == 0); + /* get prev reapped page from Fvpl */ + Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; + Fblklast = Fvplast->vpd_blkno; + } + Assert(Fblklast <= Vblklast); + if (isempty) + { + ReleaseBuffer(buf); + continue; + } + } + else { - if ( vc_enough_space (Fvpl->vpl_pgdesc[i], tlen) ) - break; + Assert(!isempty); } - if ( i == Fnpages ) - break; /* can't move item anywhere */ - ToVpI = i; - ToVpd = Fvpl->vpl_pgdesc[ToVpI]; - ToBuf = ReadBuffer(onerel, ToVpd->vpd_blkno); - ToPage = BufferGetPage(ToBuf); - /* if this page was not used before - clean it */ - if ( ! PageIsEmpty(ToPage) && ToVpd->vpd_nusd == 0 ) - vc_vacpage (ToPage, ToVpd, archrel); - } - - /* copy tuple */ - newtup = (HeapTuple) palloc (tlen); - memmove((char *) newtup, (char *) htup, tlen); - - /* store transaction information */ - TransactionIdStore(myXID, &(newtup->t_xmin)); - newtup->t_cmin = myCID; - StoreInvalidTransactionId(&(newtup->t_xmax)); - newtup->t_tmin = INVALID_ABSTIME; - newtup->t_tmax = CURRENT_ABSTIME; - ItemPointerSetInvalid(&newtup->t_chain); - - /* add tuple to the page */ - newoff = PageAddItem (ToPage, (Item)newtup, tlen, - InvalidOffsetNumber, LP_USED); - if ( newoff == InvalidOffsetNumber ) - { - elog (WARN, "\ + + vpc->vpd_blkno = blkno; + maxoff = PageGetMaxOffsetNumber(page); + for (offnum = FirstOffsetNumber; + offnum <= maxoff; + offnum = OffsetNumberNext(offnum)) + { + itemid = PageGetItemId(page, offnum); + + if (!ItemIdIsUsed(itemid)) + continue; + + htup = (HeapTuple) PageGetItem(page, itemid); + tlen = htup->t_len; + + /* try to find new page for this tuple */ + if (ToBuf == InvalidBuffer || + !vc_enough_space(ToVpd, tlen)) + { + if (ToBuf != InvalidBuffer) + { + WriteBuffer(ToBuf); + ToBuf = InvalidBuffer; + + /* + * If no one tuple can't be added to this page - + * remove page from Fvpl. - vadim 11/27/96 + */ + if (!vc_enough_space(ToVpd, vacrelstats->min_tlen)) + { + if (ToVpd != Fvplast) + { + Assert(Fnpages > ToVpI + 1); + memmove(Fvpl->vpl_pgdesc + ToVpI, + Fvpl->vpl_pgdesc + ToVpI + 1, + sizeof(VPageDescr *) * (Fnpages - ToVpI - 1)); + } + Assert(Fnpages >= 1); + Fnpages--; + if (Fnpages == 0) + break; + /* get prev reapped page from Fvpl */ + Fvplast = Fvpl->vpl_pgdesc[Fnpages - 1]; + Fblklast = Fvplast->vpd_blkno; + } + } + for (i = 0; i < Fnpages; i++) + { + if (vc_enough_space(Fvpl->vpl_pgdesc[i], tlen)) + break; + } + if (i == Fnpages) + break; /* can't move item anywhere */ + ToVpI = i; + ToVpd = Fvpl->vpl_pgdesc[ToVpI]; + ToBuf = ReadBuffer(onerel, ToVpd->vpd_blkno); + ToPage = BufferGetPage(ToBuf); + /* if this page was not used before - clean it */ + if (!PageIsEmpty(ToPage) && ToVpd->vpd_nusd == 0) + vc_vacpage(ToPage, ToVpd, archrel); + } + + /* copy tuple */ + newtup = (HeapTuple) palloc(tlen); + memmove((char *) newtup, (char *) htup, tlen); + + /* store transaction information */ + TransactionIdStore(myXID, &(newtup->t_xmin)); + newtup->t_cmin = myCID; + StoreInvalidTransactionId(&(newtup->t_xmax)); + newtup->t_tmin = INVALID_ABSTIME; + newtup->t_tmax = CURRENT_ABSTIME; + ItemPointerSetInvalid(&newtup->t_chain); + + /* add tuple to the page */ + newoff = PageAddItem(ToPage, (Item) newtup, tlen, + InvalidOffsetNumber, LP_USED); + if (newoff == InvalidOffsetNumber) + { + elog(WARN, "\ failed to add item with len = %u to page %u (free space %u, nusd %u, noff %u)", - tlen, ToVpd->vpd_blkno, ToVpd->vpd_free, - ToVpd->vpd_nusd, ToVpd->vpd_noff); - } - newitemid = PageGetItemId(ToPage, newoff); - pfree (newtup); - newtup = (HeapTuple) PageGetItem(ToPage, newitemid); - ItemPointerSet(&(newtup->t_ctid), ToVpd->vpd_blkno, newoff); - - /* now logically delete end-tuple */ - TransactionIdStore(myXID, &(htup->t_xmax)); - htup->t_cmax = myCID; - memmove ((char*)&(htup->t_chain), (char*)&(newtup->t_ctid), sizeof (newtup->t_ctid)); - - ToVpd->vpd_nusd++; - nmoved++; - ToVpd->vpd_free = ((PageHeader)ToPage)->pd_upper - ((PageHeader)ToPage)->pd_lower; - vpc->vpd_voff[vpc->vpd_noff++] = offnum; - - /* insert index' tuples if needed */ - if ( Irel != (Relation*) NULL ) - { - for (i = 0, idcur = Idesc; i < nindices; i++, idcur++) + tlen, ToVpd->vpd_blkno, ToVpd->vpd_free, + ToVpd->vpd_nusd, ToVpd->vpd_noff); + } + newitemid = PageGetItemId(ToPage, newoff); + pfree(newtup); + newtup = (HeapTuple) PageGetItem(ToPage, newitemid); + ItemPointerSet(&(newtup->t_ctid), ToVpd->vpd_blkno, newoff); + + /* now logically delete end-tuple */ + TransactionIdStore(myXID, &(htup->t_xmax)); + htup->t_cmax = myCID; + memmove((char *) &(htup->t_chain), (char *) &(newtup->t_ctid), sizeof(newtup->t_ctid)); + + ToVpd->vpd_nusd++; + nmoved++; + ToVpd->vpd_free = ((PageHeader) ToPage)->pd_upper - ((PageHeader) ToPage)->pd_lower; + vpc->vpd_voff[vpc->vpd_noff++] = offnum; + + /* insert index' tuples if needed */ + if (Irel != (Relation *) NULL) + { + for (i = 0, idcur = Idesc; i < nindices; i++, idcur++) + { + FormIndexDatum( + idcur->natts, + (AttrNumber *) & (idcur->tform->indkey[0]), + newtup, + tupdesc, + InvalidBuffer, + idatum, + inulls, + idcur->finfoP); + iresult = index_insert( + Irel[i], + idatum, + inulls, + &(newtup->t_ctid), + onerel); + if (iresult) + pfree(iresult); + } + } + + } /* walk along page */ + + if (vpc->vpd_noff > 0) /* some tuples were moved */ { - FormIndexDatum ( - idcur->natts, - (AttrNumber *)&(idcur->tform->indkey[0]), - newtup, - tupdesc, - InvalidBuffer, - idatum, - inulls, - idcur->finfoP); - iresult = index_insert ( - Irel[i], - idatum, - inulls, - &(newtup->t_ctid), - onerel); - if (iresult) pfree(iresult); + vc_reappage(&Nvpl, vpc); + WriteBuffer(buf); } - } - - } /* walk along page */ + else if (dowrite) + WriteBuffer(buf); + else + ReleaseBuffer(buf); - if ( vpc->vpd_noff > 0 ) /* some tuples were moved */ + if (offnum <= maxoff) + break; /* some item(s) left */ + + } /* walk along relation */ + + blkno++; /* new number of blocks */ + + if (ToBuf != InvalidBuffer) { - vc_reappage (&Nvpl, vpc); - WriteBuffer(buf); + Assert(nmoved > 0); + WriteBuffer(ToBuf); } - else if ( dowrite ) - WriteBuffer(buf); - else - ReleaseBuffer(buf); - - if ( offnum <= maxoff ) - break; /* some item(s) left */ - - } /* walk along relation */ - - blkno++; /* new number of blocks */ - - if ( ToBuf != InvalidBuffer ) - { - Assert (nmoved > 0); - WriteBuffer(ToBuf); - } - - if ( nmoved > 0 ) - { - /* - * We have to commit our tuple' movings before we'll truncate - * relation, but we shouldn't lose our locks. And so - quick hack: - * flush buffers and record status of current transaction - * as committed, and continue. - vadim 11/13/96 - */ - FlushBufferPool(!TransactionFlushEnabled()); - TransactionIdCommit(myXID); - FlushBufferPool(!TransactionFlushEnabled()); - myCTM = TransactionIdGetCommitTime(myXID); - } - - /* - * Clean uncleaned reapped pages from Vvpl list - * and set commit' times for inserted tuples - */ - nchkmvd = 0; - for (i = 0, vpp = Vvpl->vpl_pgdesc; i < Vnpages; i++, vpp++) - { - Assert ( (*vpp)->vpd_blkno < blkno ); - buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); - page = BufferGetPage(buf); - if ( (*vpp)->vpd_nusd == 0 ) /* this page was not used */ + + if (nmoved > 0) { - /* noff == 0 in empty pages only - such pages should be re-used */ - Assert ( (*vpp)->vpd_noff > 0 ); - vc_vacpage (page, *vpp, archrel); + + /* + * We have to commit our tuple' movings before we'll truncate + * relation, but we shouldn't lose our locks. And so - quick hack: + * flush buffers and record status of current transaction as + * committed, and continue. - vadim 11/13/96 + */ + FlushBufferPool(!TransactionFlushEnabled()); + TransactionIdCommit(myXID); + FlushBufferPool(!TransactionFlushEnabled()); + myCTM = TransactionIdGetCommitTime(myXID); } - else /* this page was used */ + + /* + * Clean uncleaned reapped pages from Vvpl list and set commit' times + * for inserted tuples + */ + nchkmvd = 0; + for (i = 0, vpp = Vvpl->vpl_pgdesc; i < Vnpages; i++, vpp++) { - ntups = 0; - moff = PageGetMaxOffsetNumber(page); - for (newoff = FirstOffsetNumber; - newoff <= moff; - newoff = OffsetNumberNext(newoff)) - { - itemid = PageGetItemId(page, newoff); - if (!ItemIdIsUsed(itemid)) - continue; - htup = (HeapTuple) PageGetItem(page, itemid); - if ( TransactionIdEquals((TransactionId)htup->t_xmin, myXID) ) - { - htup->t_tmin = myCTM; - ntups++; - } - } - Assert ( (*vpp)->vpd_nusd == ntups ); - nchkmvd += ntups; + Assert((*vpp)->vpd_blkno < blkno); + buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); + page = BufferGetPage(buf); + if ((*vpp)->vpd_nusd == 0) /* this page was not used */ + { + + /* + * noff == 0 in empty pages only - such pages should be + * re-used + */ + Assert((*vpp)->vpd_noff > 0); + vc_vacpage(page, *vpp, archrel); + } + else +/* this page was used */ + { + ntups = 0; + moff = PageGetMaxOffsetNumber(page); + for (newoff = FirstOffsetNumber; + newoff <= moff; + newoff = OffsetNumberNext(newoff)) + { + itemid = PageGetItemId(page, newoff); + if (!ItemIdIsUsed(itemid)) + continue; + htup = (HeapTuple) PageGetItem(page, itemid); + if (TransactionIdEquals((TransactionId) htup->t_xmin, myXID)) + { + htup->t_tmin = myCTM; + ntups++; + } + } + Assert((*vpp)->vpd_nusd == ntups); + nchkmvd += ntups; + } + WriteBuffer(buf); } - WriteBuffer (buf); - } - Assert ( nmoved == nchkmvd ); + Assert(nmoved == nchkmvd); + + getrusage(RUSAGE_SELF, &ru1); - getrusage(RUSAGE_SELF, &ru1); - - elog (MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. \ + elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. \ Elapsed %u/%u sec.", - (RelationGetRelationName(onerel))->data, - nblocks, blkno, nmoved, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); - - if ( Nvpl.vpl_npages > 0 ) - { - /* vacuum indices again if needed */ - if ( Irel != (Relation*) NULL ) + (RelationGetRelationName(onerel))->data, + nblocks, blkno, nmoved, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + + if (Nvpl.vpl_npages > 0) { - VPageDescr *vpleft, *vpright, vpsave; - - /* re-sort Nvpl.vpl_pgdesc */ - for (vpleft = Nvpl.vpl_pgdesc, - vpright = Nvpl.vpl_pgdesc + Nvpl.vpl_npages - 1; - vpleft < vpright; vpleft++, vpright--) - { - vpsave = *vpleft; *vpleft = *vpright; *vpright = vpsave; - } - for (i = 0; i < nindices; i++) - vc_vaconeind (&Nvpl, Irel[i], vacrelstats->ntups); + /* vacuum indices again if needed */ + if (Irel != (Relation *) NULL) + { + VPageDescr *vpleft, + *vpright, + vpsave; + + /* re-sort Nvpl.vpl_pgdesc */ + for (vpleft = Nvpl.vpl_pgdesc, + vpright = Nvpl.vpl_pgdesc + Nvpl.vpl_npages - 1; + vpleft < vpright; vpleft++, vpright--) + { + vpsave = *vpleft; + *vpleft = *vpright; + *vpright = vpsave; + } + for (i = 0; i < nindices; i++) + vc_vaconeind(&Nvpl, Irel[i], vacrelstats->ntups); + } + + /* + * clean moved tuples from last page in Nvpl list if some tuples + * left there + */ + if (vpc->vpd_noff > 0 && offnum <= maxoff) + { + Assert(vpc->vpd_blkno == blkno - 1); + buf = ReadBuffer(onerel, vpc->vpd_blkno); + page = BufferGetPage(buf); + ntups = 0; + maxoff = offnum; + for (offnum = FirstOffsetNumber; + offnum < maxoff; + offnum = OffsetNumberNext(offnum)) + { + itemid = PageGetItemId(page, offnum); + if (!ItemIdIsUsed(itemid)) + continue; + htup = (HeapTuple) PageGetItem(page, itemid); + Assert(TransactionIdEquals((TransactionId) htup->t_xmax, myXID)); + itemid->lp_flags &= ~LP_USED; + ntups++; + } + Assert(vpc->vpd_noff == ntups); + PageRepairFragmentation(page); + WriteBuffer(buf); + } + + /* now - free new list of reapped pages */ + vpp = Nvpl.vpl_pgdesc; + for (i = 0; i < Nvpl.vpl_npages; i++, vpp++) + pfree(*vpp); + pfree(Nvpl.vpl_pgdesc); } - /* - * clean moved tuples from last page in Nvpl list - * if some tuples left there - */ - if ( vpc->vpd_noff > 0 && offnum <= maxoff ) + /* truncate relation */ + if (blkno < nblocks) { - Assert (vpc->vpd_blkno == blkno - 1); - buf = ReadBuffer(onerel, vpc->vpd_blkno); - page = BufferGetPage (buf); - ntups = 0; - maxoff = offnum; - for (offnum = FirstOffsetNumber; - offnum < maxoff; - offnum = OffsetNumberNext(offnum)) - { - itemid = PageGetItemId(page, offnum); - if (!ItemIdIsUsed(itemid)) - continue; - htup = (HeapTuple) PageGetItem(page, itemid); - Assert ( TransactionIdEquals((TransactionId)htup->t_xmax, myXID) ); - itemid->lp_flags &= ~LP_USED; - ntups++; - } - Assert ( vpc->vpd_noff == ntups ); - PageRepairFragmentation(page); - WriteBuffer (buf); + blkno = smgrtruncate(onerel->rd_rel->relsmgr, onerel, blkno); + Assert(blkno >= 0); + vacrelstats->npages = blkno; /* set new number of blocks */ + } + + if (archrel != (Relation) NULL) + heap_close(archrel); + + if (Irel != (Relation *) NULL) /* pfree index' allocations */ + { + pfree(Idesc); + pfree(idatum); + pfree(inulls); + vc_clsindices(nindices, Irel); } - /* now - free new list of reapped pages */ - vpp = Nvpl.vpl_pgdesc; - for (i = 0; i < Nvpl.vpl_npages; i++, vpp++) - pfree(*vpp); - pfree (Nvpl.vpl_pgdesc); - } - - /* truncate relation */ - if ( blkno < nblocks ) - { - blkno = smgrtruncate (onerel->rd_rel->relsmgr, onerel, blkno); - Assert ( blkno >= 0 ); - vacrelstats->npages = blkno; /* set new number of blocks */ - } - - if ( archrel != (Relation) NULL ) - heap_close(archrel); - - if ( Irel != (Relation*) NULL ) /* pfree index' allocations */ - { - pfree (Idesc); - pfree (idatum); - pfree (inulls); - vc_clsindices (nindices, Irel); - } - - pfree (vpc); - -} /* vc_rpfheap */ + pfree(vpc); + +} /* vc_rpfheap */ /* - * vc_vacheap() -- free dead tuples + * vc_vacheap() -- free dead tuples * - * This routine marks dead tuples as unused and truncates relation - * if there are "empty" end-blocks. + * This routine marks dead tuples as unused and truncates relation + * if there are "empty" end-blocks. */ static void -vc_vacheap (VRelStats *vacrelstats, Relation onerel, VPageList Vvpl) +vc_vacheap(VRelStats * vacrelstats, Relation onerel, VPageList Vvpl) { - Buffer buf; - Page page; - VPageDescr *vpp; - Relation archrel; - int nblocks; - int i; - - nblocks = Vvpl->vpl_npages; - /* if the relation has an archive, open it */ - if (onerel->rd_rel->relarch != 'n') - archrel = vc_getarchrel(onerel); - else - { - archrel = (Relation) NULL; - nblocks -= Vvpl->vpl_nemend; /* nothing to do with them */ - } - - for (i = 0, vpp = Vvpl->vpl_pgdesc; i < nblocks; i++, vpp++) - { - if ( (*vpp)->vpd_noff > 0 ) + Buffer buf; + Page page; + VPageDescr *vpp; + Relation archrel; + int nblocks; + int i; + + nblocks = Vvpl->vpl_npages; + /* if the relation has an archive, open it */ + if (onerel->rd_rel->relarch != 'n') + archrel = vc_getarchrel(onerel); + else { - buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); - page = BufferGetPage (buf); - vc_vacpage (page, *vpp, archrel); - WriteBuffer (buf); + archrel = (Relation) NULL; + nblocks -= Vvpl->vpl_nemend; /* nothing to do with them */ + } + + for (i = 0, vpp = Vvpl->vpl_pgdesc; i < nblocks; i++, vpp++) + { + if ((*vpp)->vpd_noff > 0) + { + buf = ReadBuffer(onerel, (*vpp)->vpd_blkno); + page = BufferGetPage(buf); + vc_vacpage(page, *vpp, archrel); + WriteBuffer(buf); + } } - } - - /* truncate relation if there are some empty end-pages */ - if ( Vvpl->vpl_nemend > 0 ) - { - Assert ( vacrelstats->npages >= Vvpl->vpl_nemend ); - nblocks = vacrelstats->npages - Vvpl->vpl_nemend; - elog (MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u.", - (RelationGetRelationName(onerel))->data, - vacrelstats->npages, nblocks); - - /* - * we have to flush "empty" end-pages (if changed, but who knows it) - * before truncation - */ - FlushBufferPool(!TransactionFlushEnabled()); - nblocks = smgrtruncate (onerel->rd_rel->relsmgr, onerel, nblocks); - Assert ( nblocks >= 0 ); - vacrelstats->npages = nblocks; /* set new number of blocks */ - } + /* truncate relation if there are some empty end-pages */ + if (Vvpl->vpl_nemend > 0) + { + Assert(vacrelstats->npages >= Vvpl->vpl_nemend); + nblocks = vacrelstats->npages - Vvpl->vpl_nemend; + elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u.", + (RelationGetRelationName(onerel))->data, + vacrelstats->npages, nblocks); + + /* + * we have to flush "empty" end-pages (if changed, but who knows + * it) before truncation + */ + FlushBufferPool(!TransactionFlushEnabled()); + + nblocks = smgrtruncate(onerel->rd_rel->relsmgr, onerel, nblocks); + Assert(nblocks >= 0); + vacrelstats->npages = nblocks; /* set new number of blocks */ + } - if ( archrel != (Relation) NULL ) - heap_close(archrel); + if (archrel != (Relation) NULL) + heap_close(archrel); -} /* vc_vacheap */ +} /* vc_vacheap */ /* - * vc_vacpage() -- free (and archive if needed) dead tuples on a page - * and repaire its fragmentation. + * vc_vacpage() -- free (and archive if needed) dead tuples on a page + * and repaire its fragmentation. */ static void -vc_vacpage (Page page, VPageDescr vpd, Relation archrel) +vc_vacpage(Page page, VPageDescr vpd, Relation archrel) { - ItemId itemid; - HeapTuple htup; - int i; - - Assert ( vpd->vpd_nusd == 0 ); - for (i=0; i < vpd->vpd_noff; i++) - { - itemid = &(((PageHeader) page)->pd_linp[vpd->vpd_voff[i] - 1]); - if ( archrel != (Relation) NULL && ItemIdIsUsed(itemid) ) + ItemId itemid; + HeapTuple htup; + int i; + + Assert(vpd->vpd_nusd == 0); + for (i = 0; i < vpd->vpd_noff; i++) { - htup = (HeapTuple) PageGetItem (page, itemid); - vc_archive (archrel, htup); + itemid = &(((PageHeader) page)->pd_linp[vpd->vpd_voff[i] - 1]); + if (archrel != (Relation) NULL && ItemIdIsUsed(itemid)) + { + htup = (HeapTuple) PageGetItem(page, itemid); + vc_archive(archrel, htup); + } + itemid->lp_flags &= ~LP_USED; } - itemid->lp_flags &= ~LP_USED; - } - PageRepairFragmentation(page); + PageRepairFragmentation(page); -} /* vc_vacpage */ +} /* vc_vacpage */ /* - * _vc_scanoneind() -- scan one index relation to update statistic. + * _vc_scanoneind() -- scan one index relation to update statistic. * */ static void -vc_scanoneind (Relation indrel, int nhtups) +vc_scanoneind(Relation indrel, int nhtups) { - RetrieveIndexResult res; - IndexScanDesc iscan; - int nitups; - int nipages; - struct rusage ru0, ru1; + RetrieveIndexResult res; + IndexScanDesc iscan; + int nitups; + int nipages; + struct rusage ru0, + ru1; - getrusage(RUSAGE_SELF, &ru0); + getrusage(RUSAGE_SELF, &ru0); - /* walk through the entire index */ - iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); - nitups = 0; + /* walk through the entire index */ + iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); + nitups = 0; - while ((res = index_getnext(iscan, ForwardScanDirection)) - != (RetrieveIndexResult) NULL) - { - nitups++; - pfree(res); - } + while ((res = index_getnext(iscan, ForwardScanDirection)) + != (RetrieveIndexResult) NULL) + { + nitups++; + pfree(res); + } - index_endscan(iscan); + index_endscan(iscan); - /* now update statistics in pg_class */ - nipages = RelationGetNumberOfBlocks(indrel); - vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); + /* now update statistics in pg_class */ + nipages = RelationGetNumberOfBlocks(indrel); + vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); - getrusage(RUSAGE_SELF, &ru1); + getrusage(RUSAGE_SELF, &ru1); - elog (MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u. Elapsed %u/%u sec.", - indrel->rd_rel->relname.data, nipages, nitups, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + elog(MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u. Elapsed %u/%u sec.", + indrel->rd_rel->relname.data, nipages, nitups, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); - if ( nitups != nhtups ) - elog (NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", - indrel->rd_rel->relname.data, nitups, nhtups); + if (nitups != nhtups) + elog(NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", + indrel->rd_rel->relname.data, nitups, nhtups); -} /* vc_scanoneind */ +} /* vc_scanoneind */ /* - * vc_vaconeind() -- vacuum one index relation. + * vc_vaconeind() -- vacuum one index relation. * - * Vpl is the VPageList of the heap we're currently vacuuming. - * It's locked. Indrel 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. + * Vpl is the VPageList of the heap we're currently vacuuming. + * It's locked. Indrel 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. + * Finally, we arrange to update the index relation's statistics in + * pg_class. */ static void vc_vaconeind(VPageList vpl, Relation indrel, int nhtups) { - RetrieveIndexResult res; - IndexScanDesc iscan; - ItemPointer heapptr; - int nvac; - int nitups; - int nipages; - VPageDescr vp; - struct rusage ru0, ru1; - - getrusage(RUSAGE_SELF, &ru0); - - /* 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 ( (vp = vc_tidreapped (heapptr, vpl)) != (VPageDescr) NULL) + RetrieveIndexResult res; + IndexScanDesc iscan; + ItemPointer heapptr; + int nvac; + int nitups; + int nipages; + VPageDescr vp; + struct rusage ru0, + ru1; + + getrusage(RUSAGE_SELF, &ru0); + + /* 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 ((vp = vc_tidreapped(heapptr, vpl)) != (VPageDescr) NULL) + { #if 0 - elog(DEBUG, "<%x,%x> -> <%x,%x>", - ItemPointerGetBlockNumber(&(res->index_iptr)), - ItemPointerGetOffsetNumber(&(res->index_iptr)), - ItemPointerGetBlockNumber(&(res->heap_iptr)), - ItemPointerGetOffsetNumber(&(res->heap_iptr))); + elog(DEBUG, "<%x,%x> -> <%x,%x>", + ItemPointerGetBlockNumber(&(res->index_iptr)), + ItemPointerGetOffsetNumber(&(res->index_iptr)), + ItemPointerGetBlockNumber(&(res->heap_iptr)), + ItemPointerGetOffsetNumber(&(res->heap_iptr))); #endif - if ( vp->vpd_noff == 0 ) - { /* this is EmptyPage !!! */ - elog (NOTICE, "Ind %s: pointer to EmptyPage (blk %u off %u) - fixing", - indrel->rd_rel->relname.data, - vp->vpd_blkno, ItemPointerGetOffsetNumber(heapptr)); - } - ++nvac; - index_delete(indrel, &res->index_iptr); - } else { - nitups++; - } + if (vp->vpd_noff == 0) + { /* this is EmptyPage !!! */ + elog(NOTICE, "Ind %s: pointer to EmptyPage (blk %u off %u) - fixing", + indrel->rd_rel->relname.data, + vp->vpd_blkno, ItemPointerGetOffsetNumber(heapptr)); + } + ++nvac; + index_delete(indrel, &res->index_iptr); + } + else + { + nitups++; + } - /* be tidy */ - pfree(res); - } + /* be tidy */ + pfree(res); + } - index_endscan(iscan); + index_endscan(iscan); - /* now update statistics in pg_class */ - nipages = RelationGetNumberOfBlocks(indrel); - vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); + /* now update statistics in pg_class */ + nipages = RelationGetNumberOfBlocks(indrel); + vc_updstats(indrel->rd_id, nipages, nitups, false, NULL); - getrusage(RUSAGE_SELF, &ru1); + getrusage(RUSAGE_SELF, &ru1); - elog (MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.", - indrel->rd_rel->relname.data, nipages, nitups, nvac, - ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, - ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); + elog(MESSAGE_LEVEL, "Ind %s: Pages %u; Tuples %u: Deleted %u. Elapsed %u/%u sec.", + indrel->rd_rel->relname.data, nipages, nitups, nvac, + ru1.ru_stime.tv_sec - ru0.ru_stime.tv_sec, + ru1.ru_utime.tv_sec - ru0.ru_utime.tv_sec); - if ( nitups != nhtups ) - elog (NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", - indrel->rd_rel->relname.data, nitups, nhtups); + if (nitups != nhtups) + elog(NOTICE, "Ind %s: NUMBER OF INDEX' TUPLES (%u) IS NOT THE SAME AS HEAP' (%u)", + indrel->rd_rel->relname.data, nitups, nhtups); -} /* vc_vaconeind */ +} /* vc_vaconeind */ /* - * vc_tidreapped() -- is a particular tid reapped? + * vc_tidreapped() -- is a particular tid reapped? * - * vpl->VPageDescr_array is sorted in right order. + * vpl->VPageDescr_array is sorted in right order. */ -static VPageDescr +static VPageDescr vc_tidreapped(ItemPointer itemptr, VPageList vpl) { - OffsetNumber ioffno; - OffsetNumber *voff; - VPageDescr vp, *vpp; - VPageDescrData vpd; - - vpd.vpd_blkno = ItemPointerGetBlockNumber(itemptr); - ioffno = ItemPointerGetOffsetNumber(itemptr); - - vp = &vpd; - vpp = (VPageDescr*) vc_find_eq ((char*)(vpl->vpl_pgdesc), - vpl->vpl_npages, sizeof (VPageDescr), (char*)&vp, - vc_cmp_blk); - - if ( vpp == (VPageDescr*) NULL ) - return ((VPageDescr)NULL); - vp = *vpp; - - /* ok - we are on true page */ - - if ( vp->vpd_noff == 0 ) { /* this is EmptyPage !!! */ - return (vp); - } - - voff = (OffsetNumber*) vc_find_eq ((char*)(vp->vpd_voff), - vp->vpd_noff, sizeof (OffsetNumber), (char*)&ioffno, - vc_cmp_offno); + OffsetNumber ioffno; + OffsetNumber *voff; + VPageDescr vp, + *vpp; + VPageDescrData vpd; - if ( voff == (OffsetNumber*) NULL ) - return ((VPageDescr)NULL); + vpd.vpd_blkno = ItemPointerGetBlockNumber(itemptr); + ioffno = ItemPointerGetOffsetNumber(itemptr); - return (vp); + vp = &vpd; + vpp = (VPageDescr *) vc_find_eq((char *) (vpl->vpl_pgdesc), + vpl->vpl_npages, sizeof(VPageDescr), (char *) &vp, + vc_cmp_blk); -} /* vc_tidreapped */ + if (vpp == (VPageDescr *) NULL) + return ((VPageDescr) NULL); + vp = *vpp; + + /* ok - we are on true page */ + + if (vp->vpd_noff == 0) + { /* this is EmptyPage !!! */ + return (vp); + } + + voff = (OffsetNumber *) vc_find_eq((char *) (vp->vpd_voff), + vp->vpd_noff, sizeof(OffsetNumber), (char *) &ioffno, + vc_cmp_offno); + + if (voff == (OffsetNumber *) NULL) + return ((VPageDescr) NULL); + + return (vp); + +} /* vc_tidreapped */ /* - * vc_attrstats() -- compute column statistics used by the optimzer + * vc_attrstats() -- compute column statistics used by the optimzer * - * We compute the column min, max, null and non-null counts. - * Plus we attempt to find the count of the value that occurs most - * frequently in each column - * These figures are used to compute the selectivity of the column + * We compute the column min, max, null and non-null counts. + * Plus we attempt to find the count of the value that occurs most + * frequently in each column + * These figures are used to compute the selectivity of the column * - * We use a three-bucked cache to get the most frequent item - * The 'guess' buckets count hits. A cache miss causes guess1 - * to get the most hit 'guess' item in the most recent cycle, and - * the new item goes into guess2. Whenever the total count of hits - * of a 'guess' entry is larger than 'best', 'guess' becomes 'best'. + * We use a three-bucked cache to get the most frequent item + * The 'guess' buckets count hits. A cache miss causes guess1 + * to get the most hit 'guess' item in the most recent cycle, and + * the new item goes into guess2. Whenever the total count of hits + * of a 'guess' entry is larger than 'best', 'guess' becomes 'best'. * - * This method works perfectly for columns with unique values, and columns - * with only two unique values, plus nulls. + * This method works perfectly for columns with unique values, and columns + * with only two unique values, plus nulls. * - * It becomes less perfect as the number of unique values increases and - * their distribution in the table becomes more random. + * It becomes less perfect as the number of unique values increases and + * their distribution in the table becomes more random. * */ static void -vc_attrstats(Relation onerel, VRelStats *vacrelstats, HeapTuple htup) +vc_attrstats(Relation onerel, VRelStats * vacrelstats, HeapTuple htup) { - int i, attr_cnt = vacrelstats->va_natts; - VacAttrStats *vacattrstats = vacrelstats->vacattrstats; - TupleDesc tupDesc = onerel->rd_att; - Datum value; - bool isnull; - - for (i = 0; i < attr_cnt; i++) { - VacAttrStats *stats = &vacattrstats[i]; - bool value_hit = true; - - value = (Datum) heap_getattr (htup, InvalidBuffer, - stats->attr->attnum, tupDesc, &isnull); - - if (!VacAttrStatsEqValid(stats)) - continue; - - if (isnull) - stats->null_cnt++; - else { - stats->nonnull_cnt++; - if (stats->initialized == false) { - vc_bucketcpy(stats->attr, value, &stats->best, &stats->best_len); - /* best_cnt gets incremented later */ - vc_bucketcpy(stats->attr, value, &stats->guess1, &stats->guess1_len); - stats->guess1_cnt = stats->guess1_hits = 1; - vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); - stats->guess2_hits = 1; - if (VacAttrStatsLtGtValid(stats)) { - vc_bucketcpy(stats->attr, value, &stats->max , &stats->max_len); - vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); + int i, + attr_cnt = vacrelstats->va_natts; + VacAttrStats *vacattrstats = vacrelstats->vacattrstats; + TupleDesc tupDesc = onerel->rd_att; + Datum value; + bool isnull; + + for (i = 0; i < attr_cnt; i++) + { + VacAttrStats *stats = &vacattrstats[i]; + bool value_hit = true; + + value = (Datum) heap_getattr(htup, InvalidBuffer, + stats->attr->attnum, tupDesc, &isnull); + + if (!VacAttrStatsEqValid(stats)) + continue; + + if (isnull) + stats->null_cnt++; + else + { + stats->nonnull_cnt++; + if (stats->initialized == false) + { + vc_bucketcpy(stats->attr, value, &stats->best, &stats->best_len); + /* best_cnt gets incremented later */ + vc_bucketcpy(stats->attr, value, &stats->guess1, &stats->guess1_len); + stats->guess1_cnt = stats->guess1_hits = 1; + vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); + stats->guess2_hits = 1; + if (VacAttrStatsLtGtValid(stats)) + { + vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len); + vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); + } + stats->initialized = true; + } + if (VacAttrStatsLtGtValid(stats)) + { + if ((*(stats->f_cmplt)) (value, stats->min)) + { + vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); + stats->min_cnt = 0; + } + if ((*(stats->f_cmpgt)) (value, stats->max)) + { + vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len); + stats->max_cnt = 0; + } + if ((*(stats->f_cmpeq)) (value, stats->min)) + stats->min_cnt++; + else if ((*(stats->f_cmpeq)) (value, stats->max)) + stats->max_cnt++; + } + if ((*(stats->f_cmpeq)) (value, stats->best)) + stats->best_cnt++; + else if ((*(stats->f_cmpeq)) (value, stats->guess1)) + { + stats->guess1_cnt++; + stats->guess1_hits++; + } + else if ((*(stats->f_cmpeq)) (value, stats->guess2)) + stats->guess2_hits++; + else + value_hit = false; + + if (stats->guess2_hits > stats->guess1_hits) + { + swapDatum(stats->guess1, stats->guess2); + swapInt(stats->guess1_len, stats->guess2_len); + stats->guess1_cnt = stats->guess2_hits; + swapLong(stats->guess1_hits, stats->guess2_hits); + } + if (stats->guess1_cnt > stats->best_cnt) + { + swapDatum(stats->best, stats->guess1); + swapInt(stats->best_len, stats->guess1_len); + swapLong(stats->best_cnt, stats->guess1_cnt); + stats->guess1_hits = 1; + stats->guess2_hits = 1; + } + if (!value_hit) + { + vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); + stats->guess1_hits = 1; + stats->guess2_hits = 1; + } } - stats->initialized = true; - } - if (VacAttrStatsLtGtValid(stats)) { - if ( (*(stats->f_cmplt)) (value,stats->min) ) { - vc_bucketcpy(stats->attr, value, &stats->min, &stats->min_len); - stats->min_cnt = 0; - } - if ( (*(stats->f_cmpgt)) (value,stats->max) ) { - vc_bucketcpy(stats->attr, value, &stats->max, &stats->max_len); - stats->max_cnt = 0; - } - if ( (*(stats->f_cmpeq)) (value,stats->min) ) - stats->min_cnt++; - else if ( (*(stats->f_cmpeq)) (value,stats->max) ) - stats->max_cnt++; - } - if ( (*(stats->f_cmpeq)) (value,stats->best) ) - stats->best_cnt++; - else if ( (*(stats->f_cmpeq)) (value,stats->guess1) ) { - stats->guess1_cnt++; - stats->guess1_hits++; - } - else if ( (*(stats->f_cmpeq)) (value,stats->guess2) ) - stats->guess2_hits++; - else value_hit = false; - - if (stats->guess2_hits > stats->guess1_hits) { - swapDatum(stats->guess1,stats->guess2); - swapInt(stats->guess1_len,stats->guess2_len); - stats->guess1_cnt = stats->guess2_hits; - swapLong(stats->guess1_hits, stats->guess2_hits); - } - if (stats->guess1_cnt > stats->best_cnt) { - swapDatum(stats->best,stats->guess1); - swapInt(stats->best_len,stats->guess1_len); - swapLong(stats->best_cnt,stats->guess1_cnt); - stats->guess1_hits = 1; - stats->guess2_hits = 1; - } - if (!value_hit) { - vc_bucketcpy(stats->attr, value, &stats->guess2, &stats->guess2_len); - stats->guess1_hits = 1; - stats->guess2_hits = 1; - } } - } - return; + return; } /* - * vc_bucketcpy() -- update pg_class statistics for one relation + * vc_bucketcpy() -- update pg_class statistics for one relation * */ static void -vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum *bucket, int16 *bucket_len) +vc_bucketcpy(AttributeTupleForm attr, Datum value, Datum * bucket, int16 * bucket_len) { - if (attr->attbyval && attr->attlen != -1) - *bucket = value; - else { - int len = (attr->attlen != -1 ? attr->attlen : VARSIZE(value)); - - if (len > *bucket_len) - { - if (*bucket_len != 0) - pfree(DatumGetPointer(*bucket)); - *bucket = PointerGetDatum(palloc(len)); - *bucket_len = len; + if (attr->attbyval && attr->attlen != -1) + *bucket = value; + else + { + int len = (attr->attlen != -1 ? attr->attlen : VARSIZE(value)); + + if (len > *bucket_len) + { + if (*bucket_len != 0) + pfree(DatumGetPointer(*bucket)); + *bucket = PointerGetDatum(palloc(len)); + *bucket_len = len; + } + memmove(DatumGetPointer(*bucket), DatumGetPointer(value), len); } - memmove(DatumGetPointer(*bucket), DatumGetPointer(value), len); - } } /* - * vc_updstats() -- update pg_class statistics for one relation + * 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 ntups, 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. + * This routine works for both index and heap relation entries in + * pg_class. We violate no-overwrite semantics here by storing new + * values for ntups, 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 ntups, bool hasindex, VRelStats *vacrelstats) +vc_updstats(Oid relid, int npages, int ntups, bool hasindex, VRelStats * vacrelstats) { - Relation rd, ad, sd; - HeapScanDesc rsdesc, asdesc; - TupleDesc sdesc; - HeapTuple rtup, atup, stup; - Buffer rbuf, abuf; - Form_pg_class pgcform; - ScanKeyData rskey, askey; - AttributeTupleForm attp; - - /* - * update number of tuples and number of pages in pg_class - */ - ScanKeyEntryInitialize(&rskey, 0x0, ObjectIdAttributeNumber, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - - rd = heap_openr(RelationRelationName); - rsdesc = heap_beginscan(rd, false, NowTimeQual, 1, &rskey); - - if (!HeapTupleIsValid(rtup = heap_getnext(rsdesc, 0, &rbuf))) - elog(WARN, "pg_class entry for relid %d vanished during vacuuming", - relid); - - /* overwrite the existing statistics in the tuple */ - vc_setpagelock(rd, BufferGetBlockNumber(rbuf)); - pgcform = (Form_pg_class) GETSTRUCT(rtup); - pgcform->reltuples = ntups; - pgcform->relpages = npages; - pgcform->relhasindex = hasindex; - - if ( vacrelstats != NULL && vacrelstats->va_natts > 0 ) - { - VacAttrStats *vacattrstats = vacrelstats->vacattrstats; - int natts = vacrelstats->va_natts; - - ad = heap_openr(AttributeRelationName); - sd = heap_openr(StatisticRelationName); - ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, - F_INT4EQ, relid); - - asdesc = heap_beginscan(ad, false, NowTimeQual, 1, &askey); - - while (HeapTupleIsValid(atup = heap_getnext(asdesc, 0, &abuf))) + Relation rd, + ad, + sd; + HeapScanDesc rsdesc, + asdesc; + TupleDesc sdesc; + HeapTuple rtup, + atup, + stup; + Buffer rbuf, + abuf; + Form_pg_class pgcform; + ScanKeyData rskey, + askey; + AttributeTupleForm attp; + + /* + * update number of tuples and number of pages in pg_class + */ + ScanKeyEntryInitialize(&rskey, 0x0, ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + + rd = heap_openr(RelationRelationName); + rsdesc = heap_beginscan(rd, false, NowTimeQual, 1, &rskey); + + if (!HeapTupleIsValid(rtup = heap_getnext(rsdesc, 0, &rbuf))) + elog(WARN, "pg_class entry for relid %d vanished during vacuuming", + relid); + + /* overwrite the existing statistics in the tuple */ + vc_setpagelock(rd, BufferGetBlockNumber(rbuf)); + pgcform = (Form_pg_class) GETSTRUCT(rtup); + pgcform->reltuples = ntups; + pgcform->relpages = npages; + pgcform->relhasindex = hasindex; + + if (vacrelstats != NULL && vacrelstats->va_natts > 0) { - int i; - float32data selratio; /* average ratio of rows selected for a random constant */ - VacAttrStats *stats; - Datum values[ Natts_pg_statistic ]; - char nulls[ Natts_pg_statistic ]; - - attp = (AttributeTupleForm) GETSTRUCT(atup); - if ( attp->attnum <= 0) /* skip system attributes for now, */ - /* they are unique anyway */ - continue; - - for (i = 0; i < natts; i++) - { - if ( attp->attnum == vacattrstats[i].attr->attnum ) - break; - } - if ( i >= natts ) - continue; - stats = &(vacattrstats[i]); - - /* overwrite the existing statistics in the tuple */ - if (VacAttrStatsEqValid(stats)) { - - vc_setpagelock(ad, BufferGetBlockNumber(abuf)); - - if (stats->nonnull_cnt + stats->null_cnt == 0 || - (stats->null_cnt <= 1 && stats->best_cnt == 1)) - selratio = 0; - else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) + VacAttrStats *vacattrstats = vacrelstats->vacattrstats; + int natts = vacrelstats->va_natts; + + ad = heap_openr(AttributeRelationName); + sd = heap_openr(StatisticRelationName); + ScanKeyEntryInitialize(&askey, 0, Anum_pg_attribute_attrelid, + F_INT4EQ, relid); + + asdesc = heap_beginscan(ad, false, NowTimeQual, 1, &askey); + + while (HeapTupleIsValid(atup = heap_getnext(asdesc, 0, &abuf))) { - double min_cnt_d = stats->min_cnt, - max_cnt_d = stats->max_cnt, - null_cnt_d = stats->null_cnt, - nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */ - selratio = (min_cnt_d*min_cnt_d+max_cnt_d*max_cnt_d+null_cnt_d*null_cnt_d)/ - (nonnullcnt_d+null_cnt_d)/(nonnullcnt_d+null_cnt_d); + int i; + float32data selratio; /* average ratio of rows selected + * for a random constant */ + VacAttrStats *stats; + Datum values[Natts_pg_statistic]; + char nulls[Natts_pg_statistic]; + + attp = (AttributeTupleForm) GETSTRUCT(atup); + if (attp->attnum <= 0) /* skip system attributes for now, */ + /* they are unique anyway */ + continue; + + for (i = 0; i < natts; i++) + { + if (attp->attnum == vacattrstats[i].attr->attnum) + break; + } + if (i >= natts) + continue; + stats = &(vacattrstats[i]); + + /* overwrite the existing statistics in the tuple */ + if (VacAttrStatsEqValid(stats)) + { + + vc_setpagelock(ad, BufferGetBlockNumber(abuf)); + + if (stats->nonnull_cnt + stats->null_cnt == 0 || + (stats->null_cnt <= 1 && stats->best_cnt == 1)) + selratio = 0; + else if (VacAttrStatsLtGtValid(stats) && stats->min_cnt + stats->max_cnt == stats->nonnull_cnt) + { + double min_cnt_d = stats->min_cnt, + max_cnt_d = stats->max_cnt, + null_cnt_d = stats->null_cnt, + nonnullcnt_d = stats->nonnull_cnt; /* prevent overflow */ + + selratio = (min_cnt_d * min_cnt_d + max_cnt_d * max_cnt_d + null_cnt_d * null_cnt_d) / + (nonnullcnt_d + null_cnt_d) / (nonnullcnt_d + null_cnt_d); + } + else + { + double most = (double) (stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); + double total = ((double) stats->nonnull_cnt) + ((double) stats->null_cnt); + + /* + * we assume count of other values are 20% of best + * count in table + */ + selratio = (most * most + 0.20 * most * (total - most)) / total / total; + } + if (selratio > 1.0) + selratio = 1.0; + attp->attdisbursion = selratio; + WriteNoReleaseBuffer(abuf); + + /* DO PG_STATISTIC INSERTS */ + + /* + * doing system relations, especially pg_statistic is a + * problem + */ + if (VacAttrStatsLtGtValid(stats) && stats->initialized /* && + * !IsSystemRelationName( + * pgcform->relname.data) + */ ) + { + func_ptr out_function; + char *out_string; + int dummy; + + for (i = 0; i < Natts_pg_statistic; ++i) + nulls[i] = ' '; + + /* ---------------- + * initialize values[] + * ---------------- + */ + i = 0; + values[i++] = (Datum) relid; /* 1 */ + values[i++] = (Datum) attp->attnum; /* 2 */ + values[i++] = (Datum) InvalidOid; /* 3 */ + fmgr_info(stats->outfunc, &out_function, &dummy); + out_string = (*out_function) (stats->min, stats->attr->atttypid); + values[i++] = (Datum) fmgr(TextInRegProcedure, out_string); + pfree(out_string); + out_string = (char *) (*out_function) (stats->max, stats->attr->atttypid); + values[i++] = (Datum) fmgr(TextInRegProcedure, out_string); + pfree(out_string); + + sdesc = sd->rd_att; + + stup = heap_formtuple(sdesc, values, nulls); + + /* ---------------- + * insert the tuple in the relation and get the tuple's oid. + * ---------------- + */ + heap_insert(sd, stup); + pfree(DatumGetPointer(values[3])); + pfree(DatumGetPointer(values[4])); + pfree(stup); + } + } } - else { - double most = (double)(stats->best_cnt > stats->null_cnt ? stats->best_cnt : stats->null_cnt); - double total = ((double)stats->nonnull_cnt)+((double)stats->null_cnt); - /* we assume count of other values are 20% - of best count in table */ - selratio = (most*most + 0.20*most*(total-most))/total/total; - } - if (selratio > 1.0) - selratio = 1.0; - attp->attdisbursion = selratio; - WriteNoReleaseBuffer(abuf); - - /* DO PG_STATISTIC INSERTS */ - - /* doing system relations, especially pg_statistic is a problem */ - if (VacAttrStatsLtGtValid(stats) && stats->initialized /* && - !IsSystemRelationName(pgcform->relname.data)*/) { - func_ptr out_function; - char *out_string; - int dummy; - - for (i = 0; i < Natts_pg_statistic; ++i) nulls[i] = ' '; - - /* ---------------- - * initialize values[] - * ---------------- - */ - i = 0; - values[i++] = (Datum) relid; /* 1 */ - values[i++] = (Datum) attp->attnum; /* 2 */ - values[i++] = (Datum) InvalidOid; /* 3 */ - fmgr_info(stats->outfunc, &out_function, &dummy); - out_string = (*out_function)(stats->min, stats->attr->atttypid); - values[i++] = (Datum) fmgr(TextInRegProcedure,out_string); - pfree(out_string); - out_string = (char *)(*out_function)(stats->max, stats->attr->atttypid); - values[i++] = (Datum) fmgr(TextInRegProcedure,out_string); - pfree(out_string); - - sdesc = sd->rd_att; - - stup = heap_formtuple(sdesc, values, nulls); - - /* ---------------- - * insert the tuple in the relation and get the tuple's oid. - * ---------------- - */ - heap_insert(sd, stup); - pfree(DatumGetPointer(values[3])); - pfree(DatumGetPointer(values[4])); - pfree(stup); - } - } + heap_endscan(asdesc); + heap_close(ad); + heap_close(sd); } - heap_endscan(asdesc); - heap_close(ad); - heap_close(sd); - } - - /* XXX -- after write, should invalidate relcache in other backends */ - WriteNoReleaseBuffer(rbuf); /* heap_endscan release scan' buffers ? */ - - /* invalidating system relations confuses the function cache - of pg_operator and pg_opclass */ - if ( !IsSystemRelationName(pgcform->relname.data)) - RelationInvalidateHeapTuple(rd, rtup); - - /* that's all, folks */ - heap_endscan(rsdesc); - heap_close(rd); + + /* XXX -- after write, should invalidate relcache in other backends */ + WriteNoReleaseBuffer(rbuf); /* heap_endscan release scan' buffers ? */ + + /* + * invalidating system relations confuses the function cache of + * pg_operator and pg_opclass + */ + if (!IsSystemRelationName(pgcform->relname.data)) + RelationInvalidateHeapTuple(rd, rtup); + + /* that's all, folks */ + heap_endscan(rsdesc); + heap_close(rd); } /* - * vc_delhilowstats() -- delete pg_statistics rows + * vc_delhilowstats() -- delete pg_statistics rows * */ static void vc_delhilowstats(Oid relid, int attcnt, int *attnums) { - Relation pgstatistic; - HeapScanDesc pgsscan; - HeapTuple pgstup; - ScanKeyData pgskey; - - pgstatistic = heap_openr(StatisticRelationName); - - if (relid != InvalidOid ) { - ScanKeyEntryInitialize(&pgskey, 0x0, Anum_pg_statistic_starelid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 1, &pgskey); - } - else - pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 0, NULL); - - while (HeapTupleIsValid(pgstup = heap_getnext(pgsscan, 0, NULL))) - { - if ( attcnt > 0 ) - { - Form_pg_statistic pgs = (Form_pg_statistic) GETSTRUCT (pgstup); - int i; - - for (i = 0; i < attcnt; i++) - { - if ( pgs->staattnum == attnums[i] + 1 ) - break; - } - if ( i >= attcnt ) - continue; /* don't delete it */ - } - heap_delete(pgstatistic, &pgstup->t_ctid); - } - - heap_endscan(pgsscan); - heap_close(pgstatistic); + Relation pgstatistic; + HeapScanDesc pgsscan; + HeapTuple pgstup; + ScanKeyData pgskey; + + pgstatistic = heap_openr(StatisticRelationName); + + if (relid != InvalidOid) + { + ScanKeyEntryInitialize(&pgskey, 0x0, Anum_pg_statistic_starelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 1, &pgskey); + } + else + pgsscan = heap_beginscan(pgstatistic, false, NowTimeQual, 0, NULL); + + while (HeapTupleIsValid(pgstup = heap_getnext(pgsscan, 0, NULL))) + { + if (attcnt > 0) + { + Form_pg_statistic pgs = (Form_pg_statistic) GETSTRUCT(pgstup); + int i; + + for (i = 0; i < attcnt; i++) + { + if (pgs->staattnum == attnums[i] + 1) + break; + } + if (i >= attcnt) + continue; /* don't delete it */ + } + heap_delete(pgstatistic, &pgstup->t_ctid); + } + + heap_endscan(pgsscan); + heap_close(pgstatistic); } -static void vc_setpagelock(Relation rel, BlockNumber blkno) +static void +vc_setpagelock(Relation rel, BlockNumber blkno) { - ItemPointerData itm; + ItemPointerData itm; - ItemPointerSet(&itm, blkno, 1); + ItemPointerSet(&itm, blkno, 1); - RelationSetLockForWritePage(rel, &itm); + RelationSetLockForWritePage(rel, &itm); } /* - * vc_reappage() -- save a page on the array of reapped pages. + * vc_reappage() -- save a page on the array of reapped pages. * - * As a side effect of the way that the vacuuming loop for a given - * relation works, higher pages come after lower pages in the array - * (and highest tid on a page is last). + * As a side effect of the way that the vacuuming loop for a given + * relation works, higher pages come after lower pages in the array + * (and highest tid on a page is last). */ -static void +static void vc_reappage(VPageList vpl, VPageDescr vpc) { - VPageDescr newvpd; + VPageDescr newvpd; - /* allocate a VPageDescrData entry */ - newvpd = (VPageDescr) palloc(sizeof(VPageDescrData) + vpc->vpd_noff*sizeof(OffsetNumber)); + /* allocate a VPageDescrData entry */ + newvpd = (VPageDescr) palloc(sizeof(VPageDescrData) + vpc->vpd_noff * sizeof(OffsetNumber)); - /* fill it in */ - if ( vpc->vpd_noff > 0 ) - memmove (newvpd->vpd_voff, vpc->vpd_voff, vpc->vpd_noff*sizeof(OffsetNumber)); - newvpd->vpd_blkno = vpc->vpd_blkno; - newvpd->vpd_free = vpc->vpd_free; - newvpd->vpd_nusd = vpc->vpd_nusd; - newvpd->vpd_noff = vpc->vpd_noff; + /* fill it in */ + if (vpc->vpd_noff > 0) + memmove(newvpd->vpd_voff, vpc->vpd_voff, vpc->vpd_noff * sizeof(OffsetNumber)); + newvpd->vpd_blkno = vpc->vpd_blkno; + newvpd->vpd_free = vpc->vpd_free; + newvpd->vpd_nusd = vpc->vpd_nusd; + newvpd->vpd_noff = vpc->vpd_noff; - /* insert this page into vpl list */ - vc_vpinsert (vpl, newvpd); - -} /* vc_reappage */ + /* insert this page into vpl list */ + vc_vpinsert(vpl, newvpd); + +} /* vc_reappage */ static void -vc_vpinsert (VPageList vpl, VPageDescr vpnew) +vc_vpinsert(VPageList vpl, VPageDescr vpnew) { - /* allocate a VPageDescr entry if needed */ - if ( vpl->vpl_npages == 0 ) - vpl->vpl_pgdesc = (VPageDescr*) palloc(100*sizeof(VPageDescr)); - else if ( vpl->vpl_npages % 100 == 0 ) - vpl->vpl_pgdesc = (VPageDescr*) repalloc(vpl->vpl_pgdesc, (vpl->vpl_npages+100)*sizeof(VPageDescr)); - vpl->vpl_pgdesc[vpl->vpl_npages] = vpnew; - (vpl->vpl_npages)++; - + /* allocate a VPageDescr entry if needed */ + if (vpl->vpl_npages == 0) + vpl->vpl_pgdesc = (VPageDescr *) palloc(100 * sizeof(VPageDescr)); + else if (vpl->vpl_npages % 100 == 0) + vpl->vpl_pgdesc = (VPageDescr *) repalloc(vpl->vpl_pgdesc, (vpl->vpl_npages + 100) * sizeof(VPageDescr)); + vpl->vpl_pgdesc[vpl->vpl_npages] = vpnew; + (vpl->vpl_npages)++; + } static void vc_free(VRelList vrl) { - VRelList p_vrl; - MemoryContext old; - PortalVariableMemory pmem; + VRelList p_vrl; + MemoryContext old; + PortalVariableMemory pmem; - pmem = PortalGetVariableMemory(vc_portal); - old = MemoryContextSwitchTo((MemoryContext)pmem); + pmem = PortalGetVariableMemory(vc_portal); + old = MemoryContextSwitchTo((MemoryContext) pmem); - while (vrl != (VRelList) NULL) { + while (vrl != (VRelList) NULL) + { - /* free rel list entry */ - p_vrl = vrl; - vrl = vrl->vrl_next; - pfree(p_vrl); - } + /* free rel list entry */ + p_vrl = vrl; + vrl = vrl->vrl_next; + pfree(p_vrl); + } - MemoryContextSwitchTo(old); + MemoryContextSwitchTo(old); } /* - * vc_getarchrel() -- open the archive relation for a heap relation + * 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. + * The archive relation is named 'a,XXXXX' for the heap relation + * whose relid is XXXXX. */ #define ARCHIVE_PREFIX "a," -static Relation +static Relation vc_getarchrel(Relation heaprel) { - Relation archrel; - char *archrelname; + Relation archrel; + char *archrelname; - archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */ - sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id); + archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */ + sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id); - archrel = heap_openr(archrelname); + archrel = heap_openr(archrelname); - pfree(archrelname); - return (archrel); + pfree(archrelname); + return (archrel); } /* - * vc_archive() -- write a tuple to an archive relation + * 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. + * 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); + doinsert(archrel, htup); } -static bool +static bool vc_isarchrel(char *rname) { - if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0) - return (true); + if (strncmp(ARCHIVE_PREFIX, rname, strlen(ARCHIVE_PREFIX)) == 0) + return (true); - return (false); + return (false); } -static char * -vc_find_eq (char *bot, int nelem, int size, char *elm, int (*compar)(char *, char *)) +static char * +vc_find_eq(char *bot, int nelem, int size, char *elm, int (*compar) (char *, char *)) { - int res; - int last = nelem - 1; - int celm = nelem / 2; - bool last_move, first_move; - - last_move = first_move = true; - for ( ; ; ) - { - if ( first_move == true ) + int res; + int last = nelem - 1; + int celm = nelem / 2; + bool last_move, + first_move; + + last_move = first_move = true; + for (;;) { - res = compar (bot, elm); - if ( res > 0 ) - return (NULL); - if ( res == 0 ) - return (bot); - first_move = false; - } - if ( last_move == true ) - { - res = compar (elm, bot + last*size); - if ( res > 0 ) - return (NULL); - if ( res == 0 ) - return (bot + last*size); - last_move = false; - } - res = compar (elm, bot + celm*size); - if ( res == 0 ) - return (bot + celm*size); - if ( res < 0 ) - { - if ( celm == 0 ) - return (NULL); - last = celm - 1; - celm = celm / 2; - last_move = true; - continue; + if (first_move == true) + { + res = compar(bot, elm); + if (res > 0) + return (NULL); + if (res == 0) + return (bot); + first_move = false; + } + if (last_move == true) + { + res = compar(elm, bot + last * size); + if (res > 0) + return (NULL); + if (res == 0) + return (bot + last * size); + last_move = false; + } + res = compar(elm, bot + celm * size); + if (res == 0) + return (bot + celm * size); + if (res < 0) + { + if (celm == 0) + return (NULL); + last = celm - 1; + celm = celm / 2; + last_move = true; + continue; + } + + if (celm == last) + return (NULL); + + last = last - celm - 1; + bot = bot + (celm + 1) * size; + celm = (last + 1) / 2; + first_move = true; } - - if ( celm == last ) - return (NULL); - - last = last - celm - 1; - bot = bot + (celm+1)*size; - celm = (last + 1) / 2; - first_move = true; - } - -} /* vc_find_eq */ - -static int -vc_cmp_blk (char *left, char *right) + +} /* vc_find_eq */ + +static int +vc_cmp_blk(char *left, char *right) { - BlockNumber lblk, rblk; + BlockNumber lblk, + rblk; - lblk = (*((VPageDescr*)left))->vpd_blkno; - rblk = (*((VPageDescr*)right))->vpd_blkno; + lblk = (*((VPageDescr *) left))->vpd_blkno; + rblk = (*((VPageDescr *) right))->vpd_blkno; - if ( lblk < rblk ) - return (-1); - if ( lblk == rblk ) - return (0); - return (1); + if (lblk < rblk) + return (-1); + if (lblk == rblk) + return (0); + return (1); -} /* vc_cmp_blk */ +} /* vc_cmp_blk */ -static int -vc_cmp_offno (char *left, char *right) +static int +vc_cmp_offno(char *left, char *right) { - if ( *(OffsetNumber*)left < *(OffsetNumber*)right ) - return (-1); - if ( *(OffsetNumber*)left == *(OffsetNumber*)right ) - return (0); - return (1); + if (*(OffsetNumber *) left < *(OffsetNumber *) right) + return (-1); + if (*(OffsetNumber *) left == *(OffsetNumber *) right) + return (0); + return (1); -} /* vc_cmp_offno */ +} /* vc_cmp_offno */ static void -vc_getindices (Oid relid, int *nindices, Relation **Irel) +vc_getindices(Oid relid, int *nindices, Relation ** Irel) { - Relation pgindex; - Relation irel; - TupleDesc pgidesc; - HeapTuple pgitup; - HeapScanDesc pgiscan; - Datum d; - int i, k; - bool n; - ScanKeyData pgikey; - Oid *ioid; - - *nindices = i = 0; - - ioid = (Oid *) palloc(10*sizeof(Oid)); - - /* 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(relid)); - - pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey); - - while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, NULL))) { - d = (Datum) heap_getattr(pgitup, InvalidBuffer, Anum_pg_index_indexrelid, - pgidesc, &n); - i++; - if ( i % 10 == 0 ) - ioid = (Oid *) repalloc(ioid, (i+10)*sizeof(Oid)); - ioid[i-1] = DatumGetObjectId(d); - } - - heap_endscan(pgiscan); - heap_close(pgindex); - - if ( i == 0 ) { /* No one index found */ - pfree(ioid); - return; - } - - if ( Irel != (Relation **) NULL ) - *Irel = (Relation *) palloc(i * sizeof(Relation)); - - for (k = 0; i > 0; ) - { - irel = index_open(ioid[--i]); - if ( irel != (Relation) NULL ) + Relation pgindex; + Relation irel; + TupleDesc pgidesc; + HeapTuple pgitup; + HeapScanDesc pgiscan; + Datum d; + int i, + k; + bool n; + ScanKeyData pgikey; + Oid *ioid; + + *nindices = i = 0; + + ioid = (Oid *) palloc(10 * sizeof(Oid)); + + /* 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(relid)); + + pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey); + + while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, NULL))) { - if ( Irel != (Relation **) NULL ) - (*Irel)[k] = irel; - else - index_close (irel); - k++; + d = (Datum) heap_getattr(pgitup, InvalidBuffer, Anum_pg_index_indexrelid, + pgidesc, &n); + i++; + if (i % 10 == 0) + ioid = (Oid *) repalloc(ioid, (i + 10) * sizeof(Oid)); + ioid[i - 1] = DatumGetObjectId(d); } - else - elog (NOTICE, "CAN't OPEN INDEX %u - SKIP IT", ioid[i]); - } - *nindices = k; - pfree(ioid); - if ( Irel != (Relation **) NULL && *nindices == 0 ) - { - pfree (*Irel); - *Irel = (Relation *) NULL; - } + heap_endscan(pgiscan); + heap_close(pgindex); + + if (i == 0) + { /* No one index found */ + pfree(ioid); + return; + } + + if (Irel != (Relation **) NULL) + *Irel = (Relation *) palloc(i * sizeof(Relation)); + + for (k = 0; i > 0;) + { + irel = index_open(ioid[--i]); + if (irel != (Relation) NULL) + { + if (Irel != (Relation **) NULL) + (*Irel)[k] = irel; + else + index_close(irel); + k++; + } + else + elog(NOTICE, "CAN't OPEN INDEX %u - SKIP IT", ioid[i]); + } + *nindices = k; + pfree(ioid); -} /* vc_getindices */ + if (Irel != (Relation **) NULL && *nindices == 0) + { + pfree(*Irel); + *Irel = (Relation *) NULL; + } + +} /* vc_getindices */ static void -vc_clsindices (int nindices, Relation *Irel) +vc_clsindices(int nindices, Relation * Irel) { - if ( Irel == (Relation*) NULL ) - return; + if (Irel == (Relation *) NULL) + return; - while (nindices--) { - index_close (Irel[nindices]); - } - pfree (Irel); + while (nindices--) + { + index_close(Irel[nindices]); + } + pfree(Irel); -} /* vc_clsindices */ +} /* vc_clsindices */ static void -vc_mkindesc (Relation onerel, int nindices, Relation *Irel, IndDesc **Idesc) +vc_mkindesc(Relation onerel, int nindices, Relation * Irel, IndDesc ** Idesc) { - IndDesc *idcur; - HeapTuple pgIndexTup; - AttrNumber *attnumP; - int natts; - int i; - - *Idesc = (IndDesc *) palloc (nindices * sizeof (IndDesc)); - - for (i = 0, idcur = *Idesc; i < nindices; i++, idcur++) { - pgIndexTup = - SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(Irel[i]->rd_id), - 0,0,0); - Assert(pgIndexTup); - idcur->tform = (IndexTupleForm)GETSTRUCT(pgIndexTup); - for (attnumP = &(idcur->tform->indkey[0]), natts = 0; - *attnumP != InvalidAttrNumber && natts != INDEX_MAX_KEYS; - attnumP++, natts++); - if (idcur->tform->indproc != InvalidOid) { - idcur->finfoP = &(idcur->finfo); - FIgetnArgs(idcur->finfoP) = natts; - natts = 1; - FIgetProcOid(idcur->finfoP) = idcur->tform->indproc; - *(FIgetname(idcur->finfoP)) = '\0'; - } else - idcur->finfoP = (FuncIndexInfo *) NULL; - - idcur->natts = natts; - } - -} /* vc_mkindesc */ - - -static bool -vc_enough_space (VPageDescr vpd, Size len) + IndDesc *idcur; + HeapTuple pgIndexTup; + AttrNumber *attnumP; + int natts; + int i; + + *Idesc = (IndDesc *) palloc(nindices * sizeof(IndDesc)); + + for (i = 0, idcur = *Idesc; i < nindices; i++, idcur++) + { + pgIndexTup = + SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(Irel[i]->rd_id), + 0, 0, 0); + Assert(pgIndexTup); + idcur->tform = (IndexTupleForm) GETSTRUCT(pgIndexTup); + for (attnumP = &(idcur->tform->indkey[0]), natts = 0; + *attnumP != InvalidAttrNumber && natts != INDEX_MAX_KEYS; + attnumP++, natts++); + if (idcur->tform->indproc != InvalidOid) + { + idcur->finfoP = &(idcur->finfo); + FIgetnArgs(idcur->finfoP) = natts; + natts = 1; + FIgetProcOid(idcur->finfoP) = idcur->tform->indproc; + *(FIgetname(idcur->finfoP)) = '\0'; + } + else + idcur->finfoP = (FuncIndexInfo *) NULL; + + idcur->natts = natts; + } + +} /* vc_mkindesc */ + + +static bool +vc_enough_space(VPageDescr vpd, Size len) { - len = DOUBLEALIGN(len); - - if ( len > vpd->vpd_free ) - return (false); - - if ( vpd->vpd_nusd < vpd->vpd_noff ) /* there are free itemid(s) */ - return (true); /* and len <= free_space */ - - /* ok. noff_usd >= noff_free and so we'll have to allocate new itemid */ - if ( len <= vpd->vpd_free - sizeof (ItemIdData) ) - return (true); - - return (false); - -} /* vc_enough_space */ + len = DOUBLEALIGN(len); + + if (len > vpd->vpd_free) + return (false); + + if (vpd->vpd_nusd < vpd->vpd_noff) /* there are free itemid(s) */ + return (true); /* and len <= free_space */ + + /* ok. noff_usd >= noff_free and so we'll have to allocate new itemid */ + if (len <= vpd->vpd_free - sizeof(ItemIdData)) + return (true); + + return (false); + +} /* vc_enough_space */ diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 3cd011ace25..99439de9ce3 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -1,17 +1,17 @@ /*------------------------------------------------------------------------- * * view.c-- - * use rewrite rules to construct views + * 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.8 1997/08/22 14:22:14 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.9 1997/09/07 04:41:06 momjian Exp $ * *------------------------------------------------------------------------- */ -#include <stdio.h> /* for sprintf() */ +#include <stdio.h> /* for sprintf() */ #include <string.h> #include <postgres.h> @@ -42,69 +42,74 @@ *--------------------------------------------------------------------- */ static void -DefineVirtualRelation(char *relname, List *tlist) +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(res->restype)); - - typename = makeNode(TypeName); - - typename->name = pstrdup(restypename); - def->colname = pstrdup(resname); - - def->typename = typename; - - def->is_not_null = false; - def->defval = (char*) NULL; - - attrList = lappend(attrList, def); + 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(res->restype)); + + typename = makeNode(TypeName); + + typename->name = pstrdup(restypename); + def->colname = pstrdup(resname); + + def->typename = typename; + + def->is_not_null = false; + def->defval = (char *) NULL; + + 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; - createStmt.constraints = NIL; - - /* - * finally create the relation... - */ - DefineRelation(&createStmt); -} + 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; + createStmt.constraints = NIL; + + /* + * finally create the relation... + */ + DefineRelation(&createStmt); +} /*------------------------------------------------------------------ * makeViewRetrieveRuleName @@ -118,84 +123,87 @@ DefineVirtualRelation(char *relname, List *tlist) * XXX it also means viewName cannot be 16 chars long! - ay 11/94 *------------------------------------------------------------------ */ -char * +char * MakeRetrieveViewRuleName(char *viewName) { /* - char buf[100]; + char buf[100]; - memset(buf, 0, sizeof(buf)); - sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data); - buf[15] = '\0'; - namestrcpy(rule_name, buf); + 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; + char *buf; + + buf = palloc(strlen(viewName) + 5); + sprintf(buf, "_RET%s", viewName); + return buf; } static RuleStmt * -FormViewRetrieveRule(char *viewName, Query *viewParse) +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; + 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) +DefineViewRules(char *viewName, Query * viewParse) { - RuleStmt *retrieve_rule = NULL; + RuleStmt *retrieve_rule = NULL; + #ifdef NOTYET - RuleStmt *replace_rule = NULL; - RuleStmt *append_rule = NULL; - RuleStmt *delete_rule = NULL; + RuleStmt *replace_rule = NULL; + RuleStmt *append_rule = NULL; + RuleStmt *delete_rule = NULL; + #endif - - retrieve_rule = - FormViewRetrieveRule(viewName, viewParse); - + + retrieve_rule = + FormViewRetrieveRule(viewName, viewParse); + #ifdef NOTYET - - replace_rule = - FormViewReplaceRule(viewName, viewParse); - append_rule = - FormViewAppendRule(viewName, viewParse); - delete_rule = - FormViewDeleteRule(viewName, viewParse); - + + replace_rule = + FormViewReplaceRule(viewName, viewParse); + append_rule = + FormViewAppendRule(viewName, viewParse); + delete_rule = + FormViewDeleteRule(viewName, viewParse); + #endif - - DefineQueryRewrite(retrieve_rule); + + DefineQueryRewrite(retrieve_rule); #ifdef NOTYET - DefineQueryRewrite(replace_rule); - DefineQueryRewrite(append_rule); - DefineQueryRewrite(delete_rule); + DefineQueryRewrite(replace_rule); + DefineQueryRewrite(append_rule); + DefineQueryRewrite(delete_rule); #endif - -} + +} /*--------------------------------------------------------------- * UpdateRangeTableOfViewParse @@ -216,88 +224,84 @@ DefineViewRules(char *viewName, Query *viewParse) *--------------------------------------------------------------- */ static void -UpdateRangeTableOfViewParse(char *viewName, Query *viewParse) +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 = - addRangeTableEntry(NULL, (char*)viewName, "*CURRENT*", - FALSE, FALSE, NULL); - rt_entry2 = - addRangeTableEntry(NULL, (char*)viewName, "*NEW*", - FALSE, FALSE, NULL); - 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; + 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 = + addRangeTableEntry(NULL, (char *) viewName, "*CURRENT*", + FALSE, FALSE, NULL); + rt_entry2 = + addRangeTableEntry(NULL, (char *) viewName, "*NEW*", + FALSE, FALSE, NULL); + 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 + * - 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) +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); + 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); } /*------------------------------------------------------------------ @@ -309,23 +313,22 @@ DefineView(char *viewName, Query *viewParse) 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); + 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); } |