Postgresql源码(116)提升子查询案例分析
0 总结
对于SQL:select * from student, (select * from score where sno > 2) s where student.sno = s.sno;
pullup在pull_up_subqueries函数内递归完成,分几步:
- 将内层rte score追加到上层rtbable中:rte1是student、rte2带subquery是子查询、rte3是score。
- 调整所有var的varno(从1指向3)、varlevelsup(本例不涉及);还有其他调整本例不涉及。
- 将上层代表子查询的rte2的subquery清空,但rte2不删除。
- 将上层jointree中,指向子查询的rte替换为 子查询中的FromExpr(sno > 2)。

1 待分析场景
drop table student;
create table student(sno int, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);drop table course;
create table course(cno int, cname varchar(10), tno int);
insert into course values(10, 'meth', 1);
insert into course values(11, 'english', 2);drop table teacher;
create table teacher(tno int, tname varchar(10), tsex int);
insert into teacher values(1, 'te1', 1);
insert into teacher values(2, 'te2', 0);drop table score;
create table score (sno int, cno int, degree int);
insert into score values (1, 10, 100);
insert into score values (1, 11, 89);
insert into score values (2, 10, 99);
insert into score values (2, 11, 90);
insert into score values (3, 10, 87);
insert into score values (3, 11, 20);
insert into score values (4, 10, 60);
insert into score values (4, 11, 70);
带子查询的语句:select * from student, (select * from score where sno > 2) s where student.sno = s.sno;
set enable_hashjoin to off;
set enable_mergejoin to off;explain select * from student, (select * from score where sno > 2) s where student.sno = s.sno;QUERY PLAN
---------------------------------------------------------------------Nested Loop (cost=0.00..11278.20 rows=3740 width=58)Join Filter: (student.sno = score.sno)-> Seq Scan on student (cost=0.00..21.00 rows=1100 width=46)-> Materialize (cost=0.00..38.90 rows=680 width=12)-> Seq Scan on score (cost=0.00..35.50 rows=680 width=12)Filter: (sno > 2)||||||||||
||等价写法||
vvvvvvvvvvvexplain select * from student, score where score.sno > 2 and student.sno = score.sno;QUERY PLAN
---------------------------------------------------------------------Nested Loop (cost=0.00..11278.20 rows=3740 width=58)Join Filter: (student.sno = score.sno)-> Seq Scan on student (cost=0.00..21.00 rows=1100 width=46)-> Materialize (cost=0.00..38.90 rows=680 width=12)-> Seq Scan on score (cost=0.00..35.50 rows=680 width=12)Filter: (sno > 2)
pull_up_subqueries做的事情就是帮我们把子查询上拉了,下面分析上拉是如何做的。
select * from student, (select * from score where sno > 2) s where student.sno = s.sno;
Plannerinfo完整结构

注意pull_up_subqueries只对jointree做处理。
void
pull_up_subqueries(PlannerInfo *root)
{/* Top level of jointree must always be a FromExpr */Assert(IsA(root->parse->jointree, FromExpr));/* Recursion starts with no containing join nor appendrel */root->parse->jointree = (FromExpr *)pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,NULL, NULL);/* We should still have a FromExpr */Assert(IsA(root->parse->jointree, FromExpr));
}
2 pull_up_subqueries流程分析
2.1 处理FromExpr下面挂的第一个RANGETBLREF(student表)
FromExpr的第一张表是student表,指向一个rtekind = RTE_RELATION普通表类型,无需做任何处理。

2.2 处理FromExpr下面挂的第二个RANGETBLREF(子查询)
FromExpr的第二个rte是子查询(select * from score where sno > 2) s,可以看到引用的rte结构的subquery指向了内层query:

开始进入pull_up_simple_subquery内部处理,进入路径:

在分析pull_up_simple_subquery前有两个准入条件:
- rte->rtekind == RTE_SUBQUERY
- is_simple_subquery:不全部列举了,其中重要的是子查询不能带有一些特殊的语法:
is_simple_subquery...if (subquery->hasAggs ||subquery->hasWindowFuncs ||subquery->hasTargetSRFs ||subquery->groupClause ||subquery->groupingSets ||subquery->havingQual ||subquery->sortClause ||subquery->distinctClause ||subquery->limitOffset ||subquery->limitCount ||subquery->hasForUpdate ||subquery->cteList)return false;...
2.3 进入pull_up_simple_subquery开始处理子查上拉
第一步:拿到rte指向的子查询的Query树,构造PlannerInfo开始处理。
static Node *
pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,JoinExpr *lowest_outer_join,AppendRelInfo *containing_appendrel)
{Query *subquery;Query *parse = root->parse;PlannerInfo *subroot;subquery = copyObject(rte->subquery);subroot = makeNode(PlannerInfo);subroot->parse = subquery;......
第二步:递归的处理sublink、subquery等。
replace_empty_jointree(subquery);if (subquery->hasSubLinks)pull_up_sublinks(subroot);preprocess_function_rtes(subroot);pull_up_subqueries(subroot);
第三步:开始pull up
到这里subroot就是rte2的subquery子查询的结构还没有任何调整:

/** Adjust level-0 varnos in subquery so that we can append its rangetable* to upper query's. We have to fix the subquery's append_rel_list as* well.*/rtoffset = list_length(parse->rtable); // 2OffsetVarNodes((Node *) subquery, rtoffset, 0);OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0);
parse是上层查询的,上层有两个rtable。因为要把子查询拉平,所以把子查询的varno的指向调整一下,因为是要append到父查询,所以直接加上父查询rte的个数就好了(这里是2)。加完了应该指向父查询rte的3的位置(现在父查询只有两个rte,3位置是空的)。
OffsetVarNodes((Node *) subquery, 2, 0);
- 调整var→varno:1→3。
- 调整rangetblref→rindex:1→3。

/** Upper-level vars in subquery are now one level closer to their parent* than before.*/IncrementVarSublevelsUp((Node *) subquery, -1, 1);IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1);
- 这一步调整的目的:因为varlevelsup=1表示引用上一层的列(相当于距离)这里拉平后,varlevelsup就需要-1了,因为距离少了1。
- 在当前SQL中
select * from student, (select * from score where sno > 2) s where student.sno = s.sno;,开始调整var→varlevelsup字段,注意这个字段表示当前查询中使用了上层的变量,但上面子查询中(select * from score where sno > 2)没有引用上层的任何列,所以子查询中的var→varlevelsup都是0。这一步调整不会有影响。
/** Now append the adjusted rtable entries and their perminfos to upper* query. (We hold off until after fixing the upper rtable entries; no* point in running that code on the subquery ones too.)*/CombineRangeTables(&parse->rtable, &parse->rteperminfos,subquery->rtable, subquery->rteperminfos);
开始把子查询的RTE拷贝到上层,现在子查询里面的varno=3指向就对了。

/** We no longer need the RTE's copy of the subquery's query tree. Getting* rid of it saves nothing in particular so far as this level of query is* concerned; but if this query level is in turn pulled up into a parent,* we'd waste cycles copying the now-unused query tree.*/rte->subquery = NULL;
删除子查询RTE带的Query,注意现在还缺一个条件。

pull_up_simple_subqueryreturn (Node *) subquery->jointree;
返回一个jointree带着条件。

返回去后,在这里把fromlist指向的第二个rte(子查询)换成 上面计算好的jointree。
然后就拉平了。
pull_up_subqueries_recurse...else if (IsA(jtnode, FromExpr)){FromExpr *f = (FromExpr *) jtnode;ListCell *l;Assert(containing_appendrel == NULL);/* Recursively transform all the child nodes */foreach(l, f->fromlist){lfirst(l) = pull_up_subqueries_recurse(root, lfirst(l),lowest_outer_join,NULL);}}
最终效果对比
pullup前 vs pullup后

相关文章:
Postgresql源码(116)提升子查询案例分析
0 总结 对于SQL:select * from student, (select * from score where sno > 2) s where student.sno s.sno; pullup在pull_up_subqueries函数内递归完成,分几步: 将内层rte score追加到上层rtbable中:rte1是student、rte2带…...
CNP实现应用CD部署
上一篇整体介绍了cnp的功能,这篇重点介绍下CNP产品应用开发的功能。 简介 CNP的应用开发,主要是指的应用CD部署的配置管理。 应用列表,用来创建一个应用,一般与项目对应,也可以多个应用对应到一个项目。具体很灵活。…...
kubeadm join 192.168.10.16:6443 --token xxx报错Failed to request cluster-info
1、node节点执行 kubeadm join 192.168.10.16:6443 --token hak4zi.hrib9uv4p62t1uok --discovery-token-ca-cert-hash sha256:4337638eef783ee6a66045ad699722079e071c2dfbaa21e37d3174f04d58ea97 --v2 报错 [discovery] Failed to request cluster-info, will try again: G…...
车载以太网-传输层-TCP
文章目录 TCP协议TCP协议报文格式TCP报文的示例TCP建立连接TCP断开连接TCP协议测试TCP协议 车载以太网TCP协议是一种在车载以太网网络中使用的传输控制协议(TCP)。它是一种面向连接的协议,用于在车辆之间或车辆与基础设施之间传输数据。TCP协议提供了可靠的数据传输,确保数…...
java:简单入门定时任务的几种方式Timer、Quartz、Spring Task
背景 后端的定时任务在许多应用中都扮演着重要的角色,它们可用于处理重复性任务、执行定期操作或处理需要定时触发的任务。以下是一些使用场景的示例: 数据同步:在分布式系统中,不同系统之间经常需要进行数据同步。例如…...
木子-前端-方法标签属性小记(普通jsp/html篇)2023~2024
目录 1、如何在前端页面将base转成存进input的图片格式 2、通过前端页面判断当前使用方式PC端还是手机端的极简易方法 1、如何在前端页面将base转成存进input的图片格式 不是直接存进input里,只是将文件转成对应的格式。 data.picPath是base64编码,我…...
音视频项目—基于FFmpeg和SDL的音视频播放器解析(十七)
介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本…...
使用netty实现WebSocket协议通信
服务器与浏览器之间实现通信,一般都是由浏览器发起http请求,服务端对http请求进行响应,要实现服务端主动向浏览器推送数据,一般采用的方案都是websocket主动推送,或者前端实现轮询方式拉取数据,轮询方式多少…...
uniapp开发小程序,包过大解决方案
1、首先和大家说一下 微信小程序 主包限制不能超过2M 分包一共不能超过8M 然后具体解决优化步骤如下, 将主包进行分包 在pages.json 下subPackages里面进行配置分包 分包配置完 配置过的文件都需要进行修改对应的路径 2 、 在运行的时候 一定要勾选 压缩代码 有…...
Go语言中string与byte转换
简介 string与byte的转换是最常见的一种,通常我们会使用强转方式,但其实还有另一种更加高效的方式,本文会演示两种转换方式。 普通转换 func main() {fmt.Println([]byte("abcd"))fmt.Println(string([]byte{1, 2, 3})) }输出 […...
机器学习8:在病马数据集上进行算法比较(ROC曲线与AUC)
ROC曲线与AUC。使用不同的迭代次数(基模型数量)进行 Adaboost 模型训练,并记录每个模型的真阳性率和假阳性率,并绘制每个模型对应的 ROC 曲线,比较模型性能,输出 AUC 值最高的模型的迭代次数和 ROC 曲线。 …...
70. 爬楼梯 --力扣 --JAVA
题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 解题思路 通过对爬楼梯进行分解,爬到当前台阶的方式分为两种,即由上一个台阶通过爬1和上两个台阶爬2,同公…...
体感互动游戏VR游戏AR体感游戏软件开发
随着科技的不断发展,体感互动游戏正逐渐成为游戏行业的一个重要趋势。这类游戏通过利用传感器、摄像头和运动控制器等技术,使玩家能够通过身体动作与游戏进行实时互动,极大地提升了娱乐体验。 1. 游戏设计与互动元素 体感互动游戏的核心在于…...
计算3个点的6种分布在平面上的占比
假设平面的尺寸是6*6,用11的方式构造2,在用21的方式构造3 2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 3 3 3 x 3 3 2 2 2 1 2 2 2 2 2 1 2 2 在平面上有一个点x,11的操作吧平面分成了3部分2a1,2a…...
【香橙派】实战记录1——简介及烧录 Linux 镜像
文章目录 一、简介1、参数2、结构3、其他配件4、下载资料 二、基于 Windows PC 将 Linux 镜像烧写到 TF 卡的方法1、使用 balenaEtcher 烧录 Linux 镜像的方法2、效果 一、简介 Orange Pi Zero 3 香橙派是一款开源的单板卡片电脑, 新一代的arm64开发板,…...
redis之高可用
(一)redis之高可用 1、在集群当中有一个非常重要的指标,提供正常服务的时间的百分比(365天)99.9% 2、redis的高可用的含义更加广泛,正常服务是指标之一,数据容量的扩展、数据的安全性 3、在r…...
使用 Core Tools 在本地开发 Azure Functions
学习模块 使用 Core Tools 在本地创建和运行 Azure Functions - Training | Microsoft Learn 文档 使用 Core Tools 在本地开发 Azure Functions | Microsoft Learn GitHub - Azure/azure-functions-core-tools: Command line tools for Azure Functions 其它 安装适用于 A…...
Java零基础——Spring篇
1.Spring框架的介绍 1.1 传统的项目的架构 在传统的项目中,一般遵循MVC开发模型。 (1) view层与用户进行交互,显示数据或者将数据传输给view层。 (2) 在controller层创建service层对象,调用service层中业务方法。 (3) 在service层创建dao…...
jenkins清理缓存命令
def jobName "yi-cloud-operation" //删除的项目名称 def maxNumber 300 // 保留的最小编号,意味着小于该编号的构建都将被删除 Jenkins.instance.getItemByFullName(jobName).builds.findAll { it.number < maxNumber }.each { it.delet…...
什么是深度学习
一、深度学习的发展历程 1.1 Turing Testing (图灵测试) 图灵测试是人工智能是否真正能够成功的一个标准,“计算机科学之父”、“人工智能之父”英国数学家图灵在1950年的论文《机器会思考吗》中提出了图灵测试的概念。即把一个人和一台计算机分别放在两个隔离的房…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
