引言
本篇博客对查询规划的预处理过程中空FROM语句结点的替换过程进行介绍。
文件路径
/src/gausskernel/optimizer/prep/ prepjointree.cpp
函数replace_empty_jointree()
在之前的博客中我们介绍过,在optimizer查询规划的预处理过程中,包含一步“替换掉空的FROM语句”的处理步骤,这一过程是由prepjointree.cpp中的函数replace_empty_jointree完成的。值得一提的是,PostgreSQL的源码中并不包含这个函数,这个函数是openGauss中新加的函数。
void replace_empty_jointree(Query *parse)
{
RangeTblEntry *rte;
Index rti;
RangeTblRef *rtr;
/* Nothing to do if jointree is already nonempty */
if (parse->jointree->fromlist != NIL)
return;
/* We mustn't change it in the top level of a setop tree, either */
if (parse->setOperations)
return;
/* Create suitable RTE */
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RESULT;
rte->eref = makeAlias("*RESULT*", NIL);
/* Add it to rangetable */
parse->rtable = lappend(parse->rtable, rte);
rti = list_length(parse->rtable);
/* And jam a reference into the jointree */
rtr = makeNode(RangeTblRef);
rtr->rtindex = rti;
parse->jointree->fromlist = list_make1(rtr);
}
函数的作用是,如果当前的查询结点(输入的查询结点)的jointree是空树,即结点的fromlist字段是一个空指针,那么替换其为将其指向一个RTE_RESULT类型的“假”关系表。这样做的好处是可以避免以前在SELECT中使用遗漏的FROM子句时出现的一些特殊情况,比如,以前带有空联接树的子查询不能被提取出来,因为这会导致一个空的relid集使得子查询不能唯一地标识联接。
/* Nothing to do if jointree is already nonempty */
if (parse->jointree->fromlist != NIL)
return;
如果当前结点拥有一棵非空的jointree,直接返回不做替换。
/* We mustn't change it in the top level of a setop tree, either */
if (parse->setOperations)
return;
如果当前结点 setOperations 字段不为空,同样不做任何处理。Query结构体的setOperations字段用于表示查询中的集合操作,例如UNION、INTERSECT和EXCEPT,它允许将多个查询结果组合在一起并返回一个结果集,以此实现对多个子查询结果进行合并或筛选的功能。setOperations字段是一个SetOperationTree结构体,它包含了查询中使用的集合操作类型以及对应的子查询。也就是说这是集合操作树的顶层结点,函数同样不对其做任何处理。
/* Create suitable RTE */
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RESULT;
rte->eref = makeAlias("*RESULT*", NIL);
/* Add it to rangetable */
parse->rtable = lappend(parse->rtable, rte);
rti = list_length(parse->rtable);
接下来构造一个新的RangeTblEntry结点并将其附加到当前结点的range table的末尾。rtekind字段表示RangeTableEntry的类型,当rtekind字段取值为RTE_RESULT时,表示该RangeTableEntry是一个结果表,即查询中的一个子查询的结果作为表。这种情况下,该RangeTableEntry的relid字段对应的是子查询的查询计划。但是此处函数并没有构造结点的relid字段,这表明本结果表所引用的实体表并不存在,即本结果表的结果不包含任何可以被引用的元组,是一个空表。
/* And jam a reference into the jointree */
rtr = makeNode(RangeTblRef);
rtr->rtindex = rti;
parse->jointree->fromlist = list_make1(rtr);
之后调用函数list_make1(),通过一个新建的初始RTE结点创建一个空的List* 类型结构,并让当前结点的jointree->fromlist字段指向这个空表(原先其值为NULL,是一个空指针),并且这个空表与刚刚添加的RTE结点是对应的。完成上述操作,就实现了将fromlist指针替换为一个RTE_RESULT类型的“假”关系表。为什么说是假关系表呢?因为其并不与任何的实体关系表绑定,也不包含任何可以被引用的元组条目,是一张空表。通过这样做,可以避免子查询的FROM语句引用的 jointree 为空树的情况下,子查询不能被唯一标识的问题。
我将函数的作用过程简单绘制如下图。