SnowConvert AI - Serveur SQL-Azure Synapse - Procédures

Cette section documente la transformation de la syntaxe et des instructions TSQL de procédure en Snowflake JavaScript

Applies to
  • SQL Server

  • Azure Synapse Analytics

Note

Certaines parties du code de sortie sont omises pour des raisons de clarté.

1. CREATE PROCEDURE Translation

Snowflake CREATE PROCEDURE est défini en syntaxe SQL tandis que ses instructions internes sont définies en JavaScript.

Transact

 -- Additional Params: -t JavaScript
CREATE PROCEDURE HumanResources.uspGetAllEmployees
     @FirstName NVARCHAR(50),
     @Age INT
AS
    -- TSQL Statements and queries...
GO
Copy

Snowflake

CREATE OR REPLACE PROCEDURE HumanResources.uspGetAllEmployees (FIRSTNAME STRING, AGE INT)
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
    // SnowConvert AI Helpers Code section is omitted.
$$;
Copy

DATA TYPE du paramètre

Les types de données des paramètres sont en cours de traduction en équivalent Snowflake. Voir aussi les Types de données.

Assistant EXEC

Pour pouvoir exécuter les instructions d’une procédure dans l’environnement SnowFlake, ces instructions doivent être prétraitées et adaptées pour refléter leur exécution dans plusieurs variables spécifiques à la langue source.

SnowConvert AI traduit automatiquement les instructions prises en charge et utilise une aide EXEC. Cette aide fournit des capacités d’accès et de mise à jour à de nombreuses variables qui simulent la façon dont l’exécution de ces instructions se produirait dans leur environnement natif.

Par exemple, vous pouvez constater que, dans les procédures migrées, un bloc de code est toujours ajouté. Nous allons expliquer la structure de base de ce code dans la section suivante. Gardez à l’esprit que nous évaluons en permanence de nouvelles façons d’optimiser les transformations et les outils d’assistance nécessaires.

Structure

La structure de base de l’aide EXEC est la suivante :

  1. Section de déclaration des variables : On déclare ici les différentes variables ou objets qui contiendront les valeurs associées à l’exécution des instructions à l’intérieur de la procédure. Il s’agit de valeurs telles que le nombre de lignes affectées par une instruction, voire le jeu de résultats lui-même.

  2. Déclaration de la fonction fixBind : Il s’agit d’une fonction auxiliaire utilisée pour corriger les liaisons lorsqu’elles sont de type Date.

  3. Déclaration de la fonction EXEC : Il s’agit de la fonction d’assistance principale de EXEC. Elle reçoit l’instruction à exécuter, le tableau des liaisons (essentiellement les variables ou paramètres qui peuvent être modifiés par l’exécution et qui nécessitent la permanence des données tout au long de l’exécution de la procédure), le drapeau noCatch qui détermine si ERROR_HANDLERS doivent être utilisés, et la fonction catchFunction pour l’exécution du code personnalisé en cas d’exception lors de l’exécution de l’instruction. Le corps de la fonction EXEC est très simple : il s’agit d’exécuter l’instruction et de stocker toutes les données utiles produites par son exécution, le tout à l’intérieur d’un bloc de gestion des erreurs.

  4. ERROR VARS : Le bloc de capture EXEC établit une liste de variables d’erreur telles que MESSAGE_TEXT, SQLCODE, SQLSTATE, PROC_NAME et ERROR_LINE qui pourraient être utilisées pour récupérer les valeurs des fonctions définies par l’utilisateur, afin d’émuler le comportement des fonctions intégrées SQL Server ERROR_LINE, ERROR_MESSAGE, ERROR_NUMBER, ERROR_PROCEDURE et ERROR_STATE . Une fois que l’ensemble de ces variables a été définie sur une valeur, la fonction définie par l’utilisateur UPDATE_ERROR_VARS sera chargée de mettre à jour certaines variables d’environnement avec les valeurs d’erreur, afin d’y avoir accès dans le champ d’application SQL.

Code

Le bloc de code suivant représente l’assitant EXEC à l’intérieur d’une procédure :

   var _RS, ROW_COUNT, _ROWS, MESSAGE_TEXT, SQLCODE = 0, SQLSTATE = '00000', ERROR_HANDLERS, NUM_ROWS_AFFECTED, INTO;
   var fixBind = function (arg) {
      arg = arg == undefined ? null : arg instanceof Date ? arg.toISOString() : arg;
      return arg;
   };
   var fetch = (count,rows,stmt) => (count && rows.next() && Array.apply(null,Array(stmt.getColumnCount())).map((_,i) => rows.getColumnValue(i + 1))) || [];
   var EXEC = (stmt,binds = [],noCatch = false) => {
      binds = binds ? binds.map(fixBind) : binds;
      for(var stmt of stmt.split(";").filter((_) => _)) {
         try {
            _RS = snowflake.createStatement({
                  sqlText : stmt,
                  binds : binds
               });
            _ROWS = _RS.execute();
            ROW_COUNT = _RS.getRowCount();
            NUM_ROWS_AFFECTED = _RS.getNumRowsAffected();
            return {
               THEN : (action) => !SQLCODE && action(fetch(_ROWS))
            };
         } catch(error) {
            let rStack = new RegExp('At .*, line (\\d+) position (\\d+)');
            let stackLine = error.stackTraceTxt.match(rStack) || [0,-1];
            MESSAGE_TEXT = error.message.toString();
            SQLCODE = error.code.toString();
            SQLSTATE = error.state.toString();
            snowflake.execute({sqlText: `SELECT UPDATE_ERROR_VARS_UDF(?,?,?,?,?)`,binds: [stackLine[1], SQLCODE, SQLSTATE, MESSAGE_TEXT, PROC_NAME]});
            throw error;
         }
      }
   };
Copy

Exemple EXEC simple

Voici un exemple simple d’un appel à EXEC à l’intérieur d’une procédure stockée

Code source

 -- Additional Params: -t JavaScript
CREATE PROCEDURE dbo.EXEC_EXAMPLE_1
AS
   EXECUTE('SELECT 1 AS Message');
GO
Copy
 -- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_1
GO
Copy

Code attendu

CREATE OR REPLACE PROCEDURE dbo.EXEC_EXAMPLE_1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// REGION SnowConvert AI Helpers Code
	var _RS, ROW_COUNT, _ROWS, MESSAGE_TEXT, SQLCODE = 0, SQLSTATE = '00000', OBJECT_SCHEMA_NAME  = 'dbo', ERROR_HANDLERS, NUM_ROWS_AFFECTED, PROC_NAME = arguments.callee.name, DOLLAR_DOLLAR = '$' + '$';
	function* sqlsplit(sql) {
		var part = '';
		var ismark = () => sql[i] == '$' && sql[i + 1] == '$';
		for(var i = 0;i < sql.length;i++) {
			if (sql[i] == ';') {
				yield part + sql[i];
				part = '';
			} else if (ismark()) {
				part += sql[i++] + sql[i++];
				while ( i < sql.length && !ismark() ) {
					part += sql[i++];
				}
				part += sql[i] + sql[i++];
			} else part += sql[i];
		}
		if (part.trim().length) yield part;
	};
	var formatDate = (arg) => (new Date(arg - (arg.getTimezoneOffset() * 60000))).toISOString().slice(0,-1);
	var fixBind = function (arg) {
		arg = arg == undefined ? null : arg instanceof Date ? formatDate(arg) : arg;
		return arg;
	};
	var EXEC = (stmt,binds = [],severity = "16",noCatch = false) => {
		binds = binds ? binds.map(fixBind) : binds;
		for(var stmt of sqlsplit(stmt)) {
			try {
				_RS = snowflake.createStatement({
						sqlText : stmt,
						binds : binds
					});
				_ROWS = _RS.execute();
				ROW_COUNT = _RS.getRowCount();
				NUM_ROWS_AFFECTED = _RS.getNumRowsAffected();
				return {
					THEN : (action) => !SQLCODE && action(fetch(_ROWS))
				};
			} catch(error) {
				let rStack = new RegExp('At .*, line (\\d+) position (\\d+)');
				let stackLine = error.stackTraceTxt.match(rStack) || [0,-1];
				MESSAGE_TEXT = error.message.toString();
				SQLCODE = error.code.toString();
				SQLSTATE = error.state.toString();
				snowflake.execute({
					sqlText : `SELECT UPDATE_ERROR_VARS_UDF(?,?,?,?,?,?)`,
					binds : [stackLine[1],SQLCODE,SQLSTATE,MESSAGE_TEXT,PROC_NAME,severity]
				});
				throw error;
			}
		}
	};
	// END REGION

	EXEC(`SELECT 1 AS Message;`);
$$;
Copy

EXEC dans une procédure stockée avec un paramètre

Dans cet exemple, la commande EXEC se trouve à l’intérieur d’une procédure stockée et reçoit une valeur de paramètre

Code source

 -- Additional Params: -t JavaScript
CREATE PROCEDURE dbo.EXEC_EXAMPLE_2
	@p1 varchar(50) = N''
AS
	EXEC ('SELECT ' + @p1);
GO
Copy
 -- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_2 N'''Hello World!'''
GO
Copy

Code attendu

CREATE OR REPLACE PROCEDURE dbo.EXEC_EXAMPLE_2 (P1 STRING DEFAULT '')
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// SnowConvert AI Helpers Code section is omitted.

	EXEC(`SELECT
   ${P1};`);
$$;
Copy

EXEC invoquant une procédure stockée avec un paramètre

Dans cet exemple, EXEC invoque une autre procédure stockée et ajoute un paramètre

Code source

 -- Additional Params: -t JavaScript
CREATE PROCEDURE dbo.EXEC_EXAMPLE_3
	@p1 varchar(50) = N''
AS
	EXEC EXEC_EXAMPLE_2 @p1
GO
Copy
 -- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_3 N'''Hello World!'''
GO
Copy

Code attendu

CREATE OR REPLACE PROCEDURE dbo.EXEC_EXAMPLE_3 (P1 STRING DEFAULT '')
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// SnowConvert AI Helpers Code section is omitted.

	EXEC(`CALL EXEC_EXAMPLE_2(?)`,[P1]);
$$;
Copy

Paramètres avec valeur par défaut.

Dans SqlServer, certains paramètres peuvent avoir une valeur par défaut au cas où ils ne seraient pas spécifiés lors de l’appel d’une procédure.

SQL Server

CREATE PROCEDURE PROC_WITH_DEFAULT_PARAMS1
@PARAM1 INT = 0, @PARAM2 INT = 0, @PARAM3 INT = 0, @PARAM4 INT = 0
AS
BEGIN
    .
    .
    .
END
Copy

Snowflake

CREATE OR REPLACE PROCEDURE PROC_WITH_DEFAULT_PARAMS1(param1 int default 0, param2 int default 0, param3 int default 0, param4 int default 0)
RETURNS TABLE()
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
    .
    .
    .
$$;
Copy
CALL PROC_WITH_DEFAULT_PARAMS1(param2 => 10, param4 => 15);
Copy

Assistant CURSOR

CREATE OR REPLACE PROCEDURE PROC1()
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS CALLER
AS
$$
	  var CURSOR = function (stmt, binds) {
	  var statementObj, result_set, total_rows, isOpen = false, self = this, row_count;
	
	  this.CURRENT = new Object;
	
	  var fetch = (count,rows,stmt) => (count && rows.next() && Array.apply(null,Array(stmt.getColumnCount())).map((_,i) => rows.getColumnValue(i + 1))) || [];
	
	  var fixBind = function (arg) {
	      arg = arg == undefined ? null : arg instanceof Date ? formatDate(arg) : arg;
	      return arg;
	   };
	
	  this.OPEN = function(openParameters) {
		  if (result_set == undefined) {
			try {
				if (openParameters) binds = openParameters;
				if (binds instanceof Function) binds = binds();
				var finalBinds = binds && binds.map(fixBind);
				var finalStmt = stmt instanceof Function ? stmt() : stmt;
				statementObj = snowflake.createStatement({
					sqlText : finalStmt,
					binds : finalBinds
				});
				result_set = statementObj.execute();
				total_rows = statementObj.getRowCount();
				isOpen = true;
				row_count = 0;
			} catch(error) {
				RAISE(error.code,"error",error.message);
			}
			else {
				isOpen = true;
			}
		  }
	          
	      return this;
	  };
	      
	  this.CURSOR_ROWS = function () {
	      return total_rows;
	  };
	
	  this.FETCH_STATUS = function() {
	      if(total_rows > row_count)
	          return 0;
	      else
	          return -1;
	  };
	
	  this.FETCH_NEXT = function() {
		  self.res = [];
	      if (isOpen) {
			  self.res = fetch(total_rows,result_set,statementObj);
			  if (self.res)
				  row_count++;
		  }
		  return self.res && self.res.length > 0;
	  };
	
	  this.INTO = function () {
	      return self.res;
	  };
	
	  this.CLOSE = function () {
	      isOpen = false;
	  };
	
	  this.DEALLOCATE = function() {
	      this.CURRENT = row_count = result_set_table = total_rows = result_set = statementObj = self = undefined;
	  };
  };
  
  var COL1, COL2;
  var sql_stmt = ``;
	
  let c = new CURSOR(`SELECT COL1, COL2 FROM TABLE1;`,() => []);
    
  c.OPEN();
  c.FETCH_NEXT();
  [COL1, COL2] = c.INTO();
  while ( c.FETCH_STATUS()) {
        
        
        sql_stmt = `INSERT INTO TABLE2 (COL1, COL2) VALUES (` + COL1+ `, ` + COL2 + `)`;
		
        snowflake.createStatement({
            sqlText : sql_stmt
         }).execute();
  }

  c.CLOSE();
  c.DEALLOCATE();

  return 'sucess';
$$;
Copy

Assistant EXEC Insert Into

L’assistant Exec Insert into génère une fonction appelée Insert insertIntoTemporaryTable(sql). Cette fonction permettra à la transformation pour INSERT INTO TABLE_NAME EXEC(...) de TSQL à Snowflake d’imiter le comportement de l’instruction originale en insérant les données dans une table temporaire, puis en les réinsérant dans l’instruction originale.

Pour plus d’informations sur la manière dont le code de cette instruction est modifié, consultez la section relative à l’insertion dans l’exécution

Note

Ce code généré pour INSERT INTO EXEC, peut présenter des problèmes de performance lors du traitement des instructions EXECUTE contenant plusieurs requêtes.

   function insertIntoTemporaryTable(sql) {
    var table = "SnowConvertPivotTemporaryTable";
    return EXEC('CREATE OR REPLACE TEMPORARY TABLE ${table} AS ${sql}');
  }
  
  insertIntoTemporaryTable(`${DBTABLES}`)
  EXEC(`INSERT INTO MYDB.PUBLIC.T_Table SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable`);
  EXEC(`DROP TABLE SnowConvertPivotTemporaryTable`)
Copy

Assistant LIKE

Dans le cas où une expression similaire est trouvée dans une procédure, par exemple

CREATE PROCEDURE ProcedureLike @VariableValue VARCHAR(50) AS
BEGIN
	IF @VariableValue like '%c%'
	BEGIN
		Select AValue from ATable;
	END;
END;
Copy

Comme l’intérieur de la procédure est transformé en javascript, l’expression like provoquera une erreur. Afin d’éviter et de conserver la fonctionnalité, une fonction est ajoutée au début de la procédure si une expression similaire est trouvée.

   function LIKE(expr,pattern,esc,cs) {
    function fixPattern(pattern,esc) {
      const specials = '/.*+?|(){}[]\\'.split('');
      var newPattern = "";
      var fix = (c) => specials.includes(c) ? '\\' + c : c;
      for(var i = 0;i < pattern.length;i++) {
        var c = pattern[i];
        if (c === esc) {
          newPattern += pattern[i + 1]
          i++
        } else if (c === '%') {
          newPattern += ".*?"
        } else if (c === '_') {
          newPattern += "."
        } else if (c === '[' || ']') {
          newPattern += c
        } else newPattern += fix(c)
      }
      return newPattern;
    }
    return new RegExp(`^${fixPattern(pattern,esc)}$`,cs ? '' : 'i').exec(expr) != null;
  }
Copy

Cette fonction permet de répliquer la fonctionnalité de l’expression like de sql. Voyons les différents cas où elle peut être utilisée

 -- Additional Params: -t JavaScript
CREATE PROCEDURE ProcedureLike @VariableValue VARCHAR(50) AS
BEGIN
	IF @VariableValue like '%c%'
	BEGIN
		Select AValue from ATable;
	END;
	IF @VariableValue not like '%c%'
	BEGIN
		Select BValue from BTable;
	END;
  IF @VariableValue like '%c!%%' escape '!'
	BEGIN
		Select CValue from CTable;
	END;
END;
Copy

Dans le dernier code, il y a un normal like, un not like et un like avec échappement. La transformation sera

CREATE OR REPLACE PROCEDURE ProcedureLike (VARIABLEVALUE STRING)
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// SnowConvert AI Helpers Code section is omitted.

	if (LIKE(VARIABLEVALUE,`%c%`)) {
		{
			EXEC(`		Select
		   AValue
		from
		   ATable`);
		}
	}
	if (!LIKE(VARIABLEVALUE,`%c%`)) {
		{
			EXEC(`		Select
		   BValue
		from
		   BTable`);
		}
	}
	if (LIKE(VARIABLEVALUE,`%c!%%`,`!`)) {
		{
			EXEC(`		Select
		   CValue
		from
		   CTable`);
		}
	}
$$;
Copy

Notez que les fonctionnalités sont transformées en appels de fonctions

LIKE(VARIABLEVALUE,`%c%`)
!LIKE(VARIABLEVALUE,`%c%`)
LIKE(VARIABLEVALUE,`%c!%%`,`!`)
Copy

Les paramètres que la fonction LIKE reçoit sont les suivants :

  • L’expression en cours d’évaluation.

  • Le modèle de comparaison

  • S’il est présent, le caractère d’échappement, ce paramètre est facultatif.

Assistant Select

Génère une fonction appelée SELECT lorsqu’une valeur scalaire doit être définie dans une variable

 -- Additional Params: -t JavaScript
CREATE PROCEDURE MAX_EMPLOYEE_ID
AS
BEGIN
   DECLARE @VARIABLE INT 
   SET @VARIABLE = (SELECT MAX(EMPLOYEE_ID) FROM EMPLOYEES);
   RETURN @VARIABLE
END;
Copy

Dans ce cas, il générera le code suivant avec l’assistant SELECT

CREATE OR REPLACE PROCEDURE MAX_EMPLOYEE_ID ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
   // SnowConvert AI Helpers Code section is omitted.

   let VARIABLE;
   VARIABLE = EXEC(`SELECT
   MAX(EMPLOYEE_ID) FROM
   EMPLOYEES`);
   return VARIABLE;
$$;
Copy

L’assistant SELECT pourrait également être utilisé pour insérer dans une valeur locale une valeur récupérée à partir d’une requête. L’assistant a été conçu spécifiquement pour prendre en charge le même comportement que SQL Server SELECT @local_variable. Le paramètre args représente chaque opération appliquée à toutes les variables locales à l’intérieur de select. Voir aussi SELECT @Variable. Par exemple :

 -- Additional Params: -t JavaScript
CREATE PROCEDURE [PROCEDURE1] AS

DECLARE @VAR1 int;
DECLARE @VAR2 int;
select @VAR1 = col1 + col2, @VAR2 += col1 from table1;

GO
Copy

Dans ce cas, les assignations de variables seront traduites en JavaScript lambdas afin d’émuler le comportement de SQL Server.

CREATE OR REPLACE PROCEDURE PROCEDURE1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.

let VAR1;
let VAR2;
SELECT(`   col1 + col2,
   col1
   from
   table1`,[],(value) => VAR1 = value,(value) => VAR2 += value);
$$;
Copy

Assistant RAISERROR

Cette aide est générée lorsqu’il existe des utilisations d’un appel à RAISERROR dans le code source. Exemple :

 var RAISERROR = (message,severity,state) => {
    snowflake.execute({
      sqlText : `SELECT UPDATE_ERROR_VARS_UDF(?,?,?)`,
      binds : [message,severity,state]
    });
    var msg = `Message: ${message}, Level: ${severity}, State: ${state}`;
    throw msg;
  };
Copy

RAISERROR exécute la commande UPDATE_ERROR_VARS_UDF afin de stocker la valeur du message d’erreur, la gravité et l’état en tant que variables d’environnement, au cas où elles devraient être utilisées en appelant l’une des fonctions intégrées ERROR. Enfin, le message d’erreur est envoyé dans le même format que celui de SQL Server.

Assistant de la fonction Identify

Cette aide est générée chaque fois que la fonction Identify est utilisée sur un Select Into à l’intérieur d’une procédure.

  var IdentityHelper = (seed,increment) => {
      var sequenceString = "`CREATE OR REPLACE SEQUENCE SnowConvert_Temp_Seq START = ${seed} INCREMENT = ${increment}`";
      return EXEC(sequenceString);
Copy

Les paramètres de cet assistant sont les mêmes que ceux de la fonction originale, elle est créée afin de générer une séquence imitant le comportement de la fonction Identify dans TSQL, les changements apportés au code original sont les suivants :

  • Un appel de méthode supplémentaire à la fonction IdentityHelper en utilisant les mêmes paramètres que ceux trouvés dans le code source.

  • Et appel à la conception d’une fonction IDENTITY_UDF pour obtenir la valeur suivante dans la séquence.

   IdentityHelper(1,1)
   EXEC(`CREATE TABLE PUBLIC.department_table3 AS SELECT IDENTITY_UDF() /*** MSC-WARNING - MSCEWI1046 - 'identity' FUNCTION MAPPED TO 'IDENTITY_UDF', FUNCTIONAL EQUIVALENCE VERIFICATION PENDING ***/ as Primary_Rank
from PUBLIC.department_table`);
Copy

Comme dans TSQL, si aucun paramètre n’est donné, les valeurs par défaut seront (1,1).

Aide à la procédure CALL

Cet assistant est généré chaque fois qu’il y a un appel à ce qui était auparavant une fonction définie par l’utilisateur, mais qui est maintenant une procédure à la suite du processus de traduction.

    var CALL = (sql,binds = [],...args) => {
      EXEC("CALL " + sql,binds);
      _ROWS.next();
      return (_ROWS.getColumnValue(1))[0];
   };
Copy

L’objectif de cet assistant est d’encapsuler la logique requise pour appeler des procédures comme s’il s’agissait de fonctions.

N’oubliez pas que cette fonctionnalité est limitée, car les procédures ne peuvent pas être invoquées dans le cadre de requêtes telles que SELECT.

Exemple d’utilisation, en supposant que FooSelfAssign(@PAR INT) ait été traduit en procédure :

 // Input code
DECLARE @VAR1 INT = FooSelfAssign(1);
DECLARE @VAR4 INT = FooSelfAssign(FooSelfAssign(FooSelfAssign(FooSelfAssign(4))));
Copy
 // Output code
let VAR1 = CALL(`FooSelfAssign(1)`)
let VAR4 = CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(4)`)])])]);
Copy

Notez que la traduction de VAR1 est très simple, mais que pour VAR4, le CALL le plus éloigné contient une liste avec le reste des CALLs, en tant qu’annonces.

Chaque CALL successif est traduit en une liaison, s’il est contenu dans un autre CALL.

2. Variables

DECLARE @Variable

SQL Server

DECLARE @product_list VARCHAR(MAX) = ' ';
DECLARE @Variable1 AS VARCHAR(100), @Variable2 AS VARCHAR(100);
Copy

Snowflake

let PRODUCT_LIST = ` `;
let VARIABLE1;
let VARIABLE2;
Copy

DECLARE @Variable Table

Dans ce cas, DECLARE est utilisé pour déclarer une table de variables, voyons un exemple.

 -- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1
AS
BEGIN
DECLARE @VariableNameTable TABLE   
 ( 
 [Col1] INT NOT NULL,
 [Col2] INT NOT NULL 
 );
INSERT INTO @VariableNameTable Values(111,222);
Select * from @VariableNameTable;
END

Exec PROC1;
Copy

Si nous exécutons ce code dans le serveur Sql, nous obtiendrons le résultat suivant

col1

col2

111

222

Voyons maintenant la transformation dans Snowflake

CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
 // SnowConvert AI Helpers Code section is omitted.

 {
  EXEC(`CREATE OR REPLACE TEMPORARY TABLE T_VariableNameTable
(
   Col1 INT NOT NULL,
   Col2 INT NOT NULL
)`);
  EXEC(`INSERT INTO T_VariableNameTable Values(111,222)`);
  EXEC(`Select
   *
from
   T_VariableNameTable`);
 }
 EXEC(`CALL PROC1()`);
$$;
Copy

Notez que les lignes 61 à 67 sont les résultats de ces instructions à l’intérieur de la procédure.

La table de variables déclarées est transformée en table temporaire. Notez que le nom, qui dans le nom le caractère @ a été remplacé pour T_.

Si nous exécutons ce code dans Snowflake, nous n’obtiendrons aucun résultat. Il affichera simplement null. C’est parce que le dernier Select se trouve maintenant dans l’assistant EXEC. Alors, comment savoir si la table est là ?

Puisqu’elle a été créée en tant que table temporaire dans la procédure dans EXEC, nous pouvons effectuer une sélection dans cette table en dehors de la procédure.

 Select * from PUBLIC.T_VariableNameTable;
Copy

Si nous exécutons cette instruction, nous obtiendrons le résultat suivant

col1

col2

111

222

SET @Variable

Pour l’instant, la variable Set est transformée en fonction de l’expression qu’elle contient dans sa partie droite.

Si l’expression a une transformation, elle sera transformée en son équivalent JavaScript.

Exemple

 -- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1
AS
BEGIN
	SET @product_list2 = '';
    SET @product_list = '';
    SET @var1 += '';
    SET @var2 &= '';
    SET @var3 ^= '';
    SET @var4 |= '';
    SET @var5 /= '';
    SET @var6 %= '';
    SET @var7 *= '';
    SET @var8 -= '';
    SET @ProviderStatement = 'SELECT * FROM TABLE1
WHERE COL1 = '+@PARAM1+ ' AND COL2 = ' + @LOCALVAR1;
    SET @NotSupported = functionValue(a,b,c);
END
    
Copy

Snowflake

CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// SnowConvert AI Helpers Code section is omitted.
	
	PRODUCT_LIST2 = ``;
	PRODUCT_LIST = ``;
	VAR1 += ``;
	VAR2 &= ``;
	VAR3 ^= ``;
	VAR4 |= ``;
	VAR5 /= ``;
	VAR6 %= ``;
	VAR7 *= ``;
	VAR8 -= ``;
	PROVIDERSTATEMENT = `SELECT
   *
FROM
   TABLE1
WHERE
   COL1 = ${PARAM1}
   AND COL2 = ${LOCALVAR1};`;
	NOTSUPPORTED = SELECT(`   functionValue(a,b,c) !!!RESOLVE EWI!!! /*** SSC-EWI-0073 - PENDING FUNCTIONAL EQUIVALENCE REVIEW FOR 'functionValue' NODE ***/!!!`);
$$;
Copy

Comme vous pouvez le voir dans l’exemple, la valeur de la variable NOTSUPPORTED est commentée puisqu’elle n’est pas transformée pour le moment. Cela signifie que la transformation n’est pas encore terminée.

D’autres types d’ensembles sont commentés, par exemple les suivants

SQL Server

 -- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
;

SET NOCOUNT ON
;

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
;

SET NOCOUNT OFF
;
END
Copy

Snowflake

CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.

/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED*/
;
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET NOCOUNT ON*/
;
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED*/
;
/*** SSC-EWI-0040 - THE 'SET' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
/*SET NOCOUNT OFF*/
;
$$;
Copy

SELECT @Variable

Pour l’instant, SELECT @variable est transformé en un simple select, en supprimant les assignations de variables et en conservant les expressions à droite de l’opérateur. Les opérations d’affectation des variables locales dans select seront remplacées par des fonctions arrow qui représentent le même comportement que l’opération effectuée lors de l’affectation de la variable locale dans SQL Server.

SQL Server

 -- Additional Params: -t JavaScript
CREATE PROCEDURE PROC1 AS
DECLARE @VAR1 int;
DECLARE @VAR2 int;
SELECT @VAR1 = COL1 + COL2, @VAR2 = COL3 FROM TABLE1;
GO
Copy

Snowflake

CREATE OR REPLACE PROCEDURE PROC1 ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
// SnowConvert AI Helpers Code section is omitted.

let VAR1;
let VAR2;
SELECT(`   COL1 + COL2,
   COL3
   FROM
   TABLE1`,[],(value) => VAR1 = value,(value) => VAR2 = value);
$$;
Copy

3. Statements translation

SELECT

Forme de base

La forme SELECT de base n’a pas de liaisons, donc la traduction implique la création d’un appel à la fonction d’aide EXEC, avec un paramètre. Par exemple :

 -- Source code:
SELECT * FROM DEMO_TABLE_1;
Copy
 // Translated code:
EXEC(`SELECT * FROM DEMO_TABLE_1`);
Copy

IF

SQL Server

IF Conditional_Expression
   -- SQL Statement
ELSE IF Conditiona_Expression2
   -- SQL Statement
ELSE
   -- SQL Statement
Copy

Snowflake

 if (Conditional_Expression) {
    // SQL Statement
} else if (Conditional_Expression2) {
    // SQL Statement
} else{
    // SQL Statement
}
Copy

WHILE

SQL Server

WHILE ( Conditional_Expression )
BEGIN
   -- SQL STATEMENTS
END;
Copy

Snowflake

while ( Conditional_Expression )
{
  // SQL STATEMENTS
}
Copy

EXEC / EXECUTE

SQL Server

 -- Execute simple statement
Exec('Select 1');

-- Execute statement using Dynamic Sql
Exec('Select ' + @par1 + ' from [db].[t1]');

-- Execute Procedure with parameter
EXEC db.sp2 'Create proc [db].[p3] AS', @par1, 1
Copy

Snowflake

 -- Execute simple statement
EXEC(`Select 1`);

-- Execute statement using Dynamic Sql
EXEC(`Select ${PAR1} from MYDB.db.t1`);

-- Execute Procedure with parameter
EXEC(`CALL db.sp2(/*** SSC-EWI-0038 - THIS STATEMENT MAY BE A DYNAMIC SQL THAT COULD NOT BE RECOGNIZED AND CONVERTED ***/
'Select * from MYDB.db.t1', ?, 1, Default)`,[PAR1]);
Copy

THROW

La transformation pour THROW garantit que le bloc catch qui reçoit l’erreur a accès aux informations spécifiées dans l’instruction d’origine.

SQL Server

 -- Case 1
THROW

-- Case 2
THROW 123, 'The error message', 1

-- Case 3
THROW @var1, @var2, @var3
Copy

Snowflake

 // Case 1
throw {};

// Case 2
throw { code: 123, message: "The error message", status: 1 };

// Case 3
throw { code: VAR1, message: VAR2, status: VAR3 };
Copy

RAISERROR

La fonction SQL Serveur RAISERROR n’est pas prise en charge dans Snowflake. SnowConvert AI identifie toutes les utilisations afin de générer une aide qui émule le comportement original. Exemple :

SQL Server

 -- Additional Params: -t JavaScript
CREATE OR ALTER PROCEDURE  RAISERRORTEST AS
BEGIN
    DECLARE @MessageTXT VARCHAR = 'ERROR MESSAGE';
    RAISERROR (N'E_INVALIDARG', 16, 1);
    RAISERROR ('Diagram does not exist or you do not have permission.', 16, 1);
    RAISERROR(@MessageTXT, 16, 1);
END
GO
Copy

Snowflake

CREATE OR REPLACE PROCEDURE RAISERRORTEST ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
    // SnowConvert AI Helpers Code section is omitted.

    let MESSAGETXT = `ERROR MESSAGE`;
    RAISERROR("E_INVALIDARG","16","1");
    RAISERROR("Diagram does not exist or you do not have permission.","16","1");
    RAISERROR(MESSAGETXT,"16","1");
$$;
Copy

BREAK/CONTINUE

La transformation break/continue permet d’arrêter le flux du code ou de le poursuivre avec un autre bloc.

SQL Server

-- Additional Params: -t JavaScript
CREATE PROCEDURE ProcSample
AS
BEGIN
IF @@ROWCOUNT > 0
  Continue;
ELSE
  BREAK;
END
Copy

Snowflake

CREATE OR REPLACE PROCEDURE ProcSample ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
  // SnowConvert AI Helpers Code section is omitted.

  if (ROW_COUNT > 0) {
    continue;
  } else {
    break;
  }
$$;
Copy

INSERT INTO EXEC

Le code est légèrement modifié car l’instruction INSERT INTO [Table] EXEC(...) n’est pas prise en charge par Snowflake. Ceci nous permet de répliquer le comportement en ajoutant quelques lignes de code :

  • La première ligne ajoutée est un appel à insertIntoTemporaryTable où le code extrait de l’argument se trouve à l’intérieur de EXEC, ce qui permet d’insérer le jeu de résultats dans une table temporaire. Pour plus d’informations sur cette fonction, consultez la section Assistant Into EXEC.

  • L’Insert de Exec est supprimé du code et une requête récupère les résultats de EXEC dans la table temporaire.

SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable    
Copy
  • La dernière ligne ajoutée est une instruction DROP TABLE pour la table temporaire ajoutée.

   DROP TABLE SnowConvertPivotTemporaryTable    
Copy

SQL Server

INSERT INTO #Table1
EXEC ('SELECT
Table1.ID
FROM Population');

INSERT INTO #Table1
EXEC (@DBTables);
Copy

Snowflake

  insertIntoTemporaryTable(`SELECT Table1.ID FROM MYDB.PUBLIC.Population)
  EXEC(`INSERT INTO MYDB.PUBLIC.T_Table1 SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable`);
  EXEC(`DROP TABLE SnowConvertPivotTemporaryTable`)
  
  insertIntoTemporaryTable(`${DBTABLES}`)
  EXEC(`INSERT INTO MYDB.PUBLIC.T_Table1 SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable`);
  EXEC(`DROP TABLE SnowConvertPivotTemporaryTable`)
Copy

BEGIN TRANSACTION

BEGIN TRANSACTION est transformée en commande BEGIN de Snowflake et insérée dans un appel à l’assistant EXEC.

L’assistant est chargé de l’exécution effective de l’application BEGIN.

SQL Server

 -- Input code
BEGIN TRAN @transaction_name;
Copy

Snowflake

 // Output code
EXEC(`BEGIN`, []);
Copy

COMMIT TRANSACTION

COMMIT TRANSACTION est transformée en commande COMMIT de Snowflake et insérée dans un appel à l’assistant EXEC.

L’assistant est chargé de l’exécution effective de l’application COMMIT.

SQL Server

 -- Input code
COMMIT TRAN @transaction_name;
Copy

Snowflake

 // Output code
EXEC(`COMMIT`, []);
Copy

ROLLBACK TRANSACTION

ROLLBACK TRANSACTION est transformée en commande ROLLBACK de Snowflake et insérée dans un appel à l’assistant EXEC.

L’assistant est chargé de l’exécution effective de l’application ROLLBACK.

SQL Server

 -- Input code
ROLLBACK TRAN @transaction_name;
Copy

Snowflake

 // Output code
EXEC(`ROLLBACK`, []);
Copy

WAITFOR DELAY

La clause WAITFOR DELAY est transformée en fonction SYSTEM$WAIT de Snowflake. Le paramètre time_to_pass de DELAY est transformé en secondes, pour utilisation comme paramètre dans la fonction SYSTEM$WAIT.

Les autres variantes de la clause WAITFOR ne sont pas prises en charge par Snowflake et sont donc signalées par le message correspondant.

SQL Server

 -- Input code
1) WAITFOR DELAY '02:00';
2) WAITFOR TIME '13:30';
3) WAITFOR (RECEIVE TOP (1)
   @dh = conversation_handle,
   @mt = message_type_name,
   @body = message_body
   FROM [eqe]), TIMEOUT 5000;
Copy

Snowflake

 // Output code
1) EXEC(`SYSTEM$WAIT(120)`,[]);
2) /*** SSC-EWI-0040 - THE 'WAIT FOR' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
   /*WAITFOR TIME '13:30'*/
   ;
3) /*** SSC-EWI-0040 - THE 'WAIT FOR' CLAUSE IS NOT SUPPORTED IN SNOWFLAKE ***/
   /*WAITFOR (RECEIVE TOP (1)
      @dh = conversation_handle,
      @mt = message_type_name,
      @body = message_body
      FROM [eqe]), TIMEOUT 5000*/
   ;
Copy

3. Cursors

Les CURSORS n’étant pas pris en charge dans Snowflake, SnowConvert AI mappe leur fonctionnalité vers une aide JavaScript qui émule le comportement original dans la plateforme cible. Exemple :

SQL Server

 -- Additional Params: -t JavaScript
CREATE PROCEDURE [procCursorHelper] AS

DECLARE vendor_cursor CURSOR FOR   
    SELECT VendorID, Name  
    FROM Purchasing.Vendor  
    WHERE PreferredVendorStatus = 1  
    ORDER BY VendorID;
GO
Copy

Snowflake

CREATE OR REPLACE PROCEDURE procCursorHelper ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
    // SnowConvert AI Helpers Code section is omitted.

    var VENDOR_CURSOR = new CURSOR(`SELECT
       VendorID,
       Name
    FROM
       Purchasing.Vendor
    WHERE
       PreferredVendorStatus = 1
    ORDER BY VendorID`,[],false);
$$;
Copy

DECLARE CURSOR

SQL Server

DECLARE myCursor1 CURSOR FOR SELECT COL1 FROM TABLE1
Copy

Snowflake

let myCursor1 = new CURSOR(`SELECT COL1 FROM TABLE1`,() => []);
Copy

OPEN

SQL Server

OPEN myCursor1
OPEN GLOBAL myCursor2
Copy

Snowflake

myCursor1.OPEN();
myCursor2.OPEN()
Copy

FETCH

SQL Server

DECLARE @VALUE1 INT
FETCH NEXT FROM myCursor1 into @VALUE1
Copy

Snowflake

var VALUE1;
myCursor1.FETCH_NEXT();
VALUE1 = myCursor1.INTO();
Copy

CLOSE

SQL Server

CLOSE myCursor1
CLOSE GLOBAL myCursor2
Copy

Snowflake

myCursor1.CLOSE()
myCursor2.CLOSE()
Copy

DEALLOCATE

SQL Server

DEALLOCATE myCursor1
DEALLOCATE GLOBAL myCursor2
Copy

Snowflake

myCursor1.DEALLOCATE()
myCursor2.DEALLOCATE()
Copy

@@FETCH_STATUS

SQL Server

 @@FETCH_STATUS
Copy

Snowflake

myCursor1.FETCH_STATUS()
Copy

@@CURSOR_ROWS

SQL Server

 @@CURSOR_ROWS
Copy

Snowflake

myCursor1.FETCH_STATUS()
Copy

4. Expressions

Opérations binaires

SQL Server

SET @var1 = 1 + 1;
SET @var1 = 1 - 1;
SET @var1 = 1 / 1;
SET @var1 = 1 * 1;
SET @var1 = 1 OR 1;
SET @var1 = 1 AND 1;
Copy

Snowflake

VAR1 = 1 + 1;
VAR1 = 1 - 1;
VAR1 = 1 / 1;
VAR1 = 1 * 1;
VAR1 = 1 || 1;
VAR1 = 1 && 1;
Copy

Conditionnels

SQL Server

@var1 > 0
@var1 = 0
@var1 < 0
@var1 <> 0
Copy

Snowflake

VAR1 > 0
VAR1 = 0
VAR1 < 0
VAR1 != 0
Copy

Prédicat NULL

SQL Server
@var1 is null
@var2 is not null
Copy
Snowflake
VAR1 == null
VAR2 != null
Copy

5. Labels and Goto

Les étiquettes n’ont pas le même comportement dans JavaScript que dans SQL Server. Pour simuler le comportement, elles sont transformées en fonctions. Son utilisation est remplacée par un appel à la fonction générée qui contient toute la logique de l’étiquette. Exemple :

Code source

 -- Additional Params: -t JavaScript
CREATE PROCEDURE [procWithLabels] 
AS
SUCCESS_EXIT:
	SET @ErrorStatus = 0
	RETURN @ErrorStatus

ERROR_EXIT:
	RETURN @ErrorStatus
Copy

Snowflake

CREATE OR REPLACE PROCEDURE procWithLabels ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// SnowConvert AI Helpers Code section is omitted.

	SUCCESS_EXIT();
	ERROR_EXIT();
	function SUCCESS_EXIT() {
		ERRORSTATUS = 0;
		return ERRORSTATUS;
	}
	function ERROR_EXIT() {
		return ERRORSTATUS;
	}
$$;
Copy

Comme vous le voyez dans l’exemple ci-dessus, les déclarations de fonctions qui étaient les étiquettes dans le code source, seront placées à la fin du code afin de le rendre plus propre.

GOTO est une autre commande qui n’existe pas sur JavaScript. Pour simuler son comportement, leurs utilisations sont transformées en appels à la fonction (label) qui est référencée, précédés d’une instruction de retour. Exemple :

SQL Server

 -- Additional Params: -t JavaScript
CREATE PROCEDURE [procWithLabels] 
AS
DECLARE @ErrorStatus int = 0;
IF @ErrorStatus <> 0 GOTO ERROR_EXIT
	
SUCCESS_EXIT:
	SET @ErrorStatus = 0
	RETURN @ErrorStatus

ERROR_EXIT:
	RETURN @ErrorStatus
Copy

Snowflake

CREATE OR REPLACE PROCEDURE procWithLabels ()
RETURNS STRING
LANGUAGE JAVASCRIPT
COMMENT = '{"origin":"sf_sc","name":"snowconvert","version":{"major":1, "minor":0},{"attributes":{"component":"transact"}}'
EXECUTE AS CALLER
AS
$$
	// SnowConvert AI Helpers Code section is omitted.

	let ERRORSTATUS = 0;
	if (ERRORSTATUS != 0) {
		return ERROR_EXIT();
	}
	SUCCESS_EXIT();
	ERROR_EXIT();
	function SUCCESS_EXIT() {
		ERRORSTATUS = 0;
		return ERRORSTATUS;
	}
	function ERROR_EXIT() {
		return ERRORSTATUS;
	}
$$;
Copy

Comme vous le voyez dans l’exemple ci-dessus, le retour est ajouté à l’appel de la fonction, afin d’arrêter le flux de code comme le fait SQL Server avec GOTO.