源码分析-TupleTableSlot

PostgreSQL12对比PostgreSQL11,无论在功能上和代码上都有很大的变化,下面分析一下TupleTableSlot这个数据结构在两个版本的不同

首先看看数据结构
PostgreSQL11.5

typedef struct TupleTableSlot
{
	NodeTag		type;
	bool		tts_isempty;	/* true = slot is empty */
	bool		tts_shouldFree; /* should pfree tts_tuple? */
	bool		tts_shouldFreeMin;	/* should pfree tts_mintuple? */
#define FIELDNO_TUPLETABLESLOT_SLOW 4
	bool		tts_slow;		/* saved state for slot_deform_tuple */
#define FIELDNO_TUPLETABLESLOT_TUPLE 5
	HeapTuple	tts_tuple;		/* physical tuple, or NULL if virtual */
#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6
	TupleDesc	tts_tupleDescriptor;	/* slot's tuple descriptor */
	MemoryContext tts_mcxt;		/* slot itself is in this context */
	Buffer		tts_buffer;		/* tuple's buffer, or InvalidBuffer */
#define FIELDNO_TUPLETABLESLOT_NVALID 9
	int			tts_nvalid;		/* # of valid values in tts_values */
#define FIELDNO_TUPLETABLESLOT_VALUES 10
	Datum	   *tts_values;		/* current per-attribute values */
#define FIELDNO_TUPLETABLESLOT_ISNULL 11
	bool	   *tts_isnull;		/* current per-attribute isnull flags */
	MinimalTuple tts_mintuple;	/* minimal tuple, or NULL if none */
	HeapTupleData tts_minhdr;	/* workspace for minimal-tuple-only case */
#define FIELDNO_TUPLETABLESLOT_OFF 14
	uint32		tts_off;		/* saved state for slot_deform_tuple */
	bool		tts_fixedTupleDescriptor;	/* descriptor can't be changed */
} TupleTableSlot;

PostgreSQL12.1

typedef struct TupleTableSlot
{
	NodeTag		type;
#define FIELDNO_TUPLETABLESLOT_FLAGS 1
	uint16		tts_flags;		/* Boolean states */
#define FIELDNO_TUPLETABLESLOT_NVALID 2
	AttrNumber	tts_nvalid;		/* # of valid values in tts_values */
	const TupleTableSlotOps *const tts_ops; /* implementation of slot */
#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4
	TupleDesc	tts_tupleDescriptor;	/* slot's tuple descriptor */
#define FIELDNO_TUPLETABLESLOT_VALUES 5
	Datum	   *tts_values;		/* current per-attribute values */
#define FIELDNO_TUPLETABLESLOT_ISNULL 6
	bool	   *tts_isnull;		/* current per-attribute isnull flags */
	MemoryContext tts_mcxt;		/* slot itself is in this context */
	ItemPointerData tts_tid;    /* stored tuple's tid */
	Oid			tts_tableOid;   /* table oid of tuple */
} TupleTableSlot;

两个版本的TupleTableSlot数据结构差别较大,先是解释一下TupleTableSlot的作用。
HeapTuple有四种类型(物理/内存/精简/虚拟),TupleTableSlot是用来管理HeapTuple的一个数据结构,对上述四种类型的HeapTuple提供了统一的对外表示。

在11.5版本里,TupleTableSlot使用较多的成员变量来标识tuple的不同状态,例如tts_isempty,tts_shouldFree等,在12.1里,使用tts_flags来标识个各种状态并使用逻辑位与的方法来获取状态。

#define			TTS_FLAG_EMPTY			(1 << 1)
#define TTS_EMPTY(slot)	(((slot)->tts_flags & TTS_FLAG_EMPTY) != 0)

/* should pfree tuple "owned" by the slot? */
#define			TTS_FLAG_SHOULDFREE		(1 << 2)
#define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0)

/* saved state for slot_deform_heap_tuple */
#define			TTS_FLAG_SLOW		(1 << 3)
#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0)

/* fixed tuple descriptor */
#define			TTS_FLAG_FIXED		(1 << 4)
#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0)

在11.5版本里,对TupleTableSlot是直接编写函数对TupleTableSlot进行操作,但在12.1里,使用成员变量TupleTableSlotOps来提供TupleTableSlot的操作函数。这有点类似面向对象的设计,同时能把数据与操作方法有效隔离。

TupleTableSlotOps数据结构

/* routines for a TupleTableSlot implementation */
struct TupleTableSlotOps
{
	/* Minimum size of the slot */
	size_t			base_slot_size;

	/* Initialization. */
	void (*init)(TupleTableSlot *slot);

	/* Destruction. */
	void (*release)(TupleTableSlot *slot);

	/*
	 * Clear the contents of the slot. Only the contents are expected to be
	 * cleared and not the tuple descriptor. Typically an implementation of
	 * this callback should free the memory allocated for the tuple contained
	 * in the slot.
	 */
	void (*clear)(TupleTableSlot *slot);

	/*
	 * Fill up first natts entries of tts_values and tts_isnull arrays with
	 * values from the tuple contained in the slot. The function may be called
	 * with natts more than the number of attributes available in the tuple,
	 * in which case it should set tts_nvalid to the number of returned
	 * columns.
	 */
	void (*getsomeattrs)(TupleTableSlot *slot, int natts);

	/*
	 * Returns value of the given system attribute as a datum and sets isnull
	 * to false, if it's not NULL. Throws an error if the slot type does not
	 * support system attributes.
	 */
	Datum (*getsysattr)(TupleTableSlot *slot, int attnum, bool *isnull);

	/*
	 * Make the contents of the slot solely depend on the slot, and not on
	 * underlying resources (like another memory context, buffers, etc).
	 */
	void (*materialize)(TupleTableSlot *slot);

	/*
	 * Copy the contents of the source slot into the destination slot's own
	 * context. Invoked using callback of the destination slot.
	 */
	void (*copyslot) (TupleTableSlot *dstslot, TupleTableSlot *srcslot);

	/*
	 * Return a heap tuple "owned" by the slot. It is slot's responsibility to
	 * free the memory consumed by the heap tuple. If the slot can not "own" a
	 * heap tuple, it should not implement this callback and should set it as
	 * NULL.
	 */
	HeapTuple (*get_heap_tuple)(TupleTableSlot *slot);

	/*
	 * Return a minimal tuple "owned" by the slot. It is slot's responsibility
	 * to free the memory consumed by the minimal tuple. If the slot can not
	 * "own" a minimal tuple, it should not implement this callback and should
	 * set it as NULL.
	 */
	MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot);

	/*
	 * Return a copy of heap tuple representing the contents of the slot. The
	 * copy needs to be palloc'd in the current memory context. The slot
	 * itself is expected to remain unaffected. It is *not* expected to have
	 * meaningful "system columns" in the copy. The copy is not be "owned" by
	 * the slot i.e. the caller has to take responsibilty to free memory
	 * consumed by the slot.
	 */
	HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot);

	/*
	 * Return a copy of minimal tuple representing the contents of the slot. The
	 * copy needs to be palloc'd in the current memory context. The slot
	 * itself is expected to remain unaffected. It is *not* expected to have
	 * meaningful "system columns" in the copy. The copy is not be "owned" by
	 * the slot i.e. the caller has to take responsibilty to free memory
	 * consumed by the slot.
	 */
	MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);
};

可以看到,TupleTableSlotOps是一系列对TupleTableSlot操作函数的函数指针,据说PostgreSQL13支持可插拔式的存储引擎,这有可能是为这一特性做的重构。
两个版本对TupleTableSlot的操作例子
PostgreSQL11.5 ExecMaterializeSlot函数

HeapTuple
ExecMaterializeSlot(TupleTableSlot *slot)
{
	MemoryContext oldContext;

	/*
	 * sanity checks
	 */
	Assert(slot != NULL);
	Assert(!slot->tts_isempty);

	/*
	 * If we have a regular physical tuple, and it's locally palloc'd, we have
	 * nothing to do.
	 */
	if (slot->tts_tuple && slot->tts_shouldFree)
		return slot->tts_tuple;

	/*
	 * Otherwise, copy or build a physical tuple, and store it into the slot.
	 *
	 * We may be called in a context that is shorter-lived than the tuple
	 * slot, but we have to ensure that the materialized tuple will survive
	 * anyway.
	 */
	oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
	slot->tts_tuple = ExecCopySlotTuple(slot);
	slot->tts_shouldFree = true;
	MemoryContextSwitchTo(oldContext);

	/*
	 * Drop the pin on the referenced buffer, if there is one.
	 */
	if (BufferIsValid(slot->tts_buffer))
		ReleaseBuffer(slot->tts_buffer);

	slot->tts_buffer = InvalidBuffer;

	/*
	 * Mark extracted state invalid.  This is important because the slot is
	 * not supposed to depend any more on the previous external data; we
	 * mustn't leave any dangling pass-by-reference datums in tts_values.
	 * However, we have not actually invalidated any such datums, if there
	 * happen to be any previously fetched from the slot.  (Note in particular
	 * that we have not pfree'd tts_mintuple, if there is one.)
	 */
	slot->tts_nvalid = 0;

	/*
	 * On the same principle of not depending on previous remote storage,
	 * forget the mintuple if it's not local storage.  (If it is local
	 * storage, we must not pfree it now, since callers might have already
	 * fetched datum pointers referencing it.)
	 */
	if (!slot->tts_shouldFreeMin)
		slot->tts_mintuple = NULL;

	return slot->tts_tuple;
}

PostgreSQL12.1 ExecMaterializeSlot函数

static inline void
ExecMaterializeSlot(TupleTableSlot *slot)
{
	slot->tts_ops->materialize(slot);
}

可以看到在12.1里对TupleTableSlot的操作代码非常简洁,所有的逻辑都由TupleTableSlot->tts_ops->materialize指针指向的函数来处理。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值