วิธีหยุดสคริปต์ 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\'

จากนั้นมันจะทำงานและผู้ใช้มีความสุข แต่สิ่งที่ถ้า (s) เขาตัดสินใจที่จะระบุ-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 อยู่
Steve Bennett

1
@SteveBennett ค่อนข้างชัดเจนเกี่ยวกับเรื่องนั้น แต่ฉันคิดว่ามันไม่จริงทั้งหมด คุณต้องการ psql ในเวอร์ชัน 10 ไม่ใช่แบ็กเอนด์ของเซิร์ฟเวอร์
Evan Carroll

โอ้นั่นน่าสนใจ แต่ใช่รุ่นเก่าสามารถอยู่รอบ ๆ เวลา looooong
Steve Bennett

คุณสามารถ\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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.