如何在 PostgreSQL 中为单元测试伪造 inet_client_addr()?

Fli*_*mzy 8 postgresql stored-procedures unit-test

我有一个简单的存储过程,其返回值取决于inet_client_addr(). inet_client_addr()在测试我的存储过程时,如何为了单元测试的目的而覆盖?

到目前为止,我想出的唯一解决方案是围绕inet_client_addr()以下内容创建一个包装函数:

CREATE FUNCTION my_inet_client_addr() RETURNS INET AS $$
    SELECT inet_client_addr();
$$ LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)

然后在我的函数中使用它:

CREATE local_connection() RETURNS BOOLEAN AS $$
    SELECT my_inet_client_addr() = '127.0.0.1';
$$ LANGUAGE sql;
Run Code Online (Sandbox Code Playgroud)

然后在我的单元测试中,我可以重新定义my_inet_client_addr()

BEGIN;
SELECT PLAN(2);
REPLACE FUNCTION my_inet_client_addr() RETURNS INET AS $$
    SELECT '127.0.0.1'::INET;
$$ LANGUAGE sql;

is(local_connection(),TRUE,'Connection from 127.0.0.1 is local');

REPLACE FUNCTION my_inet_client_addr() RETURNS INET AS $$
    SELECT '192.168.1.1'::INET;
$$ LANGUAGE sql;

is(local_connection(),FALSE,'Connection from 192.168.1.1. is not local');

ROLLBACK;
Run Code Online (Sandbox Code Playgroud)

有没有办法在没有包装功能的情况下完成相同的任务my_inet_client_addr()

Erw*_*ter 8

inet_client_addr()是系统信息函数。
pg_catalog像其他内置函数一样位于模式中(附加模块除外)。

pg_catalog是自动的一部分search_path根据文档:

此外 public用户创建的模式之外,每个数据库还包含一个pg_catalog模式,其中包含系统表和所有内置数据类型、函数和运算符。pg_catalog始终是搜索路径的有效组成部分。如果它没有在路径中显式命名,那么在搜索路径的模式之前会隐式搜索它。这可确保始终可以找到内置名称。但是,如果您希望用户定义的名称覆盖内置名称,则可以明确地将其放置pg_catalog在搜索路径的末尾。

大胆强调我的。
所以我们创建了一个专门的模式,并将其放置之前 pg_catalogsearch_path

CREATE SCHEMA override;

CREATE OR REPLACE FUNCTION override.inet_client_addr()
  RETURNS inet AS
$func$
SELECT '127.0.0.1'::inet
$func$ language sql STABLE;
Run Code Online (Sandbox Code Playgroud)
SET search_path = override, pg_catalog, public;
Run Code Online (Sandbox Code Playgroud)

然后您的调用首先找到您的自定义覆盖函数:

SELECT inet_client_addr();
Run Code Online (Sandbox Code Playgroud)

SQL小提琴

确保非特权用户不能在 override模式中,否则他们可以对你玩各种把戏。默认情况下并非如此。根据文档:

默认情况下,未向 PUBLIC 授予对表、列、模式的任何权限或表空间的。

大胆强调我的。

如果允许同一用户在数据库中创建对象,则需要小心。
根据文档:

搜索路径中命名的第一个模式称为当前模式。除了作为搜索的第一个模式之外,如果CREATE TABLE命令未指定模式名称,它也是将在其中创建新表的模式。

始终为CREATE命令指定模式名称,并在默认情况下禁止所有对象创建以排除错误。