Présentation des UDFs JavaScript¶
Vous pouvez Ă©crire le gestionnaire dâune fonction dĂ©finie par lâutilisateur (UDF) en JavaScript. Les rubriques de cette section dĂ©crivent comment concevoir et Ă©crire un gestionnaire JavaScript.
Pour une introduction aux UDFs, y compris une liste de langages dans lesquels vous pouvez Ă©crire un gestionnaire dâUDF, reportez-vous Ă Vue dâensemble des fonctions dĂ©finies par lâutilisateur.
Une fois que vous avez un gestionnaire, vous crĂ©ez lâUDF avec SQL. Pour plus dâinformations sur lâutilisation de SQL pour crĂ©er ou appeler une UDF, reportez-vous Ă CrĂ©er une fonction dĂ©finie par lâutilisateur ou ExĂ©cutez une UDF.
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.
Note
Pour les limitations liĂ©es aux gestionnaires dâUDF JavaScript, reportez-vous Ă Limites liĂ©es aux UDF JavaScript.
Dans ce chapitre :
Comment fonctionne un gestionnaire JavaScript¶
Lorsquâun utilisateur appelle une UDF, il transmet le nom et les arguments de lâUDF Ă Snowflake. Snowflake appelle le code du gestionnaire associĂ© (avec des arguments, le cas Ă©chĂ©ant) pour exĂ©cuter la logique de lâUDF. La fonction de gestionnaire renvoie ensuite la sortie Ă Snowflake, qui la renvoie au client.
Pour chaque ligne transmise Ă une UDF, lâUDF renvoie soit une valeur scalaire (câest-Ă -dire unique), soit, si elle est dĂ©finie comme une fonction de table, un ensemble de lignes.
Exemple¶
Le code de lâexemple suivant crĂ©e une UDF appelĂ©e my_array_reverse
avec un code de gestionnaire qui accepte un ARRAY dâentrĂ©e et renvoie un ARRAY contenant les Ă©lĂ©ments dans lâordre inverse. Les types dâargument et de retour JavaScript sont convertis de et vers SQL par Snowflake, selon les mappages dĂ©crits dans Mappages de type de donnĂ©es SQL-JavaScript.
Notez que le code JavaScript doit faire rĂ©fĂ©rence aux noms de paramĂštres dâentrĂ©e en tant que majuscules, mĂȘme si les noms ne sont pas en majuscules dans le code SQL.
-- Create the UDF.
CREATE OR REPLACE FUNCTION my_array_reverse(a ARRAY)
RETURNS ARRAY
LANGUAGE JAVASCRIPT
AS
$$
return A.reverse();
$$
;
Types de données JavaScript¶
Les UDFs SQL et JavaScript fournissent des types de données similaires, mais différents, en fonction de leur prise en charge native des types de données. Les objets dans Snowflake et JavaScript sont transférés en utilisant les mappages suivants.
Entiers et doubles¶
JavaScript nâa pas de type entier ; tous les nombres sont reprĂ©sentĂ©s en double. Les UDFs JavaScript nâacceptent pas ou ne retournent pas de valeurs entiĂšres, sauf par le biais de la conversion de type (câest-Ă -dire que vous pouvez transmettre un entier dans un UDF JavaScript qui accepte un double).
SQL et JavaScript sur Snowflake acceptent des valeurs doubles. Ces valeurs sont transférées telles quelles.
Chaßnes¶
SQL et JavaScript sur Snowflake acceptent des valeurs de chaßnes. Ces valeurs sont transférées telles quelles.
Valeurs binaires¶
Toutes les valeurs binaires sont converties en objets JavaScript Uint8Array
. Ces tableaux de types peuvent ĂȘtre accessibles de la mĂȘme maniĂšre que des tableaux JavaScript normaux, mais ils sont plus efficaces et acceptent des mĂ©thodes supplĂ©mentaires.
Si un UDF JavaScript retourne un objet Uint8Array
, il est converti en une valeur binaire SQL Snowflake.
Dates¶
Tous les types dâhorodatage et de date sont convertis en objets Date()
JavaScript. Le type de date JavaScript est équivalent à TIMESTAMP_LTZ(3) dans le langage SQL Snowflake.
Considérez les notes suivantes pour les UDFs JavaScript qui acceptent une date ou une heure :
Toute précision au-delà de la milliseconde est perdue.
Un
Date
JavaScript gĂ©nĂ©rĂ© Ă partir de SQL TIMESTAMP_NTZ nâagit plus comme une durĂ©e chronomĂ©trĂ©e ; il est influencĂ© par lâheure dâĂ©tĂ©. Ceci est similaire au comportement qui survient lors de la conversion de TIMESTAMP_NTZ en TIMESTAMP_LTZ.Un
Date
JavaScript gĂ©nĂ©rĂ© Ă partir de SQL TIMESTAMP_TZ perd des informations de fuseau horaire, mais reprĂ©sente le mĂȘme moment dans le temps que lâentrĂ©e (similaire Ă la conversion de TIMESTAMP_TZ en TIMESTAMP_LTZ).DATE SQL est converti en
Date
JavaScript représentant minuit du jour actuel dans le fuseau horaire local.
De plus, considérez les notes suivantes pour les UDFs JavaScript qui retournent les types DATE et TIMESTAMP :
Les objets
Date
JavaScript sont convertis dans le type de donnĂ©es du rĂ©sultat de lâUDF, selon la mĂȘme sĂ©mantique de conversion que les conversions de TIMESTAMP_LTZ(3) vers le type de donnĂ©es de retour.Les objets JavaScript
Date
imbriqués dans des objets VARIANT sont toujours de type TIMESTAMP_LTZ(3).
Variante, objets et tableaux¶
Les UDFs JavaScript permettent une manipulation simple et intuitive des variantes et des donnĂ©es JSON. Les objets de variantes transmis dans un UDF sont transformĂ©s en types et valeurs JavaScript natifs. Toutes les valeurs prĂ©cĂ©demment listĂ©es sont traduites dans leurs types JavaScript correspondants. Les objets et tableaux de variantes sont convertis en objets et tableaux JavaScript. De mĂȘme, toutes les valeurs renvoyĂ©es par lâUDF sont transformĂ©es en valeurs de variantes appropriĂ©es. Notez que les objets et les tableaux retournĂ©s par lâUDF sont soumis Ă des limitations de taille et de profondeur.
-- flatten all arrays and values of objects into a single array
-- order of objects may be lost
CREATE OR REPLACE FUNCTION flatten_complete(v variant)
RETURNS variant
LANGUAGE JAVASCRIPT
AS '
// Define a function flatten(), which always returns an array.
function flatten(input) {
var returnArray = [];
if (Array.isArray(input)) {
var arrayLength = input.length;
for (var i = 0; i < arrayLength; i++) {
returnArray.push.apply(returnArray, flatten(input[i]));
}
} else if (typeof input === "object") {
for (var key in input) {
if (input.hasOwnProperty(key)) {
returnArray.push.apply(returnArray, flatten(input[key]));
}
}
} else {
returnArray.push(input);
}
return returnArray;
}
// Now call the function flatten() that we defined earlier.
return flatten(V);
';
select value from table(flatten(flatten_complete(parse_json(
'[
{"key1" : [1, 2], "key2" : ["string1", "string2"]},
{"key3" : [{"inner key 1" : 10, "inner key 2" : 11}, 12]}
]'))));
-----------+
VALUE |
-----------+
1 |
2 |
"string1" |
"string2" |
10 |
11 |
12 |
-----------+
Arguments JavaScript et valeurs retournées¶
Les arguments peuvent ĂȘtre rĂ©fĂ©rencĂ©s directement par leur nom dans JavaScript. Notez quâun identificateur sans guillemet doit ĂȘtre rĂ©fĂ©rencĂ© avec le nom de la variable en majuscule. Comme les arguments et les UDF sont rĂ©fĂ©rencĂ©s depuis JavaScript, ils doivent ĂȘtre des identificateurs JavaScript lĂ©gaux. Plus prĂ©cisĂ©ment, les noms dâUDF et dâarguments doivent commencer par une lettre ou $
, tandis que les caractĂšres suivants peuvent ĂȘtre alphanumĂ©riques, $
, ou _
. De plus, les noms ne peuvent pas ĂȘtre des mots rĂ©servĂ©s JavaScript.
Les trois exemples suivants illustrent des UDFs qui utilisent des arguments référencés par leur nom :
-- Valid UDF. 'N' must be capitalized.
CREATE OR REPLACE FUNCTION add5(n double)
RETURNS double
LANGUAGE JAVASCRIPT
AS 'return N + 5;';
select add5(0.0);
-- Valid UDF. Lowercase argument is double-quoted.
CREATE OR REPLACE FUNCTION add5_quoted("n" double)
RETURNS double
LANGUAGE JAVASCRIPT
AS 'return n + 5;';
select add5_quoted(0.0);
-- Invalid UDF. Error returned at runtime because JavaScript identifier 'n' cannot be resolved.
CREATE OR REPLACE FUNCTION add5_lowercase(n double)
RETURNS double
LANGUAGE JAVASCRIPT
AS 'return n + 5;';
select add5_lowercase(0.0);
Valeurs NULL et non définies¶
Lorsque vous utilisez des UDFs JavaScript, vous devez porter une attention particuliÚre aux lignes et aux variables qui peuvent contenir des valeurs NULL. Plus précisément, Snowflake contient deux valeurs NULL distinctes (SQL NULL
et variante JSON null
), tandis que JavaScript contient la valeur undefined
en plus de la valeur null
.
Les arguments SQL NULL
vers un UDF JavaScript se traduiront par la valeur undefined
JavaScript. De mĂȘme, les valeurs undefined
JavaScript renvoyées se traduisent par des valeurs SQL NULL
. Ceci est vrai pour tous les types de données, y compris la variante. Pour les types qui ne sont pas des variantes, une valeur JavaScript null
renvoyée donnera également une valeur SQL NULL
.
Les arguments et les valeurs renvoyées du type de variante font la distinction entre les valeurs undefined
et les valeurs null
de JavaScript. SQL NULL
continue de se traduire par JavaScript undefined
(et JavaScript undefined
vers SQL NULL
) ; la variante JSON null
se traduit par JavaScript null
(et JavaScript null
par variante JSON null
). Une valeur undefined
incorporĂ©e dans un objet JavaScript (comme valeur) ou un tableau entraĂźnera lâomission de lâĂ©lĂ©ment.
Créez une table avec une chaßne et une valeur
NULL
:create or replace table strings (s string); insert into strings values (null), ('non-null string');Créez une fonction qui convertit une chaßne en
NULL
et unNULL
en chaĂźne :CREATE OR REPLACE FUNCTION string_reverse_nulls(s string) RETURNS string LANGUAGE JAVASCRIPT AS ' if (S === undefined) { return "string was null"; } else { return undefined; } ';Appelez la fonction :
select string_reverse_nulls(s) from strings order by 1; +-------------------------+ | STRING_REVERSE_NULLS(S) | |-------------------------| | string was null | | NULL | +-------------------------+Créez une fonction qui montre la différence entre transmettre un
NULL
SQL et une variantenull
JSON :CREATE OR REPLACE FUNCTION variant_nulls(V VARIANT) RETURNS VARCHAR LANGUAGE JAVASCRIPT AS ' if (V === undefined) { return "input was SQL null"; } else if (V === null) { return "input was variant null"; } else { return V; } ';select null, variant_nulls(cast(null as variant)), variant_nulls(PARSE_JSON('null')) ; +------+--------------------------------------+-----------------------------------+ | NULL | VARIANT_NULLS(CAST(NULL AS VARIANT)) | VARIANT_NULLS(PARSE_JSON('NULL')) | |------+--------------------------------------+-----------------------------------| | NULL | input was SQL null | input was variant null | +------+--------------------------------------+-----------------------------------+Créez une fonction qui montre la différence entre le renvoi de
undefined
,null
et une variante contenantundefined
etnull
(notez que la valeurundefined
est supprimée de la variante renvoyée) :CREATE OR REPLACE FUNCTION variant_nulls(V VARIANT) RETURNS variant LANGUAGE JAVASCRIPT AS $$ if (V == 'return undefined') { return undefined; } else if (V == 'return null') { return null; } else if (V == 3) { return { key1 : undefined, key2 : null }; } else { return V; } $$;select variant_nulls('return undefined'::VARIANT) AS "RETURNED UNDEFINED", variant_nulls('return null'::VARIANT) AS "RETURNED NULL", variant_nulls(3) AS "RETURNED VARIANT WITH UNDEFINED AND NULL; NOTE THAT UNDEFINED WAS REMOVED"; +--------------------+---------------+---------------------------------------------------------------------------+ | RETURNED UNDEFINED | RETURNED NULL | RETURNED VARIANT WITH UNDEFINED AND NULL; NOTE THAT UNDEFINED WAS REMOVED | |--------------------+---------------+---------------------------------------------------------------------------| | NULL | null | { | | | | "key2": null | | | | } | +--------------------+---------------+---------------------------------------------------------------------------+
Conversion de type dans JavaScript¶
JavaScript convertira implicitement les valeurs entre de nombreux types diffĂ©rents. Lorsquâune valeur est retournĂ©e, elle est dâabord convertie dansa le type de retour demandĂ© avant dâĂȘtre convertie en une valeur SQL. Par exemple, si un nombre est retournĂ©, mais que lâUDF est dĂ©clarĂ© comme retournant une chaĂźne, ce nombre sera converti en chaĂźne dans JavaScript. Gardez Ă lâesprit que les erreurs de programmation JavaScript, comme le retour du type incorrect, peuvent ĂȘtre masquĂ©es par ce comportement. De plus, si une erreur est gĂ©nĂ©rĂ©e lors de la conversion du type de la valeur, une erreur se produira.
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 :
CREATE OR REPLACE FUNCTION num_test(a double) RETURNS string LANGUAGE JAVASCRIPT AS $$ return A; $$ ;select hash(1) AS a, num_test(hash(1)) AS b, a - b; +----------------------+----------------------+------------+ | A | B | A - B | |----------------------+----------------------+------------| | -4730168494964875235 | -4730168494964875000 | -235.00000 | +----------------------+----------------------+------------+
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 :
getColumnValueAsString()
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.
Erreurs JavaScript¶
Toutes les erreurs rencontrĂ©es lors de lâexĂ©cution de JavaScript apparaissent Ă lâutilisateur comme des erreurs SQL. Cela inclut les erreurs dâanalyse, les erreurs dâexĂ©cution et les erreurs non dĂ©tectĂ©es lancĂ©es dans lâUDF. Si lâerreur contient une trace dâappels, celle-ci sera imprimĂ©e avec le message dâerreur. Il est acceptable de lancer une erreur sans la capturer afin de terminer la requĂȘte et de produire une erreur SQL.
Lors du dĂ©bogage, vous pouvez trouver utile dâimprimer les valeurs des arguments avec le message dâerreur pour quâelles apparaissent dans le texte du message dâerreur SQL. Pour des UDFs dĂ©terministes, cela fournit les donnĂ©es nĂ©cessaires pour reproduire les erreurs dans un moteur JavaScript local. Un modĂšle commun est de placer un corps dâUDF JavaScript entier dans un bloc try-catch, dâajouter des valeurs dâargument au message de lâerreur capturĂ©e, et de lancer une erreur avec le message Ă©tendu. Vous devriez envisager de supprimer de tels mĂ©canismes avant de dĂ©ployer des UDFs dans un environnement de production ; lâenregistrement de valeurs dans des messages dâerreur peut rĂ©vĂ©ler involontairement des donnĂ©es sensibles.
La fonction peut gĂ©nĂ©rer et intercepter des exceptions prĂ©dĂ©finies ou des exceptions personnalisĂ©es. Un exemple simple de levĂ©e dâune exception personnalisĂ©e est disponible ici.
Voir aussi DĂ©pannage dâUDFs JavaScript.
Sécurité des UDF JavaScript¶
Les UDFs JavaScript sont conçus pour ĂȘtre sĂ»res et sĂ©curitaires en fournissant plusieurs couches dâinterrogation et dâisolation des donnĂ©es :
Les ressources de calcul de lâentrepĂŽt virtuel qui exĂ©cutent une UDF JavaScript ne sont accessibles quâĂ partir de votre compte (câest-Ă -dire que les entrepĂŽts virtuels ne partagent pas les ressources avec dâautres comptes Snowflake).
Les donnĂ©es des tables sont cryptĂ©es dans lâentrepĂŽt virtuel pour empĂȘcher tout accĂšs non autorisĂ©.
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.
Par consĂ©quent, les UDFs JavaScript ne peuvent accĂ©der quâaux donnĂ©es nĂ©cessaires Ă lâexĂ©cution de la fonction dĂ©finie et ne peuvent pas affecter lâĂ©tat du systĂšme sous-jacent, si ce nâest en consommant une quantitĂ© raisonnable de mĂ©moire et de temps processeur.