为什么我的 table 从 pg_catalog.pg_class 中消失了? (或者,如何找到主键列?)

Why did my table disappear from pg_catalog.pg_class? (Alternatively, how to find primary key columns?)

使用来自 debian 的 postgres 10.4.2(实际上,使用 postgresql docker 容器。)

我按顺序应用了一些架构文件。这些创建、删除和更改 tables 以跟随架构随时间的演变。

然后,我 运行 一个程序,它从 pg_catalog 中选择 table 定义来为现有的 table 生成代码包装器。到目前为止,这一直很好。 (该程序的代码是我开发的,因此非常值得怀疑!)

我最近将 "alter table customer add field customer_stem varchar(255) not null default ''" 和 "create index on customer(customer_stem)" 添加到一个新的架构文件中。 现在,在查找主键的 table 的查询中不再找到客户 table。

我运行的查询是这样的:

    select c.relname, i.indkey 
    from pg_catalog.pg_index i 
    inner join pg_catalog.pg_class c 
      on i.indrelid=c.relfilenode 
    inner join pg_catalog.pg_tables t 
      on c.relname=t.tablename 
    where t.schemaname='public' 
      and i.indisprimary;

这应该会告诉我哪些索引是我的 table 的主键。 但是,"customer" table 现在不在该查询范围内——pg_class 中的 relfilenode 不再匹配 pg_index.

中的任何内容
# select relname, relfilenode from pg_catalog.pg_class where relname='customer';
 relname  | relfilenode
----------+-------------
 customer |       16512

observe_dev=# select count(1) from pg_catalog.pg_index where indrelid=16512;
 count
-------
     0
(1 row)

这个 table!

应该有一个主键和一个二级索引

所以,我的猜测是 "alter table" 语句以某种方式更改了 'customer' class 的 ID,使其不再匹配 "pg_index" table,但这看起来真的很奇怪。 PSQL 仍然知道定义是什么:

                                                    Table "public.customer"
    Column     |          Type          | Collation | Nullable |        Default        | Storage  | Stats target | Description
---------------+------------------------+-----------+----------+-----------------------+----------+--------------+-------------
 customer_id   | integer                |           | not null |                       | plain    |              |
 customer_name | character varying(255) |           | not null |                       | extended |              |
 customer_stem | character varying(255) |           | not null | ''::character varying | extended |              |
Indexes:
    "customer_pkey" PRIMARY KEY, btree (customer_id)
    "customer_customer_name_idx" btree (customer_name)
    "customer_customer_stem_idx" btree (customer_stem)
Referenced by:

也有一些外键关系。

因此,发生了以下两种情况之一: 1) alter table 以某种方式导致 pg_catalog 停止更新(这似乎不太可能。) 2) 我对如何找到每个 table 的主键列的研究是错误的,我看错了 tables/columns

很可能是 2),但如果是这样,我应该如何找到该信息?

我认为您可能应该在 pg_class.oid 栏中加入 pg_index,即:

select c.relname, i.indkey 
from pg_catalog.pg_index i 
inner join pg_catalog.pg_class c 
  on i.indrelid=c.oid  -- << HERE change c.relfilenode to c.oid 
left join pg_catalog.pg_tables t 
  on c.relname=t.tablename 
where t.schemaname='public' 
  and i.indisprimary;

请注意,oidselect * from pg_catalog.pg_class\d pg_catalog.pg_class 的输出中隐藏在 psql 中,但它作为 "hidden" 列存在。

我能够在本地复制您的问题,所以我猜测添加列或索引会更改 table 的 pg_class 记录中 relfilenode 的值。

另请参阅 https://wiki.postgresql.org/wiki/Retrieve_primary_key_columns,其中详细介绍了如何查询主键 - 可能值得在 pg_attribute 的连接中添加。有点 OT,但我发现值得注意的是 'my_table_name'::regclass 被 PostgreSQL 转换为适当的 pg_class.oid。您实际上可以通过 运行 select 'my_table_name'::regclass::oid!

进行测试(并获得任何 table 的 oid

出于兴趣,我还做了一些进一步的测试,以确定是哪个 table 更改触发了 pg_class.relfilenode 更改。看来 alter table add column ...default ''` 是原因。

在未指定 default 的情况下添加列时,该值保持不变。以下脚本演示了该行为:

create table test (id uuid, col1 varchar(255));
select c.oid, c.relfilenode, c.relname from pg_catalog.pg_class c where c.oid = 'test'::regclass;
alter table test add column col2 varchar(255) not null;
select c.oid, c.relfilenode, c.relname from pg_catalog.pg_class c where c.oid = 'test'::regclass;
alter table test add column col3 varchar(255) not null default '';
select c.oid, c.relfilenode, c.relname from pg_catalog.pg_class c where c.oid = 'test'::regclass;
drop table test;

因此,由于某种原因,添加具有默认值的列似乎会更改关系的文件节点。关于 relfilenode 和其他属性的更多详细信息,也值得阅读 PostgreSQL docs on pg_class

不要在名字文本上加入目录,你只需要数字id就可以加入(目录中的名字是区分大小写的,这只会让你感到困惑) .另外:您不需要 pg_tables 目录;这只是 pg_class

上的一个视图

WRT 消失的目录条目:您是否提交了您的变更 table DDL? DDL 需要对目录执行一些技巧以对其他会话隐藏新版本。


select c.relname, i.indkey,i.indexrelid , i.indrelid  
    from pg_catalog.pg_index i 
    inner join pg_catalog.pg_class c 
      on i.indrelid=c.relfilenode 
where c.relname='target';