Si j'avais un tableau avec 3 colonnes - disons A, B et D - et que je devais en introduire un nouveau - disons C pour remplacer la position actuelle de D. J'utiliserais la méthode suivante:
- Introduisez 2 nouvelles colonnes en C et D2.
- Copiez le contenu de D vers D2.
- Supprimer D.
- Renommez D2 en D.
Le nouvel ordre serait A, B, C et D.
Je pensais que c'était une pratique légitime car (jusqu'à présent) cela ne posait aucun problème.
Cependant, aujourd'hui, je suis tombé sur un problème lorsqu'une fonction exécutant une instruction sur la même table a renvoyé l'erreur suivante:
table row type and query-specified row type do not match
Et le détail suivant:
Query provides a value for a dropped column at ordinal position 13
J'ai essayé de redémarrer PostgreSQL, de faire VACUUM FULL
et enfin de supprimer et de recréer la fonction comme suggéré ici et ici mais ces solutions n'ont pas fonctionné (à part le fait qu'ils essaient de résoudre une situation où une table système a été modifiée).
Ayant le luxe de travailler avec une très petite base de données, je l'ai exportée, supprimée puis réimportée et cela a résolu le problème de ma fonction.
J'étais conscient du fait qu'il ne fallait pas jouer avec l'ordre naturel des colonnes en modifiant les tables système (se salir les mains avec pg_attribute
, etc.) comme on le voit ici:
Est-il possible de changer l'ordre naturel des colonnes dans Postgres?
À en juger par l'erreur générée par ma fonction, je me rends compte maintenant que le changement de l'ordre des colonnes avec ma méthode est également un non-non. Quelqu'un peut-il expliquer pourquoi ce que je fais est également mauvais?
La version Postgres est 9.6.0.
Voici la fonction:
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
J'ai effectué le changement de nom / réorganisation sur les deux colonnes facebook_id
et stripe_id
(une nouvelle colonne a été ajoutée avant celles-ci, ce qui est la raison du changement de nom, mais n'est pas touché par cette requête).
Avoir les colonnes dans un certain ordre est purement sans intérêt pour l'ordre. Cependant, la raison de poser cette question est de ne pas craindre qu'un simple changement de nom et suppression d'une colonne puisse déclencher de réels problèmes pour quelqu'un qui utilise des fonctions en mode production (comme cela m'est arrivé).
Réponses:
Bogue probable sur 9.6 et 9.6.1
Cela ressemble complètement à un bug pour moi ...
Je ne sais pas pourquoi cela se produit, mais je peux confirmer que cela se produit. Il s'agit de la configuration la plus simple trouvée qui reproduit le problème (dans les versions 9.6.0 et 9.6.1).
Après cette configuration, la prochaine instruction fonctionne
À ce stade, nous DROP une colonne:
Cette modification rend l'instruction suivante pour générer une erreur
qui est la même que celle mentionnée par @Andy:
La suppression et la recréation de la fonction ne résout PAS le problème.
VACUUM FULL (la table ou la base de données entière) ne résout pas le problème.
Le rapport de bogue a été transmis à la liste de diffusion PostgreSQL appropriée et nous avons eu une réponse très rapide :
Version 9.6.2
Le 2017-03-06, je peux confirmer que je ne peux pas reproduire ce comportement sur la version 9.6.2. Autrement dit, le bogue semble avoir été corrigé sur cette version.
MISE À JOUR
Par commentaire de @Jana: «Je peux confirmer que le bogue est présent dans 9.6.1 et a été corrigé dans 9.6.2. Le correctif est également répertorié sur le site Web de la version postgres : INSÉRER ou METTRE À JOUR sur une table avec une colonne supprimée "
la source
Je sais que vous avez probablement déjà entendu cela auparavant, mais c'est une idée horrible.
SELECT *
Donc, si cela n'a pas d'importance du tout ne vous dissuade pas et nous reconnaissons que nous ne faisons que jouer à Photoshop avec une structure de lignes et que nous sommes obsédés par l'affichage, expliquons encore quelques choses.
CREATE TABLE
non plus (bien que ce soit une priorité beaucoup plus élevée)PostgreSQL est donc une mauvaise couche d'affichage. Sur tout cela, bien que cela
ALTER
fonctionne bien, vous ne devriez pas tempérer le dragon. Les deuxALTER TABLE
, et la section CAVEAT en font mention,Et, si tout cela ne suffit pas, et vous voulez toujours prétendre que c'est une bonne idée plutôt une horrible idée. Alors essayez ceci,
pg_dump -t
BEGIN
une transactionDROP
l'ancienne table entièrement,RENAME
la table temp à la table prod.COMMIT
Si tout cela semble excessif, gardez à l'esprit que la mise à jour des lignes dans la base de données nécessite de réécrire les lignes (en supposant qu'elles ne sont pas TOAST . Vous devez analyser les données et reconstruire le schéma de la table, mais de toute façon vous devez réécrire Si je devais faire cette tâche, c'est comme ça que je le ferais.
Mais, tout cela parle d'une manière générale. Personne n'a reproduit vos résultats.
Vous avez donné un cas de test que nous ne pouvons pas exécuter
Et, vous ne nous avez pas dit la version exacte sur laquelle vous vous trouvez.
la source
J'ai contourné ce bogue en sauvegardant et en restaurant ma base de données.
Étapes pour Heroku
heroku maintenance:on
heroku pg:backups:capture
heroku pg:backups:restore
heroku restart
heroku maintenance:off
la source
J'ai aussi rencontré ce bug. Pour ceux qui ne veulent pas sauvegarder / restaurer complètement leur base de données. Sachez que la simple copie de la table fonctionne. Il n'y a cependant aucun moyen "magique" de copier une table. Je l'ai fait en utilisant:
Après cela, vous devrez toujours recréer manuellement vos index, clés étrangères et valeurs par défaut. Recréer la table comme ceci a fait disparaître le bug.
la source