Cett section explique comment vous pouvez traiter des conditions d'exception et des avertissements dans un programme SQL embarqué. Il y a deux fonctionnalités non-exclusives pour cela.
WHENEVER
.
sqlca
.
Une méthode simple pour intercepter des erreurs et des avertissements est de paramétrer des actions spécifiques à exécuter dès qu'une condition particulière se produit. En général :
EXEC SQL WHENEVERcondition
action
;
condition
peut être un des éléments suivants :
SQLERROR
#L'action spécifiée est appelée dès qu'une erreur se produit durant l'exécution d'un ordre SQL.
SQLWARNING
#L'action spécifiée est appelée dès qu'un avertissement se produit durant l'exécution d'un ordre SQL.
NOT FOUND
#L'action spécifiée est appelée dès qu'un ordre SQL récupère ou affecte zéro enregistrement. (Cette condition n'est pas une erreur, mais vous pourriez être intéressé par un traitement spécial dans ce cas).
action
peut être un des éléments suivants:
CONTINUE
#Cela signifie en fait que la condition est ignorée. C'est le comportement par défaut.
GOTO label
GO TO label
#
Sauter au label spécifié (en utilisant un ordre goto
C).
SQLPRINT
#Affiche un message vers la sortie standard. C'est utile pour des programmes simples ou durant le prototypage. Le détail du message ne peut pas être configuré.
STOP
#
Appelle exit(1)
, ce qui mettra fin au
programme.
DO BREAK
#
Exécuter l'ordre C break
. Cela ne devrait
être utilisé que dans des boucles ou des ordres switch
.
DO CONTINUE
#
Exécute l'instruction C continue
. Ceci doit
seulement être utilisé dans les instructions de boucle. Son exécution
fait que le flot de contrôle est renvoyé au sommet de la boucle.
CALL name
(args
)
DO name
(args
)
#
Appelle la fonction C spécifiée avec les arguments spécifiés. (Son
utilisation est différente des instructions CALL
et
DO
dans la grammaire habituelle de PostgreSQL.)
Le standard SQL ne fournit que les actions
CONTINUE
et GOTO
(and
GO TO
).
Voici un exemple de ce que pourriez vouloir utiliser dans un programme simple. Il affichera un message quand un avertissement se produit et tuera le programme quand une erreur se produit:
EXEC SQL WHENEVER SQLWARNING SQLPRINT; EXEC SQL WHENEVER SQLERROR STOP;
L'ordre EXEC SQL WHENEVER
est une directive
du préprocesseur SQL, pas un ordre SQL. L'action sur erreur ou
avertissement qu'il met en place s'applique à tous les ordres SQL
embarqués qui apparaissent après le point où le gestionnaire est
mis en place, sauf si une autre action a été mise en place pour la
même condition entre le premier EXEC SQL WHENEVER
et l'ordre SQL entrainant la condition, quel que soit le déroulement
du programme C. Par conséquent, aucun des extraits des deux
programmes C suivants n'aura l'effet escompté :
/* * WRONG */ int main(int argc, char *argv[]) { ... if (verbose) { EXEC SQL WHENEVER SQLWARNING SQLPRINT; } ... EXEC SQL SELECT ...; ... }
/* * WRONG */ int main(int argc, char *argv[]) { ... set_error_handler(); ... EXEC SQL SELECT ...; ... } static void set_error_handler(void) { EXEC SQL WHENEVER SQLERROR STOP; }
Pour une gestion plus approfondie des erreurs, l'interface SQL
embarquée fournit une variable globale appelée sqlca
(SQL communication area, ou zone de communication SQL)
qui a la structure suivante :
struct { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[SQLERRMC_LEN]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; char sqlwarn[8]; char sqlstate[5]; } sqlca;
(Dans un programme multi-threadé, chaque thread récupère automatiquement
sa propre copie de sqlca
. Ce fonctionnement est similaire
à celui de la variable C globale errno
.)
errno
.)
sqlca
couvre à la fois les avertissements
et les erreurs. Si plusieurs avertissements ou erreurs se produisent
durant l'exécution d'un ordre, alors sqlca
ne contiendra d'informations que sur le dernier.
Si aucune erreur ne s'est produite durant le dernier ordre
SQL,
sqlca.sqlcode
vaudra 0
sqlca.sqlstate
vaudra
"00000"
. Si un avertissement ou erreur s'est produit, alors
sqlca.sqlcode
sera négatif
sqlca.sqlstate
sera différent de
"00000"
. Une valeur positive de
sqlca.sqlcode
indique une condition sans gravité
comme le fait que la dernière requête ait retourné zéro enregistrements.
sqlcode
et sqlstate
sont deux
différents schémas de code d'erreur ; les détails sont fournis plus bas.
Si le dernier ordre SQL a réussi, alors
sqlca.sqlerrd[1]
contient l'OID de la ligne traitée,
si applicable, et
sqlca.sqlerrd[2]
contient le nombre d'enregistrements
traités ou retournés, si applicable à la commande.
En cas d'erreur ou d'avertissement,
sqlca.sqlerrm.sqlerrmc
contiendra une chaine
qui décrira une erreur. Le champ
sqlca.sqlerrm.sqlerrml
contiendra la longueur
du message d'erreur qui est stocké dans
sqlca.sqlerrm.sqlerrmc
(le résultat de
strlen()
, par réellement intéressant pour
un programmeur C). Notez que certains messages sont trop longs pour
tenir dans le tableau de taille fixe sqlerrmc
;
ils seront tronqués.
En cas d'avertissement, sqlca.sqlwarn[2]
est positionné
à W
. (Dans tous les autres cas, il est positionné
à quelque chose de différent de W
.) Si
sqlca.sqlwarn[1]
est positionné à
W
, alors une valeur a été tronquée quand elle a été
stockée dans une variable hôte. sqlca.sqlwarn[0]
est
positionné à W
si n'importe lequel des autres éléments
est positionné pour indiquer un avertissement.
Les champs sqlcaid
,
sqlabc
,
sqlerrp
, et les éléments restants de
sqlerrd
et
sqlwarn
ne contiennent pour le moment
aucune information utile.
La structure sqlca
n'est pas définie dans le
standard SQL, mais est implémentée dans plusieurs autres systèmes
de base de données. Les définitions sont similaires dans leur
principe, mais si vous voulez écrire des applications portables,
vous devriez étudier les différentes implémentations de façon
attentive.
Voici un exemple qui combine l'utilisation de WHENEVER
et de sqlca
, en affichant le contenu de
sqlca
quand une erreur se produit. Cela pourrait être
utile pour déboguer ou prototyper des applications, avant d'installer
un gestionnaire d'erreurs plus « user-friendly ».
EXEC SQL WHENEVER SQLERROR CALL print_sqlca(); void print_sqlca() { fprintf(stderr, "==== sqlca ====\n"); fprintf(stderr, "sqlcode: %ld\n", sqlca.sqlcode); fprintf(stderr, "sqlerrm.sqlerrml: %d\n", sqlca.sqlerrm.sqlerrml); fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc); fprintf(stderr, "sqlerrd: %ld %ld %ld %ld %ld %ld\n", sqlca.sqlerrd[0],sqlca.sqlerrd[1],sqlca.sqlerrd[2], sqlca.sqlerrd[3],sqlca.sqlerrd[4],sqlca.sqlerrd[5]); fprintf(stderr, "sqlwarn: %d %d %d %d %d %d %d %d\n", sqlca.sqlwarn[0], sqlca.sqlwarn[1], sqlca.sqlwarn[2], sqlca.sqlwarn[3], sqlca.sqlwarn[4], sqlca.sqlwarn[5], sqlca.sqlwarn[6], sqlca.sqlwarn[7]); fprintf(stderr, "sqlstate: %5s\n", sqlca.sqlstate); fprintf(stderr, "===============\n"); }
Le résultat pourrait ressembler à ce qui suit (ici une erreur due à un nom de table mal saisi) :
==== sqlca ==== sqlcode: -400 sqlerrm.sqlerrml: 49 sqlerrm.sqlerrmc: relation "pg_databasep" does not exist on line 38 sqlerrd: 0 0 0 0 0 0 sqlwarn: 0 0 0 0 0 0 0 0 sqlstate: 42P01 ===============
SQLSTATE
contre SQLCODE
#
Les champs sqlca.sqlstate
et
sqlca.sqlcode
sont deux schémas qui fournissent
des codes d'erreurs. Les deux sont dérivés du standard SQL, mais
SQLCODE
a été marqué comme déprécié dans l'édition
SQL-92 du standard, et a été supprimé des éditions suivantes.
Par conséquent, les nouvelles applications ont fortement intérêt
à utiliser SQLSTATE
.
SQLSTATE
est un tableau de cinq caractères.
Les cinq caractères contiennent des chiffres ou des lettres en
majuscule qui représentent les codes des différentes conditions
d'erreur et d'avertissement.
SQLSTATE
a un schéma hiérarchique: les deux premiers
caractères indiquent la classe générique de la condition, les trois
caractères suivants indiquent la sous-classe de la condition générique.
Un état de succès est indiqué par le code 00000
.
Les codes SQLSTATE
sont pour la plupart définis
dans le standard SQL. Le serveur PostgreSQL
supporte nativement les codes d'erreur SQLSTATE
;
par conséquent, un haut niveau de cohérence entre toutes les applications
peut être obtenu en utilisant ce schéma de codes d'erreur.
Pour plus d'informations voyez Annexe A.
SQLCODE
, le schéma d'erreurs déprécié, est un
entier simple. Une valeur de 0 indique le succès, une valeur
positive indique un succès avec des informations supplémentaires,
une valeur négative indique une erreur. Le standard SQL ne définit
que la valeur positive +100, qui indique que l'ordre précédent a
retourné ou affecté zéro enregistrement, et aucune valeur négative
spécifique. par conséquent, ce schéma ne fournit qu'une piètre
portabilité et n'a pas de hiérarchie de code d'erreurs. Historiquement,
le processeur de SQL embarqué de PostgreSQL
a assigné des valeurs spécifiques de SQLCODE
pour
son utilisation propre, qui sont listées ci-dessous avec leur valeur
numérique et leur nom symbolique. Rappelez vous qu'ils ne sont pas
portables vers d'autres implémentations SQL.
Pour simplifier le portage des applications vers le schéma
SQLSTATE
, les valeurs SQLSTATE
sont aussi listées. Il n'y a pas, toutefois, de correspondance un à un
ou un à plusieurs entre les deux schémas (c'est en fait du plusieurs
à plusieurs), vous devriez donc consulter la liste globale
SQLSTATE
dans Annexe A
au cas par cas.
Voici les valeurs de SQLCODE
assignées :
ECPG_NO_ERROR
) #Indique pas d'erreur. (SQLSTATE 00000)
ECPG_NOT_FOUND
) #C'est un état sans danger indiquant que la dernière commande a récupéré ou traité zéro enregistrements, ou que vous êtes au bout du curseur. (SQLSTATE 02000)
Quand vous bouclez sur un curseur, vous pourriez utiliser ce code comme façon de détecter quand arrêter la boucle, comme ceci:
while (1) { EXEC SQL FETCH ... ; if (sqlca.sqlcode == ECPG_NOT_FOUND) break; }
Mais WHENEVER NOT FOUND DO BREAK
fait en
fait cela en interne, il n'y a donc habituellement aucun avantage
à écrire ceci de façon explicite.
ECPG_OUT_OF_MEMORY
) #
Indique que votre mémoire virtuelle est épuisée. La valeur
numérique est définie comme -ENOMEM
.
(SQLSTATE YE001)
ECPG_UNSUPPORTED
) #Indique que le préprocesseur a généré quelque chose que la librairie ne connait pas. Peut-être êtes vous en train d'utiliser des versions incompatibles du préprocesseur et de la librairie. (SQLSTATE YE002)
ECPG_TOO_MANY_ARGUMENTS
) #Cela signifie que la commande a spécifié plus de variables hôte que la commande n'en attendait. (SQLSTATE 07001 or 07002)
ECPG_TOO_FEW_ARGUMENTS
) #Cela signifie que la commande a spécifié moins de variables hôtes que la commande n'en attendait. (SQLSTATE 07001 or 07002)
ECPG_TOO_MANY_MATCHES
) #Cela signifie que la requête a retourné pluiseurs enregistrements mais que l'ordre n'était capable d'en recevoir qu'un (par exemple parce que les variables spécifiées ne sont pas des tableaux. (SQLSTATE 21000)
ECPG_INT_FORMAT
) #
La variable hôte est du type int
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un int
.
La librairie utilise strtol()
pour cette
conversion. (SQLSTATE 42804).
ECPG_UINT_FORMAT
) #
La variable hôte est du type unsigned int
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un unsigned int
.
La librairie utilise strtoul()
pour cette
conversion. (SQLSTATE 42804).
ECPG_FLOAT_FORMAT
) #
La variable hôte est du type float
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un float
.
La librairie utilise strtod()
pour cette
conversion. (SQLSTATE 42804).
ECPG_NUMERIC_FORMAT
) #
La variable hôte est du type numeric
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un numeric
.
(SQLSTATE 42804).
ECPG_INTERVAL_FORMAT
) #
La variable hôte est du type interval
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un interval
.
(SQLSTATE 42804).
ECPG_DATE_FORMAT
) #
La variable hôte est du type date
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un date
.
(SQLSTATE 42804).
ECPG_TIMESTAMP_FORMAT
) #
La variable hôte est du type timestamp
et la donnée dans
la base de données est d'un type différent et contient une valeur
qui ne peut pas être interprétée comme un timestamp
.
(SQLSTATE 42804).
ECPG_CONVERT_BOOL
) #
Cela signifie que la variable hôte est de type bool
et que la donnée dans la base n'est ni 't'
ni
'f'
. (SQLSTATE 42804)
ECPG_EMPTY
) #L'ordre envoyé au serveur PostgreSQL était vide. (Cela ne peut normalement pas arriver dans un programme SQL embarqué, cela pourrait donc laisser supposer une erreur interne.) (SQLSTATE YE002)
ECPG_MISSING_INDICATOR
) #Une valeur null a été retournée et aucune variable d'indicateur null n'a été fournie. (SQLSTATE 22002)
ECPG_NO_ARRAY
) #Une variable ordinaire a été utilisée à un endroit qui nécessite un tableau. (SQLSTATE 42804)
ECPG_DATA_NOT_ARRAY
) #La base a retourné une variable ordinaire à un endroir qui nécessite une variable de tableau. (SQLSTATE 42804)
ECPG_ARRAY_INSERT
) #La valeur n'a pas pu être insérée dans le tableau. (SQLSTATE 42804)
ECPG_NO_CONN
) #Le programme a essayé d'utiliser une connexion qui n'existe pas. (SQLSTATE 08003)
ECPG_NOT_CONN
) #Le programme a essayé d'utiliser une connexion qui existe mais n'est pas ouverte. (C'est une erreur interne.) (SQLSTATE YE002)
ECPG_INVALID_STMT
) #L'ordre que vous essayez d'exécuter n'a pas été préparé. (SQLSTATE 26000)
ECPG_INFORMIX_DUPLICATE_KEY
) #Erreur de clé en doublon, violation de contrainte unique (mode de compatibilité Informix). (SQLSTATE 23505)
ECPG_UNKNOWN_DESCRIPTOR
) #Le descripteur spécifié n'a pas été trouvé. L'ordre que vous essayez d'utiliser n'a pas été préparé. (SQLSTATE 33000)
ECPG_INVALID_DESCRIPTOR_INDEX
) #L'index de descripteur spécifié était hors de portée. (SQLSTATE 07009)
ECPG_UNKNOWN_DESCRIPTOR_ITEM
) #Un objet de descripteur invalide a été demandé. (C'est une erreur interne.) (SQLSTATE YE002)
ECPG_VAR_NOT_NUMERIC
) #Durant l'exécution d'un ordre dynamique, la base a retourné une valeur numeric et la variable hôte n'était pas numeric. (SQLSTATE 07006)
ECPG_VAR_NOT_CHAR
) #Durant l'exécution d'un ordre dynamique, la base a retourné une valeur non numeric et la variable hôte était numeric. (SQLSTATE 07006)
ECPG_INFORMIX_SUBSELECT_NOT_ONE
) #Un résultat de la sous-requête n'était pas un enregistrement seul (mode de compatibilité Informix). (SQLSTATE 21000)
ECPG_PGSQL
) #Une erreur causée par le serveur PostgreSQL. Le message contient le message d'erreur du serveur PostgreSQL.
ECPG_TRANS
) #Le serveur PostgreSQL a signalé que nous ne pouvons pas démarrer, valider ou annuler la transaction. (SQLSTATE 08007)
ECPG_CONNECT
) #La tentative de connexion à la base n'a pas réussi. (SQLSTATE 08001)
ECPG_DUPLICATE_KEY
) #Erreur de clé dupliquée, violation d'une contrainte unique. (SQLSTATE 23505)
ECPG_SUBSELECT_NOT_ONE
) #Un résultat de la sous-requête n'est pas un enregistrement unique. (SQLSTATE 21000)
ECPG_WARNING_UNKNOWN_PORTAL
) #Un nom de curseur invalide a été spécifié. (SQLSTATE 34000)
ECPG_WARNING_IN_TRANSACTION
) #Transaction en cours. (SQLSTATE 25001)
ECPG_WARNING_NO_TRANSACTION
) #Il n'y a pas de transaction active (en cours). (SQLSTATE 25P01)
ECPG_WARNING_PORTAL_EXISTS
) #Un nom de curseur existant a été spécifié. (SQLSTATE 42P03)