UDFs JavaScript tabulaires (UDTFs)¶

Vous pouvez Ă©crire le gestionnaire d’une fonction de table dĂ©finie par l’utilisateur (UDTF) en JavaScript.

Votre code de gestionnaire traite les lignes reçues dans l’appel de l’UDTF et renvoie un rĂ©sultat sous forme de tableau. Les lignes reçues sont partitionnĂ©es, soit implicitement par Snowflake, soit explicitement dans la syntaxe de l’appel de fonction. Vous utilisez les fonctions de rappel que vous Ă©crivez pour traiter des lignes individuelles ainsi que les partitions dans lesquelles elles sont regroupĂ©es.

Le code JavaScript doit rĂ©pondre aux exigences suivantes pour que l’UDTF soit valide :

  • Le code doit dĂ©finir un seul objet JavaScript littĂ©ral.

  • L’objet dĂ©fini doit inclure une fonction de rappel appelĂ©e processRow(). Pour plus d’informations, voir Fonctions de rappel d’objets.

Important

Si le code JavaScript ne rĂ©pond pas Ă  ces exigences, l’UDTF sera quand mĂȘme créé ; cependant, il Ă©chouera lorsqu’il sera appelĂ© dans une requĂȘte.

Note

Les fonctions tabulaires (UDTFs) ont une limite de 500 arguments d’entrĂ©e et 500 colonnes de sortie.

Fonctions de rappel d’objets¶

GrĂące au code JavaScript, Snowflake interagit avec l’UDTF en appelant diverses fonctions de rappel pendant l’exĂ©cution de la requĂȘte. Le squelette suivant dĂ©crit toutes les fonctions de rappel disponibles et leurs signatures attendues :

{
   processRow: function (row, rowWriter, context) {/*...*/},
   finalize: function (rowWriter, context) {/*...*/},
   initialize: function (argumentInfo, context) {/*...*/},
}
Copy

Notez que seule processRow() est obligatoire ; les autres fonctions sont facultatives.

processRow()¶

Cette fonction de rappel est appelĂ©e une fois pour chaque ligne de la relation d’entrĂ©e. Les arguments de l”processRow() sont transmis dans l’objet row. Pour chacun des arguments dĂ©finis dans l’instruction CREATE FUNCTION utilisĂ© pour crĂ©er l’UDTF, il y a une propriĂ©tĂ© sur l’objet row avec le mĂȘme nom en majuscules. La valeur de cette propriĂ©tĂ© est la valeur de l’argument de la ligne courante. (La valeur est convertie en une valeur JavaScript).

L’argument rowWriter est utilisĂ© par le code fourni par l’utilisateur pour produire des lignes de sortie. L’objet rowWriter dĂ©finit une seule fonction, writeRow(). La fonction writeRow() prend un argument, l’objet ligne, qui est une ligne simple dans la table de sortie reprĂ©sentĂ©e comme un objet JavaScript. Pour chaque colonne dĂ©finie dans la clause RETURNS de la commande CREATE FUNCTION, une propriĂ©tĂ© correspondante peut ĂȘtre dĂ©finie sur l’objet de ligne. La valeur de cette propriĂ©tĂ© sur l’objet de ligne deviendra la valeur de la colonne correspondante dans la relation de sortie. Toute colonne de sortie sans propriĂ©tĂ© correspondante sur l’objet de ligne aura la valeur NULL dans la table des rĂ©sultats.

finalize()¶

Cette fonction de rappel finalize() est appelée une fois, aprÚs que toutes les lignes ont été transmises dans processRow(). (Si les données sont regroupées en partitions, alors finalize() est appelé une fois pour chaque partition, aprÚs que toutes les lignes de cette partition ont été transmises à processRow()).

Cette fonction de rappel peut ĂȘtre utilisĂ©e pour sortir n’importe quel Ă©tat qui peut avoir Ă©tĂ© agrĂ©gĂ© dans processRow() en utilisant le mĂȘme gĂ©nĂ©rateur de lignes rowWriter que celui qui est transmis dans processRow().

Note

Bien que Snowflake prenne en charge les grandes partitions avec des dĂ©lais d’expiration dĂ©finis pour les traiter avec succĂšs, les partitions particuliĂšrement grandes peuvent entraĂźner des expirations (par exemple lorsque finalize prend trop de temps Ă  se terminer). Veuillez contacter le support Snowflake si vous avez besoin d’ajuster le seuil d’expiration pour des scĂ©narios d’utilisation spĂ©cifiques.

initialize()¶

Cette fonction de rappel est appelée une fois pour chaque partition avant tout appel de processRow().

Utilisez initialize() pour configurer n’importe quel Ă©tat nĂ©cessaire pendant le calcul du rĂ©sultat.

Le paramĂštre argumentInfo de la fonction initialize() contient des mĂ©tadonnĂ©es sur les arguments de la fonction dĂ©finie par l’utilisateur. Par exemple, si l’UDF est dĂ©finie comme :

CREATE FUNCTION f(argument_1 INTEGER, argument_2 VARCHAR) ...
Copy

alors argumentInfo contient des informations sur argument_1 et argument_2.

argumentInfo possĂšde une propriĂ©tĂ© pour chacun de ces arguments. Chaque propriĂ©tĂ© est un objet avec les valeurs suivantes :

  • type : chaĂźne. Le type de cet argument.

  • isConst : boolĂ©en. Si « true Â», la valeur de cet argument est constante (c’est-Ă -dire qu’elle est la mĂȘme pour chaque ligne).

  • constValue : si isConst (comme dĂ©fini ci-dessus) est « true Â», cette entrĂ©e contient la valeur constante de l’argument ; sinon, ce champ est undefined.

La fonction initialize() ne peut pas produire de lignes de sortie.

Notes gĂ©nĂ©rales sur l’utilisation des fonctions de rappel¶

  • Les trois fonctions de rappel prennent un objet context ; celui-ci est rĂ©servĂ© pour une utilisation future et est actuellement vide.

    Prudence

    La modification de l’objet context peut entraĂźner un comportement indĂ©fini.

  • D’autres fonctions et propriĂ©tĂ©s peuvent ĂȘtre dĂ©finies, si nĂ©cessaire, sur l’objet Ă  utiliser dans l’UDTF.

  • Les arguments des fonctions de rappel sont positionnels et peuvent ĂȘtre nommĂ©s librement ; cependant, pour les besoins de ce chapitre, les noms ci-dessus sont utilisĂ©s pour le reste de la discussion et des exemples.

Partitions¶

Dans certaines situations, on peut vouloir regrouper les lignes en partitions. Le partitionnement prĂ©sente deux avantages principaux :

  • Il vous permet de regrouper des lignes sur la base d’une caractĂ©ristique commune. Cela vous permet de traiter toutes les lignes du groupe ensemble, et de traiter chaque groupe indĂ©pendamment.

  • Il permet Ă  Snowflake de diviser la charge de travail pour amĂ©liorer la parallĂ©lisation et donc les performances.

Par exemple, vous pouvez partitionner les donnĂ©es sur les cours des actions dans un groupe par action. Tous les prix des actions d’une entreprise individuelle peuvent ĂȘtre traitĂ©s ensemble, et les groupes de diffĂ©rentes entreprises sont traitĂ©s indĂ©pendamment.

L’instruction suivante appelle les UDTF nommĂ©es js_udtf() sur des partitions individuelles. Chaque partition contient toutes les lignes pour lesquelles l’expression PARTITION BY donne la mĂȘme valeur (par exemple, le mĂȘme symbole boursier).

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expression>)) ...;
Copy

Lorsque vous spĂ©cifiez une expression de partition Ă  utiliser avec une UDTF, Snowflake appelle :

  • initialize() une fois pour chaque partition.

  • processRow() une fois pour chaque ligne individuelle de cette partition.

  • finalize() une fois pour chaque partition (aprĂšs avoir traitĂ© la derniĂšre ligne de cette partition).

Vous pouvez Ă©galement vouloir traiter les lignes de chaque partition dans un ordre prĂ©cis. Par exemple, si vous souhaitez calculer la moyenne mobile du cours d’une action dans le temps, vous devez ordonner les cours de l’action par horodatage (ainsi que partitionner par action ou par sociĂ©tĂ©). L’exemple suivant montre comment procĂ©der :

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY <expression> ORDER BY <expression>)) ...;
Copy

Lorsque vous spĂ©cifiez une clause ORDER BY, les lignes sont traitĂ©es dans l’ordre dĂ©fini par l’expression ORDER BY. Les lignes sont transmises dans processRow() dans l’ordre dĂ©fini par l’expression ORDER BY.

Dans la plupart des cas, le partitionnement des donnĂ©es amĂ©liore presque automatiquement les possibilitĂ©s de parallĂ©lisation et donc les performances. Snowflake exĂ©cute gĂ©nĂ©ralement plusieurs instances UDTF en parallĂšle. (Pour cette discussion, une instance d’une UDTF JavaScript est dĂ©finie comme une instance de l’objet JavaScript utilisĂ©e pour reprĂ©senter la fonction dans Snowflake.) Chaque partition de lignes est transmise Ă  une seule instance de l’UDTF.

Notez qu’il n’y a pas nĂ©cessairement de relation « une Ă  une Â» entre les partitions et les instances d’UDTF. Bien que chaque partition ne soit traitĂ©e que par une seule instance d’UDTF, l’inverse n’est pas nĂ©cessairement vrai – une seule instance d’UDTF peut traiter plusieurs partitions. Il est donc important d’utiliser initialize() et finalize() pour configurer et dĂ©monter spĂ©cifiquement chaque partition, par exemple pour Ă©viter de « transfĂ©rer Â» les valeurs accumulĂ©es lors du traitement d’une partition vers le traitement d’une autre partition.

Colonnes de résultats¶

Lorsqu’une table est jointe Ă  une fonction de table, comme dans les exemples de partitionnement ci-dessus, le jeu de rĂ©sultats peut contenir les Ă©lĂ©ments suivants, selon ce qui est sĂ©lectionnĂ© :

  • Les colonnes dĂ©finies dans la clause RETURNS de la commande CREATE FUNCTION.

  • Les colonnes de la table, y compris les colonnes utilisĂ©es pour partitionner les donnĂ©es et les autres colonnes, qu’elles soient ou non utilisĂ©es comme paramĂštres d’entrĂ©e de l’UDTF.

Notez que les lignes produites dans le rappel processRow et les lignes produites par finalize diffĂšrent des maniĂšres suivantes :

  • Lorsqu’une ligne est produite dans processRow, Snowflake peut la corrĂ©ler Ă  une ligne en entrĂ©e, Ă  savoir celle transmise dans la fonction en tant qu’argument row. Notez que si un appel processRow donnĂ© produit plus d’une ligne, les attributs d’entrĂ©e sont corrĂ©lĂ©s dans chaque ligne de sortie.

    Pour les lignes produites en processRow, toutes les colonnes d’entrĂ©e peuvent ĂȘtre jointes Ă  la relation de sortie.

Dans le rappel finalize, Snowflake est incapable de le corrĂ©ler Ă  une seule ligne, car il n’existe pas de « ligne actuelle Â» Ă  corrĂ©ler.

  • Pour les lignes produites dans le rappel finalize, seules les colonnes utilisĂ©es dans la clause PARTITION BY sont disponibles (car elles sont les mĂȘmes pour toutes les lignes de la partition courante) ; tous les autres attributs sont NULL. Si aucune clause PARTITION BY n’est spĂ©cifiĂ©e, tous ces attributs sont NULL.

Appeler des UDTFs JavaScript dans les requĂȘtes¶

Lorsque vous appelez une UDTF dans la clause FROM d’une requĂȘte, spĂ©cifiez le nom et les arguments de l’UDTF Ă  l’intĂ©rieur des parenthĂšses qui suivent le mot-clĂ© TABLE.

En d’autres termes, utilisez une forme telle que la suivante pour le mot-clĂ© TABLE lorsque vous appelez une UDTF :

SELECT ...
  FROM TABLE ( udtf_name (udtf_arguments) )
Copy

Note

Pour en savoir plus sur l’appel d’UDFs et d’UDTFs, voir ExĂ©cutez une UDF.

Pas de partitionnement¶

Cet exemple simple montre comment appeler un UDTF. Cet exemple transmet des valeurs littĂ©rales. L’UDTF renvoie simplement les paramĂštres dans l’ordre inverse de leur ordre de transmission. Cet exemple n’utilise pas le partitionnement.

SELECT * FROM TABLE(js_udtf(10.0::FLOAT, 20.0::FLOAT));
+----+----+
|  Y |  X |
|----+----|
| 20 | 10 |
+----+----+
Copy

Cet exemple appelle un UDTF et lui transmet les valeurs d’une autre table. Dans cet exemple, l’UDTF nommĂ© js_udtf est appelĂ© une fois pour chaque ligne de la table nommĂ©e tab1. Chaque fois que la fonction est appelĂ©e, les valeurs des colonnes c1 et c2 de la ligne en cours lui sont transmises. Comme ci-dessus, l’UDTF est appelĂ© sans clause PARTITION BY.

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2)) ;
Copy

Lorsqu’aucun partitionnement n’est utilisĂ©, le moteur d’exĂ©cution de Snowflake partitionne l’entrĂ©e elle-mĂȘme en fonction de diffĂ©rents facteurs, tels que la taille de l’entrepĂŽt virtuel traitant la fonction et la cardinalitĂ© de la relation d’entrĂ©e. Dans ce mode, le code utilisateur ne peut faire aucune supposition quant aux partitions. Ceci est particuliĂšrement utile lorsque la fonction n’a que besoin d’examiner les lignes en isolation pour produire sa sortie, et aucun Ă©tat n’est agrĂ©gĂ© d’une ligne Ă  l’autre.

Partitionnement explicite¶

Les UDTFs JavaScript peuvent aussi ĂȘtre appelĂ©s Ă  l’aide d’une partition. Par exemple :

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER (PARTITION BY tab1.c3 ORDER BY tab1.c1));
Copy

Partitionnement explicite avec une clause OVER vide¶

SELECT * FROM tab1, TABLE(js_udtf(tab1.c1, tab1.c2) OVER ());
Copy

Une clause OVER vide signifie que chaque ligne appartient Ă  la mĂȘme partition (c’est-Ă -dire que toute la relation d’entrĂ©e est une partition).

Note

Vous devez faire preuve de prudence lorsque vous appelez un UDTF JavaScript avec une clause OVER vide, car cela limite Snowflake à créer une instance de la fonction et, par conséquent, Snowflake est incapable de paralléliser le calcul.

Exemples d’UDTFs JavaScript¶

Cette section contient plusieurs exemples d’UDTFs JavaScript.

Exemples Hello World de base¶

L’UDTF JavaScript suivant ne prend aucun paramĂštre en compte et retourne toujours les mĂȘmes valeurs. Il est fourni principalement Ă  titre d’illustration :

CREATE OR REPLACE FUNCTION HelloWorld0()
    RETURNS TABLE (OUTPUT_COL VARCHAR)
    LANGUAGE JAVASCRIPT
    AS '{
        processRow: function f(row, rowWriter, context){
           rowWriter.writeRow({OUTPUT_COL: "Hello"});
           rowWriter.writeRow({OUTPUT_COL: "World"});
           }
        }';

SELECT output_col FROM TABLE(HelloWorld0());
Copy

Sortie :

+------------+
| OUTPUT_COL |
+============+
| Hello      |
+------------+
| World      |
+------------+
Copy

L’UDTF JavaScript suivant sert Ă©galement Ă  des fins d’illustration, mais utilise un paramĂštre d’entrĂ©e. Notez que JavaScript est sensible Ă  la casse, mais SQL force les identificateurs Ă  ĂȘtre Ă©crits en majuscules. Par consĂ©quent, lorsque le code JavaScript fait rĂ©fĂ©rence Ă  un nom de paramĂštre SQL, le code JavaScript doit utiliser des majuscules.

Notez Ă©galement que les paramĂštres de fonction sont accessibles par le paramĂštre nommĂ© row dans la fonction get_params() :

CREATE OR REPLACE FUNCTION HelloHuman(First_Name VARCHAR, Last_Name VARCHAR)
    RETURNS TABLE (V VARCHAR)
    LANGUAGE JAVASCRIPT
    AS '{
        processRow: function get_params(row, rowWriter, context){
           rowWriter.writeRow({V: "Hello"});
           rowWriter.writeRow({V: row.FIRST_NAME});  // Note the capitalization and the use of "row."!
           rowWriter.writeRow({V: row.LAST_NAME});   // Note the capitalization and the use of "row."!
           }
        }';

SELECT V AS Greeting FROM TABLE(HelloHuman('James', 'Kirk'));
Copy

Sortie :

+------------+
|  GREETING  |
+============+
| Hello      |
+------------+
| James      |
+------------+
| Kirk       |
+------------+
Copy

Exemples de base illustrant les fonctions de rappel¶

L’UDTF JavaScript suivant illustre toutes les fonctions de rappel d’API et les diffĂ©rentes colonnes de sortie. Il retourne simplement toutes les lignes telles quelles et fournit un dĂ©compte du nombre de caractĂšres vus dans chaque partition. Il illustre Ă©galement comment partager l’état d’une partition en utilisant une rĂ©fĂ©rence THIS. Notez que l’exemple utilise un rappel initialize() pour rĂ©tablir le compteur Ă  zĂ©ro ; cela est nĂ©cessaire, car une instance de fonction donnĂ©e peut ĂȘtre utilisĂ©e pour traiter plusieurs partitions :

-- set up for the sample
CREATE TABLE parts (p FLOAT, s STRING);

INSERT INTO parts VALUES (1, 'michael'), (1, 'kelly'), (1, 'brian');
INSERT INTO parts VALUES (2, 'clara'), (2, 'maggie'), (2, 'reagan');

-- creation of the UDTF
CREATE OR REPLACE FUNCTION "CHAR_SUM"(INS STRING)
    RETURNS TABLE (NUM FLOAT)
    LANGUAGE JAVASCRIPT
    AS '{
    processRow: function (row, rowWriter, context) {
      this.ccount = this.ccount + 1;
      this.csum = this.csum + row.INS.length;
      rowWriter.writeRow({NUM: row.INS.length});
    },
    finalize: function (rowWriter, context) {
     rowWriter.writeRow({NUM: this.csum});
    },
    initialize: function(argumentInfo, context) {
     this.ccount = 0;
     this.csum = 0;
    }}';
Copy

La requĂȘte suivante illustre l’appel de l’UDTF CHAR_SUM sur la table parts sans partitionnement :

SELECT * FROM parts, TABLE(char_sum(s));
Copy

Sortie :

+--------+---------+-----+
| P      | S       | NUM |
+--------+---------+-----+
| 1      | michael | 7   |
| 1      | kelly   | 5   |
| 1      | brian   | 5   |
| 2      | clara   | 5   |
| 2      | maggie  | 6   |
| 2      | reagan  | 6   |
| [NULL] | [NULL]  | 34  |
+--------+---------+-----+
Copy

Lorsqu’aucun partitionnement n’est spĂ©cifiĂ©, Snowflake dĂ©finit automatiquement les partitions. Dans cet exemple, en raison du petit nombre de lignes, une seule partition est créée (c’est-Ă -dire qu’un seul appel de finalize() est exĂ©cutĂ©). Notez que la derniĂšre ligne contient des valeurs NULL dans les colonnes d’entrĂ©e.

MĂȘme requĂȘte, mais avec un partitionnement explicite :

SELECT * FROM parts, TABLE(char_sum(s) OVER (PARTITION BY p));
Copy

Sortie :

+--------+---------+-----+
| P      | S       | NUM |
+--------+---------+-----+
| 1      | michael | 7   |
| 1      | kelly   | 5   |
| 1      | brian   | 5   |
| 1      | [NULL]  | 17  |
| 2      | clara   | 5   |
| 2      | maggie  | 6   |
| 2      | reagan  | 6   |
| 2      | [NULL]  | 17  |
+--------+---------+-----+
Copy

Cet exemple de partitions sur la colonne p donne deux partitions. Pour chaque partition, une seule ligne est retournée dans le rappel finalize(), ce qui donne un total de deux lignes, distinguées par la valeur NULL dans la colonne s. Puisque p est la colonne PARTITION BY, les lignes créées dans finalize() ont la valeur p qui définit la partition courante.

Exemples Ă©tendus utilisant les valeurs des tables et d’autres UDTFs comme entrĂ©es¶

Cet UDTF de base convertit une « plage Â» d’adresses IP en une liste complĂšte d’adresses IP. L’entrĂ©e se compose des 3 premiers segments de l’adresse IP (par exemple '192.168.1'), puis du dĂ©but et de la fin de la plage utilisĂ©e pour gĂ©nĂ©rer le dernier segment (par exemple 42 et 45) :

CREATE OR REPLACE FUNCTION range_to_values(PREFIX VARCHAR, RANGE_START FLOAT, RANGE_END FLOAT)
    RETURNS TABLE (IP_ADDRESS VARCHAR)
    LANGUAGE JAVASCRIPT
    AS $$
      {
        processRow: function f(row, rowWriter, context)  {
          var suffix = row.RANGE_START;
          while (suffix <= row.RANGE_END)  {
            rowWriter.writeRow( {IP_ADDRESS: row.PREFIX + "." + suffix} );
            suffix = suffix + 1;
            }
          }
      }
      $$;

SELECT * FROM TABLE(range_to_values('192.168.1', 42::FLOAT, 45::FLOAT));
Copy

Sortie :

+--------------+
| IP_ADDRESS   |
+==============+
| 192.168.1.42 |
+--------------+
| 192.168.1.43 |
+--------------+
| 192.168.1.44 |
+--------------+
| 192.168.1.45 |
+--------------+
Copy

En vous basant sur l’exemple prĂ©cĂ©dent, vous pouvez calculer des adresses IP individuelles pour plus d’une plage. L’instruction suivante crĂ©e une table de plages qui peut ĂȘtre utilisĂ©e pour s’étendre Ă  des adresses IP individuelles. La requĂȘte entre ensuite les lignes de la table dans l’UDTF range_to_values() pour retourner les adresses IP individuelles :

CREATE TABLE ip_address_ranges(prefix VARCHAR, range_start INTEGER, range_end INTEGER);
INSERT INTO ip_address_ranges (prefix, range_start, range_end) VALUES
    ('192.168.1', 42, 44),
    ('192.168.2', 10, 12),
    ('192.168.2', 40, 40)
    ;

SELECT rtv.ip_address
  FROM ip_address_ranges AS r, TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv;
Copy

Sortie :

+--------------+
| IP_ADDRESS   |
+==============+
| 192.168.1.42 |
+--------------+
| 192.168.1.43 |
+--------------+
| 192.168.1.44 |
+--------------+
| 192.168.2.10 |
+--------------+
| 192.168.2.11 |
+--------------+
| 192.168.2.12 |
+--------------+
| 192.168.2.40 |
+--------------+
Copy

Attention

Dans cet exemple, la syntaxe utilisĂ©e dans la clause FROM est identique Ă  la syntaxe d’une jointure interne (c’est-Ă -dire FROM t1, t2) ; cependant, l’opĂ©ration effectuĂ©e n’est pas une jointure interne vraie. Le comportement rĂ©el est la fonction range_to_values() qui est appelĂ©e avec les valeurs de chaque ligne de la table ip_address changes. En d’autres termes, ce serait l’équivalent de l’écriture :

for input_row in ip_address_ranges:
  output_row = range_to_values(input_row.prefix, input_row.range_start, input_row.range_end)
Copy

Le concept de transmettre des valeurs Ă  un UDTF peut ĂȘtre Ă©tendu Ă  plusieurs UDTFs. L’exemple suivant crĂ©e un UDTF nommĂ© fake_ipv4_to_ipv6() qui « convertit Â» une adresse IPV4 en adresses IPV6. La requĂȘte appelle alors la fonction dans le cadre d’une instruction plus complexe impliquant un autre UDTF :

-- Example UDTF that "converts" an IPV4 address to a range of IPV6 addresses.
-- (for illustration purposes only and is not intended for actual use)
CREATE OR REPLACE FUNCTION fake_ipv4_to_ipv6(ipv4 VARCHAR)
    RETURNS TABLE (IPV6 VARCHAR)
    LANGUAGE JAVASCRIPT
    AS $$
      {
        processRow: function f(row, rowWriter, context)  {
          rowWriter.writeRow( {IPV6: row.IPV4 + "." + "000.000.000.000"} );
          rowWriter.writeRow( {IPV6: row.IPV4 + "." + "..."} );
          rowWriter.writeRow( {IPV6: row.IPV4 + "." + "FFF.FFF.FFF.FFF"} );
          }
      }
      $$;

SELECT ipv6 FROM TABLE(fake_ipv4_to_ipv6('192.168.3.100'));
Copy

Sortie :

+-------------------------------+
| IPV6                          |
+===============================+
| 192.168.3.100.000.000.000.000 |
+-------------------------------+
| 192.168.3.100....             |
+-------------------------------+
| 192.168.3.100.FFF.FFF.FFF.FFF |
+-------------------------------+
Copy

La requĂȘte suivante utilise les UDTFs fake_ipv4_to_ipv6 et range_to_values() créés prĂ©cĂ©demment, avec l’entrĂ©e de la table ip_address changes. En d’autres termes, elle commence par un ensemble de plages d’adresses IP, les convertit en adresses IPV4 individuelles, puis prend chaque adresse IPV4 et la « convertit Â» en une plage d’adresses IPV6 :

SELECT rtv6.ipv6
  FROM ip_address_ranges AS r,
       TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv,
       TABLE(fake_ipv4_to_ipv6(rtv.ip_address)) AS rtv6
  WHERE r.prefix = '192.168.2'  -- limits the output for this example
  ;
Copy

Sortie :

+------------------------------+
| IPV6                         |
+==============================+
| 192.168.2.10.000.000.000.000 |
+------------------------------+
| 192.168.2.10....             |
+------------------------------+
| 192.168.2.10.FFF.FFF.FFF.FFF |
+------------------------------+
| 192.168.2.11.000.000.000.000 |
+------------------------------+
| 192.168.2.11....             |
+------------------------------+
| 192.168.2.11.FFF.FFF.FFF.FFF |
+------------------------------+
| 192.168.2.12.000.000.000.000 |
+------------------------------+
| 192.168.2.12....             |
+------------------------------+
| 192.168.2.12.FFF.FFF.FFF.FFF |
+------------------------------+
| 192.168.2.40.000.000.000.000 |
+------------------------------+
| 192.168.2.40....             |
+------------------------------+
| 192.168.2.40.FFF.FFF.FFF.FFF |
+------------------------------+
Copy

Notez que dans cet exemple, nous avons utilisĂ© deux fois la syntaxe de jointure, mais aucune des opĂ©rations n’était une vraie jointure ; les deux Ă©taient des appels Ă  un UDTF utilisant la sortie d’une table ou un autre UDTF comme entrĂ©e.

Une jointure intĂ©rieure vĂ©ritable est insensible Ă  l’ordre. Par exemple, les instructions suivantes sont identiques :

table1 INNER JOIN table2 ON ...
table2 INNER JOIN table1 ON ...

La saisie de valeurs dans un UDTF n’est pas une vĂ©ritable jointure, et les opĂ©rations ne sont pas insensibles Ă  l’ordre. Par exemple, la requĂȘte suivante est identique Ă  l’exemple prĂ©cĂ©dent, sauf qu’elle inverse l’ordre des UDTFs dans la clause FROM :

SELECT rtv6.ipv6
  FROM ip_address_ranges AS r,
       TABLE(fake_ipv4_to_ipv6(rtv.ip_address)) AS rtv6,
       TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv
 WHERE r.prefix = '192.168.2'  -- limits the output for this example
  ;
Copy

La requĂȘte Ă©choue avec le message d’erreur suivant :

SQL compilation error: error line 3 at position 35 invalid identifier 'RTV.IP_ADDRESS'

L’identificateur rtv.ip_address n’est pas valide, car il n’était pas dĂ©fini avant son utilisation. Dans une vraie jointure, cela n’arriverait pas, mais lors du traitement des UDTFs en utilisant la syntaxe de jointure, cette erreur pourrait se produire.

Ensuite, essayez une instruction qui mĂ©lange l’entrĂ©e Ă  un UDTF avec une jointure vraie ; cependant, rappelez-vous que l’entrĂ©e d’un UDTF et l’exĂ©cution d’une jointure interne utilisent la mĂȘme syntaxe, ce qui pourrait porter Ă  confusion :

-- First, create a small table of IP address owners.
-- This table uses only IPv4 addresses for simplicity.
DROP TABLE ip_address_owners;
CREATE TABLE ip_address_owners (ip_address VARCHAR, owner_name VARCHAR);
INSERT INTO ip_address_owners (ip_address, owner_name) VALUES
  ('192.168.2.10', 'Barbara Hart'),
  ('192.168.2.11', 'David Saugus'),
  ('192.168.2.12', 'Diego King'),
  ('192.168.2.40', 'Victoria Valencia')
  ;

-- Now join the IP address owner table to the IPv4 addresses.
SELECT rtv.ip_address, ipo.owner_name
  FROM ip_address_ranges AS r,
       TABLE(range_to_values(r.prefix, r.range_start::FLOAT, r.range_end::FLOAT)) AS rtv,
       ip_address_owners AS ipo
 WHERE ipo.ip_address = rtv.ip_address AND
      r.prefix = '192.168.2'   -- limits the output for this example
  ;
Copy

Sortie :

+--------------+-------------------+
| IP_ADDRESS   | OWNER_NAME        |
+==============+===================+
| 192.168.2.10 | Barbara Hart      |
+--------------+-------------------+
| 192.168.2.11 | David Saugus      |
+--------------+-------------------+
| 192.168.2.12 | Diego King        |
+--------------+-------------------+
| 192.168.2.40 | Victoria Valencia |
+--------------+-------------------+
Copy

Attention

L’exemple prĂ©cĂ©dent fonctionne comme dĂ©crit ; cependant, vous devez faire attention lorsque vous combinez des UDTFs avec des jointures vraies, car cela pourrait entraĂźner un comportement non dĂ©terministe et/ou inattendu.

De plus, notez que ce comportement pourrait changer à l’avenir.