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 + -
显示快捷键?