Eu tenho o seguinte gatilho na tabela "Posts":
create
or replace function update_last_post_series () returns trigger as $ $ begin
update
"Series"
set
"lastPostAdded" = now()
where
id = NEW."SeriesId"
and NEW."SeriesId" is not null
and (
TG_OP = 'INSERT'
or OLD."SeriesId" is null
or (
OLD.status != 'published'
and NEW.status = 'published'
)
)
and NEW."isPublished" = true
and NEW.status = 'published';return NEW;end;$ $ language plpgsql;
create trigger update_last_post_series_trg
after
insert
or
update
on "Posts" for each row execute procedure update_last_post_series();
Desde alguns dias (parece que o banco de dados pg foi atualizado para a versão 10.21, mas não tenho certeza de qual versão) o gatilho acima começou a lançar exceções:
POST_CREATE_ERROR record "old" is not assigned yet { error: record "old" is not assigned yet
Jun 28 11:57:26 name: 'error',
Jun 28 11:57:26 length: 547,
Jun 28 11:57:26 severity: 'ERROR',
Jun 28 11:57:26 code: '55000',
Jun 28 11:57:26 detail: 'The tuple structure of a not-yet-assigned record is indeterminate.',
Jun 28 11:57:26 hint: undefined,
Jun 28 11:57:26 position: undefined,
Jun 28 11:57:26 internalPosition: undefined,
Jun 28 11:57:26 internalQuery: undefined,
Jun 28 11:57:26 where: 'SQL statement "update "Series"\n set "lastPostAdded" = now()\n where id = NEW."SeriesId" and NEW."SeriesId" is not null\n and (TG_OP = \'INSERT\' or OLD."SeriesId" is null or (OLD.status != \'published\' and NEW.status = \'published\'))\n and NEW."isPublished" = true and NEW.status = \'published\'"\nPL/pgSQL function update_last_post_series() line 3 at SQL statement',
Jun 28 11:57:26 schema: undefined,
Jun 28 11:57:26 table: undefined,
Jun 28 11:57:26 column: undefined,
Jun 28 11:57:26 dataType: undefined,
Jun 28 11:57:26 constraint: undefined,
Jun 28 11:57:26 file: 'pl_exec.c',
Jun 28 11:57:26 line: '4932',
Jun 28 11:57:26 routine: 'exec_eval_datum',
Jun 28 11:57:26 sql: 'INSERT INTO "Posts" .....
Não faço ideia de por que isso está acontecendo porque parece que a condição antes de acessar o registro OLD não deve permitir que isso aconteça:
TG_OP = 'INSERT'
or OLD."SeriesId" is null -- should never be called if TG_OP = 'INSERT' because first condition is true in this case
or (OLD.status != 'published' and NEW.status = 'published') -- should never be called if TG_OP = 'INSERT' because first condition is true in this case
O que está acontecendo aqui, é possível que o executor da consulta avalie as condições separadas por OR em paralelo? Por que estava funcionando por vários anos até agora?
Usando PG 10.21.
A consulta UPDATE no gatilho depende de subexpressões em uma condição OR sendo avaliada da esquerda para a direita e curto-circuito acontecendo quando um operando esquerdo é
false
.O problema é que essa premissa não é verdadeira. A documentação avisa explicitamente que não podemos contar com isso:
Regras de avaliação de expressão :
Por que não deu erro antes? Bem, o plano de execução pode mudar devido ao tamanho da alteração dos dados ou a alterações de código em pequenas atualizações (embora sejam limitadas a correções de bugs, podem ocorrer efeitos colaterais).