Postgresql中不支持事务块中调用plpgsql回滚(多层exception、事务块有检查点)

news2025/6/8 9:28:29

前言

  • Postgresql使用子事务来实现EXCEPTION的功能,即在进入EXCEPTION的存储过程前,会自动起一个子事务,如果发生了异常,则自动回滚子事务,达成EXCEPTION的效果。
  • 那么如果在事务块内本身就带子事务(SAVEPOINT),在调用有EXCEPTION的存储过程,处理流程会有一些复杂。
  • 目前下面代码中的rollback会直接报错不支持,但报错被exception掩盖了,所以后续的行为不再分析了。只是做一些记录。

案例

drop table if exists tbl221;
create table tbl221(a int);

CREATE or replace PROCEDURE p_transaction()
LANGUAGE plpgsql
AS $$
DECLARE
    carry float;
BEGIN
    INSERT INTO tbl221 (a) VALUES (1);
    BEGIN
    	rollback;
    EXCEPTION WHEN others THEN
    	RAISE NOTICE 'in exception exception';
    	INSERT INTO tbl221 (a) VALUES (100);
    END;
    INSERT INTO tbl221 (a) VALUES (4);
EXCEPTION WHEN others THEN
    RAISE NOTICE 'in exception';
    INSERT INTO tbl221 (a) VALUES (10);
    INSERT INTO tbl221 (a) VALUES (30);
    INSERT INTO tbl221 (a) VALUES (40);
END;
$$;

CREATE or replace PROCEDURE p_transaction_caller()
LANGUAGE plpgsql
AS $$
DECLARE
    carry float;
BEGIN
	call p_transaction();
EXCEPTION WHEN others THEN
	RAISE NOTICE 'in caller exception';
END;
$$;


begin;
INSERT INTO tbl221 (a) VALUES (100);
savepoint sp1;
savepoint sp2;
CALL p_transaction_caller();
select * from tbl221;

1 回滚前SPI的connectSubid与子事务subTransactionId对应关系

分析CALL p_transaction();的执行流程:

进入exec_stmt_rollback时::重要::

_SPI_stack[0]->connectSubid = 3
_SPI_stack[1]->connectSubid = 4

CurrentTransactionState->subTransactionId = [1,2,        3,        4,     5,     6]
                                             | |         |         |      |      |
                                          base savepoint savepoint except except except
                                                                   |      |       |
                                                 p_transaction_caller     p_transaction
  • connectSubid:1是基础事务,2、3是两个检查点的事务,4是p_transaction_caller产生的子事务。第一次CALL在事务块内,所以使用子事务ID3,第二次CALL在p_transaction_caller的子事务内,所以使用子事务ID4。

当前堆栈

#0  exec_stmt_rollback (estate=0x7ffd0f874a40, stmt=0x28bc3d0) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:4964
#1  0x00007f29adcff1b5 in exec_stmts (estate=0x7ffd0f874a40, stmts=0x28bc3f0) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:2109
#2  0x00007f29adcfe8a8 in exec_stmt_block (estate=0x7ffd0f874a40, block=0x28e73a0) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1783
#3  0x00007f29adcfeefa in exec_stmts (estate=0x7ffd0f874a40, stmts=0x28bc330) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:2005
#4  0x00007f29adcfe8a8 in exec_stmt_block (estate=0x7ffd0f874a40, block=0x28e7eb0) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1783
#5  0x00007f29adcfeefa in exec_stmts (estate=0x7ffd0f874a40, stmts=0x28e7f50) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:2005
#6  0x00007f29adcfecd1 in exec_stmt_block (estate=0x7ffd0f874a40, block=0x28e7f00) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1932
#7  0x00007f29adcfe4d6 in exec_toplevel_block (estate=0x7ffd0f874a40, block=0x28e7f00) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1623
#8  0x00007f29adcfc45d in plpgsql_exec_function (func=0x27fda28, fcinfo=0x7ffd0f874d70, simple_eval_estate=0x0, simple_eval_resowner=0x0,  procedure_resowner=0x0, atomic=true) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:612
#9  0x00007f29add0e995 in plpgsql_call_handler (fcinfo=0x7ffd0f874d70) at ../pgsrc/src/pl/plpgsql/src/pl_handler.c:277
#10 0x000000000063331e in ExecuteCallStmt (stmt=0x28c0d78, params=0x0, atomic=true, dest=0xef0600 <spi_printtupDR>) at ../pgsrc/src/backend/commands/functioncmds.c:2286
#11 0x000000000091aa4b in standard_ProcessUtility (pstmt=0x28c0c68, queryString=0x28e0398 "call p_transaction()", readOnlyTree=true,  context=PROCESS_UTILITY_QUERY, params=0x0, queryEnv=0x0, dest=0xef0600 <spi_printtupDR>, qc=0x7ffd0f875630) at ../pgsrc/src/backend/tcop/utility.c:858
#12 0x000000000091a1c5 in ProcessUtility (pstmt=0x28e0df8, queryString=0x28e0398 "call p_transaction()", readOnlyTree=true, context=PROCESS_UTILITY_QUERY,  params=0x0, queryEnv=0x0, dest=0xef0600 <spi_printtupDR>, qc=0x7ffd0f875630) at ../pgsrc/src/backend/tcop/utility.c:530
#13 0x0000000000728dab in _SPI_execute_plan (plan=0x28df928, options=0x7ffd0f875790, snapshot=0x0, crosscheck_snapshot=0x0, fire_triggers=true) at ../pgsrc/src/backend/executor/spi.c:2693
#14 0x0000000000725a76 in SPI_execute_plan_extended (plan=0x28df928, options=0x7ffd0f875790) at ../pgsrc/src/backend/executor/spi.c:721
#15 0x00007f29adcff4cd in exec_stmt_call (estate=0x7ffd0f875c60, stmt=0x28b81d8) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:2219
#16 0x00007f29adcfef4b in exec_stmts (estate=0x7ffd0f875c60, stmts=0x28b82e8) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:2017
#17 0x00007f29adcfe8a8 in exec_stmt_block (estate=0x7ffd0f875c60, block=0x28f20f8) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1783
#18 0x00007f29adcfeefa in exec_stmts (estate=0x7ffd0f875c60, stmts=0x28f2198) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:2005
#19 0x00007f29adcfecd1 in exec_stmt_block (estate=0x7ffd0f875c60, block=0x28f2148) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1932
#20 0x00007f29adcfe4d6 in exec_toplevel_block (estate=0x7ffd0f875c60, block=0x28f2148) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:1623
#21 0x00007f29adcfc45d in plpgsql_exec_function (func=0x27fe5a8, fcinfo=0x7ffd0f875f90, simple_eval_estate=0x0, simple_eval_resowner=0x0,  procedure_resowner=0x0, atomic=true) at ../pgsrc/src/pl/plpgsql/src/pl_exec.c:612
#22 0x00007f29add0e995 in plpgsql_call_handler (fcinfo=0x7ffd0f875f90) at ../pgsrc/src/pl/plpgsql/src/pl_handler.c:277
#23 0x000000000063331e in ExecuteCallStmt (stmt=0x27ae170, params=0x0, atomic=true, dest=0x27ae6a0) at ../pgsrc/src/backend/commands/functioncmds.c:2286
#24 0x000000000091aa4b in standard_ProcessUtility (pstmt=0x27ae220, queryString=0x27ad678 "CALL p_transaction_caller();", readOnlyTree=false,  context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x27ae6a0, qc=0x7ffd0f876a80) at ../pgsrc/src/backend/tcop/utility.c:858
#25 0x000000000091a1c5 in ProcessUtility (pstmt=0x27ae220, queryString=0x27ad678 "CALL p_transaction_caller();", readOnlyTree=false,  context=PROCESS_UTILITY_TOPLEVEL, params=0x0, queryEnv=0x0, dest=0x27ae6a0, qc=0x7ffd0f876a80) at ../pgsrc/src/backend/tcop/utility.c:530
#26 0x0000000000918d0e in PortalRunUtility (portal=0x2859548, pstmt=0x27ae220, isTopLevel=true, setHoldSnapshot=false, dest=0x27ae6a0, qc=0x7ffd0f876a80) at ../pgsrc/src/backend/tcop/pquery.c:1158
#27 0x0000000000918f84 in PortalRunMulti (portal=0x2859548, isTopLevel=true, setHoldSnapshot=false, dest=0x27ae6a0, altdest=0x27ae6a0, qc=0x7ffd0f876a80) at ../pgsrc/src/backend/tcop/pquery.c:1315
#28 0x000000000091844b in PortalRun (portal=0x2859548, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x27ae6a0, altdest=0x27ae6a0,  qc=0x7ffd0f876a80) at ../pgsrc/src/backend/tcop/pquery.c:791
#29 0x000000000091180a in exec_simple_query (query_string=0x27ad678 "CALL p_transaction_caller();") at ../pgsrc/src/backend/tcop/postgres.c:1238
#30 0x0000000000916447 in PostgresMain (dbname=0x27e5308 "postgres", username=0x27aa1d8 "mingjie") at ../pgsrc/src/backend/tcop/postgres.c:4563
#31 0x00000000008523f0 in BackendRun (port=0x27db380) at ../pgsrc/src/backend/postmaster/postmaster.c:4396
#32 0x0000000000851d06 in BackendStartup (port=0x27db380) at ../pgsrc/src/backend/postmaster/postmaster.c:4124
#33 0x000000000084e444 in ServerLoop () at ../pgsrc/src/backend/postmaster/postmaster.c:1791
#34 0x000000000084dc6b in PostmasterMain (argc=1, argv=0x27a8180) at ../pgsrc/src/backend/postmaster/postmaster.c:1463
#35 0x000000000074a761 in main (argc=1, argv=0x27a8180) at ../pgsrc/src/backend/main/main.c:200

2 回滚前后estate->eval_econtext的状态变化

注意到SPI_rollback后,使用plpgsql_create_econtext重建了eval_econtext:

static int
exec_stmt_rollback(PLpgSQL_execstate *estate, PLpgSQL_stmt_rollback *stmt)
{
	if (stmt->chain)
		SPI_rollback_and_chain();
	else
		SPI_rollback();

	estate->simple_eval_estate = NULL;
	estate->simple_eval_resowner = NULL;
	plpgsql_create_econtext(estate);

	return PLPGSQL_RC_OK;
}

这里还是在深层exec_stmt_rollback位置,在执行真的rollback前:

现在有两层CALL:
ExecuteCallStmt          
  exec_stmt_block         (Caller的外层block直接进入exec_stmts)不调用plpgsql_create_econtext
    exec_stmts
      exec_stmt_block     (Caller的内层block走try catch进入exec_stmts)调用plpgsql_create_econtext
        exec_stmt_call
          ExecuteCallStmt    
            exec_stmt_block          (Callee的外层block直接进入exec_stmts)不调用plpgsql_create_econtext
              exec_stmts
                exec_stmt_block      (Callee的内层block走try catch进入exec_stmts)调用plpgsql_create_econtext
                  exec_stmt_rollback
  • 注意:eval_econtext生成函数plpgsql_create_econtext是在try catch的exec_stmt_block里面才会代用的:
  • exec_stmt_block函数:

ROLLBACK前estate->eval_econtext的状态

当前代码位置:
在这里插入图片描述
外层Caller的estate:

estate = 0x7ffd0f875c60
estate->eval_econtext = 0x28d4b88

内层Callee的estate:

estate = 0x7ffd0f874a40
estate->eval_econtext = 0x28d4d38

simple_econtext_stack状态(这是一个eval_econtext链表,记录了所有申请的eval_econtext)

{stack_econtext = 0x28d4d38, xact_subxid = 6, next = 0x27d9b38} callee执行exec_stmts前新建的
{stack_econtext = 0x28d4ca8, xact_subxid = 5, next = 0x27d9b08} callee执行exec_stmts前新建的
{stack_econtext = 0x28d4c18, xact_subxid = 4, next = 0x27d9a48} ExecuteCallStmt里面新建的
{stack_econtext = 0x28d4b88, xact_subxid = 4, next = 0x27d9988} caller执行exec_stmt_call前新建的
{stack_econtext = 0x28d4aa8, xact_subxid = 3, next = 0x0}       plpgsql_estate_setup建的
                                                                

对应事务
CurrentTransactionState->subTransactionId = [1,2,        3,        4,     5,     6]
                                             | |         |         |      |      |
                                          base savepoint savepoint except except except
                                                                   |      |       |
                                                 p_transaction_caller     p_transaction

和堆栈的关系

exec_stmt_block(trycatch)    old_eval_econtext=0x28d4ca8
exec_stmt_block(trycatch)    old_eval_econtext=0x28d4c18
exec_stmt_block(无)
ExecuteCallStmt
exec_stmt_block(trycatch)    old_eval_econtext=0x28d4aa8
exec_stmt_block(无)
ExecuteCallStmt

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

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

相关文章

Python文件操作-代码案例

文章目录文件打开文件open写文件上下文管理器第三方库简单应用案例使用python生成二维码使用python操作excel程序员鼓励师学生管理系统文件 变量就在内存中,文件在硬盘中. 内存空间更小,访问速度快,成本贵,数据容易丢失,硬盘空间大,访问慢,偏移,持久化存储. \\在才是 \的含义…

十分钟上手把玩树莓派——系统创建指南

无意中发现一个落灰的树莓派 故事便开始了…… 准备工作 树莓派 3B一张大于 8G 的 micro SD 卡一个读卡器HDMI 显示器及连接线、键盘、鼠标等外围设备 系统镜像下载 推荐两个树莓派镜像下载网站 树莓派官方网站&#xff1a;https://www.raspberrypi.com/software/树莓派实…

“赶快回家网”首页制作

“赶快回家网”首页制作一、实验名称&#xff1a;二、实验日期&#xff1a;三、实验目的&#xff1a;四、实验内容&#xff1a;五、实验步骤&#xff1a;六、实验结果&#xff1a;七、源程序&#xff1a;八、心得体会&#xff1a;一、实验名称&#xff1a; “赶快回家网”首页…

使用Jmeter抓取手机APP报文并进行APP接口测试

Jmeter是一个比较常用的接口测试工具&#xff0c;尤其是接口性能测试。当然它也可以用来测试手机APP的HTTP接口&#xff0c;我在Fiddler抓取手机APP报文 和 接口测试代理工具charles mock测试 分别介绍了Fiddler和charles 如何抓取APP报文&#xff0c;本文介绍使用Jmeter来抓取…

第六章 关系数据理论(规范化详解)

第六章 关系数据理论 6.1 问题的提出 本章主要讨论关系数据理论。在讨论数据库的时候&#xff0c;绕不开的一个问题是&#xff1a;针对一个具体问题&#xff0c;应该如何构建一个适合他的数据库模式。这是数据库设计的问题&#xff0c;确切地讲是关系数据库逻辑设计的问题。为…

python最新采集某站美女,还不快学起来,下载可能下架视频

前言 大家早好、午好、晚好吖 ❤ ~ 这个页面大家认识吧~ 喜欢看吧 那我们今天就来采集一下它呀~ 开发环境: 版 本: python 3.8 编辑器: pycharm 2022.3.2 专业版 requests >>> pip install requests ffmpeg 音视频合成软件 如果安装python第三方模块: win R 输…

民用建筑电力系统运行和节能中的应用——电力监控系统篇

【摘要】本文中概述电力监控系统结构和作用&#xff0c;通过列举工程实例&#xff0c;详细介绍了电力监控系统在民用建筑电力系统运行和节能中的应用&#xff0c;以及在推广和发展方面需要改进的问题。 【关键词】民用建筑&#xff1b;电力监控系统&#xff1b;运行和节能中的…

cmd常用的操作命令

使用windows系统&#xff0c;通常在cmd中输入指令&#xff0c;会调用相应的一些程序或者执行一些功能&#xff0c;学会使用CMD中的命令&#xff0c;可以加快我们一些操作&#xff0c;省时省力。 ipconfig ------查询IP地址 gpedit.msc-----组策略 sndrec32-------录音机 Nsloo…

小程序自动化测试框架【Minium】系列(三)元素定位详解

元素定位 元素定位&#xff0c;应该是很多UI自动化测试入门学习必会的技能了&#xff0c;下面我将为大家举例演示元素定位的几种方法。 1、CSS选择器 Minium 可以通过 WXSS 选择器定位元素&#xff0c;如下图所示&#xff1a; 如果有[CSS选择器]基础会上手更快 &#xff0c;如…

Maven知识点-插件-maven-surefire-plugin简介

Maven本身并不是一个单元测试框架&#xff0c;Java 世界中主流的单元测试框架为JUnit 和TestNG。 Maven 所做的只是在构建执行到特定生命周期阶段的时候&#xff0c;通过插件来执行JUnit或者TestNG的测试用例。 这一插件就是maven-surefire-plugin&#xff0c;可以称之为测试…

Docker安装ElasticSearch,并进行ik和hanlp分词

我按装的目标: 利用ElastiSearch存储数据&#xff0c;ik和hanlp分词插件 对 搜索词进行分词&#xff0c;在ES存储的库中找到与搜索词相近的内容。 安装感受: 原始环境安装老版本的ES&#xff0c;BUG不断&#xff0c;ES相关解答博客对新手有点不友好&#xff0c;完整的解释不多&…

Elasticsearch7.8.0版本进阶——分布式集群(故障转移)

目录一、Elasticsearch集群的安装1.1、Elasticsearch集群的安装&#xff08;win10环境&#xff09;1.2、Elasticsearch集群的安装&#xff08;linux环境&#xff09;二、故障转移的概述三、故障转移&#xff08;win10环境集群演示&#xff09;一、Elasticsearch集群的安装 1.1…

分布式高级篇1 —— 全文检索

Elasticsearch Elasticsearch简介一、基本概念1、index(索引)2、Type(类型)3、Document(文档)4、倒排索引二、Docker 安装 EL1、拉取镜像2、创建实例三、初步探索1、_cat2、索引一个文档(保存)3、查询文档3、更新文档4、删除文档&索引5、_bulk 批量 AP6、样本测试数据四、进…

安全测试的最常用方法你知道多少呢?

安全性测试(Security Testing)是指有关验证应用程序的安全等级和识别潜在安全性缺陷的过程&#xff0c;其主要目的是查找软件自身程序设计中存在的安全隐患&#xff0c;并检查应用程序对非法侵入的防范能力&#xff0c;安全指标不同&#xff0c;测试策略也不同。 但安全是相对的…

【ESP32+freeRTOS学习笔记-(七)中断管理】

目录1、概述2、在ISR中使用FreeRTOS中专用的API2.1 独立的用于ISR中的API2.2 关于xHigherPriorityTaskWoken 参数的初步理解3、延迟中断处理的方法-将中断中的处理推迟到任务中去4 方法一&#xff1a;用二进制信号量来同步ISR与”延时处理的任务“4.1 二进制信号量4.2 函数用法…

高中生用台灯哪种好?2023最好的台灯品牌排行榜

高中生的学习时长是最长的&#xff0c;所以导致现在许多高中生都戴上了眼镜&#xff0c;主要是因为长时间对着书本&#xff0c;没有合理的让眼睛休息&#xff0c;导致眼疲劳&#xff0c;而选择护眼台灯是最好的&#xff0c;台灯内置的护眼技术是非常实用的&#xff0c;可以改善…

SIP协议的一键对讲终端

SIP对讲终端是一款采用了ARMDSP架构&#xff0c;接收网络音频流&#xff0c;实时解码播放&#xff1b;配置了麦克风输入和扬声器输出&#xff0c;作为网络数字广播的播放终端。主要用于银行、部门机构、酒店等场所的网络广播、网络对讲。本产品配置了麦克风和3W扬声器&#xff…

psudohash:一款基于变异机制的密码列表生成工具

关于psudohash psudohash是一款功能强大的密码列表生成工具&#xff0c;该工具基于关键词变异技术实现其功能&#xff0c;并且能够根据常用密码创建模式来生成字典文件。 psudohash可以用于密码爆破任务中&#xff0c;以帮助广大研究人员测试密码的安全性。该工具能够模仿人类…

Spring Security 源码解读:权限控制

本文样例代码地址&#xff1a; spring-security-oauth2.0-client-sample。 关于此章&#xff0c;官网介绍&#xff1a;Authorization 本文使用Spring Boot 2.7.4版本&#xff0c;对应Spring Security 5.7.3版本。 Introduction 认证过程中会一并获得用户权限&#xff0c;Au…

vue-router 源码解析(二)-创建路由匹配对象

文章目录基本使用导语createRouterMatcher 创建匹配路由记录addRoute 递归添加matchercreateRouteRecordMatcher 创建matchertokenizePath 解析pathtokensToParser 记录打分insertMatcher 将matcher排序总结基本使用 const routes [{path:"/",component: Demo2,nam…