如果我有一个包含3列的表(例如A,B和D),并且我不得不引入一个新表(例如C)来替换D的当前位置。我将使用以下方法:
- 引入2个新列作为C和D2。
- 将D的内容复制到D2。
- 删除D。
- 将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,执行a VACUUM FULL
,最后按照此处和此处的建议删除并重新创建该函数,但是这些解决方案均无效(除了它们尝试解决系统表已更改的情况外)。
由于可以使用非常小的数据库,因此我将其导出,删除并重新导入,从而解决了我的功能问题。
我知道这样一个事实,即不应该通过修改系统表来弄乱列的自然顺序(用弄脏手pg_attribute
等),如下所示:
从我的函数抛出的错误来看,我现在意识到用我的方法移动列的顺序也是不行的。谁能为我的工作为什么也出错提供一些启发?
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
我进行重命名/重新排序的列都facebook_id
和stripe_id
(之前这些添加了新列,这是改名的原因,但不是由该查询感动)。
按特定顺序排列列纯粹是无聊的。但是,提出此问题的原因是出于担心,对于在生产模式下使用函数的人来说,简单的重命名和删除列可能会触发实际问题(就像我自己所做的那样)。