OSDN Git Service

Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[pg-rex/syncrep.git] / src / backend / utils / cache / temprel.c
index c8d5b1f..b4ca06b 100644 (file)
@@ -3,12 +3,12 @@
  * temprel.c
  *       POSTGRES temporary relation handling
  *
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.22 2000/05/20 23:11:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.34 2001/01/24 19:43:15 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 /*
  * This implements temp tables by modifying the relname cache lookups
  * of pg_class.
- * When a temp table is created, a linked list of temp table tuples is
- * stored here.  When a relname cache lookup is done, references to user-named
- * temp tables are converted to the internal temp table names.
+ *
+ * When a temp table is created, normal entries are made for it in pg_class,
+ * pg_type, etc using a unique "physical" relation name.  We also make an
+ * entry in the temp table list maintained by this module.  Subsequently,
+ * relname lookups are filtered through the temp table list, and attempts
+ * to look up a temp table name are changed to look up the physical name.
+ * This allows temp table names to mask a regular table of the same name
+ * for the duration of the session.  The temp table list is also used
+ * to drop the underlying physical relations at session shutdown.
  */
 
+#include "postgres.h"
+
 #include <sys/types.h>
 
-#include "postgres.h"
-#include "access/heapam.h"
-#include "access/xact.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
-#include "utils/catcache.h"
+#include "miscadmin.h"
 #include "utils/temprel.h"
 
 
@@ -41,143 +46,223 @@ static List *temp_rels = NIL;
 
 typedef struct TempTable
 {
-       char       *user_relname;
-       char       *relname;
-       Oid                     relid;
+       NameData        user_relname;   /* logical name of temp table */
+       NameData        relname;                /* underlying unique name */
+       Oid                     relid;                  /* needed properties of rel */
        char            relkind;
-       TransactionId xid;
+       /*
+        * If this entry was created during this xact, it should be deleted
+        * at xact abort.  Conversely, if this entry was deleted during this
+        * xact, it should be removed at xact commit.  We leave deleted entries
+        * in the list until commit so that we can roll back if needed ---
+        * but we ignore them for purposes of lookup!
+        */
+       bool            created_in_cur_xact;
+       bool            deleted_in_cur_xact;
 } TempTable;
 
 
+/*
+ * Create a temp-relation list entry given the logical temp table name
+ * and the already-created pg_class tuple for the underlying relation.
+ *
+ * NB: we assume a check has already been made for a duplicate logical name.
+ */
 void
 create_temp_relation(const char *relname, HeapTuple pg_class_tuple)
 {
+       Form_pg_class pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
        MemoryContext oldcxt;
        TempTable  *temp_rel;
 
-       oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
+       oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
-       temp_rel = palloc(sizeof(TempTable));
-       temp_rel->user_relname = palloc(NAMEDATALEN);
-       temp_rel->relname = palloc(NAMEDATALEN);
+       temp_rel = (TempTable *) palloc(sizeof(TempTable));
 
-       /* save user-supplied name */
-       strcpy(temp_rel->user_relname, relname);
-       StrNCpy(temp_rel->relname, NameStr(((Form_pg_class)
-                                         GETSTRUCT(pg_class_tuple))->relname), NAMEDATALEN);
+       StrNCpy(NameStr(temp_rel->user_relname), relname,
+                       NAMEDATALEN);
+       StrNCpy(NameStr(temp_rel->relname), NameStr(pg_class_form->relname),
+                       NAMEDATALEN);
        temp_rel->relid = pg_class_tuple->t_data->t_oid;
-       temp_rel->relkind = ((Form_pg_class) GETSTRUCT(pg_class_tuple))->relkind;
-       temp_rel->xid = GetCurrentTransactionId();
+       temp_rel->relkind = pg_class_form->relkind;
+       temp_rel->created_in_cur_xact = true;
+       temp_rel->deleted_in_cur_xact = false;
 
        temp_rels = lcons(temp_rel, temp_rels);
 
        MemoryContextSwitchTo(oldcxt);
 }
 
+/*
+ * Remove a temp relation map entry (part of DROP TABLE on a temp table).
+ * We don't actually remove the entry, just mark it dead.
+ *
+ * We don't have the relname for indexes, so we just pass the oid.
+ */
 void
-remove_all_temp_relations(void)
+remove_temp_rel_by_relid(Oid relid)
 {
-       List       *l,
-                          *next;
+       List       *l;
 
-       if (temp_rels == NIL)
-               return;
+       foreach(l, temp_rels)
+       {
+               TempTable  *temp_rel = (TempTable *) lfirst(l);
 
-       AbortOutOfAnyTransaction();
-       StartTransactionCommand();
+               if (temp_rel->relid == relid)
+                       temp_rel->deleted_in_cur_xact = true;
+               /* Keep scanning 'cause there could be multiple matches; see RENAME */
+       }
+}
 
-       l = temp_rels;
-       while (l != NIL)
+/*
+ * To implement ALTER TABLE RENAME on a temp table, we shouldn't touch
+ * the underlying physical table at all, just change the map entry!
+ *
+ * This routine is invoked early in ALTER TABLE RENAME to check for
+ * the temp-table case.  If oldname matches a temp table name, change
+ * the mapping to the new logical name and return TRUE (or elog if
+ * there is a conflict with another temp table name).  If there is
+ * no match, return FALSE indicating that normal rename should proceed.
+ *
+ * We also reject an attempt to rename a normal table to a name in use
+ * as a temp table name.  That would fail later on anyway when rename.c
+ * looks for a rename conflict, but we can give a more specific error
+ * message for the problem here.
+ *
+ * It might seem that we need to check for attempts to rename the physical
+ * file underlying a temp table, but that'll be rejected anyway because
+ * pg_tempXXX looks like a system table name.
+ */
+bool
+rename_temp_relation(const char *oldname,
+                                        const char *newname)
+{
+       List       *l;
+
+       foreach(l, temp_rels)
        {
-               TempTable  *temp_rel = lfirst(l);
+               TempTable  *temp_rel = (TempTable *) lfirst(l);
+               MemoryContext oldcxt;
+               TempTable  *new_temp_rel;
 
-               next = lnext(l);                /* do this first, l is deallocated */
+               if (temp_rel->deleted_in_cur_xact)
+                       continue;                       /* ignore it if logically deleted */
 
-               if (temp_rel->relkind != RELKIND_INDEX)
-               {
-                       char            relname[NAMEDATALEN];
+               if (strcmp(NameStr(temp_rel->user_relname), oldname) != 0)
+                       continue;                       /* ignore non-matching entries */
 
-                       /* safe from deallocation */
-                       strcpy(relname, temp_rel->user_relname);
-                       heap_drop_with_catalog(relname);
-               }
-               else
-                       index_drop(temp_rel->relid);
+               /* We are renaming a temp table --- is it OK to do so? */
+               if (is_temp_rel_name(newname))
+                       elog(ERROR, "Cannot rename temp table \"%s\": temp table \"%s\" already exists",
+                                oldname, newname);
+
+               /*
+                * Create a new mapping entry and mark the old one deleted in this
+                * xact.  One of these entries will be deleted at xact end.
+                *
+                * NOTE: the new mapping entry is inserted into the list just after
+                * the old one.  We could alternatively insert it before the old one,
+                * but that'd take more code.  It does need to be in one spot or the
+                * other, to ensure that deletion of temp rels happens in the right
+                * order during remove_all_temp_relations().
+                */
+               oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+               new_temp_rel = (TempTable *) palloc(sizeof(TempTable));
+               memcpy(new_temp_rel, temp_rel, sizeof(TempTable));
 
-               l = next;
+               StrNCpy(NameStr(new_temp_rel->user_relname), newname, NAMEDATALEN);
+               new_temp_rel->created_in_cur_xact = true;
+
+               lnext(l) = lcons(new_temp_rel, lnext(l));
+
+               temp_rel->deleted_in_cur_xact = true;
+
+               MemoryContextSwitchTo(oldcxt);
+
+               return true;
        }
-       temp_rels = NIL;
-       CommitTransactionCommand();
+
+       /* Old name does not match any temp table name, what about new? */
+       if (is_temp_rel_name(newname))
+               elog(ERROR, "Cannot rename \"%s\" to \"%s\": a temp table by that name already exists",
+                        oldname, newname);
+
+       return false;
 }
 
-/* we don't have the relname for indexes, so we just pass the oid */
+
+/*
+ * Remove underlying relations for all temp rels at backend shutdown.
+ */
 void
-remove_temp_relation(Oid relid)
+remove_all_temp_relations(void)
 {
+       List       *l;
 
-       MemoryContext oldcxt;
-       List       *l,
-                          *prev;
+       /* skip xact start overhead if nothing to do */
+       if (temp_rels == NIL)
+               return;
 
-       oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
+       AbortOutOfAnyTransaction();
+       StartTransactionCommand();
 
-       prev = NIL;
-       l = temp_rels;
-       while (l != NIL)
+       /*
+        * Scan the list and delete all entries not already deleted.
+        * We need not worry about list entries getting deleted from under us,
+        * because remove_temp_rel_by_relid() doesn't remove entries, only
+        * mark them dead.  Note that entries will be deleted in reverse order
+        * of creation --- that's critical for cases involving inheritance.
+        */
+       foreach(l, temp_rels)
        {
-               TempTable  *temp_rel = lfirst(l);
+               TempTable  *temp_rel = (TempTable *) lfirst(l);
 
-               if (temp_rel->relid == relid)
+               if (temp_rel->deleted_in_cur_xact)
+                       continue;                       /* ignore it if deleted already */
+
+               if (temp_rel->relkind != RELKIND_INDEX)
                {
-                       pfree(temp_rel->user_relname);
-                       pfree(temp_rel->relname);
-                       pfree(temp_rel);
-                       /* remove from linked list */
-                       if (prev != NIL)
-                       {
-                               lnext(prev) = lnext(l);
-                               pfree(l);
-                               l = lnext(prev);
-                       }
-                       else
-                       {
-                               temp_rels = lnext(l);
-                               pfree(l);
-                               l = temp_rels;
-                       }
+                       char            relname[NAMEDATALEN];
+
+                       /* safe from deallocation */
+                       strcpy(relname, NameStr(temp_rel->user_relname));
+                       heap_drop_with_catalog(relname, allowSystemTableMods);
                }
                else
-               {
-                       prev = l;
-                       l = lnext(l);
-               }
+                       index_drop(temp_rel->relid);
+               /* advance cmd counter to make catalog changes visible */
+               CommandCounterIncrement();
        }
 
-       MemoryContextSwitchTo(oldcxt);
+       CommitTransactionCommand();
 }
 
-/* remove entries from aborted transactions */
+/*
+ * Clean up temprel mapping entries during transaction commit or abort.
+ *
+ * During commit, remove entries that were deleted during this transaction;
+ * during abort, remove those created during this transaction.
+ *
+ * We do not need to worry about removing the underlying physical relation;
+ * that's someone else's job.
+ */
 void
-invalidate_temp_relations(void)
+AtEOXact_temp_relations(bool isCommit)
 {
-       MemoryContext oldcxt;
        List       *l,
                           *prev;
 
-       oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
-
        prev = NIL;
        l = temp_rels;
        while (l != NIL)
        {
-               TempTable  *temp_rel = lfirst(l);
+               TempTable  *temp_rel = (TempTable *) lfirst(l);
 
-               if (temp_rel->xid == GetCurrentTransactionId())
+               if (isCommit ? temp_rel->deleted_in_cur_xact :
+                       temp_rel->created_in_cur_xact)
                {
-                       pfree(temp_rel->user_relname);
-                       pfree(temp_rel->relname);
-                       pfree(temp_rel);
-                       /* remove from linked list */
+                       /* This entry must be removed */
                        if (prev != NIL)
                        {
                                lnext(prev) = lnext(l);
@@ -190,18 +275,26 @@ invalidate_temp_relations(void)
                                pfree(l);
                                l = temp_rels;
                        }
+                       pfree(temp_rel);
                }
                else
                {
+                       /* This entry must be preserved */
+                       temp_rel->created_in_cur_xact = false;
+                       temp_rel->deleted_in_cur_xact = false;
                        prev = l;
                        l = lnext(l);
                }
-
        }
-
-       MemoryContextSwitchTo(oldcxt);
 }
 
+
+/*
+ * Map user name to physical name --- returns NULL if no entry.
+ *
+ * This also supports testing whether a name is a temp table name;
+ * see is_temp_rel_name() macro.
+ */
 char *
 get_temp_rel_by_username(const char *user_relname)
 {
@@ -209,14 +302,20 @@ get_temp_rel_by_username(const char *user_relname)
 
        foreach(l, temp_rels)
        {
-               TempTable  *temp_rel = lfirst(l);
+               TempTable  *temp_rel = (TempTable *) lfirst(l);
+
+               if (temp_rel->deleted_in_cur_xact)
+                       continue;                       /* ignore it if logically deleted */
 
-               if (strcmp(temp_rel->user_relname, user_relname) == 0)
-                       return temp_rel->relname;
+               if (strcmp(NameStr(temp_rel->user_relname), user_relname) == 0)
+                       return NameStr(temp_rel->relname);
        }
        return NULL;
 }
 
+/*
+ * Map physical name to user name --- returns pstrdup'd input if no match.
+ */
 char *
 get_temp_rel_by_physicalname(const char *relname)
 {
@@ -224,10 +323,13 @@ get_temp_rel_by_physicalname(const char *relname)
 
        foreach(l, temp_rels)
        {
-               TempTable  *temp_rel = lfirst(l);
+               TempTable  *temp_rel = (TempTable *) lfirst(l);
+
+               if (temp_rel->deleted_in_cur_xact)
+                       continue;                       /* ignore it if logically deleted */
 
-               if (strcmp(temp_rel->relname, relname) == 0)
-                       return temp_rel->user_relname;
+               if (strcmp(NameStr(temp_rel->relname), relname) == 0)
+                       return NameStr(temp_rel->user_relname);
        }
        /* needed for bootstrapping temp tables */
        return pstrdup(relname);