postgresql with jdbc and stored procedures (functions): ResultSet - stored-procedures

I just tried to call a stored function from the server (getStat), that is looking like this:
create type stat as (type text, location text, number int);
create function getStat() returns setof stat as 'select distinct table1.type, table1.location, table1.number from table1, table2 where table2.finding=10 order by number desc;' language 'sql';
Now here is the jdbc code:
CallableStatement callable = null;
String storedProc = "{call getStat(?, ?, ?)}";
try {
callable = connection.prepareCall(storedProc);
callable.registerOutParameter(1, java.sql.Types.VARCHAR);
callable.registerOutParameter(2, java.sql.Types.VARCHAR);
callable.registerOutParameter(3, java.sql.Types.INTEGER);
boolean results = callable.execute();
System.out.println(callable.getString(1));
System.out.println(callable.getString(2));
System.out.println(callable.getInt(3));
while(results){
ResultSet rs = callable.getResultSet();
while(rs.next()){
System.out.println(rs.getString(1));
System.out.println(rs.getString(2));
System.out.println(rs.getInt(3));
}
//rs.close();
results = callable.getMoreResults();
}
Okay, and now the problem:
When I am calling it, it just prints out the first line, of the whole bulk, that should be printend. Yeah, that is clear, because I execute the following code:
System.out.println(callable.getString(1));
System.out.println(callable.getString(2));
System.out.println(callable.getInt(3));
But I do the same in the while loop...and nothing more is displayed.
Maybe the problem is something obvious, but I am missing that :(
Thanks!

No need to use a CallableStatement with the {call ...} syntax.
Just use a select and a regular Statement:
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("select * from getStat()");
while (rs.next())
{
System.out.println(rs.getString(1));
System.out.println(rs.getString(2));
System.out.println(rs.getInt(3));
}

Related

Executing sql from a javascipt UDF

I have a snowflake table being used to store records of sql being executed by a stored procedure and any error messages. The records in this table are being saved as a string with special chars escaped with javascripts escape('log') function. I then want to create a view to this log table that will return the records in an easily readable format.
My first attempt at this was to create an additional stored procedure to query the log table, pass the record to the unescape() function, then return it. Calling this procedure works as intended but we can't then create a view of this data say with something like
create view log_view as
select (call UNESCAPE_PROC());
The other idea was to use a UDF rather than a stored procedure. However this also fails as we can't execute sql code with a javascript UDF. This post touches on this idea.
My question is how can I record these executed statements in a table in such a way as they can be later viewed in a plain text readable format. I've outlined my attempts below but perhaps my approach is flawed, open to any suggestions.
Minimal working example below
Sample log table and procedure to write a statement to said table
create or replace table event_table(
event varchar,
event_stamp timestamp
);
create or replace procedure insert_to_event(stamp string)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP to log an event message with timestamp to event_table'
EXECUTE AS CALLER
AS
$$
// some variables to log in our event table
var str_stamp = (new Date()).toISOString();
to_log = `insert into dummy_table values(2, 'Bill', `+STAMP+`);`;
sql =
`INSERT INTO event_table (
event,
event_stamp
)
VALUES
('`+escape(to_log)+`', to_timestamp('`+str_stamp+`'));`;
var stmnt = snowflake.createStatement({ sqlText: sql });
stmnt.execute();
return "logged: "+ escape(to_log)
$$;
call insert_to_event(current_timestamp());
select * from event_table;
Stored procedure to return readable log records
CREATE OR REPLACE PROCEDURE UNESCAPE_PROC()
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP will select a chosen column from event_table table, pass it to javascripts unescape() fn and return it'
EXECUTE AS CALLER
AS
$$
unescape_sql =
`select event from event_table`
var errs_res = [];
try {
all_logs = snowflake.execute(
{ sqlText: unescape_sql }
);
// iterate over all columns
while (all_logs.next()) {
errs_res.push(all_logs.getColumnValue(1));
}
return unescape(errs_res)
}
catch(err){
return "something went wrong: " + err
}
$$;
call UNESCAPE_PROC();
Which returns the records in a readable form as expected
However this of course wont work as part of a view eg.
-- define a view calling this procedure??
create view log_view as
select (call UNESCAPE_PROC());
Javascript user defined function can be used in a view like this, however it cannot be used to execute sql as in the stored procedures
-- use a UDF instead
CREATE OR REPLACE FUNCTION UNESCAPE_UDF()
RETURNS string
LANGUAGE JAVASCRIPT
AS
$$
unescape_sql =
`select event from event_table`
var errs_res = [];
try {
all_logs = snowflake.execute(
{ sqlText: unescape_sql }
);
// iterate over all columns
while (all_logs.next()) {
errs_res.push(all_logs.getColumnValue(1));
}
return unescape(errs_res)
}
catch(err){
return "something went wrong: " + err
}
$$
;
select UNESCAPE_UDF();
Stored procedures will solve one half of my problem for me, whilst UDF's will solve the other half. How can I combine the functionality of these two methods to solve this issue?
A much cleaner approach using parameters binding:
create or replace procedure insert_to_event(stamp string)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP to log an event message with timestamp to event_table'
EXECUTE AS CALLER
AS
$$
// some variables to log in our event table
var str_stamp = (new Date()).toISOString();
to_log = `insert into dummy_table values(2, 'Bill', '${str_stamp}');`;
sql = `INSERT INTO event_table (event,event_stamp)
VALUES(?, try_to_timestamp(?));`;
var stmnt = snowflake.createStatement({sqlText: sql, binds:[to_log, str_stamp]});
stmnt.execute();
return "logged: "+ to_log
$$;
Call:
call insert_to_event(current_timestamp());
-- logged: insert into dummy_table values(2, 'Bill', '2022-02-03T17:45:44.140Z');
select * from event_table;
Found a solution/workaround.
Rather than using javascripts escape/unescape functions to remove special chars from the logs, we use a regex replace eg.
create or replace procedure insert_to_event(stamp string)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
COMMENT = 'SP to log an event message with timestamp to event_table'
EXECUTE AS CALLER
AS
$$
// some variables to log in our event table
var str_stamp = (new Date()).toISOString();
to_log = `insert into dummy_table values(2, 'Bill', `+STAMP+`);`;
to_log = to_log.replace(/[`~!##$%^&*|+=?'"<>\{\}\[\]\\\/]/gi, '');
sql =
`INSERT INTO event_table (
event,
event_stamp
)
VALUES
('`+to_log+`', to_timestamp('`+str_stamp+`'));`;
var stmnt = snowflake.createStatement({ sqlText: sql });
stmnt.execute();
return "logged: "+ to_log
$$;
call insert_to_event(current_timestamp());
select * from event_table;
Which writes to the log table in an easily readable format with no need for additional stored procedures/UDF's.

return timestamp value in store procedure

i'm trying to get timestamp value as result of stored procedure.
but getting error .
error message:- SQL Error [100132] [P0000]: JavaScript execution error: Incorrect Timestamp returned.
CREATE OR REPLACE PROCEDURE simple_stored_procedure_example(awesome_argument VARCHAR)
returns TIMESTAMPNTZ
language javascript
as
$$
var cmd = "SELECT EXTRACT_END_TIME FROM EXPLARITY_DB.EXPLARITY_SCHEMA.DEMO_CONTROL_TABLE WHERE PIPELINE_NAME =:1";
var sql = snowflake.createStatement(
{sqlText: cmd,
binds: ['awesome_argument']
}
);
var result1 = sql.execute();
return result1;
$$;
CALL simple_stored_procedure_example('pipeline1');
It can be done like this:
create or replace table tst_tbl(c1 timestamp);
insert into tst_tbl values ('2021-02-02 10:00:00.000');
create or replace procedure my_test(myarg VARCHAR)
returns TIMESTAMP
language javascript
as
$$
var cmd = "SELECT c1 FROM tst_tbl";
var sql = snowflake.createStatement({sqlText: cmd});
var resultSet = sql.execute();
resultSet.next();
my_date = resultSet.getColumnValue(1);
return my_date;
$$;
call my_test('test');
I get 1 row back as expected.

Trying to pass parameter as binding variable in snowflake statement

Below is my stored procedure, I'm not sure as to why it keeps throwing an error. The error I get is
SQL compilation error: syntax error line XX at position XX unexpected '?'.
I have followed the documentation here but it does not seem to work for me.
This is what I have:
CREATE OR REPLACE PROCEDURE spExample(INPUT_TABLE VARCHAR)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
result = "";
try {
var sql_cmd = "SELECT * FROM ?;";
var sql_stmt = snowflake.createStatement({sqlText: sql_cmd, binds:[INPUT_TABLE]});
sql_stmt.execute();
} catch(err) {
result += "Message: " + err.message;
}
return result;
$$;
Have I made a mistake somewhere?
Above answers subject your code to sql injection attack. And of course you can bind a table name to a variable in snowflake.
Do
var sql_cmd = "SELECT * FROM IDENTIFIER('?');";
It seems I had the exact same misunderstanding as the OP. It was good to find this answer.
In any case, it's a lot more flexible & more readable to use JavaScript template literals using backticks (instead of using single quotes or double quotes). They allow you to use expression interpolation in the format of
`Some text here. ${expression} Some more text here.`
Just fill in with your variable or variables (or expression).
Here is what I tried and it executed perfectly:
CREATE OR REPLACE PROCEDURE spExample(INPUT_TABLE VARCHAR)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
result = "";
try {
var sql_cmd = "SELECT * FROM IDENTIFIER(?);";
var sql_stmt = snowflake.createStatement({sqlText: sql_cmd, binds:[INPUT_TABLE]});
sql_stmt.execute();
} catch(err) {
result += "Message: " + err.message;
}
return result;
$$;
Call spExample('PAIDBILLS');
For me, this worked, first Quotes then brackets. I think its different for different type of query , other answers worked for me for select but not for SHOW
var sql_cmd = "SHOW WAREHOUSES like '(?)';"
CREATE OR REPLACE PROCEDURE spExample(INPUT_TABLE VARCHAR)
RETURNS varchar
LANGUAGE JAVASCRIPT
execute as owner
AS
$$
result = "";
try {
var sql_cmd = "SHOW WAREHOUSES like '(?)';";
var sql_stmt = snowflake.execute({sqlText: sql_cmd, binds:[INPUT_TABLE]});
} catch(err) {
result += "Message: " + err.message;
}
return result;
$$;
Call spExample('WREHOUSE NAME');
actually yes.
Bind variable is just that, a variable. So you can do a
SELECT * FRMO MY_TABLE WHERE MY_COLUMN=?
but you can't use bind to substitute for commands or column or table names. You can however use simple JS concatenation, like
var sql_cmd = "SELECT * FROM "+INPUT_TABLE;

How to debug a Snowflake stored procedure?

I am using the Snowflake Cloud Database, please help me with a tool to debugging the procedures or functions.
The following Snowflake Javascript Stored Procedure is a template I use to get started on a new Stored Procedures. It contains plenty of debugging tricks, such as:
it has a "where am I?" variable which gives you a understanding of where in the code you are
it gathers information in an array as the process moves along
it returns that array to the standard output of the call command
it has a "good start" of an exception block, who's contents also get pushed out to standard output on a call of the stored procedure, should it fail.
Something I've been meaning to add is to set a query tag in the code as well, that'd be helpful when reviewing query history, to easily identify the SQL commands that were used in the execution of the Stored Procedure.
This "ties into" the final "debugging trick" - you should always review the query history (actual queries your code executed) when developing stored procedures in a development or test environment, particularly when you are building dynamic SQL statements. Reviewing your query history is a must-do and will show you exactly the commands run and the order of operations of them running.
Here's the code with the sample table it uses, I hope it helps...Rich
CREATE OR REPLACE TABLE test_scripts (
load_seq number,
script varchar(2000)
);
INSERT INTO test_scripts values
(1, 'SELECT current_timestamp();'),
(2, 'SELECT current_warehouse();'),
(3, 'SELECT COUNT(*) FROM snowflake.account_usage.tables;'),
(4, 'SELECT current_date();'),
(5, 'SELECT current_account();'),
(6, 'SELECT COUNT(*) FROM snowflake.account_usage.tables;'),
(7, 'SELECT ''RICH'';');
select * from test_scripts;
CREATE OR REPLACE PROCEDURE sp_test(p1 varchar, p2 varchar)
RETURNS ARRAY
LANGUAGE javascript
EXECUTE AS caller
AS
$$
//note: you can change the RETURN to VARCHAR if needed
// but the array "looks nice"
try {
var whereAmI = 1;
var return_array = [];
var counter = 0;
var p1_str = "p1: " + P1
var p2_str = "p2: " + P2
var load_seq = P1;
var continue_flag = P2;
whereAmI = 2;
return_array.push(p1_str)
return_array.push(p2_str)
whereAmI = 3;
//which SQL do I want to run?
if (continue_flag=="YES") {
return_array.push("query 1")
var sqlquery = "SELECT * FROM test_scripts WHERE load_seq >= " + load_seq + " order by 1, 2;";
}
else {
return_array.push("query 2")
var sqlquery = "SELECT * FROM test_scripts WHERE load_seq = " + load_seq + " order by 1, 2;";
}
whereAmI = 4;
//begin the run of grabbing the commands
var stmt = snowflake.createStatement( {sqlText: sqlquery} );
var rs = stmt.execute();
whereAmI = 5;
// Loop through the results, processing one row at a time...
while (rs.next()) {
counter = counter + 1;
var tmp_load_seq = rs.getColumnValue(1);
var tmp_script = rs.getColumnValue(2);
var tmp_rs = snowflake.execute({sqlText: tmp_script});
tmp_rs.next();
var tmp_col1 = tmp_rs.getColumnValue(1);
return_array.push("tmp_col1: " + tmp_col1)
}
whereAmI = 6;
return_array.push("end process - counter: " + counter)
return return_array;
}
catch (err) {
return_array.push("error found")
return_array.push(whereAmI)
return_array.push(err)
return return_array;
}
$$;
CALL sp_test(3, 'NO');
I do not believe there is any editor / debugger for stored procedures for Snowflake. Few options:
You can break your code to smaller parts and try to troubleshoot
Use a log table and insert into log table often, so you can look at the log table to find out what went wrong
unfortunately there isn't one environment to rule them all
1. write your SQL in a Worksheet or Editor
2. write your SPROC code in a JS enabled editor
3. merge them together in a Worksheet or Editor
4. Unit test in SPROCS as shown above by #Rich Murmane
I normally just write SPROCS in a Worksheet but it isnt optimal
Logging is your friend here, as there is no debugger. In general finding and using a debugger for db stored procedures is hard to pull off. Not impossible, just unlikely.
This is a decent alternative:
CREATE or replace PROCEDURE do_log(MSG STRING)
RETURNS STRING
LANGUAGE JAVASCRIPT
EXECUTE AS CALLER
AS $$
//see if we should log - checks for do_log = true session variable
try{
var foo = snowflake.createStatement( { sqlText: `select $do_log` } ).execute();
} catch (ERROR){
return; //swallow the error, variable not set so don't log
}
foo.next();
if (foo.getColumnValue(1)==true){ //if the value is anything other than true, don't log
try{
snowflake.createStatement( { sqlText: `create temp table identifier ($log_table) if not exists (ts number, msg string)`} ).execute();
snowflake.createStatement( { sqlText: `insert into identifier ($log_table) values (:1, :2)`, binds:[Date.now(), MSG] } ).execute();
} catch (ERROR){
throw ERROR;
}
}
$$
;
Then in the stored procedure, you want to debug add a log function at the top:
function log(msg){
snowflake.createStatement( { sqlText: `call do_log(:1)`, binds:[msg] } ).execute();
}
Then above the call to the stored procedure:
set do_log = true; --true to enable logging, false (or undefined) to disable
set log_table = 'my_log_table'; --The name of the temp table where log messages go
Then in the actual stored procedure you need to add some logging lines:
log('this is another log message');
Then call the stored procedure as you would normally. Then select from my_log_table.
Important note: this uses a temp table, so you won't be able to read from that logging table in a different Snowflake connection. This means if you're using the Worksheet editor you need to keep all this stuff on the same sheet.
"Borrowed" from: https://community.snowflake.com/s/article/Snowflake-Stored-Procedure-Logging

Run view query in Symfony/propel

I've an existing project in Symfony 1.4/Propel 1.4
For database optimization purpose, I created a view of few tables. The function/ view query are as follow:
create function getPlayer() returns INTEGER DETERMINISTIC NO SQL return #getPlayer;
create view getPlay as
SELECT
CASE WHEN play.hiderid = getPlayer() THEN play.seekerid ELSE play.hiderid END AS opponent, play . *
FROM odd_play play, odd_match mat
WHERE (seekerid = getPlayer() OR hiderid = getPlayer())
AND play.id = mat.latestplay;
After creating above view, I can write following simple SQL query to get required data effectively.
select play.*
from (select #getPlayer:=1 p) p, getPlay play;
Now the problem is that, how to write this query in Symfony/Propel 1.4. Can someone please suggest how to write that query in propel 1.4?
Edit after J0K comments
I'm trying following
class GetplayPeer extends BaseGetplayPeer {
static public function getOpponents($player){
$con = Propel::getConnection();
$sql = "select play.* from (select #getPlayer:=:player p) ply, getPlay play;";
$stmt = $con->prepare($sql);
$stmt->bindParam(":player",&$player,PDO::PARAM_INT);
$rs = $stmt->execute();
//$opponents = GetplayPeer::populateObjects($rs);
echo "opponents=<pre>";print_r($rs);exit;
}
} // GetplayPeer
I'm getting 1 as output, which is not expected output.

Resources