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

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



前言

今天又遇到了一个问题,如何获取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 subprocess
import re
p_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.py
CREATE 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 EXTENSION

CREATE OR REPLACE FUNCTION gel_table_ddl(p_schema VARCHAR,p_database_name VARCHAR,p_table_name VARCHAR)
RETURNS VARCHAR
AS $$
import subprocess
import re
p_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_viewdef
pg_get_constraintdef
pg_get_functiondef
pg_get_indexdef
。但奇怪的是它缺少表ddl定义相关函数。

详细信息可以参考:https://www.postgresql.org/docs/current/functions-info.html

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

CREATE OR REPLACE FUNCTION tabledef(oidRETURNS text
LANGUAGE 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 128FROM 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_sql
FROM tabdef
$$;

执行函数结果如图所示:

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

3.使用pgddl插件

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

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

export PGHOME=/data/postgresql/pgsql
export 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




励志成为PostgreSQL大神

长按关注吧





文章转载自励志成为PostgreSQL大神,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论