Si tuviera una tabla con 3 columnas, digamos A, B y D, y tuviera que introducir una nueva, diga C para reemplazar la posición actual de D. Usaría el siguiente método:
- Introducir 2 nuevas columnas como C y D2.
- Copie el contenido de D a D2.
- Eliminar D.
- Cambiar el nombre de D2 a D.
El nuevo orden sería A, B, C y D.
Pensé que esta era una práctica legítima ya que (hasta ahora) no produjo problemas.
Sin embargo, hoy me encontré con un problema cuando una función que realizaba una declaración en la misma tabla devolvía el siguiente error:
table row type and query-specified row type do not match
Y el siguiente detalle:
Query provides a value for a dropped column at ordinal position 13
Intenté reiniciar PostgreSQL, haciendo una VACUUM FULL
y finalmente eliminando y volviendo a crear la función como se sugiere aquí y aquí, pero estas soluciones no funcionaron (aparte del hecho de que intentan abordar una situación en la que se ha alterado una tabla del sistema).
Teniendo el lujo de trabajar con una base de datos muy pequeña, la exporté, la eliminé y luego la volví a importar y eso solucionó el problema con mi función.
Era consciente del hecho de que uno no debería perder el tiempo con el orden natural de las columnas modificando las tablas del sistema (ensuciarse las manos pg_attribute
, etc.) como se ve aquí:
¿Es posible cambiar el orden natural de las columnas en Postgres?
A juzgar por el error arrojado por mi función, ahora me doy cuenta de que cambiar el orden de las columnas con mi método también es un no-no. ¿Alguien puede arrojar algo de luz sobre por qué lo que estoy haciendo también está mal?
La versión de Postgres es 9.6.0.
Aquí está la función:
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
Realicé el cambio de nombre / reordenamiento en ambas columnas facebook_id
y stripe_id
(se agregó una nueva columna antes de estas, que es la razón del cambio de nombre, pero esta consulta no la toca).
Tener las columnas en un cierto orden es puramente de interés para el orden. Sin embargo, la razón para hacer esta pregunta es preocupante porque un simple cambio de nombre y eliminación de una columna puede provocar problemas reales para alguien que usa funciones en modo de producción (como me sucedió a mí mismo).