Come fermare condizionalmente uno script psql (basato su un valore variabile)?


10

Consideriamo il seguente esempio (dall'inizio di uno script psql):

\c :db_to_run_on

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

Ora se viene eseguito questo dal comando

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

quindi funziona e l'utente è felice. Ma cosa succede se decide di specificare -v db_to_run_on=production_database? (Supponiamo che ciò possa accadere, proprio come le persone corrono rm -rf / # don't try this at home!!!ocassionalmente.) Speriamo che ci sia un nuovo backup di quella tabella ...

Quindi sorge la domanda: come verificare le variabili passate a uno script e interrompere l'ulteriore elaborazione in base al loro valore?

Risposte:


13

C'è un'opzione in psqlcui smette di eseguire comandi in caso di errore, questo è ON_ERROR_STOP. Se potessimo generare un errore in qualche modo, farebbe ciò che vogliamo.

Il problema è che dobbiamo testare la variabile e produrre un errore in qualche modo. Dal momento che non si possono usare le strutture di controllo in psql(perché non ce ne sono) *, la mia unica idea era usare SQL per i test. Bene, produrre un errore in modo condizionale è qualcosa in cui pl/pgsqlè abbastanza bravo, quindi ho scritto una funzione che genererebbe un errore. Ora posso chiamare questa funzione da una CASEstruttura semplice . Un semplice esempio:

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

*: Puoi usare qualsiasi comando shell dopo \!e condizionali della shell, ma poiché \!apre una nuova shell, eseguire qualsiasi cosa lì non ha alcun effetto per lo script psql corrente.


\set ON_ERROR_STOP on- simpatico!
msciwoj,

5

PostgreSQL 10

PostgreSQL 10 porta i condizionali in psql. Questo non è più un problema.

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

Immagino che potresti anche usare DO..

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

... non è più un problema se ti capita di eseguire PostgreSQL 10.
Steve Bennett,

1
@SteveBennett abbastanza chiaro al riguardo. Ma penso che non sia del tutto vero. È necessario solo psql sulla versione 10, non il backend del server.
Evan Carroll,

Oh, è interessante. Ma sì, le vecchie versioni possono rimanere in giro per un periodo molto lungo.
Steve Bennett,

Puoi anche \set ON_ERROR_STOP 1e poi \if yes \endifrichiedere psql versione 10 o successiva. :) (Le versioni precedenti si lamentano di \ifnon essere valide, quindi si chiudono.)
Wildcard del

1

Quello che ho trovato funziona molto bene per me è usare un linguaggio di scripting per generare un file SQL che poi installo in psql, qualcosa del genere:

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

Quindi, lo chiamo da uno script del driver:

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

Lo script del mio driver è di solito un file Rake, ma hai capito.


2
Beh si. Ho capito :) Anche se apprezzo il tuo contributo, questo è esattamente ciò che voglio evitare: usare un livello aggiuntivo.
dezso

1

Una versione più concisa della risposta di Dezso:

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

Puoi quindi chiamarlo come:

\set ON_ERROR_STOP on

SELECT CASE WHEN (
  SELECT COUNT(*) FROM mytable
) > 0 THEN pg_temp.err('Already loaded') END;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.