暂无图片
暂无图片
6
暂无图片
暂无图片
1
暂无图片

PostgreSQL获取Table ddl的多种方式,您最中意哪种呢?

文章转载自公众号:励志成为PostgreSQL大神

作者:吟游诗人义宝

前言

今天又遇到了一个问题,如何获取PostgreSQL对象的DDL语句?在Oracle数据库中我们可以使用dbms_metadata.get_ddl函数来处理,那么如何在 PG中实现它呢?

获取DDL的一些方法

1.使用pg_dump来获取ddl

使用pg_dump获取ddl。这个方法最简单。

-s, --schema-only            dump only the schema, no data-t, --table=PATTERN          dump the specified table(s) only[postgres@centos8 ~]$ pg_dump -U postgres -h 192.168.56.119 -d postgres -s -t jobs | egrep -v "^--|^$|^SET"
复制

如下图所示:可以帮助我们显示table的ddl,权限,注释,索引,约束以及触发器。

实际上pg_dump命令行还不错,但需要输入的内容太多了。可考虑使用PL/Python将逻辑封装起来,我们先尝试在操作系统上使用python脚本调用pg_dump。

import subprocessimport rep_output = subprocess.check_output(["pg_dump""–-schema-only""--dbname=postgres""–-table=jobs""--username=postgres"],shell=True)regex_pat=r'(^CREATE TABLE.+?\);$)'matches=re.findall(regex_pat, p_output.decode("utf-8"),re.DOTALL|re.MULTILINE)ddl = matches[0]print(ddl)
复制

下面是我用python编写的脚本,使用的主要是subprocess模块的check_output调用pg_dump命令。接着将返回的字符串与正则表达式匹配,提取创建表的语句。

[postgres@centos8 ~]$ python3 a1.pyCREATE TABLE public.countries (    country_id character(2) NOT NULL,    country_name character varying(40),    region_id bigint);
复制

目前这个python脚本是写死的,测试功能可用。接下来还需要在postgresql中安装插件plpython3u(python3版本),创建python语法的函数。

postgres=# CREATE EXTENSION IF NOT EXISTS plpython3u ;CREATE EXTENSIONCREATE OR REPLACE FUNCTION gel_table_ddl(p_schema VARCHAR,p_database_name VARCHAR,p_table_name VARCHAR)RETURNS VARCHARAS $$import subprocessimport rep_output = subprocess.check_output(["pg_dump""–-schema-only""--dbname="+p_database_name, "–-table="+p_table_name, "--username="+p_schema],shell=True)regex_pat=r'(^CREATE TABLE.+?\);$)'matches=re.findall(regex_pat, p_output.decode("utf-8"),re.DOTALL|re.MULTILINE)ddl = matches[0]return ddl$$ LANGUAGE plpython3u SECURITY DEFINER;
复制

执行结果如图所示

有一点要注意,在我的函数中没有提到密码,密码建议配置. pgpass来实现免密。

2.使用系统函数

PostgreSQL自带了一些函数可以查看 DDL的定义,例如:pg_get_viewdefpg_get_constraintdefpg_get_functiondefpg_get_indexdef。但奇怪的是它缺少表ddl定义相关函数。

详细信息可以参考

https://www.postgresql.org/docs/current/functions-info.html

不过这问题也难不倒人。在stackoverflow发现一个脚本,非常好用。

CREATE OR REPLACE FUNCTION tabledef(oid) RETURNS textLANGUAGE sql STRICT AS $$/* snatched from https://github.com/filiprem/pg-tools */WITH attrdef AS (    SELECT        n.nspname,        c.relname,        pg_catalog.array_to_string(c.reloptions || array(select 'toast.' || x from pg_catalog.unnest(tc.reloptions) x), ', ') as relopts,        c.relpersistence,        a.attnum,        a.attname,        pg_catalog.format_type(a.atttypid, a.atttypmod) as atttype,        (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid, truefor 128) FROM pg_catalog.pg_attrdef d            WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) as attdefault,        a.attnotnull,        (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type t            WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation) as attcollation,        a.attidentity,        a.attgenerated    FROM pg_catalog.pg_attribute a    JOIN pg_catalog.pg_class c ON a.attrelid = c.oid    JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid    LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)    WHERE a.attrelid = $1        AND a.attnum > 0        AND NOT a.attisdropped    ORDER BY a.attnum),coldef AS (    SELECT        attrdef.nspname,        attrdef.relname,        attrdef.relopts,        attrdef.relpersistence,        pg_catalog.format(            '%I %s%s%s%s%s',            attrdef.attname,            attrdef.atttype,            case when attrdef.attcollation is null then '' else pg_catalog.format(' COLLATE %I', attrdef.attcollation) end,            case when attrdef.attnotnull then ' NOT NULL' else '' end,            case when attrdef.attdefault is null then ''                else case when attrdef.attgenerated = 's' then pg_catalog.format(' GENERATED ALWAYS AS (%s) STORED', attrdef.attdefault)                    when attrdef.attgenerated <> '' then ' GENERATED AS NOT_IMPLEMENTED'                    else pg_catalog.format(' DEFAULT %s', attrdef.attdefault)                end            end,            case when attrdef.attidentity<>'' then pg_catalog.format(' GENERATED %s AS IDENTITY',                    case attrdef.attidentity when 'd' then 'BY DEFAULT' when 'a' then 'ALWAYS' else 'NOT_IMPLEMENTED' end)                else '' end        ) as col_create_sql    FROM attrdef    ORDER BY attrdef.attnum),tabdef AS (    SELECT        coldef.nspname,        coldef.relname,        coldef.relopts,        coldef.relpersistence,        string_agg(coldef.col_create_sql, E',\n    ') as cols_create_sql    FROM coldef    GROUP BY        coldef.nspname, coldef.relname, coldef.relopts, coldef.relpersistence)SELECT    format(        'CREATE%s TABLE %I.%I%s%s%s;',        case tabdef.relpersistence when 't' then ' TEMP' when 'u' then ' UNLOGGED' else '' end,        tabdef.nspname,        tabdef.relname,        coalesce(            (SELECT format(E'\n    PARTITION OF %I.%I %s\n', pn.nspname, pc.relname,                pg_get_expr(c.relpartbound, c.oid))                FROM pg_class c JOIN pg_inherits i ON c.oid = i.inhrelid                JOIN pg_class pc ON pc.oid = i.inhparent                JOIN pg_namespace pn ON pn.oid = pc.relnamespace                WHERE c.oid = $1),            format(E' (\n    %s\n)', tabdef.cols_create_sql)        ),        case when tabdef.relopts <> '' then format(' WITH (%s)', tabdef.relopts) else '' end,        coalesce(E'\nPARTITION BY '||pg_get_partkeydef($1), '')    ) as table_create_sqlFROM tabdef$$;
复制

执行函数结果如图所示:

若要获取索引,直接使用系统自带的pg_get_indexdef函数。

3.使用pgddl插件

最后来介绍的是一款插件,知道和使用它的人不多,看了一下作者写的 roadmap,还是很有动力的。我测试了一下,在PostgreSQL 13版本上也可以使用,就列出来作为一种选择。

要使用root用户安装此插件,并在安装时设置好环境变量。

export PGHOME=/data/postgresql/pgsqlexport PGDATA=/data/postgresql/pgdata[root@centos8 pgddl]# make[root@centos8 pgddl]# make install/usr/bin/mkdir -p '/data/postgresql/pgsql/share/extension'/usr/bin/mkdir -p '/data/postgresql/pgsql/share/extension'/usr/bin/install -c -m 644 .//ddlx.control '/data/postgresql/pgsql/share/extension/'/usr/bin/install -c -m 644  ddlx--0.17.sql '/data/postgresql/pgsql/share/extension/'
复制

在完成安装之后,进入PostgreSQL数据库创建扩展。

postgres=# CREATE EXTENSION ddlx;CREATE EXTENSION
复制

执行结果如图所示,像pg_dump一样,您也可以显示这个表中所有的相关信息。

如果想要单独显示其他的对象,只需输入一个名字,它会自动将该名字的ddl全部列出来。

参考链接

1.https://stackoverflow.com/questions/1846542/postgresql-get-table-definition-pg-get-tabledef

2.https://github.com/lacanoid/pgddl

3.https://proboscid.wordpress.com/2013/09/06/extracting-create-table-ddl-from-postgresql/

4.https://www.postgresql.org/docs/current/functions-info.html

规模空前,再创历史 | 2020 PG亚洲大会圆满结束
PG ACE计划的正式发布
三期PostgreSQL国际线上沙龙活动的举办
六期PostgreSQL国内线上沙龙活动的举办

中国PostgreSQL分会与腾讯云战略合作协议签订

PostgreSQL 13.0 正式版发布通告

深度报告:开源协议那些事儿

从“非主流”到“潮流”,开源早已值得拥有

Oracle中国正在进行新一轮裁员,传 N+6 补偿

PostgreSQL与MySQL版权比较

新闻|Babelfish使PostgreSQL直接兼容SQL Server应用程序

四年三冠,PostgreSQL再度荣获“年度数据库”

更多新闻资讯行业动态技术热点,请关注中国PostgreSQL分会官方网站

https://www.postgresqlchina.com

中国PostgreSQL分会生态产品

https://www.pgfans.cn

中国PostgreSQL分会资源下载站

https://www.postgreshub.cn

点击此处阅读原文

↓↓↓

文章转载自开源软件联盟PostgreSQL分会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

palosky
暂无图片
3年前
评论
暂无图片 0
pg_dump+plpython3u封装存储过程,相对而言是这几种方法中最好的。但是pg_dump本身不支持分区表完整的定义,只能将分区表的父表和字表分别导出(遍写循环提取也行),且有bug,即父表定义主键或普通索引时,导出的字子仍然有主键和索引的SQL,会有重复(当然你可以用正则表达式给去掉,较为繁琐)。pgddl这个插件中的ddlx_script对分区表支持较好,但是当分区表的主键是serial时,会漏掉sequence的ddl语句。文中提到的第二种方式,只能导出建表语句,没有注释和索引,没有serial相关的sequence定义。结论:目前没有一种完美的方式,能够完整提取DDL脚本。
3年前
暂无图片 点赞
评论