分析openGauss包内集合类型的实现方法

news2024/9/21 14:31:00

前言

Oracle中集合类型覆盖了Postgresql数组的功能,在Oracle用户中时非常常用的。

尤其是包内定义的集合类型,在SPEC定义后即可直接使用,scope也只在包在生效,使用非常灵活。

开源PG因为有数组没有实现这部分语法,下面对openGauss的包内集合类型实现方法做一些分析。

总结

  • 构造类型:plpgsql_build_tableType
  • 构造变量:build_array_type_from_elemtype
  • 一层嵌套var中没有nesttable,a(1) := 10的右值直接存入a的_int数组中。
  • 两层嵌套var中有nesttable,b(1)(1) := 10的右值存入var→vd_nesttable中。
  • 三层嵌套var中有nesttable,b(1)(1)(1) := 10的右值存入var→vd_nesttable→vd_nesttable中。

底层用的还是PG的数组。核心逻辑都在evalSubsciptsNested函数附近。

用例

drop schema if exists pkg_val_1 cascade;
drop schema if exists pkg_val_2 cascade;

create schema pkg_val_1;
create schema pkg_val_2;

set current_schema = pkg_val_2;
set behavior_compat_options='allow_procedure_compile_check';

--test package val assign
create or replace package pck1 is
type r3 is table of float;
vc r3;
type r4 is table of r3;
vd r4;
type r5 is table of r4;
ve r5;
end pck1;
/
create or replace package body pck1 is
end pck1;
/

create or replace package pck2 is
ve int;
procedure p1;
end pck2;
/
create or replace package body pck2  is
procedure p1 as 
begin
pck1.vc(1) := 1;
pck1.vc(2) := 2 + pck1.vc(1);
pck1.vd(1) := pck1.vc;
pck1.vd(2) := pck1.vc;
pck1.ve(1) := pck1.vd;
raise info '% % % %', pck1.vc(1), pck1.vd(1)(1), pck1.vd(1)(1), pck1.ve(1)(1)(2);
end;
end pck2;
/
call pck2.p1();

3 pck1编译

create or replace package pck1 is
type r3 is table of float;
vc r3;
type r4 is table of r3;
vd r4;
type r5 is table of r4;
ve r5;
end pck1;
/

堆栈

CreatePackageCommand
	PackageSpecCreate
		plpgsql_package_validator
			plpgsql_pkg_compile
				do_pkg_compile
					plpgsql_yyparse

变量列表和PG的差异

区别openGaussPostgreSQL
变量数u_sess->plsql_cxt.curr_compile_context->plpgsql_nDatumsplpgsql_nDatums
变量列表u_sess->plsql_cxt.curr_compile_context->plpgsql_Datumsplpgsql_Datums
包变量数u_sess->plsql_cxt.curr_compile_context->plpgsql_pkg_nDatums
包变量列表u_sess->plsql_cxt.curr_compile_context->plpgsql_Datums

怎么区分编包函数编函数?

u_sess->plsql_cxt.curr_compile_context->plpgsql_curr_compile

plpgsql_curr_compile非空则是编函数,使用
  u_sess->plsql_cxt.curr_compile_context->plpgsql_nDatums
  u_sess->plsql_cxt.curr_compile_context->plpgsql_Datums

plpgsql_curr_compile空则是编包函数,使用
  u_sess->plsql_cxt.curr_compile_context->plpgsql_pkg_nDatums
  u_sess->plsql_cxt.curr_compile_context->plpgsql_Datums
  
注意变量列表是一套plpgsql_Datums

命名空间区别:

openGaussPostgreSQL
命名空间区别u_sess->plsql_cxt.curr_compile_context->ns_topns_top
命名空间结构体区别int itemtype;PLpgSQL_nsitem_type itemtype;
int itemno;int itemno;
struct PLpgSQL_nsitem* prev;struct PLpgSQL_nsitem *prev;
char name[FLEXIBLE_ARRAY_MEMBER];char name[FLEXIBLE_ARRAY_MEMBER];
char* pkgname;
char* schemaName;

3.1 pck1编译结果

  • 语法解析时走plpgsql_build_tableType函数构造类型。
  • 语法解析时走build_array_type_from_elemtype构造集合类型。

从编译结果来看:

  1. 一层嵌套表类型,只需要自己的datatype是_float8数组类型就ok了,参考下图中变量"vc"。
  2. 两层嵌套表类型,var会创建nest_table变量指向内层数组类型,参考下图中变量"vd"。
  3. 三层嵌套表类型,var会创建nest_table变量→nest_table变量,两层变量记录内层数组类型,参考下图中变量"ve"。

请添加图片描述

3.2 调用者pck2执行结果

create or replace package pck2 is
ve int;
procedure p1;
end pck2;
/
create or replace package body pck2  is
procedure p1 as 
begin
pck1.vc(1) := 1;
pck1.vc(2) := 2 + pck1.vc(1);
pck1.vd(1) := pck1.vc;
pck1.vd(2) := pck1.vc;
pck1.ve(1) := pck1.vd;
raise info '% % % %', pck1.vc(1), pck1.vd(1)(1), pck1.vd(1)(1), pck1.ve(1)(1)(2);
end;
end pck2;
/

call pck2.p1();

pck1.vc(1) := 1;

PLpgSQL_stmt_assign

PLpgSQL_stmt_assign = { 
  cmd_type = 1, 
  lineno = 3, 
  varno = 11, 
  expr = {dtype = 7, dno = 0, query = "SELECT 1"}, 
  sqlString = "pck1.vc(1) := 1;"
}

varno = 11 PLpgSQL_tableelem

PLpgSQL_tableelem = {
  dtype = 6, 
  dno = 11, 
  ispkg = false, 
  subscript = {query = "SELECT 1"}, 
  tableparentno = 10, 
  parenttypoid = 0, 
  parenttypmod = 0, 
  tabletypoid = 0, 
  tabletypmod = 0, 
  tabletyplen = 0,
  elemtypoid = 0, 
  elemtyplen = 0, 
  elemtypbyval = false, 
  elemtypalign = 0 '\000', 
  assignattrno = -1, 
  pkg_name = 0x0, 
  pkg = 0x0
}

运行时exec_assign_expr

  1. 计算右值:exec_eval_expr得到1。
  2. 结果赋值:exec_assign_value,赋值中走PLPGSQL_DTYPE_TABLEELEM分支。通过tableparentno=10找到数组结构,然后往数组结构中赋值即可。

pck1.vc(2) := 2 + pck1.vc(1);

PLpgSQL_stmt_assign

PLpgSQL_stmt_assign = { 
  cmd_type = 1, 
  lineno = 4, 
  varno = 12, 
  expr = {dtype = 7, dno = 0, query = "SELECT 2 + pck1.vc[1]"}, 
  sqlString = "pck1.vc(2) := 2 + pck1.vc(1);"
}

varno = 12 PLpgSQL_tableelem

PLpgSQL_tableelem = {
  dtype = 6, 
  dno = 12, 
  ispkg = false, 
  subscript = {query = "SELECT 2"}, 
  tableparentno = 10, 
  parenttypoid = 0, 
  parenttypmod = 0, 
  tabletypoid = 0, 
  tabletypmod = 0, 
  tabletyplen = 0,
  elemtypoid = 0, 
  elemtyplen = 0, 
  elemtypbyval = false, 
  elemtypalign = 0 '\000', 
  assignattrno = -1, 
  pkg_name = 0x0, 
  pkg = 0x0
}

运行时:和上述类似。

pck1.vd(1) := pck1.vc;

PLpgSQL_stmt_assign

PLpgSQL_stmt_assign = { 
  cmd_type = 1, 
  lineno = 5, 
  varno = 14, 
  expr = {dtype = 7, dno = 0, query = "SELECT pck1.vc"}, 
  sqlString = "pck1.vd(1) := pck1.vc;"
}

varno = 14 PLpgSQL_tableelem

PLpgSQL_tableelem = {
  dtype = 6, 
  dno = 14, 
  ispkg = false, 
  subscript = {query = "SELECT 1"}, 
  tableparentno = 13, 
  parenttypoid = 0, 
  parenttypmod = 0, 
  tabletypoid = 0, 
  tabletypmod = 0, 
  tabletyplen = 0,
  elemtypoid = 0, 
  elemtyplen = 0, 
  elemtypbyval = false, 
  elemtypalign = 0 '\000', 
  assignattrno = -1, 
  pkg_name = 0x0, 
  pkg = 0x0
}

运行时:

  1. 右值计算:exec_eval_expr拿到vc数组。
  2. 左值计算:exec_assign_value进入PLPGSQL_DTYPE_TABLEELEM。开始找数组target
    • 1 找到的目标数组是{dtype = 0, dno = 3, refname = 'vd'},然后找到nested_table指向的{dtype = 0, dno = 9, ispkg = true, refname = "vd_nest"}
    • 2 这里会递归进入exec_assign_value,这次的目标是dno=9的vd_nest。
    • 3 把值赋给dno=9的vd_nest。

总结

在这里插入图片描述

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

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

相关文章

超级广场效果的耳机放大器音响电路设计

用头戴式耳机,尤其是小型耳机听音乐,总感到音乐味不够足,在低频段的效果更差。因此用本机增强耳机的低频特性,并采用立体声反相合成的办法,加上内藏简易矩阵环绕声电路,能获得强劲的低音和在较宽的范围内展…

Windows mingw64 最简易 安装配置

其实挺简单一件事 很多教程都搞复杂了 自己写一个 只需要两步 1. 下载压缩包并解压 2. 配置环境变量 (1). GitHub 下载地址 Releases niXman/mingw-builds-binaries GitHub 如果GitHub下载太慢可以来这里加速 或者用地址2 GitHub Proxy 代理加速 (ghproxy.com) (2). 下…

-Xss1m / -XX:ThreadStackSize=512k

-Xss / -XX:ThreadStackSize指定线程最大栈空间jdk1.4里默认的栈大小是256KBjdk1.5里默认的栈大小为1M配置JVM启动参数:-Xmx20m -Xms20m -Xss1m -XX:PrintCommandLineFlags-XX:ConcGCThreads3 -XX:G1ConcRefinementThreads13 -XX:GCDrainStackTargetSize64 -XX:Init…

学习grpc

Grpc简介: gRPC是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言,能够基于语言自动生成客户端和服务端…

软负载Nginx详细配置及使用案例

Nginx使用与配置 什么是nginx Nginx 是一个高性能的HTTP和反向代理服务,也是一个IMAP/POP3/SMTP服务。 处理响应请求很快高并发连接低的内存消耗具有很高的可靠性高扩展性热部署 master 管理进程与 worker 工作进程的分离设计,使得 Nginx 具有热部署的…

Quartz表达式在线生成器

Quartz Cron表达式生成器 - devTest.run Quartz是一款高效的定时任务调度框架,由于其稳定性,高可用性和灵活性,Quartz已成为Java企业级开发中应用最为广泛的定时任务调度框架之一。 Quartz的主要特点包括:可配置的作业触发器&…

学C的第二十五天【指针的进阶】

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com) 接上期: 学C的第二十四天【练习:1. 打印菱形;2. 打印自幂数;3. 求Snaaa..n项之和;4. 喝汽水问题;5. 调整数组使奇数位于偶数前面&…

第三章——处理数据

面向对象编程(OOP)的本质是设计并扩展自己的数据类型。设计自己的数据类型就是让类型与数据匹配。在创建自己的类型之前必须先了解C内置的类型。 内置的C类型分为两组:基本类型和复合类型 简单变量 程序通常都需要存储信息,为把…

git commit history导出

git log --after"2022-1-1" --dateshort --prettyformat:"%H","%an","%ae","%ad","%s" --shortstat --no-merges再简洁一点 git log --after"2022-4-1" --dateshort --prettyformat:%H,%an,%ae,%ad,%…

Network Neuroscience:整个生命周期的功能连接体指纹

导读 随着年龄的增长,人脑功能结构发生了系统性的变化。然而,功能连接(FC)作为一种检测独特“连接体指纹”的强大特征,使个体能够在同龄人中被识别出来。虽然已在年轻人样本中观察到这种指纹,但该方法在整个生命周期内的可靠性尚…

ACL 2023|大模型时代,自然语言领域还有什么学术增长点?

国际计算语言学年会(Annual Meeting of the Association for Computational Linguistics,简称 ACL)是自然语言处理(NLP)领域的顶级国际会议,ACL 2023 将于2023年7月9-14日在加拿大多伦多举行。随着人工智能…

电脑高手的选择:为何只需一款杀毒软件?

对于电脑高手来说,保护计算机免受恶意软件和病毒的侵害至关重要。然而,有些人可能认为安装多个杀毒软件能够提供更优质的防护能力。但事实上,电脑高手通常只选择安装一款杀毒软件,这其中包含着一定的原因和考虑。本文将探讨为什么…

Java版本企业电子招投标采购系统源码功能模块功能描述+数字化采购管理 采购招投标

功能模块: 待办消息,招标公告,中标公告,信息发布 描述: 全过程数字化采购管理,打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力,为外部…

基于Python+MySQL所写的医院管理系统

点击以下链接获取源码资源: https://download.csdn.net/download/qq_64505944/87985429?spm1001.2014.3001.5503 目录 摘要 I 1 需求分析 1 1.1 任务描述 1 1.2 需求分析的过程 1 1.3 业务需求 2 1.4 功能描述 2 2 总体设计 3 2.1 系统开发环境 3 2.2 系统功能流…

【云原生】Pod 的生命周期

Pod 的生命周期 本文讲解的是 Kubernetes 中 Pod 的生命周期,包括生命周期的不同阶段、存活和就绪探针、重启策略等。 Pod phase Pod 的 status 字段是一个 PodStatus 对象,PodStatus中有一个 phase 字段。 Pod 的相位(phase)…

DOM事件机制(事件流、事件委托、事件类型)以及BOM

HTML DOM 允许 JavaScript 对 HTML 事件作出反应。JavaScript 能够在事件发生时执行,比如当用户点击某个 HTML 元素时。 JavaScript与HTML之间的交互是通过事件实现的。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。 虽然 ECMAScript 把浏览器对象模型&…

【面试题28】什么是PHP-FPM?它与PHP和Nginx有什么关系

文章目录 一、前言二、什么是PHP-FPM?三、PHP-FPM的生命周期3.1 启动阶段3.2 初始化阶段3.3 请求处理阶段3.4 关闭阶段 四、PHP-FPM与PHP的关系五、PHP-FPM与Nginx的通信方式六、总结 一、前言 本文已收录于PHP全栈系列专栏:PHP面试专区。 计划将全覆盖P…

【视觉SLAM入门】2.旋转--李群与李代数

"川泽纳污" 0. 一个例子1. 群和李群2. 李代数2.1 推导和性质2.2 s o ( 3 ) \mathscr{so(3)} so(3) 和 s e ( 3 ) \mathscr{se(3)} se(3)2.3 计算李代数的幂 e x p ( ϕ \;exp(\phi exp(ϕ^ ) ) )2.4 李代数乘法2.5 从李代数乘法到导数:2.5.1 直接求导2.…

100天精通Golang(基础入门篇)——第14天:深入解析Go语言函数->从概念到实践,助您精通基础知识!(基础)

🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《I…