quote_ident() 不会为列名“first”添加引号 不要省略列别名的 AS 关键字绝对肯定:quote_all_identifiers更快

问题描述

我需要将列名正确地用双引号括起来。 quote_ident() 似乎没有这样做?

select 1 first; -- fails
select quote_ident('first'); -- produces first,not "first"

我可以使用什么命令来成功引用标识符。我正在尝试用它动态构建一个 SELECT 语句:

SELECT 'select ' 
|| string_agg(
        case when udt_name in ('varchar','text')
            then 'left(' || quote_ident(column_name) || ',65535) ' || quote_ident(column_name)
        else quote_ident(column_name)
        end,',' order by ordinal_position) 
|| ' from "public"."MyTableName"'
FROM information_schema.columns c
join parse_ident('"public"."MyTableName"') t 
on t[1] = table_schema and t[2] = table_name

这是生成

SELECT id,left(first,65535) first from "public"."MyTableName";

这是因为首先作为列名需要用双引号括起来。

解决方法

不要省略列别名的 AS 关键字

SELECT id,left(first,65535) first from "public"."MyTableName";

这是因为首先作为列名需要用双引号括起来。

不完全是。它爆炸是因为您在不应省略的地方省略了关键字 AS

这有效:

SELECT 'select ' 
|| string_agg(
        case when udt_name in ('varchar','text')
            then 'left(' || quote_ident(column_name) || ',65535) AS '  -- !!
              ||  quote_ident(column_name)
        else quote_ident(column_name)
        end,',' order by ordinal_position) 
|| ' from "public"."MyTableName"'
FROM information_schema.columns c
join parse_ident('"public"."MyTableName"') t 
on t[1] = table_schema and t[2] = table_name;

产生:

SELECT id,65535) AS first from "public"."MyTableName";

依次按预期工作。

manual about "Omitting the AS Key Word"

在SQL标准中,可选关键字AS之前可以省略 每当新列名是有效列时的输出列名 名称(即,与任何保留关键字不同)。 PostgreSQL 是 稍微严格一点:如果新列名是 AS,则需要 完全匹配任何关键字,保留与否。推荐做法是 使用 AS 或双引号输出列名,以防止任何可能的 与未来的关键字添加冲突。

可以省略表别名的关键字 AS,但不能省略列别名。

first 不是 Postgres 中的 reserved word。 (它曾经在古老的 SQL 标准 SQL-92 中被“保留”,但在标准 SQL 中也不再存在。)准确地说,它是 “非保留”*。 The manual

非保留关键词只在特定语境中具有特殊意义,在其他语境中可以作为标识符使用。

省略 AS 使其成为这样的上下文。

quote_ident() 工作可靠。 The manual:

返回适当引用的给定字符串以用作标识符 在 SQL 语句字符串中。仅在必要时才添加引号(即 如果字符串包含非标识符字符或将是 案例折叠)。嵌入的引号正确地加倍。

带有说明符 format()

%I 也是如此。

保留词未提及,但无论如何正确引用。准确地说:the SQL Key Words table的“PostgreSQL”列中所有标记为“保留”“(不能是函数或类型)”的关键字。 >

我将提交一个文档错误来添加它。

绝对肯定:quote_all_identifiers

如果您想绝对确定并且不介意所有添加的噪音,您可以强制 Postgres 使用配置参数 quote_all_identifiers 引用 all 标识符。 The manual:

当数据库生成 SQL 时,强制引用所有标识符,即使它们不是(当前)关键字。

这包括来自 quote_ident()format() 的输出。我不会那样做,害怕所有增加的噪音。

您可以在同一事务中使用 SET LOCAL 在本地设置参数。喜欢:

BEGIN;
SET LOCAL quote_all_identifiers = true;
SELECT ...
END;

更快

也就是说,我会使用 format()concat() 并以目录表 pg_attribute 为目标:更干净、更简单、更快。但不能移植到其他 RDBMS:

SELECT format('SELECT %s FROM %s;',string_agg(CASE WHEN atttypid = ANY ('{text,bpchar,varchar}'::regtype[])
                              THEN concat('left(',col,65535) AS ',col)
                              ELSE col END,'),attrelid)
FROM  (
   SELECT attrelid::regclass,atttypid,quote_ident(attname) AS col
   FROM   pg_catalog.pg_attribute
   WHERE  attrelid = 'public."MyTableName"'::regclass  -- provide once,optionally schema-qualified
   AND    attnum > 0
   AND    NOT attisdropped
   ORDER  BY attnum
   ) sub
GROUP  BY attrelid;

产生:

SELECT id,65535) AS first FROM "MyTableName";

dbfiddle here

值得注意的是,...

  • ...您只需要提供一次表名,可选择模式限定。
  • ...如果表不存在,查询会立即失败并显示有用的错误消息。
  • ... 输出表名称仅在必要时使用模式限定和双引号。
  • ...这也包括 character(N)(内部名称 bpchar)。

进一步阅读: