搜索路径如何影响标识符解析和“当前模式”?

67

是否可以定义默认情况下在哪个模式中创建新表?(称为“未限定表名”)

我看到了一些关于在Postgres中使用“搜索路径”的细节,但我认为它仅适用于检索数据,而不是创建数据。

我有一堆SQL脚本,它们创建了许多表。我想要设置数据库默认情况下在特定模式中创建表 - 当它们具有未限定的名称时,而不是修改脚本。

这可能吗?

2个回答

115

什么是架构搜索路径search_path

手册:

[...] 表通常被称为未经限定的名称,由表名组成。系统通过遵循搜索路径来确定指的是哪张表,这是一系列要查找的模式(schemas)列表。

我强调了加粗部分,这解释了标识符解析

“当前模式”(或“默认模式”)根据文档:

搜索路径中命名的第一个模式称为当前模式。除了作为第一个搜索的模式外,它还是如果CREATE TABLE命令没有指定模式名称,则创建新表的模式。

加粗强调为本人添加。系统模式pg_temp(当前会话的临时对象模式)和pg_catalog自动成为搜索路径的一部分,并且按照此顺序首先搜索。参考手册:

pg_catalog始终有效地成为搜索路径的一部分。如果未在路径中明确命名,则隐式在搜索路径模式之前进行搜索,以确保内置名称始终可寻找到。但是,如果您更喜欢用户定义的名称覆盖内置名称,则可以显式将pg_catalog放置在搜索路径的末尾。

加粗强调与原文相同。而pg_temp排在其前面,除非将其放置在不同的位置。

如何设置?

有多种方法可以设置运行时变量 search_path

  1. Set a cluster-wide default for all roles in all databases in postgresql.conf (and reload). Careful with that!

     search_path = 'blarg,public'
    

    The factory default for this setting is:

     search_path = "$user",public
    

    Note:

    The first element specifies that a schema with the same name as the current user is to be searched. If no such schema exists, the entry is ignored.

    And:

    If one of the list items is the special name $user, then the schema having the name returned by CURRENT_USER is substituted, if there is such a schema and the user has USAGE permission for it. (If not, $user is ignored.)

  2. Set it as default for one database:

     ALTER DATABASE test SET search_path = blarg,public;
    
  3. Set it as default for the role you connect with (effective cluster-wide):

     ALTER ROLE foo SET search_path = blarg,public;
    
  4. Or even (often best!) as default for a role in a database:

     ALTER ROLE foo IN DATABASE test SET search_path = blarg,public;
    
  5. Write the command at the top of your script. Or execute it in your DB session:

     SET search_path = blarg,public;
    
  6. Set a specific search_path for the scope of a function (to be safe from malicious users with sufficient privileges). Read about Writing SECURITY DEFINER Functions Safely in the manual.

    CREATE FUNCTION foo()
      RETURNS void
      LANGUAGE plpgsql SECURITY DEFINER SET search_path=blarg,public,pg_temp
      AS
    $func$
    BEGIN
       -- do stuff
    END
    $func$;

列表中数字越大,优先级越高。
手册还有更多的方法, 如设置环境变量或使用命令行选项。

要查看当前设置:

SHOW search_path;

重置reset

RESET search_path;

手册:

默认值被定义为参数在当前会话中没有发出SET命令时的值。


这就是我在上面的评论中所提到的 :) 谢谢你向我展示这些选项 ;) - thyandrecardoso
1
+1 对于 ALTER DATABASE,之前不知道,非常有用 :) - Tommaso Barbugli
1
谢谢,@Erwin。在阅读了您的答案后,我发现 RESET search_path; 对我很有帮助。(#6?) - Christian - Reinstate Monica C
1
@BKSpurgeon:blarg只是foo和bar的丑陋兄弟。一个随机的名称。 - Erwin Brandstetter
请记住,在会话期间设置搜索路径可能与最理想的pgBouncer策略不兼容。 - John Smith

37

搜索路径确实是您想要的:

% create schema blarg;
% set search_path to blarg;
% create table foo (id int);
% \d
       List of relations
 Schema | Name | Type  | Owner 
--------+------+-------+-------
 blarg  | foo  | table | pgsql

是的,你是对的...但你已经知道了: )。但我以为它不起作用,因为我之前尝试过,但在不同的会话上:一个我做了设置search_path,另一个我创建了表。我以为“set search_path”这件事会对给定的数据库产生影响。我能让它生效吗? - thyandrecardoso
1
顺便说一下,为了再次测试它,我只是把“set search_path”放在SQL脚本的顶部,而不是分别执行两个操作...谢谢! - thyandrecardoso
3
您可以在配置文件中设置search_path参数,或通过以下语句为用户永久设置search_path参数:ALTER USER <user> SET search_path = whatever; 确保翻译内容保持原意,同时通俗易懂。 - Alex Howansky
3
您可以将其设置为数据库的默认值:ALTER DATABASE db SET search_path = ...,甚至可以针对特定的用户/数据库组合进行设置(在9.1上):ALTER ROLE user IN DATABASE db SET search_path = ...,但是如果过度使用这些设置可能会导致混淆,并且请注意不清楚何时转储它们。 - araqnid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接