What is the implementation logic of ExecInsert function in PostgreSQL?
Numtriggers; iTunes +) {Trigger * trigger = & trigdesc- > triggers [I]; if (! TRIGGER_TYPE_MATCHES (trigger- > tgtype, TRIGGER_TYPE_ROW, TRIGGER_TYPE_BEFORE, TRIGGER_TYPE_INSERT)) continue If (! TriggerEnabled (estate, relinfo, trigger, LocTriggerData.tg_event, NULL, NULL, newtuple)) continue; LocTriggerData.tg_trigtuple = oldtuple = newtuple; LocTriggerData.tg_trigtuplebuf = InvalidBuffer; LocTriggerData.tg_trigger = trigger Newtuple = ExecCallTriggerFunc (& LocTriggerData, I, relinfo- > ri_TrigFunctions, relinfo- > ri_TrigInstrument, GetPerTupleMemoryContext (estate)) If (oldtuple! = newtuple & & oldtuple! = slottuple) heap_freetuple (oldtuple); if (newtuple = = NULL) return NULL; / * "do nothing" * /} if (newtuple! = slottuple) {/ * * Return the modified tuple using the es_trig_tuple_slot. We assume * the tuple was allocated in per-tuple memory context, and therefore * will go away by itself. The tuple table slot should not try to * clear it. * / TupleTableSlot * newslot = estate- > es_trig_tuple_slot; TupleDesc tupdesc = RelationGetDescr (relinfo- > ri_RelationDesc); if (newslot- > tts_tupleDescriptor! = tupdesc) ExecSetSlotDescriptor (newslot, tupdesc); ExecStoreTuple (newtuple, newslot, InvalidBuffer, false); slot = newslot;} return slot;}
4 、 ExecIRInsertTriggers
TupleTableSlot * ExecIRInsertTriggers (EState * estate, ResultRelInfo * relinfo, TupleTableSlot * slot) {TriggerDesc * trigdesc = relinfo- > ri_TrigDesc; HeapTuple slottuple = ExecMaterializeSlot (slot); HeapTuple newtuple = slottuple; HeapTuple oldtuple; TriggerData LocTriggerData; int i; LocTriggerData.type = titled TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_INSTEAD LocTriggerData.tg_relation = relinfo- > ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; LocTriggerData.tg_oldtable = NULL; LocTriggerData.tg_newtable = NULL; LocTriggerData.tg_newtuplebuf = InvalidBuffer; for (I = 0; I
< trigdesc-> < numIndices; i++) { Relation indexRelation = relationDescs[i]; IndexInfo *indexInfo; bool satisfiesConstraint; if (indexRelation == NULL) continue; indexInfo = indexInfoArray[i]; if (!indexInfo-> < numIndices; i++) { Relation indexRelation = relationDescs[i]; IndexInfo *indexInfo; bool applyNoDupErr; 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) {ExprState * predicate; / * * If predicate state not set up yet, create it (in the estate's * per-query context) * / predicate = indexInfo- > ii_PredicateState If (predicate = = NULL) {predicate = ExecPrepareQual (indexInfo- > ii_Predicate, estate); indexInfo- > ii_PredicateState = predicate;} / * Skip this index-update if the predicate isn't satisfied * / if (! ExecQual (predicate, econtext)) 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) / * Check whether to apply noDupErr to this index * / applyNoDupErr = noDupErr & & (arbiterIndexes = = NIL | | list_member_oid (arbiterIndexes, indexRelation- > rd_index- > indexrelid)); / * 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. * * For a speculative insertion (used by INSERT... ON CONFLICT), do * the same as for a deferrable unique index. * / if (! indexRelation- > rd_index- > indisunique) checkUnique = UNIQUE_CHECK_NO; else if (applyNoDupErr) checkUnique = UNIQUE_CHECK_PARTIAL; 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 * / indexInfo) / * index AM may need this * / * 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 or wait for * a conclusive outcome from a concurrent insertion; instead we'll * queue a recheck event. Similarly, noDupErr callers (speculative * inserters) will recheck later, and wait for a conclusive outcome * then. * * 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 violationOK; CEOUC_WAIT_MODE waitMode; if (applyNoDupErr) {violationOK = true; waitMode = CEOUC_LIVELOCK_PREVENTING_WAIT } else if (! indexRelation- > rd_index- > indimmediate) {violationOK = true; waitMode = CEOUC_NOWAIT;} else {violationOK = false; waitMode = CEOUC_WAIT } satisfiesConstraint = check_exclusion_or_unique_constraint (heapRelation, indexRelation, indexInfo, tupleid, values, isnull, estate, false) WaitMode, violationOK, NULL) } 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. Speculative inserters are told if there was a * speculative conflict, since that always requires a restart. * / result = lappend_oid (result, RelationGetRelid (indexRelation)); if (indexRelation- > rd_index- > indimmediate & & specConflict) * specConflict = true;}} return result;}
18 、 heap_finish_speculative
/ * * heap_finish_speculative-mark speculative insertion as successful * * To successfully finish a speculative insertion we have to clear speculative * token from tuple. To do so the t_ctid field, which will contain a * speculative token value, is modified in place to point to the tuple itself, * which is characteristic of a newly inserted ordinary tuple. * * NB: It is not ok to commit without either finishing or aborting a * speculative insertion. We could treat speculative tuples of committed * transactions implicitly as completed, but then we would have to be prepared * to deal with speculative tokens on committed tuples. That wouldn't be * difficult-no-one looks at the ctid field of a tuple with invalid xmax-* but clearing the token at completion isn't very expensive either. * An explicit confirmation WAL record also makes logical decoding simpler. * / void heap_finish_speculative (Relation relation, HeapTuple tuple) {Buffer buffer; Page page; OffsetNumber offnum; ItemId lp = NULL; HeapTupleHeader htup; buffer = ReadBuffer (relation, ItemPointerGetBlockNumber (& (tuple- > t_self); LockBuffer (buffer, BUFFER_LOCK_EXCLUSIVE); page = (Page) BufferGetPage (buffer); offnum = ItemPointerGetOffsetNumber (& (tuple- > t_self)) If (PageGetMaxOffsetNumber (page) > = offnum) lp = PageGetItemId (page, offnum); if (PageGetMaxOffsetNumber (page)
< offnum || !ItemIdIsNormal(lp)) elog(ERROR, "invalid lp"); htup = (HeapTupleHeader) PageGetItem(page, lp); /* SpecTokenOffsetNumber should be distinguishable from any real offset */ StaticAssertStmt(MaxOffsetNumber < SpecTokenOffsetNumber, "invalid speculative token constant"); /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); Assert(HeapTupleHeaderIsSpeculative(tuple->