Bagaimana cara menghentikan skrip psql secara kondisional (berdasarkan nilai variabel)?

10

Mari kita perhatikan contoh berikut (dari awal skrip psql):

\c :db_to_run_on

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

Sekarang jika dijalankan ini dengan perintah

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

lalu hanya berjalan dan pengguna senang. Tetapi bagaimana jika dia memutuskan untuk menentukan -v db_to_run_on=production_database? (Mari kita asumsikan bahwa ini bisa terjadi, sama seperti orang-orang yang menjalankannya secara kebetulan rm -rf / # don't try this at home!!!.) Mudah-mudahan ada cadangan baru dari tabel itu ...

Maka timbul pertanyaan: bagaimana cara memeriksa variabel yang diteruskan ke skrip dan menghentikan pemrosesan lebih lanjut berdasarkan nilainya?

dezso
sumber

Jawaban:

13

Ada opsi di psqlmana berhenti mengeksekusi perintah pada kesalahan, ini ON_ERROR_STOP. Jika kita dapat memunculkan kesalahan, ini akan melakukan apa yang kita inginkan.

Masalahnya adalah kita harus menguji variabel dan menghasilkan kesalahan. Karena seseorang tidak dapat menggunakan struktur kontrol di psql(karena tidak ada) *, satu-satunya ide saya adalah menggunakan SQL untuk pengujian. Ya, menghasilkan kesalahan secara kondisional adalah sesuatu yang pl/pgsqlcukup baik, jadi saya menulis sebuah fungsi yang akan menghasilkan kesalahan. Sekarang saya dapat memanggil fungsi ini dari CASEstruktur sederhana . Contoh sederhana:

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

*: Anda dapat menggunakan perintah shell apa pun setelah \!dan mengkondisikan shell, tetapi karena \!membuka shell baru, menjalankan apa pun di sana tidak memiliki efek apa pun untuk skrip psql saat ini.

dezso
sumber
\set ON_ERROR_STOP on- bagus!
msciwoj
5

PostgreSQL 10

PostgreSQL 10 membawa conditional ke psql. Ini bukan lagi masalah.

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

Saya kira Anda juga bisa menggunakan DO..

\if :db_to_run_on != 'dev_database'
do $$
  BEGIN
    RAISE 'Meaningful error message here';
  END;
$$ LANGUAGE plpgsql;
\endif
Evan Carroll
sumber
... tidak lagi menjadi masalah jika Anda menjalankan PostgreSQL 10.
Steve Bennett
1
@SteveBennett cukup jelas tentang itu. Tapi saya pikir itu tidak sepenuhnya benar. Anda hanya perlu psql pada versi 10, bukan server backend.
Evan Carroll
Oh, itu menarik. Tapi ya, versi lama bisa tetap ada waktu looooong.
Steve Bennett
Anda juga dapat \set ON_ERROR_STOP 1dan kemudian \if yes \endifmeminta psql versi 10 atau lebih tinggi. :) (Versi sebelumnya akan mengeluh tentang \iftidak valid, dan kemudian berhenti.)
Wildcard
1

Apa yang saya temukan berfungsi sangat baik bagi saya adalah menggunakan bahasa scripting untuk menghasilkan file SQL yang kemudian saya pipa ke psql, sesuatu seperti ini:

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

Lalu, saya menyebutnya dari skrip driver:

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

Skrip driver saya biasanya berupa file Rake, tetapi Anda mendapatkan idenya.

François Beausoleil
sumber
2
Baiklah. Saya mengerti :) Meskipun saya menghargai masukan Anda, inilah yang ingin saya hindari - menggunakan lapisan tambahan.
dezso
1

Jawaban dezso yang lebih ringkas:

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

Anda kemudian dapat memanggil ini seperti:

\set ON_ERROR_STOP on

SELECT CASE WHEN (
  SELECT COUNT(*) FROM mytable
) > 0 THEN pg_temp.err('Already loaded') END;
Steve Bennett
sumber