文章概要:

本文对主要就PLSQL的异常传播进行知识性小结,分为四个部分,PLSQL异常传播小结,编写小案例验证5种传播规则,示例了一个容易理解出错的案例,以及使用goto结合异常处理的案例。

一,异常传播规则

PLSQL块结构,典型如下:

declare
--声明区域
begin
--执行区域
exception
--异常处理区域
end

上述三个区域都可以产生异常(PLSQL自动抛出来的,或者代码主动抛出来的异常),PL/SQL采用统一异常处理机制,当异常发生时,程序会自动跳转到异常处理区域,交给异常处理程序进行异常匹配,

处理完异常后,程序的控制流程继续向外部传递。

就传播规则而言,分两大情况,5小情况。

1,执行区域产生异常时,异常传播方式分为下三种情况:

1)如果当前语句块有该异常的处理器,则程序流程转移到该异常处理器,并进行异常处理。然后,程序的控制流程传递到外层语句块,继续执行。

2)如果当前语句块没有该异常处理器,则在外层语句块的异常处理部分处理该异常。处理完异常后,程序的控制流程继续向外部传递。

3)如果当前语句块及其外层语句块都没有对该异常的处理,则该异常将传播到调用环境(比如ksql客户端)或主机环境。

简而言之,异常会向他的当前子块传播,不能被捕获,则向外层进行传递,

2,声明区域和异常处理区域产生的异常传播策略有明显不同:

会立刻传播到外层语句块的异常处理部分, 即使当前语句块有该异常的异常处理器也不会进行捕获处理。

4)如果外层语句块无法处理该异常,则异常继续向更外层传播,直到调用环境或主机环境。

5)当外层语句块捕获并处理内层块的异常后,程序流程继续 向外层传递并执行。

任何情况(指1-5五种情况),直到异常被捕获则终止或最终到调用环境或主机环境。

二,异常传播实例

用尽可能简单的例子对上述5个情况进行实测:

1)如果当前语句块有该异常的处理器,则程序流程转移到该异常处理器,并进行异常处理。然后,程序的控制流程传递到外层语句块,继续执行。

--1

begin
select 1/0 as result;
exception
when others then
raise notice '当前块捕获到异常';
end;
--运行结果
NOTICE: 当前块捕获到异常
ANONYMOUS BLOCK

--2

begin
begin
select 1/0 as result;
exception
when others then
raise notice '内块捕获到异常';
end;
raise notice '继续执行程序';
exception
when others then
raise notice '外块捕获到异常';
end;
--运行结果
NOTICE: 内块捕获到异常
NOTICE: 继续执行程序
ANONYMOUS BLOCK

2)如果当前语句块没有该异常处理器,则在外层语句块的异常处理部分处理该异常。处理完异常后,程序的控制流程继续向外部传递。

begin
begin
begin
select 1/0 as result;
--exception -- 注释掉异常处理块
-- when others then
-- raise notice '内块捕获到异常';
end;
exception
when others then
raise notice '外块捕获到异常';
end;
raise notice '继续执行程序';
end;
--运行结构
NOTICE: 外块捕获到异常
NOTICE: 继续执行程序
ANONYMOUS BLOCK

3)如果当前语句块及其外层语句块都没有对该异常的处理,则该异常将传播到调用环境(比如ksql客户端)或主机环境。

begin
select 1/0 as result;
end;
--运行结果
ERROR: division by zero
CONTEXT: SQL statement "select 1/0 as result"
PL/SQL function inline_code_block line 2 at SQL statement

**4)如果外层语句块无法处理该异常,则异常继续向更外层传播,直到调用环境或主机环境。 **

--1

declare
vv CONSTANT NUMBER(2):=500; ---在本地声明部声明常量,numeric field overflow
begin
raise notice '变量vv值为:%',vv;
exception
when others then
raise notice '最外层块捕获到异常';
end;
--运行结果
ERROR: numeric field overflow
DETAIL: A field with precision 2, scale 0 must round to an absolute value less than 10^2.
CONTEXT: PL/SQL function inline_code_block line 3 during statement block local variable initialization

--2

declare
declare
vv CONSTANT NUMBER(2):=500; ---在本地声明部声明常量,numeric field overflow
begin
raise notice '变量vv值为:%',vv;
exception
when others then
raise notice '声明区域捕获到异常';
end;
begin
raise notice '继续执行程序';
exception
when others then
raise notice '外层块捕获到异常';
end;
--运行结果
ERROR: numeric field overflow
DETAIL: A field with precision 2, scale 0 must round to an absolute value less than 10^2.
CONTEXT: PL/SQL function inline_code_block line 4 during statement block local variable initialization

--3

begin
declare
vv CONSTANT NUMBER(2):=500; ---在本地声明部声明常量,numeric field overflow
begin
raise notice '变量vv值为:%',vv;
exception
when others then
raise notice '声明区域捕获到异常';
end;
raise notice '继续执行程序';
exception
when others then
raise notice '外层块捕获到异常';
end;
--运行结果:
NOTICE: 外层块捕获到异常
ANONYMOUS BLOCK

5)当外层语句块捕获并处理内层块的异常后,程序流程继续向外层传递并执行。

begin
begin
declare
vv CONSTANT NUMBER(2):=500; ---在本地声明部声明常量,numeric field overflow
begin
raise notice '变量vv值为:%',vv;
exception
when others then
raise notice '声明区域捕获到异常';
end;
raise notice '继续执行程序1';
exception
when others then
raise notice '外层块捕获到异常';
end;
raise notice '继续执行程序2';
end;
--运行结果
NOTICE: 外层块捕获到异常
NOTICE: 继续执行程序2
ANONYMOUS BLOCK

三,一个易理解错误的例子

再来看一个容易理解出错的案例:

declare
--声明区域
function func_test () return int
as
declare
a int;
begin
a = 10;
select a/0;
return 1;
exception
when others then
raise notice '函数内捕获到异常';
end;
begin
raise notice '测试';
select func_test();
exception
when others then
raise notice '块外捕获到异常';
end
--运行结果:
NOTICE: 测试
NOTICE: 函数内捕获到异常
NOTICE: 块外捕获到异常 func_test
----------- (0 rows)

这个案例在函数内和快外都捕捉到了异常,难道异常传播了两次?实际不是,看下面这个例子一目了然

declare
--声明区域
function func_test () return int
as
declare
a int;
begin
a = 10;
select a/0;
return 1;
exception
when zero_divide then
raise notice '函数内捕获到异常--》%',sqlerrm;
end;
begin
raise notice '测试';
select func_test();
exception
when others then
raise notice '块外捕获到异常--》%',sqlerrm;
end
--运行结果
NOTICE: 测试
NOTICE: 函数内捕获到异常--》division by zero
NOTICE: 块外捕获到异常--》control reached end of function without RETURN func_test
----------- (0 rows)

到此实际上已经真相大白,是因为嵌套函数func_test的exception没有返回值造成。

这个例子从侧面说明了,不管何时,尽量通过异常名称捕获异常,针对特定的错误进行处理,尽量少使用OTHERS异常处理器。

但在最外层块的异常处理部分放置OTHERS异常处理器,避免有未被处理的异常,是没有问题的。

四,GOTO结合异常处理

如前文所说PL/SQL采用统一异常处理机制,当异常发生时,程序会自动跳转到异常处理区域,交给异常处理程序进行异常匹配,

处理完异常后,程序的控制流程继续向外部传递顺序执行下去。

如果在处理完异常后,修复了异常后,我们不希望异常向外部传递后顺序执行下去呢?我们可以用GOTO进行跳转。

GOTO语句不能跳转到异常控制程序。同样,GOTO语句也不能从异常控制程序跳转到当前块。

例如,下面的GOTO语句就是非法的:

declare
a int;
begin
a = 0;
<<zero_divide_label>>
a = 10/a;
raise notice '测试结果:%',a;
exception
when zero_divide then
raise notice '块外捕获到异常--》%',sqlerrm;
a = 1;
GOTO zero_divide_label; ----非法的跳转到当前块
end
--运行结果
ERROR: illegal GOTO statement, cannot transfer control to label 'zero_divide_label'
CONTEXT: compilation of PL/SQL function "inline_code_block" near line 3

但是,GOTO语句可以从一个异常控制程序中跳转到一个封闭块,上述代码调整为:

修复除零异常后重新执行原预期代码

declare
a int;
begin
a = 0;
<<zero_divide_label>>
begin
a = 10/a;
raise notice '测试结果:%',a;
exception
when zero_divide then
raise notice '块外捕获到异常--》%',sqlerrm;
a = 1;
GOTO zero_divide_label; ----合法的跳转到当前块
end;
end
--运行结果
NOTICE: 块外捕获到异常--》division by zero
NOTICE: 测试结果:10
ANONYMOUS BLOCK

PLSQL的异常传播的更多相关文章

  1. PLSQL_Oracle Exception异常分类、异常抛出、异常处理、异常传播(概念)

    2014-06-03 Created By BaoXinjian

  2. python中如何通过报错信息定位问题(异常传播轨迹)

    class SelfException(Exception): pass def main(): firstMethod() def firstMethod(): secondMethod() def ...

  3. WCF异常传播

    传送至客户端的异常肯定是CommunitionException类型,包括一般的通信过程中出错而引发的CommunicationException类型,System.IdentityModel.Sel ...

  4. plsql数据库异常---plsql 登录后,提示数据库字符集(AL32UTF8)和客户端字符集(ZHS16GBK)不一致

    今天遇到这个问题网上搜了一下答案找到了 转贴 http://blog.csdn.net/lidew521/article/details/8546155 plsql 登录后提示: Database c ...

  5. [转贴] 从零开始学C++之异常(二):程序错误、异常(语法、抛出、捕获、传播)、栈展开

    一.程序错误 编译错误,即语法错误.程序就无法被生成运行代码. 运行时错误 不可预料的逻辑错误 可以预料的运行异常 例如: 动态分配空间时可能不会成功 打开文件可能会失败 除法运算时分母可能为0 整数 ...

  6. .Net程序员学用Oracle系列(27):PLSQL 之游标、异常和事务

    1.游标 1.1.游标属性 1.2.隐式游标 1.3.游标处理及案例 2.异常 2.1.异常类别 2.2.异常函数 2.3.异常处理及案例 3.事务 3.1.开始事务.结束事务 3.2.自治事务 3. ...

  7. Netty源码分析第4章(pipeline)---->第6节: 传播异常事件

    Netty源码分析第四章: pipeline 第6节: 传播异常事件 讲完了inbound事件和outbound事件的传输流程, 这一小节剖析异常事件的传输流程 首先我们看一个最最简单的异常处理的场景 ...

  8. [Google Guava] 1.5-Throwables:简化异常和错误的传播与检查

    原文链接 译者: 沈义扬 异常传播 有时候,你会想把捕获到的异常再次抛出.这种情况通常发生在Error或RuntimeException被捕获的时候,你没想捕获它们,但是声明捕获Throwable和E ...

  9. 事件和异常的传播 · 农场主的黑科技.

    inBound事件的传播 何为inBound事件以及ChannelInboundHandler ChannelRead事件的传播ChannelRead是典型的inbound事件,以他为例了解inbou ...

  10. 『无为则无心』Python函数 — 39、Python中异常的传播

    目录 1.异常的传播 2.如何处理异常 1.异常的传播 当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播.如果函数中没有对异常进行处理,则异常会继续向函数调用者传播.如果函数调 ...

随机推荐

  1. HTTP协议发展历程

    HTTP协议发展历程 HTTP超文本传输协议是一个用于传输超文本文档的应用层协议,它是为Web浏览器与Web服务器之间的通信而设计的,HTTP协议到目前为止全部的版本可以分为HTTP 0.9.HTTP ...

  2. Js中数组空位问题

    Js中数组空位问题 JavaScript中数组空位指的是数组中的empty,其表示的是在该位置没有任何值,而且empty是区别于undefined的,同样empty也不属于Js的任何数据类型,并且在J ...

  3. springboot+vue+elementui实现文件上传下载删除DEMO

    说明 前面搜索了几个关于springboot+vue+elementui上传下载的文章,感觉写的都不尽如人意.要么是功能不完善,不好用.再者就是源码提供的实在差劲,都不完整.一气之下,自己搞了一个实用 ...

  4. Docker进阶之01-Docker Compose编排工具

    Docker Compose是什么 https://github.com/docker/compose 可以按项目为单位管理多个Docker容器,Python语言开发,底层调用Docker的API接口 ...

  5. Java面向对象之接口和抽象类的区别一目了然

    介绍 相信对于Java面向对象部分,很多人很长一段时间对于接口和抽象类的区别,使用场景都不是很熟悉,同是作为抽象层重要的对象,工作中到底什么情况下使用抽象类,不是很清楚.本文就一次性把这些概念一次性说 ...

  6. 【Azure 应用服务】App Servie网站报403 ModSecurity Action错误

    问题描述 App Service 部署应用程序,然后通过App Gateway(WAF) 提供公网访问,但是一直遇见403报错,刷新页面,回退,重新Web页面能缓解403问题. 问题分析 通过浏览器F ...

  7. 【Azure 存储服务】调用REST API获取Stroage Account Table中所有的Entity计数 -- Count

    问题描述 在Storage Account的使用中,如果想获取Table中全部Entity的计数以及大小,如果是REST API方式,如何来获取呢? 问题解答 在Azure中,所有服务的Metrics ...

  8. Effective C++ 第一章:让自己习惯C++

    Effective C++ 第一章:让自己习惯C++ 引言 最近在阅读这本<effective C++ 改善程序与设计的55个具体做法>这本书,为了以后忘记的时候回顾,写一些笔记,每次笔记 ...

  9. WebView无法加载页面报错 net:ERR_CLEARTEXT_NOT_PERMITTED 还有webView加载网页后出现ERR_UNKNOWN_URL_SCHEME

    根据网络安全配置- 从Android 9(API级别28)开始,默认情况下禁用明文支持.因此http的url均无法在webview中加载 还可以看看-https: //koz.io/android-m ...

  10. ants - 目前开源最优的协程池

    ants - 目前开源最优的协程池 目前我们的项目重度使用 ants 协程池,在开启一个 go 的时候并不是用 go 关键字,而是用一个封装的 go 函数来开启协程.框架底层,则是使用 ants 项目 ...