📄 index.c
字号:
Assert(PointerIsValid(indexRelation->rd_am)); procedure = indexRelation->rd_am->ambuild; Assert(RegProcedureIsValid(procedure)); /* * Call the access method's build procedure */ OidFunctionCall3(procedure, PointerGetDatum(heapRelation), PointerGetDatum(indexRelation), PointerGetDatum(indexInfo));}/* * IndexBuildHeapScan - scan the heap relation to find tuples to be indexed * * This is called back from an access-method-specific index build procedure * after the AM has done whatever setup it needs. The parent heap relation * is scanned to find tuples that should be entered into the index. Each * such tuple is passed to the AM's callback routine, which does the right * things to add it to the new index. After we return, the AM's index * build procedure does whatever cleanup is needed; in particular, it should * close the heap and index relations. * * The total count of heap tuples is returned. This is for updating pg_class * statistics. (It's annoying not to be able to do that here, but we can't * do it until after the relation is closed.) Note that the index AM itself * must keep track of the number of index tuples; we don't do so here because * the AM might reject some of the tuples for its own reasons, such as being * unable to store NULLs. */doubleIndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, IndexBuildCallback callback, void *callback_state){ HeapScanDesc scan; HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; List *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; Snapshot snapshot; TransactionId OldestXmin; /* * sanity checks */ Assert(OidIsValid(indexRelation->rd_rel->relam)); /* * Need an EState for evaluation of index expressions and partial-index * predicates. Also a slot to hold the current tuple. */ estate = CreateExecutorState(); econtext = GetPerTupleExprContext(estate); slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation)); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ predicate = (List *) ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, estate); /* * Ok, begin our scan of the base relation. We use SnapshotAny because we * must retrieve all tuples and do our own time qual checks. */ if (IsBootstrapProcessingMode()) { snapshot = SnapshotNow; OldestXmin = InvalidTransactionId; } else { snapshot = SnapshotAny; OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared); } scan = heap_beginscan(heapRelation, /* relation */ snapshot, /* seeself */ 0, /* number of keys */ NULL); /* scan key */ reltuples = 0; /* * Scan all tuples in the base relation. */ while ((heapTuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { bool tupleIsAlive; CHECK_FOR_INTERRUPTS(); if (snapshot == SnapshotAny) { /* do our own time qual check */ bool indexIt; /* * We could possibly get away with not locking the buffer here, * since caller should hold ShareLock on the relation, but let's * be conservative about it. */ LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin, scan->rs_cbuf)) { case HEAPTUPLE_DEAD: indexIt = false; tupleIsAlive = false; break; case HEAPTUPLE_LIVE: indexIt = true; tupleIsAlive = true; break; case HEAPTUPLE_RECENTLY_DEAD: /* * If tuple is recently deleted then we must index it * anyway to preserve MVCC semantics. (Pre-existing * transactions could try to use the index after we * finish building it, and may need to see such tuples.) */ indexIt = true; tupleIsAlive = false; break; case HEAPTUPLE_INSERT_IN_PROGRESS: /* * Since caller should hold ShareLock or better, we should * not see any tuples inserted by open transactions --- * unless it's our own transaction. (Consider INSERT * followed by CREATE INDEX within a transaction.) An * exception occurs when reindexing a system catalog, * because we often release lock on system catalogs before * committing. */ if (!TransactionIdIsCurrentTransactionId( HeapTupleHeaderGetXmin(heapTuple->t_data)) && !IsSystemRelation(heapRelation)) elog(ERROR, "concurrent insert in progress"); indexIt = true; tupleIsAlive = true; break; case HEAPTUPLE_DELETE_IN_PROGRESS: /* * Since caller should hold ShareLock or better, we should * not see any tuples deleted by open transactions --- * unless it's our own transaction. (Consider DELETE * followed by CREATE INDEX within a transaction.) An * exception occurs when reindexing a system catalog, * because we often release lock on system catalogs before * committing. */ Assert(!(heapTuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)); if (!TransactionIdIsCurrentTransactionId( HeapTupleHeaderGetXmax(heapTuple->t_data)) && !IsSystemRelation(heapRelation)) elog(ERROR, "concurrent delete in progress"); indexIt = true; tupleIsAlive = false; break; default: elog(ERROR, "unexpected HeapTupleSatisfiesVacuum result"); indexIt = tupleIsAlive = false; /* keep compiler quiet */ break; } LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); if (!indexIt) continue; } else { /* heap_getnext did the time qual check */ tupleIsAlive = true; } reltuples += 1; MemoryContextReset(econtext->ecxt_per_tuple_memory); /* Set up for predicate or expression evaluation */ ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* * In a partial index, discard tuples that don't satisfy the * predicate. */ if (predicate != NIL) { if (!ExecQual(predicate, econtext, false)) continue; } /* * For the current heap tuple, extract all the attributes we use in * this index, and note which are null. This also performs evaluation * of any expressions needed. */ FormIndexDatum(indexInfo, slot, estate, values, isnull); /* * You'd think we should go ahead and build the index tuple here, but * some index AMs want to do further processing on the data first. So * pass the values[] and isnull[] arrays, instead. */ /* Call the AM's callback routine to process the tuple */ callback(indexRelation, heapTuple, values, isnull, tupleIsAlive, callback_state); } heap_endscan(scan); ExecDropSingleTupleTableSlot(slot); FreeExecutorState(estate); /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_PredicateState = NIL; return reltuples;}/* * IndexGetRelation: given an index's relation OID, get the OID of the * relation it is an index on. Uses the system cache. */static OidIndexGetRelation(Oid indexId){ HeapTuple tuple; Form_pg_index index; Oid result; tuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexId), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for index %u", indexId); index = (Form_pg_index) GETSTRUCT(tuple); Assert(index->indexrelid == indexId); result = index->indrelid; ReleaseSysCache(tuple); return result;}/* * reindex_index - This routine is used to recreate a single index */voidreindex_index(Oid indexId){ Relation iRel, heapRelation; Oid heapId; bool inplace; /* * Open and lock the parent heap relation. ShareLock is sufficient since * we only need to be sure no schema or data changes are going on. */ heapId = IndexGetRelation(indexId); heapRelation = heap_open(heapId, ShareLock); /* * Open the target index relation and get an exclusive lock on it, to * ensure that no one else is touching this particular index. */ iRel = index_open(indexId); LockRelation(iRel, AccessExclusiveLock); /* * If it's a shared index, we must do inplace processing (because we have * no way to update relfilenode in other databases). Otherwise we can do * it the normal transaction-safe way. * * Since inplace processing isn't crash-safe, we only allow it in a * standalone backend. (In the REINDEX TABLE and REINDEX DATABASE cases, * the caller should have detected this.) */ inplace = iRel->rd_rel->relisshared; if (inplace && IsUnderPostmaster) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("shared index \"%s\" can only be reindexed in stand-alone mode", RelationGetRelationName(iRel)))); PG_TRY(); { IndexInfo *indexInfo; /* Suppress use of the target index while rebuilding it */ SetReindexProcessing(heapId, indexId); /* Fetch info needed for index_build */ indexInfo = BuildIndexInfo(iRel); if (inplace) { /* Truncate the actual file (and discard buffers) */ RelationTruncate(iRel, 0); } else { /* * We'll build a new physical relation for the index. */ setNewRelfilenode(iRel); } /* Initialize the index and rebuild */ index_build(heapRelation, iRel, indexInfo); /* * index_build will close both the heap and index relations (but not * give up the locks we hold on them). So we're done. */ } PG_CATCH(); { /* Make sure flag gets cleared on error exit */ ResetReindexProcessing(); PG_RE_THROW(); } PG_END_TRY(); ResetReindexProcessing();}/* * reindex_relation - This routine is used to recreate all indexes * of a relation (and optionally its toast relation too, if any). * * Returns true if any indexes were rebuilt. Note that a * CommandCounterIncrement will occur after each index rebuild. */boolreindex_relation(Oid relid, bool toast_too){ Relation rel; Oid toast_relid; bool is_pg_class; bool result; List *indexIds, *doneIndexes; ListCell *indexId; /* * Open and lock the relation. ShareLock is sufficient since we only need * to prevent schema and data changes in it. */ rel = heap_open(relid, ShareLock); toast_relid = rel->rd_rel->reltoastrelid; /* * Get the list of index OIDs for this relation. (We trust to the * relcache to get this with a sequential scan if ignoring system * indexes.) */ indexIds = RelationGetIndexList(rel); /* * reindex_index will attempt to update the pg_class rows for the relation * and index. If we are processing pg_class itself, we want to make sure * that the updates do not try to insert index entries into indexes we * have not processed yet. (When we are trying to recover from corrupted * indexes, that could easily cause a crash.) We can accomplish this * because CatalogUpdateIndexes will use the relcache's index list to know * which indexes to update. We just force the index list to be only the * stuff we've processed. * * It is okay to not insert entries into the indexes we have not processed * yet because all of this is transaction-safe. If we fail partway * through, the updated rows are dead and it doesn't matter whether they * have index entries. Also, a new pg_class index will be created with an * entry for its own pg_class row because we do setNewRelfilenode() before * we do index_build(). * * Note that we also clear pg_class's rd_oidindex until the loop is done, * so that that index can't be accessed either. This means we cannot * safely generate new relation OIDs while in the loop; shouldn't be a * problem. */ is_pg_class = (RelationGetRelid(rel) == RelationRelationId); doneIndexes = NIL; /* Reindex all the indexes. */ foreach(indexId, indexIds) { Oid indexOid = lfirst_oid(indexId); if (is_pg_class) RelationSetIndexList(rel, doneIndexes, InvalidOid); reindex_index(indexOid); CommandCounterIncrement(); if (is_pg_class) doneIndexes = lappend_oid(doneIndexes, indexOid); } if (is_pg_class) RelationSetIndexList(rel, indexIds, ClassOidIndexId); /* * Close rel, but continue to hold the lock. */ heap_close(rel, NoLock); result = (indexIds != NIL); /* * If the relation has a secondary toast rel, reindex that too while we * still hold the lock on the master table. */ if (toast_too && OidIsValid(toast_relid)) result |= reindex_relation(toast_relid, false); return result;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -