Print Snowflake Query in stored procedure - snowflake-cloud-data-platform

I am trying to use Snowflake stored procedures and would like to print the SQL statement in the stored procedure below to the screen.
CREATE OR REPLACE PROCEDURE read_western_provinces()
RETURNS VARCHAR NOT NULL
LANGUAGE JAVASCRIPT
AS
$$
var return_value = "";
try {
var command = "SELECT * FROM western_provinces ORDER BY province;"
var stmt = snowflake.createStatement( {sqlText: command } );
var rs = stmt.execute();
if (rs.next()) {
return_value += rs.getColumnValue(1);
return_value += ", " + rs.getColumnValue(2);
}
while (rs.next()) {
return_value += "\n";
return_value += rs.getColumnValue(1);
return_value += ", " + rs.getColumnValue(2);
}
}
catch (err) {
result = "Failed: Code: " + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
return return_value;
$$
;
How can I print the value of command variable (which contains the SQL) to the screen whenever stored procedure is executed?
thanks

I haven't found a way to do this, but there are a few options to get close at least.
You can insert output into a table.
You can return a variant instead with debugging output in an array or similar
return { "output":return_value, "debug":result }
Use a procedure to replicate something similar. See similar post from the snowflake community: https://community.snowflake.com/s/question/0D50Z00009Uu3cTSAR/debugging-procedures-technicques-print-std-out-etc)

Use getSqlText() to return a statement as text, see docs.
Add the return statement from below to the end of your procedure to have it print the statement when it is called.
CREATE OR REPLACE PROCEDURE read_western_provinces()
RETURNS VARCHAR NOT NULL
LANGUAGE JAVASCRIPT
AS
$$
var return_value = "";
try {
var command = "SELECT * FROM western_provinces ORDER BY province;"
var stmt = snowflake.createStatement( {sqlText: command } );
var rs = stmt.execute();
if (rs.next()) {
return_value += rs.getColumnValue(1);
return_value += ", " + rs.getColumnValue(2);
}
while (rs.next()) {
return_value += "\n";
return_value += rs.getColumnValue(1);
return_value += ", " + rs.getColumnValue(2);
}
}
catch (err) {
result = "Failed: Code: " + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
return stmt.getSqlText(); -- < will print/return the statement text
$$
;

If we use:
return { "output":return_value, "debug":result }
It gives an output like:

Just use call method after the procedure.
1.CALL read_western_provinces('database name','schema name');
Syntax
CALL ('database_name','schema_name');

Related

Snowflake procedure JavaScript compilation error: Uncaught SyntaxError: Unexpected token ':' in ERR_LOGGING at

Need help on a small Snowflake stored procedure written in JavaScript, i am getting the following error,
JavaScript compilation error: Uncaught SyntaxError: Unexpected token ':' in ERR_LOGGING at ' , binds: [v_err_seq,APP_NAME,ERR_MSG, MISC_STR,v_time_st])"' position 10
The code is as follows,
create or replace sequence SEQ_ERR;
create or replace table ERROR_LOG(ERR_SEQ NUMBER, APP_NAME VARCHAR(250), ERR_MSG VARCHAR(2500), MISC_STRING VARCHAR(2500), err_date timestamp);
CREATE OR REPLACE PROCEDURE ERR_LOGGING(app_name varchar, err_msg varchar, misc_str varchar )
returns string not null
language javascript
strict volatile
execute as caller
as
$$
try {
var v_time_st = getScalar("SELECT CURRENT_TIMESTAMP");
var v_err_seq = getScalar("SELECT SEQ_ERR.nextval");
error_msg = 'THis is in the line before the execute';
var sqlCmd = `insert into ERROR_LOG (ERR_SEQ , APP_NAME, ERR_MSG , MISC_STRING, ERR_DATE) VALUES (:1, :2, :3, :4, :5)`;
stmt = snowflake.execute({ sqlText: sqlCmd, binds: [v_err_seq, APP_NAME, ERR_MSG, sqlCmd,v_time_st]});
}
catch (err) {
result= " "
result = "Failed: Code: " + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
function getScalar(queryString) {
var out;
cmd1 = {sqlText: queryString};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
rs.next();
return rs.getColumnValue(1);
return out;
}
return result;
$$;
call ERR_LOGGING('TEST_APP', 'THis is an error messge', 'ON line 1');
The problems start on these lines:
var v_time_st = snowflake.execute( {sqlText: "SELECT CURRENT_TIMESTAMP;"} );
var v_err_seq = snowflake.execute( {sqlText: "SELECT SEQ_ERR.nextval;"} );
When you run a SQL statement through the stored procedures API, it returns a table in the form of an object called a ResultSet. You're returning a ResultSet to these variables, not the values you want. In order to get the values, you need to use the .next() method of the ResultSet and then the .getColumnValue() method.
The problem then continues to the next line doing the insert. It's expecting scalar values for the binds but is sent variables containing ResultSets objects with all their attendant complexity.
It's a common practice to need to get single scalar values from a SQL statement. If you know for 100% certain that your query will return exactly one row and one column, you can use a helper function like this one called getScalar. Here's an example:
create or replace procedure FOO()
returns timestamp
language javascript
strict volatile
execute as caller
as
$$
var v_time_st = getScalar("SELECT CURRENT_TIMESTAMP");
return v_time_st;
// Main function above, helper functions below.
function getScalar(queryString) {
var out;
cmd1 = {sqlText: queryString};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
rs.next();
return rs.getColumnValue(1);
return out;
}
$$;
call foo();
You can keep that helper function below your main function and out of the way. From there you can do this to get your variables assigned how you need them:
var v_time_st = getScalar("SELECT CURRENT_TIMESTAMP");
var v_err_seq = getScalar("SELECT SEQ_ERR.nextval");
Based on your update, here's refactored code that should work:
CREATE OR REPLACE PROCEDURE ERR_LOGGING(app_name varchar, err_msg varchar, misc_str varchar )
returns string not null
language javascript
strict volatile
execute as caller
as
$$
try {
var v_time_st = getScalar("SELECT CURRENT_TIMESTAMP");
var v_err_seq = getScalar("SELECT SEQ_ERR.nextval");
var sqlCmd = `insert into ERROR_LOG (ERR_SEQ NUMBER, APP_NAME, ERR_MSG , MISC_STRING, ERR_DATE) VALUES (:1, :2, :3, :4, :5)`;
stmt = snowflake.execute({ sqlText: sqlCmd, binds: [P_BATCH_ID, JOB_NAME, error_msg1, sql_command]});
}
catch (err) {
result= " "
result = "Failed: Code: " + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
function getScalar(queryString) {
var out;
cmd1 = {sqlText: queryString};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
rs.next();
return rs.getColumnValue(1);
return out;
}
return result;
$$;
call ERR_LOGGING('TEST_APP', 'THis is an error messge', 'ON line 1');

Invalid Identifier when passing a string into sql query in snowflake

I have a stored procedure which takes a string(SCHEMA_NAME) as a parameter
It then puts this string name into a query
The problem I am facing is when I pass the parameter into the sql query I get a
Invalid identifier error
Below is my code and what I have tried
CREATE OR REPLACE PROCEDURE "CREATE_SCHEMA"("SCHNAME" VARCHAR(16777216))
RETURNS VARCHAR(16777216)
LANGUAGE JAVASCRIPT
COMMENT='Creates schemas'
EXECUTE AS CALLER
AS
$$
var v_sqlCode = "select * from dbschemas where name = " + "''" + SCHNAME + "''";
try{
var sqlStmt = snowflake.createStatement({sqlText:v_sqlCode});
var sqlRS = sqlStmt.execute();
}catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt + v_sqlCode;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
$$;
I have tried writing my sql query in three ways
1. var v_sqlCode = `select * from dbschemas where name = `+ SCHNAME;
2. var v_sqlCode = "select * from dbschemas where name = " + "''" + SCHNAME + "''"
3.var v_sqlCode = `SELECT * FROM DBSCHEMAS WHERE NAME = {$SCHENAME}`
The way in which I call the stored procedure is as follows : CALL CREATE_SCHEMA('SCHEMA_NAME');
Any help would be greatly appreciated.
It is recommended to parametrize query to be executed instead of concatenating the SQL query string.
More info: Binding variables
CREATE OR REPLACE PROCEDURE CREATE_SCHEMA(SCHNAME VARCHAR(16777216))
RETURNS VARCHAR(16777216)
LANGUAGE JAVASCRIPT
COMMENT='Creates schemas'
EXECUTE AS CALLER
AS
$$
var v_sqlCode = "select * from dbschemas where name = ?";
try{
var sqlStmt = snowflake.createStatement({sqlText:v_sqlCode, binds:[SCHNAME]});
var sqlRS = sqlStmt.execute();
}catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt + v_sqlCode;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
$$;
Call:
CALL CREATE_SCHEMA('SCHEMA_NAME');

How to pass a stored procedure as a parameter in snowflake

I have a stored procedure which takes the name of another stored procedure to call on it and with the parameters
However when I try to execute this stored procedure I get an error
What am I doing wrong? From my code below I think I know how to call on a passed stored procedure however I am wondering if it is my syntax that is incorrect.
CREATE OR REPLACE PROCEDURE "ADMINDB"."TOOLKIT".LOG_PROCEDURES("P_PROCEDURE_NAME" VARCHAR, "P_PROCEDURE_PARAMETERS" VARCHAR)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT='Inserts into the table PROCEDURE_LOGGING information about the last runned procedure'
EXECUTE AS CALLER
AS
$$
var v = P_PROCEDURE_PARAMETERS;
var v_sqlCode;
if(P_PROCEDURE_PARAMETERS.length > 0){
v_sqlCode = `CALL ` + P_PROCEDURE_NAME + `(` + P_PROCEDURE_PARAMETERS + `)`;
}else{
return "there was no parameter passed";
}
try{
var sqlStmt = snowflake.createStatement({sqlText : v_sqlCode});
var sqlRS = sqlStmt.execute();
}catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt + v_sqlCode;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
return "success!";
$$;
CREATE OR REPLACE PROCEDURE "ADMINDB"."TOOLKIT".TESTFORLOG("P" VARCHAR)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT='Inserts into the table PROCEDURE_LOGGING information about the last runned procedure'
EXECUTE AS CALLER
AS
$$
var v_sqlCode = 'CREATE TABLE ' + P + `(name varchar)`;
try{
var sqlStmt = snowflake.createStatement({sqlText : v_sqlCode});
var sqlRS = sqlStmt.execute();
}catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt + v_sqlCode;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
return "success!";
$$;
CALL ADMINDB.TOOLKIT.LOG_PROCEDURES('TESTFORLOG','P');
Combining all of the above comments, here are updated stored procedures which are working when I test here on my dev account.
Notes:
be careful with the context and use fully qualified names where possible if doing cross schema/database object references
review your query history when debugging
backticks are awesome for SQL, I have one sample with and one without in the below
I like having the SQL in one block (with backticks) and then doing the replace, as illustrated.
CREATE OR REPLACE PROCEDURE sp_stacko_1(p1 varchar, p2 varchar)
RETURNS VARCHAR
LANGUAGE javascript
EXECUTE AS caller
AS
$$
var p_procedure_name = P1;
var p_params = P2;
if(p_params.length > 0) {
v_sqlCode = `
CALL <p_procedure_name>('<p_params>')
`;
v_sqlCode = v_sqlCode.replace("<p_procedure_name>", p_procedure_name);
v_sqlCode = v_sqlCode.replace("<p_params>", p_params);
}
else {
return "there was no parameter passed";
}
try {
snowflake.execute({sqlText: v_sqlCode});
}
catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt + v_sqlCode;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
return "success!";
$$;
CREATE OR REPLACE PROCEDURE sp_stacko_2(p3 varchar)
RETURNS VARCHAR
LANGUAGE javascript
EXECUTE AS caller
AS
$$
var p_table_name = P3;
var v_sqlCode = "CREATE TABLE <table_name> (name varchar)";
v_sqlCode = v_sqlCode.replace("<table_name>", p_table_name);
try {
snowflake.execute({sqlText: v_sqlCode});
}
catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt + v_sqlCode;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
return "success!";
$$;
CALL sp_stacko_1('sp_stacko_2','P');

Obtaining column name from array of strings and using it to alter SQL Table

I am writing a stored procedure in SQL which accepts a String. The string contains a list of column names which contain the column name and the type
ex) "NAME VARCHAR, AGE NUMBER"
ALTER TABLE T1
ADD COLUMN NAME VARCHAR
I am working with Snowflake
My logic is correct, however I am struggling to properly write the syntax
In the code below, the alter table statement with the array is where I am stuck
Any help would be greatly appreciated
CREATE OR REPLACE PROCEDURE ADD_COLUMNS_CONTEXTUAL_ATTRIBUTES("P_COL_NAME_SPACE_TYPE" VARCHAR(16777216))
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'Turns column string list into array'
EXECUTE AS CALLER
AS
$$
var arr = [];
var v_storing_passed_parameter = P_COL_NAME_SPACE_TYPE;
arr = v_storing_passed_parameter.split(',');
for (i = 0; i < arr.length; i++){
try{
var v_sqlCode = ***`ALTER TABLE CONTEXTUAL_ATTRIBUTES
ADD COLUMN ' + arr[i];***
var sqlStmt = snowflake.createStatement({sqlText : v_sqlCode});
var sqlRS = sqlStmt.execute();
}catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
};
return "success";
$$;
CALL ADD_COLUMNS_CONTEXTUAL_ATTRIBUTES('AGE NUMBER, NAME VARCHAR');
Here you go:
CREATE OR REPLACE PROCEDURE ADD_COLUMNS_CONTEXTUAL_ATTRIBUTES("P_COL_NAME_SPACE_TYPE" VARCHAR(16777216))
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'Turns column string list into array'
EXECUTE AS CALLER
AS
$$
var arr = [];
var v_storing_passed_parameter = P_COL_NAME_SPACE_TYPE;
arr = v_storing_passed_parameter.split(',');
for (i = 0; i < arr.length; i++){
try{
var v_sqlCode = `ALTER TABLE CONTEXTUAL_ATTRIBUTES ADD COLUMN ${arr[i]}`;
var sqlStmt = snowflake.createStatement({sqlText : v_sqlCode});
var sqlRS = sqlStmt.execute();
}catch(err){
errMessage = "Failed: Code: " + err.code + "\n State: " + err.state;
errMessage += "\n Message: " + err.message + v_sqlCode;
errMessage += "\nStack Trace:\n" + err.stackTraceTxt;
throw 'Encountered error in executing v_sqlCode. \n' + errMessage;
}
};
return "success";
$$;

USE DATABASE in stored procedure

I am trying to set my database inside the stored procedure, but getting errors, Any advice? Thanks in advance
Below code return error "Unsupported statement type 'USE'."
CREATE PROCEDURE PROC_TEST()
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
try{
var my_sql_command = "USE DATABASE EDWH_DEV";
var statement1 = snowflake.createStatement( {sqlText: my_sql_command} );
statement1.execute();
result = "Succeeded";
}
catch (err) {
result = "Failed: Code: " + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
return result;
$$;
CALL PROC_TEST();
You need to include line, On which privilege the procedure need to execute:
Include line: execute as caller It will work
CREATE or replace PROCEDURE PROC_TEST()
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
execute as caller
AS
$$
try{
var my_sql_command = "USE DATABASE GCP";
var statement1 = snowflake.createStatement( {sqlText: my_sql_command} );
statement1.execute();
result = "Succeeded";
}
catch (err) {
result = "Failed: Code: " + err.code + "\n State: " + err.state;
result += "\n Message: " + err.message;
result += "\nStack Trace:\n" + err.stackTraceTxt;
}
return result;
$$;

Resources