Як умовно зупинити скрипт 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

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.
Стів Беннетт

1
@SteveBennett досить чітко про це. Але я думаю, що це не зовсім правда. Вам потрібен лише psql у версії 10, а не серверний сервер.
Еван Керролл

О, це цікаво. Але так, старі версії можуть залишатися довгий час.
Стів Беннетт

Ви також можете \set ON_ERROR_STOP 1і потім \if yes \endifвимагати Psql версії 10 або вище. :) (Більш ранні версії будуть скаржитися на \ifнедійсність, а потім вийдуть .)
Wildcard

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

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;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.