using session variables while deploying stored procedures-snowflake - snowflake-cloud-data-platform

I have a situation where I am pushing my stored procedure DDL into git repository. These codes have some environment specific codes, like I have dev,qa and prd, while I need to replace every time while deploying.
What I am looking for is to be able to use variables, that could be replaced while deploying. Something like this
var env= 'dev';
create or replace procedure test
returns varchar
language javascript
as
$$
insert into raw_$env.employee values(1);
$$;
When I run this code I want to have the code deployed like this.
create or replace procedure test
returns varchar
language javascript
as
$$
insert into raw_raw.employee values(1);
$$;

To specify the dynamic schema name, you can use IDENTIFIER(). Then use the javascript stored procedure API to execute.
create or replace procedure test()
returns text
language javascript
execute as caller
as
$$
snowflake.createStatement({ sqlText: `set t = 'raw_' || $env || '.employee'` }).execute()
snowflake.createStatement({ sqlText: `insert into identifier($t) values(1)` }).execute()
return 'SUCCESS'
$$;
set env= 'dev';
call test();
You may want to specify the database as well.
UPDATE -- Static solution, hard coding the 'env' parameter
RUN IN SNOWSQL
!define env=dev;
create or replace procedure test()
returns text
language javascript
execute as caller
as
$$
snowflake.createStatement({ sqlText: `insert into raw_&env.employee values(1)` }).execute() return 'SUCCESS'
$$;
To verify that the substition is static:
select get_ddl('procedure','test()')
The env parameter can also be defined in a config file or on the SNOWSQL command line.

Related

Why does setting session variable in Snowflake stored procedure not work?

When I set a session variable as caller in a Snowflake stored procedure, the procedure throws error "Error: Session variable '$T' does not exist (line 5)"
This is how I defined my procedure :
create or replace procedure test()
returns varchar()
language SQL
EXECUTE AS CALLER
as
$$
begin
set T = 0;
select $T;
return 'Done !';
end;
$$;
call test();
If I set the variable T before running the procedure it works, but if I don't I get the error
It's normal because when the script is compiled, there is no session variable. To be able to compile the snowflake script successfully, the session variable should exist.
Here is a workaround:
create or replace procedure test()
returns varchar()
language SQL
EXECUTE AS CALLER
as
$$
begin
set T = 0;
select getvariable('T');
return 'Done !';
end;
$$
;
call test();

write multipe statments in snowflake

Hi i have one doubt in snowflake how to write multiple update stments using stored procedure.
i have tried like below
create or replace procedure sp_multipleupdate()
returns table()
lANGUAGE sql
as
$$
declare res rsultset(
update TEST.PUBLIC.DEPT set Dname='PM' where deptid=10;
update TEST.PUBLIC.emp set name='veavi' where deptno=20;
update TEST.PUBLIC.loc set locname='del' where id=5;
)
begin
return table(res);
end;
$$;
getting error :
000006 (0A000): Multiple SQL statements in a single API call are not supported; use one API call per statement instead.
Syntax error: unexpected '('. (line 2)
please let me know how to write query to achive this task in snowflake server .
Multiple SQL statements inside the resultset are not supported.
Rather than writing the UPDATE statements like that I would create a more generic procedure and pass arguments to it, so maybe split the above one in 3 procedures since these UPDATE statements are for different tables.
Here is a sample of a generic stored procedure:
create or replace procedure find_invoice_by_id_via_execute_immediate(id varchar)
returns table (id integer, price number(12,2))
language sql
as
declare
select_statement varchar;
res resultset;
begin
select_statement := 'SELECT * FROM invoices WHERE id = ' || id;
res := (execute immediate :select_statement);
return table(res);
end;
You can read more here.

Snowflake:Automate Cloning Process

Requirement: I am looking at how to automate the cloning process from Prod to dev every day
Either Python or Stored Procedure
You can clone a database with a single-line SQL statement; however, I tested running that in a SQL stored procedure and it will not run create statements in SQL stored procedures. A JavaScript stored procedure will work.
Since all Snowflake tasks require a stored procedure to run, you'll need a JavaScript stored procedure if you want to use tasks.
Here's a sample:
-- Recommend you do not store the SP in a database you'll be cloning
create or replace procedure UTIL_DB.PUBLIC.CLONE_MY_DB()
returns string
language javascript
as
$$
return executeNonQuery('create or replace database TEST2 clone TEST1');
function executeNonQuery(queryString) {
var out;
cmd1 = {sqlText: queryString};
stmt = snowflake.createStatement(cmd1);
var rs;
rs = stmt.execute();
rs.next();
return rs.getColumnValue('status');
return out;
};
$$;
-- Make sure the SP works
drop database if exists TEST2;
call UTIL_DB.PUBLIC.CLONE_MY_DB();
-- Create a task calling the SP.
-- Use CRON syntax to get productionized
-- https://docs.snowflake.com/en/sql-reference/sql/create-task.html
create task MY_CLONE_TASK
warehouse = TEST
schedule = '1440 minute'
as call UTIL_DB.PUBLIC.CLONE_MY_DB();
Remember that your task is not enabled by default. You'll need to do something like this for the role you want to run the task:
use role ACCOUNTADMIN;
grant execute task on account to role SYSADMIN;
use role SYSADMIN;
alter task UTIL_DB.PUBLIC.MY_CLONE_TASK resume;
Since you make reference to cloning, I will assume that your Prod and Dev environments are in the same Snowflake account. You could this pretty easily with a task that simply drops the DEV database and then clones the PROD database. I don't think you need an SP or Python for this.

in postgres, how do you call a prepared statement from within a procedure

in postgres
as an SQL command execute runs a prepared statement
from within a procedure execute runs a dynamic command.
can prepared statements be called from within a procedure?
per Frank below, part of code to insert a user into an arbitrary schema:
execute 'insert into '||v_schema||'.usr( login, email, name, role, pwd )
values( $1, $2, $3, $4, md5($5) )
returning usr_id'
into v_usr_id
using p_login, p_email, p_name, v_role, p_pwd;
notes:
INTO clause before USING clause
no parens around USING "args"
Use EXECUTE in combination with USING (available since version 8.4):
CREATE OR REPLACE FUNCTION foo(int)
RETURNS bool
LANGUAGE plpgsql
AS
$$
BEGIN
EXECUTE 'INSERT INTO tablename(columnname) VALUES($1);' USING($1); -- your input
RETURN true;
END;
$$;

Create database using a stored function

I am new to PostgreSQL and want to create a database using a stored function.
For ex:
CREATE OR REPLACE FUNCTION mt_test(dbname character varying)
RETURNS integer AS
$BODY$
Create Database $1;
Select 1;
$BODY$
LANGUAGE sql;
When I am trying to execute this function I get a syntax error.
Does Postgres support the CREATE DATABASE statement in stored functions?
This question is old, but for the sake of completeness ...
As has been pointed out in other answers, that's not simply possible because (per documentation):
CREATE DATABASE cannot be executed inside a transaction block.
It has also been reported that the restriction can be bypassed with dblink.
How to use (install) dblink in PostgreSQL?
What was missing so far is a proper function actually doing it:
CREATE OR REPLACE FUNCTION f_create_db(dbname text)
RETURNS integer AS
$func$
BEGIN
IF EXISTS (SELECT 1 FROM pg_database WHERE datname = dbname) THEN
RAISE NOTICE 'Database already exists';
ELSE
PERFORM dblink_exec('dbname=' || current_database() -- current db
, 'CREATE DATABASE ' || quote_ident(dbname));
END IF;
END
$func$ LANGUAGE plpgsql;
Checks if the db already exists in the local cluster. If not, proceed to create it - with a sanitized identifier. We would not want to invite SQL injection.
You can't create a database inside of a function because it's not possible to create a database inside a transaction.
But most probably you don't mean to create databases but schemas, which more closely resemble the MySQL's databases.
I found a tricky solution to this problem, but possible. After looking and reading almost in everywhere I tried something and it worked.
if the error is "CREATE DATABASE cannot be executed from a function or multi-command string" we can force a single command string using dblink. And make it to connect to itself.
Check for dblink installation instructions at dblink
PERFORM replication.dblink_connect('myconn','host=127.0.0.1 port=5432 dbname=mydb user=username password=secret');
PERFORM replication.dblink_exec('myconn', 'CREATE DATABASE "DBFROMUSER'||id||'" TEMPLATE "TEMPL'||type||'";',false);
PERFORM replication.dblink_disconnect('myconn');
In my case using different kinds of template.
Greetings
postgres=> create or replace function mt_test(dbname text)
returns void language plpgsql as $$
postgres$> begin
postgres$> execute 'create database '||$1;
postgres$> end;$$;
CREATE FUNCTION
postgres=> select work.mt_test('dummy_db');
ERROR: CREATE DATABASE cannot be executed from a function or multi-command string
CONTEXT: SQL statement "create database dummy_db"
PL/pgSQL function "mt_test" line 2 at EXECUTE statement
postgres=>
note the error message: CREATE DATABASE cannot be executed from a function or multi-command string
so the answer to the question:
Does postgresql support creating statement in stored function
is "no" (at least on 8.4 - you don't specify your version)

Resources