特性介紹 | PostgreSQL 的依賴約束詳解 - 系統表 pg_depend & pg_constraint


本文首發於 2015-11-04 15:28:08

前言

本文成文較早,依賴的是 PostgreSQL 9.3 版本,后續內核版本可能不兼容,但核心原理是相通的,可做參考。

表結構

pg_depend

pg_depend 是 postgres 的一張系統表,用來記錄數據庫對象之間的依賴關系,除了常見的主外鍵,還有其他一些內部依賴關系,可以通過這個系統表呈現出來。

postgres=# \d+ pg_depend
                       Table "pg_catalog.pg_depend"
   Column    |  Type   | Modifiers | Storage | Stats target | Description
-------------+---------+-----------+---------+--------------+-------------
 classid     | oid     | not null  | plain   |              | 系統OID
 objid       | oid     | not null  | plain   |              | 對象OID
 objsubid    | integer | not null  | plain   |              |
 refclassid  | oid     | not null  | plain   |              | 引用系統OID
 refobjid    | oid     | not null  | plain   |              | 引用對象ID
 refobjsubid | integer | not null  | plain   |              |
 deptype     | "char"  | not null  | plain   |              | pg_depend類型
Indexes:
    "pg_depend_depender_index" btree (classid, objid, objsubid)
    "pg_depend_reference_index" btree (refclassid, refobjid, refobjsubid)
Has OIDs: no

OID 是 Object Identifier 的縮寫,是對象 ID 的意思,因為是無符號的4字節類型,表示范圍不夠大,所以一般不用做主鍵使用,僅用在系統內部,比如系統表等應用。可以與一些整型數字進行轉換。與之相關的系統參數是 default_with_oids ,默認是 off 。

pg_depend.deptype 字段自 9.1 版本之后多了一個 extension 的類型,目前類型有:

DEPENDENCY_NORMAL (n) :普通的依賴對象,如表與schema的關系。
DEPENDENCY_AUTO (a) :自動的依賴對象,如主鍵約束。
DEPENDENCY_INTERNAL (i) :內部的依賴對象,通常是對象本身。
DEPENDENCY_EXTENSION (e) :9.1新增的的擴展依賴。
DEPENDENCY_PIN (p) :系統內置的依賴。

pg_constraint

postgres=# \d pg_constraint
     Table "pg_catalog.pg_constraint"
    Column     |     Type     | Modifiers 
---------------+--------------+-----------
 conname       | name         | not null        -- 約束名
 connamespace  | oid          | not null        -- 約束所在命名空間的OID
 contype       | "char"       | not null        -- 約束類型
 condeferrable | boolean      | not null        -- 約束是否可以推遲
 condeferred   | boolean      | not null        -- 缺省情況下,約束是否可以推遲
 convalidated  | boolean      | not null        -- 約束是否經過驗證
 conrelid      | oid          | not null        -- 約束所在的表的OID
 contypid      | oid          | not null        -- 約束所在的域的OID
 conindid      | oid          | not null        -- 如果是唯一、主鍵、外鍵或排除約束,則為支持這個約束的索引;否則為0
 confrelid     | oid          | not null        -- 如果是外鍵,則為參考的表;否則為 0
 confupdtype   | "char"       | not null        -- 外鍵更新操作代碼
 confdeltype   | "char"       | not null        -- 外鍵刪除操作代碼
 confmatchtype | "char"       | not null        -- 外鍵匹配類型
 conislocal    | boolean      | not null        
 coninhcount   | integer      | not null        -- 約束直接繼承祖先的數量
 connoinherit  | boolean      | not null        
 conkey        | smallint[]   |         -- 如果是表約束(包含外鍵,但是不包含約束觸發器),則是約束字段的列表
 confkey       | smallint[]   |         -- 如果是一個外鍵,是參考的字段的列表
 conpfeqop     | oid[]        |         -- 如果是一個外鍵,是PK = FK比較的相等操作符的列表
 conppeqop     | oid[]        |        -- 如果是一個外鍵,是PK = PK比較的相等操作符的列表
 conffeqop     | oid[]        |         -- 如果是一個外鍵,是FK = FK比較的相等操作符的列表
 conexclop     | oid[]        |         -- 如果是一個排除約束,是每個字段排除操作符的列表
 conbin        | pg_node_tree |         -- 如果是一個檢查約束,那就是其表達式的內部形式
 consrc        | text         |         -- 如果是檢查約束,則是表達式的人類可讀形式
Indexes:
    "pg_constraint_oid_index" UNIQUE, btree (oid)
    "pg_constraint_conname_nsp_index" btree (conname, connamespace)
    "pg_constraint_conrelid_index" btree (conrelid)
    "pg_constraint_contypid_index" btree (contypid)

查詢依賴關系的 SQL

如下 SQL 可以列出系統和用戶對象的各種依賴關系:

SELECT classid::regclass AS "depender object class",
    CASE classid
        WHEN 'pg_class'::regclass THEN objid::regclass::text
        WHEN 'pg_type'::regclass THEN objid::regtype::text
        WHEN 'pg_proc'::regclass THEN objid::regprocedure::text
        ELSE objid::text
    END AS "depender object identity",
    objsubid,
    refclassid::regclass AS "referenced object class",
    CASE refclassid
        WHEN 'pg_class'::regclass THEN refobjid::regclass::text
        WHEN 'pg_type'::regclass THEN refobjid::regtype::text
        WHEN 'pg_proc'::regclass THEN refobjid::regprocedure::text
        ELSE refobjid::text
    END AS "referenced object identity",
    refobjsubid,
    CASE deptype
        WHEN 'p' THEN 'pinned'
        WHEN 'i' THEN 'internal'
        WHEN 'a' THEN 'automatic'
        WHEN 'n' THEN 'normal'
    END AS "dependency type"
FROM pg_catalog.pg_depend WHERE (objid >= 16384 OR refobjid >= 16384);

我通常喜歡在 where 后面加個條件 and deptype <>'i' ,以排除 internal 依賴。

示例

創建一張表:

postgres=# create table tbl_parent(id int);
CREATE TABLE

執行查詢依賴關系的 SQL:

postgres=# 執行上面的SQL;
 depender object class | depender object identity | objsubid | referenced object class | referenced object identity | refobjsubid | dependency type
-----------------------+--------------------------+----------+-------------------------+------------- pg_class              | tbl_parent               |        0 | pg_namespace            | 2200                       |           0 | normal
(1 row)

看起來只是建了個表,沒有約束,實際上該表是建立在 schema 下面的,因此只依賴於 schema 。

添加主鍵約束:

postgres=# alter table tbl_parent add primary key(id);
ALTER TABLE
 depender object class | depender object identity | objsubid | referenced object class | referenced object identity | refobjsubid | dependency type
-----------------------+--------------------------+----------+-------------------------+------- pg_class              | tbl_parent               |        0 | pg_namespace            | 2200                       |           0 | normal
 pg_constraint         | 16469                    |        0 | pg_class                | tbl_parent                 |           1 | automatic
(2 rows)

約束類型變為了automatic,表明這個主鍵約束是依賴於表上的,是自動模式,詳細信息可以在系統表 pg_constrant 里面查詢。

正常情況下用戶刪除有依賴關系的對象時,會提示需要先刪除依賴的對象。但是如果通過系統表刪除有依賴關系的對象時,若操作有誤,就會導致異常。例如:下面的操作就會導致報錯cache lookup failed for constraint

postgres=# select oid,conname,connamespace,contype from pg_constraint where conname like 'tbl_parent%';
  oid  |     conname     | connamespace | contype
-------+-----------------+--------------+---------
 16469 | tbl_parent_pkey |         2200 | p
(1 row)
 
postgres=# delete from pg_constraint where conname like 'tbl_parent%';
DELETE 1
postgres=# select oid,conname,connamespace,contype from pg_constraint where conname like 'tbl_parent%';
 oid | conname | connamespace | contype
-----+---------+--------------+---------
(0 rows)
 
postgres=# drop table tbl_parent;
ERROR:  cache lookup failed for constraint 16469   --16496是約束的OID
postgres=#

之所以出現該報錯,是因為手動把約束對象刪除了,但在 pg_depend 里卻仍然存在依賴關系,因此,刪除該表時,由於找不到最里層的依賴對象而報錯。


歡迎關注我的微信公眾號【數據庫內核】:分享主流開源數據庫和存儲引擎相關技術。

MySQL數據庫技術
標題 網址
GitHub https://dbkernel.github.io
知乎 https://www.zhihu.com/people/dbkernel/posts
思否(SegmentFault) https://segmentfault.com/u/dbkernel
掘金 https://juejin.im/user/5e9d3ed251882538083fed1f/posts
開源中國(oschina) https://my.oschina.net/dbkernel
博客園(cnblogs) https://www.cnblogs.com/dbkernel


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM