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).
Respostas:
Provável bug no 9.6 e 9.6.1
Isso me parece completamente um bug ...
Não sei por que isso acontece, mas posso confirmar que isso acontece. Essa é a instalação mais simples encontrada que reproduz o problema (na versão 9.6.0 e 9.6.1).
Após essa configuração, a próxima instrução funciona
Neste ponto, deixamos cair uma coluna:
Essa alteração faz com que a próxima instrução gere um erro
que é o mesmo mencionado por @Andy:
Soltar e recriar a função NÃO resolve o problema.
VACUUM FULL (a tabela ou o banco de dados inteiro) não resolve o problema.
O relatório de erro foi passado para a lista de discussão apropriada do PostgreSQL e tivemos uma resposta muito rápida :
Versão 9.6.2
Em 06/03/2017, posso confirmar que não consigo reproduzir esse comportamento na versão 9.6.2. Ou seja, o bug parece ter sido corrigido nesta versão.
ATUALIZAR
Por comentário de @Jana: "Posso confirmar que o bug está presente na 9.6.1 e foi corrigido na 9.6.2. A correção também está listada no site de lançamento do postgres : A correção" espúria "a consulta" fornece um valor para uma coluna eliminada "erros durante INSERT ou UPDATE em uma tabela com uma coluna descartada "
fonte
Eu sei que você provavelmente já ouviu isso antes, mas essa é uma ideia horrível.
SELECT *
Portanto, se não importa nada não o dissuade e reconhecemos que estamos apenas jogando Photoshop com estrutura de linhas e obcecado com a exibição, vamos explicar mais algumas coisas.
CREATE TABLE
(embora isso seja uma prioridade muito maior)Portanto, o PostgreSQL é uma camada de exibição ruim. Além de tudo isso, enquanto
ALTER
funciona bem, você não deve temperar o dragão. AmbosALTER TABLE
, e da secção CAVEAT fazer menção a isso,E, se tudo isso não for suficiente, e você ainda quiser fingir que essa é uma boa ideia, é horrível. Então tente isso,
pg_dump -t
BEGIN
uma transaçãoDROP
a velha mesa inteiramente,RENAME
a tabela temporária para a tabela prod.COMMIT
Se tudo isso parecer excessivo, lembre-se de que a atualização de linhas no banco de dados exige a reescrita das linhas (supondo que elas não sejam TOAST . Você está tendo que analisar os dados e reconstruir o esquema da tabela, mas de qualquer forma você deve reescrever Se eu tivesse que fazer essa tarefa, é assim que eu faria.
Mas, tudo isso está falando em geral. Ninguém reproduziu seus resultados.
Você deu um caso de teste que não podemos executar
E você não nos disse a versão exata em que está.
fonte
Eu resolvi esse erro fazendo backup e restaurando meu banco de dados.
Passos para Heroku
heroku maintenance:on
heroku pg:backups:capture
heroku pg:backups:restore
heroku restart
heroku maintenance:off
fonte
Também encontrei esse bug. Para aqueles que não desejam fazer backup / restauração completos de seus bancos de dados. Saiba que simplesmente copiar a tabela funciona. Não existe uma maneira "mágica" de copiar uma tabela. Eu fiz isso usando:
Depois disso, você ainda precisará recriar manualmente seus índices, chaves estrangeiras e padrões. Recriar a tabela assim fez o bug desaparecer.
fonte