Procedures

Translation reference to convert Teradata PROCEDURE statement to Snowflake

Description

Teradata's PROCEDURE statement is translated to Snowflake PROCEDURE syntax.

For more information on Teradata PROCEDURE, click here.

Sample Source Patterns

Create Procedure Transformation

SnowConvert Helpers Code was removed from the example. You can find them here.

SnowConvert procedure helpers region

All procedures contain a region called SnowConvert Helpers code, these are a set of variables and functions that help to emulate some Teradata functionality that is not native in JavaScript. For simplicity in the procedures samples code this region will not be shown. Some helpers are added always like EXEC and some others like FETCH, INTO, etc. are added on demand.

The region helpers code is as follows:

// REGION SnowConvert Helpers Code
	var HANDLE_NOTFOUND;
	var fetch = (count,rows,stmt) => (count && rows.next() && Array.apply(null,Array(stmt.getColumnCount())).map((_,i) => rows.getColumnValue(i + 1))) || [];
	var _RS, ROW_COUNT, _ROWS, MESSAGE_TEXT, SQLCODE = 0, SQLSTATE = '00000', ERROR_HANDLERS, ACTIVITY_COUNT = 0, INTO, _OUTQUERIES = [], DYNAMIC_RESULTS = 9;
	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 = function (stmt,binds,noCatch,catchFunction,opts) {
	   try {
	      binds = binds ? binds.map(fixBind) : binds;
	      _RS = snowflake.createStatement({
	            sqlText : stmt,
	            binds : binds
	         });
	      _ROWS = _RS.execute();
	      ROW_COUNT = _RS.getRowCount();
	      ACTIVITY_COUNT = _RS.getNumRowsAffected();
	      HANDLE_NOTFOUND && HANDLE_NOTFOUND(_RS);
	      if (INTO) return {
	         INTO : function () {
	            return INTO();
	         }
	      };
		    if (_OUTQUERIES.length < DYNAMIC_RESULTS) _OUTQUERIES.push(_ROWS.getQueryId());
	      if (opts && opts.temp) return _ROWS.getQueryId();
	   } catch(error) {
	      MESSAGE_TEXT = error.message;
	      SQLCODE = error.code;
	      SQLSTATE = error.state;
	      var msg = `ERROR CODE: ${SQLCODE} SQLSTATE: ${SQLSTATE} MESSAGE: ${MESSAGE_TEXT}`;
	      if (catchFunction) catchFunction(error);
	      if (!noCatch && ERROR_HANDLERS) ERROR_HANDLERS(error); else throw new Error(msg);
	   }
	};
	var CURSOR = function (stmt,binds,withReturn) {
	   var rs, rows, row_count, opened = false, resultsetTable = '', self = this;
	   this.CURRENT = new Object;
	   this.INTO = function () {
	         return self.res;
	      };
	   this.OPEN = function (usingParams) {
	         try {
	            if (usingParams) binds = usingParams;
	            if (binds instanceof Function) binds = binds();
	            var finalBinds = binds && binds.map(fixBind);
	            var finalStmt = stmt instanceof Function ? stmt() : stmt;
	            if (withReturn) {
	               resultsetTable = EXEC(finalStmt,finalBinds,true,null,{
	                     temp : true
	                  });
	               finalStmt = `SELECT * FROM TABLE(RESULT_SCAN('${resultsetTable}'))`;
	               finalBinds = [];
	            }
	            rs = snowflake.createStatement({
	                  sqlText : finalStmt,
	                  binds : finalBinds
	               });
	            rows = rs.execute();
	            row_count = rs.getRowCount();
	            ACTIVITY_COUNT = rs.getRowCount();
	            opened = true;
	            return this;
	         } catch(error) {
	            ERROR_HANDLERS && ERROR_HANDLERS(error);
	         }
	      };
	   this.NEXT = function () {
	         if (row_count && rows.next()) {
	            this.CURRENT = new Object;
	            for(let i = 1;i <= rs.getColumnCount();i++) {
	               (this.CURRENT)[rs.getColumnName(i)] = rows.getColumnValue(i);
	            }
	            return true;
	         } else return false;
	      };
	   this.FETCH = function () {
	         self.res = [];
	         self.res = fetch(row_count,rows,rs);
	         if (opened) if (self.res.length > 0) {
	            SQLCODE = 0;
	            SQLSTATE = '00000';
	         } else {
	            SQLCODE = 7362;
	            SQLSTATE = '02000';
	            var fetchError = new Error('There are not rows in the response');
	            fetchError.code = SQLCODE;
	            fetchError.state = SQLSTATE;
	            if (ERROR_HANDLERS) ERROR_HANDLERS(fetchError);
	         } else {
	            SQLCODE = 7631;
	            SQLSTATE = '24501';
	         }
	         return self.res && self.res.length > 0;
	      };
	   this.CLOSE = function () {
	         if (withReturn && _OUTQUERIES.includes(resultsetTable)) {
	            _OUTQUERIES.splice(_OUTQUERIES.indexOf(resultsetTable),1);
	         }
	         rs = rows = row_count = undefined;
	         opened = false;
	         resultsetTable = '';
	      };
	};
	let PROCRESULTS = (...OUTPARAMS) => JSON.stringify([...OUTPARAMS,[..._OUTQUERIES]]);
	// END REGION

Teradata

Snowflake

Note: The stored procedure's body in Snowflake is executed as javascript functions.

If

The transformation for the IF statement is:

Teradata

Snowflake

Case

The transformation for the Case statement is:

Teradata

Snowflake

Cursor Declare, OPEN, FETCH and CLOSE

The transformation for cursor statements is:

Teradata

Snowflake

While

The transformation for while statement is:

Teradata

Snowflake

Security

The transformation for security statements is:

Teradata

Snowflake

SQL SECURITY CREATOR

EXECUTE AS OWNER

SQL SECURITY INVOKER

EXECUTE AS CALLER

SQL SECURITY DEFINER

EXECUTE AS OWNER

FOR-CURSOR-FOR loop

The transformation for FOR-CURSOR-FOR loop is:

Teradata

Snowflake

Note: The FOR loop present in the Teradata procedure is transformed to a FOR block in javascript that emulates its functionality.

Procedure parameters and variables referenced inside statements

The transformation for the procedure parameters and variables that are referenced inside the statements of the procedure is:

Teradata

Snowflake

Note: Whenever a procedure parameter or a variable declared inside the procedure is referenced inside a Teradata statement that has to be converted, this reference is escaped from the resulting text to preserve the original reference's functionality.

Leave

In Javascript, it's possible to use break with an additional parameter, thus emulating the behavior of a Teradata LEAVE jump.

Labels can also be emulated by using Javascript Labeled Statements.

The transformation for LEAVE statement is:

Teradata

Snowflake

Getting Results from Procedures

Description of the translation

In Teradata, there are two ways to return data from a procedure. The first is through output parameters and the second through Dynamic Result Sets and Cursors. Both are shown in the following example. Each important point is explained below.

Example of returning data from a Stored Procedure

Teradata

Snowflake

In this converted SQL, there are several conversions that take place:

  • The DYNAMIC RESULT SETS 2 definition is converted to a DYNAMIC_RESULTS variable.

  • When a cursor with an WITH RETURNattribute is opened (and therefore a query is executed), its query ID is stored in the_OUTQUERIEScollection in order to be later returned. The query id is obtained by thegetQueryId()function provided in the JavaScript API for Snowflake stored procedures.

  • Only the first k-query-IDs are stored in the collection, where k is the value of theDYNAMIC_RESULTSvariable. This is done to emulate Teradata's behavior, which only returns the first k-opened-cursors, even if more are opened in the stored procedure.

  • The combination of DECLARE CURSOR WITH RETURN with PREPARE is translated to:

  • The output parameters are supported via the return statement of the procedure. An array is created containing the value of each output parameter and the_OUTQUERIEScollection. ThePROCRESULTSfunction deals with the creation and filling of this array. See PROCRESULTS() helper for more information.

Example of getting data from a Stored Procedure

If the output parameters and the query IDs are returned from a procedure, a second one could call the first one to get these values, as shown below:

Teradata

Snowflake

  • The value of theP1argument fromProcedure1is returned and stored in theXvariable.

  • The_OUTQUERIESreturned fromProcedure1are stored in theresultsetvariable.

Known Issues

No issues were found.

  • MSCEWI1023 - PERFORMANCE REVIEW - THIS LOOP CONTAINS AN INSERT, DELETE OR UPDATE STATEMENT.

Last updated

Was this helpful?