Administração de Banco de Dados Adriano J. Holanda http://holanda.xyz 4/9/05 Transação O modelo de transação do PostgreSQL considera que as operações ocorrem entre e e marcação de tempo para a transação começa após. Como exemplo, executamos a função now() em tempos diferentes e o tempo retornado durante a transação é o valor de que ela iniciou: postgres=# SELECT now(); now - 05-09-04 3:9:06.46063-03 postgres=# SELECT now(); now - 05-09-04 3:9:06.46063-03 Outro aspecto importante é que qualquer erro cancela a transação, e por padrão, as operações já executadas após : postgres=# SELECT ; postgres=# SELECT /0; ERROR: division by zero postgres=# SELECT ; ERROR: current transaction is aborted, commands ignored until end of transaction block Após a divisão por zero, a transação é abortada e fica a espera do.
Savepoints Um forma de se recuperar de erros, e voltar em algum ponto da transação é através do uso do comando SAVEPOINT: postgres=# SELECT ; postgres=# SAVEPOINT s; SAVEPOINT postgres=# SELECT /0; ERROR: division by zero postgres=# TO SAVEPOINT s; postgres=# SELECT ; O comando TO SAVEPOINT s retorna a transação para o ponto s, quando ainda não havia ocorrido o erro de divisão por zero. Bloqueio e deadlocks No PostgreSQL ocorre bloqueio durante as transações, para que haja consistência nas operações concorrentes. Por exemplo ao criarmos um tabela com um único campo id como mostrado a seguir: CREATE TABLE bar AS SELECT as id; E duas operações tentarem modificar o valor de id dentro da transação, como no exemplo mostrado a seguir. A segunda operação, que ocorre depois da primeira, só conseguirá atualizar o valor do campo id após a primeira transação tiver encerrado. postgres=# UPDATE bar SET id=id+; UPDATE postgres=# UPDATE bar SET id=id+; UPDATE Para verificar como pode ocorrer deadlock, vamos criar uma tabela chamada custo e inserir dois valores para os ids e :
postgres=# CREATE TABLE custo(id INTEGER, valor INTEGER); CREATE TABLE postgres=# INSERT INTO custo VALUES (, 300); INSERT 0 postgres=# INSERT INTO custo VALUES (, 500); INSERT 0 Vamos realizar duas transações, na transação A vamos transferir 00 unidades do id para o, e na transação B transferir 50 unidades do id para o id. Como as operações são realizadas concorrentemente, haverá deadlock quando a transação B tentar bloquear que já está bloqueado pela transação A, e o sistema notar que a transação A está esperando o desbloqueio de, que está bloqueado pela transação B. -- transação A postgres=# UPDATE custo SET valor=valor-00 WHERE id = ; UPDATE postgres=# UPDATE custo SET valor=valor+00 WHERE id = ; UPDATE -- transação B postgres=# UPDATE custo SET valor=valor-50 WHERE id = ; UPDATE postgres=# UPDATE custo SET valor=valor+50 WHERE id = ; ERROR: deadlock detected DETAIL: Process 6633 waits for ShareLock on transaction Process 7735 waits for ShareLock on transaction 96; b HINT: See server log for query details. O deadlock pode ser evitado pelo uso de bloqueio da tabela com o comando LOCK que apresenta várias possibilidades para o controle do bloqueio. O comando a seguir mostra as possibilidades de bloqueio: postgres=# \h LOCK Command: LOCK Description: lock a table Syntax: LOCK [ TABLE ] [ ONLY ] name [ * ] [,...] [ IN lockmode MODE ] [ NOWAIT ] where lockmode is one of: ACCESS SHARE ROW SHARE ROW EXCLUSIVE SHARE UPDATE EXCLUSIVE SHARE SHARE ROW EXCLUSIVE EXCLUSIVE ACCESS EXCLUSIVE Cursor O uso do cursor é uma forma de evitar que grandes quantidades de dados vão para o cliente que fez a consulta de um só vez. Como exemplo vamos criar uma tabela com 6 entradas postgres=# CREATE TABLE t_teste AS SELECT * FROM generate_series(, 6) AS x; SELECT 6 e criar um cursor para percorrer as entradas da tabela dentro de um transação que inicia em postgres=# DECLARE cur_teste CURSOR FOR SELECT * FROM t_teste; DECLARE CURSOR 3
e acessar o resultado da colsulta usando o cursor postgres=# FETCH NEXT FROM cur_teste; x --- Para acessar um número de maior de entradas inserimos o número após FETCH e removemos NEXT: postgres=# FETCH 8 FROM cur_teste; x --- 3 4 5 6 7 8 9 (8 rows) Para encerrar a busca, fechamos a transação postgres=# END; Após o encerramento da transação, o cursor não existe mais: postgres=# FETCH NEXT FROM cur_teste; ERROR: cursor "cur_teste" does not exist Sequência Uma sequência no banco de dados PostgreSQL é criada pelo comando CREATE SEQUENCE: postgres=# CREATE SEQUENCE seq_teste; CREATE SEQUENCE As sequências são acessadas pelas funções: ('nome'): retorna o próximo número disponível na sequência e atualiza o contador; currval('nome'): retorna o valor atual do contador; setval('name', valor): atribui valor para o contador. A seguir, são mostradas os usos das funções que manupulam as sequências: 4
postgres=# SELECT currval('seq_teste'); currval postgres=# SELECT setval('seq_teste',8); setval -------- 8 9 5