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
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.
$$;
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 :
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.
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.
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.
ERROR VARS : Le bloc de capture EXEC établit une liste de variables d’erreur telles que
MESSAGE_TEXT,SQLCODE,SQLSTATE,PROC_NAMEetERROR_LINEqui 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’utilisateurUPDATE_ERROR_VARSsera 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;
}
}
};
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
-- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_1
GO
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;`);
$$;
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
-- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_2 N'''Hello World!'''
GO
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};`);
$$;
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
-- =============================================
-- Example to execute the stored procedure
-- =============================================
EXECUTE dbo.EXEC_EXAMPLE_3 N'''Hello World!'''
GO
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]);
$$;
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
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
$$
.
.
.
$$;
CALL PROC_WITH_DEFAULT_PARAMS1(param2 => 10, param4 => 15);
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';
$$;
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`)
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;
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;
}
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;
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`);
}
}
$$;
Notez que les fonctionnalités sont transformées en appels de fonctions
LIKE(VARIABLEVALUE,`%c%`)
!LIKE(VARIABLEVALUE,`%c%`)
LIKE(VARIABLEVALUE,`%c!%%`,`!`)
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;
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;
$$;
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
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);
$$;
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;
};
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);
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`);
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];
};
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))));
// Output code
let VAR1 = CALL(`FooSelfAssign(1)`)
let VAR4 = CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(?)`,[CALL(`FooSelfAssign(4)`)])])]);
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);
Snowflake¶
let PRODUCT_LIST = ` `;
let VARIABLE1;
let VARIABLE2;
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;
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()`);
$$;
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;
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
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 ***/!!!`);
$$;
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
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*/
;
$$;
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
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);
$$;
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;
// Translated code:
EXEC(`SELECT * FROM DEMO_TABLE_1`);
IF¶
SQL Server¶
IF Conditional_Expression
-- SQL Statement
ELSE IF Conditiona_Expression2
-- SQL Statement
ELSE
-- SQL Statement
Snowflake¶
if (Conditional_Expression) {
// SQL Statement
} else if (Conditional_Expression2) {
// SQL Statement
} else{
// SQL Statement
}
WHILE¶
SQL Server¶
WHILE ( Conditional_Expression )
BEGIN
-- SQL STATEMENTS
END;
Snowflake¶
while ( Conditional_Expression )
{
// SQL STATEMENTS
}
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
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]);
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
Snowflake¶
// Case 1
throw {};
// Case 2
throw { code: 123, message: "The error message", status: 1 };
// Case 3
throw { code: VAR1, message: VAR2, status: VAR3 };
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
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");
$$;
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
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;
}
$$;
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 à
insertIntoTemporaryTableoù le code extrait de l’argument se trouve à l’intérieur deEXEC, 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
La dernière ligne ajoutée est une instruction DROP TABLE pour la table temporaire ajoutée.
DROP TABLE SnowConvertPivotTemporaryTable
SQL Server¶
INSERT INTO #Table1
EXEC ('SELECT
Table1.ID
FROM Population');
INSERT INTO #Table1
EXEC (@DBTables);
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`)
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;
Snowflake¶
// Output code
EXEC(`BEGIN`, []);
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;
Snowflake¶
// Output code
EXEC(`COMMIT`, []);
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;
Snowflake¶
// Output code
EXEC(`ROLLBACK`, []);
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;
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*/
;
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
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);
$$;
DECLARE CURSOR¶
SQL Server¶
DECLARE myCursor1 CURSOR FOR SELECT COL1 FROM TABLE1
Snowflake¶
let myCursor1 = new CURSOR(`SELECT COL1 FROM TABLE1`,() => []);
OPEN¶
SQL Server¶
OPEN myCursor1
OPEN GLOBAL myCursor2
Snowflake¶
myCursor1.OPEN();
myCursor2.OPEN()
FETCH¶
SQL Server¶
DECLARE @VALUE1 INT
FETCH NEXT FROM myCursor1 into @VALUE1
Snowflake¶
var VALUE1;
myCursor1.FETCH_NEXT();
VALUE1 = myCursor1.INTO();
CLOSE¶
SQL Server¶
CLOSE myCursor1
CLOSE GLOBAL myCursor2
Snowflake¶
myCursor1.CLOSE()
myCursor2.CLOSE()
DEALLOCATE¶
SQL Server¶
DEALLOCATE myCursor1
DEALLOCATE GLOBAL myCursor2
Snowflake¶
myCursor1.DEALLOCATE()
myCursor2.DEALLOCATE()
@@FETCH_STATUS¶
SQL Server¶
@@FETCH_STATUS
Snowflake¶
myCursor1.FETCH_STATUS()
@@CURSOR_ROWS¶
SQL Server¶
@@CURSOR_ROWS
Snowflake¶
myCursor1.FETCH_STATUS()
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;
Snowflake¶
VAR1 = 1 + 1;
VAR1 = 1 - 1;
VAR1 = 1 / 1;
VAR1 = 1 * 1;
VAR1 = 1 || 1;
VAR1 = 1 && 1;
Conditionnels¶
SQL Server¶
@var1 > 0
@var1 = 0
@var1 < 0
@var1 <> 0
Snowflake¶
VAR1 > 0
VAR1 = 0
VAR1 < 0
VAR1 != 0
Prédicat NULL¶
SQL Server¶
@var1 is null
@var2 is not null
Snowflake¶
VAR1 == null
VAR2 != null
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
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;
}
$$;
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
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;
}
$$;
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.