Skip to content

Schema Diff: converting a column between an integer type and SERIAL produces an incomplete script #10292

Description

@dpage

Bug Description

Schema Diff can tell that a column is a SERIAL/BIGSERIAL/SMALLSERIAL on one side and a plain integer column on the other, but the script it generates does not finish the conversion, so applying it leaves the target column functionally different from the source.

With a source table declared as:

CREATE TABLE public.t (id bigserial NOT NULL, val text);

and a target declared as:

CREATE TABLE public.t (id integer NOT NULL, val text);

the comparison produces the column type change and, from the sequence node, the sequence itself:

ALTER TABLE public.t
    ALTER COLUMN id TYPE bigint;

CREATE SEQUENCE IF NOT EXISTS public.t_id_seq
    INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1;

ALTER SEQUENCE public.t_id_seq
    OWNED BY public.t.id;

but never the ALTER COLUMN id SET DEFAULT nextval('public.t_id_seq'::regclass) that makes the column a serial. The sequence is created and owned by the column, yet the column has no default, so inserts that omit id still fail on the target whilst succeeding on the source.

The cause is that Schema Diff compares columns after they have been reprojected onto the SERIAL pseudo-type (get_formatted_columns() with with_serial=True), and the reprojection empties the column's nextval() default because the default is implied by the pseudo-type. Once the two sides genuinely differ in serialness, that emptied default is all the SQL generator has to work from, so the default is simply never written.

The reverse direction (source plain integer, target bigserial) has an ordering problem rather than a missing statement: the column's DROP DEFAULT and the sequence's DROP SEQUENCE both get generated, but a sequence owned by a column whose default still references it cannot be dropped, so the order in which the script emits them matters.

Expected Behaviour

Applying a Schema Diff script for a column that changes between an integer type and a serial should leave the target column equivalent to the source, defaults included.

Context

Noticed whilst fixing #10236 (commit 01e5bf0), which stopped identical serial columns being reported as different and stopped the invalid ALTER COLUMN ... TYPE bigserial being generated for serial columns with genuine differences. That change deliberately left this conversion case alone: it is now valid SQL rather than invalid SQL, but it is still incomplete.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions