📄 index.c
字号:
ii->ii_ExpressionsState = NIL; /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); ii->ii_PredicateState = NIL; /* other info */ ii->ii_Unique = indexStruct->indisunique; return ii;}/* ---------------- * FormIndexDatum * Construct values[] and isnull[] arrays for a new index tuple. * * indexInfo Info about the index * slot Heap tuple for which we must prepare an index entry * estate executor state for evaluating any index expressions * values Array of index Datums (output area) * isnull Array of is-null indicators (output area) * * When there are no index expressions, estate may be NULL. Otherwise it * must be supplied, *and* the ecxt_scantuple slot of its per-tuple expr * context must point to the heap tuple passed in. * * Notice we don't actually call index_form_tuple() here; we just prepare * its input arrays values[] and isnull[]. This is because the index AM * may wish to alter the data before storage. * ---------------- */voidFormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull){ ListCell *indexpr_item; int i; if (indexInfo->ii_Expressions != NIL && indexInfo->ii_ExpressionsState == NIL) { /* First time through, set up expression evaluation state */ indexInfo->ii_ExpressionsState = (List *) ExecPrepareExpr((Expr *) indexInfo->ii_Expressions, estate); /* Check caller has set up context correctly */ Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot); } indexpr_item = list_head(indexInfo->ii_ExpressionsState); for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { int keycol = indexInfo->ii_KeyAttrNumbers[i]; Datum iDatum; bool isNull; if (keycol != 0) { /* * Plain index column; get the value we need directly from the * heap tuple. */ iDatum = slot_getattr(slot, keycol, &isNull); } else { /* * Index expression --- need to evaluate it. */ if (indexpr_item == NULL) elog(ERROR, "wrong number of index expressions"); iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item), GetPerTupleExprContext(estate), &isNull, NULL); indexpr_item = lnext(indexpr_item); } values[i] = iDatum; isnull[i] = isNull; } if (indexpr_item != NULL) elog(ERROR, "wrong number of index expressions");}/* ---------------- * set relhasindex of relation's pg_class entry * * If isprimary is TRUE, we are defining a primary index, so also set * relhaspkey to TRUE. Otherwise, leave relhaspkey alone. * * If reltoastidxid is not InvalidOid, also set reltoastidxid to that value. * This is only used for TOAST relations. * * NOTE: an important side-effect of this operation is that an SI invalidation * message is sent out to all backends --- including me --- causing relcache * entries to be flushed or updated with the new hasindex data. This must * happen even if we find that no change is needed in the pg_class row. * ---------------- */voidsetRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid){ Relation pg_class; HeapTuple tuple; Form_pg_class classtuple; bool dirty = false; HeapScanDesc pg_class_scan = NULL; /* * Find the tuple to update in pg_class. In bootstrap mode we can't use * heap_update, so cheat and overwrite the tuple in-place. In normal * processing, make a copy to scribble on. */ pg_class = heap_open(RelationRelationId, RowExclusiveLock); if (!IsBootstrapProcessingMode()) { tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); } else { ScanKeyData key[1]; ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key); tuple = heap_getnext(pg_class_scan, ForwardScanDirection); } if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", relid); classtuple = (Form_pg_class) GETSTRUCT(tuple); /* Apply required updates */ if (pg_class_scan) LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); if (classtuple->relhasindex != hasindex) { classtuple->relhasindex = hasindex; dirty = true; } if (isprimary) { if (!classtuple->relhaspkey) { classtuple->relhaspkey = true; dirty = true; } } if (OidIsValid(reltoastidxid)) { Assert(classtuple->relkind == RELKIND_TOASTVALUE); if (classtuple->reltoastidxid != reltoastidxid) { classtuple->reltoastidxid = reltoastidxid; dirty = true; } } if (pg_class_scan) LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK); if (pg_class_scan) { /* Write the modified tuple in-place */ WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); /* Send out shared cache inval if necessary */ if (!IsBootstrapProcessingMode()) CacheInvalidateHeapTuple(pg_class, tuple); } else if (dirty) { simple_heap_update(pg_class, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ CatalogUpdateIndexes(pg_class, tuple); } else { /* no need to change tuple, but force relcache rebuild anyway */ CacheInvalidateRelcacheByTuple(tuple); } if (!pg_class_scan) heap_freetuple(tuple); else heap_endscan(pg_class_scan); heap_close(pg_class, RowExclusiveLock);}/* * setNewRelfilenode - assign a new relfilenode value to the relation * * Caller must already hold exclusive lock on the relation. */voidsetNewRelfilenode(Relation relation){ Oid newrelfilenode; RelFileNode newrnode; SMgrRelation srel; Relation pg_class; HeapTuple tuple; Form_pg_class rd_rel; /* Can't change relfilenode for nailed tables (indexes ok though) */ Assert(!relation->rd_isnailed || relation->rd_rel->relkind == RELKIND_INDEX); /* Can't change for shared tables or indexes */ Assert(!relation->rd_rel->relisshared); /* Allocate a new relfilenode */ newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, relation->rd_rel->relisshared, NULL); /* * Find the pg_class tuple for the given relation. This is not used * during bootstrap, so okay to use heap_update always. */ pg_class = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(RelationGetRelid(relation)), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", RelationGetRelid(relation)); rd_rel = (Form_pg_class) GETSTRUCT(tuple); /* create another storage file. Is it a little ugly ? */ /* NOTE: any conflict in relfilenode value will be caught here */ newrnode = relation->rd_node; newrnode.relNode = newrelfilenode; srel = smgropen(newrnode); smgrcreate(srel, relation->rd_istemp, false); smgrclose(srel); /* schedule unlinking old relfilenode */ RelationOpenSmgr(relation); smgrscheduleunlink(relation->rd_smgr, relation->rd_istemp); /* update the pg_class row */ rd_rel->relfilenode = newrelfilenode; rd_rel->relpages = 0; /* it's empty until further notice */ rd_rel->reltuples = 0; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); /* Make sure the relfilenode change is visible */ CommandCounterIncrement();}/* * This is invoked by the various index AMs once they have finished * constructing an index. Constructing an index involves counting the * number of tuples in both the relation and the index, so we take * advantage of the opportunity to update pg_class to ensure that the * planner takes advantage of the index we just created. But, only * update statistics during normal index definitions, not for indices * on system catalogs created during bootstrap processing. We must * close the relations before updating statistics to guarantee that * the relcache entries are flushed when we increment the command * counter in UpdateStats(). But we do not release any locks on the * relations; those will be held until end of transaction. */voidIndexCloseAndUpdateStats(Relation heap, double heapTuples, Relation index, double indexTuples){ Oid hrelid = RelationGetRelid(heap); Oid irelid = RelationGetRelid(index); if (!IsNormalProcessingMode()) return; heap_close(heap, NoLock); index_close(index); UpdateStats(hrelid, heapTuples); UpdateStats(irelid, indexTuples);}/* ---------------- * UpdateStats * * Update pg_class' relpages and reltuples statistics for the given relation * (which can be either a table or an index). Note that this is not used * in the context of VACUUM, only CREATE INDEX. * ---------------- */static voidUpdateStats(Oid relid, double reltuples){ Relation whichRel; Relation pg_class; HeapTuple tuple; BlockNumber relpages; Form_pg_class rd_rel; HeapScanDesc pg_class_scan = NULL; bool in_place_upd; /* * This routine handles updates for both the heap and index relation * statistics. In order to guarantee that we're able to *see* the index * relation tuple, we bump the command counter id here. The index * relation tuple was created in the current transaction. */ CommandCounterIncrement(); /* * CommandCounterIncrement() flushes invalid cache entries, including * those for the heap and index relations for which we're updating * statistics. Now that the cache is flushed, it's safe to open the * relation again. We need the relation open in order to figure out how * many blocks it contains. */ /* * Grabbing lock here is probably redundant ... */ whichRel = relation_open(relid, ShareLock); /* * Find the tuple to update in pg_class. Normally we make a copy of the * tuple using the syscache, modify it, and apply heap_update. But in * bootstrap mode we can't use heap_update, so we cheat and overwrite the * tuple in-place. (Note: as of PG 8.0 this isn't called during * bootstrap, but leave the code here for possible future use.) * * We also must cheat if reindexing pg_class itself, because the target * index may presently not be part of the set of indexes that * CatalogUpdateIndexes would update (see reindex_relation). In this case * the stats updates will not be WAL-logged and so could be lost in a * crash. This seems OK considering VACUUM does the same thing. */ pg_class = heap_open(RelationRelationId, RowExclusiveLock); in_place_upd = IsBootstrapProcessingMode() || ReindexIsProcessingHeap(RelationRelationId); if (!in_place_upd) { tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); } else { ScanKeyData key[1]; ScanKeyInit(&key[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key); tuple = heap_getnext(pg_class_scan, ForwardScanDirection); } if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", relid); rd_rel = (Form_pg_class) GETSTRUCT(tuple); /* * Update statistics in pg_class, if they changed. (Avoiding an * unnecessary update is not just a tiny performance improvement; it also * reduces the window wherein concurrent CREATE INDEX commands may * conflict.) */ relpages = RelationGetNumberOfBlocks(whichRel); if (rd_rel->relpages != (int32) relpages || rd_rel->reltuples != (float4) reltuples) { if (in_place_upd) { /* Bootstrap or reindex case: overwrite fields in place. */ LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); rd_rel->relpages = (int32) relpages; rd_rel->reltuples = (float4) reltuples; LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK); WriteNoReleaseBuffer(pg_class_scan->rs_cbuf); if (!IsBootstrapProcessingMode()) CacheInvalidateHeapTuple(pg_class, tuple); } else { /* During normal processing, must work harder. */ rd_rel->relpages = (int32) relpages; rd_rel->reltuples = (float4) reltuples; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); } } if (in_place_upd) heap_endscan(pg_class_scan); else heap_freetuple(tuple); /* * We shouldn't have to do this, but we do... Modify the reldesc in place * with the new values so that the cache contains the latest copy. (XXX * is this really still necessary? The relcache will get fixed at next * CommandCounterIncrement, so why bother here?) */ whichRel->rd_rel->relpages = (int32) relpages; whichRel->rd_rel->reltuples = (float4) reltuples; heap_close(pg_class, RowExclusiveLock); relation_close(whichRel, NoLock);}/* * index_build - invoke access-method-specific index build procedure */voidindex_build(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo){ RegProcedure procedure; /* * sanity checks */ Assert(RelationIsValid(indexRelation));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -