PL/pgSQL regclass 引用名为 like 关键字的表

tom*_*mka 5 postgresql syntax datatypes plpgsql regclass

我想通过在现有表名的基础上附加后缀来创建一个新表名。Postgres 9.5 PL/pgSQL 函数获取作为regclass类型传入的现有表名,并以字符串形式返回新名称。我format()用来构造新名称,通常会使用%I占位符。只要传入的表名不匹配任何 PL/pgSQL 关键字,这就会起作用。在这种情况下,无论我选择(%I%s)什么类型的组件,引用都是错误的。

考虑以下函数:

CREATE OR REPLACE FUNCTION make_name(old_name regclass)
RETURNS text                                           
LANGUAGE plpgsql AS                                    
$$                                                     
BEGIN                                                  
    RETURN format('%I_new', old_name);                 
END;                                                   
$$;                                                    
Run Code Online (Sandbox Code Playgroud)

进一步假设有两个表:treenodeoverlay。为这两个函数调用此函数会产生以下新名称:

SELECT make_name('overlay'::regclass);
     make_name     
-------------------
 """overlay"""_new
(1 row)

SELECT make_name('treenode'::regclass);
  make_name   
--------------
 treenode_new
(1 row)
Run Code Online (Sandbox Code Playgroud)

事实证明overlay它也是一个 PL/pgSQL 函数(就像format,但treenode不是),它似乎改变了引用行为。如果%s与 一起使用format(),结果将是"overlay"_new。当我使用||运算符时也会发生同样的情况。如果我使用text作为输入参数类型,一切都按预期工作,但我更喜欢使用regclass.

有没有办法用不带引号的关键字匹配regclass表名(例如overlay)来格式化字符串,就像非关键字匹配regclass表名(例如treenode)的情况一样?

Erw*_*ter 8

解释

我将一一解决多层误解 - 得出一个简单、安全的解决方案。

0.

overlay被引用的原因不是因为它是一个函数名,而是因为它是一个保留字
此外,overlay()不是“PL/pgSQL 函数”(也不是 PL/pgSQL 关键字),它是 Postgres 核心中内置的标准 SQL 函数(LANGUAGE internal因此是幕后的 C)。事实上,PL/pgSQL 与这些都无关。

1.

一个简单的逻辑错误。

format('%I_new', old_name)
Run Code Online (Sandbox Code Playgroud)

这将在附加'_new'之前使用双引号转义任何非标准标识符,并且即使text作为输入类型也会失败。在引用全部内容之前,您必须附加“_new”

format('%I', old_name || '_new')
Run Code Online (Sandbox Code Playgroud)

但这仍然不适用于regclass. 继续阅读。

2.

用 为regclass变量添加引号是无稽之谈%I,因为它的文本表示已经在需要的地方自动引用了。(类似于quote_ident(),但不能为 NULL;因为输入是regclass它不能为 NULL 开始。)您将应用它两次。始终%s用于regclass输入(字符串原样)。

3.

format()是矫枉过正。只需使用quote_ident()

quote_ident(old_name || '_new')
Run Code Online (Sandbox Code Playgroud) 4.

最重要的是,如上所述,变量的text表示regclass会自动转义。这是演员阵容中内置的。要获取原始文本,请直接从系统目录中pg_class检索它。一个regclass只是oid这个表的内部。

SELECT relname FROM pg_class WHERE oid = $1;
Run Code Online (Sandbox Code Playgroud)

解决方案

这可以满足您的要求:

CREATE OR REPLACE FUNCTION make_name(old_name regclass)
  RETURNS text AS
$func$
SELECT quote_ident(relname || '_new') FROM pg_class WHERE oid = $1;
$func$  LANGUAGE sql STABLE;
Run Code Online (Sandbox Code Playgroud)

测试

CREATE TEMP TABLE "sUICiDAL' namE"();
CREATE TEMP TABLE overlay();

SELECT make_name('overlay') AS n1
     , make_name('"sUICiDAL'' namE"')  AS n2;

     n1      |          n2
-------------+----------------------
 overlay_new | "sUICiDAL' namE_new"
Run Code Online (Sandbox Code Playgroud)

请注意,这overlay_new是一个完全合法的标识符,因此没有被引用。

如果您希望名称未转义(无双引号),请使用:

SELECT relname || '_new' FROM pg_class WHERE oid = $1;
Run Code Online (Sandbox Code Playgroud)