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