OSDN Git Service

f6b72481fb9b772720ce012920d99f2f623651b6
[pg-rex/syncrep.git] / src / backend / utils / mmgr / portalmem.c
1 /*-------------------------------------------------------------------------
2  *
3  * portalmem.c
4  *        backend portal memory management
5  *
6  * Portals are objects representing the execution state of a query.
7  * This module provides memory management services for portals, but it
8  * doesn't actually run the executor for them.
9  *
10  *
11  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
12  * Portions Copyright (c) 1994, Regents of the University of California
13  *
14  * IDENTIFICATION
15  *        $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.64 2004/02/03 17:34:03 tgl Exp $
16  *
17  *-------------------------------------------------------------------------
18  */
19 #include "postgres.h"
20
21 #include "miscadmin.h"
22 #include "commands/portalcmds.h"
23 #include "executor/executor.h"
24 #include "utils/hsearch.h"
25 #include "utils/memutils.h"
26 #include "utils/portal.h"
27
28 /*
29  * estimate of the maximum number of open portals a user would have,
30  * used in initially sizing the PortalHashTable in EnablePortalManager()
31  */
32 #define PORTALS_PER_USER           64
33
34
35 /* ----------------
36  *              Global state
37  * ----------------
38  */
39
40 #define MAX_PORTALNAME_LEN              NAMEDATALEN
41
42 typedef struct portalhashent
43 {
44         char            portalname[MAX_PORTALNAME_LEN];
45         Portal          portal;
46 } PortalHashEnt;
47
48 static HTAB *PortalHashTable = NULL;
49
50 #define PortalHashTableLookup(NAME, PORTAL) \
51 do { \
52         PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
53         \
54         MemSet(key, 0, MAX_PORTALNAME_LEN); \
55         StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
56         hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
57                                                                                  key, HASH_FIND, NULL); \
58         if (hentry) \
59                 PORTAL = hentry->portal; \
60         else \
61                 PORTAL = NULL; \
62 } while(0)
63
64 #define PortalHashTableInsert(PORTAL, NAME) \
65 do { \
66         PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
67         \
68         MemSet(key, 0, MAX_PORTALNAME_LEN); \
69         StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
70         hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
71                                                                                  key, HASH_ENTER, &found); \
72         if (hentry == NULL) \
73                 ereport(ERROR, \
74                                 (errcode(ERRCODE_OUT_OF_MEMORY), \
75                                  errmsg("out of memory"))); \
76         if (found) \
77                 elog(ERROR, "duplicate portal name"); \
78         hentry->portal = PORTAL; \
79         /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
80         PORTAL->name = hentry->portalname; \
81 } while(0)
82
83 #define PortalHashTableDelete(PORTAL) \
84 do { \
85         PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
86         \
87         MemSet(key, 0, MAX_PORTALNAME_LEN); \
88         StrNCpy(key, PORTAL->name, MAX_PORTALNAME_LEN); \
89         hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
90                                                                                  key, HASH_REMOVE, NULL); \
91         if (hentry == NULL) \
92                 elog(WARNING, "trying to delete portal name that does not exist"); \
93 } while(0)
94
95 static MemoryContext PortalMemory = NULL;
96
97
98 /* ----------------------------------------------------------------
99  *                                 public portal interface functions
100  * ----------------------------------------------------------------
101  */
102
103 /*
104  * EnablePortalManager
105  *              Enables the portal management module at backend startup.
106  */
107 void
108 EnablePortalManager(void)
109 {
110         HASHCTL         ctl;
111
112         Assert(PortalMemory == NULL);
113
114         PortalMemory = AllocSetContextCreate(TopMemoryContext,
115                                                                                  "PortalMemory",
116                                                                                  ALLOCSET_DEFAULT_MINSIZE,
117                                                                                  ALLOCSET_DEFAULT_INITSIZE,
118                                                                                  ALLOCSET_DEFAULT_MAXSIZE);
119
120         ctl.keysize = MAX_PORTALNAME_LEN;
121         ctl.entrysize = sizeof(PortalHashEnt);
122
123         /*
124          * use PORTALS_PER_USER as a guess of how many hash table entries to
125          * create, initially
126          */
127         PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
128                                                                   &ctl, HASH_ELEM);
129 }
130
131 /*
132  * GetPortalByName
133  *              Returns a portal given a portal name, or NULL if name not found.
134  */
135 Portal
136 GetPortalByName(const char *name)
137 {
138         Portal          portal;
139
140         if (PointerIsValid(name))
141                 PortalHashTableLookup(name, portal);
142         else
143                 portal = NULL;
144
145         return portal;
146 }
147
148 /*
149  * CreatePortal
150  *              Returns a new portal given a name.
151  *
152  * allowDup: if true, automatically drop any pre-existing portal of the
153  * same name (if false, an error is raised).
154  *
155  * dupSilent: if true, don't even emit a WARNING.
156  */
157 Portal
158 CreatePortal(const char *name, bool allowDup, bool dupSilent)
159 {
160         Portal          portal;
161
162         AssertArg(PointerIsValid(name));
163
164         portal = GetPortalByName(name);
165         if (PortalIsValid(portal))
166         {
167                 if (!allowDup)
168                         ereport(ERROR,
169                                         (errcode(ERRCODE_DUPLICATE_CURSOR),
170                                          errmsg("cursor \"%s\" already exists", name)));
171                 if (!dupSilent)
172                         ereport(WARNING,
173                                         (errcode(ERRCODE_DUPLICATE_CURSOR),
174                                          errmsg("closing existing cursor \"%s\"",
175                                                         name)));
176                 PortalDrop(portal, false);
177         }
178
179         /* make new portal structure */
180         portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);
181
182         /* initialize portal heap context; typically it won't store much */
183         portal->heap = AllocSetContextCreate(PortalMemory,
184                                                                                  "PortalHeapMemory",
185                                                                                  ALLOCSET_SMALL_MINSIZE,
186                                                                                  ALLOCSET_SMALL_INITSIZE,
187                                                                                  ALLOCSET_SMALL_MAXSIZE);
188
189         /* initialize portal fields that don't start off zero */
190         portal->cleanup = PortalCleanup;
191         portal->createXact = GetCurrentTransactionId();
192         portal->strategy = PORTAL_MULTI_QUERY;
193         portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
194         portal->atStart = true;
195         portal->atEnd = true;           /* disallow fetches until query is set */
196
197         /* put portal in table (sets portal->name) */
198         PortalHashTableInsert(portal, name);
199
200         return portal;
201 }
202
203 /*
204  * CreateNewPortal
205  *              Create a new portal, assigning it a random nonconflicting name.
206  */
207 Portal
208 CreateNewPortal(void)
209 {
210         static unsigned int unnamed_portal_count = 0;
211
212         char            portalname[MAX_PORTALNAME_LEN];
213
214         /* Select a nonconflicting name */
215         for (;;)
216         {
217                 unnamed_portal_count++;
218                 sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
219                 if (GetPortalByName(portalname) == NULL)
220                         break;
221         }
222
223         return CreatePortal(portalname, false, false);
224 }
225
226 /*
227  * PortalDefineQuery
228  *              A simple subroutine to establish a portal's query.
229  *
230  * Notes: commandTag shall be NULL if and only if the original query string
231  * (before rewriting) was an empty string.      Also, the passed commandTag must
232  * be a pointer to a constant string, since it is not copied.  The caller is
233  * responsible for ensuring that the passed sourceText (if any), parse and
234  * plan trees have adequate lifetime.  Also, queryContext must accurately
235  * describe the location of the parse and plan trees.
236  */
237 void
238 PortalDefineQuery(Portal portal,
239                                   const char *sourceText,
240                                   const char *commandTag,
241                                   List *parseTrees,
242                                   List *planTrees,
243                                   MemoryContext queryContext)
244 {
245         AssertArg(PortalIsValid(portal));
246         AssertState(portal->queryContext == NULL);      /* else defined already */
247
248         Assert(length(parseTrees) == length(planTrees));
249
250         Assert(commandTag != NULL || parseTrees == NIL);
251
252         portal->sourceText = sourceText;
253         portal->commandTag = commandTag;
254         portal->parseTrees = parseTrees;
255         portal->planTrees = planTrees;
256         portal->queryContext = queryContext;
257 }
258
259 /*
260  * PortalCreateHoldStore
261  *              Create the tuplestore for a portal.
262  */
263 void
264 PortalCreateHoldStore(Portal portal)
265 {
266         MemoryContext oldcxt;
267
268         Assert(portal->holdContext == NULL);
269         Assert(portal->holdStore == NULL);
270
271         /*
272          * Create the memory context that is used for storage of the tuple
273          * set. Note this is NOT a child of the portal's heap memory.
274          */
275         portal->holdContext =
276                 AllocSetContextCreate(PortalMemory,
277                                                           "PortalHeapMemory",
278                                                           ALLOCSET_DEFAULT_MINSIZE,
279                                                           ALLOCSET_DEFAULT_INITSIZE,
280                                                           ALLOCSET_DEFAULT_MAXSIZE);
281
282         /* Create the tuple store, selecting cross-transaction temp files. */
283         oldcxt = MemoryContextSwitchTo(portal->holdContext);
284
285         /* XXX: Should maintenance_work_mem be used for the portal size? */
286         portal->holdStore = tuplestore_begin_heap(true, true, work_mem);
287
288         MemoryContextSwitchTo(oldcxt);
289 }
290
291 /*
292  * PortalDrop
293  *              Destroy the portal.
294  *
295  *              isError: if true, we are destroying portals at the end of a failed
296  *              transaction.  (This causes PortalCleanup to skip unneeded steps.)
297  */
298 void
299 PortalDrop(Portal portal, bool isError)
300 {
301         AssertArg(PortalIsValid(portal));
302
303         /* Not sure if this case can validly happen or not... */
304         if (portal->portalActive)
305                 elog(ERROR, "cannot drop active portal");
306
307         /*
308          * Remove portal from hash table.  Because we do this first, we will
309          * not come back to try to remove the portal again if there's any
310          * error in the subsequent steps.  Better to leak a little memory than
311          * to get into an infinite error-recovery loop.
312          */
313         PortalHashTableDelete(portal);
314
315         /* let portalcmds.c clean up the state it knows about */
316         if (PointerIsValid(portal->cleanup))
317                 (*portal->cleanup) (portal, isError);
318
319         /*
320          * Delete tuplestore if present.  We should do this even under error
321          * conditions; since the tuplestore would have been using cross-
322          * transaction storage, its temp files need to be explicitly deleted.
323          */
324         if (portal->holdStore)
325         {
326                 MemoryContext oldcontext;
327
328                 oldcontext = MemoryContextSwitchTo(portal->holdContext);
329                 tuplestore_end(portal->holdStore);
330                 MemoryContextSwitchTo(oldcontext);
331                 portal->holdStore = NULL;
332         }
333
334         /* delete tuplestore storage, if any */
335         if (portal->holdContext)
336                 MemoryContextDelete(portal->holdContext);
337
338         /* release subsidiary storage */
339         MemoryContextDelete(PortalGetHeapMemory(portal));
340
341         /* release portal struct (it's in PortalMemory) */
342         pfree(portal);
343 }
344
345 /*
346  * DropDependentPortals
347  *              Drop any portals using the specified context as queryContext.
348  *
349  * This is normally used to make sure we can safely drop a prepared statement.
350  */
351 void
352 DropDependentPortals(MemoryContext queryContext)
353 {
354         HASH_SEQ_STATUS status;
355         PortalHashEnt *hentry;
356
357         hash_seq_init(&status, PortalHashTable);
358
359         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
360         {
361                 Portal          portal = hentry->portal;
362
363                 if (portal->queryContext == queryContext)
364                         PortalDrop(portal, false);
365         }
366 }
367
368
369 /*
370  * Pre-commit processing for portals.
371  *
372  * Any holdable cursors created in this transaction need to be converted to
373  * materialized form, since we are going to close down the executor and
374  * release locks.  Remove all other portals created in this transaction.
375  * Portals remaining from prior transactions should be left untouched.
376  *
377  * XXX This assumes that portals can be deleted in a random order, ie,
378  * no portal has a reference to any other (at least not one that will be
379  * exercised during deletion).  I think this is okay at the moment, but
380  * we've had bugs of that ilk in the past.  Keep a close eye on cursor
381  * references...
382  */
383 void
384 AtCommit_Portals(void)
385 {
386         HASH_SEQ_STATUS status;
387         PortalHashEnt *hentry;
388         TransactionId xact = GetCurrentTransactionId();
389
390         hash_seq_init(&status, PortalHashTable);
391
392         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
393         {
394                 Portal          portal = hentry->portal;
395
396                 /*
397                  * Do not touch active portals --- this can only happen in the
398                  * case of a multi-transaction utility command, such as VACUUM.
399                  */
400                 if (portal->portalActive)
401                         continue;
402
403                 if (portal->cursorOptions & CURSOR_OPT_HOLD)
404                 {
405                         /*
406                          * Do nothing to cursors held over from a previous
407                          * transaction.
408                          */
409                         if (portal->createXact != xact)
410                                 continue;
411
412                         /*
413                          * We are exiting the transaction that created a holdable
414                          * cursor.      Instead of dropping the portal, prepare it for
415                          * access by later transactions.
416                          *
417                          * Note that PersistHoldablePortal() must release all resources
418                          * used by the portal that are local to the creating
419                          * transaction.
420                          */
421                         PortalCreateHoldStore(portal);
422                         PersistHoldablePortal(portal);
423                 }
424                 else
425                 {
426                         /* Zap all non-holdable portals */
427                         PortalDrop(portal, false);
428                 }
429         }
430 }
431
432 /*
433  * Abort processing for portals.
434  *
435  * At this point we reset the "active" flags and run the cleanup hook if
436  * present, but we can't release memory until the cleanup call.
437  *
438  * The reason we need to reset active is so that we can replace the unnamed
439  * portal, else we'll fail to execute ROLLBACK when it arrives.  Also, we
440  * want to run the cleanup hook now to be certain it knows that we had an
441  * error abort and not successful conclusion.
442  */
443 void
444 AtAbort_Portals(void)
445 {
446         HASH_SEQ_STATUS status;
447         PortalHashEnt *hentry;
448         TransactionId xact = GetCurrentTransactionId();
449
450         hash_seq_init(&status, PortalHashTable);
451
452         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
453         {
454                 Portal          portal = hentry->portal;
455
456                 portal->portalActive = false;
457
458                 /*
459                  * Do nothing else to cursors held over from a previous
460                  * transaction. (This test must include checking CURSOR_OPT_HOLD,
461                  * else we will fail to clean up a VACUUM portal if it fails after
462                  * its first sub-transaction.)
463                  */
464                 if (portal->createXact != xact &&
465                         (portal->cursorOptions & CURSOR_OPT_HOLD))
466                         continue;
467
468                 /* let portalcmds.c clean up the state it knows about */
469                 if (PointerIsValid(portal->cleanup))
470                 {
471                         (*portal->cleanup) (portal, true);
472                         portal->cleanup = NULL;
473                 }
474         }
475 }
476
477 /*
478  * Post-abort cleanup for portals.
479  *
480  * Delete all portals not held over from prior transactions.
481  */
482 void
483 AtCleanup_Portals(void)
484 {
485         HASH_SEQ_STATUS status;
486         PortalHashEnt *hentry;
487         TransactionId xact = GetCurrentTransactionId();
488
489         hash_seq_init(&status, PortalHashTable);
490
491         while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
492         {
493                 Portal          portal = hentry->portal;
494
495                 /*
496                  * Let's just make sure no one's active...
497                  */
498                 portal->portalActive = false;
499
500                 /*
501                  * Do nothing else to cursors held over from a previous
502                  * transaction. (This test must include checking CURSOR_OPT_HOLD,
503                  * else we will fail to clean up a VACUUM portal if it fails after
504                  * its first sub-transaction.)
505                  */
506                 if (portal->createXact != xact &&
507                         (portal->cursorOptions & CURSOR_OPT_HOLD))
508                         continue;
509
510                 /* Else zap it with prejudice. */
511                 PortalDrop(portal, true);
512         }
513 }