/* * Evaluates a list of parameters, using the given executor state. It * requires a list of the parameter values themselves, and a list of * their types. It returns a filled-in ParamListInfo -- this can later * be passed to CreateQueryDesc(), which allows the executor to make use * of the parameters during query execution. */ static ParamListInfo EvaluateParams(EState *estate, List *params, List *argtypes) { int nargs = length(argtypes); ParamListInfo paramLI; List *exprstates; List *l; int i = 0; /* Parser should have caught this error, but check for safety */ if (length(params) != nargs) elog(ERROR, "wrong number of arguments"); exprstates = (List *) ExecPrepareExpr((Expr *) params, estate); paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData)); foreach(l, exprstates) { ExprState *n = lfirst(l); bool isNull; paramLI[i].value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), &isNull, NULL); paramLI[i].kind = PARAM_NUM; paramLI[i].id = i + 1; paramLI[i].isnull = isNull; i++; }
/* ---------------- * FormIndexDatum * Construct Datum[] and nullv[] arrays for a new index tuple. * * indexInfo Info about the index * heapTuple Heap tuple for which we must prepare an index entry * heapDescriptor tupledesc for heap tuple * estate executor state for evaluating any index expressions * datum Array of index Datums (output area) * nullv 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. * * For largely historical reasons, we don't actually call index_formtuple() * here, we just prepare its input arrays datum[] and nullv[]. * ---------------- */ void FormIndexDatum(IndexInfo *indexInfo, HeapTuple heapTuple, TupleDesc heapDescriptor, EState *estate, Datum *datum, char *nullv) { List *indexprs; 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->val == heapTuple); } indexprs = 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 = heap_getattr(heapTuple, keycol, heapDescriptor, &isNull); } else { /* * Index expression --- need to evaluate it. */ if (indexprs == NIL) elog(ERROR, "wrong number of index expressions"); iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexprs), GetPerTupleExprContext(estate), &isNull, NULL); indexprs = lnext(indexprs); } datum[i] = iDatum; nullv[i] = (isNull) ? 'n' : ' '; } if (indexprs != NIL) elog(ERROR, "wrong number of index expressions"); }
/* * Evaluates a list of parameters, using the given executor state. It * requires a list of the parameter expressions themselves, and a list of * their types. It returns a filled-in ParamListInfo -- this can later * be passed to CreateQueryDesc(), which allows the executor to make use * of the parameters during query execution. */ static ParamListInfo EvaluateParams(EState *estate, List *params, List *argtypes) { int nargs = list_length(argtypes); ParamListInfo paramLI; List *exprstates; ListCell *le, *la; int i = 0; /* Parser should have caught this error, but check for safety */ if (list_length(params) != nargs) elog(ERROR, "wrong number of arguments"); if (nargs == 0) return NULL; exprstates = (List *) ExecPrepareExpr((Expr *) params, estate); /* sizeof(ParamListInfoData) includes the first array element */ paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) + (nargs - 1) *sizeof(ParamExternData)); paramLI->numParams = nargs; forboth(le, exprstates, la, argtypes) { ExprState *n = lfirst(le); ParamExternData *prm = ¶mLI->params[i]; prm->ptype = lfirst_oid(la); prm->pflags = 0; prm->value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), &prm->isnull, NULL); i++; }
/* * IndexSpoolInsert - * * Copy from ExecInsertIndexTuples. */ static void IndexSpoolInsert(BTSpool **spools, TupleTableSlot *slot, ItemPointer tupleid, EState *estate, bool reindex) { ResultRelInfo *relinfo; int i; int numIndices; RelationPtr indices; IndexInfo **indexInfoArray; Relation heapRelation; ExprContext *econtext; /* * Get information from the result relation relinfo structure. */ relinfo = estate->es_result_relation_info; numIndices = relinfo->ri_NumIndices; indices = relinfo->ri_IndexRelationDescs; indexInfoArray = relinfo->ri_IndexRelationInfo; heapRelation = relinfo->ri_RelationDesc; /* * We will use the EState's per-tuple context for evaluating predicates * and index expressions (creating it if it's not already there). */ econtext = GetPerTupleExprContext(estate); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; for (i = 0; i < numIndices; i++) { Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; IndexInfo *indexInfo; if (indices[i] == NULL) continue; /* Skip non-btree indexes on reindex mode. */ if (reindex && spools != NULL && spools[i] == NULL) continue; indexInfo = indexInfoArray[i]; /* If the index is marked as read-only, ignore it */ if (!indexInfo->ii_ReadyForInserts) continue; /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { List *predicate; /* * If predicate state not set up yet, create it (in the estate's * per-query context) */ predicate = indexInfo->ii_PredicateState; if (predicate == NIL) { predicate = (List *) ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, estate); indexInfo->ii_PredicateState = predicate; } /* Skip this index-update if the predicate isn'loader satisfied */ if (!ExecQual(predicate, econtext, false)) continue; } FormIndexDatum(indexInfo, slot, estate, values, isnull); /* * Insert or spool the tuple. */ if (spools != NULL && spools[i] != NULL) { IndexTuple itup = index_form_tuple(RelationGetDescr(indices[i]), values, isnull); itup->t_tid = *tupleid; _bt_spool(itup, spools[i]); pfree(itup); } else { /* Insert one by one */ index_insert(indices[i], values, isnull, tupleid, heapRelation, indices[i]->rd_index->indisunique); } } }
/* * 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. */ double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo, IndexBuildCallback callback, void *callback_state) { HeapScanDesc scan; HeapTuple heapTuple; TupleDesc heapDescriptor; Datum attdata[INDEX_MAX_KEYS]; char nulls[INDEX_MAX_KEYS]; double reltuples; List *predicate; TupleTable tupleTable; TupleTableSlot *slot; EState *estate; ExprContext *econtext; Snapshot snapshot; TransactionId OldestXmin; /* * sanity checks */ Assert(OidIsValid(indexRelation->rd_rel->relam)); heapDescriptor = RelationGetDescr(heapRelation); /* * Need an EState for evaluation of index expressions and * partial-index predicates. */ estate = CreateExecutorState(); econtext = GetPerTupleExprContext(estate); /* * If this is a predicate (partial) index, we will need to evaluate * the predicate using ExecQual, which requires the current tuple to * be in a slot of a TupleTable. Likewise if there are any * expressions. */ if (indexInfo->ii_Predicate != NIL || indexInfo->ii_Expressions != NIL) { tupleTable = ExecCreateTupleTable(1); slot = ExecAllocTableSlot(tupleTable); ExecSetSlotDescriptor(slot, heapDescriptor, false); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ predicate = (List *) ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, estate); } else { tupleTable = NULL; slot = NULL; predicate = NIL; } /* * 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 */ (ScanKey) 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; uint16 sv_infomask; /* * HeapTupleSatisfiesVacuum may update tuple's hint status * bits. 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); sv_infomask = heapTuple->t_data->t_infomask; switch (HeapTupleSatisfiesVacuum(heapTuple->t_data, OldestXmin)) { 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 keep VACUUM from complaining. */ 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. */ 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; } /* check for hint-bit update by HeapTupleSatisfiesVacuum */ if (sv_infomask != heapTuple->t_data->t_infomask) SetBufferCommitInfoNeedsSave(scan->rs_cbuf); 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 */ if (slot) ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* * In a partial index, discard tuples that don't satisfy the * predicate. We can also discard recently-dead tuples, since * VACUUM doesn't complain about tuple count mismatch for partial * indexes. */ if (predicate != NIL) { if (!tupleIsAlive) continue; 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, heapTuple, heapDescriptor, estate, attdata, nulls); /* * 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 attdata and nulls arrays, instead. */ /* Call the AM's callback routine to process the tuple */ callback(indexRelation, heapTuple, attdata, nulls, tupleIsAlive, callback_state); } heap_endscan(scan); if (tupleTable) ExecDropTupleTable(tupleTable, true); FreeExecutorState(estate); /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_PredicateState = NIL; return reltuples; }