변수 값을 기준으로 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는 그것에 대해 꽤 분명합니다. 그러나 나는 그것이 사실이 아니라고 생각합니다. 서버 백엔드가 아닌 버전 10의 psql 만 필요합니다.
Evan Carroll

아, 흥미 롭습니다. 그러나 예, 이전 버전은 looooong 시간 동안 머물 수 있습니다.
Steve Bennett

당신은 또한 수 있습니다 \set ON_ERROR_STOP 1다음 \if yes \endifpsql의 버전 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

내 드라이버 스크립트는 일반적으로 레이크 파일이지만 아이디어를 얻습니다.


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;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.