Isso é esperado, comportamento documentado.
Tom Lane explica aqui.
Documentado no manual aqui:
Declarações de dados modificando em WITH
são executadas exatamente uma vez, e
sempre para a conclusão , independentemente da consulta principal lê tudo (ou mesmo qualquer) de sua produção. Observe que isso é diferente da regra para SELECT
em WITH
: como declarado na seção anterior, a execução de a SELECT
é realizada apenas na medida em que a consulta primária exigir sua saída .
Negrito ênfase minha. "Data-modificando" são INSERT
, UPDATE
e DELETE
consultas. (Ao contrário de SELECT
.). O manual mais uma vez:
Você pode usar as declarações que modifique os dados ( INSERT
, UPDATE
ou DELETE
) em WITH
.
Função adequada
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;
Larguei as cláusulas padrão (ruído) e
STRICT
é o sinônimo curto deRETURNS NULL ON NULL INPUT
.
Certifique-se de alguma forma que os nomes dos parâmetros não entrem em conflito com os nomes das colunas. Eu comecei com _
, mas essa é apenas a minha preferência pessoal.
Se coin
puder ser NULL
, sugiro:
SET coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END
Se users.id
é a chave primária, então RETURNS TABLE
nem ROWs 1000
faz sentido. Somente uma única linha pode ser atualizada / retornada. Mas isso não vem ao caso principal.
Chamada adequada
Não faz sentido usar a RETURNING
cláusula e retornar valores de sua função se você for ignorar os valores retornados na chamada. Também não faz sentido decompor as linhas retornadas SELECT * FROM ...
se você as ignorar de qualquer maneira.
Apenas retorne uma constante escalar ( RETURNING 1
), defina a função como RETURNS int
(ou solte-a RETURNING
completamente e faça-a RETURNS void
) e chame-a comSELECT my_function(...)
Solução
Desde que você ...
realmente não me importo com o resultado
.. apenas SELECT
uma forma constante do CTE. É garantido que será executado desde que seja referenciado no exterior SELECT
(direta ou indiretamente).
WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;
Se você realmente tem uma função de retorno de conjunto e ainda não se importa com a saída:
WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;
Não há necessidade de retornar mais de uma linha. A função ainda é chamada.
Por fim, não está claro por que você precisa do CTE para começar. Provavelmente apenas uma prova de conceito.
Intimamente relacionado:
Resposta relacionada no SO:
E considere: