数据库函数代码管理 - 自动备份函数\过程代码到SVN\github\gitlab

标签

PostgreSQL , 存储过程 , plpgsql , plpython , pljava , svn , github , 版本管理


背景

市面上有很多代码的版本管理软件,比如gitlab, github, svn等等。

商业数据库的编程能力较强,比如Oracle的PL/SQL,很多传统企业会将对一致性、可靠性要求非常高的业务逻辑放到数据库中,这就造成了数据库内不仅仅存储数据,也存储了部分业务代码。

PostgreSQL 作为开源界最高级的开源数据库,同样支持强大的函数语言plpgsql,同时通过语言扩展,支持pljava, plpython, pltcl, plperl等等,同样,使用PostgreSQL数据库函数处理业务逻辑,也可以像商业数据库一样保证数据一致性、可靠性。

但是问题来了,数据库中存储的业务逻辑代码,如何管理呢?

一种方法是在人工管理,在数据库中执行前、后保留代码到gitlab, github, svn等自建或公共的代码库中。

另一种方法是让数据库直接对接代码库,实时将函数代码提交到代码库。

我们看看PostgreSQL如何实现?

机制

如果要让数据库自动、实时的将修改或新建的函数代码内容提交到代码库,首先要有一个自动机制。

PostgreSQL 有两种机制可以实现:

1. 一种是事件触发器,在执行DDL时,自动触发,此时可以提取DDL内容,然后你想干什么就随你了,PostgreSQL可通过自定义函数操作GITHUB,GITLAB,SVN等。

2. 另一种方法是HOOK,是的,PostgreSQL提供了很多HOOK,允许用户通过钩子做一些旁路逻辑,比如我们在EXECUTE后,截获execute的内容并处理它。截获后PostgreSQL可通过自定义函数操作GITHUB,GITLAB,SVN等。

事件触发器

PostgreSQL的事件触发器指在发生某些DDL事件后,可以触发调用事件触发器函数,函数中我们可以处理很多东西。

1. 事件触发器语法

https://www.postgresql.org/docs/9.6/static/sql-createeventtrigger.html

CREATE EVENT TRIGGER name
    ON event
    [ WHEN filter_variable IN (filter_value [, ... ]) [ AND ... ] ]
    EXECUTE PROCEDURE function_name();

解说

event:指事件,

ddl_command_start, ddl_command_end, table_rewrite and sql_drop。

https://www.postgresql.org/docs/9.6/static/event-trigger-definition.html

filter_variable:TAG

filter_value:指event对应的command tag,比如本文要用到的CREATE FUNCTION,详见如下

https://www.postgresql.org/docs/9.6/static/event-trigger-matrix.html

2. 事件触发器函数的语法

与语言有关,比如plpgsql语言写的事件触发器函数为

create or replace function function_name() returns event_trigger as $$
declare
  ...
begin
  ...
end;
$$ language plpgsql strict;

3. 事件触发器相关的系统函数调用

https://www.postgresql.org/docs/9.6/static/functions-event-triggers.html

3.1 pg_event_trigger_ddl_commands() 捕获命令结束时的信息,本文要用到objid字段,即函数的objectid,然后调用pg_get_functiondef(oid)得到函数的定义。

Name Type Description
classid Oid OID of catalog the object belongs in
objid Oid OID of the object in the catalog
objsubid integer Object sub-id (e.g. attribute number for columns)
command_tag text command tag
object_type text Type of the object
schema_name text Name of the schema the object belongs in, if any; otherwise NULL. No quoting is applied.
object_identity text Text rendering of the object identity, schema-qualified. Each and every identifier present in the identity is quoted if necessary.
in_extension bool whether the command is part of an extension script
command pg_ddl_command A complete representation of the command, in internal format. This cannot be output directly, but it can be passed to other functions to obtain different pieces of information about the command.

3.2 pg_event_trigger_dropped_objects() 捕获被DROP的对象

与本文无关,不列出

3.3 pg_event_trigger_table_rewrite_oid()和pg_event_trigger_table_rewrite_reason() 捕获table rewrite事件涉及的表和原因

与本文无关,不列出

4. 获取函数定义,pg_get_functiondef(oid)

postgres=# \df *.*def*
                                       List of functions
   Schema   |            Name             | Result data type |  Argument data types  |  Type
------------+-----------------------------+------------------+-----------------------+---------
 pg_catalog | pg_get_function_arg_default | text             | oid, integer          | normal
 pg_catalog | pg_get_functiondef          | text             | oid                   | normal
 pg_catalog | pg_get_indexdef             | text             | oid                   | normal
 pg_catalog | pg_get_indexdef             | text             | oid, integer, boolean | normal
 pg_catalog | pg_get_ruledef              | text             | oid                   | normal
 pg_catalog | pg_get_ruledef              | text             | oid, boolean          | normal
 pg_catalog | pg_get_triggerdef           | text             | oid                   | normal
 pg_catalog | pg_get_triggerdef           | text             | oid, boolean          | normal
 pg_catalog | pg_get_viewdef              | text             | oid                   | normal
 pg_catalog | pg_get_viewdef              | text             | oid, boolean          | normal
 pg_catalog | pg_get_viewdef              | text             | oid, integer          | normal
 pg_catalog | pg_get_viewdef              | text             | text                  | normal
 pg_catalog | pg_get_viewdef              | text             | text, boolean         | normal

有了这些要素,我们就可以利用UDF,实时的记录函数的内容,并提到版本管理库了。

HOOK(钩子)

在代码中,我们可以得到目前PG已经定义了哪些HOOK,允许你使用其进行旁路。

grep -i hook src/tools/pgindent/typedefs.list  

ClientAuthentication_hook_type
CoerceParamHook
ExecutorCheckPerms_hook_type
ExecutorEnd_hook_type
ExecutorFinish_hook_type
ExecutorRun_hook_type
ExecutorStart_hook_type
ExplainOneQuery_hook_type
FmgrHookEventType
GucBoolAssignHook
GucBoolCheckHook
GucEnumAssignHook
GucEnumCheckHook
GucIntAssignHook
GucIntCheckHook
GucRealAssignHook
GucRealCheckHook
GucShowHook
GucStringAssignHook
GucStringCheckHook
PGNoticeHooks
ParamFetchHook
ParseParamRefHook
ParserSetupHook
PostParseColumnRefHook
PreParseColumnRefHook
ProcessUtility_hook_type
VariableAssignHook
check_password_hook_type
create_upper_paths_hook_type
emit_log_hook_type
explain_get_index_name_hook_type
fmgr_hook_type
get_attavgwidth_hook_type
get_index_stats_hook_type
get_relation_info_hook_type
get_relation_stats_hook_type
join_search_hook_type
needs_fmgr_hook_type
object_access_hook_type
planner_hook_type
post_parse_analyze_hook_type
row_security_policy_hook_type
set_join_pathlist_hook_type
set_rel_pathlist_hook_type
shmem_startup_hook_type

例子

这些插件使用到数据库的HOOK,比如用来统计SQL的资源开销,认证延迟等。

contrib/pg_stat_statements/pg_stat_statements.c  

contrib/auto_explain/auto_explain.c  

contrib/auth_delay/auth_delay.c

本文的CASE,你如果要将CREATE FUNCTION的内容,自动写入SVN,也能使用钩子完成,不再举例。

本地表存储函数代码,版本管理

我们除了可以将代码存入版本管理软件GITHUB、GITLAB、SVN等,还有一种简便的方法,比如存入数据库的表里面。

例子

1. 创建存储函数代码的表

create table svn_func(
  id serial8 primary key,  -- 序列
  tx int8, -- 事务号
  objid oid, -- 函数唯一标示 pg_proc.oid
  object_type text, -- 类型
  schema_name text, -- schema name
  object_identity text, -- 全长对象名: schema_name.object_name
  in_extension bool, -- 对象是否属于extension
  crt_time timestamp, -- DDL时间
  content text  -- DDL翻译成文本
);

2. 创建事件触发器函数

create or replace function push_to_svn_func() returns event_trigger as $$
declare
  r record;
begin
  for r in SELECT * FROM pg_event_trigger_ddl_commands() LOOP
    insert into svn_func(tx, objid, object_type, schema_name, object_identity, in_extension, crt_time, content)
      values
       (
          txid_current(),
      r.objid,
          r.object_type,
          r.schema_name,
          r.object_identity,
          r.in_extension,
          now(),
          pg_get_functiondef(r.objid)
    );
  end LOOP;
end;
$$ language plpgsql strict;

3. 创建事件触发器

create event trigger et1 on ddl_command_end  when TAG in ('create function') execute procedure push_to_svn_func();

4. 测试

4.1 创建函数

create or replace function f123(id int) returns int as $$
declare
begin
return id+1;
end;
$$ language plpgsql strict;
CREATE FUNCTION

4.2 创建同名,但是参数不同的函数

create or replace function f123(id int, diff int) returns int as $$
declare
begin
return id+diff;
end;
$$ language plpgsql strict;
CREATE FUNCTION

4.3 创建完全相同的函数,写入不同的SCHEMA

postgres=# create schema test;
CREATE SCHEMA
postgres=# create or replace function test.f123(id int, diff int) returns int as $$
declare
begin
return id+diff;
end;
$$ language plpgsql strict;
CREATE FUNCTION

4.4 覆盖创建原有函数

postgres=# create or replace function test.f123(id int, diff int) returns int as $$
declare
begin
return id+diff;
end;
$$ language plpgsql strict;
CREATE FUNCTION

4.5 查看函数内容记录

postgres=# select * from svn_func;
-[ RECORD 1 ]---+----------------------------------------------------------------------------------------------------------------
id              | 1
tx              | 46056990
objid           | 172533
object_type     | function
schema_name     | public
object_identity | public.push_to_svn_func()
in_extension    | f
crt_time        | 2017-03-05 13:37:25.518273
content         | CREATE OR REPLACE FUNCTION public.push_to_svn_func()                                                           +
                |  RETURNS event_trigger                                                                                         +
                |  LANGUAGE plpgsql                                                                                              +
                |  STRICT                                                                                                        +
                | AS $function$                                                                                                  +
                | declare                                                                                                        +
                |   r record;                                                                                                    +
                | begin                                                                                                          +
                |   for r in SELECT * FROM pg_event_trigger_ddl_commands() LOOP                                                  +
                |     insert into svn_func(tx, objid, object_type, schema_name, object_identity, in_extension, crt_time, content)+
                |       values                                                                                                   +
                |        (                                                                                                       +
                |           txid_current(),                                                                                      +
                |   r.objid,                                                                                                     +
                |           r.object_type,                                                                                       +
                |           r.schema_name,                                                                                       +
                |           r.object_identity,                                                                                   +
                |           r.in_extension,                                                                                      +
                |           now(),                                                                                               +
                |           pg_get_functiondef(r.objid)                                                                          +
                | );                                                                                                             +
                |   end LOOP;                                                                                                    +
                | end;                                                                                                           +
                | $function$                                                                                                     +
                |
-[ RECORD 2 ]---+----------------------------------------------------------------------------------------------------------------
id              | 2
tx              | 46056991
objid           | 172508
object_type     | function
schema_name     | public
object_identity | public.f123(integer)
in_extension    | f
crt_time        | 2017-03-05 13:37:50.630288
content         | CREATE OR REPLACE FUNCTION public.f123(id integer)                                                             +
                |  RETURNS integer                                                                                               +
                |  LANGUAGE plpgsql                                                                                              +
                |  STRICT                                                                                                        +
                | AS $function$                                                                                                  +
                | declare                                                                                                        +
                | begin                                                                                                          +
                | return id+1;                                                                                                   +
                | end;                                                                                                           +
                | $function$                                                                                                     +
                |
-[ RECORD 3 ]---+----------------------------------------------------------------------------------------------------------------
id              | 3
tx              | 46056992
objid           | 172573
object_type     | function
schema_name     | public
object_identity | public.f123(integer,integer)
in_extension    | f
crt_time        | 2017-03-05 13:38:38.068266
content         | CREATE OR REPLACE FUNCTION public.f123(id integer, diff integer)                                               +
                |  RETURNS integer                                                                                               +
                |  LANGUAGE plpgsql                                                                                              +
                |  STRICT                                                                                                        +
                | AS $function$                                                                                                  +
                | declare                                                                                                        +
                | begin                                                                                                          +
                | return id+diff;                                                                                                +
                | end;                                                                                                           +
                | $function$                                                                                                     +
                |
-[ RECORD 4 ]---+----------------------------------------------------------------------------------------------------------------
id              | 4
tx              | 46056994
objid           | 172575
object_type     | function
schema_name     | test
object_identity | test.f123(integer,integer)
in_extension    | f
crt_time        | 2017-03-05 13:39:06.332268
content         | CREATE OR REPLACE FUNCTION test.f123(id integer, diff integer)                                                 +
                |  RETURNS integer                                                                                               +
                |  LANGUAGE plpgsql                                                                                              +
                |  STRICT                                                                                                        +
                | AS $function$                                                                                                  +
                | declare                                                                                                        +
                | begin                                                                                                          +
                | return id+diff;                                                                                                +
                | end;                                                                                                           +
                | $function$                                                                                                     +
                |
-[ RECORD 5 ]---+----------------------------------------------------------------------------------------------------------------
id              | 5
tx              | 46056995
objid           | 172575
object_type     | function
schema_name     | test
object_identity | test.f123(integer,integer)
in_extension    | f
crt_time        | 2017-03-05 13:39:14.66429
content         | CREATE OR REPLACE FUNCTION test.f123(id integer, diff integer)                                                 +
                |  RETURNS integer                                                                                               +
                |  LANGUAGE plpgsql                                                                                              +
                |  STRICT                                                                                                        +
                | AS $function$                                                                                                  +
                | declare                                                                                                        +
                | begin                                                                                                          +
                | return id+diff;                                                                                                +
                | end;                                                                                                           +
                | $function$                                                                                                     +
                |   

4.6 回退测试

比如你想将某个函数,回退到以前的版本,在svn_func表中选定一条ID的content, 执行即可。

do language plpgsql $$
declare
  sql text;
begin
  select content into sql from svn_func where id=2;
  execute sql;
end;
$$;

数据库直连代码库(github,gitlab,svn...)

前面的例子介绍了如何将函数版本存入表中,如果你想将函数内容存入代码管理库,也很简单,下面提供一些伪代码。

例子

1. 创建高级过程语言,通过他们编写的函数与代码管理库交互。

pljava u, plpython u, .....

2. 编写对应的pl函数,输入为content等, 写入代码管理库。

假设函数名为plpython_svn(content,其他参数);

3. 将第二步编写的函数,通过事件触发器调用。

4. 创建事件触发器函数

create or replace function push_to_svn_func() returns event_trigger as $$
declare
  r record;
begin
  for r in SELECT * FROM pg_event_trigger_ddl_commands() LOOP
    insert into svn_func(tx, objid, object_type, schema_name, object_identity, in_extension, crt_time, content)
      values
       (
          txid_current(),
      r.objid,
          r.object_type,
          r.schema_name,
          r.object_identity,
          r.in_extension,
          now(),
          pg_get_functiondef(r.objid)
    );
      -- 调用plpython_svnc(....), 将内容提交到SVN
  end LOOP;
end;
$$ language plpgsql strict;

5. 创建事件触发器

create event trigger et1 on ddl_command_end  when TAG in ('create function') execute procedure push_to_svn_func();

小结

1. 通过事件触发器、UDF,我们可以将DDL的内容写入表中,也可以提交到代码管理库中。

2. 事件触发器其他用途,譬如我们使用逻辑复制,DDL不记录在REDO中,幸好可以通过事件触发器完成DDL复制。

参考

https://www.postgresql.org/docs/9.6/static/functions-event-triggers.html

https://www.postgresql.org/docs/9.6/static/event-trigger-definition.html

《PostgreSQL Oracle 兼容性之 - 事件触发器实现类似Oracle的回收站功能》

《PostgreSQL 事件触发器 - DDL审计 , DDL逻辑复制 , 打造DDL统一管理入》

《PostgreSQL 事件触发器 - PostgreSQL 9.3 Event Trigger》

《PostgreSQL 事件触发器 - DDL审计 , DDL逻辑复制 , 打造DDL统一管理入》

pgsql-http插件

https://github.com/pramsey/pgsql-http

时间: 2024-09-11 03:15:45

数据库函数代码管理 - 自动备份函数\过程代码到SVN\github\gitlab的相关文章

数据库Oracle数据的异地的自动备份_oracle

正在看的ORACLE教程是:数据库Oracle数据的异地的自动备份.在大型商业应用中,数据的异地容灾备份十分重要,也必不可少.笔者根据自己的实践经验,设计了一套简洁地实现异地数据自动备份的方法,可供数据库管理人员参考.文中所有的程序都经过测试,运行良好.这里模拟的异地运行环境为一主一备两套Sun Solaris系统,所采用的备份方法为Oracle数据库提供的标准备份:export. 相关命令 文中主要用到三个命令,这里先单独介绍一下: export: 将数据库中数据备份成一个二进制系统文件,它有

mysql定时自动备份数据库脚本代码(linux/windows)

操作步骤: 方法一 1.安装p7zip:由于源里面没有此包,直接下载源码安装. 地址:http://p7zip.sourceforge.net/  代码如下 复制代码 wget http://sourceforge.net/projects/p7zip/files/p7zip/9.20.1/p7zip_9.20.1_src_all.tar.bz2 tar xvf p7zip_9.20.1_src_all.tar.bz2 cd p7zip_9.20.1_src_all make && mak

不怕丢数据 VPS定时自动备份终极指南

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 近半年,先后丢失5个网站数据,大多都是由于VPS硬盘损坏造成,RAID10为了速度是很不保险的 最近的2次分别 是DirectSpace 和 BuyVM 所以,必需备份,作好VPS随时准备丢失数据的准备 昨天花了一整天,把目前所有的站做了每日同步,以下分享给大家实际操作方法. 转载请注明原文出处 http://www.21andy.com/b

Centos使用crontab定时任务自动备份网站

不需要什么专业的软件,使用centos自身的命令及服务就可以完成每天的自动备份. 下面说下常见的备份方法: 1. 文件备份 用tar命令压缩文件做备份: #tar -zcvf backup.tar.gz dir1 dir1为要备份的目录或者文件 备份恢复的命令: #tar -zxvf backup.tar.gz 2. 数据库备份 大多数数据库系统有命令支持数据库的备份和恢复. 比如mysql, 数据库的备份命令为:  代码如下 复制代码 #mysqldump -host=dbhost -user

Centos中crontab自动备份网站实现方法

crontab简介 crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于"crontab"文件中,以供之后读取和执行.该词来源于希腊语 chronos(χρνο),原意是时间. 通常,crontab储存的指令被守护进程激活, crond常常在后台运行,每一分钟检查是否有预定的作业需要执行.这类作业一般称为cron jobs. crontab用法 crontab的格式如下面:  代码如下 复制代码 f1 f2

php分页函数示例代码分享

 这篇文章主要介绍了php分页函数示例代码,需要的朋友可以参考下 分享一例php分页函数代码,用此函数实现分页代码很不错.   代码,php分页函数.  代码如下: <?php /* * Created on 2011-07-28 * Author : LKK , http://lianq.net * 使用方法: require_once('mypage.php'); $result=mysql_query("select * from mytable", $myconn); $

如何自动备份公司数据到云存储

背景 做过运维的都知道,最怕就是数据丢失,其它故障什么都好说,数据没了,那可是要死人的,所以DBA这个岗位在公司都比较重视,当然薪水也不错,一个合格的DBA要做的事很多,远不止只做数据备份恢复工作(数据备份恢复是最基本基本的了),像调优.数据库设计什么的也都需要参与,甚至在项目立项时就要介入了. 之所以说了那么多关于DBA的事,就是简单跟大家铺垫下,告诉大家数据对公司或个人来讲是多么的重要,那既然重要,我们就要备份好它,一般做法都是数据库本机留一个备份,然后再异地备份一份,但是随着目前云成为大家

如何自动备份网站数据和数据

背景 网站的数据就是网站的灵魂,可是数据总是会因为各种原因,如服务器损坏,人员误操作而丢失 ,站长们最需要的就是能及时备份自己的数据.对于win服务器下的自动数据备份,网站文件,自然是比较好备份的,那么如果数据库也可以自动备份,那就基本实现完美自动备份. 阿里云对象存储(Object Storage Service,简称OSS),是阿里云对外提供的海量,安全,低成本,高可靠的云存储服务.用户可以通过调用API,在任何应用.任何时间.任何地点上传和下载数据,也可以通过用户Web控制台对数据进行简单

数据自动备份解决方案 图文_服务器其它

1:网盘自动备份(隔离备份) 隔离备份介绍:直接在网盘内建立项目.文件进行稿写操作很可能会与网盘数据不同步导致数据丢失完整性,对文件造成损坏,所以这种方式是不可取的.因此采用隔离备份,所谓隔离备份就是在A文件夹进行稿写,当关闭计算机时自动备份A文件夹的所有内容到 B文件夹(这里B文件夹是网盘目录)  进行隔离后,稿写与备份互不干扰,双份数据.达成目的流程如下: 1.开机时候网盘程序运行,自动备份网盘文件夹内的内容 2.关机时拷贝当前正在稿写的文件夹内容到网盘文件夹 数据测试截图:   左侧是网盘