我有一个在后端使用 postgres 数据库的项目,我正在创建一个 Dockerfile,它可以自动设置数据库的本地开发实例。该数据库包含大量函数,这些函数历来存储在上下文相关的 sql 文件中,例如用户.sql、公司.sql。这很好,因为每当进行更改时,我都可以简单地执行相关的 sql 文件,其中
REPLACE
所有视图并删除/重新创建所有函数。
但是,当尝试在新的 postgres 实例上运行这些脚本时(在
CREATE
ing所有表之后),它们会失败,因为大多数视图/函数引用尚未定义的其他视图函数。
我已经开始调查是否有一个特定的顺序可以运行脚本来避免这个问题,但是由于它们的设计没有考虑到这个目的,这可能是不可能的,并且有大量的实体在起作用,所以这很可能是一项不平凡的任务。
除了重构脚本之外,还有什么方法可以实现这一点吗?
您可以编写一个简单的递归查询,从不依赖于其他视图的所有视图开始,并递归地添加依赖于这些视图的视图。然后以正确的顺序输出这些视图的视图定义,您就得到了脚本:
WITH RECURSIVE viewids AS (
/* all views that don't depend on other views */
SELECT t.oid, 1 as level
FROM pg_class t
JOIN pg_rewrite AS r ON r.ev_class = t.oid
WHERE r.rulename = '_RETURN'
AND t.relkind = 'v'
AND t.relnamespace NOT IN ('pg_catalog'::regnamespace,
'information_schema'::regnamespace,
'pg_toast'::regnamespace)
AND NOT EXISTS (
/* depends on a view */
SELECT 1
FROM pg_depend AS d
JOIN pg_class AS t2 ON d.refobjid = t2.oid
WHERE d.objid = r.oid
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass
AND d.deptype = 'n'
AND d.refobjsubid <> 0
AND t2.relkind = 'v'
)
AND NOT EXISTS (
/* depends on an extension */
SELECT 1
FROM pg_depend
WHERE objid = t.oid
AND classid = 'pg_class'::regclass
AND refclassid = 'pg_extension'::regclass
AND deptype = 'e'
)
UNION ALL
/* all views that depend on these views */
SELECT t.oid, viewids.level + 1
FROM pg_class AS t
JOIN pg_rewrite AS r ON r.ev_class = t.oid
JOIN pg_depend AS d ON d.objid = r.oid
JOIN viewids ON viewids.oid = d.refobjid
WHERE t.relkind = 'v'
AND r.rulename = '_RETURN'
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass
AND d.deptype = 'n'
AND d.refobjsubid <> 0
)
/* order the views by level, eliminating duplicates */
SELECT format('CREATE VIEW %s AS%s',
oid::regclass,
pg_get_viewdef(oid::regclass))
FROM viewids
GROUP BY oid
ORDER BY max(level);
WITH RECURSIVE viewids AS (
/* all views that don't depend on other views */
SELECT t.oid, 1 as level
FROM pg_class t
JOIN pg_rewrite AS r ON r.ev_class = t.oid
WHERE r.rulename = '_RETURN'
AND t.relkind in ('v', 'm')
AND t.relnamespace NOT IN ('pg_catalog'::regnamespace,
'information_schema'::regnamespace,
'pg_toast'::regnamespace)
AND NOT EXISTS (
/* depends on a view */
SELECT 1
FROM pg_depend AS d
JOIN pg_class AS t2 ON d.refobjid = t2.oid
WHERE d.objid = r.oid
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass
AND d.deptype = 'n'
AND d.refobjsubid <> 0
AND t2.relkind in ('v', 'm')
)
AND NOT EXISTS (
/* depends on an extension */
SELECT 1
FROM pg_depend
WHERE objid = t.oid
AND classid = 'pg_class'::regclass
AND refclassid = 'pg_extension'::regclass
AND deptype = 'e'
)
UNION ALL
/* all views that depend on these views */
SELECT t.oid, viewids.level + 1
FROM pg_class AS t
JOIN pg_rewrite AS r ON r.ev_class = t.oid
JOIN pg_depend AS d ON d.objid = r.oid
JOIN viewids ON viewids.oid = d.refobjid
WHERE t.relkind IN ('v','m')
AND r.rulename = '_RETURN'
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass
AND d.deptype = 'n'
AND d.refobjsubid <> 0
), max_levels as (
select oid, max(level) as level
from viewids
group by oid
), views_q as (
SELECT
oid,
level,
format('CREATE %s %s AS%s',
CASE WHEN t.relkind = 'm' THEN 'MATERIALIZED VIEW'
ELSE 'VIEW'
END,
oid::regclass,
pg_get_viewdef(oid::regclass))
FROM max_levels
INNER JOIN pg_class t USING(oid)
ORDER BY level, oid
), index_q as (
select distinct on (objid)
objid as oid,
level + 0.5 as level,
pg_get_indexdef(objid) as indexdef
from pg_depend
inner join max_levels on refobjid = oid
where deptype = 'a'
order by objid
)
SELECT * FROM index_q
UNION ALL
SELECT * FROM views_q
ORDER BY level, oid;
WITH RECURSIVE viewids AS (
SELECT 'foo.bar'::regclass::oid, 1 as level
UNION ALL
SELECT t.oid, viewids.level + 1
FROM pg_class AS t
JOIN pg_rewrite AS r ON r.ev_class = t.oid
JOIN pg_depend AS d ON d.objid = r.oid
JOIN viewids ON viewids.oid = d.refobjid
WHERE t.relkind IN ('v','m')
AND r.rulename = '_RETURN'
AND d.classid = 'pg_rewrite'::regclass
AND d.refclassid = 'pg_class'::regclass
AND d.deptype = 'n'
AND d.refobjsubid <> 0
), max_levels as (
select oid, max(level) as level
from viewids
group by oid
), views_q as (
SELECT
oid,
level,
format('CREATE %s %s AS%s',
CASE WHEN t.relkind = 'm' THEN 'MATERIALIZED VIEW'
ELSE 'VIEW'
END,
oid::regclass,
pg_get_viewdef(oid::regclass))
FROM max_levels
INNER JOIN pg_class t USING(oid)
ORDER BY level, oid
), index_q as (
select distinct on (objid)
objid as oid,
level + 0.5 as level,
pg_get_indexdef(objid) as indexdef
from pg_depend
inner join max_levels on refobjid = oid
where deptype = 'a'
order by objid
)
SELECT * FROM index_q
UNION ALL
SELECT * FROM views_q
ORDER BY level, oid