1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
/*-------------------------------------------------------------------------
*
* temprel.c
* POSTGRES temporary relation handling
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.31 2000/11/16 22:30:33 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 (get_temp_rel_by_username(newname) != NULL)
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.
*/
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;
temp_rels = lcons(new_temp_rel, temp_rels);
temp_rel->deleted_in_cur_xact = true;
MemoryContextSwitchTo(oldcxt);
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;
}
/*
* 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.
*/
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)
{
char relname[NAMEDATALEN];
/* safe from deallocation */
strcpy(relname, NameStr(temp_rel->user_relname));
heap_drop_with_catalog(relname, 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 */
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;
}
}
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 is the normal way to test whether a name is a temp table name.
*/
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);
}
|