引言
从本篇文章开始,我将对扫描算子开始学习。本篇文章学习SeqScan算子。
代码位置
src\gausskernel\runtime\executor\nodeSeqscan.cpp
功能作用
SeqScan是最基本的扫描算子,用于扫描一个没有任何索引等信息的普通物理表。其最关键的函数是ExecSeqScan,扫描获取元组并返回一个TupleTableSlot对象。
ExecSeqScan函数代码如下:
//代码清单1
TupleTableSlot* ExecSeqScan(SeqScanState* node)
{
if (node->scanBatchMode) {
return (TupleTableSlot *)SeqNextBatchMode(node);
} else {
return ExecScan((ScanState *) node, node->ScanNextMtd, (ExecScanRecheckMtd) SeqRecheck);
}
}
传入参数
其传入参数是一个SeqScanState类型的node节点,SeqScanState结构体定义为:
//代码清单2
typedef ScanState SeqScanState;
typedef struct ScanState {
PlanState ps; /* its first field is NodeTag */
Relation ss_currentRelation;
TableScanDesc ss_currentScanDesc;
TupleTableSlot* ss_ScanTupleSlot;
bool ss_ReScan;
Relation ss_currentPartition;
bool isPartTbl;
int currentSlot; /* current iteration position */
ScanDirection partScanDirection;
List* partitions; /* list of Partition */
List* subpartitions; /* list of SubPartition */
LOCKMODE lockMode;
List* runTimeParamPredicates;
bool runTimePredicatesReady;
bool is_scan_end; /* @hdfs Mark whether iterator is over or not, if the scan uses informational constraint. */
SeqScanAccessor* ss_scanaccessor; /* prefetch related */
int part_id;
List* subPartLengthList;
int startPartitionId; /* start partition id for parallel threads. */
int endPartitionId; /* end partition id for parallel threads. */
RangeScanInRedis rangeScanInRedis; /* if it is a range scan in redistribution time */
bool isSampleScan; /* identify is it table sample scan or not. */
SampleScanParams sampleScanInfo; /* TABLESAMPLE params include type/seed/repeatable. */
SeqScanGetNextMtd fillNextSlotFunc;
ExecScanAccessMtd ScanNextMtd;
bool scanBatchMode;
ScanBatchState* scanBatchState;
} ScanState;
类似继承的效果
代码清代2中ScanState结构体内定义的第一行是 PlanState ps;
可以视作ScanState结构体“继承”于PlanState结构体。因为对一个ScanState结构体指针node执行(PlanState*)node
语句是完全被允许的,并且类型转换后,node视作PlanState*类型使用,并只能访问ps所指向的内容。
图解如下:
node指针和ps指针指向同一个位置,所以类型转换前,node能读取ScanState的信息,类型转换后,node只能读取PlanState的信息。这样达到了一个类似继承的效果。
这样做可以给编写代码带来方便,手动进行函数重载,详细内容我将在后文介绍。
主要字段解释
类型 | 命名 | 含义 |
---|---|---|
Relation | ss_currentRelation | 正在被扫描的关系 |
TableScanDesc | ss_currentScanDesc | 目前扫描的描述信息 |
TupleTableSlot* | ss_ScanTupleSlot | 存储扫描获得的元组 |
bool | isPartTbl | 是否是分区表 |
LOCKMODE | lockMode | 锁模式 |
bool | runTimePredicatesReady | 是否准备好运行时过滤谓词 |
List* | runTimeParamPredicates | 过滤谓词的列表 |
bool | scanBatchMode | 是否批量获取元组 |
ScanBatchState | scanBatchState | 存储批量元组 |
有关分区表的内容将在今后的文章中解释。
返回参数
其返回参数是一个TupleTableSlot类型结构体的指针。TupleTableSlot结构体是数据库执行过程中临时存储元组数据的一个数据结构,通常在执行过程中作为中间数据,传递给上层进行处理。
结构体定义如下:
//代码清单3
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? */
bool tts_slow; /* saved state for slot_deform_tuple */
Tuple tts_tuple; /* physical tuple, or NULL if virtual */
#ifdef PGXC
/*
* PGXC extension to support tuples sent from remote Datanode.
*/
char* tts_dataRow; /* Tuple data in DataRow format */
int tts_dataLen; /* Actual length of the data row */
bool tts_shouldFreeRow; /* should pfree tts_dataRow? */
struct AttInMetadata* tts_attinmeta; /* store here info to extract values from the DataRow */
Oid tts_xcnodeoid; /* Oid of node from where the datarow is fetched */
MemoryContext tts_per_tuple_mcxt;
#endif
TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
MemoryContext tts_mcxt; /* slot itself is in this context */
Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
int tts_nvalid; /* # of valid values in tts_values */
Datum* tts_values; /* current per-attribute values */
bool* tts_isnull; /* current per-attribute isnull flags */
Datum* tts_lobPointers;
MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
long tts_off; /* saved state for slot_deform_tuple */
long tts_meta_off; /* saved state for slot_deform_cmpr_tuple */
TableAmType tts_tupslotTableAm; /* slots's tuple table type */
} TupleTableSlot;
主要字段解释
类型 | 命名 | 含义 |
---|---|---|
NodeTag | type | 结点类型 |
Tuple | tts_tuple | 存储的物理元组 |
MemoryContext | tts_mcxt | slot槽所在内存上下文 |
Buffer | tts_buffer | 元组的缓冲区 |
int | tts_nvalid | tts_values 中的有效值个数 |
Datum* | tts_values | 属性值 |
TableAmType | tts_tupslotTableAm | slot槽的类型 |
其中tts_values里存储属性值。例如获取了某一个表的某一行数据:
那么tts_values数组中所存储的就是:
执行过程
如果scanBatchMode为真,则调用SeqNextBatchMode函数。否则调用ExecScan函数。
-
ExecScan函数:返回单个元组
-
SeqNextBatchMode函数:批量返回元组
观察SeqNextBatchMode函数的返回值ScanBatchResult结构体指针,如代码清单4.
//代码清单4
struct ScanBatchResult {
int rows; /* rows number for current page. */
TupleTableSlot** scanTupleSlotInBatch; /* array size of BatchMaxSize, stores tuples scanned in a page */
};
可以看到结构体里存放着一个TupleTableSlot** 二维指针,用于存放返回的批量元组。
小结
本篇文章中,我学习了TupleTableSlot结构体、ScanState结构体以及ExecSeqScan函数的作用。接下来的几篇文章我将继续学习其他扫描算子。下一篇文章:IndexScan算子。