Idempotent PostgreSQL DDL scripts

匿名 (未验证) 提交于 2019-12-03 08:52:47

问题:

I'm looking for a way to script postgreSQL schema changes in an idempotent manner.

In MSSQL I could do something like this:

if(not exists(select * from information_schema.columns where table_name = 'x' and column_name = 'y')) begin     alter table x add y int end go 

PostgreSQL doesn't seem to allow ad-hoc pl/pgsql in the same way MSSQL does with T-SQL so I can't use control structures in a SQL script and run it with psql -f x.sql.

I know PostgreSQL will throw an error if the object already exists but I don't want to have to ignore errors.

I could use some schema versioning technique like dbdeploy but I really like the simplicity of running a set of files through psql without incurring any unwanted side effects.

Is this possible?

Thanks, Mark

回答1:

you might find this blogpost helpful: http://www.depesz.com/index.php/2008/06/18/conditional-ddl/



回答2:

Disclaimer: I know, this is a very old question and already has an accepted answer.

But I'd like to register here a totally idempotent script, without external links.


A simple script demonstrating PostgreSQL 9.5+ idempotent built-in capabilities. You can run this script as many times as you wish. Here we go:
--Table CREATE TABLE IF NOT EXISTS person (   id integer NOT NULL,   person_name character varying(40) NOT NULL,   updated_date date,   CONSTRAINT person_pkey PRIMARY KEY (id) );  --Index CREATE INDEX IF NOT EXISTS idx_person_name ON person (person_name);  --Sequence CREATE SEQUENCE IF NOT EXISTS seq_person_inc;  --Function CREATE OR REPLACE FUNCTION simple_sum(a_integer int, b_integer int) RETURNS INT     AS $$ SELECT a_integer+b_integer $$ LANGUAGE SQL;  --View CREATE OR REPLACE VIEW vw_select_1 AS     SELECT 1;  --Role DO $$ BEGIN     CREATE ROLE rick_deckard;     EXCEPTION        WHEN duplicate_object THEN         RAISE NOTICE 'Role already exists. Ignoring...';     END$$;  --Simple insert INSERT INTO person (id, person_name) VALUES (1, 'HAL-9000');   --Upsert (insert + update) INSERT INTO person (id, person_name) VALUES (1, 'Betrayer') ON CONFLICT ON CONSTRAINT person_pkey DO UPDATE SET person_name = EXCLUDED.person_name;  --Upsert (ignoring duplicate error) INSERT INTO person (id, person_name) VALUES (1, 'HAL-9000') ON CONFLICT ON CONSTRAINT person_pkey DO NOTHING;  --Upsert (ignoring any error) INSERT INTO person (id, person_name) VALUES (1, 'HAL-9000') ON CONFLICT DO NOTHING;  --Field DO $$ BEGIN     ALTER TABLE person ADD COLUMN id_another_person INTEGER; EXCEPTION        WHEN duplicate_column THEN         RAISE NOTICE 'Field already exists. Ignoring...'; END$$;  --Constraint DO $$ BEGIN     ALTER TABLE person ADD CONSTRAINT person_id_another_person_fkey FOREIGN KEY (id_another_person) REFERENCES person (id); EXCEPTION        WHEN duplicate_object THEN         RAISE NOTICE 'Constraint already exists. Ignoring...'; END$$;  --Trigger CREATE OR REPLACE FUNCTION person_trigger_function() RETURNS trigger AS $BODY$ BEGIN    --Something complex here =)    RETURN NEW;  END; $BODY$ LANGUAGE plpgsql;  DO $$ BEGIN     CREATE TRIGGER person_trigger BEFORE INSERT OR UPDATE ON person FOR EACH ROW EXECUTE PROCEDURE person_trigger_function(); EXCEPTION        WHEN duplicate_object THEN         RAISE NOTICE 'Trigger already exists. Ignoring...'; END$$;  --Drop DROP TRIGGER IF EXISTS person_trigger ON person; DROP INDEX IF EXISTS idx_person_name; ALTER TABLE person DROP COLUMN IF EXISTS person_name; ALTER TABLE person DROP CONSTRAINT IF EXISTS person_id_another_person_fkey; DROP ROLE IF EXISTS rick_deckard; DROP VIEW IF EXISTS vw_select_1; DROP FUNCTION IF EXISTS simple_sum(integer, integer); DROP FUNCTION IF EXISTS person_trigger_function(); DROP TABLE IF EXISTS person; DROP SEQUENCE IF EXISTS seq_person_inc; 


回答3:

You should be able to use plpgsql:

create language plpgsql; create function f() ... as $$ <plpgsql code> $$language plpgsql; select f(); 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!