Se eu fizer uma consulta usando CTEs parecida com esta:
WITH cte_a AS (
SELECT a.id, a.something
FROM a
WHERE a.something IS NOT NULL
),
-- [...] some other CTEs
cte_d AS (
SELECT
cte_a.id,
cte_a.something
FROM cte_a
JOIN
-- something
JOIN
-- something
WHERE
-- something
ORDER BY cte_a.id ASC
FOR UPDATE -- Here, will the `FOR UPDATE` locks `a` rows?
),
-- rest of the query, which will update `a.something`.
O bloqueio para atualização será aplicado nas linhas da tabela a
ou o bloqueio será aplicado em uma tabela materializada gerada por cte_a
?
Se o bloqueio se aplicar à tabela materializada, o seguinte resolveria o problema?
WITH cte_a AS NOT MATERIALIZED (
-- Rest of the query
Estou usando o PostgreSQL v14.
Se isso mudar alguma coisa, estou mais interessado no comportamento com o nível de isolamento READ COMMITTED padrão.
A base da minha resposta é fornecida no manual:
Então:
Para bloquear apenas linhas de tabelas específicas na
FROM
cláusula, liste seus nomes na cláusula de bloqueio. Caso contrário, todas as linhas qualificadas de todas as tabelas daFROM
lista serão bloqueadas.Sem restrição de tabela na cláusula de bloqueio, todas as linhas selecionadas de todas as tabelas na
FROM
cláusula serão bloqueadas.Uma cláusula de bloqueio na consulta primária não é propagada para CTEs.
Isso ainda não esclarece completamente o seu caso complexo :
SELECT
da tabelaa
emcte_a
.cte_d
temcte_a
em suaFROM
cláusula, além de algumas outras tabelas, além de mais filtros.FOR UPDATE
cláusula de bloqueio sem nomear tabelas específicas.UPDATE a
na consulta primária.Por experiência e alguns testes superficiais rápidos adicionais:
A cláusula de bloqueio não se aplica a
WITH
consultas naFROM
lista em nenhum caso. Não apenas na consulta primária, mas também em outros CTEs.Além do mais, adicionar uma cláusula de bloqueio evita o "inlining" do mesmo CTE (otimização automática ou explicitamente exigida no
NOT MATERIALIZED
Postgres 12+). Portanto, todas as linhas qualificadas neste CTE específico são bloqueadas, filtros adicionais em outros CTEs ("posteriores") ou na consulta primária não restringem o conjunto.Conclusão
Para bloquear linhas de table
a
, você deve adicionar a cláusula de bloqueio em uma consulta onde essa tabela esteja listada naFROM
cláusula. Sua consulta poderia ser assim:Porém , isso pode bloquear mais linhas do que o necessário. Tente aplicar todos os filtros antes da cláusula de bloqueio. (Nas mesmas
SELECT
contagens.)Eu provavelmente reescreveria toda a consulta.
Como testar?
Configuração mínima:
Em seguida, inicie uma transação em uma sessão e deixe-a aberta:
Em seguida, tente em outra sessão (conexão separada/janela de consulta separada):
Isso ocorrerá ou será interrompido por um bloqueio de linha (nesse caso, você aborta).
Em cada sessão, execute
ROLLBACK;
antes do próximo teste.Varie para ver os efeitos de cada constelação.