Writing stored procedures in Snowflake Scripting¶
This topic provides an introduction to writing a stored procedure in SQL by using Snowflake Scripting. For more information about Snowflake Scripting, see the Snowflake Scripting Developer Guide.
Introduction¶
To write a stored procedure that uses Snowflake Scripting:
Use the CREATE PROCEDURE or WITH … CALL … command with LANGUAGE SQL.
In the body of the stored procedure (the AS clause), you use a Snowflake Scripting block.
Note
If you are creating a Snowflake Scripting procedure in SnowSQL or the Classic Console, you must use string literal delimiters (
'
or$$
) around the body of the stored procedure.For details, see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector.
Snowflake limits the maximum size of the source code in the body of a Snowflake Scripting stored procedure. Snowflake recommends limiting the size to 100 KB. (The code is stored in a compressed form, and the exact limit depends on the compressibility of the code.)
You can capture log and trace data as your handler code executes. For more information, see Logging, tracing, and metrics.
Note
The same rules around caller’s rights vs. owner’s rights apply to these stored procedures.
The same considerations and guidelines in Working with stored procedures apply to Snowflake Scripting stored procedures.
The following is an example of a simple stored procedure that returns the value of the argument that is passed in:
CREATE OR REPLACE PROCEDURE output_message(message VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
BEGIN
RETURN message;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE output_message(message VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
RETURN message;
END;
$$
;
The following is an example of calling the stored procedure:
CALL output_message('Hello World');
The following is an example of creating and calling an anonymous stored procedure by using the WITH … CALL … command:
WITH anonymous_output_message AS PROCEDURE (message VARCHAR)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
RETURN message;
END;
$$
CALL anonymous_output_message('Hello World');
Note that in an anonymous stored procedure, you must use string literal delimiters ('
or $$
) around the body of the procedure.
Using arguments passed to a stored procedure¶
If you pass in any arguments to your stored procedure, you can refer to those arguments by name in any Snowflake Scripting expression. See the next sections for more details:
Simple example of using arguments passed to a stored procedure
Using an argument when building a string for a SQL statement
Simple example of using arguments passed to a stored procedure¶
The following stored procedure uses the values of the arguments in IF and RETURN statements.
CREATE OR REPLACE PROCEDURE return_greater(number_1 INTEGER, number_2 INTEGER)
RETURNS INTEGER NOT NULL
LANGUAGE SQL
AS
BEGIN
IF (number_1 > number_2) THEN
RETURN number_1;
ELSE
RETURN number_2;
END IF;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE return_greater(number_1 INTEGER, number_2 INTEGER)
RETURNS INTEGER NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
IF (number_1 > number_2) THEN
RETURN number_1;
ELSE
RETURN number_2;
END IF;
END;
$$
;
The following is an example of calling the stored procedure:
CALL return_greater(2, 3);
Using an argument in a SQL statement (binding)¶
As is the case with Snowflake Scripting variables, if you need to use an argument in a SQL statement, put a colon (:
) in front
of the argument name. For more information, see Using a variable in a SQL statement (binding).
The following sections contain examples that use bind variables in stored procedures:
Example of using a bind variable to set the value of a property
Example that uses bind variables to set parameters in a command
Example that uses a bind variable in a WHERE clause¶
The following stored procedure uses the id
argument in the WHERE clause of a SELECT statement. In the WHERE
clause, the argument is specified as :id
.
CREATE OR REPLACE PROCEDURE find_invoice_by_id(id VARCHAR)
RETURNS TABLE (id INTEGER, price NUMBER(12,2))
LANGUAGE SQL
AS
DECLARE
res RESULTSET DEFAULT (SELECT * FROM invoices WHERE id = :id);
BEGIN
RETURN TABLE(res);
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE find_invoice_by_id(id VARCHAR)
RETURNS TABLE (id INTEGER, price NUMBER(12,2))
LANGUAGE SQL
AS
$$
DECLARE
res RESULTSET DEFAULT (SELECT * FROM invoices WHERE id = :id);
BEGIN
RETURN TABLE(res);
END;
$$
;
The following is an example of calling the stored procedure:
CALL find_invoice_by_id('2');
In addition, the TO_QUERY function provides a simple syntax for accepting a SQL string directly in the FROM clause of a SELECT statement. For a comparison of the TO_QUERY function with dynamic SQL, see Constructing SQL at runtime.
Example of using a bind variable to set the value of a property¶
The following stored procedure uses the comment
argument to add a comment for a table in a
CREATE TABLE statement. In the statement, the argument is specified as :comment
.
CREATE OR REPLACE PROCEDURE test_bind_comment(comment VARCHAR)
RETURNS STRING
LANGUAGE SQL
AS
BEGIN
CREATE OR REPLACE TABLE test_table_with_comment(a VARCHAR, n NUMBER) COMMENT = :comment;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE test_bind_comment(comment VARCHAR)
RETURNS STRING
LANGUAGE SQL
AS
$$
BEGIN
CREATE OR REPLACE TABLE test_table_with_comment(a VARCHAR, n NUMBER) COMMENT = :comment;
END;
$$
;
The following is an example of calling the stored procedure:
CALL test_bind_comment('My Test Table');
View the comment for the table by querying the TABLES view in the INFORMATION_SCHEMA:
SELECT comment FROM information_schema.tables WHERE table_name='TEST_TABLE_WITH_COMMENT';
+---------------+
| COMMENT |
|---------------|
| My Test Table |
+---------------+
You can also view the comment by running a SHOW TABLES command.
Example that uses bind variables to set parameters in a command¶
Assume you have a stage named st
with CSV files:
CREATE OR REPLACE STAGE st;
PUT file://good_data.csv @st;
PUT file://errors_data.csv @st;
You want to load the data in the CSV files into a table named test_bind_stage_and_load
:
CREATE OR REPLACE TABLE test_bind_stage_and_load (a VARCHAR, b VARCHAR, c VARCHAR);
The following stored procedure uses the FROM, ON_ERROR, and VALIDATION_MODE parameters in
a COPY INTO <table> statement. In the statement, the parameter values are specified as
:my_stage_name
, :on_error
, and :valid_mode
, respectively.
CREATE OR REPLACE PROCEDURE test_copy_files_validate(
my_stage_name VARCHAR,
on_error VARCHAR,
valid_mode VARCHAR)
RETURNS STRING
LANGUAGE SQL
AS
BEGIN
COPY INTO test_bind_stage_and_load
FROM :my_stage_name
ON_ERROR=:on_error
FILE_FORMAT=(type='csv')
VALIDATION_MODE=:valid_mode;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE test_copy_files_validate(
my_stage_name VARCHAR,
on_error VARCHAR,
valid_mode VARCHAR)
RETURNS STRING
LANGUAGE SQL
AS
$$
BEGIN
COPY INTO test_bind_stage_and_load
FROM :my_stage_name
ON_ERROR=:on_error
FILE_FORMAT=(type='csv')
VALIDATION_MODE=:valid_mode;
END;
$$
;
The following is an example of calling the stored procedure:
CALL test_copy_files_validate('@st', 'skip_file', 'return_all_errors');
Examples that use a bind variable for an array¶
You can expand a bind variable that represents an array into a list of individual values
by using the spread operator (**
). For more information and examples, see Expansion operators.
Using an argument as an object identifier¶
If you need to use an argument to refer to an object (for example, a table name in the FROM clause of a SELECT statement), use the IDENTIFIER keyword to indicate that the argument represents an object identifier. For example:
CREATE OR REPLACE PROCEDURE get_row_count(table_name VARCHAR)
RETURNS INTEGER NOT NULL
LANGUAGE SQL
AS
DECLARE
row_count INTEGER DEFAULT 0;
res RESULTSET DEFAULT (SELECT COUNT(*) AS COUNT FROM IDENTIFIER(:table_name));
c1 CURSOR FOR res;
BEGIN
FOR row_variable IN c1 DO
row_count := row_variable.count;
END FOR;
RETURN row_count;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE get_row_count(table_name VARCHAR)
RETURNS INTEGER NOT NULL
LANGUAGE SQL
AS
$$
DECLARE
row_count INTEGER DEFAULT 0;
res RESULTSET DEFAULT (SELECT COUNT(*) AS COUNT FROM IDENTIFIER(:table_name));
c1 CURSOR FOR res;
BEGIN
FOR row_variable IN c1 DO
row_count := row_variable.count;
END FOR;
RETURN row_count;
END;
$$
;
The following is an example of calling the stored procedure:
CALL get_row_count('invoices');
The following example executes a CREATE TABLE … AS SELECT (CTAS) statement in a stored procedure based on the table names provided in arguments.
CREATE OR REPLACE PROCEDURE ctas_sp(existing_table VARCHAR, new_table VARCHAR)
RETURNS TEXT
LANGUAGE SQL
AS
BEGIN
CREATE OR REPLACE TABLE IDENTIFIER(:new_table) AS
SELECT * FROM IDENTIFIER(:existing_table);
RETURN 'Table created';
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE ctas_sp(existing_table VARCHAR, new_table VARCHAR)
RETURNS TEXT
LANGUAGE SQL
AS
$$
BEGIN
CREATE OR REPLACE TABLE IDENTIFIER(:new_table) AS
SELECT * FROM IDENTIFIER(:existing_table);
RETURN 'Table created';
END;
$$
;
Before calling the procedure, create a simple table and insert data:
CREATE OR REPLACE TABLE test_table_for_ctas_sp (
id NUMBER(2),
v VARCHAR(2))
AS SELECT
column1,
column2,
FROM
VALUES
(1, 'a'),
(2, 'b'),
(3, 'c');
Call the stored procedure to create a new table that is based on this table:
CALL ctas_sp('test_table_for_ctas_sp', 'test_table_for_ctas_sp_backup');
Using an argument when building a string for a SQL statement¶
Note that if you are building a SQL statement as a string to be passed to EXECUTE IMMEDIATE (see Assigning a query to a declared RESULTSET), do not prefix the argument with a colon. For example:
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;
Returning tabular data¶
If you need to return tabular data (for example, data from a RESULTSET) from your stored procedure, specify RETURNS TABLE(…) in your CREATE PROCEDURE statement.
If you know the Snowflake data types of the columns in the returned table, specify the column names and types in the RETURNS TABLE().
CREATE OR REPLACE PROCEDURE get_top_sales()
RETURNS TABLE (sales_date DATE, quantity NUMBER)
...
Otherwise (for example, if you are determining the column types during run time), you can omit the column names and types:
CREATE OR REPLACE PROCEDURE get_top_sales()
RETURNS TABLE ()
...
Note
Currently, in the RETURNS TABLE(...)
clause, you can’t specify GEOGRAPHY as a column type. This
applies whether you are creating a stored or anonymous procedure.
CREATE OR REPLACE PROCEDURE test_return_geography_table_1()
RETURNS TABLE(g GEOGRAPHY)
...
WITH test_return_geography_table_1() AS PROCEDURE
RETURNS TABLE(g GEOGRAPHY)
...
CALL test_return_geography_table_1();
If you attempt to specify GEOGRAPHY as a column type, calling the stored procedure results in the error:
Stored procedure execution error: data type of returned table does not match expected returned table type
To work around this issue, you can omit the column arguments and types in RETURNS TABLE()
.
CREATE OR REPLACE PROCEDURE test_return_geography_table_1()
RETURNS TABLE()
...
WITH test_return_geography_table_1() AS PROCEDURE
RETURNS TABLE()
...
CALL test_return_geography_table_1();
If you need to return the data in a RESULTSET, use TABLE() in your RETURN statement.
For example:
CREATE OR REPLACE PROCEDURE get_top_sales()
RETURNS TABLE (sales_date DATE, quantity NUMBER)
LANGUAGE SQL
AS
DECLARE
res RESULTSET DEFAULT (SELECT sales_date, quantity FROM sales ORDER BY quantity DESC LIMIT 10);
BEGIN
RETURN TABLE(res);
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE get_top_sales()
RETURNS TABLE (sales_date DATE, quantity NUMBER)
LANGUAGE SQL
AS
$$
DECLARE
res RESULTSET DEFAULT (SELECT sales_date, quantity FROM sales ORDER BY quantity DESC LIMIT 10);
BEGIN
RETURN TABLE(res);
END;
$$
;
The following is an example of calling the stored procedure:
CALL get_top_sales();
Calling a stored procedure from another stored procedure¶
In a stored procedure, if you need to call another stored procedure, use one of the following approaches:
Calling a stored procedure without using the returned value¶
Use a CALL statement to call the stored procedure (as you normally would).
If you need to pass in any variables or arguments as input arguments in the CALL statement, remember to use a colon (:
) in
front of the variable name. (See Using a variable in a SQL statement (binding).)
The following is an example of a stored procedure that calls another stored procedure but does not depend on the return value.
First, create a table for use in the example:
-- Create a table for use in the example.
CREATE OR REPLACE TABLE int_table (value INTEGER);
Then, create the stored procedure that you will call from another stored procedure:
-- Create a stored procedure to be called from another stored procedure.
CREATE OR REPLACE PROCEDURE insert_value(value INTEGER)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
BEGIN
INSERT INTO int_table VALUES (:value);
RETURN 'Rows inserted: ' || SQLROWCOUNT;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
-- Create a stored procedure to be called from another stored procedure.
CREATE OR REPLACE PROCEDURE insert_value(value INTEGER)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
INSERT INTO int_table VALUES (:value);
RETURN 'Rows inserted: ' || SQLROWCOUNT;
END;
$$
;
Next, create a second stored procedure that calls the first stored procedure:
CREATE OR REPLACE PROCEDURE insert_two_values(value1 INTEGER, value2 INTEGER)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
BEGIN
CALL insert_value(:value1);
CALL insert_value(:value2);
RETURN 'Finished calling stored procedures';
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE insert_two_values(value1 INTEGER, value2 INTEGER)
RETURNS VARCHAR NOT NULL
LANGUAGE SQL
AS
$$
BEGIN
CALL insert_value(:value1);
CALL insert_value(:value2);
RETURN 'Finished calling stored procedures';
END;
$$
;
Finally, call the second stored procedure:
CALL insert_two_values(4, 5);
Using the value returned from a stored procedure call¶
If are calling a stored procedure that returns a scalar value and you need to access that value, use the
INTO :snowflake_scripting_variable
clause in the CALL statement to capture the value in a
Snowflake Scripting variable.
The following example calls the get_row_count
stored procedure that was defined in
Using an argument as an object identifier.
CREATE OR REPLACE PROCEDURE count_greater_than(table_name VARCHAR, maximum_count INTEGER)
RETURNS BOOLEAN NOT NULL
LANGUAGE SQL
AS
DECLARE
count1 NUMBER;
BEGIN
CALL get_row_count(:table_name) INTO :count1;
IF (:count1 > maximum_count) THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE count_greater_than(table_name VARCHAR, maximum_count INTEGER)
RETURNS BOOLEAN NOT NULL
LANGUAGE SQL
AS
$$
DECLARE
count1 NUMBER;
BEGIN
CALL get_row_count(:table_name) INTO :count1;
IF (:count1 > maximum_count) THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;
$$
;
The following is an example of calling the stored procedure:
CALL count_greater_than('invoices', 3);
If the stored procedure returns a table, you can capture the return value by setting a RESULTSET to a string containing the CALL statement. (See Assigning a query to a declared RESULTSET.)
To retrieve the return value from the call, you can use a CURSOR for the RESULTSET. For example:
DECLARE
res1 RESULTSET;
BEGIN
res1 := (CALL my_procedure());
LET c1 CURSOR FOR res1;
FOR row_variable IN c1 DO
IF (row_variable.col1 > 0) THEN
...;
ELSE
...;
END IF;
END FOR;
...
Using nested stored procedures¶
A nested stored procedure is a stored procedure that’s defined within the scope of an anonymous block or a block in another stored procedure (the parent stored procedure).
You declare a nested stored procedure in the DECLARE section of a block, which can be part of a CREATE PROCEDURE statement. The following example shows a nested stored procedure declaration:
DECLARE
<nested_stored_procedure_name> PROCEDURE (<arguments>)
RETURNS <data_type>
AS
BEGIN
<nested_procedure_procedure_statements>
END;
BEGIN
<statements>
END;
For information about the declaration syntax of a nested stored procedure, see Nested stored procedure declaration syntax.
A nested stored procedure only exists within the scope of its block. It can be called from any section of its block (DECLARE, BEGIN … END, and EXCEPTION). A single block can contain multiple nested stored procedures, and one nested stored procedure can call another nested stored procedure in the same block. A nested procedure can’t be called or accessed from outside of its block.
A nested stored procedure operates in the same security context as the block that defines it. When a nested stored procedure is defined in a parent stored procedure, it automatically runs with the same privileges as the parent stored procedure.
Note
Both a nested stored procedure declaration and the CALL WITH command create a temporary stored procedure with limited scope. They differ in the following ways:
A CALL WITH statement can appear anywhere that a SQL statement can, including within a stored procedure, but a nested stored procedure declaration must be in a Snowflake Scripting block.
A CALL WITH stored procedure only exists in the scope of its statement, but a nested stored procedure exists in the scope of its Snowflake Scripting block.
Benefits of nested stored procedures¶
Nested stored procedures provide the following benefits:
They can enhance and simplify security by encapsulating logic inside an anonymous block or parent stored procedure, which prevents access to it from outside the block or parent.
They keep code modular by splitting it logically into smaller chunks, which can make it easier to maintain and debug.
They improve maintainability by reducing the need for global variables or additional arguments, because a nested stored procedure can directly access the local variables of its block.
Usage notes for calling nested stored procedures¶
The following usage notes apply to calling a nested stored procedure:
To pass arguments to a nested stored procedure, a block can use constant values, Snowflake Scripting variables, bind variables, SQL (session) variables, and calls to user-defined functions.
When there is a mismatch between the data type of the value being passed in and the data type of an argument, Snowflake performs supported coercions automatically. For information about which coercions Snowflake can perform automatically, see Data type conversion.
Usage notes for variables in a nested stored procedure¶
The following usage notes apply to variables in a nested stored procedure:
A nested stored procedure can reference variables from its block that were declared before the nested stored procedure declaration in the DECLARE section of its block. It can’t reference variables declared after it in the DECLARE section.
A nested stored procedure can’t access variables declared in a LET statement in the BEGIN … END section of a block.
The value of a referenced variable reflects its value at the time when the nested stored procedure is called.
A nested stored procedure can modify a referenced variable value, and the modified value persists in the block and across multiple invocations of the same nested procedure in a single execution of its anonymous block or in a single call to its parent stored procedure.
The value of a variable that was declared before a nested stored procedure call can be passed as an argument to the nested stored procedure. The variable value can be passed as an argument in a call even if the variable was declared after the nested stored procedure declaration or in a LET statement.
For example, the following stored procedure declares several variables:
CREATE OR REPLACE PROCEDURE outer_sp ()
RETURNS NUMBER
LANGUAGE SQL
AS
$$
DECLARE
var_before_nested_proc NUMBER DEFAULT 1;
test_nested_variables PROCEDURE(arg1 NUMBER)
-- <nested_sp_logic>
var_after_nested_proc NUMBER DEFAULT 2;
BEGIN
LET var_let_before_call NUMBER DEFAULT 3;
LET result := CALL nested_proc(:<var_name>);
LET var_let_after_call NUMBER DEFAULT 3;
RETURN result;
END;
$$;
In this example, only var_before_nested_proc
can be referenced in nested_sp_logic
.
In the nested stored procedure call, the value of any of the following variables can be passed to the nested stored
procedure as an argument in var_name
:
var_before_nested_proc
var_after_nested_proc
var_let_before_call
The value of var_let_after_call
can’t be passed to the nested stored procedure as an argument.
Limitations for nested stored procedures¶
The following limitations apply to defining nested stored procedures:
They can’t be defined inside other nested stored procedures or inside control structures, such as FOR or WHILE loops.
Each nested stored procedure must have a unique name in its block. That is, nested stored procedures can’t be overloaded.
They don’t support output (OUT) arguments.
They don’t support optional arguments with default values.
The following limitations apply to calling nested stored procedures:
They can’t be called in an EXECUTE IMMEDIATE statement.
They can’t be called in asynchronous child jobs.
They don’t support named input arguments (
arg_name => arg
). Arguments must be specified by position. For more information, see CALL.
Examples of nested stored procedures¶
The following examples use nested stored procedures:
Define a nested stored procedure that returns a scalar value
Define a nested stored procedure that calls another nested stored procedure
Define a nested stored procedure that returns tabular data¶
The following example defines a nested stored procedure that returns a tabular data. The example creates a
parent stored procedure called nested_procedure_example_table
with a nested stored procedure
called nested_return_table
. The code includes the following logic:
Declares a variable called
res
of type RESULTSET.Includes the following logic in the nested stored procedure:
Declares a variable called
res2
.Inserts values into a table called
nested_table
.Sets the
res2
variable to the results of a SELECT on the table.Returns the tabular data in the result set.
Creates the table
nested_table
in the parent stored procedure.Calls the nested stored procedure
nested_return_table
and sets theres
variable to the results of the call to the nested stored procedure.Returns the tabular results in the
res
variable.
CREATE OR REPLACE PROCEDURE nested_procedure_example_table()
RETURNS TABLE()
LANGUAGE SQL
AS
$$
DECLARE
res RESULTSET;
nested_return_table PROCEDURE()
RETURNS TABLE()
AS
DECLARE
res2 RESULTSET;
BEGIN
INSERT INTO nested_table VALUES(1);
INSERT INTO nested_table VALUES(2);
res2 := (SELECT * FROM nested_table);
RETURN TABLE(res2);
END;
BEGIN
CREATE OR REPLACE TABLE nested_table(col1 INT);
res := (CALL nested_return_table());
RETURN TABLE(res);
END;
$$;
Call the stored procedure:
CALL nested_procedure_example_table();
+------+
| COL1 |
|------|
| 1 |
| 2 |
+------+
Define a nested stored procedure that returns a scalar value¶
The following example defines a nested stored procedure that returns a scalar value. The example creates a
parent stored procedure called nested_procedure_example_scalar
with a nested stored procedure
called simple_counter
. The code includes the following logic:
Declares a variable called
counter
of type NUMBER, and sets the value of this variable to0
.Specifies that the nested stored procedure adds
1
to the current value of thecounter
variable.Calls the nested stored procedure three times in the parent stored procedure. The value of the
counter
variable is carried over between invocations of the nested stored procedure.Returns the value of the
counter
variable, which is3
.
CREATE OR REPLACE PROCEDURE nested_procedure_example_scalar()
RETURNS VARCHAR
LANGUAGE SQL
AS
$$
DECLARE
counter NUMBER := 0;
simple_counter PROCEDURE()
RETURNS VARCHAR
AS
BEGIN
counter := counter + 1;
RETURN counter;
END;
BEGIN
CALL simple_counter();
CALL simple_counter();
CALL simple_counter();
RETURN counter;
END;
$$;
Call the stored procedure:
CALL nested_procedure_example_scalar();
+---------------------------------+
| NESTED_PROCEDURE_EXAMPLE_SCALAR |
|---------------------------------|
| 3 |
+---------------------------------+
Define a nested stored procedure in an anonymous block¶
The following example is the same as the example in Define a nested stored procedure that returns a scalar value, except that it defines a nested stored procedure in an anonymous block instead of a stored procedure:
EXECUTE IMMEDIATE $$
DECLARE
counter NUMBER := 0;
simple_counter PROCEDURE()
RETURNS VARCHAR
AS
BEGIN
counter := counter + 1;
RETURN counter;
END;
BEGIN
CALL simple_counter();
CALL simple_counter();
CALL simple_counter();
RETURN counter;
END;
$$;
+-----------------+
| anonymous block |
|-----------------|
| 3 |
+-----------------+
Define a nested stored procedure that is passed arguments¶
The following example defines a nested stored procedure that is passed arguments. In the example, the nested stored procedure inserts values into the following table:
CREATE OR REPLACE TABLE log_nested_values(col1 INT, col2 INT);
The example creates a parent stored procedure called nested_procedure_example_arguments
with a nested stored procedure
called log_and_multiply_numbers
. The nested stored procedure takes two arguments of type NUMBER. The code includes the
following logic:
Declares variables
a
,b
, andx
of type NUMBER.Includes a nested stored procedure that performs the following actions:
Inserts the two number values passed to it by the parent stored procedure into the
log_nested_values
table using bind variables.Sets the value of variable
x
to the result of multiplying the two argument values.Returns the value of
x
to the parent stored procedure.
Sets the value of variable
a
to5
and variableb
to10
.Calls the nested stored procedure.
Returns the value of the
x
variable, which was set in the nested stored procedure.
CREATE OR REPLACE PROCEDURE nested_procedure_example_arguments()
RETURNS NUMBER
LANGUAGE SQL
AS
$$
DECLARE
a NUMBER;
b NUMBER;
x NUMBER;
log_and_multiply_numbers PROCEDURE(num1 NUMBER, num2 NUMBER)
RETURNS NUMBER
AS
BEGIN
INSERT INTO log_nested_values VALUES(:num1, :num2);
x := :num1 * :num2;
RETURN x;
END;
BEGIN
a := 5;
b := 10;
CALL log_and_multiply_numbers(:a, :b);
RETURN x;
END;
$$;
Call the stored procedure:
CALL nested_procedure_example_arguments();
+------------------------------------+
| NESTED_PROCEDURE_EXAMPLE_ARGUMENTS |
|------------------------------------|
| 50 |
+------------------------------------+
Query the log_nested_values
table to confirm that the nested stored procedure inserted the
values passed to it:
SELECT * FROM log_nested_values;
+------+------+
| COL1 | COL2 |
|------+------|
| 5 | 10 |
+------+------+
Define a nested stored procedure that calls another nested stored procedure¶
The following example defines a nested stored procedure that calls another nested stored procedure. The example creates a
parent stored procedure called nested_procedure_example_call_from_nested
with two nested stored procedures
called counter_nested_proc
and call_counter_nested_proc
. The code includes the following logic:
Declares a variable called
counter
of type NUMBER, and sets the value of this variable to0
.Includes the nested stored procedure
counter_nested_proc
that adds10
to the value ofcounter
.Includes the nested stored procedure
call_counter_nested_proc
that adds15
to the value ofcounter
and also callscounter_nested_proc
(which adds another10
to the value ofcounter
).Calls both nested stored procedures in the parent stored procedure.
Returns the value of the
counter
variable, which is35
.
CREATE OR REPLACE PROCEDURE nested_procedure_example_call_from_nested()
RETURNS NUMBER
LANGUAGE SQL
AS
$$
DECLARE
counter NUMBER := 0;
counter_nested_proc PROCEDURE()
RETURNS NUMBER
AS
DECLARE
var1 NUMBER := 10;
BEGIN
counter := counter + var1;
END;
call_counter_nested_proc PROCEDURE()
RETURNS NUMBER
AS
DECLARE
var2 NUMBER := 15;
BEGIN
counter := counter + var2;
CALL counter_nested_proc();
END;
BEGIN
counter := 0;
CALL counter_nested_proc();
CALL call_counter_nested_proc();
RETURN counter;
END;
$$;
Call the stored procedure:
CALL nested_procedure_example_call_from_nested();
+-------------------------------------------+
| NESTED_PROCEDURE_EXAMPLE_CALL_FROM_NESTED |
|-------------------------------------------|
| 35 |
+-------------------------------------------+
Using and setting SQL variables in a stored procedure¶
By default, Snowflake Scripting stored procedures run with owner’s rights. When a stored procedure runs with owner’s rights, it can’t access SQL (or session) variables.
However, a caller’s rights stored procedure can read the caller’s session variables and use
them in the logic of the stored procedure. For example, a caller’s rights stored procedure
can use the value in a SQL variable in a query. To create a stored procedure that runs with
caller’s rights, specify the EXECUTE AS CALLER
parameter in the
CREATE PROCEDURE statement.
These examples illustrate this key difference between caller’s rights and owner’s rights stored procedures. They attempt to use SQL variables in two ways:
Set a SQL variable before calling the stored procedure, then use the SQL variable inside the stored procedure.
Set a SQL variable inside the stored procedure, then use the SQL variable after returning from the stored procedure.
Both using the SQL variable and setting the SQL variable work correctly in a caller’s rights stored procedure. Both fail when using an owner’s rights stored procedure, even if the caller is the owner.
For more information about owner’s rights and caller’s rights, see Understanding caller’s rights and owner’s rights stored procedures.
Using a SQL variable in a stored procedure¶
The following example uses a SQL variable in a stored procedure.
First, set a SQL variable in a session:
SET example_use_variable = 2;
Create a simple stored procedure that runs with caller’s rights and uses this SQL variable:
CREATE OR REPLACE PROCEDURE use_sql_variable_proc()
RETURNS NUMBER
LANGUAGE SQL
EXECUTE AS CALLER
AS
DECLARE
sess_var_x_2 NUMBER;
BEGIN
sess_var_x_2 := 2 * $example_use_variable;
RETURN sess_var_x_2;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE use_sql_variable_proc()
RETURNS NUMBER
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
DECLARE
sess_var_x_2 NUMBER;
BEGIN
sess_var_x_2 := 2 * $example_use_variable;
RETURN sess_var_x_2;
END;
$$
;
Call the stored procedure:
CALL use_sql_variable_proc();
+-----------------------+
| USE_SQL_VARIABLE_PROC |
|-----------------------|
| 4 |
+-----------------------+
Set the SQL variable to a different value:
SET example_use_variable = 9;
Call the procedure again to see that the returned value has changed:
CALL use_sql_variable_proc();
+-----------------------+
| USE_SQL_VARIABLE_PROC |
|-----------------------|
| 18 |
+-----------------------+
Setting a SQL variable in a stored procedure¶
You can set a SQL variable in a stored procedure that’s running with caller’s rights. For more information, including guidelines for using SQL variables in stored procedures, see Caller’s rights stored procedures.
Note
Although you can set a SQL variable inside a stored procedure and leave it set after the end of the procedure, Snowflake does not recommend doing this.
The following example sets a SQL variable in a stored procedure.
First, set a SQL variable in a session:
SET example_set_variable = 55;
Confirm the value of the SQL variable:
SHOW VARIABLES LIKE 'example_set_variable';
+----------------+-------------------------------+-------------------------------+----------------------+-------+-------+---------+
| session_id | created_on | updated_on | name | value | type | comment |
|----------------+-------------------------------+-------------------------------+----------------------+-------+-------+---------|
| 10363782631910 | 2024-11-27 08:18:32.007 -0800 | 2024-11-27 08:20:17.255 -0800 | EXAMPLE_SET_VARIABLE | 55 | fixed | |
+----------------+-------------------------------+-------------------------------+----------------------+-------+-------+---------+
For example, the following stored procedure sets the SQL variable example_set_variable
to a new value and returns the new value:
CREATE OR REPLACE PROCEDURE set_sql_variable_proc()
RETURNS NUMBER
LANGUAGE SQL
EXECUTE AS CALLER
AS
BEGIN
SET example_set_variable = $example_set_variable - 3;
RETURN $example_set_variable;
END;
Note: If you use Snowflake CLI, SnowSQL, the Classic Console, or the
execute_stream
or execute_string
method in Python Connector
code, use this example instead (see Using Snowflake Scripting in Snowflake CLI, SnowSQL, the Classic Console, and Python Connector):
CREATE OR REPLACE PROCEDURE set_sql_variable_proc()
RETURNS NUMBER
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
BEGIN
SET example_set_variable = $example_set_variable - 3;
RETURN $example_set_variable;
END;
$$
;
Call the stored procedure:
CALL set_sql_variable_proc();
+-----------------------+
| SET_SQL_VARIABLE_PROC |
|-----------------------|
| 52 |
+-----------------------+
Confirm the new value of the SQL variable:
SHOW VARIABLES LIKE 'example_set_variable';
+----------------+-------------------------------+-------------------------------+----------------------+-------+-------+---------+
| session_id | created_on | updated_on | name | value | type | comment |
|----------------+-------------------------------+-------------------------------+----------------------+-------+-------+---------|
| 10363782631910 | 2024-11-27 08:18:32.007 -0800 | 2024-11-27 08:24:04.027 -0800 | EXAMPLE_SET_VARIABLE | 52 | fixed | |
+----------------+-------------------------------+-------------------------------+----------------------+-------+-------+---------+