Skip to content

PostgreSQL - 断言 (Assert)

在 PostgreSQL 中,ASSERT 语句是 PL/pgSQL 过程语言中可用的调试工具。它用于在函数或 DO 块执行期间检查条件是否为真。如果条件评估为 false 或 NULL,则会引发 ASSERT_FAILURE 异常,立即停止执行。

重要提示: 断言主要用于开发和测试,而不是用于生产数据验证。它们可以全局禁用,因此绝不应在生产环境中使用它们来强制执行业务规则或验证用户输入。为此,请使用 CHECK 约束或显式的 IF ... THEN RAISE EXCEPTION; 块。

断言由 plpgsql.check_asserts 配置参数控制。默认情况下,它处于 on 状态。

-- 检查当前设置
SHOW plpgsql.check_asserts;
-- 为当前会话禁用断言
SET plpgsql.check_asserts = off;

禁用后,ASSERT 语句将完全被忽略,并且没有性能开销,这就是为什么它们可以安全地保留在生产代码中的原因。

ASSERT 语句的语法非常简单:

ASSERT condition [ , message ];
  • condition: 期望为真的布尔表达式。
  • message(可选): 如果断言失败,将显示自定义文本消息。如果未提供,则使用默认消息 “assertion failed”(断言失败)。

这个 DO 块检查一个数字是否为正数。由于条件 10 > 0 为真,ASSERT 静默通过并继续执行。

DO $$
BEGIN
ASSERT 10 > 0, 'The number is not positive'; -- 数字不是正数
RAISE NOTICE 'Assertion passed: The number is positive.'; -- 断言通过:数字是正数。
END $$;

输出:

NOTICE: Assertion passed: The number is positive.
DO

这里,我们检查除数是否为非零。断言 x <> 0 为真,因此它通过。让我们看看如果它为假会发生什么。

DO $$
DECLARE
x INT := 0;
BEGIN
ASSERT x <> 0, 'FATAL: Cannot divide by zero!'; -- 致命错误:不能除以零!
RAISE NOTICE 'This line will not be reached.'; -- 这行将不会被执行。
END $$;

断言失败并抛出带有我们自定义消息的异常:

ERROR: FATAL: Cannot divide by zero!
CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT
SQL state: P0004

让我们创建一个函数来计算折扣,并断言输入价格是正数。

CREATE OR REPLACE FUNCTION calculate_discount(price NUMERIC, percentage INT)
RETURNS NUMERIC AS $$
BEGIN
-- During development, ensure we never receive a negative price.
-- 在开发期间,确保我们不会收到负数价格。
ASSERT price > 0, 'Input price must be positive.'; -- 输入价格必须为正数。
RETURN price * (percentage / 100.0);
END;
$$ LANGUAGE plpgsql;

用有效价格调用会正常工作:

SELECT calculate_discount(200, 10);
-- Result:
-- 结果:
calculate_discount
--------------------
20.00

用无效价格调用会触发断言:

SELECT calculate_discount(-50, 10);
-- Result:
-- 结果:
ERROR: Input price must be positive.
CONTEXT: PL/pgSQL function calculate_discount(numeric,integer) line 4 at ASSERT
SQL state: P0004
方法用例环境
ASSERT检查内部逻辑、函数前置条件和不变量。开发与测试
RAISE EXCEPTION验证必须强制执行的参数和用户输入。生产环境
CHECK 约束在表级别强制执行数据完整性规则。生产环境