OSDN Git Service

Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[pg-rex/syncrep.git] / src / backend / utils / cache / temprel.c
1 /*-------------------------------------------------------------------------
2  *
3  * temprel.c
4  *        POSTGRES temporary relation handling
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/temprel.c,v 1.34 2001/01/24 19:43:15 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 /*
17  * This implements temp tables by modifying the relname cache lookups
18  * of pg_class.
19  *
20  * When a temp table is created, normal entries are made for it in pg_class,
21  * pg_type, etc using a unique "physical" relation name.  We also make an
22  * entry in the temp table list maintained by this module.  Subsequently,
23  * relname lookups are filtered through the temp table list, and attempts
24  * to look up a temp table name are changed to look up the physical name.
25  * This allows temp table names to mask a regular table of the same name
26  * for the duration of the session.  The temp table list is also used
27  * to drop the underlying physical relations at session shutdown.
28  */
29
30 #include "postgres.h"
31
32 #include <sys/types.h>
33
34 #include "catalog/heap.h"
35 #include "catalog/index.h"
36 #include "miscadmin.h"
37 #include "utils/temprel.h"
38
39
40 /* ----------------
41  *              global variables
42  * ----------------
43  */
44
45 static List *temp_rels = NIL;
46
47 typedef struct TempTable
48 {
49         NameData        user_relname;   /* logical name of temp table */
50         NameData        relname;                /* underlying unique name */
51         Oid                     relid;                  /* needed properties of rel */
52         char            relkind;
53         /*
54          * If this entry was created during this xact, it should be deleted
55          * at xact abort.  Conversely, if this entry was deleted during this
56          * xact, it should be removed at xact commit.  We leave deleted entries
57          * in the list until commit so that we can roll back if needed ---
58          * but we ignore them for purposes of lookup!
59          */
60         bool            created_in_cur_xact;
61         bool            deleted_in_cur_xact;
62 } TempTable;
63
64
65 /*
66  * Create a temp-relation list entry given the logical temp table name
67  * and the already-created pg_class tuple for the underlying relation.
68  *
69  * NB: we assume a check has already been made for a duplicate logical name.
70  */
71 void
72 create_temp_relation(const char *relname, HeapTuple pg_class_tuple)
73 {
74         Form_pg_class pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
75         MemoryContext oldcxt;
76         TempTable  *temp_rel;
77
78         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
79
80         temp_rel = (TempTable *) palloc(sizeof(TempTable));
81
82         StrNCpy(NameStr(temp_rel->user_relname), relname,
83                         NAMEDATALEN);
84         StrNCpy(NameStr(temp_rel->relname), NameStr(pg_class_form->relname),
85                         NAMEDATALEN);
86         temp_rel->relid = pg_class_tuple->t_data->t_oid;
87         temp_rel->relkind = pg_class_form->relkind;
88         temp_rel->created_in_cur_xact = true;
89         temp_rel->deleted_in_cur_xact = false;
90
91         temp_rels = lcons(temp_rel, temp_rels);
92
93         MemoryContextSwitchTo(oldcxt);
94 }
95
96 /*
97  * Remove a temp relation map entry (part of DROP TABLE on a temp table).
98  * We don't actually remove the entry, just mark it dead.
99  *
100  * We don't have the relname for indexes, so we just pass the oid.
101  */
102 void
103 remove_temp_rel_by_relid(Oid relid)
104 {
105         List       *l;
106
107         foreach(l, temp_rels)
108         {
109                 TempTable  *temp_rel = (TempTable *) lfirst(l);
110
111                 if (temp_rel->relid == relid)
112                         temp_rel->deleted_in_cur_xact = true;
113                 /* Keep scanning 'cause there could be multiple matches; see RENAME */
114         }
115 }
116
117 /*
118  * To implement ALTER TABLE RENAME on a temp table, we shouldn't touch
119  * the underlying physical table at all, just change the map entry!
120  *
121  * This routine is invoked early in ALTER TABLE RENAME to check for
122  * the temp-table case.  If oldname matches a temp table name, change
123  * the mapping to the new logical name and return TRUE (or elog if
124  * there is a conflict with another temp table name).  If there is
125  * no match, return FALSE indicating that normal rename should proceed.
126  *
127  * We also reject an attempt to rename a normal table to a name in use
128  * as a temp table name.  That would fail later on anyway when rename.c
129  * looks for a rename conflict, but we can give a more specific error
130  * message for the problem here.
131  *
132  * It might seem that we need to check for attempts to rename the physical
133  * file underlying a temp table, but that'll be rejected anyway because
134  * pg_tempXXX looks like a system table name.
135  */
136 bool
137 rename_temp_relation(const char *oldname,
138                                          const char *newname)
139 {
140         List       *l;
141
142         foreach(l, temp_rels)
143         {
144                 TempTable  *temp_rel = (TempTable *) lfirst(l);
145                 MemoryContext oldcxt;
146                 TempTable  *new_temp_rel;
147
148                 if (temp_rel->deleted_in_cur_xact)
149                         continue;                       /* ignore it if logically deleted */
150
151                 if (strcmp(NameStr(temp_rel->user_relname), oldname) != 0)
152                         continue;                       /* ignore non-matching entries */
153
154                 /* We are renaming a temp table --- is it OK to do so? */
155                 if (is_temp_rel_name(newname))
156                         elog(ERROR, "Cannot rename temp table \"%s\": temp table \"%s\" already exists",
157                                  oldname, newname);
158
159                 /*
160                  * Create a new mapping entry and mark the old one deleted in this
161                  * xact.  One of these entries will be deleted at xact end.
162                  *
163                  * NOTE: the new mapping entry is inserted into the list just after
164                  * the old one.  We could alternatively insert it before the old one,
165                  * but that'd take more code.  It does need to be in one spot or the
166                  * other, to ensure that deletion of temp rels happens in the right
167                  * order during remove_all_temp_relations().
168                  */
169                 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
170
171                 new_temp_rel = (TempTable *) palloc(sizeof(TempTable));
172                 memcpy(new_temp_rel, temp_rel, sizeof(TempTable));
173
174                 StrNCpy(NameStr(new_temp_rel->user_relname), newname, NAMEDATALEN);
175                 new_temp_rel->created_in_cur_xact = true;
176
177                 lnext(l) = lcons(new_temp_rel, lnext(l));
178
179                 temp_rel->deleted_in_cur_xact = true;
180
181                 MemoryContextSwitchTo(oldcxt);
182
183                 return true;
184         }
185
186         /* Old name does not match any temp table name, what about new? */
187         if (is_temp_rel_name(newname))
188                 elog(ERROR, "Cannot rename \"%s\" to \"%s\": a temp table by that name already exists",
189                          oldname, newname);
190
191         return false;
192 }
193
194
195 /*
196  * Remove underlying relations for all temp rels at backend shutdown.
197  */
198 void
199 remove_all_temp_relations(void)
200 {
201         List       *l;
202
203         /* skip xact start overhead if nothing to do */
204         if (temp_rels == NIL)
205                 return;
206
207         AbortOutOfAnyTransaction();
208         StartTransactionCommand();
209
210         /*
211          * Scan the list and delete all entries not already deleted.
212          * We need not worry about list entries getting deleted from under us,
213          * because remove_temp_rel_by_relid() doesn't remove entries, only
214          * mark them dead.  Note that entries will be deleted in reverse order
215          * of creation --- that's critical for cases involving inheritance.
216          */
217         foreach(l, temp_rels)
218         {
219                 TempTable  *temp_rel = (TempTable *) lfirst(l);
220
221                 if (temp_rel->deleted_in_cur_xact)
222                         continue;                       /* ignore it if deleted already */
223
224                 if (temp_rel->relkind != RELKIND_INDEX)
225                 {
226                         char            relname[NAMEDATALEN];
227
228                         /* safe from deallocation */
229                         strcpy(relname, NameStr(temp_rel->user_relname));
230                         heap_drop_with_catalog(relname, allowSystemTableMods);
231                 }
232                 else
233                         index_drop(temp_rel->relid);
234                 /* advance cmd counter to make catalog changes visible */
235                 CommandCounterIncrement();
236         }
237
238         CommitTransactionCommand();
239 }
240
241 /*
242  * Clean up temprel mapping entries during transaction commit or abort.
243  *
244  * During commit, remove entries that were deleted during this transaction;
245  * during abort, remove those created during this transaction.
246  *
247  * We do not need to worry about removing the underlying physical relation;
248  * that's someone else's job.
249  */
250 void
251 AtEOXact_temp_relations(bool isCommit)
252 {
253         List       *l,
254                            *prev;
255
256         prev = NIL;
257         l = temp_rels;
258         while (l != NIL)
259         {
260                 TempTable  *temp_rel = (TempTable *) lfirst(l);
261
262                 if (isCommit ? temp_rel->deleted_in_cur_xact :
263                         temp_rel->created_in_cur_xact)
264                 {
265                         /* This entry must be removed */
266                         if (prev != NIL)
267                         {
268                                 lnext(prev) = lnext(l);
269                                 pfree(l);
270                                 l = lnext(prev);
271                         }
272                         else
273                         {
274                                 temp_rels = lnext(l);
275                                 pfree(l);
276                                 l = temp_rels;
277                         }
278                         pfree(temp_rel);
279                 }
280                 else
281                 {
282                         /* This entry must be preserved */
283                         temp_rel->created_in_cur_xact = false;
284                         temp_rel->deleted_in_cur_xact = false;
285                         prev = l;
286                         l = lnext(l);
287                 }
288         }
289 }
290
291
292 /*
293  * Map user name to physical name --- returns NULL if no entry.
294  *
295  * This also supports testing whether a name is a temp table name;
296  * see is_temp_rel_name() macro.
297  */
298 char *
299 get_temp_rel_by_username(const char *user_relname)
300 {
301         List       *l;
302
303         foreach(l, temp_rels)
304         {
305                 TempTable  *temp_rel = (TempTable *) lfirst(l);
306
307                 if (temp_rel->deleted_in_cur_xact)
308                         continue;                       /* ignore it if logically deleted */
309
310                 if (strcmp(NameStr(temp_rel->user_relname), user_relname) == 0)
311                         return NameStr(temp_rel->relname);
312         }
313         return NULL;
314 }
315
316 /*
317  * Map physical name to user name --- returns pstrdup'd input if no match.
318  */
319 char *
320 get_temp_rel_by_physicalname(const char *relname)
321 {
322         List       *l;
323
324         foreach(l, temp_rels)
325         {
326                 TempTable  *temp_rel = (TempTable *) lfirst(l);
327
328                 if (temp_rel->deleted_in_cur_xact)
329                         continue;                       /* ignore it if logically deleted */
330
331                 if (strcmp(NameStr(temp_rel->relname), relname) == 0)
332                         return NameStr(temp_rel->user_relname);
333         }
334         /* needed for bootstrapping temp tables */
335         return pstrdup(relname);
336 }