Écriture de procédures stockées en JavaScript¶
Cette rubrique explique comment écrire du code JavaScript pour une procédure stockée.
Note
Pour créer et appeler une procédure anonyme, utilisez CALL (avec procédure anonyme). La création et l’appel d’une procédure anonyme ne nécessitent pas un rôle avec des privilèges de schéma CREATE PROCEDURE.
Vous pouvez capturer des données d’enregistrement et de trace pendant l’exécution du code de votre gestionnaire. Pour plus d’informations, reportez-vous à Journalisation, traçage et métriques.
Comprendre l’API JavaScript¶
Cette API JavaScript est similaire aux APIs des connecteurs et des pilotes Snowflake (Node.js, JDBC, Python, etc.), sans être identique.
L’API vous permet d’effectuer des opérations telles que :
Exécuter une instruction SQL.
Récupérer les résultats d’une requête (c’est-à-dire un jeu de résultats).
Récupérer les métadonnées relatives au jeu de résultats (nombre de colonnes, types de données des colonnes, etc.).
Ces opérations sont effectuées en appelant des méthodes sur les objets suivants :
snowflake, qui dispose de méthodes pour créer un objetStatementet exécuter une commande SQL.Statementqui vous aide à exécuter des instructions préparées et à accéder aux métadonnées de ces instructions préparées, et vous permet de récupérer un objet ResultSet.ResultSet, qui contient les résultats d’une requête (par exemple, les lignes de données extraites pour une instruction SELECT).SfDate, qui est une extension de JavaScript Date (avec des méthodes supplémentaires) et sert de type de retour pour les types de données SQL de Snowflake TIMESTAMP_LTZ, TIMESTAMP_NTZ et TIMESTAMP_TZ.
Ces objets sont décrits en détail dans API de procédures stockées JavaScript.
Une procédure stockée typique contient un code similaire au pseudo-code suivant :
Ce code utilise un objet nommé snowflake, qui est un objet spécial qui existe sans être déclaré. L’objet est fourni dans le contexte de chaque procédure stockée et expose l’API pour vous permettre d’interagir avec le serveur.
Les autres variables (par exemple statement1) sont créées avec des instructions JavaScript var. Par exemple :
Comme illustré dans l’exemple de code ci-dessus, l’objet snowflake vous permet de créer un objet Statement en appelant l’une des méthodes dans l’API.
Voici un exemple qui récupère un ResultSet et le répète à travers celui-ci :
La section Exemples (à la fin de cette rubrique) fournit des exemples supplémentaires permettant d’exercer chacun des objets, ainsi que de nombreuses méthodes, dans l’API JavaScript de procédure stockée.
Mappage de type de données SQL et JavaScript¶
Lorsque vous appelez, utilisez et récupérez des valeurs à partir de procédures stockées, vous devez souvent convertir un type de données SQL Snowflake en un type de données JavaScript, ou inversement.
La conversion SQL > JavaScript peut avoir lieu dans les situations suivantes :
Appel d’une procédure stockée avec un argument. L’argument est un type de données SQL ; lorsqu’il est stocké dans une variable JavaScript de la procédure stockée, il doit être converti.
Lors de la récupération d’une valeur d’un objet ResultSet dans une variable JavaScript. Le ResultSet contient la valeur sous la forme d’un type de données SQL et la variable JavaScript doit stocker la valeur sous l’un des types de données JavaScript.
La conversion JavaScript > SQL peut avoir lieu dans les situations suivantes :
Renvoi d’une valeur depuis la procédure stockée. L’instruction
returncontient généralement une variable JavaScript qui doit être convertie en un type de données SQL.Lors de la construction dynamique d’une instruction SQL utilisant une valeur dans une variable JavaScript.
Lors de la liaison de la valeur d’une variable JavaScript à une instruction préparée.
Pour plus d’informations sur la façon dont Snowflake fait correspondre les types de données JavaScript et SQL, voir Mappages de type de données SQL-JavaScript.
Conseils généraux¶
Continuation de la facturation¶
Les instructions SQL peuvent être assez longues et il n’est pas toujours pratique de les insérer sur une seule ligne. JavaScript traite une nouvelle ligne comme la fin d’une instruction. Si vous souhaitez fractionner une instruction longue SQL sur plusieurs lignes, vous pouvez utiliser les techniques habituelles JavaScript pour gérer les chaînes longues, notamment :
Mettre une barre oblique inverse (caractère de continuation de ligne) immédiatement avant la fin de la ligne. Par exemple :
Utiliser des guillemets simples (guillemets simples) plutôt que des guillemets doubles autour de la chaîne. Par exemple :
Accumuler la chaîne. Par exemple :
Considérations relatives aux procédures stockées JavaScript¶
Plage de nombres JavaScript¶
La plage pour les nombres dont la précision est intacte est de
-(2^53 -1)
dans
(2^53 -1)
La plage de valeurs valides dans les types de données Snowflake NUMBER(p, s) et DOUBLE est plus grande. La récupération d’une valeur de Snowflake et son stockage dans une variable numérique JavaScript peut entraîner une perte de précision. Par exemple :
Les deux premières colonnes doivent correspondre, et la troisième doit contenir 0.0.
Le problème s’applique aux fonctions JavaScript définies par l’utilisateur (UDFs) et aux procédures stockées.
Si vous rencontrez le problème dans les procédures stockées avec getColumnValue(), vous pouvez l’éviter en récupérant une valeur sous forme de chaîne, par exemple avec :
Vous pouvez ensuite renvoyer la chaîne à partir de la procédure stockée et la convertir en un type de données numérique dans SQL.
Traitement des erreurs JavaScript¶
Dans la mesure où une procédure stockée est écrite en JavaScript, elle peut utiliser la syntaxe try/catch de JavaScript.
La procédure stockée peut générer une exception prédéfinie ou une exception personnalisée. Un exemple simple de levée d’une exception personnalisée est disponible ici.
Vous pouvez exécuter vos instructions SQL dans un bloc d’essai. Si une erreur se produit, votre bloc de saisie peut alors annuler toutes les instructions (si vous les mettez dans une transaction). La section Exemples contient un exemple d”annulation d’une transaction dans une procédure stockée.
Restrictions sur les procédures stockées¶
Les procédures stockées présentent les restrictions suivantes :
Le code JavaScript ne peut pas appeler la fonction JavaScript
eval().Les procédures stockées JavaScript acceptent l’accès à la bibliothèque JavaScript standard. Notez que ceci exclut de nombreux objets et méthodes généralement fournis par les navigateurs. Il n’existe aucun mécanisme pour importer, inclure ou appeler des bibliothèques supplémentaires. Autoriser les bibliothèques tierces pourrait créer des failles de sécurité.
Le code JavaScript est exécuté dans un moteur restreint, empêchant les appels système du contexte JavaScript (par exemple, pas d’accès au réseau et au disque) et limitant les ressources système disponibles pour le moteur, en particulier la mémoire.
Sensibilité à la casse dans les arguments JavaScript¶
Les noms d’arguments ne sont pas sensibles à la casse dans la partie SQL du code de procédure stockée, mais le sont dans la partie JavaScript.
Pour les procédures stockées (et les UDFs) qui utilisent JavaScript, les identificateurs (tels que les noms d’arguments) dans la partie SQL de l’instruction sont automatiquement convertis en majuscules (sauf si vous délimitez l’identificateur par des guillemets), tandis que les noms d’arguments dans la partie JavaScript seront laissés dans leur casse d’origine. Cela peut entraîner l’échec de votre procédure stockée sans retourner de message d’erreur explicite, car les arguments ne sont pas vus.
Voici un exemple de procédure stockée dans laquelle le nom d’un argument dans le code JavaScript ne correspond pas à celui de l’argument dans le code SQL simplement parce que la casse sera différente :
Dans l’exemple ci-dessous, la première instruction d’affectation est incorrecte, car le nom argument1 est en minuscule.
L’utilisation d’identificateurs en majuscules (en particulier de noms d’arguments) de manière cohérente dans vos instructions SQL et votre code JavaScript tend à réduire les erreurs silencieuses.
Délimiteurs JavaScript¶
La partie JavaScript du code de procédure stockée doit être encadrée par des guillemets simples ' ou par des signes à deux dollars $$ .
L’utilisation de $$ facilite la gestion du code JavaScript contenant des guillemets simples sans « échapper » ces guillemets.
Surcharge de noms de procédures stockées¶
Pour plus d’informations sur la surcharge et les conventions de dénomination, voir Nommage et surcharge de procédures et d’UDFs.
Variables de liaison¶
La liaison d’une variable à une instruction SQL vous permet d’utiliser la valeur de la variable dans l’instruction.
Vous pouvez lier des valeurs NULL ainsi que des valeurs non NULL.
Le type de données de la variable doit convenir à l’utilisation de la valeur dans l’instruction SQL . Actuellement, seules les variables JavaScript de type nombre, chaîne et SfDate peuvent être liées. (Pour plus d’informations sur le mappage entre les types de données SQL et les types de données JavaScript, voir Mappage de type de données SQL et JavaScript .)
Voici un bref exemple de liaison :
Voici un exemple plus complet. Cet exemple lie des informations TIMESTAMP (horodatage). La liaison directe des données SQL TIMESTAMP n’étant pas prise en charge, cet exemple transmet l’horodatage sous la forme d’un VARCHAR, puis la lie à l’instruction. Notez que l’instruction SQL elle-même convertit le VARCHAR en TIMESTAMP en appelant la fonction TO_TIMESTAMP() :
Cette fonction simple renvoie TRUE si l’horodatage spécifié est antérieur à maintenant et FALSE dans le cas contraire.
Cela montre comment lier un VARCHAR, un TIMESTAMP_LTZ et d’autres types de données à une instruction INSERT . Le TIMESTAMP_LTZ lie une variable SfDate créée dans la procédure stockée.
Créez une table.
Créez une procédure stockée. Cette procédure accepte un
VARCHARet convertit le VARCHAR enTIMESTAMP_LTZà l’aide de SQL. La procédure récupère ensuite la valeur convertie d’un ResultSet. La valeur est stockée dans une variable JavaScript de type SfDate. La procédure stockée lie ensuite leVARCHARd’origine et leTIMESTAMP_LTZà une instructionINSERT. Cela illustre également la liaison de données numériques JavaScript.Appelez la procédure.
Vérifiez que la ligne a été insérée.
Pour des exemples supplémentaires de liaison de données dans JavaScript, voir Paramètres d’instruction de liaison.
Exigences en matière de code¶
Le code JavaScript doit définir un seul objet JavaScript littéral pour que la procédure stockée soit valide.
Si le code JavaScript ne répond pas à cette exigence, la procédure stockée sera créée. Cependant, elle échouera à l’appel.
Taille du code¶
Snowflake limite la taille maximale du code source JavaScript dans le corps d’une procédure stockée JavaScript. Snowflake recommande de limiter la taille à 100 KB. (Le code est stocké sous une forme compressée et la limite exacte dépend de la capacité de compression du code).
Erreurs d’exécution¶
La plupart des erreurs dans les procédures stockées s’affichent au moment de l’exécution, car le code JavaScript est interprété au moment de l’exécution de la procédure stockée plutôt qu’à la création de la procédure stockée.
Prise en charge de SQL dynamique¶
Les procédures stockées peuvent être utilisées pour construire dynamiquement des instructions SQL. Par exemple, vous pouvez créer une chaîne de commande SQL contenant un mélange d’entrées SQLpréconfigurées et d’utilisateurs (par exemple, le numéro de compte de l’utilisateur).
Pour des exemples, voir Création dynamique d’une instruction SQL et la section Exemples.
API synchrone¶
L’API pour les procédures stockées Snowflake est synchrone. Dans une procédure stockée, vous ne pouvez exécuter qu’un seul thread à la fois.
Notez que cela diffère de la règle pour JavaScript qui s’exécute avec le connecteur Node.js, qui vous permet d’exécuter des threads asynchrones.
Exemples¶
Exemples de base¶
L’exemple suivant montre la syntaxe de base de la création et de l’appel d’une procédure stockée. Il n’exécute aucun code SQL ni code de procédure. Cependant, il fournit un point de départ pour des exemples plus réalistes plus tard :
Notez que le délimiteur
$$marque le début et la fin du code JavaScript.Appelez maintenant la procédure que vous venez de créer :
L’exemple suivant montre comment exécuter une instruction SQL dans une procédure stockée :
Créez une table :
Créez une procédure stockée. Cela insère une ligne dans une table existante nommée
stproc_test_table1et renvoie la valeur « Réussie ». La valeur renvoyée n’est pas particulièrement utile du point de vue SQL, mais elle vous permet de renvoyer des informations sur le statut (par exemple, « Réussie » ou « Échouée ») à l’utilisateur.Appelez la procédure stockée :
Confirmez que la procédure stockée a inséré la ligne :
L’exemple suivant récupère un résultat :
Créez une procédure pour compter le nombre de lignes dans une table (équivalent à
select count(*) from table) :Demandez à la procédure stockée combien de lignes se trouvent dans la table :
Vérifiez indépendamment que vous avez le bon numéro :
Exemple de procédure stockée récursive¶
L’exemple suivant illustre une procédure stockée récursive de base, mais pas particulièrement réaliste :
Création dynamique d’une instruction SQL¶
L’exemple suivant montre comment créer dynamiquement une instruction SQL :
Note
Comme indiqué dans injection SQL (dans ce chapitre), veillez à vous protéger contre les attaques lorsque vous utilisez du SQL dynamique.
Créez la procédure stockée. Cette procédure vous permet de transmettre le nom d’une table et d’obtenir le nombre de lignes de cette table (équivalent à
select count(*) from table_name) :Appelez la procédure stockée :
Affichez les résultats de
select count(*)pour la même table :
Récupération des métadonnées du jeu de résultats¶
Cet exemple illustre la récupération d’une petite quantité de métadonnées à partir d’un ensemble de résultats :
Interception d’une erreur en utilisant Try/Catch¶
Cet exemple montre comment utiliser un bloc Try/Catch JavaScript pour intercepter une erreur dans une procédure stockée :
Créez la procédure stockée :
Appelez la procédure stockée. cela devrait renvoyer une erreur indiquant le numéro de l’erreur et d’autres informations :
L’exemple suivant montre la levée d’une exception personnalisée :
Créez la procédure stockée :
Appelez la procédure stockée avec des valeurs valides et non valides :
Utilisation des transactions dans les procédures stockées¶
L’exemple suivant englobe plusieurs instructions liées dans une transaction et utilise la fonction « try/catch » pour valider ou annuler. Le paramètre force_failure permet à l’appelant de choisir entre une exécution réussie et une erreur délibérée.
Consignation d’une erreur¶
Vous pouvez capturer des données de journal et de trace à partir du code du gestionnaire JavaScript en utilisant l’objet snowflake dans l’API JavaScript. Dans ce cas, les messages du journal et les données de trace sont stockés dans une table d’événements que vous pouvez analyser à l’aide de requêtes.
Pour plus d’informations, reportez-vous à ce qui suit :
Utilisation de RESULT_SCAN pour récupérer le résultat d’une procédure stockée¶
L’exemple suivant vous montre comment utiliser la fonction RESULT_SCAN pour récupérer et traiter le résultat d’une instruction CALL :
Créer et charger la table :
Créez la procédure stockée. Cette procédure retourne une chaîne bien formatée qui ressemble à un jeu de résultats de trois lignes, mais qui est en réalité une seule chaîne :
Appelez la procédure stockée, puis récupérez les résultats en utilisant RESULT_SCAN :
Vous pouvez effectuer des opérations plus complexes sur la valeur renvoyée par la fonction RESULT_SCAN. Dans ce cas, étant donné que la valeur renvoyée est une chaîne unique, vous pouvez extraire les « lignes » individuelles qui semblent être contenues dans cette chaîne et stocker ces lignes dans une autre table.
Astuce
Vous pouvez également utiliser l”opérateur de canal (->>) au lieu de la fonction RESULT_SCAN pour exécuter une instruction CALL et traiter son jeu de résultats avec une seule commande.
L’exemple suivant, qui est la suite de l’exemple précédent, illustre une façon de procéder :
Créez une table pour le stockage à long terme. Cette table contient le nom de la province et l’ID de province après les avoir extraits de la chaîne renvoyée par la commande CALL :
Appelez la procédure stockée, puis récupérez le résultat à l’aide de RESULT_SCAN, puis extrayez les trois lignes de la chaîne et placez-les dans la table :
Vérifiez que cela a fonctionné en affichant les lignes dans la table :
Voici à peu près le même code, mais par petites étapes :
Créez une table nommée
one_string. Cette table stocke temporairement le résultat de la commande CALL. Le résultat de CALL est une chaîne unique, donc cette table ne stocke qu’une seule valeur VARCHAR.Appelez la procédure stockée, puis récupérez le résultat (une chaîne) en utilisant RESULT_SCAN, puis enregistrez-le dans la table intermédiaire nommée
one_string:Cela montre la nouvelle ligne de la table
one_string. Rappelez-vous que bien que ceci soit formaté pour ressembler à trois lignes, il s’agit en fait d’une seule chaîne :Les commandes suivantes montrent comment extraire plusieurs lignes de la chaîne :
Ensuite, créez une table nommée
three_strings. Cette table contiendra le résultat après l’avoir divisé en lignes/chaînes individuelles :Convertissez maintenant cette chaîne de la table
one_stringen trois chaînes distinctes et montrez qu’il s’agit désormais de trois chaînes :Convertissez maintenant les trois chaînes en trois lignes dans notre table à long terme nommée
all_provinces:Affichez les trois lignes de la table à long terme :
Renvoi d’un tableau de messages d’erreur¶
Votre procédure stockée peut exécuter plusieurs instructions SQL et vous pouvez renvoyer un message d’état/d’erreur pour chaque instruction SQL. Cependant, une procédure stockée retourne une seule ligne ; elle n’est pas conçue pour renvoyer plusieurs lignes.
Si tous vos messages entrent dans une seule valeur de type ARRAY, vous pouvez obtenir tous les messages d’une procédure stockée avec un effort supplémentaire.
L’exemple suivant montre une façon de procéder (les messages d’erreur affichés ne sont pas réels, mais vous pouvez étendre ce code pour qu’il fonctionne avec vos instructions SQL réelles) :
Rappelez-vous que ceci n’est pas une solution à usage général. La taille maximale des types de données ARRAY est limitée et l’intégralité de votre jeu de résultats doit correspondre à un seul ARRAY.
Renvoi d’un jeu de résultats¶
Cette section prolonge l’exemple précédent décrit dans Renvoi d’un tableau de messages d’erreur. Cet exemple est plus général et vous permet de renvoyer un jeu de résultats à partir d’une requête.
Une procédure stockée renvoie une seule ligne contenant une seule colonne. Elle n’est pas conçue pour renvoyer un jeu de résultats. Toutefois, si votre jeu de résultats est suffisamment petit pour tenir dans une valeur unique de type VARIANT ou ARRAY, vous pouvez renvoyer un jeu de résultats à partir d’une procédure stockée avec du code supplémentaire :
Cela montre comment combiner les deux lignes précédentes en une seule ligne :
Pour plus de commodité, vous pouvez insérer la ligne précédente dans une vue. Cette vue convertit également la chaîne “null” en une vraie valeur NULL. Vous devez seulement créer la vue une fois. Toutefois, vous devez appeler la procédure stockée immédiatement avant d’effectuer des sélections dans cette vue à chaque utilisation de la vue. N’oubliez pas que l’appel à RESULT_SCAN dans la vue est extrait de l’instruction la plus récente, qui doit être CALL :
Vous pouvez même l’utiliser comme une vue vraie (c.-à-d. en sélectionnant un sous-ensemble) :
Rappelez-vous que ceci n’est pas une solution à usage général. La taille maximale des types de données VARIANT et ARRAY est limitée et l’intégralité de votre jeu de résultats doit correspondre à une seule VARIANT ou ARRAY.
Protection de la vie privée¶
Cet exemple montre une procédure stockée utile pour un détaillant en ligne. Cette procédure stockée respecte la vie privée des clients, tout en protégeant les intérêts légitimes du détaillant et du client. Si un client demande au détaillant de supprimer ses données pour des raisons de confidentialité, cette procédure stockée supprime la plupart des données du client, mais conserve l’historique des achats du client si l’une des conditions suivantes est vraie :
Tout article acheté a une garantie qui n’a pas encore expiré.
Le client doit encore de l’argent (ou le client doit être remboursé).
Une version plus réaliste de ce système supprimerait les lignes individuelles pour lesquelles le paiement a été effectué et la garantie a expiré.
Commencez par créer les tables et les charger :
Créez la procédure stockée :
Affichez les données dans les tables avant de supprimer l’une de ces données :
Le client #1 a une garantie qui est toujours en vigueur. La procédure stockée supprime les commentaires de révision qu’il a publiés, mais conserve son enregistrement d’achat en raison de la garantie :
Le client #2 doit encore de l’argent. La procédure stockée supprime ses commentaires de révision, mais conserve son enregistrement d’achat :
Le client #3 ne doit pas d’argent (et ne lui doit pas d’argent). Sa garantie a expiré, donc la procédure stockée supprime à la fois les commentaires de révision et les enregistrements d’achat :
Utilisation de variables de session avec les procédures stockées des droits de l’appelant et des droits du propriétaire¶
Ces exemples illustrent l’une des principales différences entre les procédures stockées relatives aux droits de l’appelant et aux droits du propriétaire. Ils tentent d’utiliser les variables de session de deux manières :
Définissez une variable de session avant d’appeler la procédure stockée, puis utilisez la variable de session à l’intérieur de la procédure stockée.
Définissez une variable de session dans la procédure stockée, puis utilisez-la après un renvoi depuis les procédures stockées.
L’utilisation de la variable de session et la définition de la variable de session fonctionnent correctement dans la procédure stockée des droits de l’appelant. Les deux échouent lors de l’utilisation d’une procédure stockée avec les droits du propriétaire, même si l’appelant est le propriétaire.
Procédure stockée avec droits de l’appelant¶
L’exemple suivant illustre une procédure stockée avec droits de l’appelant.
Créer et charger une table :
Définir une variable de session :
Créer une procédure stockée de droits d’un appelant qui utilise une variable de session et en définit une autre :
Appeler la procédure :
Afficher la valeur de la variable de session définie dans la procédure stockée :
Note
Bien que vous puissiez définir une variable de session dans une procédure stockée et la conserver après la fin de la procédure, Snowflake déconseille d’agir ainsi.
Procédure stockée avec droits du propriétaire¶
L’exemple suivant montre la procédure stockée avec droits du propriétaire.
Créez une procédure stockée avec droits du propriétaire qui utilise une variable de session :
Appelez la procédure (elle devrait échouer) :
Créez une procédure stockée de droits du propriétaire qui tente de définir une variable de session :
Appelez la procédure (elle devrait échouer) :
Résolution des problèmes¶
Une technique de dépannage générale consiste à utiliser un bloc Try/Catch JavaScript pour intercepter l’erreur et afficher les informations sur l’erreur. L’objet d’erreur contient :
Code d’erreur.
Message d’erreur.
État d’erreur.
Trace de pile au point d’échec.
Pour plus d’informations, y compris un exemple d’utilisation de ces informations, voir Interception d’une erreur en utilisant Try/Catch (dans cette rubrique).
Les sections suivantes fournissent des suggestions supplémentaires pour aider à résoudre des problèmes spécifiques.
Procédure stockée ou UDF renvoie NULL de manière inattendue¶
- Cause:
Votre procédure stockée/UDF a un paramètre. À l’intérieur de la procédure/UDF, le paramètre est désigné par son nom en minuscule, mais Snowflake a automatiquement converti le nom en majuscule.
- Solution:
Deux possibilités :
Utilisez la majuscule pour le nom de la variable dans le code JavaScript, ou
Placez le nom de la variable entre guillemets dans le code SQL.
Pour plus de détails, voir Arguments JavaScript et valeurs retournées.
La procédure stockée ne termine jamais son exécution¶
- Cause:
Vous pourriez avoir une boucle infinie dans votre code JavaScript.
- Solution:
Recherchez cela et corrigez les boucles infinies.
Erreur : Failed: empty argument passed¶
- Cause:
Votre procédure stockée peut contenir « sqltext » alors qu’elle devrait avoir « sqlText » (le premier est en minuscule ; le second est en casse mixte).
- Solution:
Utilisez « sqlText ».
Erreur : JavaScript out of memory error: UDF thread memory limit exceeded¶
- Cause:
Vous pourriez avoir une boucle infinie dans votre code JavaScript.
- Solution:
Recherchez cela et corrigez les boucles infinies. En particulier, veillez à ne plus appeler la prochaine ligne lorsque l’ensemble de résultats est épuisé (c’est-à-dire lorsque
resultSet.next()renvoiefalse).