跟我一起读postgresql源码(十)——Executor(查询执行模块之——Scan节点(下)) (2)

这里根据FunctionScan中的functions字段对每一个函数构造用于表达式计算的结构(存储在funcexpr中)和,还要构造函数返回元组的描述符存储在tupdesc中,此时用于存储函数结果集的tuplestoreslate字段为NULL。
上面这些做完以后,就可以根据所涉及的所有函数的FunctionScanPerFuncState结构来构造返回值的TupleDesc(即最后的返回值一定是这几个函数返回值的组合):
例如:

postgres=# SELECT * FROM dup(42) WITH ORDINALITY AS t(ls,n,xxx),increment(42); ls | n | xxx | increment ----+------------+-----+----------- 42 | 42 is text | 1 | 43 (1 行) typedef struct FunctionScanState { ScanState ss; /* its first field is NodeTag */ int eflags; //node's capability flags bool ordinality; //is this scan WITH ORDINALITY? bool simple; //true if we have 1 function and no ordinality int64 ordinal; //current ordinal column value int nfuncs; //number of functions being executed /* per-function execution states (private in nodeFunctionscan.c) */ struct FunctionScanPerFuncState *funcstates; /* array of length nfuncs */ MemoryContext argcontext; //memory context to evaluate function arguments in } FunctionScanState;

在 FunctionScan 节点的执行过程(ExecFunctionScan 函数)中,将 FunctionNext 传递给 ExecScan函数,FunctionNext函数首先判断tuplestorestate是否为空(首次执行时为空),如果为空则执行函数ExecMakeTableFunctionResult生成所有结果集并存储在tuplestorestate中,此后每次执行节点将调用tuplestore_gettupleslot获取结果集中的一个元组。

最后,FunctionScan节点清理过程需要淸理tuplestorestate结构。

10.ValuesScan 节点

VALUES计算由值表达式指定的一个行值或者一组行值。更常见的是把它用来生成一个大型命令内的"常量表", 但是它也可以被独自使用。

当多于一行被指定时,所有行都必须具有相同数量的元素。结果表的列数据类型 由出现在该列的表达式的显式或者推导类型组合决定,决定的规则与UNION相同。

在大型的命令中,在语法上允许VALUES出现在 SELECT出现的任何地方。因为语法把它当做一个 SELECT,可以为一个VALUES 命令使用ORDER BY、 LIMIT(或者等效的FETCH FIRST) 以及OFFSET子句。

我们举例吧,一个纯粹的VALUES命令:

VALUES (1, 'one'), (2, 'two'), (3, 'three');

将返回一个具有两列、三行的表。

postgres=# VALUES (1, 'one'), (2, 'two'), (3, 'three'); column1 | column2 ---------+--------- 1 | one 2 | two 3 | three (3 行) postgres=# EXPLAIN VALUES (1, 'one'), (2, 'two'), (3, 'three'); QUERY PLAN -------------------------------------------------------------- Values Scan on "*VALUES*" (cost=0.00..0.04 rows=3 width=36) (1 行)

更常用地,VALUES可以被用在一个大型 SQL 命令中。 在INSERT中最常用:

postgres=# insert into test values (1,'xxxx'); INSERT 0 1 postgres=# explain insert into test_new values (1); QUERY PLAN ------------------------------------------------------ Insert on test_new (cost=0.00..0.01 rows=1 width=0) -> Result (cost=0.00..0.01 rows=1 width=0) (2 行)

具体的可以看这个:
这样我们就对VALUES子句不陌生了,下面继续说。

ValuesScan节点是用来对VALUES子句给出的元组集合进行扫描(INSERT语句中的VALUES子句走的是RESULT节点)。如下所示,ValuesScan节点中的values_lists存储了VALUES子句中的表达式链表。

typedef struct ValuesScan { Scan scan; List *values_lists; /* list of expression lists */ } ValuesScan;

ValuesScan节点的初始化过程(ExeclnitValuesScan函数)处理values_lists中的表达式生成Values表达式,并存储在ValuesScanState的exprlists数组中,array_len记录数组长度,cuxr_idx和
markedJdx用于存储数组中的偏移量。同时还会分配内存上下文rowconext用于表达式计箅(ss.ps.ps_ExprContext本来就是用来做表达式计算的,但是为了防止对于一个过长的VALUES子句发生的内存泄露,使用rowconext对VALUES每一行做统一处理,在每一行处理完成后就使用rowconext释放该段内存。)。

typedef struct ValuesScanState { ScanState ss; /* its first field is NodeTag */ ExprContext *rowcontext; //per-expression-list context List **exprlists; //array of expression lists being evaluated int array_len; //size of array int curr_idx; //current array index (0-based) } ValuesScanState;

ValuesScan 节点执行过程(ExecValuesScan 函数)调用 ExecScan 实现,ExecScan 通过 ValuesNext获取扫描元组,ValuesNext则通过curr_idx从exprlists中获取需要处理的表达式,并计算出结果元组返回。

由于额外地申请了rowconext上下文,因此在ValuesScan节点清理过程(ExecEndValuesScan函数)中需要释放内存上下文rowcontext。

11.CteScan 节点

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzggxd.html