| /*------------------------------------------------------------------------- |
| * |
| * hashscan.c |
| * manage scans on hash tables |
| * |
| * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * $PostgreSQL: pgsql/src/backend/access/hash/hashscan.c,v 1.40 2006/03/05 15:58:20 momjian Exp $ |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "access/hash.h" |
| #include "utils/memutils.h" |
| #include "utils/resowner.h" |
| |
| /* |
| * We track all of a backend's active scans on hash indexes using a list |
| * of HashScanListData structs, which are allocated in TopMemoryContext. |
| * It's okay to use a long-lived context because we rely on the ResourceOwner |
| * mechanism to clean up unused entries after transaction or subtransaction |
| * abort. We can't safely keep the entries in the executor's per-query |
| * context, because that might be already freed before we get a chance to |
| * clean up the list. (XXX seems like there should be a better way to |
| * manage this...) |
| */ |
| |
| typedef struct HashScanListData |
| { |
| IndexScanDesc hashsl_scan; |
| ResourceOwner hashsl_owner; |
| struct HashScanListData *hashsl_next; |
| } HashScanListData; |
| |
| typedef HashScanListData *HashScanList; |
| |
| static HashScanList HashScans = NULL; |
| |
| |
| /* |
| * ReleaseResources_hash() --- clean up hash subsystem resources. |
| * |
| * This is here because it needs to touch this module's static var HashScans. |
| */ |
| void |
| ReleaseResources_hash(void) |
| { |
| HashScanList l; |
| HashScanList prev; |
| HashScanList next; |
| |
| /* |
| * Release all HashScanList items belonging to the current ResourceOwner. |
| * Note that we do not release the underlying IndexScanDesc; that's in |
| * executor memory and will go away on its own (in fact quite possibly |
| * has gone away already, so we mustn't try to touch it here). |
| * |
| * Note: this should be a no-op during normal query shutdown. However, in |
| * an abort situation ExecutorEnd is not called and so there may be open |
| * index scans to clean up. |
| */ |
| prev = NULL; |
| |
| for (l = HashScans; l != NULL; l = next) |
| { |
| next = l->hashsl_next; |
| if (l->hashsl_owner == CurrentResourceOwner) |
| { |
| if (prev == NULL) |
| HashScans = next; |
| else |
| prev->hashsl_next = next; |
| |
| pfree(l); |
| /* prev does not change */ |
| } |
| else |
| prev = l; |
| } |
| } |
| |
| /* |
| * _hash_regscan() -- register a new scan. |
| */ |
| void |
| _hash_regscan(IndexScanDesc scan) |
| { |
| HashScanList new_el; |
| |
| new_el = (HashScanList) MemoryContextAlloc(TopMemoryContext, |
| sizeof(HashScanListData)); |
| new_el->hashsl_scan = scan; |
| new_el->hashsl_owner = CurrentResourceOwner; |
| new_el->hashsl_next = HashScans; |
| HashScans = new_el; |
| } |
| |
| /* |
| * _hash_dropscan() -- drop a scan from the scan list |
| */ |
| void |
| _hash_dropscan(IndexScanDesc scan) |
| { |
| HashScanList chk, |
| last; |
| |
| last = NULL; |
| for (chk = HashScans; |
| chk != NULL && chk->hashsl_scan != scan; |
| chk = chk->hashsl_next) |
| last = chk; |
| |
| if (chk == NULL) |
| elog(ERROR, "hash scan list trashed; can't find 0x%p", (void *) scan); |
| |
| if (last == NULL) |
| HashScans = chk->hashsl_next; |
| else |
| last->hashsl_next = chk->hashsl_next; |
| |
| pfree(chk); |
| } |
| |
| /* |
| * Is there an active scan in this bucket? |
| */ |
| bool |
| _hash_has_active_scan(Relation rel, Bucket bucket) |
| { |
| Oid relid = RelationGetRelid(rel); |
| HashScanList l; |
| |
| for (l = HashScans; l != NULL; l = l->hashsl_next) |
| { |
| if (relid == l->hashsl_scan->indexRelation->rd_id) |
| { |
| HashScanOpaque so = (HashScanOpaque) l->hashsl_scan->opaque; |
| |
| if (so->hashso_bucket_valid && |
| so->hashso_bucket == bucket) |
| return true; |
| } |
| } |
| |
| return false; |
| } |