我是 PG/GP 的新手。 我需要编写函数返回完整的 Greenplum 表定义(如 dbeaver DDL 选项卡中)。在我的系统功能
get_table_def
中,catalog.pg_gettabledef
不存在,或者对我来说不可用。
不知道为什么,可能是用户权限不够。
我已经找到一个函数,它返回
setof text
,并带有表结构定义:
CREATE OR REPLACE FUNCTION describe_table(p_schema_name varchar, p_table_name varchar)
RETURNS SETOF text
LANGUAGE plpgsql
VOLATILE
AS $$
DECLARE
v_table_ddl text;
column_record record;
table_rec record;
constraint_rec record;
firstrec boolean;
BEGIN
FOR table_rec IN
SELECT c.relname, c.oid FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE relkind = 'r'
AND n.nspname = p_schema_name
AND relname~ ('^('||p_table_name||')$')
ORDER BY c.relname
LOOP
FOR column_record IN
SELECT
b.nspname as schema_name,
b.relname as table_name,
a.attname as column_name,
pg_catalog.format_type(a.atttypid, a.atttypmod) as column_type,
CASE WHEN
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) IS NOT NULL THEN
'DEFAULT '|| (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
FROM pg_catalog.pg_attrdef d
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)
ELSE
''
END as column_default_value,
CASE WHEN a.attnotnull = true THEN
'NOT NULL'
ELSE
'NULL'
END as column_not_null,
a.attnum as attnum,
e.max_attnum as max_attnum
FROM
pg_catalog.pg_attribute a
INNER JOIN
(SELECT c.oid,
n.nspname,
c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.oid = table_rec.oid
ORDER BY 2, 3) b
ON a.attrelid = b.oid
INNER JOIN
(SELECT
a.attrelid,
max(a.attnum) as max_attnum
FROM pg_catalog.pg_attribute a
WHERE a.attnum > 0
AND NOT a.attisdropped
GROUP BY a.attrelid) e
ON a.attrelid=e.attrelid
WHERE a.attnum > 0
AND NOT a.attisdropped
ORDER BY a.attnum
LOOP
IF column_record.attnum = 1 THEN
v_table_ddl:='CREATE TABLE '||column_record.schema_name||'.'||column_record.table_name||' (';
ELSE
v_table_ddl:=v_table_ddl||',';
END IF;
IF column_record.attnum <= column_record.max_attnum THEN
v_table_ddl:=v_table_ddl||chr(10)||
' '||column_record.column_name||' '||column_record.column_type||' '||column_record.column_default_value||' '||column_record.column_not_null;
END IF;
END LOOP;
firstrec := TRUE;
FOR constraint_rec IN
SELECT conname, pg_get_constraintdef(c.oid) as constrainddef
FROM pg_constraint c
WHERE conrelid=(
SELECT attrelid FROM pg_attribute
WHERE attrelid = (
SELECT oid FROM pg_class WHERE relname = table_rec.relname
AND relnamespace = (SELECT ns.oid FROM pg_namespace ns WHERE ns.nspname = p_schema_name)
) AND attname='tableoid'
)
LOOP
v_table_ddl:=v_table_ddl||','||chr(10);
v_table_ddl:=v_table_ddl||'CONSTRAINT '||constraint_rec.conname;
v_table_ddl:=v_table_ddl||chr(10)||' '||constraint_rec.constrainddef;
firstrec := FALSE;
END LOOP;
v_table_ddl:=v_table_ddl||');';
RETURN NEXT v_table_ddl;
END LOOP;
END;
$$
EXECUTE ON ANY;
现在我想编写另一个类似的函数,它基于选择3行数据集返回
setof text
(Greenplum表的其他部分DDL,其中包含分区信息):
CREATE OR REPLACE FUNCTION kasudra_dds.describe_table1(p_schema_name varchar, p_table_name varchar)
RETURNS SETOF text
LANGUAGE plpgsql
VOLATILE
AS $$
DECLARE
v_table_ddl text;
column_record record;
table_rec record;
constraint_rec record;
firstrec boolean;
BEGIN
FOR table_rec IN
select
n.nspname as schemaname,
c.relname as tablename,
a.attname as columnname,
p.parlevel as partitionlevel,
p.i + 1 as position_in_partition_key
from
pg_namespace n,
pg_class c,
pg_attribute a,
(
select
p.parrelid,
p.parlevel,
p.paratts[i.i] as attnum,
i.i
from
pg_partition p,
generate_series(0,
(
select
max(array_upper(pg_partition.paratts, 1)) as max
from
pg_partition)) i(i)
where
p.paratts[i.i] is not null) p
where
p.parrelid = c.oid and
c.relnamespace = n.oid and
p.attnum = a.attnum and
a.attrelid = c.oid and
n.nspname = $1 and
c.relname = $2
order by
p.parlevel
LOOP
v_table_ddl:=v_table_ddl||'PARTITION BY LIST ('||table_rec.columnname||')'||chr(10);
RETURN NEXT v_table_ddl;
END LOOP;
END;
$$
EXECUTE ON ANY;
但它返回 3 行,其中包含
null
值,而不是 setof text
。
我哪里做错了?
您得到
null
,因为您没有初始化变量 v_table_ddl
,因此它默认为 null
。 null || anything
是 null
。
v_table_ddl text := ''; -- init to empty string
在
DECLARE
部分可以解决这个问题。
但是整个功能看起来很复杂。我怀疑你一开始就想返回
setof text
。看起来您只想返回一个串联的 text
值。而且您很可能也不需要循环。
模板功能看起来也不是很成熟。其一,它在没有正确双引号的情况下连接标识符,这对 SQL 注入是开放的。参见:
至少可以用普通 SQL 中的聚合来替换内循环。
要在 Postgres 中获取重新设计的
CREATE TABLE
脚本,请使用 pgAdmin(或任何执行此操作的 GUI),或从 shell 中使用 pg_dump
:
pg_dump db_name -t 'schema_name.table_name' --schema-only --no-owner --no-acl --no-comments --no-tablespaces
相关: