I am trying to do error handling for snowflake stored procedure. The stored procedure has 4 SQL statement inside. I need check errors for all for statement. When error pops, i need to know which Statement error. How to achieve this? Thanks in advance. I appreciate it.
CREATE OR REPLACE PROCEDURE dw.sp_fidctp_audit_trail_load(SYSDATE varchar)
RETURNS STRING
LANGUAGE javascript
AS
'
var sql_command = "truncate table STG.tb_fidctp_audit_trail_input";
var result = snowflake.execute ( {sqlText: sql_command, binds: [SYSDATE]});
sql_command =`COPY INTO STG.tb_fidctp_audit_trail_input (record_id,ORDER_ID,PARENT_ORDER_ID,EVENT_TYPE,EVENT_DATETIME,PRIMARY_STATE,SECONDARY_STATE,CURRENT_SERVICE_ID,CURRENT_EXECUTOR_ID,TRADING_QUANTITY,LIMIT_PRICE,EVENT_TEXT,ORDER_NOTES,REASON_TEXT,ROUTED_ORDER_CODE,ROOT_ORDER_ID,INSTRUMENT_CODE,ORDER_SOURCE,LEAVES,ACCT_ID,CUST_CROSS_ID,ORDER_FLAGS)
from #STG.CTP_STAGE/AUDIT_TRAIL.TOP.`;
sql_command += SYSDATE;
sql_command += ".psv.gz file_format = (FORMAT_NAME = ''STG.CTP_AUDIT_TRAIL_FF'', ERROR_ON_COLUMN_COUNT_MISMATCH = TRUE, encoding = ''iso-8859-1'') ";
try{
result = snowflake.execute (
{sqlText: sql_command, binds: [SYSDATE]}
);
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err + "ERROR ON COPY STATEMENT"; // Return a success/error indicator.
}
sql_command = `delete from DW.tb_fidctp_audit_trail
where SYSDATE = to_date((:1), ''YYYYMMDD'')`;
result = snowflake.execute ( {sqlText: sql_command, binds: [SYSDATE]});
sql_command = ` insert into DW.tb_fidctp_audit_trail(sysdate, order_id, parent_order_id, root_order_id, event_datetime, instrument_code, event_text,reason_text, current_service_id, current_executor_id, trading_quantity, leaves )
select to_date(left(event_datetime, 8), ''YYYYMMDD''), order_id, parent_order_id, root_order_id, to_timestamp(left(event_datetime, 24), ''YYYYMMDD HH24:MI:SS.FF''), instrument_code, event_text, reason_text, current_service_id, current_executor_id, cast(trading_quantity as NUMBER(18,0)), cast(leaves as NUMBER(18,0))
from STG.tb_fidctp_audit_trail_input
`;
try{
snowflake.execute (
{sqlText: sql_command, binds: [SYSDATE]}
);
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err + "ERROR ON INSERT STATEMENT"; // Return a success/error indicator.
}
'
;
There are a few things to do here. Let's start with the create header:
CREATE OR REPLACE PROCEDURE dw.sp_fidctp_audit_trail_load(SYSDATE varchar)
RETURNS STRING
LANGUAGE javascript
AS
'
When you use a single quote to enclose the body of a JavaScript procedure, it makes using single quotes in SQL statements inside the body very cumbersome, having to double the single quotes. To avoid that, you can use an alternate string terminator in Snowflake, $$ like this:
CREATE OR REPLACE PROCEDURE dw.sp_fidctp_audit_trail_load(SYSDATE varchar)
RETURNS STRING
LANGUAGE javascript
AS
$$
// Body goes here. There's no longer a need to escape single quotes
$$;
The issue with the return values is in this part:
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err + "ERROR ON COPY STATEMENT"; // Return a success/error indicator.
}
In JavaScript, when the return statement is used in the main function, it leaves the main function, which terminates the stored procedure. This means if the copy statement succeeds, the procedure will exit and return "Succeeded." If the copy statement fails, it will hit a different return statement and exit with the error message.
Either way, it will terminate execution after that code block.
Take the return statements out of the successful branches of the try/catch blocks except for the last one.
You can just print the statement in the CATCH statement, either by printing the sql_command variable, or using getSqlText() function from the Statement object.
CREATE OR REPLACE PROCEDURE test_catch_error_sp(SYSDATE varchar)
RETURNS STRING
LANGUAGE javascript
AS
$$
var commands = [];
var params = [];
var results = {"success": [], "failed": []};
commands[0] = "create or replace table test1 (a int)";
params[0] = [];
commands[1] = "INSERT INTO test1 values (?)";
params[1] = [SYSDATE];
commands[2] = "drop table test1";
params[2] = [];
var stmt = null;
for (i = 0; i < commands.length; i++) {
try {
stmt = snowflake.createStatement({sqlText: commands[i], binds: params[i]});
var result = stmt.execute();
results['success'].push(stmt.getSqlText());
stmt = null; // reset it
} catch (err) {
// if createStatement failed, then stmt will still be null
results['failed'].push(
"Failed: " + err + ". ERROR ON STATEMENT: '" + stmt ? stmt.getSqlText() : commands[i] + "'"
);
}
}
return JSON.stringify(results);
$$
;
call test_catch_error_sp('2020-01-01');
-- returns:
-- {"success":["create or replace table test1 (a int)","drop table test1"],"failed":["INSERT INTO test1 values (?)"]}
Trying to run Describe table and running RESULT_SCAN on the query id of the describe table query.
Procedure:
var qry = ` describe table TEST_TABLE `;
var qry_rslt = snowflake.execute({sqlText: qry});
var qry_id= qry_rslt.getQueryId();
var qry2 = ` select * from table(result_scan('`+qry_id+`')) `
snowflake.execute({sqlText: qry2});
The procedure is returning Null and not running the SQL. On manually running the result scan query it says statement not found.
ANy idea how to read describe result.
You're not actually reading the results of the second query. It's running it but not collecting the results. This will collect the first column only of the result set:
create or replace procedure test()
returns string
language javascript
as
$$
var qry = ` describe table TEST_TABLE `;
var qry_rslt = snowflake.execute({sqlText: qry});
var qry_id= qry_rslt.getQueryId();
var qry2 = ` select * from table(result_scan('${qry_id}')) `;
rs = snowflake.execute({sqlText: qry2});
var out = "";
var i = 0;
while (rs.next()) {
if (i++ > 0) out += ",";
out += rs.getColumnValue(1);
}
return out;
$$;
call test();
Are you looking to get the entire DDL in one statement? If so you can run get_ddl and then read just the first row, first column. It will have the DDL for the entire table. If you want it as a table, you'll need to read the rows and columns to do what needs to be done with them.
I am trying to write a Snowflake UDF that accepts a stage name and specific folder name as input parameters and returns the latest file id ( striping from full file name) as the output. Could anyone help me with a simple code to achieve this?
I'm not sure if you want a UDF or stored procedure. The syntax to create would be similar so I think this can help. Here is a stored procedure which will fetch latest staged file from a given stage and path. Just be aware of the limit 1 in query, multiple staged files may share the same last modified date while this procedure returns a scalar (single) value.
Stored Procedure Definition
create or replace procedure "MYDB"."MYSCHEMA"."LATEST_STAGED_FILE"(stage_name text, folder text)
returns string not null
language javascript
execute as caller
as
$$
var sql_text = "list #" + STAGE_NAME + "/" + FOLDER ;
var sql_command0 = snowflake.createStatement({ sqlText: sql_text});
var sql_command1 = snowflake.createStatement({ sqlText:`SELECT "name" FROM table(result_scan(last_query_id())) WHERE "last_modified" = (select MAX("last_modified") from table(result_scan(last_query_id()))) LIMIT 1;`});
try {
sql_command0.execute();
var resultSet = sql_command1.execute();
while(resultSet.next())
{
var resultFile = resultSet.getColumnValue('name').split("/")
return resultFile[resultFile.length - 1]
}
}
catch (err) {
return "Failed: " + err;
}
$$;
You can then call the stored procedure like
call "MYDB"."MYSCHEMA"."LATEST_STAGED_FILE"('MYDB.MYSCHEMA.MYSTAGE', 'mypath/myotherpath');
References
select from list #
list stage via SP
I am trying to assign a simple sql query result in stored procedure to a variable which I will use it later in another sql statement. If I execute below statement with out putting in procedure it works but not with stored procedure, I get an error while calling sp , Can someone please help me here ?
Procedure code:
$$
VAR NAME = 'ABC'
SET (COUNT_VALUE) = (SELECT COUNT(*) FROM COUNT_TABLE)
With out Procedure below code works
SET (COUNT_VALUE) = (SELECT COUNT(*) FROM COUNT_TABLE)
select $COUNT_VALUE
Thanks
Here's an example that uses a simple helper function to return a result set from a query. You can read the Snowflake docs to see what the result set API has in it. The getResultSet function returns a variable that has the results of the query. Since the SQL is a count, you need to use rs.next() once to get to the first row and then read the value in the aliased count(*). You could also use rs.getColumnValue(1) to get a column by ordinal position, but I recommend using column names with SQL aliases is necessary.
create or replace procedure COUNT_EXAMPLE()
returns string
language javascript
as
$$
var rowCount = 0;
var sql = "select count(*) as ROW_COUNT from SNOWFLAKE_SAMPLE_DATA.TPCH_SF10000.ORDERS;";
try {
var rs = getResultSet(sql);
if (rs.next()) {
var rowCount = rs.getColumnValue("ROW_COUNT");
} else {
return "Error: Count query failed.";
}
}
catch(err) {
return "Error: " + err.message;
}
return "The table has " + rowCount + " rows.";
//--------------------------- End of main function ---------------------------
function getResultSet(sql){
cmd1 = {sqlText: sql};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
return rs;
}
$$;
call COUNT_EXAMPLE();
In a snowflake stored procedure I am executing CTAS statements and want to retrieve the number of rows in the resultant object. We don't have access to QUERY_HISTORY (we get an error), and RESULT_SCAN(LAST_QUERY_ID()) doesn't help either (it gives us back the Table xyz Created result, but does not have meta-data i.e. number of rows created).
I can do it with a Select Count(*) in a separate query, but that seems to be a hack since the Row Count is right there in the History.
CREATE OR REPLACE PROCEDURE EDW_ADMIN.DAG_TEST()
RETURNS VARCHAR(512)
LANGUAGE JAVASCRIPT
AS
$$
{
let strCTAS = "";
let rsCTAS;
let rsRowsAffected;
let rowsAffected = 0;
strCTAS = "CREATE OR REPLACE TABLE EDW_ADMIN.DEMO_PROC_TEMP AS SELECT * FROM RAW_BIR.H_RPTUNIT;";
rsCTAS = snowflake.execute( {sqlText: strCTAS} );
// This works in a Query Worksheet in the browser, but gives me the following error when called from a procedure
// "[Stored procedure execution error: Requested information on the current user is not accessible in stored procedure.]"
rsRowsAffected = snowflake.execute( {sqlText: "SELECT ROWS_PRODUCED FROM TABLE(INFORMATION_SCHEMA.QUERY_HISTORY(RESULT_LIMIT=>100)) WHERE QUERY_ID = LAST_QUERY_ID();"} );
rsRowsAffected.next();
rowsAffected = rsRowsAffected.getColumnValue(1);
// This works, but you are doing execute i/o which is really un-necessary
// rsRowsAffected = snowflake.execute( {sqlText: "SELECT COUNT(*) FROM EDW_ADMIN.DEMO_PROC_TEMP;"} );
// rsRowsAffected.next();
// rowsAffected = rsRowsAffected.getColumnValue(1);
// This does NOT work, RESULT_SCAN has no metadata associated with it, this returns "Table DEMO_PROC_TEMP successfully created."
// rsRowsAffected = snowflake.execute ( {sqlText: "SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));" } );
// rsRowsAffected.next();
// rowsAffected = rsRowsAffected.getColumnValue(1);
return rowsAffected;
}
$$
;
CALL EDW_ADMIN.DAG_TEST();
DROP EDW_ADMIN.DEMO_PROC_TEMP;
DROP PROCEDURE EDW_ADMIN.DAG_TEST();
Try adding execute as caller to the stored procedure declaration. For example:
create or replace procedure p()
returns text
language javascript
execute as caller
as
$$
const stmt1 = snowflake.createStatement( { sqlText: "create or replace table t as select $1 x from values (1),(2),(3)" } )
const rs1 = stmt1.execute()
const stmt2 = snowflake.createStatement( { sqlText: "SELECT ROWS_PRODUCED FROM TABLE(INFORMATION_SCHEMA.QUERY_HISTORY(RESULT_LIMIT=>100)) WHERE QUERY_ID = LAST_QUERY_ID()" } )
const rs2 = stmt2.execute()
rs2.next()
const rowsAffected = rs2.getColumnValue(1)
return rowsAffected
$$
;
call p();
returns 3