> Bem, não fiz algo muito bem feito, mas pode ilustrar bem o
> funcionamento do SELECT FOR UPDATE:
>
> CREATE TABLE CONTADOR(
>        ID INTEGER NOT NULL,
>        DESCRICAO VARCHAR(30) NOT NULL,
>        VALOR INTEGER NOT NULL
> );
>
> ALTER TABLE CONTADOR ADD PRIMARY KEY( id );
>
> -- Função que retorna o último contador gravado e incrementa 1 ao final
> CREATE OR REPLACE FUNCTION UF_ADD_CONTADOR( ai_id INTEGER )
> RETURNS INTEGER AS
> $BODY$
> DECLARE
>        li_id INTEGER;
> BEGIN
>        -- Pega o valor. A cláusula FOR UPDATE faz um ROW LOCK EXCLUSIVE
>        -- apenas sobre o registro especificado. Somente após um COMMIT ou
>        -- ROLLBACK o LOCK será liberado.
>        -- NOTA: o registro deve existir na tabela. Você pode fazer o 
> tratamento
>        -- para criá-lo caso não exista ou então deixar pré-cadastrado
>        SELECT
>                valor
>        INTO
>                li_id
>        FROM
>                contador
>        WHERE
>                id = ai_id
>        FOR UPDATE;
>
>        -- Incrementa o valor do contador
>        UPDATE
>                contador
>        SET
>                valor = valor +1
>        WHERE
>                id = ai_id;
>
>        -- Retorna o valor
>        RETURN li_id;
> END
> $BODY$
> LANGUAGE 'plpgsql' VOLATILE;
>
>
> -- Insere o registro na tabela de contador para que possa ser incrementado
> INSERT INTO contador ( id, descricao, valor ) VALUES( 1, 'Contador Teste', 1 
> );
>
> -- Para recuperar o valor dentro de uma transação...
> -- NOTA: se outra transação estiver executando o mesmo comando, ela irá 
> aguardar
> -- até que a primeira transação realize um ROLLBACK ou COMMIT.
> -- Caso um ROLLBACK seja executado, o valor não é incrementado.
> SELECT uf_add_contador( 1 ) as valor

Sinto muito, mas essa implementação pode ter um efeito colateral. Duas
transações concorrentes podem "catar" o mesmo valor. O SELECT... FOR
UPDATE não impede a leitura do registro.
Pode ocorrer um ROLLBACK automático na transação que chegar por último
na hora fizer o UPDATE da sua função. Se a aplicação não tratar esse
ROLLBACK (e tentar novamente até conseguir sucesso no COMMIT) isso
pode se tornar uma bola de neve e um catastrófico "lock geral" numa
aplicação com muita concorrência.

A implementação de sequências foi inventada exatamente pra isso, sem
dor de cabeça. Não acho legal tentar contornar por fora algo que um
banco de dados sério sabe fazer com maestria.

[]s
Flavio Gurgel
_______________________________________________
pgbr-geral mailing list
pgbr-geral@listas.postgresql.org.br
https://listas.postgresql.org.br/cgi-bin/mailman/listinfo/pgbr-geral

Responder a