

 Amazon Redshift ne prendra plus en charge la création de nouveaux Python à UDFs partir du patch 198. UDFs Le Python existant continuera de fonctionner jusqu'au 30 juin 2026. Pour plus d’informations, consultez le [ billet de blog ](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/). 

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.

# Présentation des procédures stockées dans Amazon Redshift
<a name="stored-procedure-create"></a>

Cette rubrique décrit les détails relatifs à l’objectif et à l’utilisation des procédures stockées.

Les procédures stockées permettent généralement d’encapsuler la logique de transformation et de validation des données, et la logique propre à l’activité. En associant plusieurs étapes SQL dans une même procédure stockée, vous pouvez réduire les allers-retours entre vos applications et la base de données.

Pour un contrôle d’accès détaillé, vous pouvez créer des procédures stockées qui permettent d’exécuter des fonctions sans offrir à l’utilisateur un accès aux tables sous-jacentes. Par exemple, seul le propriétaire ou un superutilisateur peut tronquer une table, et un utilisateur a besoin de privilèges d’écriture pour insérer des données dans une table. Au lieu d’accorder à un utilisateur des privilèges sur les tables sous-jacentes, vous pouvez créer une procédure stockée qui exécute la tâche. Vous accordez ensuite à l’utilisateur les privilèges nécessaires à l’exécution de la procédure stockée. 

Une procédure stockée dotée de l’attribut de sécurité DEFINER s’exécute avec les privilèges du propriétaire de la procédure stockée. Par défaut, une procédure stockée dispose d’une sécurité INVOKER, ce qui signifie que la procédure utilise les privilèges de l’utilisateur qui l’appelle. 

Pour créer une procédure stockée, utilisez la commande [CREATE PROCEDURE](r_CREATE_PROCEDURE.md). Pour exécuter une procédure, utilisez la commande [CALL](r_CALL_procedure.md). Vous trouverez des exemples plus loin dans la présente section.

**Note**  
Certains clients peuvent rencontrer l’erreur suivante lors de la création d’une procédure stockée Amazon Redshift.  

```
ERROR: 42601: [Amazon](500310) unterminated dollar-quoted string at or near "$$
```
Cette erreur est due à l’incapacité du client à analyser correctement l’instruction CREATE PROCEDURE avec des points-virgules délimitant les instructions et le signe dollar (\$1) entre guillemets. Il en résulte qu’une partie seulement de l’instruction est envoyée au serveur Amazon Redshift. Généralement, vous pouvez contourner cette erreur à l’aide de l’option `Run as batch` ou `Execute selected` du client.   
Par exemple, avec un client Aginity, utilisez l’option `Run entire script as batch`. Lorsque vous utilisez la Workbench/J, we recommend version 124. When you use SQL Workbench/J version 125 de SQL, pensez à spécifier un autre délimiteur pour contourner le problème.   
CREATE PROCEDURE contient des instructions SQL délimitées par un point-virgule (;). La définition d’un autre délimiteur tel que la barre oblique (/) et son positionnement à la fin de l’instruction CREATE PROCEDURE permet que l’instruction soit envoyée au serveur Amazon Redshift pour traitement. Voici un exemple.  

```
CREATE OR REPLACE PROCEDURE test()
AS $$
BEGIN
  SELECT 1 a;
END;
$$
LANGUAGE plpgsql
;
/
```
Vous pouvez utiliser un client prenant en charge l'analyse des instructions CREATE PROCEDURE, tel que l'[éditeur de requêtes de la console Amazon Redshift](https://docs.aws.amazon.com/redshift/latest/mgmt/query-editor.html) ou. TablePlus 

**Topics**
+ [

# Dénomination des procédures stockées
](stored-procedure-naming.md)
+ [

# Sécurité et privilèges des procédures stockées
](stored-procedure-security-and-privileges.md)
+ [

# Retour d’un ensemble de résultats depuis une procédure stockée
](stored-procedure-result-set.md)
+ [

# Gestion des transactions
](stored-procedure-transaction-management.md)
+ [

# Interception des erreurs
](stored-procedure-trapping-errors.md)
+ [

# Enregistrement des procédures stockées
](c_PLpgSQL-logging.md)
+ [

# Limitations des procédures stockées
](stored-procedure-constraints.md)

L’exemple suivant illustre une procédure sans arguments en sortie. Par défaut, les arguments sont des arguments en entrée (IN).

```
CREATE OR REPLACE PROCEDURE test_sp1(f1 int, f2 varchar)
AS $$
BEGIN
  RAISE INFO 'f1 = %, f2 = %', f1, f2;
END;
$$ LANGUAGE plpgsql;

call test_sp1(5, 'abc');
INFO: f1 = 5, f2 = abc
CALL
```



**Note**  
 Lorsque vous écrivez des procédures stockées, nous vous recommandons une bonne pratique pour sécuriser les valeurs sensibles :   
Ne codez pas en dur des informations sensibles dans la logique des procédures stockées. Par exemple, n’attribuez pas de mot de passe utilisateur dans une instruction CREATE USER dans le corps d’une procédure stockée. Cela présente un risque pour la sécurité, car les valeurs codées en dur peuvent être enregistrées en tant que métadonnées de schéma dans les tables du catalogue. Transmettez plutôt des valeurs sensibles, telles que des mots de passe, en tant qu’arguments à la procédure stockée, au moyen de paramètres.   
Pour plus d’informations sur les procédures stockées, consultez [CREATE PROCEDURE](r_CREATE_PROCEDURE.md) et [Création de procédures stockées dans Amazon Redshift](stored-procedure-overview.md). Pour plus d’informations sur les tables de catalogue, consultez [Tables catalogue système](c_intro_catalog_views.md).

L’exemple suivant illustre une procédure avec des arguments en sortie. Les arguments sont en entrée (IN), en entrée et en sortie (INOUT), et en sortie (OUT).

```
CREATE OR REPLACE PROCEDURE test_sp2(f1 IN int, f2 INOUT varchar(256), out_var OUT varchar(256))
AS $$
DECLARE
  loop_var int;
BEGIN
  IF f1 is null OR f2 is null THEN
    RAISE EXCEPTION 'input cannot be null';
  END IF;
  DROP TABLE if exists my_etl;
  CREATE TEMP TABLE my_etl(a int, b varchar);
    FOR loop_var IN 1..f1 LOOP
        insert into my_etl values (loop_var, f2);
        f2 := f2 || '+' || f2;
    END LOOP;
  SELECT INTO out_var count(*) from my_etl;
END;
$$ LANGUAGE plpgsql;


call test_sp2(2,'2019');

         f2          | column2
---------------------+---------
 2019+2019+2019+2019 | 2
(1 row)
```

# Dénomination des procédures stockées
<a name="stored-procedure-naming"></a>

Cette rubrique décrit les détails relatifs aux noms de procédures stockées.

Si vous définissez une procédure avec le même nom, mais des types de données différents pour les arguments en entrée, vous créez une nouvelle procédure. Par conséquent, le nom de la procédure est surchargé. Pour plus d’informations, consultez [Surcharge des noms de procédure](#stored-procedure-overloading-name). Amazon Redshift ne permet pas de surcharger les procédures en fonction des arguments de sortie. Vous ne pouvez pas avoir deux procédures ayant le même nom et les mêmes types de données pour les arguments en entrée, mais des types différents pour les arguments en sortie.

Le propriétaire ou un super-utilisateur peut remplacer le corps d’une procédure stockée par une nouvelle procédure ayant la même signature. Pour modifier la signature ou les types de retour d’une procédure stockée, supprimez la procédure stockée et recréez-la. Pour plus d’informations, consultez [DROP PROCEDURE](r_DROP_PROCEDURE.md) et [CREATE PROCEDURE](r_CREATE_PROCEDURE.md).

Vous pouvez éviter les conflits potentiels et les résultats inattendus en prenant en considération vos conventions de dénomination des procédures stockées avant de les mettre en œuvre. Comme vous pouvez surcharger les noms de procédure, ceux-ci peuvent entrer en conflit avec des noms de procédure Amazon Redshift existants et futurs.

## Surcharge des noms de procédure
<a name="stored-procedure-overloading-name"></a>

Une procédure est identifiée par son nom et sa signature, à savoir le nombre d’arguments en entrée et leurs types de données. Deux procédures d’un même schéma peuvent porter le même nom si elles ont des signatures différentes. Autrement dit, vous pouvez surcharger les noms de procédure.

Lorsque vous exécutez une procédure, le moteur de requête détermine quelle procédure appeler en fonction du nombre d’arguments que vous fournissez et de leurs types de données. Vous pouvez utiliser une surcharge pour simuler des procédures ayant un nombre variable d’arguments, jusqu’à la limite autorisée par la commande CREATE PROCEDURE. Pour plus d’informations, consultez [CREATE PROCEDURE](r_CREATE_PROCEDURE.md).

## Prévention des conflits de dénomination
<a name="stored-procedure-name-conflicts"></a>

Nous vous recommandons de nommer toutes les procédures en utilisant le préfixe `sp_`. Amazon Redshift réserve le préfixe `sp_` exclusivement aux procédures stockées. En préfixant vos noms de procédure avec `sp_`, vous garantissez que le nom de votre procédure n’entrera pas en conflit avec le nom de procédure Amazon Redshift existant ou futur. 

# Sécurité et privilèges des procédures stockées
<a name="stored-procedure-security-and-privileges"></a>

Cette rubrique décrit les informations d’identification de base de données nécessaires pour créer et exécuter des procédures stockées.

Par défaut, tous les utilisateurs disposent des privilèges nécessaires pour créer une procédure. Pour créer une procédure, vous devez disposer du privilège USAGE sur la langue PL/pgSQL, which is granted to PUBLIC by default. Only superusers and owners have the privilege to call a procedure by default. Superusers can run REVOKE USAGE on PL/pgSQL d'un utilisateur s'il souhaite empêcher celui-ci de créer une procédure stockée. 

Pour appeler une procédure, vous devez disposer du privilège EXECUTE sur la procédure. Par défaut, le privilège EXECUTE pour les nouvelles procédures est accordé au propriétaire de la procédure et aux super-utilisateurs. Pour plus d’informations, consultez [GRANT](r_GRANT.md). 

L’utilisateur qui crée une procédure est le propriétaire par défaut. Par défaut, le propriétaire possède les privilèges CREATE, DROP et EXECUTE sur la procédure. Les super-utilisateurs disposent de tous les privilèges. 

L’attribut SECURITY contrôle les privilèges d’une procédure relatifs à l’accès aux objets de base de données. Lorsque vous créez une procédure stockée, vous pouvez définir l’attribut SECURITY sur DEFINER ou INVOKER. Cet attribut détermine les privilèges utilisés lors de l’exécution des instructions dans le corps de la procédure stockée. Si vous spécifiez SECURITY INVOKER, la procédure utilise les privilèges de l’utilisateur invoquant la procédure. Si vous spécifiez SECURITY DEFINER, la procédure utilise les privilèges du propriétaire de la procédure. INVOKER est la valeur par défaut. 

Parce qu’une procédure SECURITY DEFINER s’exécute avec les privilèges de l’utilisateur qui la possède, vous devez vous assurer que la procédure ne peut pas être utilisée à mauvais escient. Pour vous assurer que les procédures SECURITY DEFINER ne peuvent pas être utilisées à mauvais escient, procédez comme suit :
+ Accordez l’autorisation EXECUTE sur les procédures définies avec SECURITY DEFINER à des utilisateurs spécifiques, et non à PUBLIC.
+ Qualifiez tous les objets de base de données dont la procédure a besoin pour accéder à l’aide des noms de schéma. Par exemple, utilisez `myschema.mytable` plutôt que `mytable`.
+ Si vous ne pouvez pas qualifier un nom d’objet par son schéma, lors de la création de la procédure, définissez `search_path` à l’aide de l’option SET. Définissez `search_path` de façon à exclure tout schéma accessible en écriture par des utilisateurs non fiables. Cette approche empêche tout appelant de la procédure de créer des objets (tels que des tables ou des vues) qui masquent les objets destinés à être utilisés par la procédure. Pour plus d’informations sur l’option SET, consultez [CREATE PROCEDURE](r_CREATE_PROCEDURE.md). 

L’exemple suivant définit `search_path` sur `admin` pour s’assurer que la table `user_creds` est accessible depuis le schéma `admin` et non depuis le schéma public ou autre défini dans le `search_path` de l’appelant.

```
CREATE OR REPLACE PROCEDURE sp_get_credentials(userid int, o_creds OUT varchar)
AS $$
BEGIN
  SELECT creds INTO o_creds
  FROM user_creds
  WHERE user_id = $1;
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER
-- Set a secure search_path
SET search_path = admin;
```

# Retour d’un ensemble de résultats depuis une procédure stockée
<a name="stored-procedure-result-set"></a>

Cette rubrique décrit la manière dont les procédures stockées renvoient des données.

Vous pouvez retourner un ensemble de résultats à l’aide d’un curseur ou d’une table temporaire.

## Retour d’un curseur
<a name="stored-procedure-return-cursor"></a>

Pour retourner un curseur, créez une procédure avec un argument INOUT défini avec un type de données `refcursor`. Lorsque vous appelez la procédure, donnez un nom au curseur. Vous pouvez ensuite extraire les résultats du curseur par leur nom.

L’exemple suivant crée une procédure nommée `get_result_set` avec un argument INOUT nommé `rs_out` à l’aide du type de données `refcursor`. La procédure ouvre le curseur à l’aide d’une instruction SELECT.

```
CREATE OR REPLACE PROCEDURE get_result_set (param IN integer, rs_out INOUT refcursor)
AS $$
BEGIN
  OPEN rs_out FOR SELECT * FROM fact_tbl where id >= param;
END;
$$ LANGUAGE plpgsql;
```

La commande CALL suivante ouvre le curseur avec le nom `mycursor`. N’utlisez les curseurs qu’au sein des transactions. 

```
BEGIN;
CALL get_result_set(1, 'mycursor');
```

Une fois que le curseur est ouvert, vous pouvez procéder à l’extraction depuis ce curseur, comme l’illustre l’exemple suivant.

```
FETCH ALL FROM mycursor;

    id | secondary_id | name
-------+--------------+---------
     1 |            1 | Joe
     1 |            2 | Ed
     2 |            1 | Mary
     1 |            3 | Mike
(4 rows)
```

À la fin, la transaction est validée ou annulée.

```
COMMIT;   
```

Un curseur retourné par une procédure stockée est soumise aux mêmes contraintes et considérations de performance, telles que décrites dans DECLARE CURSOR; Pour plus d’informations, consultez [Contraintes de curseur](declare.md#declare-constraints).

L’exemple suivant montre l’appel de la procédure stockée `get_result_set` à l’aide d’un type de données `refcursor` depuis JDBC. Le littéral `'mycursor'` (nom du curseur) est transmis à `prepareStatement`. Puis, les résultats sont extraits de `ResultSet`.

```
static void refcursor_example(Connection conn) throws SQLException {
    conn.setAutoCommit(false);
    PreparedStatement proc = conn.prepareStatement("CALL get_result_set(1, 'mycursor')");
    proc.execute();
    ResultSet rs = statement.executeQuery("fetch all from mycursor");
    while (rs.next()) {
      int n = rs.getInt(1);
      System.out.println("n " + n);
    }
```

## Utilisation d’une table temporaire
<a name="stored-procedure-return-cursor"></a>

Pour retourner les résultats, vous pouvez retourner un descripteur vers une table temporaire contenant les lignes de résultats. Le client peut fournir un nom comma paramètre à la procédure stockée. À l’intérieur de la procédure stockée, Dynamic SQL peut être utilisé pour œuvrer sur la table temporaire. Vous en trouverez un exemple ci-dessous.

```
CREATE PROCEDURE get_result_set(param IN integer, tmp_name INOUT varchar(256)) as $$
DECLARE
  row record;
BEGIN
  EXECUTE 'drop table if exists ' || tmp_name;
  EXECUTE 'create temp table ' || tmp_name || ' as select * from fact_tbl where id <= ' || param;
END;
$$ LANGUAGE plpgsql;

CALL get_result_set(2, 'myresult');
 tmp_name
-----------
 myresult
(1 row)

SELECT * from myresult;
 id | secondary_id | name
----+--------------+------
  1 |            1 | Joe
  2 |            1 | Mary
  1 |            2 | Ed
  1 |            3 | Mike
(4 rows)
```

# Gestion des transactions
<a name="stored-procedure-transaction-management"></a>

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
<a name="stored-procedure-transaction-management-default-mode"></a>

Le comportement de validation automatique du mode transactionnel par défaut entraîne la validation individuelle de chaque commande SQL qui s’exécute séparément. Un appel à une procédure stockée est traité comme une simple commande SQL. Les instructions SQL à l’intérieur d’une procédure se comportent comme si elles étaient dans un bloc de transaction qui commence implicitement quand l’appel démarre et se termine quand l’appel finit. Un appel imbriqué à une autre procédure est traité comme toute autre instruction SQL et opère au sein du contexte de la même transaction que l’appelant. Pour plus d’informations sur le comportement automatique de validation, consultez [Niveaux d’isolement dans Amazon Redshift](c_serial_isolation.md).

Cependant, supposons que vous appelez une procédure stockée depuis un bloc de transaction spécifié par l’utilisateur (défini par BEGIN...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 l’ensemble du travail effectué dans la transaction actuelle et démarre implicitement une nouvelle transaction. Pour plus d’informations, consultez [COMMIT](r_COMMIT.md). 
+ ROLLBACK : restaure le travail effectué dans la transaction actuelle et démarre implicitement une nouvelle transaction. Pour plus d’informations, consultez [ROLLBACK](r_ROLLBACK.md). 

TRUNCATE est une autre instruction qui peut être émise depuis une procédure stockée et qui influe sur la gestion de la transaction. Dans Amazon Redshift, TRUNCATE émet une validation implicitement. Ce comportement demeure le même dans le contexte des procédures stockées. Quand une instruction TRUNCATE est émise depuis une procédure stockée, elle valide la transaction en cours et en démarre une nouvelle. Pour plus d’informations, consultez [TRUNCATE](r_TRUNCATE.md). 

Toutes les instructions qui suivent une instruction COMMIT, ROLLBACK ou TRUNCATE s’exécutent dans le contexte d’une nouvelle transaction. C’est le cas jusqu’à ce qu’une instruction COMMIT, ROLLBACK ou TRUNCATE soit détectée ou que la procédure stockée se termine.

Lorsque vous utilisez une instruction COMMIT, ROLLBACK ou TRUNCATE depuis une procédure stockée, les contraintes suivantes s’appliquent :
+ Si la procédure stockée est appelée depuis un bloc de transaction, elle ne peut pas émettre d’instruction COMMIT, ROLLBACK ou TRUNCATE. 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 les options `SET config`, elle ne peut pas émettre d’instruction COMMIT, ROLLBACK ou TRUNCATE. 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, est fermé automatiquement quand une instruction COMMIT, ROLLBACK ou TRUNCATE est traitée. Pour les contraintes sur les curseurs explicites et implicites, consultez [Limitations des procédures stockées](stored-procedure-constraints.md).

Vous ne pouvez pas exécuter COMMIT ou ROLLBACK avec Dynamic SQL. Toutefois, TRUNCATE peut être exécuté avec Dynamic SQL. Pour de plus amples informations, veuillez consulter [Instructions SQL dynamiques](c_PLpgSQL-statements.md#r_PLpgSQL-dynamic-sql). 

Lorsque vous utilisez des procédures stockées, considérez que les instructions BEGIN et END in PL/pgSQL sont uniquement destinées au regroupement. Ils ne démarrent pas et ne finissent pas une transaction. Pour plus d’informations, consultez [Block](c_PLpgSQL-structure.md#r_PLpgSQL-block). 

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 instruction TRUNCATE après l’insertion dans `test_table_a`. L’instruction TRUNCATE émet une validation implicite qui valide la transaction courante (3335) et en démarre 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 une commande TRUNCATE depuis un appel imbriqué. L’instruction TRUNCATE valide tout le travail effectué jusque-là 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` a été fermé quand l’instruction TRUNCATE a été validée.

```
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 instruction TRUNCATE 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 super-utilisateur ni le propriétaire d’une table peut émettre une instruction TRUNCATE 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 commande `TRUNCATE`. 

```
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 deux fois COMMIT. Le premier COMMIT valide l’ensemble du travail effectué dans la transaction 10363 et démarre implicitement la transaction 10364. La transaction 10364 est validée par la deuxième instruction 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’exemple suivant émet une instruction ROLLBACK si `sum_vals` est supérieur à 2. La première instruction ROLLBACK restaure tout le travail effectué dans la transaction 10377 et démarre 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
<a name="stored-procedure-transaction-management-nonatomic-mode"></a>

Une procédure stockée créée en mode NONATOMIC a un comportement de contrôle des transactions différent de celui d’une procédure créée en mode par défaut. À l’instar de la validation automatique des commandes SQL en dehors des procédures stockées, chaque instruction SQL à l’intérieur d’une procédure NONATOMIC s’exécute dans sa propre transaction et est validée automatiquement. Si un utilisateur commence un bloc de transaction explicite dans une procédure stockée NONATOMIC, les instructions SQL 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 stockées NONATOMIC, vous pouvez ouvrir un bloc de transaction explicite à l’intérieur de la procédure à l’aide de l’instruction START TRANSACTION. 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 boucles FOR à l’intérieur d’une procédure NONATOMIC, veillez à ouvrir un bloc de transaction explicite avant de parcourir les résultats d’une requête. Sinon, le curseur est fermé lorsque l’instruction SQL à l’intérieur de la boucle est automatiquement validée.

Voici quelques éléments à prendre en compte lors de l’utilisation du mode de comportement NONATOMIC :
+ Chaque instruction SQL contenue dans la procédure stockée est automatiquement validée s’il n’y a pas de bloc de transaction ouvert et si l’auto-validation de la session est définie sur ON.
+ Vous pouvez émettre une COMMIT/ROLLBACK/TRUNCATE déclaration 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 lancer une instruction START TRANSACTION pour commencer un bloc de transactions à l’intérieur de la procédure stockée.

Les exemples suivants illustrent le comportement des transactions lors de l’utilisation de procédures stockées NONATOMIC. Pour tous les exemples suivants, la validation automatique est définie sur ON.

Dans l’exemple suivant, une procédure stockée NONATOMIC comporte deux instructions INSERT. Lorsque la procédure est appelée en dehors d’un bloc de transactions, chaque instruction INSERT au sein 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 à partir d’un bloc BEGIN..COMMIT, 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 instructions INSERT se trouvent entre START TRANSACTION...COMMIT. Lorsque la procédure est appelée en dehors d’un bloc de transaction, les deux instructions INSERT se trouvent dans 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 à l’intérieur d’un bloc BEGIN...COMMIT, la START TRANSACTION à l’intérieur de la procédure ne fait rien parce qu’il y a déjà une transaction ouverte. La commande COMMIT de la procédure valide la transaction en cours (xid=1876) et en démarre 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\$1table\$1a possède trois valeurs. L’objectif est de parcourir les trois valeurs et de les insérer dans la table test\$1table\$1b. Si une procédure stockée NONATOMIC est créée de la manière suivante, le curseur « cur1 » n’existe plus après l’exécution de l’instruction INSERT dans la première boucle. En effet, la validation automatique de la commande 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 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
```

# Interception des erreurs
<a name="stored-procedure-trapping-errors"></a>

Cette rubrique décrit la façon dont Amazon Redshift gère les erreurs.

Lorsqu’une requête ou une commande dans une procédure stockée provoque une erreur, les requêtes suivantes ne s’exécutent pas et la transaction est annulée. Vous pouvez toutefois intercepter les erreurs à l’aide d’un bloc EXCEPTION.

**Note**  
Par défaut, une erreur empêche l’exécution des requêtes suivantes, même si la procédure stockée ne contient pas d’autres conditions génératrices d’erreurs.

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  WHEN OTHERS THEN
    statements
END;
```

Lorsqu'une exception se produit et que vous ajoutez un bloc de gestion des exceptions, vous pouvez écrire des instructions RAISE et la plupart des autres PL/pgSQL instructions. Par exemple, vous pouvez générer une exception avec un message personnalisé ou insérer un enregistrement dans une table de journalisation.

Lorsque vous saisissez le bloc de gestion des exceptions, la transaction actuelle est annulée et une nouvelle transaction est créée pour exécuter les instructions dans le bloc. Si les instructions du bloc s’exécutent sans erreur, la transaction est validée et l’exception est renvoyée. Enfin, la procédure stockée se ferme.

La seule condition prise en charge dans un bloc d’exception est OTHERS, qui établit une correspondance avec tout type d’erreur, à l’exception de l’annulation de requête. De plus, si une erreur se produit dans un bloc de gestion des exceptions, elle peut être interceptée par un bloc de gestion des exceptions externe.

Lorsqu’une erreur se produit à l’intérieur de la procédure NONATOMIC, l’erreur n’est pas relancée si elle est gérée par un bloc d’exception. Consultez l' PL/pgSQL instruction `RAISE` pour lancer une exception interceptée par le bloc de gestion des exceptions. Cette déclaration n’est valable que dans les blocs de traitement des exceptions. Pour plus d’informations, consultez [RAISE](c_PLpgSQL-statements.md#r_PLpgSQL-messages-errors).

**Contrôle des conséquences d’une erreur dans une procédure stockée, avec le gestionnaire CONTINUE**

 Le gestionnaire `CONTINUE` est un type de gestionnaire d’exceptions qui contrôle le flux d’exécution au sein d’une procédure stockée NONATOMIC. En l’utilisant, vous pouvez intercepter et gérer les exceptions sans mettre fin au bloc d’instructions existant. Normalement, lorsqu’une erreur se produit dans une procédure stockée, le flux est interrompu et l’erreur est renvoyée à l’appelant. Cependant, dans certains cas d’utilisation, la condition d’erreur n’est pas suffisamment grave pour justifier l’interruption du flux. Vous souhaiterez peut-être gérer l’erreur de façon fluide, en utilisant la logique de gestion des erreurs de votre choix dans une transaction distincte, puis continuer à exécuter les instructions qui suivent l’erreur. L’exemple suivant montre la syntaxe. 

```
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  [ CONTINUE_HANDLER | EXIT_HANDLER ] WHEN OTHERS THEN
    handler_statements
END;
```

Plusieurs tables système sont disponibles pour vous aider à recueillir des informations sur les différents types d’erreurs. Pour plus d’informations, consultez [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md), [STL\$1ERROR](r_STL_ERROR.md) et [SYS\$1STREAM\$1SCAN\$1ERRORS](r_SYS_STREAM_SCAN_ERRORS.md). Il existe également des tables système supplémentaires que vous pouvez utiliser pour résoudre les erreurs. Pour plus d’informations, consultez [Informations de référence sur les tables et les vues système](cm_chap_system-tables.md).

## Exemple
<a name="stored-procedure-trapping-errors-examples"></a>

L’exemple suivant montre comment écrire des instructions dans le bloc de gestion des exceptions. La procédure stockée utilise le comportement de gestion des transactions par défaut.

```
CREATE TABLE employee (firstname varchar, lastname varchar);
INSERT INTO employee VALUES ('Tomas','Smith');
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp() AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp();

INFO:  An exception occurred.
ERROR:  column "invalid" does not exist
CONTEXT:  SQL statement "select invalid"
PL/pgSQL function "update_employee_sp" line 3 at execute statement
```

Dans cet exemple, si nous appelons `update_employee_sp`, le message d’information *An exception occurred (Une exception s’est produite)* est déclenché et le message d’erreur est généré dans le journal `employee_error_log` de la table de journalisation. L’exception d’origine est renvoyée avant la fin de la procédure stockée. Les requêtes suivantes présentent les enregistrements résultant de l’exécution de l’exemple.

```
SELECT * from employee;

firstname | lastname 
-----------+----------
 Tomas     | Smith

SELECT * from employee_error_log;

          message                     
------------------------------------------------
 Error message: column "invalid" does not exist
```

Pour plus d’informations sur RAISE, y compris une aide au formatage et une liste de niveaux supplémentaires, consultez [Instructions PL/pgSQL prises en charge](c_PLpgSQL-statements.md).

L’exemple suivant montre comment écrire des instructions dans le bloc de gestion des exceptions. La procédure stockée utilise un comportement de gestion des transactions NONATOMIC. Dans cet exemple, aucune erreur n’est renvoyée à l’appelant à la fin de l’appel de la procédure. L’instruction UPDATE n’est pas restaurée en raison de l’erreur dans l’instruction suivante. Le message d’information est affiché et le message d’erreur est inséré dans la table de journalisation.

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

-- Create the SP in NONATOMIC mode
CREATE OR REPLACE PROCEDURE update_employee_sp_2() NONATOMIC AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_2();
INFO:  An exception occurred.
CALL

SELECT * from employee;

 firstname | lastname 
-----------+----------
 Adam      | Smith
(1 row)

SELECT * from employee_error_log;

                    message                     
------------------------------------------------
 Error message: column "invalid" does not exist
(1 row)
```

Cet exemple montre comment créer une procédure avec deux sous-blocs. Lorsque la procédure stockée est appelée, l’erreur du premier sous-bloc est traitée par son bloc de gestion des exceptions. Une fois le premier sous-bloc terminé, la procédure continue d’exécuter le deuxième sous-bloc. Vous pouvez voir dans le résultat qu’aucune erreur n’est déclenchée à la fin de l’appel de la procédure. Les opérations UPDATE et INSERT sur la table employee sont validées. Les messages d’erreur des deux blocs d’exception sont insérés dans la table de journalisation.

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp_3() NONATOMIC AS
$$
BEGIN
    BEGIN
        UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
        EXECUTE 'select invalid1';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the first block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
    BEGIN
        INSERT INTO employee VALUES ('Edie','Robertson');
        EXECUTE 'select invalid2';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the second block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_3();
INFO:  An exception occurred in the first block.
INFO:  An exception occurred in the second block.
CALL

SELECT * from employee;

 firstname | lastname  
-----------+-----------
 Adam      | Smith
 Edie      | Robertson
(2 rows)

SELECT * from employee_error_log;

                     message                     
-------------------------------------------------
 Error message: column "invalid1" does not exist
 Error message: column "invalid2" does not exist
(2 rows)
```

L’exemple suivant montre comment utiliser le gestionnaire d’exceptions CONTINUE. Cet exemple crée deux tables et les utilise dans une procédure stockée. Le gestionnaire CONTINUE contrôle le flux d’exécution dans une procédure stockée avec un comportement de gestion des transactions NONATOMIC.

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_1() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (2);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Appelez la procédure stockée :

```
CALL sp_exc_handling_1();
```

Le flux se déroule comme suit :

1. Une erreur se produit, car une tentative est faite d’insérer un type de données incompatible dans une colonne. Le contrôle passe au bloc EXCEPTION. Lorsque le bloc de gestion des exceptions est entré, la transaction actuelle est annulée et une nouvelle transaction est créée pour exécuter les instructions qu’elle contient.

1. Si les instructions de CONTINUE\$1HANDLER s’exécutent sans erreur, le contrôle passe à l’instruction qui suit immédiatement celle à l’origine de l’exception. (Si une instruction dans CONTINUE\$1HANDLER déclenche une nouvelle exception, vous pouvez la gérer avec un gestionnaire d’exceptions dans le bloc EXCEPTION.)

Une fois que vous avez appelé l’exemple de procédure stockée, les tables contiennent les enregistrements suivants :
+ Si vous exécutez `SELECT * FROM tbl_1;`, cela renvoie deux enregistrements. Ceux-ci contiennent les valeurs `1` et `2`.
+ Si vous exécutez `SELECT * FROM tbl_error_logging;`, cela renvoie un enregistrement avec les valeurs suivantes : *Encountered error*, *42703* et *column "val" does not exist in tbl\$11*.

L’autre exemple suivant de gestion des erreurs utilise à la fois un gestionnaire EXIT et un gestionnaire CONTINUE. Il crée deux tables : une table de données et une table de journalisation. Il crée également une procédure stockée qui illustre la gestion des erreurs :

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_2() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    BEGIN
        INSERT INTO tbl_1 VALUES (100);
        -- Expect an error for the insert statement following, because of the invalid value
        INSERT INTO tbl_1 VALUES ("val");
        INSERT INTO tbl_1 VALUES (101);
    EXCEPTION EXIT_HANDLER WHEN OTHERS THEN
        INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
    END;
    INSERT INTO tbl_1 VALUES (2);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (3);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Après avoir créé la procédure stockée, appelez-la comme suit :

```
CALL sp_exc_handling_2();
```

Lorsqu’une erreur se produit dans le bloc d’exception interne, qui est placé entre crochets par l’ensemble interne BEGIN et END, elle est gérée par le gestionnaire EXIT. Toutes les erreurs survenant dans le bloc extérieur sont gérées par le gestionnaire CONTINUE. 

Une fois que vous avez appelé l’exemple de procédure stockée, les tables contiennent les enregistrements suivants :
+ Si vous exécutez `SELECT * FROM tbl_1;`, cela renvoie quatre enregistrements, avec les valeurs 1, 2, 3 et 100.
+ Si vous exécutez `SELECT * FROM tbl_error_logging;`, cela renvoie deux enregistrements. Ils ont les valeurs suivantes : *Encountered error*, *42703* et *column "val" does not exist in tbl\$11*.

Si la table **tbl\$1error\$1logging** n’existe pas, elle déclenche une exception.

L’exemple suivant montre comment utiliser le gestionnaire d’exceptions CONTINUE avec la boucle FOR. Cet exemple crée trois tables et les utilise dans une boucle FOR dans une procédure stockée. La boucle FOR est une variante avec ensemble de résultats, ce qui signifie qu’elle itère sur les résultats d’une requête :

```
CREATE TABLE tbl_1 (a int);
INSERT INTO tbl_1 VALUES (1), (2), (3);
CREATE TABLE tbl_2 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_loop() NONATOMIC AS
$$
DECLARE
 rec RECORD;
BEGIN
    FOR rec IN SELECT a FROM tbl_1
    LOOP
        IF rec.a = 2 THEN
            -- Expect an error for the insert statement following, because of the invalid value
            INSERT INTO tbl_2 VALUES("val");
        ELSE
            INSERT INTO tbl_2 VALUES (rec.a);
        END IF;
    END LOOP;
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

Appelez la procédure stockée :

```
CALL sp_exc_handling_loop();
```

Une fois que vous avez appelé l’exemple de procédure stockée, les tables contiennent les enregistrements suivants :
+  Si vous exécutez `SELECT * FROM tbl_2;`, cela renvoie deux enregistrements. Ceux-ci contiennent les valeurs 1 et 3.
+ Si vous exécutez `SELECT * FROM tbl_error_logging;`, cela renvoie un enregistrement avec les valeurs suivantes : *Encountered error*, *42703* et *column "val" does not exist in tbl\$12*.

Remarques concernant l’utilisation du gestionnaire CONTINUE :
+ Les mots-clés CONTINUE\$1HANDLER et EXIT\$1HANDLER ne peuvent être utilisés que dans les procédures stockées NONATOMIC.
+ Les mots-clés CONTINUE\$1HANDLER et EXIT\$1HANDLER sont facultatifs. EXIT\$1HANDLER est celui par défaut.

# Enregistrement des procédures stockées
<a name="c_PLpgSQL-logging"></a>

Cette rubrique décrit les procédures stockées et les vues qu’Amazon Redshift utilise pour la journalisation des procédures stockées.

Les détails sur les procédures stockées sont enregistrés dans les vues et tables système suivantes :
+ SVL\$1STORED\$1PROC\$1CALL : les détails enregistrés concernent l’heure de début et l’heure de fin de l’appel de la procédure stockée, ainsi que l’information selon laquelle l’appel prend fin avant qu’il ne soit terminé. Pour plus d’informations, consultez [SVL\$1STORED\$1PROC\$1CALL](r_SVL_STORED_PROC_CALL.md).
+ SVL\$1STORED\$1PROC\$1MESSAGES : les messages stockés dans les procédures stockées émises par la requête RAISE sont enregistrés avec le niveau de journalisation correspondant. Pour plus d’informations, consultez [SVL\$1STORED\$1PROC\$1MESSAGES](r_SVL_STORED_PROC_MESSAGES.md).
+ SVL\$1QLOG : l’ID de requête de l’appel de procédure est enregistré pour chaque requête appelée à partir d’une procédure stockée. Pour plus d’informations, consultez [SVL\$1QLOG](r_SVL_QLOG.md).
+ STL\$1UTILITYTEXT : les appels de procédure stockée sont enregistrés une fois qu’ils sont terminés. Pour plus d’informations, consultez [STL\$1UTILITYTEXT](r_STL_UTILITYTEXT.md).
+ PG\$1PROC\$1INFO : cette vue catalogue système affiche les informations sur les procédures stockées. Pour plus d’informations, consultez [PG\$1PROC\$1INFO](r_PG_PROC_INFO.md).

# Limitations des procédures stockées
<a name="stored-procedure-constraints"></a>

Cette rubrique décrit les limites des procédures stockées Amazon Redshift.

Les considérations suivantes s’appliquent lorsque vous utilisez des procédures stockées Amazon Redshift.

## Différences entre Amazon Redshift et PostgreSQL pour la prise en charge des procédures stockées
<a name="stored-procedure-differences"></a>

 Voici les différences entre la prise en charge des procédures stockées dans Amazon Redshift et PostgreSQL :
+ Amazon Redshift ne prend pas en charge les sous-transactions et, par conséquent, offre une prise en charge limitée pour les blocs de gestion des exceptions.

## Considérations et limites
<a name="stored-procedure-limits"></a>

Vous trouverez ci-après des considérations sur les procédures stockées dans Amazon Redshift :
+ Le nombre maximal de procédures stockées pour une base de données est de 10 000.
+ La taille maximale du code source pour une procédure est de 2 Mo.
+ Le nombre maximal de curseurs implicites et explicites que vous pouvez ouvrir simultanément dans une session utilisateur est de 1 (un). Les boucles FOR qui itèrent sur l’ensemble des résultats d’une instruction SQL ouvrent les curseurs implicites. Les curseurs imbriqués ne sont pas pris en charge.
+ Les curseurs explicites et implicites ont les mêmes restrictions sur la taille de l’ensemble de résultats que les curseurs Amazon Redshift standard. Pour plus d’informations, consultez [Contraintes de curseur](declare.md#declare-constraints). 
+ Le nombre maximal de niveaux pour les appels imbriqués est de 16.
+ Le nombre maximal de paramètres de procédure est de 32 pour les arguments en entrée et de 32 pour les arguments en sortie.
+ Le nombre maximal de variables dans une procédure stockée est de 1 024.
+ Toute commande SQL qui nécessite son propre contexte de transaction n’est pas prise en charge à l’intérieur d’une procédure stockée. En voici quelques exemples :
  + PREPARE
  + CREATE/DROP DATABASE
  + CREATE EXTERNAL TABLE
  + VACUUM
  + SET LOCAL
  + ALTER TABLE APPEND
+ L’appel de la méthode `registerOutParameter` via le pilote JDBC (Java Database Connectivity) n’est pas pris en charge pour le type de données `refcursor`. Pour obtenir un exemple de l’utilisation du type de données `refcursor`, consultez [Retour d’un ensemble de résultats depuis une procédure stockée](stored-procedure-result-set.md).