Esto se espera, comportamiento documentado.
Tom Lane lo explica aquí.
Documentado en el manual aquí:
Declaraciones de datos modificadores en WITHse ejecutan exactamente una vez, y
siempre hasta el final , independientemente de si la consulta primaria lee todos (o cualquier) de su producción. Tenga en cuenta que esto es diferente de la regla para SELECTin WITH: como se indicó en la sección anterior, la ejecución de SELECTa solo se lleva a cabo hasta que la consulta primaria exija su salida .
El énfasis audaz es mío. "Datos modificadores" son INSERT, UPDATEy DELETEconsultas. (A diferencia de SELECT). El manual una vez más:
Puede utilizar las declaraciones de datos modificadores ( INSERT, UPDATEo DELETE) en WITH.
Función adecuada
CREATE OR REPLACE FUNCTION public.__post_users_id_coin (_coins integer, _userid integer)
RETURNS TABLE (id integer) AS
$func$
UPDATE users u
SET coin = u.coin + _coins -- see below
WHERE u.id = _userid
RETURNING u.id
$func$ LANGUAGE sql COST 100 ROWS 1000 STRICT;
Eliminé las cláusulas predeterminadas (ruido) y
STRICTes el sinónimo corto deRETURNS NULL ON NULL INPUT .
Asegúrese de alguna manera de que los nombres de los parámetros no entren en conflicto con los nombres de las columnas. Prefiero _, pero esa es solo mi preferencia personal.
Si coinpuede ser NULLsugiero:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Si users.ides la clave principal, entonces RETURNS TABLEni ROWs 1000tiene sentido. Solo se puede actualizar / devolver una sola fila. Pero eso está al lado del punto principal.
Llamada adecuada
No tiene sentido usar la RETURNINGcláusula y devolver los valores de su función si va a ignorar los valores devueltos en la llamada de todos modos. Tampoco tiene sentido descomponer filas devueltas SELECT * FROM ...si las ignora de todos modos.
Simplemente devuelva una constante escalar ( RETURNING 1), defina la función como RETURNS int(o suelte por RETURNINGcompleto y hágala RETURNS void) y llámela conSELECT my_function(...)
Solución
Desde que tu ...
realmente no me importa el resultado
.. solo SELECTuna forma constante del CTE. Se garantiza que se ejecutará siempre que se haga referencia en el exterior SELECT(directa o indirectamente).
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Si realmente tiene una función de retorno de conjunto y aún no le importa la salida:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
No es necesario devolver más de 1 fila. La función todavía se llama.
Finalmente, no está claro por qué necesita el CTE para empezar. Probablemente solo una prueba de concepto.
Estrechamente relacionada:
Respuesta relacionada sobre SO:
Y considere: