如何使用 PLPGSQL 确定当前 search_path 中是否存在表?

cim*_*non 12 postgresql plpgsql catalogs

我正在为一个应用程序编写一个安装脚本,该脚本是另一个应用程序的插件,所以我想检查另一个应用程序的表是否存在。如果没有,我想给用户一个有用的错误。但是,我不知道什么模式将保存这些表。

DO LANGUAGE plpgsql $$
BEGIN
    PERFORM 1
    FROM
        pg_catalog.pg_class c
        JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    WHERE
        n.nspname = current_setting('search_path')
        AND c.relname = 'foo'
        AND c.relkind = 'r'; -- not sure if I actually need this or not...

    IF NOT FOUND THEN
        RAISE 'This application depends on tables created by another application';
    END IF;
END;
$$;
Run Code Online (Sandbox Code Playgroud)

但是,current_setting('search_path')返回"$user",public默认包含的 TEXT ,这不是很有用。

我唯一能想到的另一件事是尝试从表中选择并捕获异常。它可以完成这项工作,但我认为它不是很优雅,而且我读到它使用起来很昂贵(尽管在这种情况下可能没问题,因为我只运行一次?)。

Erw*_*ter 20

快速(和脏?)

在 Postgres 9.4 或更新版本中使用to_regclass()

SELECT to_regclass('foo');
Run Code Online (Sandbox Code Playgroud)

如果搜索路径中没有该名称的关系,则返回 NULL。

在 Postgres 9.3 或更早版本(或任何版本)中,使用强制转换来regclass

SELECT 'foo'::regclass;
Run Code Online (Sandbox Code Playgroud)

引发了一个异常,如果没有找到的对象!

看:

如果'foo'找到,oid则返回 - 表示为文本。这是关系名称,根据当前搜索路径进行模式限定,并在必要时加双引号。

如果未找到该关系,您可以确定它不存在于搜索路径中的任何位置 - 或者根本不存在模式限定名称 ( schema.foo)。

如果发现有两个缺点

  1. 搜索包括search_path 的隐式模式,即pg_catalogpg_temp。但是您可能希望出于您的目的排除临时表和系统表。(?)

  2. 流延到regclass系统中的目录的发现任何关系(表状物体)pg_class:表,索引,视图,序列等不只是一个普通的表。其他同名对象可能会产生误报。

缓慢而确定(仍然很快,真的)

我们回到类似于您的查询的内容。但不要使用current_setting('search_path'),它会返回裸设置。使用专用的系统信息功能current_schemas()。手册:

current_schemas(boolean) name[]
搜索路径中的模式名称,可选地包括隐式模式

"$user"search_path巧妙地解决。如果不存在具有当前名称的模式SESSION_USER,则解析为空。您还可以输出隐式模式(pg_catalog可能还有pg_temp) - 但我假设不是手头的情况,所以:

DO 
$do$
BEGIN
   IF EXISTS (
      SELECT  -- list can be empty
      FROM   pg_catalog.pg_class c
      JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
      WHERE  n.nspname = ANY(current_schemas(FALSE))
      AND    n.nspname NOT LIKE 'pg_%'  -- exclude system schemas!
      AND    c.relname = 'foo'
      AND    c.relkind = 'r')           -- you probably need this
   THEN
      RAISE 'This application depends on tables created by another application';
   END IF;
END
$do$;
Run Code Online (Sandbox Code Playgroud)

db<>fiddle here
旧的sqlfiddle