【GaussDB】507内核版本新特性-视图失效重编译
背景
众所周知,原生postgresql的视图如果引用了一个表,那么对这个表进行删除或者修改字段类型或者字段长度时,大概率会报错,说有视图依赖,但相同的场景在oracle并不会发生。
举个常见的场景:
业务表都在数据库A用户下,另外有个B用户权限低,申请访问A用户下的部分数据,于是A用户就把几张表授权给了B用户,然后B用户为了方便查询,创建了一个视图来关联这几张表查询,A用户并不知情。某天A用户需要对其中一张表的一个字段扩长,此时不同数据库的表现差异就来了:
- ORACLE 中顺利执行字段长度修改
- 原生PG中 执行字段长度修改报错
我改我的东西还要问你意见?
在这个数据库中,A地位明显高于B的地位,B用户甚至可能不跑业务,只是偶尔查下数据,于是原生PG这个设定让很多开发及运维人员感到头疼。
在GaussDB 507 版本,终于支持了视图失效重编译这个功能
官方文档
注意《失效重编译》存在同名章节,一个在《参考-存储过程》里,一个在《特性使用指导》里,有部分内容重叠,各自又有多的内容。
简单测试
先看看未开启这个功能时是什么情况:
gaussdb=# show enable_view_invalidation;
enable_view_invalidation
--------------------------
off
(1 row)
gaussdb=# create table t_test_view_depend (a number(12,3),b varchar2(20));
CREATE TABLE
gaussdb=# create view v_test_view_depend as select * from t_test_view_depend;
CREATE VIEW
gaussdb=# alter table t_test_view_depend MODIFY b varchar2(30);
ERROR: cannot alter type of a column used by a view or rule
DETAIL: rule _RETURN on view v_test_view_depend depends on column "b"
gaussdb=# alter table t_test_view_depend MODIFY a number(13,4);
ERROR: cannot alter type of a column used by a view or rule
DETAIL: rule _RETURN on view v_test_view_depend depends on column "a"
gaussdb=# drop table t_test_view_depend;
ERROR: table t_test_view_depend cannot be dropped because other objects depend on it.
DETAIL: view v_test_view_depend depends on table t_test_view_depend
HINT: Use DROP ... CASCADE to drop the dependent objects.
gaussdb=#
无论是删表还是改字段长度都不行。
然后打开开关,
gaussdb=# set enable_view_invalidation to on;
SET
gaussdb=# alter table t_test_view_depend MODIFY b varchar2(30);
ALTER TABLE
gaussdb=# alter table t_test_view_depend MODIFY a number(13,4);
ALTER TABLE
gaussdb=# select * from v_test_view_depend;
a | b
---+---
(0 rows)
gaussdb=# drop table t_test_view_depend;
DROP TABLE
gaussdb=# select * from v_test_view_depend;
ERROR: View "v_test_view_depend" is invalid and please check view definition.
LINE 1: select * from v_test_view_depend;
^
gaussdb=# create table t_test_view_depend (a number(12,3),b varchar2(20));
CREATE TABLE
gaussdb=# select * from v_test_view_depend;
a | b
---+---
(0 rows)
gaussdb=#
可以看到打开enable_view_invalidation后,表的字段就可以被修改了,改完后查询视图也没有报错;表也可以被drop了,重建表后视图查询也不报错。
历史问题测试
顺便测一下之前我这篇文章【GaussDB】浅浅说下GaussDB中视图依赖关系的一个处理逻辑里提到的视图依赖coredump场景。
drop view if exists v_test_dep;
drop package if exists pkg_test_dep;
drop package if exists pkg_subtype;
create or replace package pkg_subtype is
subtype varchar_8 is varchar(8);
end;
/
create or replace package pkg_test_dep is
function func(i int) return pkg_subtype.varchar_8;
end;
/
create or replace package body pkg_test_dep is
function func(i int) return pkg_subtype.varchar_8 is
begin
return i::text;
end;
end;
/
create or replace view v_test_dep as
select 1 from dual where pkg_test_dep.func('1')='1';
create or replace package pkg_subtype is
subtype varchar_8 is int;
end;
/
select * from v_test_dep;
先测没打开视图依赖的表现:
gaussdb=# select * from v_test_dep;
ERROR: The function 57554 header changes during execution.
DETAIL: N/A
gaussdb=# select * from v_test_dep;
Connection has been closed by peer, remote ip:(null), port:5432.
The connection to the server was lost. Attempting reset: Failed.
!>
和506版本有所变化,之前是查询视图就coredump,到507版本后,第一次查询报错,第二次才触发coredump
然后测打开视图依赖的表现,这里又要分两种情况:
- 在修改了视图依赖对象后,查询视图之前打开开关,还是会coredump
gaussdb=# create or replace package pkg_subtype is
gaussdb$# subtype varchar_8 is int;
gaussdb$# end;
gaussdb$# /
gaussdb=# set enable_view_invalidation to on;
SET
gaussdb=# select * from v_test_dep;
Connection has been closed by peer, remote ip:(null), port:5432.
The connection to the server was lost. Attempting reset: Failed.
!>
- 在修改视图依赖之前打开开关,不coredump了,视图直接变为可查询。
gaussdb=# set enable_view_invalidation to on;
SET
gaussdb=# create or replace package pkg_subtype is
gaussdb$# subtype varchar_8 is int;
gaussdb$# end;
gaussdb$# /
CREATE PACKAGE
gaussdb=# select * from v_test_dep;
?column?
----------
1
(1 row)
也就是说华为引入的这个视图失效重编译特性,意外解决了这个coredump问题。
为什么说是意外,因为如果他们知道这个coredump场景,就不会只在打开这个参数时才不coredump,数据库默认是关闭了这个参数的。
乱序创建场景
以上是失效重编译,对应的场景是按顺序正常创建对象后再去修改视图依赖的对象;其实还有另一个场景,就是不按顺序创建,比如先建视图,再建视图里的表,这个场景在GaussDB 507版本中也得到了支持,不过是另一个参数 enable_force_create_obj ,这个参数在之前版本只覆盖pl对象,即 package/procedure/function ,而在507版本中,新增了对 view 的覆盖。
从文档也可以看到变化:
- 506:
参数说明:用于控制一次性入库功能的开启与关闭。开启该参数后,若创建或重建函数、包时存在未定义的对象,会创建一个虚拟的对象用于编译,并在函数体编译的过程中通过try catch捕获异常,使创建或重建过程能够运行成功。多租场景下,该参数可在PDB级别设置。
- 507:
参数说明:用于控制一次性入库功能的开启与关闭。开启该参数后,若创建或重建函数、包时存在未定义的对象,会创建一个虚拟的对象用于编译,并在函数体编译的过程中通过try catch捕获异常,使创建或重建过程能够运行成功; 创建视图时,如果视图依赖的对象不存在或缺少查询权限,该视图仍能够创建成功。 多租场景下,该参数可在PDB级别设置。
这个参数就不进行测试演示了,文档已经说清楚了效果。如果展开具体场景,细节会非常多,对本文而言有点喧宾夺主了。不过需要注意的是,这个参数也是默认关闭的,但对于存储过程依赖多的,建议还是打开。
元数据
-
pg_depend
原生PG对于视图内对象的依赖关系管理,在GaussDB中还是保留的,即pg_depend,这个不再多言,可以自行查找相关材料,或者看我以前写的这篇文章《从pg_depend和pg_class开始了解MogDB/openGauss/postgresql的系统元数据设计》。 -
pg_object
视图的生效失效状态记录在pg_object.valid,但要注意,这个状态为false时并不代表视图就无法查询,这个处理逻辑和ORACLE是类似的:
当修改视图内引用的对象时,视图变为无效,下次查询视图时自动重新编译,编译通过则变为有效,并返回查询结果;或者手动编译为有效后再查询。不过什么样的修改会导致视图变为无效,GaussDB和ORACLE不完全一样,这个我也懒得展开说了。
- gs_dep_source
gs_dep_source里面对于每个视图,会有一条object_type为v的记录,里面的source会记录展开后的完整SQL,包括schema和select *的字段,操作符也会变成operator的语法,自动类型转换也会显示出来。比如本文中的两个例子。
CREATE OR REPLACE VIEW public.v_test_dep("?column?") AS SELECT 1 AS "?column?" FROM pg_catalog.dual WHERE ((public.pkg_test_dep.func(1))::text OPERATOR(pg_catalog.=) '1');
CREATE OR REPLACE VIEW admin.v_test_view_depend(a, b) AS SELECT t_test_view_depend.a, t_test_view_depend.b FROM admin.t_test_view_depend;
- pg_rewrite
pg_rewrite.ev_action_def里会记录视图的查询语句部分,但这里面的东西并不保持稳定不变:
- 这里在正常创建视图,且未做任何修改操作时,记录的是创建视图的 "真原始SQL:
select * from t_test_view_depend
- 在经历过视图失效重编译成生效后,会变成和
gs_dep_source.source里记录的查询SQL部分一样,比如
SELECT t_test_view_depend.a, t_test_view_depend.b FROM admin.t_test_view_depend
内置函数变化
- 引入了一个新内置函数 transform_view_dep_source()
描述:刷新GS_DEP_SOURCE系统表的source字段。即将视图定义语句中SELECT *的星号扩展;将视图创建时对象使用的schema补充写入;刷新视图依赖关系。该函数仅支持管理员用户调用。
参数:无
返回值类型:void
个人猜测这个是在从低版本升级到507时自动执行的,因为低版本的gs_dep_source之前记录的是原始SQL,而不是展开后的SQL。然后我去507的升级脚本里搜了一下,果然搜到了
"upgrade_catalog_otherdb\upgrade-post_catalog_otherdb_98_145.sql"
SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 8401;
CREATE OR REPLACE FUNCTION pg_catalog.transform_view_dep_source()
RETURNS void
LANGUAGE internal
AS $function$transform_view_dep_source$function$;
SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 0;
SELECT pg_catalog.transform_view_dep_source();
也就是说实际上在507使用过程中,这个函数一般是用不上的。
- 修改了一个原有内置函数 gs_compile_schema(schema_name in varchar2 default null, compile_all in boolean default false, retry_times in int default 10)
原本只支持对PL对象的编译,现在新增了对视图的编译。
总结
GaussDB 507引入的这个视图无效重编译特性是非常有必要的,无论是从ORACLE迁移还是新开发系统,视图不应该阻碍对表定义的修改。我大概测试下来,各项行为基本符合预期,不过我也没去做更复杂的测试。但个人认为,这个参数还是先打开再去跑PoC业务验证更好,有问题也能尽早报给华为去修复。

