1 /*-------------------------------------------------------------------------
2 *
3 * hashscan.c
4 * manage scans on hash tables
5 *
6 * Portions Copyright (c) 1996-2016, 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
ReleaseResources_hash(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
_hash_regscan(IndexScanDesc scan)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
_hash_dropscan(IndexScanDesc scan)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
_hash_has_active_scan(Relation rel,Bucket bucket)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 }
154