处理 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
子句来处理异常,但是需要捕获异常的函数要昂贵得多,所以最好事先避免异常。
更多信息:
The manual on how to trap errors (handle exceptions) in PL/pgSQL
示例:Is SELECT or INSERT in a function prone to race conditions?
-
如何避免示例中的异常
一个 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
在 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
子句来处理异常,但是需要捕获异常的函数要昂贵得多,所以最好事先避免异常。
更多信息:
The manual on how to trap errors (handle exceptions) in PL/pgSQL
示例:Is SELECT or INSERT in a function prone to race conditions?
如何避免示例中的异常
一个 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