PersistentTidIsKnownResult PersistentStore_TidIsKnown( PersistentStoreSharedData *storeSharedData, ItemPointer persistentTid, ItemPointer maxTid) { *maxTid = storeSharedData->maxTid; Assert(!PersistentStore_IsZeroTid(persistentTid)); // UNDONE: I think the InRecovery test only applies to physical Master Mirroring on Standby. /* Only test this outside of recovery scenarios */ if (Persistent_BeforePersistenceWork()) return PersistentTidIsKnownResult_BeforePersistenceWork; if (storeSharedData->needToScanIntoSharedMemory) return PersistentTidIsKnownResult_ScanNotPerformedYet; if (PersistentStore_IsZeroTid(&storeSharedData->maxTid)) return PersistentTidIsKnownResult_MaxTidIsZero; if (ItemPointerCompare( persistentTid, &storeSharedData->maxTid) <= 0) // Less-than or equal. return PersistentTidIsKnownResult_Known; else return PersistentTidIsKnownResult_NotKnown; }
Datum tidsmaller(PG_FUNCTION_ARGS) { ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2); }
Datum bttidcmp(PG_FUNCTION_ARGS) { ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); PG_RETURN_INT32(ItemPointerCompare(arg1, arg2)); }
Datum tidge(PG_FUNCTION_ARGS) { ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0); }
static void PersistentStore_DoInsertTuple( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, Relation persistentRel, /* The persistent table relation. */ Datum *values, bool flushToXLog, /* When true, the XLOG record for this change will be flushed to disk. */ ItemPointer persistentTid) /* TID of the stored tuple. */ { bool *nulls; HeapTuple persistentTuple = NULL; XLogRecPtr xlogInsertEndLoc; /* * In order to keep the tuples the exact same size to enable direct reuse of * free tuples, we do not use NULLs. */ nulls = (bool*)palloc0(storeData->numAttributes * sizeof(bool)); /* * Form the tuple. */ persistentTuple = heap_form_tuple(persistentRel->rd_att, values, nulls); if (!HeapTupleIsValid(persistentTuple)) elog(ERROR, "Failed to build persistent tuple ('%s')", storeData->tableName); /* * (We have an exclusive lock (higher up) here so we can direct the insert to the last page.) */ { // Do not assert valid ItemPointer -- it is ok if it is (0,0)... BlockNumber blockNumber = BlockIdGetBlockNumber( &storeSharedData->maxTid.ip_blkid); frozen_heap_insert_directed( persistentRel, persistentTuple, blockNumber); } if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_DoInsertTuple: old maximum known TID %s, new insert TID %s ('%s')", ItemPointerToString(&storeSharedData->maxTid), ItemPointerToString2(&persistentTuple->t_self), storeData->tableName); if (ItemPointerCompare( &storeSharedData->maxTid, &persistentTuple->t_self) == -1) { // Current max is Less-Than. storeSharedData->maxTid = persistentTuple->t_self; } /* * Return the TID of the INSERT tuple. * Return the XLOG location of the INSERT tuple's XLOG record. */ *persistentTid = persistentTuple->t_self; xlogInsertEndLoc = XLogLastInsertEndLoc(); heap_freetuple(persistentTuple); if (flushToXLog) { XLogFlush(xlogInsertEndLoc); XLogRecPtr_Zero(&nowaitXLogEndLoc); } else nowaitXLogEndLoc = xlogInsertEndLoc; pfree(nulls); }
static void PersistentStore_InitScanVerifyFreeEntries( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData) { int64 freeOrderNum; ItemPointerData freeTid; PersistentFreeEntry *entry; HASH_SEQ_STATUS iterateStatus; freeOrderNum = storeSharedData->maxFreeOrderNum; freeTid = storeSharedData->freeTid; if (freeOrderNum == 0) { if (!PersistentStore_IsZeroTid(&freeTid)) elog(ERROR, "Expected free TID to be (0,0) when free order number is 0 in '%s'", storeData->tableName); } else { PersistentFreeEntryKey key; PersistentFreeEntry *removeEntry; if (freeEntryHashTable == NULL) elog(ERROR, "Looking for free order number " INT64_FORMAT " and the free entry hash table is empty for '%s'", freeOrderNum, storeData->tableName); while (true) { MemSet(&key, 0, sizeof(key)); key.persistentTid = freeTid; entry = (PersistentFreeEntry*) hash_search(freeEntryHashTable, (void *) &key, HASH_FIND, NULL); if (entry == NULL) elog(ERROR, "Did not find free entry for free TID %s (free order number " INT64_FORMAT ") for '%s'", ItemPointerToString(&freeTid), freeOrderNum, storeData->tableName); if (PersistentStore_IsZeroTid(&entry->previousFreeTid)) elog(ERROR, "Previous free TID not expected to be (0,0) -- persistent Free Entry hashtable corrupted for '%s' " "(expected free order number " INT64_FORMAT ", entry free order number " INT64_FORMAT ")", storeData->tableName, freeOrderNum, entry->freeOrderNum); if (freeOrderNum != entry->freeOrderNum) elog(ERROR, "Free entry for free TID %s has wrong free order number (expected free order number " INT64_FORMAT ", found free order number " INT64_FORMAT ") for '%s'", ItemPointerToString(&freeTid), freeOrderNum, entry->freeOrderNum, storeData->tableName); if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "PersistentStore_InitScanVerifyFreeEntries ('%s'): Free order number " INT64_FORMAT ", free TID %s, previous free TID %s", storeData->tableName, freeOrderNum, ItemPointerToString(&freeTid), ItemPointerToString2(&entry->previousFreeTid)); freeTid = entry->previousFreeTid; Insist(!PersistentStore_IsZeroTid(&freeTid)); // Note the error check above. if (freeOrderNum == 1) { /* * The last free entry uses its own TID in previous_free_tid. */ if (ItemPointerCompare( &entry->key.persistentTid, &freeTid) != 0) { elog(ERROR, "Expected previous_free_tid %s to match the persistent TID %s for the last free entry (free order number 1) for '%s'", ItemPointerToString(&freeTid), ItemPointerToString2(&entry->key.persistentTid), storeData->tableName); } } removeEntry = hash_search( freeEntryHashTable, (void *) &entry->key, HASH_REMOVE, NULL); if (removeEntry == NULL) elog(ERROR, "Persistent Free Entry hashtable corrupted for '%s'", storeData->tableName); entry = NULL; freeOrderNum--; if (freeOrderNum == 0) break; } } if (freeEntryHashTable != NULL) { hash_seq_init( &iterateStatus, freeEntryHashTable); /* * Verify the hash table has no free entries left. */ while ((entry = hash_seq_search(&iterateStatus)) != NULL) { elog(ERROR, "Found at least one unaccounted for free entry for '%s'. Example: free order number " INT64_FORMAT ", free TID %s, previous free TID %s", storeData->tableName, entry->freeOrderNum, ItemPointerToString(&entry->key.persistentTid), ItemPointerToString2(&entry->previousFreeTid)); } hash_destroy(freeEntryHashTable); freeEntryHashTable = NULL; } if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "PersistentStore_InitScanVerifyFreeEntries ('%s'): Successfully verified " INT64_FORMAT " free entries", storeData->tableName, storeSharedData->maxFreeOrderNum); }
static void PersistentStore_DoInitScan( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData) { PersistentStoreScan storeScan; ItemPointerData persistentTid; int64 persistentSerialNum; ItemPointerData previousFreeTid; Datum *values; int64 globalSequenceNum; values = (Datum*)palloc(storeData->numAttributes * sizeof(Datum)); MemSet(&storeSharedData->maxTid, 0, sizeof(ItemPointerData)); PersistentStore_BeginScan( storeData, storeSharedData, &storeScan); while (PersistentStore_GetNext( &storeScan, values, &persistentTid, &persistentSerialNum)) { /* * We are scanning from low to high TID. */ Assert( PersistentStore_IsZeroTid(&storeSharedData->maxTid) || ItemPointerCompare( &storeSharedData->maxTid, &persistentTid) == -1); // Less-Than. storeSharedData->maxTid = persistentTid; PersistentStore_ExtractOurTupleData( storeData, values, &persistentSerialNum, &previousFreeTid); if (Debug_persistent_recovery_print) (*storeData->printTupleCallback)( PersistentRecovery_DebugPrintLevel(), "SCAN", &persistentTid, values); if (!PersistentStore_IsZeroTid(&previousFreeTid)) { /* * Non-zero previousFreeTid implies a free entry. */ if (storeSharedData->maxFreeOrderNum < persistentSerialNum) { storeSharedData->maxFreeOrderNum = persistentSerialNum; storeSharedData->freeTid = persistentTid; } if (!gp_persistent_skip_free_list) { PersistentStore_InitScanAddFreeEntry( &persistentTid, &previousFreeTid, /* freeOrderNum */ persistentSerialNum); } } else { storeSharedData->inUseCount++; if (storeSharedData->maxInUseSerialNum < persistentSerialNum) { storeSharedData->maxInUseSerialNum = persistentSerialNum; storeData->myHighestSerialNum = storeSharedData->maxInUseSerialNum; } } if (storeData->scanTupleCallback != NULL) (*storeData->scanTupleCallback)( &persistentTid, persistentSerialNum, values); } PersistentStore_EndScan(&storeScan); pfree(values); globalSequenceNum = GlobalSequence_Current(storeData->gpGlobalSequence); /* * Note: Originally the below IF STMT was guarded with a InRecovery flag check. * However, this routine should not be called during recovery since the entries are * not consistent... */ Assert(!InRecovery); if (globalSequenceNum < storeSharedData->maxInUseSerialNum) { /* * We seem to have a corruption problem. * * Use the gp_persistent_repair_global_sequence GUC to get the system up. */ if (gp_persistent_repair_global_sequence) { elog(LOG, "Need to Repair global sequence number " INT64_FORMAT " so use scanned maximum value " INT64_FORMAT " ('%s')", globalSequenceNum, storeSharedData->maxInUseSerialNum, storeData->tableName); } else { elog(ERROR, "Global sequence number " INT64_FORMAT " less than maximum value " INT64_FORMAT " found in scan ('%s')", globalSequenceNum, storeSharedData->maxInUseSerialNum, storeData->tableName); } } else { storeSharedData->maxCachedSerialNum = globalSequenceNum; } if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "PersistentStore_DoInitScan ('%s'): maximum in-use serial number " INT64_FORMAT ", maximum free order number " INT64_FORMAT ", free TID %s, maximum known TID %s", storeData->tableName, storeSharedData->maxInUseSerialNum, storeSharedData->maxFreeOrderNum, ItemPointerToString(&storeSharedData->freeTid), ItemPointerToString2(&storeSharedData->maxTid)); if (!gp_persistent_skip_free_list) { PersistentStore_InitScanVerifyFreeEntries( storeData, storeSharedData); } else { if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "PersistentStore_DoInitScan ('%s'): Skipping verification because gp_persistent_skip_free_list GUC is ON", storeData->tableName); } }
static bool PersistentStore_GetFreeTuple( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, ItemPointer freeTid) { Datum *values; HeapTuple tupleCopy; int64 persistentSerialNum; ItemPointerData previousFreeTid; MemSet(freeTid, 0, sizeof(ItemPointerData)); if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_GetFreeTuple: Enter: maximum free order number " INT64_FORMAT ", free TID %s ('%s')", storeSharedData->maxFreeOrderNum, ItemPointerToString(&storeSharedData->freeTid), storeData->tableName); if (storeSharedData->maxFreeOrderNum == 0) { return false; // No free tuples. } if (gp_persistent_skip_free_list) { if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_GetFreeTuple: Skipping because gp_persistent_skip_free_list GUC is ON ('%s')", storeData->tableName); return false; // Pretend no free tuples. } Assert(storeSharedData->freeTid.ip_posid != 0); /* * Read the current last free tuple. */ values = (Datum*)palloc(storeData->numAttributes * sizeof(Datum)); PersistentStore_ReadTuple( storeData, storeSharedData, &storeSharedData->freeTid, values, &tupleCopy); PersistentStore_ExtractOurTupleData( storeData, values, &persistentSerialNum, &previousFreeTid); if (PersistentStore_IsZeroTid(&previousFreeTid)) elog(ERROR, "Expected persistent store tuple at %s to be free ('%s')", ItemPointerToString(&storeSharedData->freeTid), storeData->tableName); if (storeSharedData->maxFreeOrderNum == 1) Assert(ItemPointerCompare(&previousFreeTid, &storeSharedData->freeTid) == 0); if (persistentSerialNum != storeSharedData->maxFreeOrderNum) elog(ERROR, "Expected persistent store tuple at %s to have order number " INT64_FORMAT " (found " INT64_FORMAT ", '%s')", ItemPointerToString(&storeSharedData->freeTid), storeSharedData->maxFreeOrderNum, persistentSerialNum, storeData->tableName); *freeTid = storeSharedData->freeTid; storeSharedData->maxFreeOrderNum--; storeSharedData->freeTid = previousFreeTid; pfree(values); heap_freetuple(tupleCopy); if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_GetFreeTuple: Exit: maximum free order number " INT64_FORMAT ", free TID %s ('%s')", storeSharedData->maxFreeOrderNum, ItemPointerToString(&storeSharedData->freeTid), storeData->tableName); return true; }
void PersistentStore_ReadTuple( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, ItemPointer readTid, Datum *values, HeapTuple *tupleCopy) { Relation persistentRel; HeapTupleData tuple; Buffer buffer; bool *nulls; #ifdef USE_ASSERT_CHECKING if (storeSharedData == NULL || !PersistentStoreSharedData_EyecatcherIsValid(storeSharedData)) elog(ERROR, "Persistent store shared-memory not valid"); #endif if (Debug_persistent_store_print) elog(PersistentStore_DebugPrintLevel(), "PersistentStore_ReadTuple: Going to read tuple at TID %s ('%s', shared data %p)", ItemPointerToString(readTid), storeData->tableName, storeSharedData); if (PersistentStore_IsZeroTid(readTid)) elog(ERROR, "TID for fetch persistent tuple is invalid (0,0) ('%s')", storeData->tableName); // UNDONE: I think the InRecovery test only applies to physical Master Mirroring on Standby. /* Only test this outside of recovery scenarios */ if (!InRecovery && (PersistentStore_IsZeroTid(&storeSharedData->maxTid) || ItemPointerCompare( readTid, &storeSharedData->maxTid) == 1 // Greater-than. )) { elog(ERROR, "TID %s for fetch persistent tuple is greater than the last known TID %s ('%s')", ItemPointerToString(readTid), ItemPointerToString2(&storeSharedData->maxTid), storeData->tableName); } persistentRel = (*storeData->openRel)(); tuple.t_self = *readTid; if (!heap_fetch(persistentRel, SnapshotAny, &tuple, &buffer, false, NULL)) { elog(ERROR, "Failed to fetch persistent tuple at %s (maximum known TID %s, '%s')", ItemPointerToString(&tuple.t_self), ItemPointerToString2(&storeSharedData->maxTid), storeData->tableName); } *tupleCopy = heaptuple_copy_to(&tuple, NULL, NULL); ReleaseBuffer(buffer); /* * In order to keep the tuples the exact same size to enable direct reuse of * free tuples, we do not use NULLs. */ nulls = (bool*)palloc(storeData->numAttributes * sizeof(bool)); heap_deform_tuple(*tupleCopy, persistentRel->rd_att, values, nulls); (*storeData->closeRel)(persistentRel); if (Debug_persistent_store_print) { elog(PersistentStore_DebugPrintLevel(), "PersistentStore_ReadTuple: Successfully read tuple at TID %s ('%s')", ItemPointerToString(readTid), storeData->tableName); (*storeData->printTupleCallback)( PersistentStore_DebugPrintLevel(), "STORE READ TUPLE", readTid, values); } pfree(nulls); }
/* * Rebuild free TID list based on freeEntryHashTable. Returns number * of free tuples in the rebuilt free list. */ uint64 PersistentStore_RebuildFreeList( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData) { Datum *values; PersistentStoreScan storeScan; ItemPointerData persistentTid; ItemPointerData previousFreeTid; ItemPointerData previousTid; uint64 persistentSerialNum; uint64 freeOrderNum; values = (Datum*)palloc(storeData->numAttributes * sizeof(Datum)); /* * PT shared data must be already initialized, even when we are * called during recovery. */ Assert(!PersistentStore_IsZeroTid(&storeSharedData->maxTid)); if (storeSharedData->maxFreeOrderNum < 1) { elog(LOG, "no free tuples in %s, not building any free list", storeData->tableName); return 0; } elog(LOG, "rebuilding free list in %s with " INT64_FORMAT " free tuples", storeData->tableName, storeSharedData->maxFreeOrderNum); /* * Scan PT for free entries (in TID order) and establish links * with previous free entry as we go on. */ previousTid.ip_posid = 0; freeOrderNum = 0; PersistentStore_BeginScan(storeData, storeSharedData, &storeScan); while (PersistentStore_GetNext( &storeScan, values, &persistentTid, (int64 *)&persistentSerialNum)) { /* * We are scanning from low to high TID. All TIDs we * encounter should be smaller or equal to the known * maxTid. */ Assert(ItemPointerCompare( &storeSharedData->maxTid, &persistentTid) >= 0); PersistentStore_ExtractOurTupleData( storeData, values, (int64 *)&persistentSerialNum, &previousFreeTid); if (!PersistentStore_IsZeroTid(&previousFreeTid)) { values[storeData->attNumPersistentSerialNum - 1] = Int64GetDatum(++freeOrderNum); values[storeData->attNumPreviousFreeTid - 1] = ItemPointerIsValid(&previousTid) ? PointerGetDatum(&previousTid) : PointerGetDatum(&persistentTid); #ifdef FAULT_INJECTOR /* * Inject fault after free list is partially built - a few * tuples are updated but at least one is yet to be * updated. */ if (freeOrderNum > 3) { FaultInjector_InjectFaultIfSet( RebuildPTDB, DDLNotSpecified, "", // databaseName ""); // tableName } #endif PersistentStore_UpdateTuple( storeData, storeSharedData, &persistentTid, values, true); ItemPointerCopy(&persistentTid, &previousTid); } } PersistentStore_EndScan(&storeScan); pfree(values); if (ItemPointerIsValid(&previousTid)) { Assert(freeOrderNum > 0); ItemPointerCopy(&previousTid, &storeSharedData->freeTid); storeSharedData->maxFreeOrderNum = freeOrderNum; elog(LOG, "rebuilt free list in %s: maxFreeOrderNum = " INT64_FORMAT " freeTid = %s", storeData->tableName, freeOrderNum, ItemPointerToString(&persistentTid)); } return freeOrderNum; }
/* * Try to walk the free TID chain using free TIDs recorded in * freeEntryHashTable. Return true if the chain is valid, false * otherwise. * * Note: we have already validated that number of free entries is * equal to max free order number. */ static bool PersistentStore_InitScanVerifyFreeEntries( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData) { int64 freeOrderNum; ItemPointerData freeTid; PersistentFreeEntryKey key; PersistentFreeEntry *entry; freeOrderNum = storeSharedData->maxFreeOrderNum; freeTid = storeSharedData->freeTid; elog(LOG, "beginning free list validation for %s, maxFreeOrderNum = " INT64_FORMAT, storeData->tableName, freeOrderNum); if (freeOrderNum == 0) { if (!PersistentStore_IsZeroTid(&freeTid)) { PersistentStore_DiagnoseDumpTable(storeData, storeSharedData); ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("free list validation failed"), errdetail("expected free TID to be (0,0) when " "free order number is 0 in '%s'", storeData->tableName))); return false; } } else { if (freeEntryHashTable == NULL) { PersistentStore_DiagnoseDumpTable(storeData, storeSharedData); ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("free list validation failed"), errdetail("looking for free order number " INT64_FORMAT " and the free entry hash table is empty for '%s'", freeOrderNum, storeData->tableName))); return false; } while (freeOrderNum > 0) { MemSet(&key, 0, sizeof(key)); key.persistentTid = freeTid; entry = (PersistentFreeEntry*) hash_search(freeEntryHashTable, (void *) &key, HASH_FIND, NULL); if (entry == NULL) { PersistentStore_DiagnoseDumpTable(storeData, storeSharedData); ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("free list validation failed"), errdetail( "did not find free entry for free TID %s (" "free order number " INT64_FORMAT ") for '%s'", ItemPointerToString(&freeTid), freeOrderNum, storeData->tableName))); return false; } if (PersistentStore_IsZeroTid(&entry->previousFreeTid)) { PersistentStore_DiagnoseDumpTable(storeData, storeSharedData); ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("free list validation failed"), errdetail( "previous free TID not expected to be (0,0) -- " "persistent Free Entry hashtable corrupted for " "'%s' (expected free order number " INT64_FORMAT ", entry free order number " INT64_FORMAT ")", storeData->tableName, freeOrderNum, entry->freeOrderNum))); return false; } if (freeOrderNum != entry->freeOrderNum) { PersistentStore_DiagnoseDumpTable(storeData, storeSharedData); ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("free list validation failed"), errdetail( "free entry for free TID %s has wrong free " "order number (expected free order number " INT64_FORMAT ", found free order number " INT64_FORMAT ") for '%s'", ItemPointerToString(&freeTid), freeOrderNum, entry->freeOrderNum, storeData->tableName))); return false; } if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "PersistentStore_InitScanVerifyFreeEntries ('%s'): Free order" " number " INT64_FORMAT ", free TID %s, previous free TID %s", storeData->tableName, freeOrderNum, ItemPointerToString(&freeTid), ItemPointerToString2(&entry->previousFreeTid)); freeTid = entry->previousFreeTid; Insist(!PersistentStore_IsZeroTid(&freeTid)); // Note the error check above. if (freeOrderNum == 1) { /* * The last free entry uses its own TID in previous_free_tid. */ if (ItemPointerCompare( &entry->key.persistentTid, &freeTid) != 0) { PersistentStore_DiagnoseDumpTable(storeData, storeSharedData); ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("free list validation failed"), errdetail( "expected previous_free_tid %s to match the" " persistent TID %s for the last free entry" " (free order number 1) for '%s'", ItemPointerToString(&freeTid), ItemPointerToString2(&entry->key.persistentTid), storeData->tableName))); return false; } } entry = NULL; freeOrderNum--; } } if (Debug_persistent_recovery_print) elog(PersistentRecovery_DebugPrintLevel(), "PersistentStore_InitScanVerifyFreeEntries ('%s'): successfully " "verified " INT64_FORMAT " free entries", storeData->tableName, storeSharedData->maxFreeOrderNum); return true; }
/* * Check if the free TID is valid. If not, the free list is corrupted and we * pretend there are no free tuples to reset the free list. The corrupted free * list will be detached and cleaned during recovery or pt rebuild. */ static bool PersistentStore_ValidateFreeTID( PersistentStoreData *storeData, PersistentStoreSharedData *storeSharedData, ItemPointer previousFreeTid) { Datum *values; HeapTuple tupleCopy; int64 persistentSerialNum; bool tidIsValid = true; if (storeSharedData->maxFreeOrderNum <= 0) return true; /* No tuple to check */ values = (Datum*)palloc(storeData->numAttributes * sizeof(Datum)); PersistentStore_ReadTuple( storeData, storeSharedData, &storeSharedData->freeTid, values, &tupleCopy); PersistentStore_ExtractOurTupleData( storeData, values, &persistentSerialNum, previousFreeTid); if (storeSharedData->maxFreeOrderNum == 1 && ItemPointerCompare(previousFreeTid, &storeSharedData->freeTid) != 0) { tidIsValid = false; ereport(WARNING, (errmsg("integrity check for PT freeTid failed"), errdetail("expected to have previous FreeTID at %s equal to itself (found %s, %s)", ItemPointerToString(&storeSharedData->freeTid), ItemPointerToString2(previousFreeTid), storeData->tableName))); } if (PersistentStore_IsZeroTid(previousFreeTid)) { tidIsValid = false; ereport(WARNING, (errmsg("integrity check for PT freeTid failed"), errdetail("expected to have previous FreeTID at %s to be free (found %s, %s)", ItemPointerToString(&storeSharedData->freeTid), ItemPointerToString2(previousFreeTid), storeData->tableName))); } if (persistentSerialNum != storeSharedData->maxFreeOrderNum) { tidIsValid = false; ereport(WARNING, (errmsg("integrity check for PT freeTid failed"), errdetail("expected persistent store tuple at %s to have order number " INT64_FORMAT " (found " INT64_FORMAT ", '%s')", ItemPointerToString(&storeSharedData->freeTid), storeSharedData->maxFreeOrderNum, persistentSerialNum, storeData->tableName))); } pfree(values); heap_freetuple(tupleCopy); /* If the free TID is not valid, switch to a new free list here */ if (!tidIsValid) { ItemPointerSet(previousFreeTid, 0, 0); storeSharedData->maxFreeOrderNum = 0; MemSet(&storeSharedData->freeTid, 0, sizeof(ItemPointerData)); ereport(WARNING, (errmsg("switched to new free TID list"))); } return tidIsValid; }