GPDB-疑难杂症-PlaceHolderVar

news2024/11/18 12:22:26

GPDB-疑难杂症-PlaceHolderVar

从GPDB5升级到GPDB6时,遇到以往可以执行的SQL不能执行了。报错:PlaceHolderVar found where not expected!语法不兼容了?

postgres=# CREATE TABLE t1( id1 int) WITH   (appendonly=true,  compresstype=none,  blocksize=8192) DISTRIBUTED BY (id1);
postgres=# CREATE TABLE t4(id1 int, id2 text ) DISTRIBUTED BY (id1);
postgres=# set optimizer=off;


postgres=# explain analyze select count(t1.id1),count(t4id2) tid2 from t1 left join (select id1,format('%s',id2)t4id2 from t4) t31 on t1.id1=t31.id1 having count(t4id2) is not null ;
ERROR: PlaceHolderVar found where not expected (var.c:757)

向GPDB提交了bug:https://github.com/greenplum-db/gpdb/issues/16193

1、为什么GPDB5可以执行,GPDB6报错

找到GPDB6代码报错位置:pull_var_clause_walker函数中

471ecdb8a314dbd7700935a4b8ee4ed6.png

可知,由入参context的phbehavior标签为PVC_REJECT_PLACEHOLDERS决定了报错。

那么就需要知道这个标签在什么地方设置的,接着通过gdb跟踪,向堆栈上层追溯,可以总结:在函数make_subplan_tlist中设置了该标签

73f5cddde644254beedac934bc90bf81.png

接着,对比下GPDB5的代码,看下有何不同:

236003e092ff97bc3b42ce916be686ad.png

pull_var_clause函数仅2个入参,没有GPDB6中的第3个入参,并且pull_var_clause_walker中也没有对应报错的地方:

22e3f9ebec48c8c82556c662843ca230.png

好了,到此,明白GPDB5和GPDB6代码的区别,了解到为什么仅GPDB6会报错了。至于为什么会有这个标签呢?

我们找到提交patch的commit:PG9.0.23内核修复bug时引入了该标签

Fix estimate_num_groups() to not fail on PlaceHolderVars, per report from Stefan Kaltenbrunner. The most reasonable behavior (at least for the near term) seems to be to ignore the PlaceHolderVar and examine its argument instead.  In support of this, change the API of pull_var_clause() to allow callers to request recursion into PlaceHolderVars.  Currently estimate_num_groups() is the only customer for that behavior, but where there's one there may be others.

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1d97c19a0f748e94b9582dcbaec33ad74be062f9

2、GPDB6报错的机制

GPDB6中报错的机制是什么?为什么会有PlaceHolderVar,他是干什么的?

2.1以下面例子说明为什么使用PlaceHolderVar

yzs=#select sno,sname,ssex from student;
  sno |  sname   | ssex
------|----------|------
    1 | zhangsan |  1
    2 | lisi     |  1
(2 rows)


yzs=#select sno,cno,degree from student;
  sno |  cno  | degree
------|-------|-------
    1 |   1   |   36
(1 row)

正常的结果:

yzs=#select * from student st left join (select sno,coalesce(degree,60) from score) sc on st.sno=sc.sno;
  sno |  sname   | ssex  | sno | coalesce
------|----------|-------|-----|---------
   2  | lisi     |  1    |     |         
   1  | zhangsan |  1    |  1  |   36    
(2 rows)

若假设,上述子查询被强制提升,并不使用PlaceHolderVar替代,则会出现下面的结果:

yzs=#select * from student st left join (select sno,coalesce(degree,60) from score) sc on st.sno=sc.sno;
  sno |  sname   | ssex  | sno | coalesce
------|----------|-------|-----|---------
   2  | lisi     |  1    |     |   60     
   1  | zhangsan |  1    |  1  |   36    
(2 rows)

可以看到,进行子查询提升后,若不使用PlaceHolderVar替代,结果就不一样了。coalesce函数对NULL进行了处理,表示若为NULL,则使用60替代,可以看到,对left join结果中的NULL做了处理,被强制提升后,逻辑树就不一样了。为了使得逻辑树等价,就使用PlaceHolderVar来替代原来的节点,做下标记,其实该结构封装了原来的节点。

注:因为提升子查询后,PG会把子查询的关系并入FROM-LIST中,这样关系个数就会增加,从而增加join路径,以便提供更多join路径,有更多选择。

2.2提升子查询的条件

简单子查询会被提升,那么什么是简单子查询?简单子查询指简单的查询语句或者join语句组成,由函数is_simple_subquery判断:

bool
is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte,
           JoinExpr *lowest_outer_join)
{
  if (!IsA(subquery, Query) ||
    subquery->commandType != CMD_SELECT ||
    subquery->utilityStmt != NULL)
    elog(ERROR, "subquery is bogus");


  if (subquery->setOperations)
    return false;
  if (subquery->hasAggs ||
    subquery->hasWindowFuncs ||
    subquery->groupClause ||
    subquery->havingQual ||
    subquery->windowClause ||
    subquery->sortClause ||
    subquery->distinctClause ||
    subquery->limitOffset ||
    subquery->limitCount ||
    subquery->hasForUpdate ||
    subquery->cteList ||
    root->parse->cteList)
    return false;
  if (rte && rte->security_barrier)
    return false;
  if (rte && rte->lateral){
    bool    restricted;
    Relids    safe_upper_varnos;
    if (lowest_outer_join != NULL){
      restricted = true;
      safe_upper_varnos = get_relids_in_jointree((Node *) lowest_outer_join, true);
    }else{
      restricted = false;
      safe_upper_varnos = NULL;  /* doesn't matter */
    }
    if (jointree_contains_lateral_outer_refs((Node *) subquery->jointree, restricted, safe_upper_varnos))
      return false;
    if (lowest_outer_join != NULL){
      Relids    lvarnos = pull_varnos_of_level((Node *) subquery->targetList, 1);
      if (!bms_is_subset(lvarnos, safe_upper_varnos))
        return false;
    }
  }
  if (expression_returns_set((Node *) subquery->targetList))
    return false;
  if (contain_volatile_functions((Node *) subquery->targetList))
    return false;
  if (subquery->jointree->fromlist == NIL)
    return false;
  return true;
}

总结起来有6点

1)顶层操作(语法树的树顶)不是集合操作(UNION/INTERSECT/EXECEPT)

2)子查询中不含有SORT、LIMIT、CTE-LIST

3)子查询中不能有更新操作

4)子查询的目标列不能是聚合函数类型

5)子查询目标列不能含有易失函数

6)子查询存在连接条件

2.3什么时候使用PlaceHolderVar替代

e48589da0a534e7900fcf8238f049bf4.png

我们看pullup_replace_vars_callback函数:

824827ae7fa71ecd9f3e2cd824046a12.png

make_placeholder_expr函数创建PlaceHolderVar节点。创建该节点的条件为蓝框内条件。主要看下被提升的节点需要是一个非严格函数

2.3什么地方导致拒绝PlaceHolderVar

782ae0e0a63351b7900208707854cae8.png

havingQual中若有PlaceHolderVar,则拒绝。

2.3什么是非严格的函数

参数是NULL,则输出也是NULL则是严格的。在定义函数时可以指定strict。当然若函数是严格的,还需继续判断参数。

909f70c7d0de982dc2ed8c598940c3e7.png

具体哪些是严格的,可以参见contain_nonstrict_functions_walker函数。

3、总结

1)子查询是一个join,是一个简单子查询。并且函数format是一个非严格函数。所以该子查询需要提升。为了保证逻辑等价,会使用PlaceHolderVar替代原format函数的表达式节点

2)havingQual条件即having count(t4id2) is not null,t4id2是子查询中的非严格函数。

3)GPDB6对havingQual有了限制,禁止该表达式中出现PlaceHolderVar节点

4)可以通过不使用havingQual以及修改函数strict属性以及参数类型来规避,或者通过改造使之不满足子查询提升的6个条件来临时避免该问题。

5)当然,感觉这是一个bug。官方也在提出bug后做出了修复:

https://github.com/greenplum-db/gpdb/pull/16240

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/898916.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

@Repeatable的作用以及具体如何使用

文章目录 1. 前言2. 先说结论3. 案例演示 1. 前言 最近无意看到某些注解上有Repeatable,出于比较好奇,因此稍微研究并写下此文章。 2. 先说结论 Repeatable的作用:使被他注释的注解可以在同一个地方重复使用。 具体使用如下: T…

shell脚本文本 三剑客AWK

TOC 一.AWK工具介绍 AWK是一种处理文本文件的语言,是一个强大的文本分析工具可以在无交互的模式下实现复杂的文本操作相较于sed常作用于一整行的处理,awk则比较倾向于一行当中分成数个字段来处理,因为awk相当适合小型的文本数据 1.1AWK命令…

听GPT 讲Prometheus源代码--discovery

Prometheus是一个开源的系统监控和警报工具包,以下是Prometheus源代码中一些主要的文件夹及其作用: cmd/:这个目录包含了Prometheus主要的命令行工具,如prometheus/,promtool/等。每个子目录都代表一个可执行的命令行应…

Unsafe upfileupload

文章目录 client checkMIME Typegetimagesize 文件上传功能在web应用系统很常见,比如很多网站注册的时候需要上传头像、上传附件等等。当用户点击上传按钮后,后台会对上传的文件进行判断 比如是否是指定的类型、后缀名、大小等等,然后将其按…

高德地图开发者平台Python应用实践:快速入门周边商业环境信息查询

高德地图开发平台提供了丰富的API接口,可以方便地进行地图数据的开发和分析。在商业分析数据采集中,使用高德地图开发平台的周边查询功能可以快速获取周边商圈、小区等信息,为商业决策提供数据支持。 针对您的需求,我建议采用以下…

stream.map return

出现以下告警信息 Statement lambda can be replaced with expression lambda less... (CtrlF1) This inspection reports lambda expressions with code block bodies when expression-style bodies can be used 将 List<StudentDetailDto> studentDetailDtoList link…

防丢器Airtag国产版

Airtag是什么&#xff1f; AirTag是苹果公司设计的一款定位神奇&#xff0c;它通过一款纽扣电池进行供电&#xff0c;即可实现长达1-2年的关键物品的定位、查找的功能。 按照苹果公司自己的话说—— 您“丢三落四这门绝技&#xff0c;要‍失‍传‍了”。 AirTag 可帮你轻松追…

Objectarx 2021使用vs2019生成报错 /RTCc rejects conformant code

error C2338: /RTCc rejects conformant code错误解决 使用VS2019/VS2022生成项目报错 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C1189 #error: /RTCc rejects conformant code, so it is not supported by the C Standard Library. Either remove this compiler opti…

网络安全---webshell实践

一、首先环境配置 1.上传文件并解压 2.进入目录下 为了方便解释&#xff0c;我们只用两个节点&#xff0c;启动之后&#xff0c;大家可以看到有 3 个容器&#xff08;可想像成有 3 台服务器就成&#xff09;。 二、使用蚁剑去连接 因为两台节点都在相同的位置存在 ant.jsp&…

vue2.6升级vue2.7(panjiachen升级指南)vue-cli5多页面应用升级的坑

vue2.7升级指南 vue2.7升级指南 之前的架子使用的是 panjiachen&#xff0c;使用的是 vue2.6.14&#xff0c;现在升级为 vue2.7.x 升级vue/cli vue upgrade 这里推荐使用 vue upgrade 命令自动升级 # 确保安装全局 vue/cli $ npm install -g vue/cli $ vue upgradeWARN Th…

Vue 2 自定义指令

Vue 2自定义指令 Vue自定义指令允许我们在DOM元素上添加自己想要的行为来扩展Vue的功能。 一个自定义指令需要一个名称和一个定义对象。在定义对象中&#xff0c;你可以使用一些钩子函数来控制指令的行为&#xff1a; bind&#xff1a;在指令被绑定到元素上时使用&#xff0…

基于IMX6ULLmini的linux裸机开发系列七:中断处理流程

中断上下文 cpu通过内核寄存器来运行指令并进行数据的读写处理的&#xff0c;它在进入中断前一个时刻的具体值&#xff0c;称为中断上下文 中断上下文是指CPU在进入中断之前保存的寄存器状态和其他相关信息。当CPU接收到中断请求时&#xff0c;它会保存当前正在执行的指令的状…

广州华锐互动:3D数字孪生开发编辑器助力企业高效开发数字孪生应用

3D数字孪生开发编辑器是一种新兴的技术&#xff0c;它可以帮助企业更好地管理和维护其物联网设备。这些工具可以帮助企业实现对设备的实时监控、故障排除和优化&#xff0c;从而提高生产效率和降低成本。 数字孪生系统是一种将物理世界与数字世界相结合的技术&#xff0c;它可以…

Python web实战之细说 Django 的单元测试

关键词&#xff1a; Python Web 开发、Django、单元测试、测试驱动开发、TDD、测试框架、持续集成、自动化测试 大家好&#xff0c;今天&#xff0c;我将带领大家进入 Python Web 开发的新世界&#xff0c;深入探讨 Django 的单元测试。通过本文的实战案例和详细讲解&#xff…

【C#学习笔记】C#特性的继承,封装,多态

文章目录 封装访问修饰符静态类和静态方法静态构造函数 继承继承原则sealed修饰符里氏替换原则继承中的构造函数 多态接口接口的实例化 抽象类和抽象方法抽象类和接口的异同 虚方法同名方法new覆盖的父类方法继承的同名方法 运行时的多态性编译时的多态性 照理继承封装多态应该…

Chapter 14: Using Web Services | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介Python and Web ServicesUsing Web ServiceseXtensible Markup Language - XMLParsing XMLJavaScript Object Notation - JSONParsing JSONApplication Programming InterfacesSecurity and API usageGlossary Python for Everybody Expl…

all in one之安装docker、青龙和青龙卸载更新(第三章)

安装docker和青龙 ubuntu安装docker 参考教程0 参考教程1 参考教程2 apt-get install docker-ce docker-ce-cli containerd.io更改docker国内源 一、国内加速地址 Docker中国区官方镜像 https://registry.docker-cn.com网易 http://hub-mirror.c.163.comustc https://d…

Monitor.Analog采集软件详细设计说明

Monitor.Analog模拟量采集软件概要设计&#xff1a; 1. 引言&#xff1a; 模拟量采集软件的目标是实现对模拟量信号的采集、处理和展示。该软件旨在提供一个用户友好的界面&#xff0c;允许用户配置采集参数、实时监测模拟量信号&#xff0c;并提供数据分析和导出功能。 2. 功能…

多功能数据采集主机——数据集中采集

无论是机房监控系统还是仓库监控系统&#xff0c;又或者是其他大型场所的监控系统都会用的一个设备——多功能数据采集主机。 在环境监控系统中会用到温湿度、水浸、烟感等多种传感器&#xff0c;时时监测周围环境&#xff0c;这些传感器都可以通过多功能数据采集主机&#xff…

学习笔记230816---vue项目中使用第三方组件{el-dropdown}如何设置禁止事件功能

问题描述 使用第三方组件elementui&#xff0c;在导航菜单el-menu的el-menu-item中嵌入一个下拉菜框el-dropdown。点击...icon弹出下拉菜单el-dropdown-menu&#xff0c;那么这时会触发事件冒泡&#xff0c;el-menu-item菜单项的点击事件也会触发。 解决方法 阻止事件冒泡&am…