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é).