

 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.

# Création de procédures stockées dans Amazon Redshift
<a name="stored-procedure-overview"></a>

Cette rubrique décrit comment créer et utiliser des procédures stockées dans Amazon Redshift. Une procédure stockée est un ensemble d’instructions SQL que plusieurs programmes peuvent utiliser.

Vous pouvez définir une procédure stockée Amazon Redshift à l’aide de PL/pgSQ, le langage procédural de PostgreSQL, pour exécuter un ensemble de requêtes SQL et d’opérations logiques. La procédure est stockée dans la base de données et est accessible à tout utilisateur disposant de privilèges suffisants dans la base de données. 

Contrairement à une fonction définie par l’utilisateur, une procédure stockée peut incorporer des instructions DDL (Data-Definition Language) et DML (Data-Manipulation Language), en plus des requêtes SELECT. Une procédure stockée n’a pas besoin de retourner une valeur. Vous pouvez utiliser le langage procédural, y compris les boucles et les expressions conditionnelles, pour contrôler le flux logique. 

Pour plus d’informations sur les commandes SQL qui permettent de créer et de gérer des procédures stockées, consultez les rubriques suivantes :
+ [CREATE PROCEDURE](r_CREATE_PROCEDURE.md)
+ [ALTER PROCEDURE](r_ALTER_PROCEDURE.md)
+ [DROP PROCEDURE](r_DROP_PROCEDURE.md)
+ [SHOW PROCEDURE](r_SHOW_PROCEDURE.md)
+ [CALL](r_CALL_procedure.md)
+ [GRANT](r_GRANT.md)
+ [REVOKE](r_REVOKE.md)
+ [ALTER DEFAULT PRIVILEGES](r_ALTER_DEFAULT_PRIVILEGES.md)

**Topics**
+ [

# Présentation des procédures stockées dans Amazon Redshift
](stored-procedure-create.md)
+ [

# Guide de référence du langage PL/pgSQL
](c_pl_pgSQL_reference.md)

# 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).

# Guide de référence du langage PL/pgSQL
<a name="c_pl_pgSQL_reference"></a>

Les procédures stockées dans Amazon Redshift s'appuient sur le langage procédural PostgreSQL PL/pgSQL, avec quelques différences majeures. Ce guide de référence contient les détails de la syntaxe PL/pgSQL telle qu'elle a été implémentée dans Amazon Redshift. Pour de plus amples informations sur PL/pgSQL, veuillez consulter [PL/pgSQL - SQL Procedural Language](https://www.postgresql.org/docs/8.0/plpgsql.html) dans la documentation de PostgreSQL.

**Topics**
+ [

# Conventions de référence du langage PL/pgSQL
](c_PL_reference_conventions.md)
+ [

# Structure de PL/pgSQL
](c_PLpgSQL-structure.md)
+ [

# Instructions PL/pgSQL prises en charge
](c_PLpgSQL-statements.md)

# Conventions de référence du langage PL/pgSQL
<a name="c_PL_reference_conventions"></a>

Dans cette section, vous trouverez les conventions utilisées pour écrire la syntaxe du langage de procédure stockée PL/pgSQL. 

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/fr_fr/redshift/latest/dg/c_PL_reference_conventions.html)

# Structure de PL/pgSQL
<a name="c_PLpgSQL-structure"></a>

PL/pgSQL est un langage procédural avec un grand nombre de constructions communes à d'autres langages procéduraux. 

**Topics**
+ [

## Block
](#r_PLpgSQL-block)
+ [

## Déclaration de variable
](#r_PLpgSQL-variable-declaration)
+ [

## Déclaration d'alias
](#r_PLpgSQL-alias-declaration)
+ [

## Variables intégrées
](#r_PLpgSQL-builtin-variables)
+ [

## Types d'enregistrement
](#r_PLpgSQL-record-type)

## Block
<a name="r_PLpgSQL-block"></a>

PL/pgSQL est un langage structuré en blocs. Le corps complet d'une procédure est défini dans un bloc, lequel contient les déclarations de variables et les instructions PL/pgSQL. Une instruction peut également être un bloc imbriqué ou un sous-bloc. 

Terminez les déclarations et les instructions par un point-virgule. Faites suivre le mot clé END d'un point-virgule dans un bloc ou un sous-bloc. N'utilisez pas de point-virgule après les mots clés DECLARE et BEGIN. 

Vous pouvez écrire tous les mots clés et les identifiants en mélangeant majuscules et minuscules. Les identifiants sont implicitement convertis en minuscules à moins d'être placés entre guillemets doubles.

Un tiret double (--) marque le début d'un commentaire qui s'étend jusqu'à la fin de la ligne. Les caractères /\$1 marquent le début d'un commentaire de bloc qui s'étend jusqu'à l'occurrence suivante de \$1/. Vous ne pouvez pas imbriquer des commentaires de bloc. Toutefois, vous pouvez placer des commentaires avec tiret double dans un commentaire de bloc, et un tiret double peut masquer les délimiteurs de commentaire de bloc /\$1 et \$1/.

Toute instruction figurant dans la section des instructions d'un bloc peut être un sous-bloc. Vous pouvez utiliser des sous-blocs pour effectuer un regroupement logique ou pour localiser des variables dans un petit groupe d'instructions.

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
END [ label ];
```

Les variables déclarées dans la section des déclarations précédant un bloc sont initialisées à leur valeur par défaut à chaque entrée dans le bloc. En d'autres termes, elles ne sont pas initialisées une seule fois par appel de fonction.

Vous en trouverez un exemple ci-dessous.

```
CREATE PROCEDURE update_value() AS $$
DECLARE
  value integer := 20;
BEGIN
  RAISE NOTICE 'Value here is %', value;  -- Value here is 20
  value := 50;
  --
  -- Create a subblock
  --
  DECLARE
    value integer := 80;
  BEGIN
    RAISE NOTICE 'Value here is %', value;  -- Value here is 80
  END;

  RAISE NOTICE 'Value here is %', value;  -- Value here is 50
END;
$$ LANGUAGE plpgsql;
```

Utilisez une étiquette pour identifier le bloc à utiliser dans une instruction EXIT ou pour qualifier les noms des variables déclarées dans le bloc.

Ne confondez pas l'utilisation de BEGIN/END pour regrouper des instructions dans PL/pgSQL avec les commandes de base de données pour le contrôle des transactions. Dans PL/pgSQL, les mots clés BEGIN et END sont utilisés uniquement à des fins de regroupement. Ils ne démarrent pas et ne finissent pas une transaction.

## Déclaration de variable
<a name="r_PLpgSQL-variable-declaration"></a>

Déclarez toutes les variables d'un bloc, à l'exception des variables de boucle, dans la section DECLARE du bloc. Les variables peuvent utiliser un type de données Amazon Redshift quelconque valide. Pour connaître les types de données pris en charge, consultez [Types de données](c_Supported_data_types.md). 

Les variables PL/pgSQL peuvent être d'un type de données quelconque pris en charge par Amazon Redshift, plus `RECORD` et `refcursor`. Pour plus d'informations sur `RECORD`, consultez [Types d'enregistrement](#r_PLpgSQL-record-type). Pour plus d'informations sur `refcursor`, consultez [Curseurs](c_PLpgSQL-statements.md#r_PLpgSQL-cursors). 

```
DECLARE
name [ CONSTANT ] type [ NOT NULL ] [ { DEFAULT | := } expression ];
```

Vous trouverez ci-après des exemples de déclarations de variables.

```
customerID integer;
numberofitems numeric(6);
link varchar;
onerow RECORD;
```

La variable de boucle d'une boucle FOR effectuant une itération sur une plage d'entiers est automatiquement déclarée en tant que variable de type entier. 

La clause DEFAULT, si elle est spécifiée, fournit la valeur initiale attribuée à la variable lors de l'entrée dans le bloc. Si la clause DEFAULT n'est pas spécifiée, la variable est initialisée avec la valeur SQL NULL. L'option CONSTANT empêche l'attribution d'une valeur à la variable, afin que sa valeur reste constante pendant toute la durée du bloc. Si NOT NULL est spécifié, l'attribution d'une valeur null entraîne une erreur d'exécution. Toutes les variables déclarées comme NOT NULL doivent avoir une valeur par défaut non null spécifiée.

La valeur par défaut est évaluée à chaque entrée dans le bloc. Par exemple, l'attribution de `now()` à une variable de type `timestamp` fait que la variable contiendra l'heure de l'appel actuel de la fonction, et non pas l'heure de la précompilation de la fonction.

```
quantity INTEGER DEFAULT 32;
url VARCHAR := 'http://mysite.com';
user_id CONSTANT INTEGER := 10;
```

Le type de données `refcursor` est le type de données des variables de curseur au sein des procédures stockées. Une valeur `refcursor` peut être retournée d'une procédure stockée. Pour de plus amples informations, consultez [Retour d’un ensemble de résultats depuis une procédure stockée](stored-procedure-result-set.md).

## Déclaration d'alias
<a name="r_PLpgSQL-alias-declaration"></a>

Si la signature d'une procédure stockée omet le nom de l'argument, vous pouvez déclarer un alias pour cet argument.

```
name ALIAS FOR $n;
```

## Variables intégrées
<a name="r_PLpgSQL-builtin-variables"></a>

Les variables intégrées suivantes sont prises en charge :
+ FOUND
+ SQLSTATE
+ SQLERRM
+ GET DIAGNOSTICS integer\$1var := ROW\$1COUNT;

FOUND est une variable spéciale de type booléen. FOUND commence avec la valeur false au sein de chaque appel de procédure. La variable FOUND est définie par les types d'instructions suivants :
+ SELECT INTO

  Attribue à FOUND la valeur true si une ligne est renvoyée, et la valeur false si aucune ligne n'est renvoyée.
+ UPDATE, INSERT et DELETE

  Attribue à FOUND la valeur true si au moins une ligne est affectée, et la valeur false si aucune ligne n'est affectée.
+ FETCH

  Attribue à FOUND la valeur true si une ligne est renvoyée, et la valeur false si aucune ligne n'est renvoyée.
+ Instruction FOR

  Attribue à FOUND la valeur true si l'instruction FOR effectue une ou plusieurs itérations, sinon la valeur false. Cela s'applique aux trois variantes de l'instruction FOR : les boucles FOR de type entier, les boucles FOR de type jeu d'enregistrements et les boucles FOR de type jeu d'enregistrements dynamique. 

  FOUND est définie à la sortie de la boucle FOR. Pendant l'exécution de la boucle, la variable FOUND n'est pas modifiée par l'instruction FOR. Toutefois, elle peut être modifiée en exécutant d'autres instructions dans le corps de la boucle.

Vous en trouverez un exemple ci-dessous.

```
CREATE TABLE employee(empname varchar);
CREATE OR REPLACE PROCEDURE show_found()
AS  $$
DECLARE
  myrec record;
BEGIN
  SELECT INTO myrec * FROM employee WHERE empname = 'John';
  IF NOT FOUND THEN
    RAISE EXCEPTION 'employee John not found';
  END IF;
END;
$$ LANGUAGE plpgsql;
```

Dans un gestionnaire d'exceptions, la variable spéciale SQLSTATE contient le code d'erreur correspondant à l'exception qui a été levée. La variable spéciale SQLERRM contient le message d'erreur associé à l'exception. Ces variables sont indéfinies en dehors des gestionnaires d'exception et affichent une erreur si elles sont utilisées.

Vous en trouverez un exemple ci-dessous.

```
CREATE OR REPLACE PROCEDURE sqlstate_sqlerrm() AS
$$
BEGIN
  UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
  EXECUTE 'select invalid';
  EXCEPTION WHEN OTHERS THEN
  RAISE INFO 'error message SQLERRM %', SQLERRM;
  RAISE INFO 'error message SQLSTATE %', SQLSTATE;
END;
$$ LANGUAGE plpgsql;
```

ROW\$1COUNT est utilisée avec la commande GET DIAGNOSTICS. Elle indique le nombre de lignes traitées par la dernière commande SQL envoyée au moteur SQL.

Vous en trouverez un exemple ci-dessous.

```
CREATE OR REPLACE PROCEDURE sp_row_count() AS
$$
DECLARE
  integer_var int;
BEGIN
  INSERT INTO tbl_row_count VALUES(1);
  GET DIAGNOSTICS integer_var := ROW_COUNT;
  RAISE INFO 'rows inserted = %', integer_var;
END;
$$ LANGUAGE plpgsql;
```

## Types d'enregistrement
<a name="r_PLpgSQL-record-type"></a>

Un type RECORD n'est pas un vrai type de données, seulement un espace réservé. Les variables de type d'enregistrement assument la structure de ligne réelle de la ligne à laquelle elles sont attribuées au cours de l'exécution d'une commande SELECT ou FOR. La sous-structure d'une variable d'enregistrement peut changer chaque fois qu'une valeur lui est attribuée. Tant qu'une variable d'enregistrement n'a pas été attribuée, elle n'a aucune sous-structure. Toute tentative d'accès à un champ compris dedans génère une erreur d'exécution.

```
name RECORD;
```

Vous en trouverez un exemple ci-dessous.

```
CREATE TABLE tbl_record(a int, b int);
INSERT INTO tbl_record VALUES(1, 2);
CREATE OR REPLACE PROCEDURE record_example()
LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
BEGIN
  FOR rec IN SELECT a FROM tbl_record
  LOOP
    RAISE INFO 'a = %', rec.a;
  END LOOP;
END;
$$;
```

# Instructions PL/pgSQL prises en charge
<a name="c_PLpgSQL-statements"></a>

 Les instructions PL/pgSQL complémentent les commandes SQL avec des constructions procédurales, y compris des expressions de boucle et conditionnelles, pour contrôler le flux logique. La plupart des commandes SQL peuvent être utilisées, y compris celles du langage de manipulation de données (DML) telles que COPY, UNLOAD et INSERT, et celles du langage de définition de données (DDL) telles que CREATE TABLE. Pour obtenir la liste complète des commandes SQL, veuillez consulter [Commandes SQL](c_SQL_commands.md). De plus, les instructions PL/pgSQL suivantes sont prises en charge par Amazon Redshift. 

**Topics**
+ [

## Affectation
](#r_PLpgSQL-assignment)
+ [

## SELECT INTO
](#r_PLpgSQL-select-into)
+ [

## No-op
](#r_PLpgSQL-no-op)
+ [

## Instructions SQL dynamiques
](#r_PLpgSQL-dynamic-sql)
+ [

## Return
](#r_PLpgSQL-return)
+ [

## Conditions : IF
](#r_PLpgSQL-conditionals-if)
+ [

## Conditions : CASE
](#r_PLpgSQL-conditionals-case)
+ [

## Boucles
](#r_PLpgSQL-loops)
+ [

## Curseurs
](#r_PLpgSQL-cursors)
+ [

## RAISE
](#r_PLpgSQL-messages-errors)
+ [

## Contrôle de transaction
](#r_PLpgSQL-transaction-control)

## Affectation
<a name="r_PLpgSQL-assignment"></a>

L'instruction d'affectation affecte une valeur à une variable. L'expression doit renvoyer une valeur unique.

```
identifier := expression;
```

L'utilisation de l'élément non standard `=` pour l'affectation, à la place de `:=`, est également acceptée.

Si le type de données de l'expression ne correspond pas au type de données de la variable ou si la variable a une taille ou une précision, la valeur obtenue est implicitement convertie.

Des exemples sont fournis ci-dessous.

```
customer_number := 20;
tip := subtotal * 0.15;
```

## SELECT INTO
<a name="r_PLpgSQL-select-into"></a>

L'instruction SELECT INTO affecte le résultat de plusieurs colonnes (mais d'une seule ligne) dans une variable d'enregistrement ou une liste de variables scalaires.

```
SELECT INTO target select_expressions FROM ...;
```

Dans la syntaxe précédente, *target* peut être une variable d'enregistrement ou une liste séparée par des virgules de variables simples et de champs d'enregistrement. La liste *select\$1expressions* et le reste de la commande sont les mêmes qu'en langage SQL standard.

Si une liste de variables est utilisée comme *target*, les valeurs sélectionnées doivent correspondre exactement à la structure de la cible, ou une erreur d'exécution se produit. Lorsqu'une variable d'enregistrement est la cible, elle se configure automatiquement sur le type de ligne des colonnes de résultat de la requête.

La clause INTO peut apparaître presque partout dans l'instruction SELECT. Elle apparaît habituellement juste après la clause SELECT ou juste avant la clause FROM. Ainsi, elle apparaît juste avant ou juste après la liste *select\$1expressions*.

Si la requête ne renvoie aucune ligne, des valeurs NULL sont affectées à *target*. Si la requête renvoie plusieurs lignes, la première ligne est affectée à *target* et les autres sont ignorées. À moins que l'instruction contienne une commande ORDER BY, la première ligne n'est pas déterministe.

Pour déterminer si l'affectation a retourné au moins une ligne, utilisez la variable FOUND spéciale.

```
SELECT INTO customer_rec * FROM cust WHERE custname = lname;
IF NOT FOUND THEN
  RAISE EXCEPTION 'employee % not found', lname;
END IF;
```

Pour tester si un résultat d'enregistrement est null, vous pouvez utiliser l'opérateur conditionnel IS NULL. Aucune méthode ne permet de déterminer si des lignes supplémentaires peuvent avoir été ignorées. L'exemple suivant traite le cas où aucune ligne n'a été renvoyée.

```
CREATE OR REPLACE PROCEDURE select_into_null(return_webpage OUT varchar(256))
AS $$
DECLARE
  customer_rec RECORD;
BEGIN
  SELECT INTO customer_rec * FROM users WHERE user_id=3;
  IF customer_rec.webpage IS NULL THEN
    -- user entered no webpage, return "http://"
    return_webpage = 'http://';
  END IF;
END;
$$ LANGUAGE plpgsql;
```

## No-op
<a name="r_PLpgSQL-no-op"></a>

L'instruction no-op (`NULL;`) est une instruction d'espace réservé qui ne fait rien. Une instruction no-op peut indiquer qu'une branche d'une chaîne IF-THEN-ELSE est vide.

```
NULL;
```

## Instructions SQL dynamiques
<a name="r_PLpgSQL-dynamic-sql"></a>

Pour générer des commandes dynamiques qui peuvent impliquer différentes tables ou différents types de données chaque fois qu'elles sont exécutées à partir d'une procédure stockée PL/pgSQL, utilisez l'instruction `EXECUTE`.

```
EXECUTE command-string [ INTO target ];
```

Dans le code précédent, *command-string* est une expression de chaîne (de type texte) qui contient la commande à exécuter. Cette valeur *command-string* est envoyée au moteur SQL. Aucune substitution des variables PL/pgSQL n'est effectuée sur la chaîne de commande. Les valeurs des variables doivent être insérées dans la chaîne de commande lorsqu'elle est construite.

**Note**  
Vous ne pouvez pas utiliser les instructions COMMIT et ROLLBACK depuis SQL dynamique. Pour plus d'informations sur l'utilisation des instructions COMMIT et ROLLBACK dans une procédure stockée, consultez [Gestion des transactions](stored-procedure-transaction-management.md). 

Lorsque vous utilisez des commandes dynamiques, vous devez souvent traiter l'échappement des guillemets simples. Nous vous recommandons de placer le texte fixe entre guillemets dans le corps de votre fonction en utilisant des guillemets dollar. Les valeurs dynamiques à insérer dans une requête construite nécessitent un traitement spécial, car elles peuvent contenir elles-mêmes des guillemets. L'exemple suivant suppose des guillemets dollar pour la fonction dans son ensemble, afin que les guillemets n'aient pas besoin d'être doublés.

```
EXECUTE 'UPDATE tbl SET '
  || quote_ident(colname)
  || ' = '
  || quote_literal(newvalue)
  || ' WHERE key = '
  || quote_literal(keyvalue);
```

L'exemple précédent illustre les fonctions `quote_ident(text)` et `quote_literal(text)`. Cet exemple transmet des variables contenant des identifiants de colonne et de table à la fonction `quote_ident`. Il transmet également des variables qui contiennent des chaînes littérales dans la commande construite à la fonction `quote_literal`. Ces deux fonctions prennent les mesures appropriées pour retourner le texte d'entrée entre guillemets doubles ou simples respectivement, avec n'importe quels caractères spéciaux intégrés correctement échappés.

Les guillemets dollar sont utiles uniquement pour citer du texte fixe. N'écrivez pas l'exemple précédent dans le format suivant.

```
EXECUTE 'UPDATE tbl SET '
  || quote_ident(colname)
  || ' = $$'
  || newvalue
  || '$$ WHERE key = '
  || quote_literal(keyvalue);
```

En effet, cet exemple s'interrompt si le contenu de `newvalue` contient \$1\$1. Le même problème s'applique à tout autre délimiteur de guillemets dollar que vous pouvez choisir. Pour citer en toute sécurité du texte qui n'est pas connu à l'avance, utilisez la fonction `quote_literal`.

## Return
<a name="r_PLpgSQL-return"></a>

L'instruction RETURN retourne à l'appelant à partir d'une procédure stockée.

```
RETURN;
```

Vous en trouverez un exemple ci-dessous.

```
CREATE OR REPLACE PROCEDURE return_example(a int)
AS $$  
BEGIN
  FOR b in 1..10 LOOP
    IF b < a THEN
      RAISE INFO 'b = %', b;
    ELSE
      RETURN;
    END IF;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
```

## Conditions : IF
<a name="r_PLpgSQL-conditionals-if"></a>

L'instruction conditionnelle IF peut prendre les formes suivantes dans le langage PL/pgSQL qu'Amazon Redshift utilise :
+ IF ... THEN

  ```
  IF boolean-expression THEN
    statements
  END IF;
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  IF v_user_id <> 0 THEN
    UPDATE users SET email = v_email WHERE user_id = v_user_id;
  END IF;
  ```
+ IF ... THEN ... ELSE

  ```
  IF boolean-expression THEN
    statements
  ELSE
    statements
  END IF;
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  IF parentid IS NULL OR parentid = ''
  THEN
    return_name = fullname;
    RETURN;
  ELSE
    return_name = hp_true_filename(parentid) || '/' || fullname;
    RETURN;
  END IF;
  ```
+ IF ... THEN ... ELSIF ... THEN ... ELSE 

  Le mot clé ELSIF peut également être orthographié ELSEIF.

  ```
  IF boolean-expression THEN
    statements
  [ ELSIF boolean-expression THEN
    statements
  [ ELSIF boolean-expression THEN
    statements
      ...] ]
  [ ELSE
    statements ]
  END IF;
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  IF number = 0 THEN
    result := 'zero';
  ELSIF number > 0 THEN
    result := 'positive';
  ELSIF number < 0 THEN
    result := 'negative';
  ELSE
    -- the only other possibility is that number is null
    result := 'NULL';
  END IF;
  ```

## Conditions : CASE
<a name="r_PLpgSQL-conditionals-case"></a>

L'instruction conditionnelle CASE peut prendre les formes suivantes dans le langage PL/pgSQL qu'Amazon Redshift utilise :
+ CASE simple 

  ```
  CASE search-expression
  WHEN expression [, expression [ ... ]] THEN
    statements
  [ WHEN expression [, expression [ ... ]] THEN
    statements
    ... ]
  [ ELSE
    statements ]
  END CASE;
  ```

  Une instruction CASE simple fournit une exécution conditionnelle basée sur l'égalité des opérandes.

  La valeur *search-expression* est évaluée une fois et comparée successivement à chaque *expression* dans les clauses WHEN. Si une correspondance est trouvée, les instructions (*statements*) correspondantes s'exécutent, puis le contrôle passe à l'instruction qui suit END CASE. Les expressions WHEN suivantes ne sont pas évaluées. Si aucune correspondance n'est trouvée, les instructions (*statements*) ELSE s'exécutent. Toutefois, en l'absence de ELSE, une exception CASE\$1NOT\$1FOUND est levée.

  Vous en trouverez un exemple ci-dessous.

  ```
  CASE x
  WHEN 1, 2 THEN
    msg := 'one or two';
  ELSE
    msg := 'other value than one or two';
  END CASE;
  ```
+ CASE avec recherche 

  ```
  CASE
  WHEN boolean-expression THEN
    statements
  [ WHEN boolean-expression THEN
    statements
    ... ]
  [ ELSE
    statements ]
  END CASE;
  ```

  La forme de CASE avec recherche fournit une exécution conditionnelle basée sur la véracité des expressions booléennes. 

  Chaque expression booléenne (*boolean-expression*) de la clause WHEN est évaluée à son tour jusqu'à ce qu'une d'elles fournisse la valeur true. Les instructions correspondantes s'exécutent alors, puis le contrôle passe à l'instruction qui suit END CASE. Les *expressions* WHEN suivantes ne sont pas évaluées. Si aucun résultat true n'est trouvé, les instructions (*statements*) ELSE sont exécutées. Toutefois, en l'absence de ELSE, une exception CASE\$1NOT\$1FOUND est levée.

  Vous en trouverez un exemple ci-dessous.

  ```
  CASE
  WHEN x BETWEEN 0 AND 10 THEN
    msg := 'value is between zero and ten';
  WHEN x BETWEEN 11 AND 20 THEN
    msg := 'value is between eleven and twenty';
  END CASE;
  ```

## Boucles
<a name="r_PLpgSQL-loops"></a>

Les instructions de boucle peuvent prendre les formes suivantes dans le langage PL/pgSQL qu'Amazon Redshift utilise :
+ Boucle simple 

  ```
  [<<label>>]
  LOOP
    statements
  END LOOP [ label ];
  ```

  Une boucle simple définit une boucle sans condition qui se répète indéfiniment jusqu'à ce qu'elle soit terminée par une instruction EXIT ou RETURN. Une étiquette facultative peut être utilisée par les instructions EXIT et CONTINUE au sein de boucles imbriquées pour spécifier à quelle boucle l'instruction EXIT ou CONTINUE fait référence.

  Vous en trouverez un exemple ci-dessous.

  ```
  CREATE OR REPLACE PROCEDURE simple_loop()
  LANGUAGE plpgsql
  AS $$
  BEGIN
    <<simple_while>>
    LOOP
      RAISE INFO 'I am raised once';  
      EXIT simple_while;
      RAISE INFO 'I am not raised';
    END LOOP;
    RAISE INFO 'I am raised once as well';
  END;
  $$;
  ```
+ Quitter une boucle

  ```
  EXIT [ label ] [ WHEN expression ];
  ```

  Si aucune étiquette (*label*) n'est présente, la boucle la plus interne est interrompue et l'instruction qui suit le END LOOP s'exécute alors. Si une étiquette *label* est présente, ce doit être l'étiquette du niveau actuel ou d'un niveau extérieur de la boucle imbriquée ou du bloc. La boucle ou le bloc nommé est alors interrompu et le contrôle passe à l'instruction située après le END correspondant à la boucle ou au bloc.

  Si WHEN est spécifié, la sortie de la boucle se produit uniquement si l'*expression* est vraie. Dans le cas contraire, le contrôle passe à l'instruction qui suit EXIT.

  Vous pouvez utiliser EXIT avec tous les types de boucle. Il n'est pas limité à une utilisation avec des boucles sans condition.

  Lorsqu'il est utilisé avec un bloc BEGIN, EXIT passe le contrôle à l'instruction suivante située après la fin du bloc. Une étiquette doit être utilisée à cet effet. Une instruction EXIT sans étiquette n'est jamais considérée comme correspondant à un bloc BEGIN.

  Vous en trouverez un exemple ci-dessous.

  ```
  CREATE OR REPLACE PROCEDURE simple_loop_when(x int)
  LANGUAGE plpgsql
  AS $$
  DECLARE i INTEGER := 0;
  BEGIN
    <<simple_loop_when>>
    LOOP
      RAISE INFO 'i %', i;
      i := i + 1;
      EXIT simple_loop_when WHEN (i >= x);
    END LOOP;
  END;
  $$;
  ```
+ Continuer une boucle 

  ```
  CONTINUE [ label ] [ WHEN expression ];
  ```

  Si aucune étiquette (*label*) n'est fournie, l'exécution saute à l'itération suivante de la boucle la plus interne. Ainsi, toutes les instructions restantes du corps de la boucle sont ignorées. Le contrôle retourne ensuite à l'expression de contrôle de boucle (le cas échéant) pour déterminer si une autre itération de boucle est requise. Si une étiquette (*label*) est présente, elle spécifie l'étiquette de la boucle dont l'exécution est poursuivie.

  Si l'instruction WHEN est spécifiée, l'itération suivante de la boucle est commencée seulement si l'*expression* est vraie. Dans le cas contraire, le contrôle passe à l'instruction qui suit CONTINUE.

  Vous pouvez utiliser CONTINUE avec tous les types de boucle. Il n'est pas limité à une utilisation avec des boucles sans condition.

  ```
  CONTINUE mylabel;
  ```
+ Boucle WHILE 

  ```
  [<<label>>]
  WHILE expression LOOP
    statements
  END LOOP [ label ];
  ```

  L'instruction WHILE répète une série d'instructions tant que l'expression booléenne (*boolean-expression*) équivaut à true. L'expression est vérifiée juste avant chaque entrée dans le corps de la boucle.

  Vous en trouverez un exemple ci-dessous.

  ```
  WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- some computations here
  END LOOP;
  
  WHILE NOT done LOOP
    -- some computations here
  END LOOP;
  ```
+ Boucle FOR (variante avec entier) 

  ```
  [<<label>>]
  FOR name IN [ REVERSE ] expression .. expression LOOP
    statements
  END LOOP [ label ];
  ```

  La boucle FOR (variante avec entier) crée une boucle qui effectue des itérations sur une plage de valeurs entières. Le nom de la variable est défini automatiquement de type entier et existe uniquement au sein de la boucle. Toute définition existante du nom de la variable est ignorée au sein de la boucle. Les deux expressions qui donnent les limites inférieure et supérieure de la plage sont évaluées une fois à l'entrée de la boucle. Si vous spécifiez REVERSE, la valeur du pas est soustraite au lieu d'être ajoutée après chaque itération.

  Si la limite inférieure est supérieure à la limite supérieure (ou inférieure à celle-ci si REVERSE est utilisé), le corps de la boucle ne s'exécute pas. Aucune erreur n'est levée.

  Si une étiquette est attachée à la boucle FOR, Vous pouvez référencer la variable entière de boucle avec un nom complet en utilisant cette étiquette.

  Vous en trouverez un exemple ci-dessous.

  ```
  FOR i IN 1..10 LOOP
    -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
  END LOOP;
  
  FOR i IN REVERSE 10..1 LOOP
    -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop
  END LOOP;
  ```
+ Boucle FOR (variante avec ensemble de résultats) 

  ```
  [<<label>>]
  FOR target IN query LOOP
    statements
  END LOOP [ label ];
  ```

  La cible (*target*) est une variable d'enregistrement ou une liste séparée par des virgules de variables scalaires. La cible se voit successivement attribuer chaque ligne résultant de la requête et le corps de la boucle est exécuté pour chaque ligne.

  La boucle FOR (variante avec ensemble de résultats) permet à une procédure stockée d'itérer sur les résultats d'une requête et de manipuler ces données en conséquence.

  Vous en trouverez un exemple ci-dessous.

  ```
  CREATE PROCEDURE cs_refresh_reports() AS $$
  DECLARE
    reports RECORD;
  BEGIN
    FOR reports IN SELECT * FROM cs_reports ORDER BY sort_key LOOP
      -- Now "reports" has one record from cs_reports
      EXECUTE 'INSERT INTO ' || quote_ident(reports.report_name) || ' ' || reports.report_query;
    END LOOP;
    RETURN;
  END;
  $$ LANGUAGE plpgsql;
  ```
+ Boucle FOR avec instruction SQL dynamique

  ```
  [<<label>>]
  FOR record_or_row IN EXECUTE text_expression LOOP 
    statements
  END LOOP;
  ```

  Une boucle FOR avec instruction SQL dynamique permet à une procédure stockée d'itérer sur les résultats d'une requête dynamique et de manipuler ces données en conséquence.

  Vous en trouverez un exemple ci-dessous.

  ```
  CREATE OR REPLACE PROCEDURE for_loop_dynamic_sql(x int)
  LANGUAGE plpgsql
  AS $$
  DECLARE
    rec RECORD;
    query text;
  BEGIN
    query := 'SELECT * FROM tbl_dynamic_sql LIMIT ' || x;
    FOR rec IN EXECUTE query
    LOOP
      RAISE INFO 'a %', rec.a;
    END LOOP;
  END;
  $$;
  ```

## Curseurs
<a name="r_PLpgSQL-cursors"></a>

Plutôt que d'exécuter immédiatement une requête entière, vous pouvez configurer un curseur. Un *curseur *encapsule une requête et lit le résultat de la requête en traitant plusieurs lignes à la fois. Une des raisons de procéder ainsi est d'éviter un dépassement de mémoire lorsque le résultat contient un grand nombre de lignes. Une autre raison est de retourner une référence à un curseur qu'une procédure stockée a créé, ce qui permet à l'appelant de lire les lignes. Cette approche fournit une méthode efficace pour retourner de grands ensembles de lignes à partir de procédures stockées.

Pour utiliser les curseurs dans une procédure stockée NONATOMIC, placez la boucle du curseur entre START TRANSACTION...COMMIT.

Pour configurer un curseur, commencez par déclarer une variable de curseur. Tout accès aux curseurs dans PL/pgSQL passe par les variables de curseur, qui sont toujours du type de données spécial `refcursor`. Un type de données `refcursor` détient simplement une référence à un curseur. 

Vous pouvez créer une variable de curseur en la déclarant en tant que variable de type `refcursor`. Vous pouvez également utiliser la syntaxe de déclaration de curseur suivante.

```
name CURSOR [ ( arguments ) ] FOR query ;
```

Dans ce qui précède, *arguments* (le cas échéant) est une liste séparée par des virgules de paires nom-type de données (*name datatype*) dont chacune définit les noms à remplacer par des valeurs de paramètres dans la requête (*query*). Les valeurs réelles à substituer à ces noms sont spécifiées ultérieurement, à l'ouverture du curseur.

Des exemples sont fournis ci-dessous.

```
DECLARE
  curs1 refcursor;
  curs2 CURSOR FOR SELECT * FROM tenk1;
  curs3 CURSOR (key integer) IS SELECT * FROM tenk1 WHERE unique1 = key;
```

Ces trois variables ont le type de données `refcursor`, mais la première peut être utilisée avec une requête quelconque. Au contraire, une requête entièrement spécifiée est liée à la seconde, et une requête paramétrée est liée à la dernière. La valeur `key` est remplacée par une valeur de paramètre entière à l'ouverture du curseur. La variable `curs1` est dite *non liée*, car elle n'est liée à aucune requête particulière.

Avant de pouvoir utiliser un curseur pour récupérer des lignes, vous devez l'ouvrir. Le langage PL/pgSQL a trois formes d'instruction OPEN, dont deux utilisent des variables de curseur non liées, tandis que la troisième utilise une variable de curseur liée :
+ Ouverture pour sélection : la variable de curseur est ouverte et reçoit la requête spécifiée à exécuter. Le curseur ne doit pas être déjà ouvert. De plus, il doit avoir été déclaré en tant que curseur non lié (c'est-à-dire, en tant que simple variable `refcursor`). La requête SELECT est traitée de la même manière que les autres instructions SELECT dans le langage PL/pgSQL. 

  ```
  OPEN cursor_name FOR SELECT ...;                     
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;    
  ```
+ Ouverture pour exécution : la variable de curseur est ouverte et reçoit la requête spécifiée à exécuter. Le curseur ne doit pas être déjà ouvert. De plus, il doit avoir été déclaré en tant que curseur non lié (c'est-à-dire, en tant que simple variable `refcursor`). La requête est spécifiée en tant qu'expression de chaîne de la même manière que dans la commande EXECUTE. Cette approche est flexible et permet à la requête de varier d'une exécution à l'autre.

  ```
  OPEN cursor_name FOR EXECUTE query_string;
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);
  ```
+ Ouverture d'un curseur lié : cette forme d'instruction OPEN est utilisée pour ouvrir une variable de curseur à laquelle sa requête a été liée quand elle a été déclarée. Le curseur ne doit pas être déjà ouvert. La liste des expressions des valeurs d'argument réelles doit apparaître si et seulement si le curseur a été déclaré comme acceptant des arguments. Ces valeurs sont substituées dans la requête. 

  ```
  OPEN bound_cursor_name [ ( argument_values ) ];
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  OPEN curs2;
  OPEN curs3(42);
  ```

Une fois qu'un curseur a été ouvert, vous pouvez l'utiliser à l'aide des instructions décrites ci-après. Ces instructions ne sont pas tenues de figurer dans la procédure stockée qui a ouvert le curseur. Vous pouvez retourner une valeur `refcursor` à partir d'une procédure stockée et laisser l'appelant opérer sur le curseur. Tous les portails sont implicitement fermés à la fin de la transaction. Par conséquent, vous pouvez utiliser une valeur `refcursor` pour référencer un curseur ouvert seulement jusqu'à la fin de la transaction.
+ L'instruction FETCH extrait la ligne suivante du curseur dans une cible. Cette cible peut être une variable de ligne, une variable d'enregistrement ou une liste séparée par des virgules de variables simples, comme avec SELECT INTO. Comme avec SELECT INTO, vous pouvez vérifier la variable spéciale FOUND pour voir si une ligne a été obtenue.

  ```
  FETCH cursor INTO target;
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  FETCH curs1 INTO rowvar;
  ```
+ L'instruction CLOSE ferme le portail sous-jacent à un curseur ouvert. Vous pouvez utiliser cette instruction pour libérer des ressources avant la fin de la transaction. Vous pouvez également utiliser cette instruction pour libérer la variable de curseur, qui pourra ainsi être de nouveau ouverte.

  ```
  CLOSE cursor;
  ```

  Vous en trouverez un exemple ci-dessous.

  ```
  CLOSE curs1;
  ```

## RAISE
<a name="r_PLpgSQL-messages-errors"></a>

Utilisez l'instruction `RAISE level` pour signaler les messages et les erreurs.

```
RAISE level 'format' [, variable [, ...]];
```

Les niveaux possibles sont NOTICE, INFO, LOG, WARNING et EXCEPTION. EXCEPTION lève une erreur, ce qui entraîne normalement l'annulation de la transaction en cours. Les autres niveaux génèrent seulement des messages de différents niveaux de priorité. 

Dans la chaîne de format, % est remplacé par la représentation de chaîne de l'argument facultatif suivant. Écrivez %% pour obtenir un % littéral. Actuellement, les arguments facultatifs doivent être des variables simples et non pas des expressions, et le format doit être un littéral de chaîne simple.

Dans l'exemple suivant, la valeur de `v_job_id` remplace le % dans la chaîne.

```
RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;
```

Utilisez l'instruction `RAISE` pour relancer l'exception capturée par un bloc de gestion des exceptions. Cette déclaration n'est valable que dans les blocs de gestion des exceptions des procédures stockées en mode NONATOMIC.

```
RAISE;
```

## Contrôle de transaction
<a name="r_PLpgSQL-transaction-control"></a>

Vous pouvez utiliser les instructions de contrôle de transaction du langage PL/pgSQL qu'Amazon Redshift utilise. Pour plus d'informations sur l'utilisation des instructions COMMIT, ROLLBACK et TRUNCATE dans une procédure stockée, consultez [Gestion des transactions](stored-procedure-transaction-management.md). 

Dans les procédures stockées en mode NONATOMIC, utilisez `START TRANSACTION` pour démarrer un bloc de transaction.

```
START TRANSACTION;
```

**Note**  
L'instruction PL/pgSQL START TRANSACTION est différente de la commande SQL START TRANSACTION pour les raisons suivantes :  
Dans les procédures stockées, START TRANSACTION n'est pas synonyme de BEGIN.
L'instruction PL/pgSQL ne prend pas en charge les mots-clés facultatifs de niveau d'isolement et d'autorisations d'accès.