如何在 postgres 中自动交换 table 名称及其引用而不会出现任何问题?

How can I atomically swap table names and its references in postgres without any issues?

我想交换两个 table 的名字。表 A <> 表 B。也自动避免 read/writes 的任何问题。我知道我可以在交易中做到这一点。

我正在使用 CREATE TABLE TableB (LIKE TableA INCLUDING ALL); 创建 table。然后我还将 FK 从 A 复制到 B。然后我正在做一个 INSERT INTO TABLEA.... 来复制数据(与此无关)。完成所有这些后,我使用 ALTER TABLE RENAME 重命名 table 以在事务中交换名称 TableA <> TableB。所以 TableA 变成 TableA_OldTableB 变成新的 TableA (下面的例子)

但是,这不会更新其他 table 中对这个新 table 的引用,例如 TableC 仍然持有针对 TableA_Old 的 FK。当我在新重命名的 table TableA(之前是 TableB)上执行 \d+ 时,我没有看到引用。他们仍然指向 TableA_Old.

这是一个例子

我创建表A

CREATE TABLE TableA (
    id serial PRIMARY KEY
);


testdb=> \d TableA
                            Table "public.tablea"
 Column |  Type   | Collation | Nullable |              Default               
--------+---------+-----------+----------+------------------------------------
 id     | integer |           | not null | nextval('tablea_id_seq'::regclass)
Indexes:
    "tablea_pkey" PRIMARY KEY, btree (id)

我参照TableA创建TableC

CREATE TABLE TableC(
    id serial PRIMARY KEY,
    table_a_id INT,
   CONSTRAINT table_a_table_c_fk
      FOREIGN KEY(table_a_id) 
      REFERENCES TableA(id)
);


\d TableC
                              Table "public.tablec"
   Column   |  Type   | Collation | Nullable |              Default               
------------+---------+-----------+----------+------------------------------------
 id         | integer |           | not null | nextval('tablec_id_seq'::regclass)
 table_a_id | integer |           |          | 
Indexes:
    "tablec_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea(id)

可以参考一下

现在我使用函数

创建看起来像 TableATableB
create or replace function create_table_like(source_table text, new_table text)
returns void language plpgsql
as $$
declare
    rec record;
begin
    execute format(
        'create table %s (like %s including all)',
        new_table, source_table);
    for rec in
        select oid, conname
        from pg_constraint
        where contype = 'f' 
        and conrelid = source_table::regclass
    loop
        execute format(
            'alter table %s add constraint %s %s',
            new_table,
            replace(rec.conname, source_table, new_table),
            pg_get_constraintdef(rec.oid));
    end loop;
end $$;

select create_table_like('TableA', 'TableB');

\d TableB

testdb=> select create_table_like('TableA', 'TableB');
 create_table_like 
-------------------
 
(1 row)

testdb=> \d TableB

                            Table "public.tableb"
 Column |  Type   | Collation | Nullable |              Default               
--------+---------+-----------+----------+------------------------------------
 id     | integer |           | not null | nextval('tablea_id_seq'::regclass)
Indexes:
    "tableb_pkey" PRIMARY KEY, btree (id)

现在我重命名它们

BEGIN;
ALTER TABLE TableA RENAME to TableA_OLD;
ALTER TABLE TableB RENAME to TableA;
COMMIT;

现在看结构的时候,参考的还是TableA_OLD from TableC

testdb=> \d TableA
                            Table "public.tablea"
 Column |  Type   | Collation | Nullable |              Default               
--------+---------+-----------+----------+------------------------------------
 id     | integer |           | not null | nextval('tablea_id_seq'::regclass)
Indexes:
    "tableb_pkey" PRIMARY KEY, btree (id)

testdb=> \d TableB



testdb=> \d TableA_old
                          Table "public.tablea_old"
 Column |  Type   | Collation | Nullable |              Default               
--------+---------+-----------+----------+------------------------------------
 id     | integer |           | not null | nextval('tablea_id_seq'::regclass)
Indexes:
    "tablea_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "tablec" CONSTRAINT "table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea_old(id)

TableC指向旧的table - REFERENCES tablea_old(id)

testdb=> \d TableC
                              Table "public.tablec"
   Column   |  Type   | Collation | Nullable |              Default               
------------+---------+-----------+----------+------------------------------------
 id         | integer |           | not null | nextval('tablec_id_seq'::regclass)
 table_a_id | integer |           |          | 
Indexes:
    "tablec_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "table_a_table_c_fk" FOREIGN KEY (table_a_id) REFERENCES tablea_old(id)

有没有一种安全的方法可以做到这一点而无需再次删除和重新创建约束?

我知道我还可以更新 pg_class 中的 relfilenode 指针并交换它们。但是,这是有风险的,因为 TableB 的架构可能略有不同。所以,我想知道我是否可以更新系统目录中的其他一些 table 或使用不需要删除约束的 DDL。

一个对象的真实身份是它的对象 ID,一个用于在外键约束中引用对象的数字,其他内部数据库很重要(大多数函数体除外)。重命名对象不会改变其身份。您必须删除并重新创建外键约束。

如果您使用 rename table 命令,那么将自动重命名所有依赖对象、外键、链接到此 table 的引用。例如:

ALTER TABLE public.table1 RENAME TO table2;