Koşullu olarak bir psql komut dosyasını (değişken bir değere dayalı olarak) nasıl durdurabilirim?


10

Aşağıdaki örneği ele alalım (psql betiğinin başından itibaren):

\c :db_to_run_on

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

Şimdi bunu komut tarafından çalıştırılıyorsa

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

sonra sadece çalışır ve kullanıcı mutlu. Peki ya belirtmeye karar verirse -v db_to_run_on=production_database? (Diyelim ki, tıpkı insanlar zaman zaman koşuyor gibi rm -rf / # don't try this at home!!!.) Umarım bu tablonun yeni bir yedeği vardır ...

Böylece soru ortaya çıkıyor: bir betiğe geçirilen değişkenler nasıl kontrol edilir ve değerlerine göre daha fazla işlem nasıl durdurulur?

Yanıtlar:


13

psqlHata halinde komutları çalıştırmayı durduran bir seçenek var , bu ON_ERROR_STOP. Bir şekilde bir hata oluşturabilirsek, bu istediğimizi yapar.

Sorun, değişkeni test etmemiz ve bir şekilde bir hata üretmemiz gerektiğidir. Biri kontrol yapılarını kullanamadığı için psql(hiçbiri olmadığı için) *, tek fikrim test için SQL kullanmaktı. Şartlı olarak bir hata üretmek pl/pgsqloldukça iyi bir şeydir , bu yüzden hata oluşturacak bir fonksiyon yazdım. Şimdi bu işlevi basit bir CASEyapıdan çağırabilirim . Basit bir örnek:

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

*: Kabuktan sonraki herhangi bir kabuk komutunu \!ve koşulunu kullanabilirsiniz , ancak \!yeni bir kabuk açtığından, herhangi bir şey yürütmenin geçerli psql betiği için herhangi bir etkisi yoktur.


\set ON_ERROR_STOP on- Güzel!
msciwoj

5

PostgreSQL 10

PostgreSQL 10, psql'ye koşul getiriyor. Bu artık sorun değil.

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

Sanırım sen de kullanabilirsin DO..

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

... PostgreSQL 10 çalıştırıyorsanız artık sorun değil
Steve Bennett

1
@SteveBennett bu konuda oldukça net. Ama bence bu tamamen doğru değil. Sunucu arka ucuna değil, yalnızca sürüm 10'da psql'ye ihtiyacınız vardır.
Evan Carroll

Vay ilginçmiş. Ama evet, eski versiyonlar çok uzun süre kalabilir.
Steve Bennett

Ayrıca edebilir \set ON_ERROR_STOP 1ve daha sonra \if yes \endifpsql'in sürüm 10 veya daha yüksek gerektirecek. :) (Önceki sürümler \ifgeçersiz olmaktan şikayet edecek ve sonra kapanacaktır .)
Wildcard

1

Bulduğum şey benim için çok iyi çalışıyor, daha sonra psql içine boru gibi bir SQL dosyası oluşturmak için bir komut dosyası dili kullanmaktır:

#!/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"

Sonra, bunu bir sürücü komut dosyasından çağırıyorum:

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

Sürücü betiğim genellikle bir Rake dosyasıdır, ancak fikri anlarsınız.


2
İyi evet. Anladım :) Girişinizi takdir ederken, tam olarak kaçınmak istediğim şey bu - ek bir katman kullanarak.
dezso

1

Dezso'nun cevabının daha özlü bir versiyonu:

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

Daha sonra bunu şöyle çağırabilirsiniz:

\set ON_ERROR_STOP on

SELECT CASE WHEN (
  SELECT COUNT(*) FROM mytable
) > 0 THEN pg_temp.err('Already loaded') END;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.