SnowConvert AI - SQL Server-Azure Synapse - Procedimentos

Esta seção documenta a transformação da sintaxe e das instruções TSQL de procedimento para o snowflake javascript

Applies to
  • SQL Server

  • Azure Synapse Analytics

Nota

Algumas partes do código de saída foram omitidas por motivos de clareza.

1. CREATE PROCEDURE Translation

O Snowflake CREATE PROCEDURE é definido na sintaxe SQL, enquanto suas instruções internas são definidas em 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

Tipos de dados de parâmetros

Os tipos de dados de parâmetros estão sendo traduzidos para o equivalente do Snowflake. Consulte também Tipos de dados.

Auxiliar EXEC

Para que seja possível executar instruções de um procedimento no ambiente SnowFlake, essas instruções precisam ser pré-processadas e adaptadas para refletir sua execução em diversas variáveis específicas da linguagem de origem.

O SnowConvert AI traduz automaticamente as instruções compatíveis e faz uso de um auxiliar EXEC. Esse auxiliar fornece recursos de acesso e atualização para muitas variáveis que simulam como seria a execução dessas instruções em seu ambiente nativo.

Por exemplo, você pode ver que, nos procedimentos migrados, há um bloco de código que é sempre adicionado. Na próxima seção, explicaremos a estrutura básica desse código. Lembre-se de que estamos sempre avaliando e buscando maneiras novas e aprimoradas de agilizar as transformações e qualquer auxiliar que precisarmos.

Estrutura

A estrutura básica do auxiliar EXEC é a seguinte:

  1. Seção de declaração de variáveis: Aqui, declaramos as diferentes variáveis ou objetos que conterão valores associados à execução das instruções dentro do procedimento. Isso inclui valores como o número de linhas afetadas por uma instrução ou até mesmo o próprio conjunto de resultados.

  2. Declaração da função fixBind: Esta é uma função auxiliar usada para corrigir vinculações quando elas são do tipo Date.

  3. Declaração da função EXEC: Esta é a principal função do auxiliar EXEC. Ela recebe a instrução a ser executada, a matriz de vinculações (basicamente as variáveis ou os parâmetros que podem ser modificados pela execução e que exigem a permanência dos dados durante toda a execução do procedimento), o sinalizador noCatch que determina se ERROR_HANDLERS deve ser usado e a função catchFunction para executar o código personalizado quando houver uma exceção na execução da instrução. O corpo da função EXEC é muito simples: executa a instrução e armazena todos os dados valiosos produzidos por sua execução, tudo dentro de um bloco de tratamento de erros.

  4. ERROR VARS: O bloco de captura EXEC configura uma lista de variáveis de erro, como MESSAGE_TEXT, SQLCODE, SQLSTATE, PROC_NAME e ERROR_LINE, que podem ser usadas para recuperar valores de funções definidas pelo usuário, a fim de emular o comportamento das funções integradas do SQL Server ERROR_LINE, ERROR_MESSAGE, ERROR_NUMBER, ERROR_PROCEDURE e ERROR_STATE . Depois que todas essas variáveis forem definidas com um valor, a função definida pelo usuário UPDATE_ERROR_VARS será responsável por atualizar algumas variáveis de ambiente com os valores de erro, para ter acesso a elas no escopo SQL.

Código

O bloco de código a seguir representa o auxiliar EXEC dentro de um procedimento:

   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

Exemplo simples de EXEC

Este é um exemplo simples de uma chamada EXEC dentro de um procedimento armazenado

Código-fonte

 -- 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

Código esperado

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 em um procedimento armazenado com um parâmetro

Neste exemplo, o comando EXEC está dentro de um procedimento armazenado e recebe um valor de parâmetro

Código-fonte

 -- 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

Código esperado

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 invocando um procedimento armazenado com um parâmetro

Neste exemplo, o EXEC invoca outro procedimento armazenado e adiciona um parâmetro

Código-fonte

 -- 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

Código esperado

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

Parâmetros com valor padrão.

No SqlServer, pode haver parâmetros com um valor padrão, caso não sejam especificados quando um procedimento estiver sendo chamado.

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

auxiliar 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

Auxiliar Insert Into EXEC

O auxiliar Insert into Exec gera uma função chamada Insert insertIntoTemporaryTable(sql). Essa função permitirá que a transformação de INSERT INTO TABLE_NAME EXEC(...) de TSQL para Snowflake imite o comportamento da instrução original, inserindo seus dados em uma tabela temporária e, em seguida, adicionando-os novamente ao Insert original.

Para obter mais informações sobre como o código para essa instrução é modificado, consulte a seção para o Insert Into Exec

Nota

Esse código gerado para INSERT INTO EXEC pode apresentar problemas de desempenho ao lidar com instruções EXECUTE que contêm várias consultas.

   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

Auxiliar LIKE

Caso uma expressão semelhante seja encontrada em um procedimento, por exemplo

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

Como o interior do procedimento é transformado em javascript, a expressão like gerará um erro. Para manter a funcionalidade, uma função é adicionada no início do procedimento se uma expressão semelhante for encontrada.

   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

Com essa função, podemos replicar a funcionalidade da expressão like do sql. Vejamos os diferentes casos em que ele pode ser usado

 -- 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

No último código, há um like normal, um not like e um like com escape. A transformação será

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

Observe que likes são transformados em chamadas de função

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

Os parâmetros que a função LIKE recebe são os seguintes:

  • A expressão que está sendo avaliada.

  • O padrão de comparação

  • Se estiver presente, o caractere de escape, que é um parâmetro opcional.

Auxiliar Select

Gera uma função chamada SELECT quando um valor escalar precisa ser definido em uma variável

 -- 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

Nesse caso, ele gerará o seguinte código com o auxiliar 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

O auxiliar SELECT também pode ser usado para inserir em um valor local um valor recuperado de uma consulta. O auxiliar foi projetado especificamente para suportar o mesmo comportamento do SQL Server SELECT @local_variable. O parâmetro args representa cada operação aplicada a todas as variáveis locais dentro do select. Consulte também SELECT @Variable. Por exemplo:

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

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

GO
Copy

Nesse caso, as atribuições de variáveis serão convertidas para lambdas JavaScript a fim de emular o comportamento do 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

Auxiliar RAISERROR

Esse auxiliar é gerado quando há usos de uma chamada RAISERROR no código-fonte. Exemplo:

 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

O RAISERROR executa o UPDATE_ERROR_VARS_UDF para armazenar o valor da mensagem de erro, a gravidade e o estado como variáveis de ambiente, caso precisem ser usados ao chamar qualquer uma das funções integradas de ERROR. Por fim, a mensagem de erro é lançada com o mesmo formato do SQL Server.

Auxiliar de função Identity

Esse auxiliar é gerado sempre que a função Identity é usada em um Select Into dentro de um procedimento.

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

Os parâmetros desse auxiliar são os mesmos da função original. Ele foi criado para gerar uma sequência que imite o comportamento da função Identity no TSQL. As alterações no código original são as seguintes:

  • Uma chamada de método adicional para a função IdentityHelper usando os mesmos parâmetros encontrados no código-fonte.

  • E uma chamada para IDENTITY_UDF um design de função para obter o próximo valor na sequência.

   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

Assim como no TSQL, se nenhum parâmetro for fornecido, (1,1) serão os valores padrão.

Auxiliar de procedimento CALL

Esse auxiliar é gerado sempre que há uma chamada para o que antes era uma função definida pelo usuário, mas que agora é um procedimento como resultado do processo de conversão.

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

O objetivo desse auxiliar é encapsular a lógica necessária para chamar procedimentos como se fossem funções.

Lembre-se de que essa funcionalidade é limitada, pois os procedimentos não podem ser invocados em consultas como SELECT.

Exemplo de uso, supondo que FooSelfAssign(@PAR INT) tenha sido convertido para um procedimento:

 // 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

Observe que a conversão para VAR1 é muito simples, mas para VAR4, o CALL mais externo contém uma lista com o restante de CALLs, como vinculações.

Cada CALL sucessivo é convertido para uma vinculação, se estiver contido em outro 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

Nesse caso, DECLARE é usado para declarar uma tabela de variáveis. Vejamos um exemplo.

 -- 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

Se executarmos esse código no Sql Server, obteremos o seguinte resultado

col1

col2

111

222

Agora, vamos ver a transformação no 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

Observe que das linhas 61 a 67 estão os resultados dessas instruções dentro do procedimento.

A tabela de variáveis Declare é transformada em uma tabela temporária. Observe que no nome o caractere @ foi substituído por T_.

Se executarmos esse código no Snowflake, não obteremos nenhum resultado. Ele exibirá apenas null. Isso ocorre porque o último Select está agora no auxiliar EXEC. Então, como sabemos que a tabela está lá?

Como ela foi criada como uma tabela temporária dentro do procedimento em um EXEC, podemos fazer um Select para essa tabela fora do procedimento.

 Select * from PUBLIC.T_VariableNameTable;
Copy

Se executarmos essa instrução, obteremos o seguinte resultado

col1

col2

111

222

SET @Variable

Por enquanto, Set Variable é transformado de acordo com a expressão que está no lado direito.

Se a expressão tiver uma transformação, ela será transformada em seu equivalente no JavaScript.

Exemplo

 -- 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

Como você pode ver no exemplo, o valor da variável NOTSUPPORTED é comentado, pois ela não está sendo transformada por enquanto. Observe que isso significa que a transformação ainda não foi concluída.

Outros tipos de conjuntos são comentados, por exemplo, os seguintes

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

Por enquanto, SELECT @variable está sendo transformado em um Select simples, removendo as atribuições de variáveis e mantendo as expressões no lado direito do operador. As operações de atribuição das variáveis locais no Select serão substituídas por funções arrow que representam o mesmo comportamento da operação realizada durante a atribuição da variável local no 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

Forma básica

O formulário SELECT básico não tem vinculações, portanto, a tradução implica a criação de uma chamada para a função auxiliar EXEC com um parâmetro. Por exemplo:

 -- 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

A transformação para THROW garante que o bloco de captura que recebe o erro tenha acesso às informações especificadas na instrução original.

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

A função do SQL Server RAISERROR não é compatível com o Snowflake. O SnowConvert AI identifica todos os usos para gerar um auxiliar que emula o comportamento original. Exemplo:

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

A transformação Break/Continue garante que o fluxo do código seja interrompido ou continue com outro bloco.

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

O código foi ligeiramente modificado devido ao fato de a instrução INSERT INTO [Table] EXEC(...) não ser compatível com o Snowflake, o que nos permite replicar o comportamento adicionando algumas linhas de código:

  • A primeira linha adicionada é uma chamada para insertIntoTemporaryTable para onde o código extraído do argumento dentro de EXEC; isso inserirá o conjunto de resultados em uma tabela temporária. Para obter mais informações sobre a função, consulte a seção Auxiliar Insert Into EXEC.

  • O Exec do Insert é removido do código e uma consulta recupera os resultados do EXEC da tabela temporária.

SELECT * FROM MYDB.PUBLIC.SnowConvertPivotTemporaryTable    
Copy
  • A última linha adicionada é uma instrução DROP TABLE para a tabela temporária adicionada.

   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 é transformado no comando BEGIN do Snowflake e inserido em uma chamada do auxiliar EXEC.

O auxiliar é responsável por executar de fato o resultado BEGIN.

SQL Server

 -- Input code
BEGIN TRAN @transaction_name;
Copy

Snowflake

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

COMMIT TRANSACTION

COMMIT TRANSACTION é transformado no comando COMMIT do Snowflake e inserido em uma chamada do auxiliar EXEC.

O auxiliar é responsável por executar de fato o resultado COMMIT.

SQL Server

 -- Input code
COMMIT TRAN @transaction_name;
Copy

Snowflake

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

ROLLBACK TRANSACTION

ROLLBACK TRANSACTION é transformado no comando ROLLBACK do Snowflake e inserido em uma chamada do auxiliar EXEC.

O auxiliar é responsável por executar de fato o resultado ROLLBACK.

SQL Server

 -- Input code
ROLLBACK TRAN @transaction_name;
Copy

Snowflake

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

WAITFOR DELAY

A cláusula WAITFOR DELAY é transformada na função SYSTEM$WAIT do Snowflake. O parâmetro time_to_pass do DELAY é transformado em segundos, para uso como parâmetro na função SYSTEM$WAIT.

As outras variantes da cláusula WAITFOR não são compatíveis com o Snowflake e, portanto, são marcadas com a mensagem correspondente.

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

Visto que CURSORS não são compatíveis com o Snowflake, o SnowConvert AI mapeia sua funcionalidade para um auxiliar JavaScript que emula o comportamento original na plataforma de destino. Exemplo:

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

Operações binárias

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

Condicionais

SQL Server

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

Snowflake

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

Predicado NULL

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

5. Labels and Goto

Rótulos não têm o mesmo comportamento no JavaScript que o SQL Server tem. Para simular o comportamento, eles estão sendo transformados em funções. Seu uso está sendo substituído por uma chamada da função gerada que contém toda a lógica do rótulo. Exemplo:

Código-fonte

 -- 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

Como você vê no exemplo acima, as declarações de função que eram os rótulos no código-fonte serão colocadas no final do código para torná-lo mais limpo.

GOTO é outro comando que não existe no JavaScript. Para simular seu comportamento, seus usos estão sendo transformados em chamadas para a função (rótulo) que é referenciada, precedida por uma instrução de retorno. Exemplo:

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

Como você vê no exemplo acima, return é adicionado à chamada da função para interromper o fluxo do código, como o SQL Server com GOTO.