quote_ident() 不向列名 "first" 添加引号

quote_ident() does not add quotes to column name "first"

我需要得到一个用双引号正确括起来的列名。 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";

Which blows up because first as a column name needs to be wrapped in double quotes.

不完全是。它爆炸了,因为你在不应该省略的地方省略了关键字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, left(first, 65535) AS first from "public"."MyTableName";

依次按预期工作。

manual about "Omitting the AS Key Word":

In the SQL standard, the optional key word AS can be omitted before an output column name whenever the new column name is a valid column name (that is, not the same as any reserved keyword). PostgreSQL is slightly more restrictive: AS is required if the new column name matches any keyword at all, reserved or not. Recommended practice is to use AS or double-quote output column names, to prevent any possible conflict against future keyword additions.

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

first 不是 reserved word in Postgres. (It used to be "reserved" in the ancient SQL standard SQL-92, but not any more in standard SQL, either.) It is "non-reserved"* to be precise. The manual:

Non-reserved key words only have a special meaning in particular contexts and can be used as identifiers in other contexts.

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

quote_ident() 工作可靠。 The manual:

Returns the given string suitably quoted to be used as an identifier in an SQL statement string. Quotes are added only if necessary (i.e., if the string contains non-identifier characters or would be case-folded). Embedded quotes are properly doubled.

format() 与说明符 %I 的作用相同。

保留字未提及,但无论如何正确引用。准确的说:[=的PostgreSQL]栏中所有标有"reserved""(不能是function或type)"的关键字44=].

我将提交文档错误以添加它。

绝对确定:quote_all_identifiers

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

When the database generates SQL, force all identifiers to be quoted, even if they are not (currently) keywords.

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

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

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

更快

也就是说,我会使用 format()concat() 并以目录 table pg_attribute 为目标:更干净、更简单、更快。但不能 portable 到其他 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, left(first, 65535) AS first FROM "MyTableName";

db<>fiddle here

值得注意的是,...

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

进一步阅读: