PostgreSQL 9.6 列删除和对带有 CTE 的 SQL 函数的副作用

And*_*ndy 15 postgresql metadata database-internals functions postgresql-9.6

如果我有一个包含 3 列的表格——比如 A、B 和 D——并且我不得不引入一个新的——比如 C 来替换 D 的当前位置。我将使用以下方法:

  1. 引入 2 个新列作为 C 和 D2。
  2. 将 D 的内容复制到 D2。
  3. 删除 D。
  4. 将 D2 重命名为 D。

新的顺序是 A、B、C 和 D。

我认为这是一种合法的做法,因为(到目前为止)它没有产生任何问题。

但是,今天我遇到了一个问题,当一个函数在同一张表上执行语句时返回以下错误:

table row type and query-specified row type do not match

以及以下细节:

Query provides a value for a dropped column at ordinal position 13

我尝试重新启动 PostgreSQL,VACUUM FULL按照此处此处的建议执行并最终删除并重新创建该函数,但这些解决方案不起作用(除了他们尝试解决系统表已被更改的情况之外)。

有幸使用一个非常小的数据库,我导出了它,删除了它,然后重新导入它并解决了我的功能问题


我意识到这样一个事实,即不应通过修改系统表(弄脏pg_attribute等)弄乱列的自然顺序,如下所示:

是否可以更改 Postgres 中列的自然顺序?

从我的函数抛出的错误来看,我现在意识到用我的方法改变列的顺序也是一个禁忌。任何人都可以说明为什么我所做的也是错误的?


Postgres 版本是 9.6.0。

这是函数:

CREATE OR REPLACE FUNCTION "public"."__post_users" ("facebookid" text, "useremail" text, "username" text) RETURNS TABLE (authentication_code text, id integer, key text, stripe_id text) AS '

-- First, select the user:
WITH select_user AS
(SELECT
users.id
FROM
users
WHERE
useremail = users.email),

-- Second, update the user (if user exists):
update_user AS
(UPDATE
users
SET
authentication_code = GEN_RANDOM_UUID(),
authentication_date = current_timestamp,
facebook_id = facebookid
WHERE EXISTS (SELECT * FROM select_user)
AND
useremail = users.email
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id),

-- Third, insert the user (if user does not exist):
insert_user AS
(INSERT INTO
users (authentication_code, authentication_date, email, key, name, facebook_id)
SELECT
GEN_RANDOM_UUID(),
current_timestamp,
useremail,
GEN_RANDOM_UUID(),
COALESCE(username, SUBSTRING(useremail FROM ''([^@]+)'')),
facebookid
WHERE NOT EXISTS (SELECT * FROM select_user)
RETURNING
users.authentication_code,
users.id,
users.key,
users.stripe_id)

-- Finally, select the authentication code, ID, key and Stripe ID:
SELECT
*
FROM
update_user
UNION ALL
SELECT
*
FROM
insert_user' LANGUAGE "sql" COST 100 ROWS 1
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
Run Code Online (Sandbox Code Playgroud)

我进行重命名/重新排序的列都facebook_idstripe_id(之前这些添加了新列,这是改名的原因,但不是由该查询感动)。

将列按特定顺序排列完全不符合顺序。但是,提出这个问题的原因是担心简单的重命名和删除列可能会引发在生产模式下使用函数的人的实际问题(就像我自己发生的那样)。

joa*_*olo 16

9.6 和 9.6.1 上的可能错误

这对我来说完全像是一个错误......

我不知道它为什么会发生,但我可以确认它发生了。这是重现问题的最简单的设置(在版本 9.6.0 和 9.6.1 中)。

CREATE TABLE users
(
    id SERIAL PRIMARY KEY,
    email TEXT NOT NULL,
    column_that_we_will_drop TEXT
) ;

-- Function that uses the previous table, and that has a CTE
CREATE OR REPLACE FUNCTION __post_users
    (_useremail text) 
RETURNS integer AS
$$
-- Need a CTE to produce the error. A 'constant' one suffices.
WITH something_even_if_useless(a) AS
(
    VALUES (1)
)
UPDATE
    users
SET
    id = id
WHERE 
    -- The CTE needs to be referenced, if the next
    -- condition were not in place, the problem is not reproduced
    EXISTS (SELECT * FROM something_even_if_useless)
    AND email = _useremail
RETURNING
    id
$$
LANGUAGE "sql" ;
Run Code Online (Sandbox Code Playgroud)

完成此设置后,下一条语句才有效

SELECT * FROM __post_users('a@b.com');
Run Code Online (Sandbox Code Playgroud)

此时,我们删除一列:

ALTER TABLE users 
    DROP COLUMN column_that_we_will_drop ;
Run Code Online (Sandbox Code Playgroud)

此更改使下一条语句生成错误

SELECT * FROM __post_users('a@b.com');
Run Code Online (Sandbox Code Playgroud)

这与@Andy 提到的相同:

ERROR: table row type and query-specified row type do not match
SQL state: 42804
Detail: Query provides a value for a dropped column at ordinal position 3.
Context: SQL function "__post_users" statement 1
    SELECT * FROM __post_users('a@b.com');
Run Code Online (Sandbox Code Playgroud)

删除并重新创建函数并不能解决问题。
VACUUM FULL(表或整个数据库)不能解决问题。


错误报告已传递到相应的 PostgreSQL 邮件列表,我们得到了非常快速的响应

我无法在 HEAD 或 9.6 分支提示中重现这一点。我相信它已经被这个补丁修复了,它在 9.6.1 之后出现了一点:

https://git.postgresql.org/gitweb/?p=postgresql.git&a=commitdiff&h=f4d865f22

但是谢谢你的报告!

问候,汤姆·莱恩


版本 9.6.2

在 2017 年 3 月 6 日,我可以确认我无法在 9.6.2 版上重现此行为。也就是说,该错误似乎已在此版本中得到纠正。

更新

根据@Jana 的评论:“我可以确认该错误存在于 9.6.1 中并已在 9.6.2 中修复。该修复程序也列在postgres 发布网站上:修复虚假的“查询为删除的列提供值”期间的错误在具有删除列的表上插入或更新”


  • 我使用的是 9.6.0,@joanolo 是正确的,我可以用他的方法重现错误。如果 Tom 不能重现它,那可能是这个特定版本(我假设是 9.6.1?)的一个孤立错误。如答案中所述,当_删除_表中的列_和_使用具有影响_那个_表的 CTE 的函数时,会出现问题。因此,问题不仅仅是重新排序,这就是我试图解决的问题。我将编辑标题以反映这一点。 (4认同)