mirror of
https://github.com/postgres/postgres.git
synced 2025-06-05 23:56:58 +03:00
in different namespaces. Also, cleanup work on relation namespace support: drop, alter, rename commands work for tables in non-default namespaces.
336 lines
9.0 KiB
C
336 lines
9.0 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* temprel.c
|
|
* POSTGRES temporary relation handling
|
|
*
|
|
* 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.36 2002/03/29 19:06:16 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* This implements temp tables by modifying the relname cache lookups
|
|
* of pg_class.
|
|
*
|
|
* 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 "catalog/heap.h"
|
|
#include "catalog/index.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/temprel.h"
|
|
|
|
|
|
/* ----------------
|
|
* global variables
|
|
* ----------------
|
|
*/
|
|
|
|
static List *temp_rels = NIL;
|
|
|
|
typedef struct TempTable
|
|
{
|
|
NameData user_relname; /* logical name of temp table */
|
|
NameData relname; /* underlying unique name */
|
|
Oid relid; /* needed properties of rel */
|
|
char relkind;
|
|
|
|
/*
|
|
* 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(CacheMemoryContext);
|
|
|
|
temp_rel = (TempTable *) palloc(sizeof(TempTable));
|
|
|
|
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 = 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_temp_rel_by_relid(Oid relid)
|
|
{
|
|
List *l;
|
|
|
|
foreach(l, temp_rels)
|
|
{
|
|
TempTable *temp_rel = (TempTable *) lfirst(l);
|
|
|
|
if (temp_rel->relid == relid)
|
|
temp_rel->deleted_in_cur_xact = true;
|
|
|
|
/*
|
|
* Keep scanning 'cause there could be multiple matches; see
|
|
* RENAME
|
|
*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 = (TempTable *) lfirst(l);
|
|
MemoryContext oldcxt;
|
|
TempTable *new_temp_rel;
|
|
|
|
if (temp_rel->deleted_in_cur_xact)
|
|
continue; /* ignore it if logically deleted */
|
|
|
|
if (strcmp(NameStr(temp_rel->user_relname), oldname) != 0)
|
|
continue; /* ignore non-matching entries */
|
|
|
|
/* 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));
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove underlying relations for all temp rels at backend shutdown.
|
|
*/
|
|
void
|
|
remove_all_temp_relations(void)
|
|
{
|
|
List *l;
|
|
|
|
/* skip xact start overhead if nothing to do */
|
|
if (temp_rels == NIL)
|
|
return;
|
|
|
|
AbortOutOfAnyTransaction();
|
|
StartTransactionCommand();
|
|
|
|
/*
|
|
* 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 = (TempTable *) lfirst(l);
|
|
|
|
if (temp_rel->deleted_in_cur_xact)
|
|
continue; /* ignore it if deleted already */
|
|
|
|
if (temp_rel->relkind != RELKIND_INDEX)
|
|
heap_drop_with_catalog(temp_rel->relid, allowSystemTableMods);
|
|
else
|
|
index_drop(temp_rel->relid);
|
|
/* advance cmd counter to make catalog changes visible */
|
|
CommandCounterIncrement();
|
|
}
|
|
|
|
CommitTransactionCommand();
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
AtEOXact_temp_relations(bool isCommit)
|
|
{
|
|
List *l,
|
|
*prev;
|
|
|
|
prev = NIL;
|
|
l = temp_rels;
|
|
while (l != NIL)
|
|
{
|
|
TempTable *temp_rel = (TempTable *) lfirst(l);
|
|
|
|
if (isCommit ? temp_rel->deleted_in_cur_xact :
|
|
temp_rel->created_in_cur_xact)
|
|
{
|
|
/* This entry must be removed */
|
|
if (prev != NIL)
|
|
{
|
|
lnext(prev) = lnext(l);
|
|
pfree(l);
|
|
l = lnext(prev);
|
|
}
|
|
else
|
|
{
|
|
temp_rels = lnext(l);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
List *l;
|
|
|
|
foreach(l, temp_rels)
|
|
{
|
|
TempTable *temp_rel = (TempTable *) lfirst(l);
|
|
|
|
if (temp_rel->deleted_in_cur_xact)
|
|
continue; /* ignore it if logically deleted */
|
|
|
|
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)
|
|
{
|
|
List *l;
|
|
|
|
foreach(l, temp_rels)
|
|
{
|
|
TempTable *temp_rel = (TempTable *) lfirst(l);
|
|
|
|
if (temp_rel->deleted_in_cur_xact)
|
|
continue; /* ignore it if logically deleted */
|
|
|
|
if (strcmp(NameStr(temp_rel->relname), relname) == 0)
|
|
return NameStr(temp_rel->user_relname);
|
|
}
|
|
/* needed for bootstrapping temp tables */
|
|
return pstrdup(relname);
|
|
}
|