处理PostgreSQL异常的优雅方式?

Tre*_*reg 17 sql postgresql exception-handling dynamic-sql plpgsql

在PostgreSQL中,我想创建一个安全包装机制,如果发生异常,它将返回空结果.考虑以下:

SELECT * FROM myschema.mytable;
Run Code Online (Sandbox Code Playgroud)

我可以在客户端应用程序中进行安全包装:

try {
    result = execute_query('SELECT value FROM myschema.mytable').fetchall();
}
catch(pg_exception) {
    result = []
}
Run Code Online (Sandbox Code Playgroud)

但我可以直接在SQL中做这样的事情吗?我想使下面的代码工作,但似乎它应该被放入DO $$ ... $$块中,在这里我迷路了.

BEGIN
    SELECT * FROM myschema.mytable;
EXCEPTION WHEN others THEN
    SELECT unnest(ARRAY[]::TEXT[])
END
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 12

PL/pgSQL中的异常处理

通常,plpgsql代码始终包含在BEGIN .. END块中.这可以在DO语句或函数的主体内部.块可以嵌套在里面 - 但它们不能存在于外部,不要将它与普通的SQL混淆.

每个BEGIN块都可以选择包含一个EXCEPTION用于处理异常的子句,但是需要捕获异常的函数要贵得多,因此最好先验一下异常.

更多信息:

如何避免示例中的异常

一个DO语句不能返回任何东西.创建一个函数,将表和模式名称作为参数并返回您想要的任何内容:

CREATE OR REPLACE FUNCTION f_tbl_value(_tbl text, _schema text = 'public')
  RETURNS TABLE (value text) AS
$func$
DECLARE
   _t regclass := to_regclass(_schema || '.' || _tbl);
BEGIN
   IF _t IS NULL THEN
      value := ''; RETURN NEXT;    -- return single empty string
   ELSE
      RETURN QUERY EXECUTE
      'SELECT value FROM ' || _t;  -- return set of values
   END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

呼叫:

SELECT * FROM f_tbl_value('my_table');
Run Code Online (Sandbox Code Playgroud)

要么:

SELECT * FROM f_tbl_value('my_table', 'my_schema');
Run Code Online (Sandbox Code Playgroud)
  • 假设您想要一组具有单个text列的行或者如果该表不存在则需要空字符串.

  • value如果给定的表存在,还假设列存在.你也可以测试一下,但你没有要求.

  • 两个参数都是区分大小写的 text值.这与SQL语句中的标识符处理方式略有不同.如果你从不重复引用标识符,请传递小写名称,你没事.

  • 架构名称默认为'public'我的示例.适应您的需求.您甚至可以完全忽略架构并默认为当前架构search_path.

  • to_regclass()是Postgres 9.4中的新功能.对于旧版本替代:

    IF EXISTS (
       SELECT 1
       FROM   information_schema.tables 
       WHERE  table_schema = _schema
       AND    table_name = _tbl
    );
    
    Run Code Online (Sandbox Code Playgroud)

    这实际上更准确,因为它可以准确测试您的需求.更多选项和详细说明:

  • 使用动态SQL时始终防止SQL注入!演员regclass在这里做的伎俩.更多细节: