From 9f1a22304695cf2b6ed4497b4e12af8bb40704de Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 20 Jun 2000 06:41:13 +0000 Subject: [PATCH] Make renaming a temp table behave sensibly. We don't need to touch the underlying table at all, just change the mapping entry ... but that logic was missing. --- src/backend/commands/rename.c | 10 ++- src/backend/utils/cache/temprel.c | 134 ++++++++++++++++++++++++------ src/include/utils/temprel.h | 19 +++-- 3 files changed, 130 insertions(+), 33 deletions(-) diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c index 7f46a3d83b..2daebf7c5e 100644 --- a/src/backend/commands/rename.c +++ b/src/backend/commands/rename.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.45 2000/05/25 21:30:20 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.46 2000/06/20 06:41:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,7 @@ #include "utils/acl.h" #include "utils/relcache.h" #include "utils/syscache.h" +#include "utils/temprel.h" /* @@ -199,6 +200,13 @@ renamerel(const char *oldrelname, const char *newrelname) elog(ERROR, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs", newrelname); + /* + * Check for renaming a temp table, which only requires altering + * the temp-table mapping, not the physical table. + */ + if (rename_temp_relation(oldrelname, newrelname)) + return; /* all done... */ + /* * Instead of using heap_openr(), do it the hard way, so that we * can rename indexes as well as regular relations. diff --git a/src/backend/utils/cache/temprel.c b/src/backend/utils/cache/temprel.c index 0023fa0641..d09e35336e 100644 --- a/src/backend/utils/cache/temprel.c +++ b/src/backend/utils/cache/temprel.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.23 2000/05/30 00:49:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.24 2000/06/20 06:41:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,14 +16,21 @@ /* * 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 #include "postgres.h" + #include "catalog/heap.h" #include "catalog/index.h" #include "utils/catcache.h" @@ -39,32 +46,37 @@ static List *temp_rels = NIL; typedef struct TempTable { - char *user_relname; - char *relname; - Oid relid; + char *user_relname; /* logical name of temp table */ + char *relname; /* underlying unique name */ + Oid relid; /* needed properties of rel */ char relkind; - TransactionId xid; + TransactionId xid; /* xact in which temp tab was created */ } 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); - temp_rel = palloc(sizeof(TempTable)); - temp_rel->user_relname = palloc(NAMEDATALEN); - temp_rel->relname = palloc(NAMEDATALEN); + temp_rel = (TempTable *) palloc(sizeof(TempTable)); + temp_rel->user_relname = (char *) palloc(NAMEDATALEN); + temp_rel->relname = (char *) palloc(NAMEDATALEN); - /* 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(temp_rel->user_relname, relname, NAMEDATALEN); + StrNCpy(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->relkind = pg_class_form->relkind; temp_rel->xid = GetCurrentTransactionId(); temp_rels = lcons(temp_rel, temp_rels); @@ -72,6 +84,9 @@ create_temp_relation(const char *relname, HeapTuple pg_class_tuple) MemoryContextSwitchTo(oldcxt); } +/* + * Remove underlying relations for all temp rels at backend shutdown. + */ void remove_all_temp_relations(void) { @@ -87,7 +102,7 @@ remove_all_temp_relations(void) l = temp_rels; while (l != NIL) { - TempTable *temp_rel = lfirst(l); + TempTable *temp_rel = (TempTable *) lfirst(l); next = lnext(l); /* do this first, l is deallocated */ @@ -108,11 +123,14 @@ remove_all_temp_relations(void) CommitTransactionCommand(); } -/* we don't have the relname for indexes, so we just pass the oid */ +/* + * Remove a temp relation map entry (part of DROP TABLE on a temp table) + * + * we don't have the relname for indexes, so we just pass the oid + */ void remove_temp_relation(Oid relid) { - MemoryContext oldcxt; List *l, *prev; @@ -123,7 +141,7 @@ remove_temp_relation(Oid relid) l = temp_rels; while (l != NIL) { - TempTable *temp_rel = lfirst(l); + TempTable *temp_rel = (TempTable *) lfirst(l); if (temp_rel->relid == relid) { @@ -154,7 +172,12 @@ remove_temp_relation(Oid relid) MemoryContextSwitchTo(oldcxt); } -/* remove entries from aborted transactions */ +/* + * Remove freshly-created map entries during transaction abort. + * + * The underlying physical rel will be removed by normal abort processing. + * We just have to delete the map entry. + */ void invalidate_temp_relations(void) { @@ -168,7 +191,7 @@ invalidate_temp_relations(void) l = temp_rels; while (l != NIL) { - TempTable *temp_rel = lfirst(l); + TempTable *temp_rel = (TempTable *) lfirst(l); if (temp_rel->xid == GetCurrentTransactionId()) { @@ -194,12 +217,70 @@ invalidate_temp_relations(void) prev = l; l = lnext(l); } - } MemoryContextSwitchTo(oldcxt); } +/* + * 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 map entry 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. + * + * A nitpicker might complain that the rename should be undone if the + * current xact is later aborted, but I'm not going to fix that now. + * This whole mapping mechanism ought to be replaced with something + * schema-based, anyhow. + */ +bool +rename_temp_relation(const char *oldname, + const char *newname) +{ + List *l; + + foreach(l, temp_rels) + { + TempTable *temp_rel = (TempTable *) lfirst(l); + + if (strcmp(temp_rel->user_relname, oldname) == 0) + { + if (get_temp_rel_by_username(newname) != NULL) + elog(ERROR, "Cannot rename temp table \"%s\": temp table \"%s\" already exists", + oldname, newname); + /* user_relname was palloc'd NAMEDATALEN, so safe to re-use it */ + StrNCpy(temp_rel->user_relname, newname, NAMEDATALEN); + return true; + } + } + + /* Old name does not match any temp table name, what about new? */ + if (get_temp_rel_by_username(newname) != NULL) + elog(ERROR, "Cannot rename \"%s\" to \"%s\": a temp table by that name already exists", + oldname, newname); + + return false; +} + + +/* + * Map user name to physical name --- returns NULL if no entry. + * + * This is the normal way to test whether a name is a temp table name. + */ char * get_temp_rel_by_username(const char *user_relname) { @@ -207,7 +288,7 @@ 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 (strcmp(temp_rel->user_relname, user_relname) == 0) return temp_rel->relname; @@ -215,6 +296,9 @@ get_temp_rel_by_username(const char *user_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) { @@ -222,7 +306,7 @@ get_temp_rel_by_physicalname(const char *relname) foreach(l, temp_rels) { - TempTable *temp_rel = lfirst(l); + TempTable *temp_rel = (TempTable *) lfirst(l); if (strcmp(temp_rel->relname, relname) == 0) return temp_rel->user_relname; diff --git a/src/include/utils/temprel.h b/src/include/utils/temprel.h index 7a1dfa9e1b..337188b612 100644 --- a/src/include/utils/temprel.h +++ b/src/include/utils/temprel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: temprel.h,v 1.9 2000/04/12 17:16:55 momjian Exp $ + * $Id: temprel.h,v 1.10 2000/06/20 06:41:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,11 +16,16 @@ #include "access/htup.h" -void create_temp_relation(const char *relname, HeapTuple pg_class_tuple); -void remove_all_temp_relations(void); -void invalidate_temp_relations(void); -void remove_temp_relation(Oid relid); -char *get_temp_rel_by_username(const char *user_relname); -char *get_temp_rel_by_physicalname(const char *relname); +extern void create_temp_relation(const char *relname, + HeapTuple pg_class_tuple); +extern void remove_temp_relation(Oid relid); +extern bool rename_temp_relation(const char *oldname, + const char *newname); + +extern void remove_all_temp_relations(void); +extern void invalidate_temp_relations(void); + +extern char *get_temp_rel_by_username(const char *user_relname); +extern char *get_temp_rel_by_physicalname(const char *relname); #endif /* TEMPREL_H */ -- 2.40.0