【问题标题】:Find dependent objects for a table or view查找表或视图的依赖对象
【发布时间】:2011-05-26 15:53:59
【问题描述】:

背景

在 PostgreSQL 中删除(或替换)对象时,如果存在依赖关系,则删除将失败(不指定 CASCADE)。

问题

数据库返回的错误信息没有列出依赖对象。

示例解决方案

查询可能类似于:

SELECT * FROM information_schema i, pg_depend pd WHERE
  i.object_id = pd.object_id AND
  i.object_type = 'TABLE' AND
  i.object_schema = 'public' AND
  i.object_name = 'table_with_dependents';

objid 不见了。

相关

问题

如何按名称和类型生成依赖对象列表?

【问题讨论】:

  • 作为jpmc26 noted in a comment,在 Postgres 9.3+ 中,错误消息确实列出了依赖对象。
  • @Wildcard:错误消息对于机器解析来说太脆弱了。

标签: postgresql metadata


【解决方案1】:

建议的解决方案不适用于我的 postgresql 9.1.4

这行得通:

SELECT dependent_ns.nspname as dependent_schema
, dependent_view.relname as dependent_view 
, source_ns.nspname as source_schema
, source_table.relname as source_table
, pg_attribute.attname as column_name
FROM pg_depend 
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid 
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid 
JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid 
JOIN pg_attribute ON pg_depend.refobjid = pg_attribute.attrelid 
    AND pg_depend.refobjsubid = pg_attribute.attnum 
JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
WHERE 
source_ns.nspname = 'my_schema'
AND source_table.relname = 'my_table'
AND pg_attribute.attnum > 0 
AND pg_attribute.attname = 'my_column'
ORDER BY 1,2;

【讨论】:

  • 如果您能显示dependee.relname 的架构会很好,因为这样您知道对象名称但不知道找到它的架构。并且由于不同,如果在不同的方案中有两个具有相同 relname 的对象,你会认为只有一个依赖
  • @prince 获取架构,将 ns.nspname 添加到 SELECT 列表,将 JOIN pg_namespace ns ON ns.oid = dependee.relnamespace 添加到 FROM 列表。
【解决方案2】:

简单的方法是:

BEGIN;
DROP TABLE tablename CASCADE;
DROP VIEW viewname CASCADE;
ROLLBACK;

【讨论】:

  • 我确信有更好的方法涉及访问 pg_depend 和 pg_class 目录。运行 psql -E 然后使用 \d 查看表应该会显示 psql 用来确定什么依赖于什么的查询。
  • 我认为 9.3+ 中不需要 CASCADE。错误消息详细信息现在包括无法删除的依赖对象。
  • 如果你要删除一个表,你需要包含“table”:begin;删除 table fld_ovrd 级联;回滚;
  • 对于该列,它将是:ALTER TABLE table_name DROP COLUMN column_name;
  • 关于CASCADE。如果您指定 CASCADE 并且对象有太多的依赖对象,日志将被压缩为如下消息:[2021-09-21 13:37:38] [00000] drop cascades to 22 other objects。但是,如果您不指定 CASCADE 并且存在依赖对象,则情况会变得更糟,因为事务将永远不会关闭(ROLLBACK 永远不会执行)。因此,您需要手动关闭会话或运行ROLLBACK;。否则你可能想知道为什么像SELECT 1; 这样的命令会重复失败的事务^
【解决方案3】:

对于 PostgreSQL 9.3 以后的版本,使用以下视图和函数来显示任何用户对象依赖关系。我还更新了https://wiki.postgresql.org/wiki/Pg_depend_display。

/**** Usage Examples ****
-- Examine the entire object hierarchy
SELECT report.dependency_tree('');

-- Dependencies for any relations with names containing match (in regular expression)
SELECT report.dependency_tree('match');

-- Dependencies for relations person & address
SELECT report.dependency_tree('{person,address}'::text[]);

-- Dependencies for function slice
SELECT report.dependency_tree(ARRAY['slice'::regproc]);

-- Dependencies for type hstore
SELECT report.dependency_tree(ARRAY['hstore'::regtype]);

-- Dependencies for triggers by the name updated
SELECT report.dependency_tree(ARRAY(
  SELECT oid FROM pg_trigger WHERE tgname ~ 'updated'
  ));

-- Dependencies for foreign key constraint names starting with product
SELECT report.dependency_tree(ARRAY(
  SELECT oid FROM pg_constraint
  WHERE conname ~ '^product.*_fk'
  ));
*/

DROP VIEW IF EXISTS report.dependency;
CREATE OR REPLACE VIEW report.dependency AS
WITH RECURSIVE preference AS (
  SELECT 10 AS max_depth
    , 16384 AS min_oid -- user objects only
    , '^(londiste|pgq|pg_toast)'::text AS schema_exclusion
    , '^pg_(conversion|language|ts_(dict|template))'::text AS class_exclusion
    , '{"SCHEMA":"00", "TABLE":"01", "TABLE CONSTRAINT":"02", "DEFAULT VALUE":"03",
        "INDEX":"05", "SEQUENCE":"06", "TRIGGER":"07", "FUNCTION":"08",
        "VIEW":"10", "MATERIALIZED VIEW":"11", "FOREIGN TABLE":"12"}'::json AS type_sort_orders
)
, dependency_pair AS (
    SELECT objid
      , array_agg(objsubid ORDER BY objsubid) AS objsubids
      , upper(obj.type) AS object_type
      , coalesce(obj.schema, substring(obj.identity, E'(\\w+?)\\.'), '') AS object_schema
      , obj.name AS object_name
      , obj.identity AS object_identity
      , refobjid
      , array_agg(refobjsubid ORDER BY refobjsubid) AS refobjsubids
      , upper(refobj.type) AS refobj_type
      , coalesce(CASE WHEN refobj.type='schema' THEN refobj.identity
                                                ELSE refobj.schema END
          , substring(refobj.identity, E'(\\w+?)\\.'), '') AS refobj_schema
      , refobj.name AS refobj_name
      , refobj.identity AS refobj_identity
      , CASE deptype
            WHEN 'n' THEN 'normal'
            WHEN 'a' THEN 'automatic'
            WHEN 'i' THEN 'internal'
            WHEN 'e' THEN 'extension'
            WHEN 'p' THEN 'pinned'
        END AS dependency_type
    FROM pg_depend dep
      , LATERAL pg_identify_object(classid, objid, 0) AS obj
      , LATERAL pg_identify_object(refclassid, refobjid, 0) AS refobj
      , preference
    WHERE deptype = ANY('{n,a}')
    AND objid >= preference.min_oid
    AND (refobjid >= preference.min_oid OR refobjid = 2200) -- need public schema as root node
    AND coalesce(obj.schema, substring(obj.identity, E'(\\w+?)\\.'), '') !~ preference.schema_exclusion
    AND coalesce(CASE WHEN refobj.type='schema' THEN refobj.identity
                                                ELSE refobj.schema END
          , substring(refobj.identity, E'(\\w+?)\\.'), '') !~ preference.schema_exclusion
    GROUP BY objid, obj.type, obj.schema, obj.name, obj.identity
      , refobjid, refobj.type, refobj.schema, refobj.name, refobj.identity, deptype
)
, dependency_hierarchy AS (
    SELECT DISTINCT
        0 AS level,
        refobjid AS objid,
        refobj_type AS object_type,
        refobj_identity AS object_identity,
        --refobjsubids AS objsubids,
        NULL::text AS dependency_type,
        ARRAY[refobjid] AS dependency_chain,
        ARRAY[concat(preference.type_sort_orders->>refobj_type,refobj_type,':',refobj_identity)] AS dependency_sort_chain
    FROM dependency_pair root
    , preference
    WHERE NOT EXISTS
       (SELECT 'x' FROM dependency_pair branch WHERE branch.objid = root.refobjid)
    AND refobj_schema !~ preference.schema_exclusion
    UNION ALL
    SELECT
        level + 1 AS level,
        child.objid,
        child.object_type,
        child.object_identity,
        --child.objsubids,
        child.dependency_type,
        parent.dependency_chain || child.objid,
        parent.dependency_sort_chain || concat(preference.type_sort_orders->>child.object_type,child.object_type,':',child.object_identity)
    FROM dependency_pair child
    JOIN dependency_hierarchy parent ON (parent.objid = child.refobjid)
    , preference
    WHERE level < preference.max_depth
    AND child.object_schema !~ preference.schema_exclusion
    AND child.refobj_schema !~ preference.schema_exclusion
    AND NOT (child.objid = ANY(parent.dependency_chain)) -- prevent circular referencing
)
SELECT * FROM dependency_hierarchy
ORDER BY dependency_chain ;

-- Procedure to report depedency tree using regexp search pattern (relation-only)
CREATE OR REPLACE FUNCTION report.dependency_tree(search_pattern text)
  RETURNS TABLE(dependency_tree text)
  SECURITY DEFINER LANGUAGE SQL
  AS $function$
WITH target AS (
  SELECT objid, dependency_chain
  FROM report.dependency
  WHERE object_identity ~ search_pattern
)
, list AS (
  SELECT
    format('%*s%s %s', -4*level
          , CASE WHEN object_identity ~ search_pattern THEN '*' END
          , object_type, object_identity
    ) AS dependency_tree
  , dependency_sort_chain
  FROM target
  JOIN report.dependency report
    ON report.objid = ANY(target.dependency_chain) -- root-bound chain
    OR target.objid = ANY(report.dependency_chain) -- leaf-bound chain
  WHERE length(search_pattern) > 0
  -- Do NOT waste search time on blank/null search_pattern.
  UNION
  -- Query the entire dependencies instead.
  SELECT
    format('%*s%s %s', 4*level, '', object_type, object_identity) AS depedency_tree
  , dependency_sort_chain
  FROM report.dependency
  WHERE length(coalesce(search_pattern,'')) = 0
)
SELECT dependency_tree FROM list
ORDER BY dependency_sort_chain;
$function$ ;

-- Procedure to report depedency tree by specific relation name(s) (in text array)
CREATE OR REPLACE FUNCTION report.dependency_tree(object_names text[])
  RETURNS TABLE(dependency_tree text)
  SECURITY DEFINER LANGUAGE SQL
  AS $function$
WITH target AS (
  SELECT objid, dependency_chain
  FROM report.dependency
  JOIN unnest(object_names) AS target(objname) ON objid = objname::regclass
)
, list AS (
  SELECT DISTINCT
    format('%*s%s %s', -4*level
          , CASE WHEN object_identity = ANY(object_names) THEN '*' END
          , object_type, object_identity
    ) AS dependency_tree
  , dependency_sort_chain
  FROM target
  JOIN report.dependency report
    ON report.objid = ANY(target.dependency_chain) -- root-bound chain
    OR target.objid = ANY(report.dependency_chain) -- leaf-bound chain
)
SELECT dependency_tree FROM list
ORDER BY dependency_sort_chain;
$function$ ;

-- Procedure to report depedency tree by oid
CREATE OR REPLACE FUNCTION report.dependency_tree(object_ids oid[])
  RETURNS TABLE(dependency_tree text)
  SECURITY DEFINER LANGUAGE SQL
  AS $function$
WITH target AS (
  SELECT objid, dependency_chain
  FROM report.dependency
  JOIN unnest(object_ids) AS target(objid) USING (objid)
)
, list AS (
  SELECT DISTINCT
    format('%*s%s %s', -4*level
          , CASE WHEN report.objid = ANY(object_ids) THEN '*' END
          , object_type, object_identity
    ) AS dependency_tree
  , dependency_sort_chain
  FROM target
  JOIN report.dependency report
    ON report.objid = ANY(target.dependency_chain) -- root-bound chain
    OR target.objid = ANY(report.dependency_chain) -- leaf-bound chain
)
SELECT dependency_tree FROM list
ORDER BY dependency_sort_chain;
$function$ ;

【讨论】:

    【解决方案4】:

    在查询中包含嵌套视图,如下所示:

    WITH RECURSIVE view_deps AS (
    SELECT DISTINCT dependent_ns.nspname as dependent_schema
    , dependent_view.relname as dependent_view
    , source_ns.nspname as source_schema
    , source_table.relname as source_table
    FROM pg_depend
    JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
    JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
    JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid
    JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
    JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
    WHERE NOT (dependent_ns.nspname = source_ns.nspname AND dependent_view.relname = source_table.relname)
    UNION
    SELECT DISTINCT dependent_ns.nspname as dependent_schema
    , dependent_view.relname as dependent_view
    , source_ns.nspname as source_schema
    , source_table.relname as source_table
    FROM pg_depend
    JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
    JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
    JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid
    JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
    JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
    INNER JOIN view_deps vd
        ON vd.dependent_schema = source_ns.nspname
        AND vd.dependent_view = source_table.relname
        AND NOT (dependent_ns.nspname = vd.dependent_schema AND dependent_view.relname = vd.dependent_view)
    )
    
    SELECT *
    FROM view_deps
    ORDER BY source_schema, source_table;
    

    如果您关心特定的表属性,请将其添加到递归 CTE 的顶部:

    JOIN pg_attribute ON pg_depend.refobjid = pg_attribute.attrelid 
        AND pg_depend.refobjsubid = pg_attribute.attnum 
    ...
    WHERE 
    source_ns.nspname = 'my_schema'
    AND source_table.relname = 'my_table'
    AND pg_attribute.attnum > 0 
    AND pg_attribute.attname = 'my_column'
    

    【讨论】:

      【解决方案5】:

      pg_constraint 包含数据库中的所有约束,您可以使用所有外键约束中的 confrelid 和 conrelid 列出依赖表的 oid

      查询看起来像这样

      select confrelid,conrelid from pg_constraint where contype='f';

      【讨论】:

        猜你喜欢
        • 2016-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多