(変数値に基づいて)条件付きでpsqlスクリプトを停止する方法は?


10

次の例を考えてみましょう(psqlスクリプトの最初から)。

\c :db_to_run_on

TRUNCATE the_most_important_table;
-- tried to avoid similarities to anything that exists out there

これをコマンドで実行すると

psql [connection details] -v db_to_run_on=\'dev_database\'

その後、それは実行され、ユーザーは満足しています。しかし、もし彼が特定することを決定した場合はどうなります-v db_to_run_on=production_databaseか?(人々がrm -rf / # don't try this at home!!!たまに実行するように、これが起こる可能性があると仮定しましょう。)うまくいけば、そのテーブルの新しいバックアップがあるでしょう...

したがって、問題が発生します。スクリプトに渡された変数を確認し、その値に基づいて以降の処理を停止する方法は?

回答:


13

psqlエラー時にコマンドの実行を停止するオプションがありますON_ERROR_STOP。これはです。どういうわけかエラーが発生する可能性がある場合、これは私たちが望むことを行います。

問題は、変数をテストして何らかの方法でエラーを生成する必要があることです。で制御構造を使用できないためpsql(何もないため)*、SQLをテストに使用することしかありませんでした。まあ、条件付きでエラーを生成することpl/pgsqlはかなり得意なことなので、エラーを生成する関数を書きました。簡単なCASE構造からこの関数を呼び出すことができます。簡単な例:

-- let's assume for clarity that there is no function with this name in the database
CREATE OR REPLACE FUNCTION error_generator()
RETURNS boolean AS
$body$
BEGIN
    RAISE 'Meaningful error message here';
    RETURN FALSE; -- just for aesthetical purposes
END;
$body$
LANGUAGE plpgsql;

\set ON_ERROR_STOP on

BEGIN;

-- test for the variable value
-- notice that if :var is not set, it fails as well (with a syntax error)
SELECT CASE WHEN 1 = :var THEN error_generator() ELSE TRUE END;

INSERT INTO test_table (integer_value, text_value)
VALUES (:var, 'something');

COMMIT;

*:シェルの後\!に条件付きで任意のシェルコマンドを使用できます\!が、新しいシェルを開くため、そこに何かを実行しても、現在のpsqlスクリプトに影響はありません。


\set ON_ERROR_STOP on-いいね!
msciwoj 2017

5

PostgreSQL 10

PostgreSQL 10はpsqlに条件をもたらします。これはもはや問題ではありません。

\if :db_to_run_on = 'dev_database'
  TRUNCATE the_most_important_table;
\endif

あなたも使うことができると思いますDO

\if :db_to_run_on != 'dev_database'
do $$
  BEGIN
    RAISE 'Meaningful error message here';
  END;
$$ LANGUAGE plpgsql;
\endif

...たまたまPostgreSQL 10を実行している場合は問題になりません
Steve Bennett

1
@SteveBennettはそれについてかなり明確です。しかし、それは完全に真実ではないと思います。サーバーのバックエンドではなく、バージョン10のpsqlのみが必要です。
エヴァンキャロル

ああ、面白いですね。しかし、そうです、古いバージョンは非常に長い間とどまることができます。
スティーブベネット

また\set ON_ERROR_STOP 1\if yes \endifpsqlバージョン10以降を要求することもできます。:)(以前のバージョンは\if無効であると不平を言って終了します。)
ワイルドカード

1

私にとって非常にうまく機能しているのは、スクリプト言語を使用してSQLファイルを生成し、それを次にpsqlにパイプすることです。

#!/usr/bin/env ruby

raise "Not a good database name: #{ARGV.first.inspect}" unless ARGV.first =~ /^(dev|test)/

puts "\\timing off"
puts "set client_min_messages='warning';"
puts
puts "TRUNCATE the_most_important_table;"
puts "-- more commands"

次に、これをドライバースクリプトから呼び出します。

#!/bin/bash
/usr/bin/ruby generator ${1} | /usr/bin/psql --dbname=${1} --file=- --single-transaction

私のドライバースクリプトは通常Rakeファイルですが、そのアイデアは理解できます。


2
はい、そうです。私はそれを手に入れました:)私はあなたの入力に感謝していますが、これはまさに私が避けたいものです-追加のレイヤーを使用します。
dezso 2012

1

dezsoの答えのより簡潔なバージョン:

CREATE OR REPLACE FUNCTION pg_temp.err(msg varchar) RETURNS boolean     
AS $$ BEGIN RAISE '%',msg; END; $$ LANGUAGE plpgsql;

その後、次のように呼び出すことができます。

\set ON_ERROR_STOP on

SELECT CASE WHEN (
  SELECT COUNT(*) FROM mytable
) > 0 THEN pg_temp.err('Already loaded') END;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.