Làm thế nào để ngăn chặn một cách có điều kiện một tập lệnh psql (dựa trên một giá trị biến)?


10

Chúng ta hãy xem xét ví dụ sau (từ khi bắt đầu tập lệnh psql):

\c :db_to_run_on

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

Bây giờ nếu nó được chạy bằng lệnh này

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

Sau đó, nó chỉ chạy và người dùng hạnh phúc. Nhưng nếu (các) anh ấy quyết định chỉ định -v db_to_run_on=production_databasethì sao? (Chúng ta hãy giả sử rằng điều này có thể xảy ra, giống như mọi người chạy một cách rm -rf / # don't try this at home!!!ngẫu nhiên.) Hy vọng có một bản sao lưu mới của bảng đó ...

Vì vậy, câu hỏi đặt ra: làm thế nào để kiểm tra các biến được truyền cho một tập lệnh và dừng xử lý thêm dựa trên giá trị của chúng?

Câu trả lời:


13

Có một tùy chọn trong psqlđó dừng thực thi các lệnh bị lỗi, đây là ON_ERROR_STOP. Nếu chúng ta có thể đưa ra một lỗi nào đó, điều này sẽ làm những gì chúng ta muốn.

Vấn đề là chúng ta phải kiểm tra biến và tạo ra lỗi nào đó. Vì người ta không thể sử dụng các cấu trúc điều khiển trong psql(vì không có) *, nên ý tưởng duy nhất của tôi là sử dụng SQL để thử nghiệm. Chà, tạo ra một lỗi có điều kiện là một thứ pl/pgsqlkhá tốt, vì vậy tôi đã viết một hàm tạo ra lỗi. Bây giờ tôi có thể gọi hàm này từ một CASEcấu trúc đơn giản . Một ví dụ đơn giản:

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

*: Bạn có thể sử dụng bất kỳ lệnh shell nào sau \!và các điều kiện của shell, nhưng kể từ khi \!mở shell mới, thực thi bất kỳ thứ gì không có bất kỳ ảnh hưởng nào đối với tập lệnh psql hiện tại.


\set ON_ERROR_STOP on- đẹp!
msciwoj

5

PostgreQuery 10

PostgreQuery 10 mang lại điều kiện cho psql. Đây không còn là một vấn đề.

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

Tôi đoán bạn cũng có thể sử dụng DO..

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

... không còn là vấn đề nếu bạn tình cờ chạy PostgreSQL 10.
Steve Bennett

1
@SteveBennett khá rõ ràng về điều đó. Nhưng tôi nghĩ nó không hoàn toàn đúng. Bạn chỉ cần psql trên phiên bản 10, không phải phụ trợ máy chủ.
Evan Carroll

Ah điều đó thật thú vị. Nhưng vâng, các phiên bản cũ có thể tồn tại trong một thời gian ngắn.
Steve Bennett

Bạn cũng có thể \set ON_ERROR_STOP 1và sau đó \if yes \endifyêu cầu psql phiên bản 10 trở lên. :) (Các phiên bản trước sẽ phàn nàn về \ifviệc không hợp lệ và sau đó thoát.)
Wildcard

1

Những gì tôi tìm thấy hoạt động rất tốt đối với tôi là sử dụng ngôn ngữ kịch bản để tạo tệp SQL mà sau đó tôi chuyển sang psql, đại loại như thế này:

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

Sau đó, tôi gọi điều này từ một kịch bản trình điều khiển:

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

Kịch bản trình điều khiển của tôi thường là một tệp Rake, nhưng bạn có ý tưởng.


2
Vâng, vâng. Tôi hiểu rồi :) Trong khi tôi đánh giá cao đầu vào của bạn, đây chính xác là những gì tôi muốn tránh - sử dụng một lớp bổ sung.
dezso

1

Một phiên bản ngắn gọn hơn của câu trả lời của dezso:

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

Sau đó, bạn có thể gọi như thế này:

\set ON_ERROR_STOP on

SELECT CASE WHEN (
  SELECT COUNT(*) FROM mytable
) > 0 THEN pg_temp.err('Already loaded') END;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.