/* * FindRandomNodeNotInList finds a random node from the shared hash that is not * a member of the current node list. The caller is responsible for making the * necessary node count checks to ensure that such a node exists. * * Note that this function has a selection bias towards nodes whose positions in * the shared hash are sequentially adjacent to the positions of nodes that are * in the current node list. This bias follows from our decision to first pick a * random node in the hash, and if that node is a member of the current list, to * simply iterate to the next node in the hash. Overall, this approach trades in * some selection bias for simplicity in design and for bounded execution time. */ static WorkerNode * FindRandomNodeNotInList(HTAB *WorkerNodesHash, List *currentNodeList) { WorkerNode *workerNode = NULL; HASH_SEQ_STATUS status; uint32 workerNodeCount = 0; uint32 currentNodeCount PG_USED_FOR_ASSERTS_ONLY = 0; bool lookForWorkerNode = true; uint32 workerPosition = 0; uint32 workerIndex = 0; workerNodeCount = hash_get_num_entries(WorkerNodesHash); currentNodeCount = list_length(currentNodeList); Assert(workerNodeCount > currentNodeCount); /* * We determine a random position within the worker hash between [1, N], * assuming that the number of elements in the hash is N. We then get to * this random position by iterating over the worker hash. Please note that * the random seed has already been set by the postmaster when starting up. */ workerPosition = (random() % workerNodeCount) + 1; hash_seq_init(&status, WorkerNodesHash); for (workerIndex = 0; workerIndex < workerPosition; workerIndex++) { workerNode = (WorkerNode *) hash_seq_search(&status); } while (lookForWorkerNode) { bool listMember = ListMember(currentNodeList, workerNode); if (workerNode->inWorkerFile && !listMember) { lookForWorkerNode = false; } else { /* iterate to the next worker node in the hash */ workerNode = (WorkerNode *) hash_seq_search(&status); /* reached end of hash; start from the beginning */ if (workerNode == NULL) { hash_seq_init(&status, WorkerNodesHash); workerNode = (WorkerNode *) hash_seq_search(&status); } } } /* we stopped scanning before completion; therefore clean up scan */ hash_seq_term(&status); return workerNode; }
/** * Return the next variable from a scan of the hash of variables. Note * that this function is not re-entrant. * * @param prev The last variable retrieved by a scan, or NULL if * starting a new scan. * * @return The next variable encountered in the scan. NULL if we have * finished. */ veil_variable_t * vl_next_variable(veil_variable_t *prev) { static bool doing_shared; static HTAB *hash; static HASH_SEQ_STATUS status; static veil_variable_t result; VarEntry *var; if (!session_hash) { session_hash = create_session_hash(); } if (!prev) { doing_shared = true; /* Initialise a scan of the shared hash. */ hash = vl_get_shared_hash(); hash_seq_init(&status, hash); } var = hash_seq_search(&status); if (!var) { /* No more entries from that hash. */ if (doing_shared) { /* Switch to, and get var from, the session hash. */ doing_shared = false; hash = session_hash; hash_seq_init(&status, hash); var = hash_seq_search(&status); } } if (var) { /* Yay, we have an entry. */ result.name = var->key; result.shared = var->shared; if (var->obj) { result.type = vl_ObjTypeName(var->obj->type); } else { result.type = vl_ObjTypeName(OBJ_UNDEFINED);; } return &result; } else { /* Thats all. There are no more entries */ return NULL; } }
/* * Abort processing for portals. * * At this point we reset the "active" flags and run the cleanup hook if * present, but we can't release memory until the cleanup call. * * The reason we need to reset active is so that we can replace the unnamed * portal, else we'll fail to execute ROLLBACK when it arrives. Also, we * want to run the cleanup hook now to be certain it knows that we had an * error abort and not successful conclusion. */ void AtAbort_Portals(void) { HASH_SEQ_STATUS status; PortalHashEnt *hentry; TransactionId xact = GetCurrentTransactionId(); hash_seq_init(&status, PortalHashTable); while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) { Portal portal = hentry->portal; portal->portalActive = false; /* * Do nothing else to cursors held over from a previous * transaction. (This test must include checking CURSOR_OPT_HOLD, * else we will fail to clean up a VACUUM portal if it fails after * its first sub-transaction.) */ if (portal->createXact != xact && (portal->cursorOptions & CURSOR_OPT_HOLD)) continue; /* let portalcmds.c clean up the state it knows about */ if (PointerIsValid(portal->cleanup)) { (*portal->cleanup) (portal, true); portal->cleanup = NULL; } } }
/* Forget any invalid pages >= minblkno, because they've been dropped */ static void forget_invalid_pages(RelFileNode node, ForkNumber forkno, BlockNumber minblkno) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { if (RelFileNodeEquals(hentry->key.node, node) && hentry->key.forkno == forkno && hentry->key.blkno >= minblkno) { if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2) { char *path = relpath(hentry->key.node, forkno); elog(DEBUG2, "page %u of relation %s has been dropped", hentry->key.blkno, path); pfree(path); } if (hash_search(invalid_page_tab, (void *) &hentry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
/* * PersistentFilespace_LookupMirrorDbid() * * Check the gp_persistent_filespace table to identify what dbid it contains * that does not match the primary dbid. If there are no filespaces currently * defined this check will return 0 even if there is an active mirror, because * the segment doesn't know any better. */ int16 PersistentFilespace_LookupMirrorDbid(int16 primaryDbid) { HASH_SEQ_STATUS status; FilespaceDirEntry dirEntry; int16 mirrorDbid = 0; PersistentFilespace_VerifyInitScan(); /* Start scan */ hash_seq_init(&status, persistentFilespaceSharedHashTable); dirEntry = (FilespaceDirEntry) hash_seq_search(&status); if (dirEntry != NULL) { if (dirEntry->dbId1 == primaryDbid) { mirrorDbid = dirEntry->dbId2; } else if (dirEntry->dbId2 == primaryDbid) { mirrorDbid = dirEntry->dbId1; } else { elog(FATAL, "dbid %d not found in gp_persistent_filespace_node", (int) primaryDbid); } /* Terminate the scan early */ hash_seq_term(&status); } return mirrorDbid; }
char * serialize_filesystem_credentials(int *size) { HASH_SEQ_STATUS status; struct FileSystemCredential *entry; StringInfoData buffer; HTAB * currentFilesystemCredentials; MemoryContext currentFilesystemCredentialsMemoryContext; get_current_credential_cache_and_memcxt(¤tFilesystemCredentials, ¤tFilesystemCredentialsMemoryContext); Insist(NULL != currentFilesystemCredentials); Insist(NULL != currentFilesystemCredentialsMemoryContext); initStringInfo(&buffer); hash_seq_init(&status, currentFilesystemCredentials); while (NULL != (entry = hash_seq_search(&status))) serialize_filesystem_credential(&buffer, entry); *size = buffer.len; return buffer.data; }
/* Forget any invalid pages in a whole database */ static void forget_invalid_pages_db(Oid dbid) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { if (hentry->key.node.dbNode == dbid) { elog(DEBUG2, "page %u of relation %u/%u/%u has been dropped", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); if (hash_search(invalid_page_tab, (void *) &hentry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
void FileRepAckPrimary_ShmemReInit(void) { HASH_SEQ_STATUS hash_status; FileRepAckHashEntry_s *entry; if (fileRepAckHashShmem == NULL) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), (errmsg("not enough shared memory for mirroring")))); } fileRepAckHashShmem->ipcArrayIndex = IndexIpcArrayAckHashShmem; if (fileRepAckHashShmem->hash == NULL) { ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), (errmsg("not enough shared memory for mirroring")))); } LWLockAcquire(FileRepAckHashShmemLock, LW_EXCLUSIVE); hash_seq_init(&hash_status, fileRepAckHashShmem->hash); while ((entry = (FileRepAckHashEntry_s *) hash_seq_search(&hash_status)) != NULL) { FileRepAckPrimary_RemoveHashEntry(entry->fileName); } LWLockRelease(FileRepAckHashShmemLock); return; }
/* * WorkerGetNodeWithName finds and returns a node from the membership list that * has the given hostname. The function returns null if no such node exists. */ WorkerNode * WorkerGetNodeWithName(const char *hostname) { WorkerNode *workerNode = NULL; HASH_SEQ_STATUS status; hash_seq_init(&status, WorkerNodesHash); workerNode = (WorkerNode *) hash_seq_search(&status); while (workerNode != NULL) { if (workerNode->inWorkerFile) { int nameCompare = strncmp(workerNode->workerName, hostname, WORKER_LENGTH); if (nameCompare == 0) { hash_seq_term(&status); break; } } workerNode = (WorkerNode *) hash_seq_search(&status); } return workerNode; }
/* * Clears the contents of the entire Local MDVSN specified. */ void mdver_local_mdvsn_nuke(mdver_local_mdvsn *local_mdvsn) { Assert(NULL != local_mdvsn); Assert(NULL != local_mdvsn->htable); HASH_SEQ_STATUS iterator; hash_seq_init(&iterator, local_mdvsn->htable); mdver_entry *entry = NULL; int num_deleted = 0; while ((entry = (mdver_entry *) hash_seq_search(&iterator)) != NULL) { #if USE_ASSERT_CHECKING mdver_entry *result = (mdver_entry *) #endif hash_search(local_mdvsn->htable, (void *) &(entry->key), HASH_REMOVE, NULL); Assert(NULL != result); num_deleted++; } #ifdef MD_VERSIONING_INSTRUMENTATION elog(gp_mdversioning_loglevel, "Nuke at Local MDVSN deleted %d entries", num_deleted); #endif local_mdvsn->nuke_happened = true; return; }
/* * Post-abort cleanup for portals. * * Delete all portals not held over from prior transactions. */ void AtCleanup_Portals(void) { HASH_SEQ_STATUS status; PortalHashEnt *hentry; TransactionId xact = GetCurrentTransactionId(); hash_seq_init(&status, PortalHashTable); while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) { Portal portal = hentry->portal; /* * Let's just make sure no one's active... */ portal->portalActive = false; /* * Do nothing else to cursors held over from a previous * transaction. (This test must include checking CURSOR_OPT_HOLD, * else we will fail to clean up a VACUUM portal if it fails after * its first sub-transaction.) */ if (portal->createXact != xact && (portal->cursorOptions & CURSOR_OPT_HOLD)) continue; /* Else zap it with prejudice. */ PortalDrop(portal, true); } }
/* Forget any invalid pages in a whole database */ static void forget_invalid_pages_db(Oid dbid) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { if (hentry->key.node.dbNode == dbid) { if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2) { char *path = relpathperm(hentry->key.node, hentry->key.forkno); elog(DEBUG2, "page %u of relation %s has been dropped", hentry->key.blkno, path); pfree(path); } if (hash_search(invalid_page_tab, (void *) &hentry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
/* * RelfilenodeMapInvalidateCallback * Flush mapping entries when pg_class is updated in a relevant fashion. */ static void RelfilenodeMapInvalidateCallback(Datum arg, Oid relid) { HASH_SEQ_STATUS status; RelfilenodeMapEntry *entry; /* nothing to do if not active or deleted */ if (RelfilenodeMapHash == NULL) return; /* if relid is InvalidOid, we must invalidate the entire cache */ if (relid == InvalidOid) { hash_destroy(RelfilenodeMapHash); RelfilenodeMapHash = NULL; return; } hash_seq_init(&status, RelfilenodeMapHash); while ((entry = (RelfilenodeMapEntry *) hash_seq_search(&status)) != NULL) { /* Same OID may occur in more than one tablespace. */ if (entry->relid == relid) { if (hash_search(RelfilenodeMapHash, (void *) &entry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
/* Complain about any remaining invalid-page entries */ void XLogCheckInvalidPages(void) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; bool foundone = false; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); /* * Our strategy is to emit WARNING messages for all remaining entries and * only PANIC after we've dumped all the available info. */ while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { report_invalid_page(WARNING, hentry->key.node, hentry->key.forkno, hentry->key.blkno, hentry->present); foundone = true; } if (foundone) elog(PANIC, "WAL contains references to invalid pages"); hash_destroy(invalid_page_tab); invalid_page_tab = NULL; }
/* * DynamicScan_CreateIterator * Creates an iterator state (i.e., DynamicPartitionIterator) * and saves it to the estate's DynamicTableScanInfo. */ static void DynamicScan_CreateIterator(ScanState *scanState, Scan *scan) { EState *estate = scanState->ps.state; Assert(NULL != estate); DynamicTableScanInfo *partitionInfo = estate->dynamicTableScanInfo; /* * Ensure that the dynahash exists even if the partition selector * didn't choose any partition for current scan node [MPP-24169]. */ InsertPidIntoDynamicTableScanInfo(scan->partIndex, InvalidOid, InvalidPartitionSelectorId); Assert(NULL != partitionInfo && NULL != partitionInfo->pidIndexes); Assert(partitionInfo->numScans >= scan->partIndex); Assert(NULL == partitionInfo->iterators[scan->partIndex - 1]); DynamicPartitionIterator *iterator = palloc(sizeof(DynamicPartitionIterator)); iterator->curRelOid = InvalidOid; iterator->partitionMemoryContext = AllocSetContextCreate(CurrentMemoryContext, "DynamicTableScanPerPartition", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); iterator->partitionOids = partitionInfo->pidIndexes[scan->partIndex - 1]; Assert(iterator->partitionOids != NULL); iterator->shouldCallHashSeqTerm = true; HASH_SEQ_STATUS *partitionIterator = palloc(sizeof(HASH_SEQ_STATUS)); hash_seq_init(partitionIterator, iterator->partitionOids); iterator->partitionIterator = partitionIterator; partitionInfo->iterators[scan->partIndex - 1] = iterator; }
/* Forget any invalid pages in a whole database */ static void forget_invalid_pages_db(Oid tblspc, Oid dbid) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { if ((!OidIsValid(tblspc) || hentry->key.node.spcNode == tblspc) && hentry->key.node.dbNode == dbid) { elog(DEBUG2, "page %u of relation %u/%u/%u has been dropped", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "forget_invalid_pages_db: %u of relation %u/%u/%u has been dropped", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); if (hash_search(invalid_page_tab, (void *) &hentry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
/* * Release connection created by calling GetConnection. */ void mysql_rel_connection(MYSQL *conn) { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; if (ConnectionHash == NULL) return; hash_seq_init(&scan, ConnectionHash); while ((entry = (ConnCacheEntry *) hash_seq_search(&scan))) { if (entry->conn == NULL) continue; if (entry->conn == conn) { elog(DEBUG3, "disconnecting mysql_fdw connection %p", entry->conn); _mysql_close(entry->conn); entry->conn = NULL; hash_seq_term(&scan); break; } } }
void PersistentTablespace_ActivateStandby(int16 oldmaster, int16 newmaster) { TablespaceDirEntry tablespaceDirEntry; HASH_SEQ_STATUS hstat; WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; hash_seq_init(&hstat, persistentTablespaceSharedHashTable); if (Persistent_BeforePersistenceWork()) elog(ERROR, "persistent table changes forbidden"); PersistentTablespace_VerifyInitScan(); WRITE_PERSISTENT_STATE_ORDERED_LOCK; LWLockAcquire(TablespaceHashLock, LW_SHARED); while ((tablespaceDirEntry = hash_seq_search(&hstat)) != NULL) { PersistentFileSysObjName fsObjName; Oid tblspc = tablespaceDirEntry->key.tablespaceOid; ItemPointerData persistentTid; uint64 persistentSerialNum; tablespaceDirEntry = PersistentTablespace_FindEntryUnderLock(tblspc); if (tablespaceDirEntry == NULL) elog(ERROR, "cannot find persistent tablespace entry %u", tblspc); persistentSerialNum = tablespaceDirEntry->persistentSerialNum; ItemPointerCopy(&tablespaceDirEntry->persistentTid, &persistentTid); /* * We release TablespaceHashLock in the middle of the loop and * re-acquire it after doing persistent table change. This is needed * to prevent holding the lock for any purpose other than to protect * the tablespace shared hash table. Not releasing this lock could * result in file I/O and potential deadlock due to other LW locks * being acquired in the process. Releasing the lock this way is safe * because we are still holding PersistentObjLock in exclusive mode. * Any change to the filespace shared hash table is also protected by * PersistentObjLock. */ LWLockRelease(TablespaceHashLock); PersistentFileSysObjName_SetTablespaceDir(&fsObjName, tblspc); PersistentFileSysObj_ActivateStandby(&fsObjName, &persistentTid, persistentSerialNum, oldmaster, newmaster, /* flushToXlog */ false); LWLockAcquire(TablespaceHashLock, LW_SHARED); } LWLockRelease(TablespaceHashLock); WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; }
/* Complain about any remaining invalid-page entries */ void XLogCheckInvalidPages(void) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; bool foundone = false; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); /* * Our strategy is to emit WARNING messages for all remaining entries and * only PANIC after we've dumped all the available info. */ while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { if (hentry->present) elog(WARNING, "page %u of relation %u/%u/%u was uninitialized", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); else elog(WARNING, "page %u of relation %u/%u/%u did not exist", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); foundone = true; } if (foundone) elog(PANIC, "WAL contains references to invalid pages"); }
/* * Deallocate least used entries. * Caller must hold an exclusive lock on pgss->lock. */ static void entry_dealloc(void) { HASH_SEQ_STATUS hash_seq; pgssEntry **entries; pgssEntry *entry; int nvictims; int i; /* Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them. */ entries = palloc(hash_get_num_entries(pgss_hash) * sizeof(pgssEntry *)); i = 0; hash_seq_init(&hash_seq, pgss_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { entries[i++] = entry; entry->counters.usage *= USAGE_DECREASE_FACTOR; } qsort(entries, i, sizeof(pgssEntry *), entry_cmp); nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100); nvictims = Min(nvictims, i); for (i = 0; i < nvictims; i++) { hash_search(pgss_hash, &entries[i]->key, HASH_REMOVE, NULL); } pfree(entries); }
/* * dumpDynamicTableScanPidIndex * Write out pids for a given dynamic table scan. */ void dumpDynamicTableScanPidIndex(int index) { if (index < 0 || dynamicTableScanInfo == NULL || index > dynamicTableScanInfo->numScans || dynamicTableScanInfo->pidIndexes[index] == NULL) { return; } Assert(dynamicTableScanInfo != NULL && index < dynamicTableScanInfo->numScans && dynamicTableScanInfo->pidIndexes[index] != NULL); HASH_SEQ_STATUS status; hash_seq_init(&status, dynamicTableScanInfo->pidIndexes[index]); StringInfoData pids; initStringInfo(&pids); Oid *partOid = NULL; while ((partOid = (Oid *)hash_seq_search(&status)) != NULL) { appendStringInfo(&pids, "%d ", *partOid); } elog(LOG, "Dynamic Table Scan %d pids: %s", index, pids.data); pfree(pids.data); }
/* * smgrIsAppendOnlyMirrorResyncEofs() -- Returns true if there Append-Only Mirror Resync * EOF work that needs to be done post-commit or post-abort work. * * Note that the list does not include anything scheduled for termination * by upper-level transactions. */ bool smgrIsAppendOnlyMirrorResyncEofs(EndXactRecKind endXactRecKind) { int nestLevel = GetCurrentTransactionNestLevel(); HASH_SEQ_STATUS iterateStatus; AppendOnlyMirrorResyncEofs *entry; if (AppendOnlyMirrorResyncEofsTable == NULL) { return false; } hash_seq_init(&iterateStatus, AppendOnlyMirrorResyncEofsTable); while ((entry = hash_seq_search(&iterateStatus)) != NULL) { if (entry->key.nestLevel >= nestLevel) { /* Deregister seq scan and exit early. */ hash_seq_term(&iterateStatus); return true; } } return false; }
/* * RelfilenodeMapInvalidateCallback * Flush mapping entries when pg_class is updated in a relevant fashion. */ static void RelfilenodeMapInvalidateCallback(Datum arg, Oid relid) { HASH_SEQ_STATUS status; RelfilenodeMapEntry *entry; /* callback only gets registered after creating the hash */ Assert(RelfilenodeMapHash != NULL); hash_seq_init(&status, RelfilenodeMapHash); while ((entry = (RelfilenodeMapEntry *) hash_seq_search(&status)) != NULL) { /* * If relid is InvalidOid, signalling a complete reset, we must remove * all entries, otherwise just remove the specific relation's entry. * Always remove negative cache entries. */ if (relid == InvalidOid || /* complete reset */ entry->relid == InvalidOid || /* negative cache entry */ entry->relid == relid) /* individual flushed relation */ { if (hash_search(RelfilenodeMapHash, (void *) &entry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
/* Forget any invalid pages >= minblkno, because they've been dropped */ static void forget_invalid_pages(RelFileNode node, BlockNumber minblkno) { HASH_SEQ_STATUS status; xl_invalid_page *hentry; if (invalid_page_tab == NULL) return; /* nothing to do */ hash_seq_init(&status, invalid_page_tab); while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL) { if (RelFileNodeEquals(hentry->key.node, node) && hentry->key.blkno >= minblkno) { elog(DEBUG2, "page %u of relation %u/%u/%u has been dropped", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "forget_invalid_pages: page %u of relation %u/%u/%u has been dropped", hentry->key.blkno, hentry->key.node.spcNode, hentry->key.node.dbNode, hentry->key.node.relNode); if (hash_search(invalid_page_tab, (void *) &hentry->key, HASH_REMOVE, NULL) == NULL) elog(ERROR, "hash table corrupted"); } } }
/* * Pre-prepare processing for portals. * * Currently we refuse PREPARE if the transaction created any holdable * cursors, since it's quite unclear what to do with one. However, this * has the same API as CommitHoldablePortals and is invoked in the same * way by xact.c, so that we can easily do something reasonable if anyone * comes up with something reasonable to do. * * Returns TRUE if any holdable cursors were processed, FALSE if not. */ bool PrepareHoldablePortals(void) { bool result = false; HASH_SEQ_STATUS status; PortalHashEnt *hentry; hash_seq_init(&status, PortalHashTable); while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) { Portal portal = hentry->portal; /* Is it a holdable portal created in the current xact? */ if ((portal->cursorOptions & CURSOR_OPT_HOLD) && portal->createSubid != InvalidSubTransactionId && portal->status == PORTAL_READY) { /* * We are exiting the transaction that created a holdable cursor. * Can't do PREPARE. */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD"))); } } return result; }
/* * shmem_shutdown hook: Dump statistics into file. * * Note: we don't bother with acquiring lock, because there should be no * other processes running when this is called. */ static void pgss_shmem_shutdown(int code, Datum arg) { FILE *file; HASH_SEQ_STATUS hash_seq; int32 num_entries; pgssEntry *entry; /* Don't try to dump during a crash. */ if (code) return; /* Safety check ... shouldn't get here unless shmem is set up. */ if (!pgss || !pgss_hash) return; /* Don't dump if told not to. */ if (!pgss_save) return; file = AllocateFile(PGSS_DUMP_FILE, PG_BINARY_W); if (file == NULL) goto error; if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1) goto error; num_entries = hash_get_num_entries(pgss_hash); if (fwrite(&num_entries, sizeof(int32), 1, file) != 1) goto error; hash_seq_init(&hash_seq, pgss_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { int len = entry->key.query_len; if (fwrite(entry, offsetof(pgssEntry, mutex), 1, file) != 1 || fwrite(entry->query, 1, len, file) != len) goto error; } if (FreeFile(file)) { file = NULL; goto error; } return; error: ereport(LOG, (errcode_for_file_access(), errmsg("could not write pg_stat_statement file \"%s\": %m", PGSS_DUMP_FILE))); if (file) FreeFile(file); unlink(PGSS_DUMP_FILE); }
/* * initialize the seq search of the DispatchedFilespaceDirHashTable. */ static void DispatchedFilespace_SeqSearch_Init(void) { if (DispatchedFileSpace_SeqSearch_Initialized || (!DispatchedFilespaceDirHashTable)) { return; } hash_seq_init(&DispatchedFileSpace_SeqSearch, DispatchedFilespaceDirHashTable); DispatchedFileSpace_SeqSearch_Initialized = true; }
static void xact_callback(XactEvent event, void *arg) { HASH_SEQ_STATUS scan; PlxConnHashEntry *entry = NULL; char *sql = NULL; switch (event) { case XACT_EVENT_PRE_COMMIT: sql = "commit;"; break; case XACT_EVENT_ABORT: sql = "rollback;"; break; case XACT_EVENT_PRE_PREPARE: ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("cannot prepare a transaction that modified remote tables"))); break; case XACT_EVENT_COMMIT: case XACT_EVENT_PREPARE: /* Pre-commit should have closed the open transaction */ ereport(ERROR, (errcode(ERRCODE_RAISE_EXCEPTION), errmsg("missed cleaning up connection during pre-commit"))); //FIXME cur_func in NULL break; default: return; } /* * Scan all connection cache entries to find open remote transactions, and * close them. */ hash_seq_init(&scan, plx_conn_cache); while ((entry = (PlxConnHashEntry *) hash_seq_search(&scan))) { PlxConn *plx_conn = entry->plx_conn; if (plx_conn->xlevel > 0) { PGresult *pg_result; pg_result = PQexec(plx_conn->pq_conn, sql); if (pg_result) PQclear(pg_result); plx_conn->xlevel = 0; } } UnregisterXactCallback(xact_callback, NULL); UnregisterSubXactCallback(subxact_callback, NULL); is_remote_transaction = false; }
/* * End a rewrite. * * state and any other resources are freed. */ void end_heap_rewrite(RewriteState state) { HASH_SEQ_STATUS seq_status; UnresolvedTup unresolved; /* * Write any remaining tuples in the UnresolvedTups table. If we have any * left, they should in fact be dead, but let's err on the safe side. */ hash_seq_init(&seq_status, state->rs_unresolved_tups); while ((unresolved = hash_seq_search(&seq_status)) != NULL) { ItemPointerSetInvalid(&unresolved->tuple->t_data->t_ctid); raw_heap_insert(state, unresolved->tuple); } /* Write the last page, if any */ if (state->rs_buffer_valid) { if (state->rs_use_wal) log_newpage(&state->rs_new_rel->rd_node, MAIN_FORKNUM, state->rs_blockno, state->rs_buffer, true); RelationOpenSmgr(state->rs_new_rel); PageSetChecksumInplace(state->rs_buffer, state->rs_blockno); smgrextend(state->rs_new_rel->rd_smgr, MAIN_FORKNUM, state->rs_blockno, (char *) state->rs_buffer, true); } /* * If the rel is WAL-logged, must fsync before commit. We use heap_sync * to ensure that the toast table gets fsync'd too. * * It's obvious that we must do this when not WAL-logging. It's less * obvious that we have to do it even if we did WAL-log the pages. The * reason is the same as in tablecmds.c's copy_relation_data(): we're * writing data that's not in shared buffers, and so a CHECKPOINT * occurring during the rewriteheap operation won't have fsync'd data we * wrote before the checkpoint. */ if (RelationNeedsWAL(state->rs_new_rel)) heap_sync(state->rs_new_rel); /* Deleting the context frees everything */ MemoryContextDelete(state->rs_cxt); }
/* * Pre-commit processing for portals. * * Remove all non-holdable portals created in this transaction. * Portals remaining from prior transactions should be left untouched. */ void AtCommit_Portals(void) { HASH_SEQ_STATUS status; PortalHashEnt *hentry; hash_seq_init(&status, PortalHashTable); while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) { Portal portal = hentry->portal; /* * Do not touch active portals --- this can only happen in the case of * a multi-transaction utility command, such as VACUUM. * * Note however that any resource owner attached to such a portal is * still going to go away, so don't leave a dangling pointer. */ if (portal->status == PORTAL_ACTIVE) { portal->resowner = NULL; continue; } /* * Do nothing to cursors held over from a previous transaction * (including holdable ones just frozen by CommitHoldablePortals). */ if (portal->createSubid == InvalidSubTransactionId) continue; /* Zap all non-holdable portals */ PortalDrop(portal, true); /* Restart the iteration */ hash_seq_init(&status, PortalHashTable); } }