处理 PostgreSQL 异常的优雅方式?

Elegant way of handling PostgreSQL exceptions?

在 PostgreSQL 中,我想创建一个安全包装机制,如果发生异常,该机制 returns 为空结果。考虑以下因素:

SELECT * FROM myschema.mytable;

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

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

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

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

如果您只选择一列,那么 COALESCE() 函数应该能够为您解决问题

SELECT COALESCE( value, '{}'::text[] ) FROM myschema.mytable

如果您需要更多行,您可能需要创建一个带有类型的函数。

PL/pgSQL

中的异常处理

通常,plpgsql 代码总是包装在一个 BEGIN .. END 块中。它可以在 DO 语句或函数的主体内。块可以嵌套在内部 - 但它们不能存在于 外部 ,不要将其与普通的 SQL.

混淆

每个 BEGIN 块可以选择包含一个 EXCEPTION 子句来处理异常,但是需要捕获异常的函数要昂贵得多,所以最好事先避免异常。

更多信息:

如何避免示例中的异常

一个 DO statement can't return anything. Create a function 以 table 和架构名称作为参数,returns 任何你想要的:

CREATE OR REPLACE FUNCTION f_tbl_value(_tbl text, _schema text = 'public')
  RETURNS TABLE (value text)
  LANGUAGE plpgsql 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 IF;
END
$func$;

致电:

SELECT * FROM f_tbl_value('my_table');

或者:

SELECT * FROM f_tbl_value('my_table', 'my_schema');

假设您想要一组具有单个 text 列的行,或者如果 table 不存在,则需要一个空字符串。

还假设如果给定的 table 存在,则列 value 存在。你也可以测试它,但你没有要求它。

两个输入参数仅区分大小写 如果双引号。就像 identifiers are handled in SQL statements.

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

to_regclass() 是 Postgres 9.4 中的新增内容。对于旧版本替换:

IF EXISTS (
   SELECT FROM information_schema.tables 
   WHERE  table_schema = _schema
   AND    table_name = _tbl
);

这实际上更准确,因为它准确地测试了您所需要的。更多选项和详细解释:

  • Table name as a PostgreSQL function parameter

在使用动态 SQL 时始终防御 SQL 注入!强制转换为 regclass 就可以解决这个问题。更多详情:

  • How to check if a table exists in a given schema