Here is my sample code below:
CREATE OR REPLACE PROCEDURE database.schema.sp_sample(dynamic_columns VARCHAR, dynamic_where_clause VARCHAR)
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
EXECUTE AS CALLER
AS
$$
rs="Success";
try {
retrieve_queries_sql = `SELECT COL_1, COL_2, ? FROM table ? GROUP BY ?`;
var stmt = snowflake.createStatement( {sqlText: retrieve_queries_sql, binds:[DYNAMIC_COLUMNS, DYNAMIC_WHERE_CLAUSE, DYNAMIC_COLUMNS]} );
var rs = stmt.execute();
}
catch(err) {
rs= "Failed Message: "+err.message;
}
return rs;
$$;
When I try to bind these columns Snowflake throws an error Unexpected ? and I even tried using :1, :2 even that didnt work same error Unexpected :
Dont want to prefer interpolation ${dynamic_columns} or ${dynamic_where_clause} as it can lead to sql injection concerns
How to bind the arguments in the best possible way? Any suggestions appreciated.
in other UDF the parameters to the JavaScript function need to be ALL_CAPS in the JavaScript body, as the SQL part (the function declaration) the SQL is case insensitive, while java it's "how it really is" which is upper case. Or you can double quote then in the declaration, as lower case, to have then lower case in the body.
Related
SPLIT_PART function used in a select statement in Snowflake Stored procedure returns null value.
var stmt5 = snowflake.CreateStatement({sqltext:`Select SPLIT_PART(SPLIT_PART(:1,''/'',2),''.gz'',1)`,binds:[stagename]});
var queryText=stmt5.getSqlText();
var x=stmt5.execute();
x.next();
stagename is retrieved from the List #my_stage command result and the result of the Split Part is used in the Copy Command for inserting records into a snowflake table.
If somebody responds will share the code thru an email to help me fix the issue. Thanks in advance.
You're using JavaScript with backticks, so there's no need to escape the single quotes. By doing so, it actually looks like an empty string. This is how it would look in your SQL history tab:
Select SPLIT_PART(SPLIT_PART(:1,''/'',2),''.gz'',1)
If you put a file name in and try that:
Select SPLIT_PART(SPLIT_PART('mypath/myfile.gz',''/'',2),''.gz'',1)
This doesn't actually return a null value. It's a syntax error, so maybe this execute call is in a try/catch block. That would return a null value when you try to use .next(),
Try it with the quotes single:
var stmt5 = snowflake.CreateStatement({sqltext:`Select SPLIT_PART(SPLIT_PART(:1,'/',2),'.gz',1)`,binds:[stagename]});
I am creating a snowflake stored procedure where I need to store the output if the SQL query into an array , whose values will be later used to create SQL Command which will be the output. Please suggest how do I store the output in an array.
Please refer to the doc on how to setup SP in Snowflake:
https://docs.snowflake.com/en/sql-reference/stored-procedures-api.html
https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html
There are lots of examples over there.
You just need to know how to query Snowflake and get results, and the rest are just standard Javascript.
Example as below:
create or replace procedure test_sp()
returns string
language javascript
as
$$
var my_data = [];
var stmt = snowflake.createStatement({sqlText: "SELECT * FROM table_name" });
var rs = stmt.execute();
while (rs.next()) {
my_data.push(rs.getColumnValue(1));
my_data.push(rs.getColumnValue(2));
}
// then you can use my_data array in later part of your code
return '';
$$;
I am trying to create an UDTF where I am planning to pass the dynamic where clause as an argument to the UDTF
CREATE OR REPLACE FUNCTION FUNCTION_NAME(where_clause VARCHAR)
RETURNS TABLE ()
LANGUAGE JAVASCRIPT
AS
$$ var sql_command=SELECT COL1, COL2 FROM TABLE_1 JOIN TABLE_2 ON ....
+where_clause+ GROUP BY ...;
var stmt = snowflake.createStatement( {sqlText: sql_command} );
var resultSet = stmt.execute(); resultSet.next();
$$
Is this something possible and also I want to be able to handle quotes in the arguments during calling of the function
for example if the call is:
SELECT * FROM TABLE(FUNCTION_NAME('WHERE COL_1='value''));
How do I handle quotes during calling of the function. Thanks for the help in advance
I wanted to underline the answer of Felipe. JavaScript UDTFs do not support "Stored Procedures API".
https://docs.snowflake.com/en/sql-reference/stored-procedures-api.html
So you can not create and run a dynamic SQL inside of a JavaScript UDTF. It must have "processRow" callback function to process the values coming as parameters and return them as rows.
https://docs.snowflake.com/en/sql-reference/udf-js-table-functions.html#basic-hello-world-examples
You can create a stored procedure to run a dynamic SQL and return the result as an array/json, but it won't be effective.
For your original question, you just need to escape the quote by repeating it or using slash:
call FUNCTION_NAME('WHERE i=''1''');
call FUNCTION_NAME('WHERE i=\'1\'');
I ran into another issue with using a data reader around a sproc with multiple ref cursors coming out. I am getting a not supported exception. Unfortunately, i can see from where it is coming from the source code of npgsql however.. i am not sure if i agree with throwing that exception. The code we have written works with oracle (both fully managed and managed flavors), sql server. Any help appreciated to keep it consistent for an api across some of those key flavors of dbms out there.
sproc body
CREATE OR REPLACE FUNCTION public.getmultipleresultsets (
v_organizationid integer)
RETURNS Setof refcursor
LANGUAGE 'plpgsql'
AS $BODY$
declare public override void AddCursorOutParameter(DbCommand command,
string RefCursorName)
{
NpgsqlParameter parameter = (NpgsqlParameter)CreateParameter(RefCursorName, false);
parameter.NpgsqlDbType = NpgsqlDbType.Refcursor;
parameter.NpgsqlValue = DBNull.Value;
parameter.Direction = ParameterDirection.Output;
command.Parameters.Add(parameter);
}
cv_1 refcursor;
cv_2 refcursor;
BEGIN
open cv_1 for
SELECT a.errorCategoryId, a.name, a.bitFlag
FROM ErrorCategories a
ORDER BY name;
RETURN next cv_1;
open cv_2 for
SELECT *
FROM StgNetworkStats ;
RETURN next cv_2;
END;
$BODY$;
Key Reader code that wraps postgres sql (Entlib implementation of npgsql)
private IDataReader DoExecuteReader(DbCommand command, CommandBehavior cmdBehavior)
{
try
{
var sql = new StringBuilder();
using (var reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
{
sql.AppendLine($"FETCH ALL IN \"{ reader.GetString(0) }\";");
}
}
command.CommandText = sql.ToString();
command.CommandType = CommandType.Text;
IDataReader reader2 = command.ExecuteReader(cmdBehavior);
return reader2;
}
catch (Exception)
{
throw;
}
}
The command building code is shown below
Helper.InitializeCommand(cmd, 300, "getmultipleresultsets");
db.AddReturnValueParameter(cmd);
db.AddInParameter(cmd, "organizationId", DbType.Int32, ORGANIZATIONID);
db.AddCursorOutParameter(cmd, "CV_1");
db.AddCursorOutParameter(cmd, "CV_2
The code that adds the refcursor parameter goes something like this
You code above seems to garble the PostgreSQL function with the .NET client code attempting to read its result.
Regardless, your function is declared to return a set of refcursors - this is not the same as two output parameters; you seem to be confusing the name of the cursor (cursors have names, but not ints, for example) with the name of the parameter (int parameters do have names).
Please note that PostgreSQL does not actually have output parameters - a function always returns a single table, and that's it. PostgreSQL does have a function syntax with output parameters, but that is only a way to construct the schema of the output table. This is unlike SQL Server, which apparently can return both a table and a set of named output parameters. To facilitate portability, when reading results, if Npgsql sees any NpgsqlParameter with direction out, it will attempt to find a resultset with the name of the parameter and will simply populate the NpgsqlParameter's Value with the first row's value for that column. This practice has zero added value over simply reading the resultset yourself - it's just there for compatibility.
To sum it up, I'd suggest you read the refcursors with your reader and then fetch their results as appropriate.
In the JDBC code, I have the following that is working with SQL Server:
CallableStatement stmt = connection.prepareCall("{ call getName() }");
ResultSet rs = stmt.executeQuery();
if(rs != null)
{
while(rs.next())
{
//do something with rs.getString("name")
}
}
Multiple rows are returned for the above situation.
I understand that the use of a cursor is required to loop through the table in Oracle, but is there any way to keep the above code the same and accomplish the same thing?
Sample PL/SQL code would be much appreciated.
Thanks in advance.
You could implement getName() as a pipelined function:
CREATE OR REPLACE name_record AS OBJECT ( name VARCHAR2(100) );
/
CREATE OR REPLACE name_table AS TABLE OF name_record;
/
CREATE OR REPLACE FUNCTION getName RETURN name_table PIPELINED
AS
n name_record;
BEGIN
-- I have no idea what you're doing here to generate your list of names, so
-- I'll pretend it's a simple query
FOR i IN (SELECT name FROM someTable) LOOP
n := name_record( i.name );
PIPE ROW(n);
END LOOP;
END;
/
You would need to change the actual query in Java to SELECT name FROM TABLE(getName()).
This is straight JDBC, so it'll work with any database that has a valid JDBC driver.
It assumes, of course, that the stored proc exists in both and that you aren't using any non-standard, vendor-proprietary code in your class.