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 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 REGIONTeradata
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 2definition is converted to aDYNAMIC_RESULTSvariable.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 the
DYNAMIC_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 RETURNwithPREPAREis 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 the
P1argument fromProcedure1is returned and stored in theXvariable.The
_OUTQUERIESreturned fromProcedure1are stored in theresultsetvariable.
Known Issues
No issues were found.
Related EWIs
MSCEWI1023 - PERFORMANCE REVIEW - THIS LOOP CONTAINS AN INSERT, DELETE OR UPDATE STATEMENT.
Last updated
Was this helpful?