diff options
Diffstat (limited to 'src/backend/executor/execUtils.c')
-rw-r--r-- | src/backend/executor/execUtils.c | 534 |
1 files changed, 2 insertions, 532 deletions
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 0736d2a3102..88ba16bc6da 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -28,10 +28,6 @@ * ExecOpenScanRelation Common code for scan node init routines. * ExecCloseScanRelation * - * ExecOpenIndices \ - * ExecCloseIndices | referenced by InitPlan, EndPlan, - * ExecInsertIndexTuples / ExecInsert, ExecUpdate - * * RegisterExprContextCallback Register function shutdown callback * UnregisterExprContextCallback Deregister function shutdown callback * @@ -44,19 +40,14 @@ #include "access/relscan.h" #include "access/transam.h" -#include "catalog/index.h" -#include "executor/execdebug.h" +#include "executor/executor.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" -#include "storage/lmgr.h" #include "utils/memutils.h" -#include "utils/tqual.h" +#include "utils/rel.h" static bool get_last_attnums(Node *node, ProjectionInfo *projInfo); -static bool index_recheck_constraint(Relation index, Oid *constr_procs, - Datum *existing_values, bool *existing_isnull, - Datum *new_values); static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -870,527 +861,6 @@ ExecCloseScanRelation(Relation scanrel) heap_close(scanrel, NoLock); } - -/* ---------------------------------------------------------------- - * ExecInsertIndexTuples support - * ---------------------------------------------------------------- - */ - -/* ---------------------------------------------------------------- - * ExecOpenIndices - * - * Find the indices associated with a result relation, open them, - * and save information about them in the result ResultRelInfo. - * - * At entry, caller has already opened and locked - * resultRelInfo->ri_RelationDesc. - * ---------------------------------------------------------------- - */ -void -ExecOpenIndices(ResultRelInfo *resultRelInfo) -{ - Relation resultRelation = resultRelInfo->ri_RelationDesc; - List *indexoidlist; - ListCell *l; - int len, - i; - RelationPtr relationDescs; - IndexInfo **indexInfoArray; - - resultRelInfo->ri_NumIndices = 0; - - /* fast path if no indexes */ - if (!RelationGetForm(resultRelation)->relhasindex) - return; - - /* - * Get cached list of index OIDs - */ - indexoidlist = RelationGetIndexList(resultRelation); - len = list_length(indexoidlist); - if (len == 0) - return; - - /* - * allocate space for result arrays - */ - relationDescs = (RelationPtr) palloc(len * sizeof(Relation)); - indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *)); - - resultRelInfo->ri_NumIndices = len; - resultRelInfo->ri_IndexRelationDescs = relationDescs; - resultRelInfo->ri_IndexRelationInfo = indexInfoArray; - - /* - * For each index, open the index relation and save pg_index info. We - * acquire RowExclusiveLock, signifying we will update the index. - * - * Note: we do this even if the index is not IndexIsReady; it's not worth - * the trouble to optimize for the case where it isn't. - */ - i = 0; - foreach(l, indexoidlist) - { - Oid indexOid = lfirst_oid(l); - Relation indexDesc; - IndexInfo *ii; - - indexDesc = index_open(indexOid, RowExclusiveLock); - - /* extract index key information from the index's pg_index info */ - ii = BuildIndexInfo(indexDesc); - - relationDescs[i] = indexDesc; - indexInfoArray[i] = ii; - i++; - } - - list_free(indexoidlist); -} - -/* ---------------------------------------------------------------- - * ExecCloseIndices - * - * Close the index relations stored in resultRelInfo - * ---------------------------------------------------------------- - */ -void -ExecCloseIndices(ResultRelInfo *resultRelInfo) -{ - int i; - int numIndices; - RelationPtr indexDescs; - - numIndices = resultRelInfo->ri_NumIndices; - indexDescs = resultRelInfo->ri_IndexRelationDescs; - - for (i = 0; i < numIndices; i++) - { - if (indexDescs[i] == NULL) - continue; /* shouldn't happen? */ - - /* Drop lock acquired by ExecOpenIndices */ - index_close(indexDescs[i], RowExclusiveLock); - } - - /* - * XXX should free indexInfo array here too? Currently we assume that - * such stuff will be cleaned up automatically in FreeExecutorState. - */ -} - -/* ---------------------------------------------------------------- - * ExecInsertIndexTuples - * - * This routine takes care of inserting index tuples - * into all the relations indexing the result relation - * when a heap tuple is inserted into the result relation. - * Much of this code should be moved into the genam - * stuff as it only exists here because the genam stuff - * doesn't provide the functionality needed by the - * executor.. -cim 9/27/89 - * - * This returns a list of index OIDs for any unique or exclusion - * constraints that are deferred and that had - * potential (unconfirmed) conflicts. - * - * CAUTION: this must not be called for a HOT update. - * We can't defend against that here for lack of info. - * Should we change the API to make it safer? - * ---------------------------------------------------------------- - */ -List * -ExecInsertIndexTuples(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate) -{ - List *result = NIL; - ResultRelInfo *resultRelInfo; - int i; - int numIndices; - RelationPtr relationDescs; - Relation heapRelation; - IndexInfo **indexInfoArray; - ExprContext *econtext; - Datum values[INDEX_MAX_KEYS]; - bool isnull[INDEX_MAX_KEYS]; - - /* - * Get information from the result relation info structure. - */ - resultRelInfo = estate->es_result_relation_info; - numIndices = resultRelInfo->ri_NumIndices; - relationDescs = resultRelInfo->ri_IndexRelationDescs; - indexInfoArray = resultRelInfo->ri_IndexRelationInfo; - heapRelation = resultRelInfo->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 each index, form and insert the index tuple - */ - for (i = 0; i < numIndices; i++) - { - Relation indexRelation = relationDescs[i]; - IndexInfo *indexInfo; - IndexUniqueCheck checkUnique; - bool satisfiesConstraint; - - if (indexRelation == 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't satisfied */ - if (!ExecQual(predicate, econtext, false)) - continue; - } - - /* - * FormIndexDatum fills in its values and isnull parameters with the - * appropriate values for the column(s) of the index. - */ - FormIndexDatum(indexInfo, - slot, - estate, - values, - isnull); - - /* - * The index AM does the actual insertion, plus uniqueness checking. - * - * For an immediate-mode unique index, we just tell the index AM to - * throw error if not unique. - * - * For a deferrable unique index, we tell the index AM to just detect - * possible non-uniqueness, and we add the index OID to the result - * list if further checking is needed. - */ - if (!indexRelation->rd_index->indisunique) - checkUnique = UNIQUE_CHECK_NO; - else if (indexRelation->rd_index->indimmediate) - checkUnique = UNIQUE_CHECK_YES; - else - checkUnique = UNIQUE_CHECK_PARTIAL; - - satisfiesConstraint = - index_insert(indexRelation, /* index relation */ - values, /* array of index Datums */ - isnull, /* null flags */ - tupleid, /* tid of heap tuple */ - heapRelation, /* heap relation */ - checkUnique); /* type of uniqueness check to do */ - - /* - * If the index has an associated exclusion constraint, check that. - * This is simpler than the process for uniqueness checks since we - * always insert first and then check. If the constraint is deferred, - * we check now anyway, but don't throw error on violation; instead - * we'll queue a recheck event. - * - * An index for an exclusion constraint can't also be UNIQUE (not an - * essential property, we just don't allow it in the grammar), so no - * need to preserve the prior state of satisfiesConstraint. - */ - if (indexInfo->ii_ExclusionOps != NULL) - { - bool errorOK = !indexRelation->rd_index->indimmediate; - - satisfiesConstraint = - check_exclusion_constraint(heapRelation, - indexRelation, indexInfo, - tupleid, values, isnull, - estate, false, errorOK); - } - - if ((checkUnique == UNIQUE_CHECK_PARTIAL || - indexInfo->ii_ExclusionOps != NULL) && - !satisfiesConstraint) - { - /* - * The tuple potentially violates the uniqueness or exclusion - * constraint, so make a note of the index so that we can re-check - * it later. - */ - result = lappend_oid(result, RelationGetRelid(indexRelation)); - } - } - - return result; -} - -/* - * Check for violation of an exclusion constraint - * - * heap: the table containing the new tuple - * index: the index supporting the exclusion constraint - * indexInfo: info about the index, including the exclusion properties - * tupleid: heap TID of the new tuple we have just inserted - * values, isnull: the *index* column values computed for the new tuple - * estate: an EState we can do evaluation in - * newIndex: if true, we are trying to build a new index (this affects - * only the wording of error messages) - * errorOK: if true, don't throw error for violation - * - * Returns true if OK, false if actual or potential violation - * - * When errorOK is true, we report violation without waiting to see if any - * concurrent transaction has committed or not; so the violation is only - * potential, and the caller must recheck sometime later. This behavior - * is convenient for deferred exclusion checks; we need not bother queuing - * a deferred event if there is definitely no conflict at insertion time. - * - * When errorOK is false, we'll throw error on violation, so a false result - * is impossible. - */ -bool -check_exclusion_constraint(Relation heap, Relation index, IndexInfo *indexInfo, - ItemPointer tupleid, Datum *values, bool *isnull, - EState *estate, bool newIndex, bool errorOK) -{ - Oid *constr_procs = indexInfo->ii_ExclusionProcs; - uint16 *constr_strats = indexInfo->ii_ExclusionStrats; - Oid *index_collations = index->rd_indcollation; - int index_natts = index->rd_index->indnatts; - IndexScanDesc index_scan; - HeapTuple tup; - ScanKeyData scankeys[INDEX_MAX_KEYS]; - SnapshotData DirtySnapshot; - int i; - bool conflict; - bool found_self; - ExprContext *econtext; - TupleTableSlot *existing_slot; - TupleTableSlot *save_scantuple; - - /* - * If any of the input values are NULL, the constraint check is assumed to - * pass (i.e., we assume the operators are strict). - */ - for (i = 0; i < index_natts; i++) - { - if (isnull[i]) - return true; - } - - /* - * Search the tuples that are in the index for any violations, including - * tuples that aren't visible yet. - */ - InitDirtySnapshot(DirtySnapshot); - - for (i = 0; i < index_natts; i++) - { - ScanKeyEntryInitialize(&scankeys[i], - 0, - i + 1, - constr_strats[i], - InvalidOid, - index_collations[i], - constr_procs[i], - values[i]); - } - - /* - * Need a TupleTableSlot to put existing tuples in. - * - * To use FormIndexDatum, we have to make the econtext's scantuple point - * to this slot. Be sure to save and restore caller's value for - * scantuple. - */ - existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap)); - - econtext = GetPerTupleExprContext(estate); - save_scantuple = econtext->ecxt_scantuple; - econtext->ecxt_scantuple = existing_slot; - - /* - * May have to restart scan from this point if a potential conflict is - * found. - */ -retry: - conflict = false; - found_self = false; - index_scan = index_beginscan(heap, index, &DirtySnapshot, index_natts, 0); - index_rescan(index_scan, scankeys, index_natts, NULL, 0); - - while ((tup = index_getnext(index_scan, - ForwardScanDirection)) != NULL) - { - TransactionId xwait; - ItemPointerData ctid_wait; - Datum existing_values[INDEX_MAX_KEYS]; - bool existing_isnull[INDEX_MAX_KEYS]; - char *error_new; - char *error_existing; - - /* - * Ignore the entry for the tuple we're trying to check. - */ - if (ItemPointerEquals(tupleid, &tup->t_self)) - { - if (found_self) /* should not happen */ - elog(ERROR, "found self tuple multiple times in index \"%s\"", - RelationGetRelationName(index)); - found_self = true; - continue; - } - - /* - * Extract the index column values and isnull flags from the existing - * tuple. - */ - ExecStoreTuple(tup, existing_slot, InvalidBuffer, false); - FormIndexDatum(indexInfo, existing_slot, estate, - existing_values, existing_isnull); - - /* If lossy indexscan, must recheck the condition */ - if (index_scan->xs_recheck) - { - if (!index_recheck_constraint(index, - constr_procs, - existing_values, - existing_isnull, - values)) - continue; /* tuple doesn't actually match, so no - * conflict */ - } - - /* - * At this point we have either a conflict or a potential conflict. If - * we're not supposed to raise error, just return the fact of the - * potential conflict without waiting to see if it's real. - */ - if (errorOK) - { - conflict = true; - break; - } - - /* - * If an in-progress transaction is affecting the visibility of this - * tuple, we need to wait for it to complete and then recheck. For - * simplicity we do rechecking by just restarting the whole scan --- - * this case probably doesn't happen often enough to be worth trying - * harder, and anyway we don't want to hold any index internal locks - * while waiting. - */ - xwait = TransactionIdIsValid(DirtySnapshot.xmin) ? - DirtySnapshot.xmin : DirtySnapshot.xmax; - - if (TransactionIdIsValid(xwait)) - { - ctid_wait = tup->t_data->t_ctid; - index_endscan(index_scan); - XactLockTableWait(xwait, heap, &ctid_wait, - XLTW_RecheckExclusionConstr); - goto retry; - } - - /* - * We have a definite conflict. Report it. - */ - error_new = BuildIndexValueDescription(index, values, isnull); - error_existing = BuildIndexValueDescription(index, existing_values, - existing_isnull); - if (newIndex) - ereport(ERROR, - (errcode(ERRCODE_EXCLUSION_VIOLATION), - errmsg("could not create exclusion constraint \"%s\"", - RelationGetRelationName(index)), - error_new && error_existing ? - errdetail("Key %s conflicts with key %s.", - error_new, error_existing) : - errdetail("Key conflicts exist."), - errtableconstraint(heap, - RelationGetRelationName(index)))); - else - ereport(ERROR, - (errcode(ERRCODE_EXCLUSION_VIOLATION), - errmsg("conflicting key value violates exclusion constraint \"%s\"", - RelationGetRelationName(index)), - error_new && error_existing ? - errdetail("Key %s conflicts with existing key %s.", - error_new, error_existing) : - errdetail("Key conflicts with existing key."), - errtableconstraint(heap, - RelationGetRelationName(index)))); - } - - index_endscan(index_scan); - - /* - * Ordinarily, at this point the search should have found the originally - * inserted tuple, unless we exited the loop early because of conflict. - * However, it is possible to define exclusion constraints for which that - * wouldn't be true --- for instance, if the operator is <>. So we no - * longer complain if found_self is still false. - */ - - econtext->ecxt_scantuple = save_scantuple; - - ExecDropSingleTupleTableSlot(existing_slot); - - return !conflict; -} - -/* - * Check existing tuple's index values to see if it really matches the - * exclusion condition against the new_values. Returns true if conflict. - */ -static bool -index_recheck_constraint(Relation index, Oid *constr_procs, - Datum *existing_values, bool *existing_isnull, - Datum *new_values) -{ - int index_natts = index->rd_index->indnatts; - int i; - - for (i = 0; i < index_natts; i++) - { - /* Assume the exclusion operators are strict */ - if (existing_isnull[i]) - return false; - - if (!DatumGetBool(OidFunctionCall2Coll(constr_procs[i], - index->rd_indcollation[i], - existing_values[i], - new_values[i]))) - return false; - } - - return true; -} - /* * UpdateChangedParamSet * Add changed parameters to a plan node's chgParam set |