Se eu tivesse uma tabela com 3 colunas - digamos A, B e D - e tivesse que introduzir uma nova - digamos C para substituir a posição atual de D. Eu usaria o seguinte método:
- Introduzir 2 novas colunas como C e D2.
- Copie o conteúdo de D para D2.
- Excluir D.
- Renomeie D2 para D.
A nova ordem seria A, B, C e D.
Eu pensei que essa era uma prática legítima, pois (até agora) ela não produziu problemas.
No entanto, hoje me deparei com um problema quando uma função que executava uma instrução na mesma tabela retornava o seguinte erro:
table row type and query-specified row type do not match
E o seguinte detalhe:
Query provides a value for a dropped column at ordinal position 13
Tentei reiniciar o PostgreSQL, executando VACUUM FULL
e, finalmente, excluindo e recriando a função, conforme sugerido aqui e aqui, mas essas soluções não funcionaram (exceto pelo fato de tentarem resolver uma situação em que uma tabela do sistema foi alterada).
Tendo o luxo de trabalhar com um banco de dados muito pequeno, eu o exportei, excluí-o e reimportei-o, e isso corrigiu o problema com a minha função.
Eu estava ciente do fato de que não se deve mexer com a ordem natural das colunas modificando as tabelas do sistema (sujar as mãos compg_attribute
etc.), como visto aqui:
É possível alterar a ordem natural das colunas no Postgres?
A julgar pelo erro gerado pela minha função, agora percebo que mudar a ordem das colunas com o meu método também é um não-não. Alguém pode esclarecer por que o que estou fazendo também está errado?
A versão do Postgres é 9.6.0.
Aqui está a função:
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
Eu executei a renomeação / reordenação nas duas colunas facebook_id
e stripe_id
(uma nova coluna foi adicionada antes delas, que é o motivo da renomeação, mas não é tocada por esta consulta).
Ter as colunas em uma determinada ordem é totalmente desinteressante para a ordem. No entanto, a razão para fazer esta pergunta é preocupante, pois uma simples renomeação e exclusão de uma coluna pode desencadear problemas reais para alguém que usa funções no modo de produção (como aconteceu comigo).