Skip to content

[FEATURE] Support swapping primary keys instead of dropping + creatingΒ #250

@tonycosentini

Description

@tonycosentini

Describe the feature

It would be great if pg-schema-diff can support swapping primary keys instead of dropping and re-adding.

For example (I used pg-schema-diff 1.0.2 for this), here is a before schema:

CREATE SEQUENCE test_id_seq START WITH 1 NO MINVALUE NO MAXVALUE CACHE 1;

CREATE TABLE test (
	id BIGINT DEFAULT nextval('test_id_seq') NOT NULL,
    project_id BIGINT NOT NULL, 
   	PRIMARY KEY (id)
);

and an after schema:

CREATE SEQUENCE test_id_seq START WITH 1 NO MINVALUE NO MAXVALUE CACHE 1;

CREATE TABLE test (
	id BIGINT DEFAULT nextval('test_id_seq') NOT NULL,
    project_id BIGINT NOT NULL, 
   	PRIMARY KEY (project_id, id)
);

This is the diff it generates:

/*
Statement 0
  - ACQUIRES_ACCESS_EXCLUSIVE_LOCK: Index drops will lock out all accesses to the table. They should be fast.
  - INDEX_DROPPED: Dropping this index means queries that use this index might perform worse because they will no longer will be able to leverage it.
*/
SET SESSION statement_timeout = 3000;
SET SESSION lock_timeout = 3000;
ALTER TABLE "public"."test" DROP CONSTRAINT "test_pkey";

/*
Statement 1
  - INDEX_BUILD: This might affect database performance. Concurrent index builds require a non-trivial amount of CPU, potentially affecting database performance. They also can take a while but do not lock out writes.
*/
SET SESSION statement_timeout = 1200000;
SET SESSION lock_timeout = 3000;
CREATE UNIQUE INDEX CONCURRENTLY test_pkey ON public.test USING btree (project_id, id);

/*
Statement 2
*/
SET SESSION statement_timeout = 3000;
SET SESSION lock_timeout = 3000;
ALTER TABLE "public"."test" ADD CONSTRAINT "test_pkey" PRIMARY KEY USING INDEX "test_pkey";

However, I think a swap can be done instead with something like:

-- test_pkey_v2 is used as an example, but a temp name like used in other pg-schema-diff operations would work
CREATE UNIQUE INDEX CONCURRENTLY test_pkey_v2 ON public.test USING btree (project_id, id);
ALTER TABLE "public"."test" DROP CONSTRAINT "test_pkey", ADD CONSTRAINT "test_pkey" PRIMARY KEY USING INDEX "test_pkey_v2";

Motivation

The primary key replacement would be atomic and the table would never be in a state without a primary key + index triggering full scans.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions