snowflake stored procedure is throwing unexpected Token error - stored-procedures

I have written down a procedure by following the snowflake documentation to generate a list of 'SET' statement and execute them in Snowflake. I'm getting Unexpected identifier error while calling the procedure. could somebody please help me out here.
create or replace procedure SET_ENV()
returns string
language JavaScript
as
$$
MASTER_QUERY = {sqlText: SELECT ('SET '||TABLE_NAME||'=
CONCAT($Database_pre,'||'''.'''||',$Schema_pre,'||'''.'''||','''||TABLE_NAME||''');') AS
QUERY
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'
AND( TABLE_NAME NOT LIKE 'TMP_%' AND TABLE_NAME NOT LIKE '%WRK_%')};
STATEMENT = snowflake.createStatement(MASTER_QUERY);
rs = STATEMENT.execute();
var s = '';
while (rs.next()) {
EXECUTION_QUERY = {sqlText: rs.getColumnValue("QUERY")};
stmtEx = snowflake.createStatement(EXECUTION_QUERY);
stmtEx.execute();
s += rs.getColumnValue(1) + "\n";
}
return s;
$$
;
CALL SET_ENV()
The error i'm getting is as follows.
JavaScript compilation error: Uncaught SyntaxError: Unexpected identifier in SET_ENV at 'MASTER_QUERY = {sqlText: 'SELECT ('SET '||TABLE_NAME||'= ' position 35

I'm able to run it after I add backtick (`) characters to enclose the SQL command:
create or replace procedure SET_ENV()
returns string
language JavaScript
execute as CALLER
as
$$
MASTER_QUERY = {sqlText: `SELECT ('SET '||TABLE_NAME||'=
CONCAT($Database_pre,'||'''.'''||',$Schema_pre,'||'''.'''||','''||TABLE_NAME||''');') AS
QUERY
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'
AND( TABLE_NAME NOT LIKE 'TMP_%' AND TABLE_NAME NOT LIKE '%WRK_%')` };
STATEMENT = snowflake.createStatement(MASTER_QUERY);
rs = STATEMENT.execute();
var s = '';
while (rs.next()) {
EXECUTION_QUERY = {sqlText: rs.getColumnValue("QUERY")};
stmtEx = snowflake.createStatement(EXECUTION_QUERY);
stmtEx.execute();
s += rs.getColumnValue(1) + "\n";
}
return s;
$$;
set DATABASE_PRE='DBPRE';
set Schema_pre = 'SCHPRE';
call SET_ENV();
Important: You should also define the procedure as "execute as CALLER" to be able to access session variables. I suggest you define arguments instead of accessing session variables.

Related

Snowflake working with stored procedure on error handling

I have a sample stored procedure in Snowflake where it does the below steps one after the other
Insert Metadata with start details
Get the total count of table which is passed as argument
Update Metadata with success/failure
Issue here is When the given table name doesn't exist on database, The stored procedure exits with error however its not updating the Metadata with error details and the Metadata entry shows status as "In-Progress" while the stored procedure failed at point #2.
I'm looking for a clean exit when the table doesn't exist in database then stored procedure should update metadata with error details and exit cleanly.
Can someone have a look on below code and suggest if I'm missing something here please. I'm new to snowflake and procedure's as well. Thanks.
CREATE OR REPLACE PROCEDURE abc.PROC_GET_COUNT(table_name varchar)
RETURNS VARCHAR(16777216)
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS $$
// SQL Queries
var get_execution_id_sql = "select t.nextval from table(getnextval(EXECUTION_SEQUENCE)) t";
var get_count_sql = `select count(*) from abc.`+ TABLE_NAME +;
var result_set1 = snowflake.createStatement({sqlText: get_execution_id_sql}).execute();
result_set1.next();
var seq_num= result_set1.getColumnValue(1);
var insert_meta_sql1= `INSERT into abc.ERROR_LOG (EXECUTION_ID, STATUS, START_TS) values ( '` +seq_num+ `', 'In_Progress', CURRENT_TIMESTAMP)`;
try {
message = 'In insert Metadata with start details';
snowflake.execute({sqlText: insert_meta_sql1});
message = 'In Process of get count';
get_count_out = snowflake.execute ({sqlText: get_count_sql});
get_count_out.next();
rec_count = get_count_out.getColumnValue(1);
upd_meta_sql = `UPDATE abc.ERROR_LOG SET END_TS = current_timestamp, STATUS = 'SUCCESS', MESSAGE = '` + TABLE_NAME + ` - Total count: ` + rec_count + `' where EXECUTION_ID = '` + seq_num + `';
message = 'In update Metadata with end details';
snowflake.execute ({sqlText: upd_meta_sql});
} catch (err) {
upd_meta_sql = `UPDATE abc.ERROR_LOG SET
END_TS = current_timestamp,
STATUS = 'FAILED',
MESSAGE = '` + message + `. Error Details -- \n Code: `+ err.code +`\n State: `+ err.state +`\n Message: `+ err.message +`\n Stack Trace: `+ err.stackTraceTxt +`'
where EXECUTION_ID = '` + seq_num + `';
snowflake.execute ({sqlText: upd_meta_sql});
return "Failed: " + message + ' -- ' +err;
}
return 'SUCCESS';
$$;
There are some syntax errors in the sample code, but I assume that all of them are caused when you are copying the script here. The main issue is err.message contains text with single quote characters which breaks the last SQL.
Here is the scripts for my test environment (it would be very helpful for others if you share yours when asking a question which needs debugging):
create schema abc;
create table deneme ( id number ) as select seq4()
from table(generator(rowcount=>100));
create or replace table ERROR_LOG (EXECUTION_ID number,
MESSAGE varchar, STATUS varchar, START_TS timestamp , END_TS timestamp);
create or replace sequence seq1;
Here is the fixed version of the procedure:
CREATE OR REPLACE PROCEDURE abc.PROC_GET_COUNT(table_name varchar)
RETURNS VARCHAR(16777216)
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS $$
var get_execution_id_sql = "select t.nextval from table(getnextval(seq1)) t";
var get_count_sql = `select count(*) from abc.`+ TABLE_NAME ;
var result_set1 = snowflake.createStatement({sqlText: get_execution_id_sql}).execute();
result_set1.next();
var seq_num= result_set1.getColumnValue(1);
var insert_meta_sql1= `INSERT into abc.ERROR_LOG (EXECUTION_ID, STATUS, START_TS) values ( ` + seq_num + `, 'In_Progress', CURRENT_TIMESTAMP)`;
try {
message = 'In insert Metadata with start details';
snowflake.execute({sqlText: insert_meta_sql1});
message = 'In Process of get count';
get_count_out = snowflake.execute ({sqlText: get_count_sql});
get_count_out.next();
rec_count = get_count_out.getColumnValue(1);
upd_meta_sql = `UPDATE abc.ERROR_LOG SET END_TS = current_timestamp, STATUS = 'SUCCESS', MESSAGE = '` + TABLE_NAME + ` - Total count: ` + rec_count + `' where EXECUTION_ID = '` + seq_num +`'`;
message = 'In update Metadata with end details';
snowflake.execute ({sqlText: upd_meta_sql});
} catch (err) {
upd_meta_sql = `UPDATE abc.ERROR_LOG SET
END_TS = current_timestamp,
STATUS = 'FAILED',
MESSAGE = '` + message + `. Error Details -- \n Code: `+ err.code +`\n State: `+ err.state +`\n Message: `+ err.message.replace( /'/g , "''" ) +`\n Stack Trace: `+ err.stackTraceTxt +`'
where EXECUTION_ID = '` + seq_num + `'`;
snowflake.execute ({sqlText: upd_meta_sql});
return "Failed: " + message + ' -- ' +err;
}
return 'SUCCESS';
$$;
As you may notice, I used err.message.replace( /'/g , "''" ) instead of err.message when generating the SQL.
call PROC_GET_COUNT( 'deneme' ); -- succesful as the table exits
call PROC_GET_COUNT( 'deneme21' ); -- shows error
This is the content of the message column of second try:
In Process of get count. Error Details --
Code: 100183
State: P0000
Message: SQL compilation error:
Object 'GOKHAN_DB.ABC.DENEME21' does not exist or not authorized.
Stack Trace: At Snowflake.execute, line 20 position 30
I see the issue has to do with single quotes being returned in the error message.
I recommend, setting the message variable. While you are writing shorter code, its more cryptic and harder to find the issue.
By breaking the code down into simpler steps you will find the issue easier.
CREATE OR REPLACE SCHEMA abc;
USE SCHEMA ABC;
CREATE SEQUENCE IF NOT EXISTS EXECUTION_SEQUENCE
WITH
START WITH = 1
INCREMENT BY = 1
COMMENT = 'DEMO SEQUENCE ' ;
CREATE OR REPLACE TABLE abc.ERROR_LOG(EXECUTION_ID number , STATUS varchar, MESSAGE VARCHAR, START_TS timestamp_ltz , END_TS timestamp_ltz);
CREATE TABLE ABC.TEST_TABLE1(ID NUMBER, MESSAGE VARCHAR);
INSERT INTO ABC.TEST_TABLE1(ID, MESSAGE)
SELECT SEQ4() + 1 , RANDSTR(50, RANDOM())
FROM TABLE(GENERATOR(ROWCOUNT=>50));
SELECT * FROM ABC.TEST_TABLE1 ORDER BY 1 ASC;
CREATE OR REPLACE PROCEDURE abc.PROC_GET_COUNT(table_name varchar)
RETURNS VARCHAR(16777216)
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS $$
// SQL Queries
var get_execution_id_sql = "select t.nextval from table(getnextval(EXECUTION_SEQUENCE)) t";
var get_count_sql = `select count(*) from abc.`+ TABLE_NAME +`;`
var result_set1 = snowflake.createStatement({sqlText: get_execution_id_sql}).execute();
result_set1.next();
var seq_num= result_set1.getColumnValue(1);
var insert_meta_sql1= `INSERT into abc.ERROR_LOG (EXECUTION_ID, STATUS, START_TS,MESSAGE) values ( '` +seq_num+ `', 'In_Progress', CURRENT_TIMESTAMP(), '`+TABLE_NAME+`')`;
try {
message = 'In insert Metadata with start details';
snowflake.execute({sqlText: insert_meta_sql1});
message = 'In Process of get count';
get_count_out = snowflake.execute ({sqlText: get_count_sql});
get_count_out.next();
rec_count = get_count_out.getColumnValue(1);
upd_meta_sql = `UPDATE abc.ERROR_LOG SET END_TS = current_timestamp(), STATUS = 'SUCCESS', MESSAGE = '` + TABLE_NAME + ` - Total count: ` + rec_count + `' where EXECUTION_ID = ` + seq_num + `;`
message = 'In update Metadata with end details';
snowflake.execute ({sqlText: upd_meta_sql});
} catch (err) {
message = `Error Details\r\nCode: ` + err.code + `\r\nState:` + err.state +`\r\nMessage: ` + err.message + `\r\nStack Trace: ` + err.stackTraceTxt;
// **** you need to replace the single quotes that are being returned in your error message
message = message.replace(/'/g, `"`);
upd_meta_sql = `UPDATE abc.ERROR_LOG
SET END_TS = current_timestamp()
,STATUS = 'FAILED'
, MESSAGE = '` + message +`' where EXECUTION_ID = ` + seq_num + `;`
snowflake.execute ({sqlText: upd_meta_sql});
return "Failed: " + message + ' -- ' +err;
}
return 'SUCCESS';
$$;
CALL abc.PROC_GET_COUNT('TEST_TABLE1');
CALL abc.PROC_GET_COUNT('TEST_TABLE_MISSING');
SELECT * FROM abc.ERROR_LOG ORDER BY 1 ASC;
Solution is here:

Trying to query data where string has a single quote

Trying to go through a list of values where there might be one with a single quote in it. When I try to pass it through a function, it fails.
It gives the error,
Execution error in store procedure REPORT_PRESENTATION_DATA: SQL compilation error: parse error line 1 at position 164 near '<EOF>'. syntax error line 1 at position 158 unexpected 't'. At Statement.execute, line 90 position 41
I then add
myCommonValue = myCommonValue.replace("'","''");
but now get the error,
JavaScript execution error: Uncaught TypeError: Cannot read property 'replace' of null in REPORT_PRESENTATION_DATA at ' myCommonValue = myCommonValue.replace("'","''");' position 36 stackstrace: REPORT_PRESENTATION_DATA line: 85
Here's a code sample:
CREATE OR REPLACE PROCEDURE MY_SNOWFLAKE_PROCEDURE()
RETURNS VARIANT
LANGUAGE JAVASCRIPT
AS
$$
...
sqlText = ` SELECT fields FROM MYTABLE `;
statement = snowflake.createStatement({ sqlText });
const rs = statement.execute();
let results = [];
while (rs.next()) {
let myColumnValue = rs.getColumnValue(1);
myColumnValue = myColumnValue.replace("'","''");
// Using the column value which could contain a single quote in the string,
// get a second result set. IE: "gov't" may be the string value passed into Query_GroupValues
sqlText = ` SELECT * FROM table(Query_GroupValues('${startdate}', '${enddate}', ARRAY_CONSTRUCT('${myColumnValue}'))) `;
statement = snowflake.createStatement({ sqlText });
const rsTableValue = statement.execute();
if (statement.getRowCount() > 0) {
let myValue = rsTableValue.getColumnValue(1);
struct = {
attributes: {
value: myValue
}
}
results.push(struct);
}
}
...
$$;
Looks like myColumnValue was null when it's replace() method was invoked. Adding a guard should fix it. For Example: if (myColumnValue!=null) myColumnValue = myColumnValue.replace("'","''");

Ruby - Oci8 - Call funtion with rowtype not working

I have an oracle function like this:
CREATE OR REPLACE FUNCTION MY_FUNCTION (MY_ID IN VARCHAR2)
RETURN vendedor%rowtype AS
res vendedor%rowtype;
BEGIN
select * into res
from vendedor
where id = MY_ID;
return res;
END MY_FUNCTION;
And I want to call this function from my ruby code using oci8 gem. This is my ruby code:
connectString = ENV['DB_CONNECTION']
conn = OCI8.new(connectString)
cursor = conn.parse ('begin :ret := MY_FUNCTION(:MY_ID); end;')
cursor.bind_param(':MY_ID', '123', String)
cursor.bind_param(':ret', nil, OCI8::RAW) # This is the question
cursor.exec()
result = cursor[':ret']
cursor.close
conn.logoff
What is the parameter's bind type of :ret variable?
To execute this code throw this error:
ORA-06550: line 1, column 15: PLS-00382: expression is of wrong type ORA-06550: line 1,
column 7: PL/SQL: Statement ignored
Replacing cursor.bind_param(':ret', nil, OCI8::RAW)
with
cursor.bind_param(':ret', OCI8::Cursor) would do the trick,
but you need to convert your function to
CREATE OR REPLACE FUNCTION MY_FUNCTION (MY_ID IN VARCHAR2) RETURN sys_refcursor AS
res sys_refcursor;
BEGIN
open res for
select *
from vendedor
where id = MY_ID;
return res;
END MY_FUNCTION;
to prepare to return sys_refcursor type
For get the result in ruby code:
result = cursor[':ret']
while r = result.fetch_hash
retFinal = r
end
retFinal variable has the cursor's last result

Stored procedure not compiling with 'Set'

I have a Stored procedure which I am not able to compile.
CREATE PROCEDURE FINGOODSCH(IN STRDATE DATE,
IN prodln Char(5))
LANGUAGE SQL
RESULT SETS 1
SET OPTION DBGVIEW =*SOURCE
BEGIN
Declare IN_DATE NUMERIC(7,0);
SET IN_DATE = 0;
/* SET IN_DATE = (DECIMAL(CHAR(SUBSTR(CHAR(STRDATE),1,4); */
/* SUBSTR(CHAR(STRDATE),6,2) CONCAT */
/* SUBSTR(CHAR(STRDATE),9,2))) - 19000000) ; */
Declare FinGoodSCH Cursor for
Select TRIM(ORDNO) as OrderNumber,
( '20' || SUBSTR(CHAR(ODUDT),2,2) || '-' ||
SUBSTR(CHAR(ODUDT),4,2) || '-' ||
SUBSTR(CHAR(ODUDT),6,2)) as OrderDueDate,
TRIM(FITEM) as ModelNumber,
TRIM(DPTNO) as ProductionLine
From ORMAST
Where
DPTNO = prodln
OPEN FinGoodSCH ;
END
THe issue is withthe statement 'SET IN_DATE = 0; (I know I can use Default to set it to 0, but thats not what I am looking for)'. If I remove this statement, it will compile. Compilation error is:
SQL0104 30 3 Position 33 Token FINGOODSCH was not valid. Valid
tokens: GLOBAL.
Also, I tried declaring it with decimal but it did not work
You have to do the declarations in SQL before any executable code....
CREATE PROCEDURE FINGOODSCH(IN STRDATE DATE,
IN prodln Char(5))
LANGUAGE SQL
RESULT SETS 1
SET OPTION DBGVIEW =*SOURCE
BEGIN
Declare IN_DATE NUMERIC(7,0);
Declare FinGoodSCH Cursor for
Select TRIM(ORDNO) as OrderNumber,
( '20' || SUBSTR(CHAR(ODUDT),2,2) || '-' ||
SUBSTR(CHAR(ODUDT),4,2) || '-' ||
SUBSTR(CHAR(ODUDT),6,2)) as OrderDueDate,
TRIM(FITEM) as ModelNumber,
TRIM(DPTNO) as ProductionLine
From ORMAST
Where
DPTNO = prodln;
-- Executable code starts here....
SET IN_DATE = (DECIMAL(CHAR(SUBSTR(CHAR(STRDATE),1,4);
SUBSTR(CHAR(STRDATE),6,2) CONCAT
SUBSTR(CHAR(STRDATE),9,2))) - 19000000) ;
OPEN FinGoodSCH ;
END
Note that if you happened to want to use IN_DATE in your cursor, you'd still do it like the above. The value of any variables used in the DECLARE CURSOR statement are not evaluated until the cursor is opened in DB2.

ADODB.Recordset error '800a0e78' - Error when I pass 2 parameters (ASP/MSSQL)

My classic ASP page thorws up an error when I pass 2 paremeters to the Exec code for my
stored procedure. Basically I want to send 1 paremeter to cover the column I am looking for
and one for the search-term. For example imSchool, New York University. I have data checks on the data sent as well as Record Set.State code showing that everytime i choose SQL Query with the two paramerts the error "Operation is not allowed when the object is closed." always shows.
I tried to open the object at " While Not rs.EOF" line of code where it error out bit no luck.
I am thinking my SQLQuery is bad because when I run the Stored Procedure in MSSQL with the same input I get a return table everytime. SQL Server 2008 R2 and Classic ASP.
HERE IS MY IF STATMENT WITH SQLQUERY CODE (keep in mind the 1st one works fine and data is selected from the DB)
SQLQuery = "Exec sp_vw_InternImport"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.CursorType = 3
rs.Open SQLQuery, OBJdbConnection
If filterColmn <> "" Then
SQlQuery = "Exec sp_vw_InternImport_ColID #LookUpID=N'" + filterID + "'" + ", #LookUpCol=N'" + filterID + "'"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.CursorType = 3
rs.Open SQLQuery, OBJdbConnection
End If
HERE IS MY STORED PROCEDURE CODE
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_vw_InternImport_ColID]
(
#LookUpID nvarchar (255),
#LookUpCol nvarchar (50)
)
AS
SET NOCOUNT ON
BEGIN
IF #LookUpCol = 'imYear'
SELECT * FROM v_InternImport WHERE imYear = #LookUpID
ELSE
IF #LookUpCol = 'imSchool'
SELECT * FROM v_InternImport WHERE imSchool = #LookUpID
ELSE
IF #LookUpCol = 'imDiscipline'
SELECT * FROM v_InternImport WHERE imDiscipline = #LookUpID
IF #LookUpCol = 'imDegree'
SELECT * FROM v_InternImport WHERE imDegree = #LookUpID
END
When passing arguments to stored procedure directly, you don't have to "assign" the parameters. Doing this probably results in the whole value passed (e.g. #LookUpCol will have the value of #LookUpCol ='imYear') thus your SP won't select anything and you have empty and closed recordset.
Try having such code instead:
SQlQuery = "Exec sp_vw_InternImport_ColID '" & filterID & "', '" & filterID & "'"
You can't use the same object twice at the same time (like rs), you have to make it another name or you have to close it and start over.. This should work:
SQLQuery = "Exec sp_vw_InternImport"
If filterColmn <> "" Then SQLQuery = "Exec sp_vw_InternImport_ColID #LookUpID=N'" + filterID + "'" + ", #LookUpCol=N'" + filterID + "'"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.CursorType = 3
rs.Open SQLQuery, OBJdbConnection
rs.Close
Or in your example, if you close the first rs object before you go on to the next, it may help.
set rs = nothing ... then .... set rs = server.createobject
So like this:
SQLQuery = "Exec sp_vw_InternImport"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.CursorType = 3
rs.Open SQLQuery, OBJdbConnection
rs.Close ------ Close it before you re-open it
If filterColmn <> "" Then
SQlQuery = "Exec sp_vw_InternImport_ColID #LookUpID=N'" + filterID + "'" + ", #LookUpCol=N'" + filterID + "'"
rs.Open SQLQuery, OBJdbConnection
rs.Close ------ Always Close your Objects!
End If

Resources