Last active
March 11, 2025 23:24
-
-
Save MarkusH/4c03d0867ba37b6b89379bced12cb3ae to your computer and use it in GitHub Desktop.
A script that outputs the raw SQL to convert Django's serial columns to identity columns.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| WITH tab AS ( | |
| SELECT | |
| a.attrelid::regclass::text AS t, | |
| a.attname AS c, | |
| pg_get_serial_sequence(a.attrelid::regclass::text, a.attname) AS s, | |
| nextval(pg_get_serial_sequence(a.attrelid::regclass::text, a.attname)) AS v | |
| FROM pg_attribute a | |
| JOIN pg_class c ON c.oid = a.attrelid | |
| WHERE | |
| a.attrelid::regclass::text LIKE '%' | |
| AND c.relkind IN ('r', 'p') /* regular and partitioned tables */ | |
| AND a.attnum > 0 | |
| AND NOT a.attisdropped | |
| AND a.atttypid = ANY ('{int,int8,int2}'::regtype[]) | |
| AND EXISTS ( | |
| SELECT FROM pg_attrdef ad | |
| WHERE | |
| ad.adrelid = a.attrelid | |
| AND ad.adnum = a.attnum | |
| AND ( | |
| pg_get_expr(ad.adbin, ad.adrelid) | |
| = | |
| 'nextval(''' | |
| || ( | |
| pg_get_serial_sequence(a.attrelid::regclass::text, a.attname) | |
| )::regclass | |
| || '''::regclass)' | |
| ) | |
| ) | |
| ORDER BY a.attnum | |
| ) | |
| SELECT | |
| 'BEGIN; ' | |
| || 'LOCK TABLE ' || quote_ident(t) || ' IN ACCESS EXCLUSIVE MODE; ' | |
| || 'ALTER TABLE ' || quote_ident(t) || ' ALTER COLUMN ' || quote_ident(c) || ' DROP DEFAULT; ' | |
| || 'DROP SEQUENCE ' || s || '; ' | |
| || 'ALTER TABLE ' || quote_ident(t) || ' ALTER ' || quote_ident(c) || ' ADD GENERATED BY DEFAULT AS IDENTITY (RESTART ' || v || '); ' | |
| || 'COMMIT;' | |
| FROM tab; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This should execute directly
DO $$ DECLARE rec record; table_ident text; seq_ident text; max_val bigint; BEGIN FOR rec IN SELECT a.attrelid::regclass::text AS t, a.attname AS c, pg_get_serial_sequence(a.attrelid::regclass::text, a.attname) AS s FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid WHERE a.attrelid::regclass::text LIKE '%' AND c.relkind IN ('r', 'p') -- regular and partitioned tables AND a.attnum > 0 AND NOT a.attisdropped AND a.atttypid = ANY ('{int,int8,int2}'::regtype[]) AND EXISTS ( SELECT 1 FROM pg_attrdef ad WHERE ad.adrelid = a.attrelid AND ad.adnum = a.attnum AND ( pg_get_expr(ad.adbin, ad.adrelid) = 'nextval(''' || (pg_get_serial_sequence(a.attrelid::regclass::text, a.attname))::regclass || '''::regclass)' ) ) ORDER BY a.attnum LOOP -- Handle schema-qualified table names table_ident := CASE WHEN position('.' in rec.t) > 0 THEN quote_ident(split_part(rec.t, '.', 1)) || '.' || quote_ident(split_part(rec.t, '.', 2)) ELSE quote_ident(rec.t) END; -- Handle schema-qualified sequence names seq_ident := CASE WHEN position('.' in rec.s) > 0 THEN quote_ident(split_part(rec.s, '.', 1)) || '.' || quote_ident(split_part(rec.s, '.', 2)) ELSE quote_ident(rec.s) END; -- Lock the table to prevent concurrent modifications EXECUTE 'LOCK TABLE ' || table_ident || ' IN ACCESS EXCLUSIVE MODE'; -- Get the max value of the column to set the new identity restart value EXECUTE 'SELECT COALESCE(MAX(' || quote_ident(rec.c) || '), 0) + 1 FROM ' || table_ident INTO max_val; -- Drop the default and sequence EXECUTE 'ALTER TABLE ' || table_ident || ' ALTER COLUMN ' || quote_ident(rec.c) || ' DROP DEFAULT'; EXECUTE 'DROP SEQUENCE ' || seq_ident; -- Convert the column to GENERATED ALWAYS AS IDENTITY EXECUTE 'ALTER TABLE ' || table_ident || ' ALTER COLUMN ' || quote_ident(rec.c) || ' ADD GENERATED ALWAYS AS IDENTITY (RESTART WITH ' || max_val || ')'; END LOOP; END $$;