Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Gestione delle transazioni
Puoi creare una procedura archiviata con un comportamento predefinito di gestione delle transazioni o un comportamento non atomico.
Gestione delle transazioni con procedura archiviata in modalità predefinita
Il comportamento di commit automatico della modalità di transazione predefinita fa sì che ogni SQL comando eseguito separatamente venga eseguito individualmente. Una chiamata a una procedura memorizzata viene trattata come un singolo SQL comando. Le SQL istruzioni all'interno di una procedura si comportano come se si trovassero in un blocco di transazioni che inizia implicitamente all'inizio della chiamata e termina al termine della chiamata. Una chiamata annidata a un'altra procedura viene trattata come qualsiasi altra SQL istruzione e opera nel contesto della stessa transazione del chiamante. Per ulteriori informazioni sulla funzionalità WLM automatica, consultare Isolamento serializzabile.
Tuttavia, supponiamo di chiamare una procedura memorizzata dall'interno di un blocco di transazione specificato dall'utente (definito da... BEGIN COMMIT). In questo caso tutte le istruzioni nella procedura archiviata vengono eseguite nel contesto della transazione specificata dall'utente. La procedura non conferma implicitamente all'uscita. L'intermediario controlla la conferma della procedura o il ripristino allo stato precedente.
Se si verifica qualsiasi errore durante l'esecuzione di una procedura archiviata, tutte le modifiche apportate nella transazione corrente vengono ripristinate allo stato precedente.
Puoi utilizzare le seguenti istruzioni di controllo delle transazioni in una procedura archiviata:
COMMIT— esegue tutto il lavoro svolto nella transazione corrente e avvia implicitamente una nuova transazione. Per ulteriori informazioni, consulta COMMIT.
ROLLBACK— ripristina il lavoro svolto nella transazione corrente e avvia implicitamente una nuova transazione. Per ulteriori informazioni, consulta ROLLBACK.
TRUNCATEè un'altra dichiarazione che è possibile emettere all'interno di una procedura memorizzata e influenza la gestione delle transazioni. In Amazon Redshift, TRUNCATE emette un commit in modo implicito. Il comportamento rimane lo stesso nel contesto delle procedure archiviate. Quando una TRUNCATE dichiarazione viene emessa dall'interno di una procedura memorizzata, conferma la transazione corrente e ne inizia una nuova. Per ulteriori informazioni, consulta TRUNCATE.
Tutte le istruzioni che seguono un'TRUNCATEistruzione COMMITROLLBACK, o vengono eseguite nel contesto di una nuova transazione. Lo fanno fino a quando non viene rilevata un'TRUNCATEistruzione COMMITROLLBACK, o o o fino alla chiusura della stored procedure.
Quando si utilizza un'TRUNCATEistruzione COMMITROLLBACK, o dall'interno di una stored procedure, si applicano i seguenti vincoli:
Se la stored procedure viene richiamata dall'interno di un blocco di transazione, non può emettere un'istruzioneCOMMIT,ROLLBACK, orTRUNCATE. Questa restrizione si applica all'interno del body della procedura archiviata e all'interno di una chiamata di procedura nidificata.
Se la stored procedure viene creata con
SET config
opzioni, non può emettere un'TRUNCATEistruzione COMMITROLLBACK, o. Questa restrizione si applica all'interno del body della procedura archiviata e all'interno di una chiamata di procedura nidificata.Qualsiasi cursore aperto (esplicitamente o implicitamente) viene chiuso automaticamente quando viene elaborata un'TRUNCATEistruzione COMMITROLLBACK, o. Per i vincoli sui cursori espliciti e impliciti, consultaLimitazioni delle procedure memorizzate.
Inoltre, non è possibile eseguire COMMIT o ROLLBACK utilizzare la dinamica. SQL Tuttavia, puoi eseguire TRUNCATE utilizzando la modalità dinamicaSQL. Per ulteriori informazioni, consulta SQL dinamico.
Quando lavorate con le stored procedure, tenete presente che le END istruzioni BEGIN and in PL/pg SQL servono solo al raggruppamento. Non iniziano o terminano la transazione. Per ulteriori informazioni, consultare Blocco.
L'esempio seguente dimostra il comportamento delle transazioni quando si chiama una procedura archiviata dall'interno di un blocco di transazioni esplicite. Le due istruzioni insert emesse da al di fuori della procedura archiviata e quella dall'interno fanno tutte parte della stessa transazione (3382). La transazione viene confermata quando l'utente emette il commit esplicito.
CREATE OR REPLACE PROCEDURE sp_insert_table_a(a int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO test_table_a values (a); END; $$; Begin; insert into test_table_a values (1); Call sp_insert_table_a(2); insert into test_table_a values (3); Commit; select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-----+---------+---------------------------------------- 103 | 3382 | 599 | UTILITY | Begin; 103 | 3382 | 599 | QUERY | insert into test_table_a values (1); 103 | 3382 | 599 | UTILITY | Call sp_insert_table_a(2); 103 | 3382 | 599 | QUERY | INSERT INTO test_table_a values ( $1 ) 103 | 3382 | 599 | QUERY | insert into test_table_a values (3); 103 | 3382 | 599 | UTILITY | COMMIT
Al contrario, consideriamo un esempio quando le stesse istruzioni vengono emesse dal di fuori di un blocco di transazione esplicito e la sessione ha l'autocommit impostato su ON. In questo caso, ogni istruzione viene eseguita nella propria transazione.
insert into test_table_a values (1); Call sp_insert_table_a(2); insert into test_table_a values (3); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-----+---------+------------------------------------------------------------------------------------------------------------------------------------------------- 103 | 3388 | 599 | QUERY | insert into test_table_a values (1); 103 | 3388 | 599 | UTILITY | COMMIT 103 | 3389 | 599 | UTILITY | Call sp_insert_table_a(2); 103 | 3389 | 599 | QUERY | INSERT INTO test_table_a values ( $1 ) 103 | 3389 | 599 | UTILITY | COMMIT 103 | 3390 | 599 | QUERY | insert into test_table_a values (3); 103 | 3390 | 599 | UTILITY | COMMIT
L'esempio seguente emette un'TRUNCATEistruzione dopo l'inserimento in. test_table_a
L'TRUNCATEistruzione emette un commit implicito che esegue il commit della transazione corrente (3335) e ne avvia una nuova (3336). La nuova transazione viene confermata all'uscita della procedura.
CREATE OR REPLACE PROCEDURE sp_truncate_proc(a int, b int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO test_table_a values (a); TRUNCATE test_table_b; INSERT INTO test_table_b values (b); END; $$; Call sp_truncate_proc(1,2); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-------+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 103 | 3335 | 23636 | UTILITY | Call sp_truncate_proc(1,2); 103 | 3335 | 23636 | QUERY | INSERT INTO test_table_a values ( $1 ) 103 | 3335 | 23636 | UTILITY | TRUNCATE test_table_b 103 | 3335 | 23636 | UTILITY | COMMIT 103 | 3336 | 23636 | QUERY | INSERT INTO test_table_b values ( $1 ) 103 | 3336 | 23636 | UTILITY | COMMIT
L'esempio seguente genera un TRUNCATE da una chiamata annidata. Il TRUNCATE commette tutto il lavoro svolto finora nelle procedure esterne e interne di una transazione (3344). Avvia una nuova transazione (3345). La nuova transazione viene confermata all'uscita della procedura esterna.
CREATE OR REPLACE PROCEDURE sp_inner(c int, d int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO inner_table values (c); TRUNCATE outer_table; INSERT INTO inner_table values (d); END; $$; CREATE OR REPLACE PROCEDURE sp_outer(a int, b int, c int, d int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO outer_table values (a); Call sp_inner(c, d); INSERT INTO outer_table values (b); END; $$; Call sp_outer(1, 2, 3, 4); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 103 | 3344 | 23636 | UTILITY | Call sp_outer(1, 2, 3, 4); 103 | 3344 | 23636 | QUERY | INSERT INTO outer_table values ( $1 ) 103 | 3344 | 23636 | UTILITY | CALL sp_inner( $1 , $2 ) 103 | 3344 | 23636 | QUERY | INSERT INTO inner_table values ( $1 ) 103 | 3344 | 23636 | UTILITY | TRUNCATE outer_table 103 | 3344 | 23636 | UTILITY | COMMIT 103 | 3345 | 23636 | QUERY | INSERT INTO inner_table values ( $1 ) 103 | 3345 | 23636 | QUERY | INSERT INTO outer_table values ( $1 ) 103 | 3345 | 23636 | UTILITY | COMMIT
L'esempio seguente mostra che il cursore cur1
era chiuso al momento del commit dell'TRUNCATEistruzione.
CREATE OR REPLACE PROCEDURE sp_open_cursor_truncate() LANGUAGE plpgsql AS $$ DECLARE rec RECORD; cur1 cursor for select * from test_table_a order by 1; BEGIN open cur1; TRUNCATE table test_table_b; Loop fetch cur1 into rec; raise info '%', rec.c1; exit when not found; End Loop; END $$; call sp_open_cursor_truncate(); ERROR: cursor "cur1" does not exist CONTEXT: PL/pgSQL function "sp_open_cursor_truncate" line 8 at fetch
L'esempio seguente emette un'TRUNCATEistruzione e non può essere richiamato dall'interno di un blocco di transazione esplicito.
CREATE OR REPLACE PROCEDURE sp_truncate_atomic() LANGUAGE plpgsql AS $$ BEGIN TRUNCATE test_table_b; END; $$; Begin; Call sp_truncate_atomic(); ERROR: TRUNCATE cannot be invoked from a procedure that is executing in an atomic context. HINT: Try calling the procedure as a top-level call i.e. not from within an explicit transaction block. Or, if this procedure (or one of its ancestors in the call chain) was created with SET config options, recreate the procedure without them. CONTEXT: SQL statement "TRUNCATE test_table_b" PL/pgSQL function "sp_truncate_atomic" line 2 at SQL statement
L'esempio seguente mostra che un utente che non è un superutente o il proprietario di una tabella può emettere un'TRUNCATEistruzione sulla tabella. L'utente lo fa usando una procedura archiviata Security Definer
. Nell'esempio vengono illustrate le seguenti operazioni:
user1 crea la tabella
test_tbl
.user1 crea la procedura archiviata
sp_truncate_test_tbl
.user1 concede il privilegio
EXECUTE
sulla procedura archiviata a user2.user2 esegue la procedura archiviata per troncare la tabella
test_tbl
. L'esempio mostra il conteggio delle righe prima e dopo il comandoTRUNCATE
.
set session_authorization to user1; create table test_tbl(id int, name varchar(20)); insert into test_tbl values (1,'john'), (2, 'mary'); CREATE OR REPLACE PROCEDURE sp_truncate_test_tbl() LANGUAGE plpgsql AS $$ DECLARE tbl_rows int; BEGIN select count(*) into tbl_rows from test_tbl; RAISE INFO 'RowCount before Truncate: %', tbl_rows; TRUNCATE test_tbl; select count(*) into tbl_rows from test_tbl; RAISE INFO 'RowCount after Truncate: %', tbl_rows; END; $$ SECURITY DEFINER; grant execute on procedure sp_truncate_test_tbl() to user2; reset session_authorization; set session_authorization to user2; call sp_truncate_test_tbl(); INFO: RowCount before Truncate: 2 INFO: RowCount after Truncate: 0 CALL reset session_authorization;
L'esempio seguente viene emesso COMMIT due volte. Il primo esegue tutto COMMIT il lavoro svolto nella transazione 10363 e avvia implicitamente la transazione 10364. La transazione 10364 viene confermata dalla seconda dichiarazione. COMMIT
CREATE OR REPLACE PROCEDURE sp_commit(a int, b int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO test_table values (a); COMMIT; INSERT INTO test_table values (b); COMMIT; END; $$; call sp_commit(1,2); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+-------+------+---------+----------------------------------------------------------------------------------------------------------------- 100 | 10363 | 3089 | UTILITY | call sp_commit(1,2); 100 | 10363 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10363 | 3089 | UTILITY | COMMIT 100 | 10364 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10364 | 3089 | UTILITY | COMMIT
L'esempio seguente emette un'ROLLBACKistruzione se sum_vals
è maggiore di 2. La prima ROLLBACK istruzione ripristina tutto il lavoro svolto nella transazione 10377 e avvia una nuova transazione 10378. La transazione 10378 viene confermata all'uscita della procedura.
CREATE OR REPLACE PROCEDURE sp_rollback(a int, b int) LANGUAGE plpgsql AS $$ DECLARE sum_vals int; BEGIN INSERT INTO test_table values (a); SELECT sum(c1) into sum_vals from test_table; IF sum_vals > 2 THEN ROLLBACK; END IF; INSERT INTO test_table values (b); END; $$; call sp_rollback(1, 2); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+-------+------+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 100 | 10377 | 3089 | UTILITY | call sp_rollback(1, 2); 100 | 10377 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10377 | 3089 | QUERY | SELECT sum(c1) from test_table 100 | 10377 | 3089 | QUERY | Undoing 1 transactions on table 133646 with current xid 10377 : 10377 100 | 10378 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10378 | 3089 | UTILITY | COMMIT
Gestione delle transazioni con procedura archiviata in modalità NONATOMIC
Una procedura memorizzata creata in NONATOMIC modalità ha un comportamento di controllo delle transazioni diverso da una procedura creata in modalità predefinita. Analogamente al comportamento di commit automatico dei SQL comandi esterni alle stored procedure, ogni SQL istruzione all'interno di una NONATOMIC procedura viene eseguita nella propria transazione e viene eseguita automaticamente il commit. Se un utente avvia un blocco di transazione esplicito all'interno di una NONATOMIC stored procedure, SQL le istruzioni all'interno del blocco non vengono eseguite automaticamente. Il blocco delle transazioni controlla il commit o il rollback delle istruzioni al suo interno.
Nelle NONATOMIC stored procedure, è possibile aprire un blocco di transazione esplicito all'interno della procedura utilizzando l'STARTTRANSACTIONistruzione. Tuttavia, se esiste già un blocco di transazione aperto, questa istruzione non servirà a nulla perché Amazon Redshift non supporta le transazioni secondarie. La transazione precedente continua.
Quando lavorate con i FOR loop di cursore all'interno di una NONATOMIC procedura, assicuratevi di aprire un blocco di transazione esplicito prima di scorrere i risultati di una query. Altrimenti, il cursore viene chiuso quando l'SQListruzione all'interno del ciclo viene confermata automaticamente.
Alcune delle considerazioni relative all'utilizzo del comportamento NONATOMIC in modalità sono le seguenti:
Ogni SQL istruzione all'interno della stored procedure viene confermata automaticamente se non è presente alcun blocco di transazione aperto e la sessione ha l'autocommit impostato su ON.
È possibile emettere un'TRUNCATEistruzioneCOMMIT/ROLLBACK/per terminare la transazione se la stored procedure viene richiamata dall'interno di un blocco di transazioni. Questo non è possibile nella modalità predefinita.
È possibile emettere un'STARTTRANSACTIONistruzione per iniziare un blocco di transazione all'interno della stored procedure.
Gli esempi seguenti illustrano il comportamento delle transazioni quando si lavora con le NONATOMIC stored procedure. La sessione per tutti i seguenti esempi ha il commit automatico impostato su ON.
Nell'esempio seguente, una NONATOMIC stored procedure ha due INSERT istruzioni. Quando la procedura viene chiamata all'esterno di un blocco di transazione, ogni INSERT istruzione all'interno della procedura viene eseguita automaticamente.
CREATE TABLE test_table_a(v int); CREATE TABLE test_table_b(v int); CREATE OR REPLACE PROCEDURE sp_nonatomic_insert_table_a(a int, b int) NONATOMIC AS $$ BEGIN INSERT INTO test_table_a values (a); INSERT INTO test_table_b values (b); END; $$ LANGUAGE plpgsql; Call sp_nonatomic_insert_table_a(1,2); Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+---------------------------------------- 1 | 1792 | 1073807554 | UTILITY | Call sp_nonatomic_insert_table_a(1,2); 1 | 1792 | 1073807554 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1792 | 1073807554 | UTILITY | COMMIT 1 | 1793 | 1073807554 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1793 | 1073807554 | UTILITY | COMMIT (5 rows)
Tuttavia, quando la procedura viene richiamata dall'interno di un.. BEGIN COMMITblock, tutte le istruzioni fanno parte della stessa transazione (xid=1799).
Begin; INSERT INTO test_table_a values (10); Call sp_nonatomic_insert_table_a(20,30); INSERT INTO test_table_b values (40); Commit; Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+------------------------------------------ 1 | 1799 | 1073914035 | UTILITY | Begin; 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_a values (10); 1 | 1799 | 1073914035 | UTILITY | Call sp_nonatomic_insert_table_a(20,30); 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_b values (40); 1 | 1799 | 1073914035 | UTILITY | COMMIT (7 rows)
In questo esempio, due istruzioni sono comprese traINSERT... START TRANSACTION COMMIT. Quando la procedura viene chiamata all'esterno di un blocco di transazioni, le due INSERT istruzioni si trovano nella stessa transazione (xid=1866).
CREATE OR REPLACE PROCEDURE sp_nonatomic_txn_block(a int, b int) NONATOMIC AS $$ BEGIN START TRANSACTION; INSERT INTO test_table_a values (a); INSERT INTO test_table_b values (b); COMMIT; END; $$ LANGUAGE plpgsql; Call sp_nonatomic_txn_block(1,2); Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+---------------------------------------- 1 | 1865 | 1073823998 | UTILITY | Call sp_nonatomic_txn_block(1,2); 1 | 1866 | 1073823998 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1866 | 1073823998 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1866 | 1073823998 | UTILITY | COMMIT (4 rows)
Quando la procedura viene chiamata dall'interno di un... BEGIN COMMITblock, all'STARTTRANSACTIONinterno della procedura non fa nulla perché c'è già una transazione aperta. L'COMMITinterno della procedura esegue il commit della transazione corrente (xid=1876) e ne avvia una nuova.
Begin; INSERT INTO test_table_a values (10); Call sp_nonatomic_txn_block(20,30); INSERT INTO test_table_b values (40); Commit; Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+---------------------------------------- 1 | 1876 | 1073832133 | UTILITY | Begin; 1 | 1876 | 1073832133 | QUERY | INSERT INTO test_table_a values (10); 1 | 1876 | 1073832133 | UTILITY | Call sp_nonatomic_txn_block(20,30); 1 | 1876 | 1073832133 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1876 | 1073832133 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1876 | 1073832133 | UTILITY | COMMIT 1 | 1878 | 1073832133 | QUERY | INSERT INTO test_table_b values (40); 1 | 1878 | 1073832133 | UTILITY | COMMIT (8 rows)
Questo esempio illustra come utilizzare i loop del cursore. La tabella test_table_a ha tre valori. L'obiettivo è effettuare l'iterazione dei tre valori e inserirli nella tabella test_table_b. Se una NONATOMIC stored procedure viene creata nel modo seguente, genererà il cursore di errore «cur1" does not exist dopo l'esecuzione dell'istruzione nel primo ciclo. INSERT Questo perché il commit automatico di INSERT chiude il cursore aperto.
insert into test_table_a values (1), (2), (3); CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC LANGUAGE plpgsql AS $$ DECLARE rec RECORD; cur1 cursor for select * from test_table_a order by 1; BEGIN open cur1; Loop fetch cur1 into rec; exit when not found; raise info '%', rec.v; insert into test_table_b values (rec.v); End Loop; END $$; CALL sp_nonatomic_cursor(); INFO: 1 ERROR: cursor "cur1" does not exist CONTEXT: PL/pgSQL function "sp_nonatomic_cursor" line 7 at fetch
Per far funzionare il ciclo del cursore, mettilo tra... START TRANSACTION COMMIT.
insert into test_table_a values (1), (2), (3); CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC LANGUAGE plpgsql AS $$ DECLARE rec RECORD; cur1 cursor for select * from test_table_a order by 1; BEGIN START TRANSACTION; open cur1; Loop fetch cur1 into rec; exit when not found; raise info '%', rec.v; insert into test_table_b values (rec.v); End Loop; COMMIT; END $$; CALL sp_nonatomic_cursor(); INFO: 1 INFO: 2 INFO: 3 CALL