2009-04-08 106 views
3

我正在尋找一種以冪等方式腳本postgreSQL模式更改的方法。冪等PostgreSQL DDL腳本

在MSSQL中,我可以做這樣的事情:

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似乎並沒有讓特設PL/pgSQL裏以同樣的方式MSSQL確實與T-SQL,所以我不能用控制結構在SQL腳本中並使用psql -f x.sql運行。

我知道PostgreSQL會拋出一個錯誤,如果對象已經存在但我不想忽略錯誤。

我可以使用像dbdeploy這樣的模式版本控制技術,但我真的很喜歡通過psql運行一組文件的簡單性,而不會產生任何不需要的副作用。

這可能嗎?

謝謝, 馬克

回答

2

您應該能夠使用plpgsql

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

聲明:我知道,這是一個很老問題並且已經有一個被接受的答案。

但我想在這裏註冊一個完全idempotent腳本,沒有外部鏈接。


一個簡單的腳本,演示PostgreSQL 9.5+冪等內置功能。您可以根據需要多次運行此腳本。 這裏我們去:

--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;