heapam.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,410 行 · 第 1/5 页
C
2,410 行
ItemPointerheap_get_latest_tid(Relation relation, Snapshot snapshot, ItemPointer tid){ ItemId lp = NULL; Buffer buffer; PageHeader dp; OffsetNumber offnum; HeapTupleData tp; HeapTupleHeader t_data; ItemPointerData ctid; bool invalidBlock, linkend, valid; /* * get the buffer from the relation descriptor Note that this does a * buffer pin. */ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); if (!BufferIsValid(buffer)) elog(ERROR, "ReadBuffer(\"%s\", %lu) failed", RelationGetRelationName(relation), (unsigned long) ItemPointerGetBlockNumber(tid)); LockBuffer(buffer, BUFFER_LOCK_SHARE); /* * get the item line pointer corresponding to the requested tid */ dp = (PageHeader) BufferGetPage(buffer); offnum = ItemPointerGetOffsetNumber(tid); invalidBlock = true; if (!PageIsNew(dp)) { lp = PageGetItemId(dp, offnum); if (ItemIdIsUsed(lp)) invalidBlock = false; } if (invalidBlock) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); return NULL; } /* * more sanity checks */ tp.t_datamcxt = NULL; t_data = tp.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); tp.t_len = ItemIdGetLength(lp); tp.t_self = *tid; ctid = tp.t_data->t_ctid; /* * check time qualification of tid */ HeapTupleSatisfies(&tp, relation, buffer, dp, snapshot, 0, (ScanKey) NULL, valid); linkend = true; if ((t_data->t_infomask & HEAP_XMIN_COMMITTED) != 0 && !ItemPointerEquals(tid, &ctid)) linkend = false; LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); if (!valid) { if (linkend) return NULL; heap_get_latest_tid(relation, snapshot, &ctid); *tid = ctid; } return tid;}/* * heap_insert - insert tuple into a heap * * The new tuple is stamped with current transaction ID and the specified * command ID. */Oidheap_insert(Relation relation, HeapTuple tup, CommandId cid){ Buffer buffer; if (relation->rd_rel->relhasoids) {#ifdef NOT_USED /* this is redundant with an Assert in HeapTupleSetOid */ Assert(tup->t_data->t_infomask & HEAP_HASOID);#endif /* * If the object id of this tuple has already been assigned, trust * the caller. There are a couple of ways this can happen. At * initial db creation, the backend program sets oids for tuples. * When we define an index, we set the oid. Finally, in the * future, we may allow users to set their own object ids in order * to support a persistent object store (objects need to contain * pointers to one another). */ if (!OidIsValid(HeapTupleGetOid(tup))) HeapTupleSetOid(tup, newoid()); else CheckMaxObjectId(HeapTupleGetOid(tup)); } else { /* check there is not space for an OID */ Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); } tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); tup->t_data->t_infomask |= HEAP_XMAX_INVALID; HeapTupleHeaderSetXmin(tup->t_data, GetCurrentTransactionId()); HeapTupleHeaderSetCmin(tup->t_data, cid); tup->t_tableOid = relation->rd_id;#ifdef TUPLE_TOASTER_ACTIVE /* * If the new tuple is too big for storage or contains already toasted * attributes from some other relation, invoke the toaster. */ if (HeapTupleHasExtended(tup) || (MAXALIGN(tup->t_len) > TOAST_TUPLE_THRESHOLD)) heap_tuple_toast_attrs(relation, tup, NULL);#endif /* Find buffer to insert this tuple into */ buffer = RelationGetBufferForTuple(relation, tup->t_len, InvalidBuffer); /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); RelationPutHeapTuple(relation, buffer, tup); pgstat_count_heap_insert(&relation->pgstat_info); /* XLOG stuff */ if (!relation->rd_istemp) { xl_heap_insert xlrec; xl_heap_header xlhdr; XLogRecPtr recptr; XLogRecData rdata[3]; Page page = BufferGetPage(buffer); uint8 info = XLOG_HEAP_INSERT; xlrec.target.node = relation->rd_node; xlrec.target.tid = tup->t_self; rdata[0].buffer = InvalidBuffer; rdata[0].data = (char *) &xlrec; rdata[0].len = SizeOfHeapInsert; rdata[0].next = &(rdata[1]); xlhdr.t_natts = tup->t_data->t_natts; xlhdr.t_infomask = tup->t_data->t_infomask; xlhdr.t_hoff = tup->t_data->t_hoff; /* * note we mark rdata[1] as belonging to buffer; if XLogInsert * decides to write the whole page to the xlog, we don't need to * store xl_heap_header in the xlog. */ rdata[1].buffer = buffer; rdata[1].data = (char *) &xlhdr; rdata[1].len = SizeOfHeapHeader; rdata[1].next = &(rdata[2]); rdata[2].buffer = buffer; /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */ rdata[2].data = (char *) tup->t_data + offsetof(HeapTupleHeaderData, t_bits); rdata[2].len = tup->t_len - offsetof(HeapTupleHeaderData, t_bits); rdata[2].next = NULL; /* * If this is the single and first tuple on page, we can reinit * the page instead of restoring the whole thing. Set flag, and * hide buffer references from XLogInsert. */ if (ItemPointerGetOffsetNumber(&(tup->t_self)) == FirstOffsetNumber && PageGetMaxOffsetNumber(page) == FirstOffsetNumber) { info |= XLOG_HEAP_INIT_PAGE; rdata[1].buffer = rdata[2].buffer = InvalidBuffer; } recptr = XLogInsert(RM_HEAP_ID, info, rdata); PageSetLSN(page, recptr); PageSetSUI(page, ThisStartUpID); } else { /* No XLOG record, but still need to flag that XID exists on disk */ MyXactMadeTempRelUpdate = true; } END_CRIT_SECTION(); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); WriteBuffer(buffer); /* * If tuple is cachable, mark it for invalidation from the caches in * case we abort. Note it is OK to do this after WriteBuffer releases * the buffer, because the "tup" data structure is all in local * memory, not in the shared buffer. */ CacheInvalidateHeapTuple(relation, tup); return HeapTupleGetOid(tup);}/* * simple_heap_insert - insert a tuple * * Currently, this routine differs from heap_insert only in supplying * a default command ID. But it should be used rather than using * heap_insert directly in most places where we are modifying system catalogs. */Oidsimple_heap_insert(Relation relation, HeapTuple tup){ return heap_insert(relation, tup, GetCurrentCommandId());}/* * heap_delete - delete a tuple * * NB: do not call this directly unless you are prepared to deal with * concurrent-update conditions. Use simple_heap_delete instead. * * relation - table to be modified * tid - TID of tuple to be deleted * ctid - output parameter, used only for failure case (see below) * cid - delete command ID to use in verifying tuple visibility * crosscheck - if not SnapshotAny, also check tuple against this * wait - true if should wait for any conflicting update to commit/abort * * Normal, successful return value is HeapTupleMayBeUpdated, which * actually means we did delete it. Failure return codes are * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated * (the last only possible if wait == false). On a failure return, * *ctid is set to the ctid link of the target tuple (possibly a later * version of the row). */intheap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, CommandId cid, Snapshot crosscheck, bool wait){ ItemId lp; HeapTupleData tp; PageHeader dp; Buffer buffer; int result; uint16 sv_infomask; Assert(ItemPointerIsValid(tid)); buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); if (!BufferIsValid(buffer)) elog(ERROR, "ReadBuffer failed"); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); dp = (PageHeader) BufferGetPage(buffer); lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); tp.t_datamcxt = NULL; tp.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); tp.t_len = ItemIdGetLength(lp); tp.t_self = *tid; tp.t_tableOid = relation->rd_id;l1: sv_infomask = tp.t_data->t_infomask; result = HeapTupleSatisfiesUpdate(tp.t_data, cid); if (sv_infomask != tp.t_data->t_infomask) SetBufferCommitInfoNeedsSave(buffer); if (result == HeapTupleInvisible) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); elog(ERROR, "attempted to delete invisible tuple"); } else if (result == HeapTupleBeingUpdated && wait) { TransactionId xwait = HeapTupleHeaderGetXmax(tp.t_data); /* sleep until concurrent transaction ends */ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); if (!TransactionIdDidCommit(xwait)) goto l1; /* * xwait is committed but if xwait had just marked the tuple for * update then some other xaction could update this tuple before * we got to this point. */ if (!TransactionIdEquals(HeapTupleHeaderGetXmax(tp.t_data), xwait)) goto l1; if (!(tp.t_data->t_infomask & HEAP_XMAX_COMMITTED)) { tp.t_data->t_infomask |= HEAP_XMAX_COMMITTED; SetBufferCommitInfoNeedsSave(buffer); } /* if tuple was marked for update but not updated... */ if (tp.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; } if (crosscheck != SnapshotAny && result == HeapTupleMayBeUpdated) { /* Perform additional check for serializable RI updates */ sv_infomask = tp.t_data->t_infomask; if (!HeapTupleSatisfiesSnapshot(tp.t_data, crosscheck)) result = HeapTupleUpdated; if (sv_infomask != tp.t_data->t_infomask) SetBufferCommitInfoNeedsSave(buffer); } if (result != HeapTupleMayBeUpdated) { Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated || result == HeapTupleBeingUpdated); *ctid = tp.t_data->t_ctid; LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); return result; } START_CRIT_SECTION(); /* store transaction information of xact deleting the tuple */ tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE | HEAP_MOVED); HeapTupleHeaderSetXmax(tp.t_data, GetCurrentTransactionId()); HeapTupleHeaderSetCmax(tp.t_data, cid); /* Make sure there is no forward chain link in t_ctid */ tp.t_data->t_ctid = tp.t_self; /* XLOG stuff */ if (!relation->rd_istemp) { xl_heap_delete xlrec; XLogRecPtr recptr; XLogRecData rdata[2]; xlrec.target.node = relation->rd_node; xlrec.target.tid = tp.t_self; rdata[0].buffer = InvalidBuffer; rdata[0].data = (char *) &xlrec; rdata[0].len = SizeOfHeapDelete; rdata[0].next = &(rdata[1]); rdata[1].buffer = buffer; rdata[1].data = NULL; rdata[1].len = 0; rdata[1].next = NULL; recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE, rdata); PageSetLSN(dp, recptr); PageSetSUI(dp, ThisStartUpID); } else { /* No XLOG record, but still need to flag that XID exists on disk */ MyXactMadeTempRelUpdate = true; } END_CRIT_SECTION(); LockBuffer(buffer, BUFFER_LOCK_UNLOCK);#ifdef TUPLE_TOASTER_ACTIVE /* * If the relation has toastable attributes, we need to delete no * longer needed items there too. We have to do this before * WriteBuffer because we need to look at the contents of the tuple, * but it's OK to release the context lock on the buffer first. */ if (HeapTupleHasExtended(&tp)) heap_tuple_toast_attrs(relation, NULL, &(tp));#endif pgstat_count_heap_delete(&relation->pgstat_info); /* * Mark tuple for invalidation from system caches at next command * boundary. We have to do this before WriteBuffer because we need to * look at the contents of the tuple, so we need to hold our refcount * on the buffer. */ CacheInvalidateHeapTuple(relation, &tp); WriteBuffer(buffer); return HeapTupleMayBeUpdated;}/* * simple_heap_delete - delete a tuple * * This routine may be used to delete a tuple when concurrent updates of * the target tuple are not expected (for example, because we have a lock * on the relation associated with the tuple). Any failure is reported * via ereport(). */voidsimple_heap_delete(Relation relation, ItemPointer tid){ ItemPointerData ctid; int result; result = heap_delete(relation, tid, &ctid, GetCurrentCommandId(), SnapshotAny, true /* wait for commit */); switch (result) { case HeapTupleSelfUpdated: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; case HeapTupleMayBeUpdated: /* done successfully */ break; case HeapTupleUpdated: elog(ERROR, "tuple concurrently updated"); break; default: elog(ERROR, "unrecognized heap_delete status: %u", result); break; }}/* * heap_update - replace a tuple * * NB: do not call this directly unless you are prepared to deal with * concurrent-update conditions. Use simple_heap_update instead. * * relation - table to be modified * otid - TID of old tuple to be replaced * newtup - newly constructed tuple data to store * ctid - output parameter, used only for failure case (see below) * cid - update command ID to use in verifying old tuple visibility * crosscheck - if not SnapshotAny, also check old tuple against this * wait - true if should wait for any conflicting update to commit/abort * * Normal, successful return value is HeapTupleMayBeUpdated, which * actually means we *did* update it. Failure return codes are * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated * (the last only possible if wait == false). On a failure return, * *ctid is set to the ctid link of the old tuple (possibly a later
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?