如何有条件地停止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!!!偶尔运行一样。)希望该表有一个新的备份...

因此出现了一个问题:如何检查传递给脚本的变量并根据其值停止进一步处理?

Answers:


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;

*:您可以在shell的后面\!和条件中使用任何shell命令,但是由于\!打开了一个新shell,因此执行任何操作都不会对当前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,而不需要服务器后端。
埃文·卡罗尔

哦,那很有趣。但是,是的,旧版本可以保留很长一段时间。
史蒂夫·本内特

您还可以\set ON_ERROR_STOP 1然后\if yes \endif要求psql版本10或更高版本。:)(早期版本会抱怨\if无效,然后退出。)
通配符

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 2012年

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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.