Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Gestion des transactions
Vous pouvez créer une procédure stockée avec un comportement de gestion des transactions par défaut ou un comportement non atomique.
Mode par défaut de gestion des transactions des procédures stockées
Le comportement de validation automatique par défaut du mode transaction entraîne la validation individuelle de chaque SQL commande exécutée séparément. Un appel à une procédure stockée est traité comme une SQL commande unique. Les SQL instructions d'une procédure se comportent comme si elles se trouvaient dans un bloc de transactions qui commence implicitement au début de l'appel et se termine à la fin de l'appel. Un appel imbriqué vers une autre procédure est traité comme n'importe quelle autre SQL instruction et fonctionne dans le contexte de la même transaction que l'appelant. Pour plus d’informations sur le comportement automatique de validation, consultez Isolement sérialisable.
Supposons toutefois que vous appeliez une procédure stockée depuis un bloc de transactions défini par l'utilisateur (défini parBEGIN... COMMIT). Dans ce cas, toutes les instructions de la procédure stockée s’exécutent dans le contexte de la transaction spécifiée par l’utilisateur. La procédure n’est pas validée implicitement à la sortie. L’appelant contrôle la validation ou l’annulation de la procédure.
En cas d’erreur lors de l’exécution d’une procédure stockée, toutes les modifications apportées lors de la transaction en cours sont annulées.
Vous pouvez utiliser les instructions de contrôle de transaction suivantes dans une procédure stockée :
COMMIT— valide tout le travail effectué dans le cadre de la transaction en cours et lance implicitement une nouvelle transaction. Pour de plus amples informations, veuillez consulter COMMIT.
ROLLBACK— annule le travail effectué dans la transaction en cours et lance implicitement une nouvelle transaction. Pour de plus amples informations, veuillez consulter ROLLBACK.
TRUNCATEest une autre instruction que vous pouvez émettre à partir d'une procédure stockée et qui influence la gestion des transactions. Dans Amazon Redshift, TRUNCATE émet un commit implicitement. Ce comportement demeure le même dans le contexte des procédures stockées. Lorsqu'une TRUNCATE instruction est émise à partir d'une procédure stockée, elle valide la transaction en cours et en commence une nouvelle. Pour de plus amples informations, veuillez consulter TRUNCATE.
Toutes les instructions qui suivent une TRUNCATE instruction COMMITROLLBACK, ou sont exécutées dans le contexte d'une nouvelle transaction. Ils le font jusqu'à ce qu'une TRUNCATE instruction COMMITROLLBACK, ou soit détectée ou que la procédure stockée se termine.
Lorsque vous utilisez une TRUNCATE instruction COMMITROLLBACK, ou issue d'une procédure stockée, les contraintes suivantes s'appliquent :
Si la procédure stockée est appelée depuis un bloc de transactions, elle ne peut pas émettre d'TRUNCATEinstruction COMMITROLLBACK, ou. Cette restriction s’applique dans le corps de la procédure stockée elle-même et dans tout appel de procédure imbriquée.
Si la procédure stockée est créée avec des
SET config
options, elle ne peut pas émettre d'TRUNCATEinstruction COMMITROLLBACK, ou. Cette restriction s’applique dans le corps de la procédure stockée elle-même et dans tout appel de procédure imbriquée.Tout curseur ouvert (explicitement ou implicitement) se ferme automatiquement lorsqu'une TRUNCATE instruction COMMITROLLBACK, ou est traitée. Pour les contraintes sur les curseurs explicites et implicites, consultez Limites des procédures stockées.
De plus, vous ne pouvez pas exécuter COMMIT ou ROLLBACK utiliser DynamicSQL. Cependant, vous pouvez exécuter TRUNCATE en mode dynamiqueSQL. Pour de plus amples informations, veuillez consulter Instructions SQL dynamiques.
Lorsque vous travaillez avec des procédures stockées, considérez que les END instructions BEGIN and de PL/pg SQL sont uniquement destinées au regroupement. Ils ne démarrent pas et ne finissent pas une transaction. Pour de plus amples informations, veuillez consulter Bloc.
L’exemple suivant illustre le comportement d’une transaction lors de l’appel d’une procédure stockée depuis un bloc de transaction explicite. Les deux instructions d’insertion émises depuis l’extérieur de la procédure stockée et celle émise depuis l’intérieur font toutes deux partie de la même transaction (3382). La transaction est validée quand l’utilisateur émet la validation explicite.
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
À l’inverse, prenons un exemple où les mêmes instructions sont émises depuis l’extérieur d’un bloc de transactions explicite et où le paramètre de validation automatique de la session est activé. Dans ce cas, chaque instruction s’exécute dans sa propre transaction.
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'exemple suivant émet une TRUNCATE instruction après l'avoir test_table_a
insérée dans. L'TRUNCATEinstruction émet un commit implicite qui valide la transaction en cours (3335) et en lance une nouvelle (3336). La nouvelle transaction est validée quand la procédure cesse.
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'exemple suivant émet un appel TRUNCATE à partir d'un appel imbriqué. Les TRUNCATE validations sont l'ensemble du travail effectué jusqu'à présent dans les procédures externes et internes d'une transaction (3344). Elle démarre une nouvelle transaction (3345). La nouvelle transaction est validée quand la procédure externe cesse.
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'exemple suivant montre que le curseur cur1
était fermé lors de la validation de l'TRUNCATEinstruction.
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'exemple suivant émet une TRUNCATE instruction et ne peut pas être appelé depuis un bloc de transaction explicite.
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'exemple suivant montre qu'un utilisateur qui n'est pas un superutilisateur ou le propriétaire d'une table peut émettre une TRUNCATE instruction sur la table. L’utilisateur effectue cette opération à l’aide d’une procédure stockée Security Definer
. L’exemple illustre les actions suivantes :
L’utilisateur1 crée la table
test_tbl
.L’utilisateur1 crée une procédure stockée
sp_truncate_test_tbl
.L’utilisateur1 accorde un privilège
EXECUTE
au niveau de la procédure stockée à l’utilisateur2.L’utilisateur2 exécute la procédure stockée pour tronquer la table
test_tbl
. L’exemple montre le nombre de lignes avant et après la commandeTRUNCATE
.
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'exemple suivant émet COMMIT deux fois. Le premier COMMIT valide tout le travail effectué dans la transaction 10363 et lance implicitement la transaction 10364. La transaction 10364 est validée par la deuxième COMMIT instruction.
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'exemple suivant émet une ROLLBACK instruction si la sum_vals
valeur est supérieure à 2. Le premier ROLLBACK relevé annule tout le travail effectué dans le cadre de la transaction 10377 et lance une nouvelle transaction 10378. La transaction 10378 est validée quand la procédure se termine.
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
Gestion des transactions des procédures stockées en mode non atomique
Une procédure stockée créée en NONATOMIC mode possède un comportement de contrôle des transactions différent de celui d'une procédure créée en mode par défaut. À l'instar du comportement de validation automatique des SQL commandes en dehors des procédures stockées, chaque SQL instruction d'une NONATOMIC procédure s'exécute dans sa propre transaction et est validée automatiquement. Si un utilisateur lance un bloc de transaction explicite dans une procédure NONATOMIC stockée, les SQL instructions contenues dans le bloc ne sont pas automatiquement validées. Le bloc de transaction contrôle la validation ou la restauration des instructions qu’il contient.
Dans les procédures NONATOMIC stockées, vous pouvez ouvrir un bloc de transaction explicite à l'intérieur de la procédure à l'aide de l'STARTTRANSACTIONinstruction. Cependant, s’il existe déjà un bloc de transaction ouvert, cette déclaration ne fera rien car Amazon Redshift ne prend pas en charge les sous transactions. La transaction précédente se poursuit.
Lorsque vous utilisez des FOR boucles de curseur dans une NONATOMIC procédure, assurez-vous d'ouvrir un bloc de transaction explicite avant de parcourir les résultats d'une requête. Dans le cas contraire, le curseur est fermé lorsque l'SQLinstruction contenue dans la boucle est automatiquement validée.
Voici certaines des considérations à prendre en compte lors de l'utilisation du comportement du NONATOMIC mode :
Chaque SQL instruction contenue dans la procédure stockée est automatiquement validée s'il n'y a aucun bloc de transaction ouvert, et la validation automatique de la session est définie sur ON.
Vous pouvez émettre une TRUNCATE instructionCOMMIT/ROLLBACK/pour mettre fin à la transaction si la procédure stockée est appelée depuis un bloc de transactions. Cela n’est pas possible en mode par défaut.
Vous pouvez émettre une START TRANSACTION instruction pour lancer un bloc de transactions dans la procédure stockée.
Les exemples suivants illustrent le comportement des transactions lors de l'utilisation de procédures NONATOMIC stockées. Pour tous les exemples suivants, la validation automatique est définie sur ON.
Dans l'exemple suivant, une procédure NONATOMIC stockée comporte deux INSERT instructions. Lorsque la procédure est appelée en dehors d'un bloc de transactions, chaque INSERT instruction de la procédure est automatiquement validée.
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)
Cependant, lorsque la procédure est appelée depuis unBEGIN.. COMMITbloc, toutes les instructions font partie de la même transaction (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)
Dans cet exemple, deux INSERT instructions se situent entre STARTTRANSACTION... COMMIT. Lorsque la procédure est appelée en dehors d'un bloc de transactions, les deux INSERT instructions font partie de la même transaction (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)
Lorsque la procédure est appelée depuis unBEGIN... COMMITbloquer, l'STARTTRANSACTIONintérieur de la procédure ne fait rien car il y a déjà une transaction ouverte. L'COMMITintérieur de la procédure valide la transaction en cours (xid=1876) et en lance une nouvelle.
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)
Cet exemple montre comment travailler avec des boucles de curseur. La table test_table_a possède trois valeurs. L’objectif est de parcourir les trois valeurs et de les insérer dans la table test_table_b. Si une procédure NONATOMIC stockée est créée de la manière suivante, elle lancera le curseur d'erreur « cur1 » n'existe pas après avoir exécuté l'INSERTinstruction dans la première boucle. Cela est dû au fait que la validation automatique du INSERT ferme le curseur ouvert.
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
Pour que la boucle du curseur fonctionne, placez-la entre STARTTRANSACTION... 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