]> granicus.if.org Git - postgresql/blob - src/backend/access/hash/hashscan.c
Reduce pinning and buffer content locking for btree scans.
[postgresql] / src / backend / access / hash / hashscan.c
1 /*-------------------------------------------------------------------------
2  *
3  * hashscan.c
4  *        manage scans on hash tables
5  *
6  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        src/backend/access/hash/hashscan.c
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "access/hash.h"
19 #include "access/relscan.h"
20 #include "utils/memutils.h"
21 #include "utils/rel.h"
22 #include "utils/resowner.h"
23
24
25 /*
26  * We track all of a backend's active scans on hash indexes using a list
27  * of HashScanListData structs, which are allocated in TopMemoryContext.
28  * It's okay to use a long-lived context because we rely on the ResourceOwner
29  * mechanism to clean up unused entries after transaction or subtransaction
30  * abort.  We can't safely keep the entries in the executor's per-query
31  * context, because that might be already freed before we get a chance to
32  * clean up the list.  (XXX seems like there should be a better way to
33  * manage this...)
34  */
35 typedef struct HashScanListData
36 {
37         IndexScanDesc hashsl_scan;
38         ResourceOwner hashsl_owner;
39         struct HashScanListData *hashsl_next;
40 } HashScanListData;
41
42 typedef HashScanListData *HashScanList;
43
44 static HashScanList HashScans = NULL;
45
46
47 /*
48  * ReleaseResources_hash() --- clean up hash subsystem resources.
49  *
50  * This is here because it needs to touch this module's static var HashScans.
51  */
52 void
53 ReleaseResources_hash(void)
54 {
55         HashScanList l;
56         HashScanList prev;
57         HashScanList next;
58
59         /*
60          * Release all HashScanList items belonging to the current ResourceOwner.
61          * Note that we do not release the underlying IndexScanDesc; that's in
62          * executor memory and will go away on its own (in fact quite possibly has
63          * gone away already, so we mustn't try to touch it here).
64          *
65          * Note: this should be a no-op during normal query shutdown. However, in
66          * an abort situation ExecutorEnd is not called and so there may be open
67          * index scans to clean up.
68          */
69         prev = NULL;
70
71         for (l = HashScans; l != NULL; l = next)
72         {
73                 next = l->hashsl_next;
74                 if (l->hashsl_owner == CurrentResourceOwner)
75                 {
76                         if (prev == NULL)
77                                 HashScans = next;
78                         else
79                                 prev->hashsl_next = next;
80
81                         pfree(l);
82                         /* prev does not change */
83                 }
84                 else
85                         prev = l;
86         }
87 }
88
89 /*
90  *      _hash_regscan() -- register a new scan.
91  */
92 void
93 _hash_regscan(IndexScanDesc scan)
94 {
95         HashScanList new_el;
96
97         new_el = (HashScanList) MemoryContextAlloc(TopMemoryContext,
98                                                                                            sizeof(HashScanListData));
99         new_el->hashsl_scan = scan;
100         new_el->hashsl_owner = CurrentResourceOwner;
101         new_el->hashsl_next = HashScans;
102         HashScans = new_el;
103 }
104
105 /*
106  *      _hash_dropscan() -- drop a scan from the scan list
107  */
108 void
109 _hash_dropscan(IndexScanDesc scan)
110 {
111         HashScanList chk,
112                                 last;
113
114         last = NULL;
115         for (chk = HashScans;
116                  chk != NULL && chk->hashsl_scan != scan;
117                  chk = chk->hashsl_next)
118                 last = chk;
119
120         if (chk == NULL)
121                 elog(ERROR, "hash scan list trashed; cannot find 0x%p", (void *) scan);
122
123         if (last == NULL)
124                 HashScans = chk->hashsl_next;
125         else
126                 last->hashsl_next = chk->hashsl_next;
127
128         pfree(chk);
129 }
130
131 /*
132  * Is there an active scan in this bucket?
133  */
134 bool
135 _hash_has_active_scan(Relation rel, Bucket bucket)
136 {
137         Oid                     relid = RelationGetRelid(rel);
138         HashScanList l;
139
140         for (l = HashScans; l != NULL; l = l->hashsl_next)
141         {
142                 if (relid == l->hashsl_scan->indexRelation->rd_id)
143                 {
144                         HashScanOpaque so = (HashScanOpaque) l->hashsl_scan->opaque;
145
146                         if (so->hashso_bucket_valid &&
147                                 so->hashso_bucket == bucket)
148                                 return true;
149                 }
150         }
151
152         return false;
153 }