Writing Stored Procedures in Snowpark (Java)

With this preview feature, you can write a stored procedure in Java. You can use the Snowpark library within your stored procedure to perform queries, updates, and other work on tables in Snowflake.

This topic explains how to write a stored procedure in Java.

In this Topic:

Introduction

With Snowpark Stored Procedures, you can build and run your data pipeline within Snowflake, using a Snowflake warehouse as the compute framework. For the code for your data pipeline, you use the Snowpark API for Java to write stored procedures. To schedule the execution of these stored procedures, you use tasks.

In this preview, Snowpark Stored Procedures have the following limitations:

  • Concurrency is not supported in stored procedures. For example, from within your stored procedure, you cannot submit queries from multiple threads.

  • If you are executing your stored procedure from a task, you must specify a warehouse when creating the task. (You cannot use Snowflake-managed compute resources to run the task.)

  • You cannot use some of the Snowpark APIs in your stored procedure. See Accessing Data in Snowflake from Your Stored Procedure for the list of unsupported APIs.

The next sections provide more information about Snowpark Stored Procedures for Java.

Prerequisites

You must use version 1.3.0 or a more recent version of the Snowpark library.

If you are writing a pre-compiled stored procedure, you must compile your classes to run in Java version 11.x.

Setting Up Your Development Environment for Snowpark

Next, set up your development environment to use the Snowpark library. See Setting Up Your Development Environment for Snowpark Java.

Choosing to Create an In-Line or Pre-Compiled Stored Procedure

As is the case with Java UDFs, you can either create an in-line stored procedure or a pre-compiled stored procedure.

  • In an in-line stored procedure, you write your Java code in the AS clause of the CREATE PROCEDURE statement. For example:

    CREATE OR REPLACE PROCEDURE MYPROC(fromTable STRING, toTable STRING, count INT)
      RETURNS STRING
      LANGUAGE JAVA
      RUNTIME_VERSION = '11'
      PACKAGES = ('com.snowflake:snowpark:latest')
      HANDLER = 'MyJavaClass.run'
      AS
      $$
        import com.snowflake.snowpark_java.*;
    
        public class MyJavaClass {
          public String run(Session session, String fromTable, String toTable, int count) {
            session.table(fromTable).limit(count).write().saveAsTable(toTable);
            return "SUCCESS";
          }
        }
      $$;
    

    Note

    For faster execution on repeated calls, you can set TARGET_PATH to the location of a JAR file in which Snowflake should save the compiled classes. See Creating the Stored Procedure.

  • In a pre-compiled stored procedure, you write your Java code in a .java source file.

    For example:

    import com.snowflake.snowpark_java.*;
    
    public class MyJavaClass {
      public String run(Session session, String fromTable, String toTable, int count) {
        session.table(fromTable).limit(count).write().saveAsTable(toTable);
        return "SUCCESS";
      }
    }
    

    You then compile your code, package the classes in a JAR file, upload the JAR file to a stage, and execute the CREATE PROCEDURE command, pointing to the JAR file on the stage. For example:

    CREATE OR REPLACE PROCEDURE MYPROC(value INT, fromTable STRING, toTable STRING, count INT)
      RETURNS INT
      LANGUAGE JAVA
      RUNTIME_VERSION = '11'
      PACKAGES = ('com.snowflake:snowpark:latest')
      IMPORTS = ('@mystage/MyCompiledJavaCode.jar')
      HANDLER = 'MyJavaClass.run';
    

Writing the Java Code for the Stored Procedure

For the code for your stored procedure, you must write a Java method. The following sections provide guidelines for writing your code:

Planning to Write Your Stored Procedure

The Java code for your stored procedure has the same constraints as the code for a Java UDF. When planning to write your stored procedures, you should take those constraints into consideration.

Limit the Amount of Memory Consumed

As is the case with Java UDFs, Snowflake places limits on a method in terms of the amount of memory needed.

In your method, you should avoid consuming too much memory.

Write Thread-Safe Code

Similarly, as is the case with Java UDFs, you should make sure that your method is thread safe.

Understand the Security Restrictions

Your method runs within a restricted engine, so you should follow the same rules as documented for Java UDFs.

Decide on Using Owner’s Rights or Caller’s Rights

In addition, when planning to write your stored procedure, consider whether you want the stored procedure to run with caller’s rights or owner’s rights.

Writing the Class

The method that you define should be part of a class.

When writing the class, note the following:

  • The class and method must not be protected or private.

  • If the method is not static and you want to define a constructor, define a zero-argument constructor for the class. Snowflake invokes this zero-argument constructor at initialization time to create an instance of your class.

  • You can define different methods for different stored procedures in the same class.

Writing the Method

When writing the method for the stored procedure, note the following:

  • Specify the Snowpark Session object as the first argument of your method.

    When you call your stored procedure, Snowflake automatically creates a Session object and passes it to your stored procedure. (You cannot create the Session object yourself.)

  • For the rest of the arguments and for the return value, use the Java types that correspond to Snowflake data types.

  • Your method must return a value. For stored procedures in Java, a return value is required.

Accessing Data in Snowflake from Your Stored Procedure

To access data in Snowflake, use the Snowpark library APIs.

When handling a call to your Java stored procedure, Snowflake creates a Snowpark Session object and passes the object to the method for your stored procedure.

As is the case with stored procedures in other languages, the context for the session (e.g. the privileges, current database and schema, etc.) is determined by whether the stored procedure runs with caller’s rights or owner’s rights. For details, see Session State.

You can use this Session object to call APIs in the Snowpark library. For example, you can create a DataFrame for a table or execute an SQL statement.

See the Snowpark Developer Guide for Java for more information.

Note

You cannot use the following Snowpark APIs in a stored procedure:

The following is an example of a Java method that copies a specified number of rows from one table to another table. The method takes the following arguments:

  • A Snowpark Session object

  • The name of the table to copy the rows from

  • The name of the table to save the rows to

  • The number of rows to copy

The method in this example returns a string.

import com.snowflake.snowpark_java.*;

public class MyClass
{
  public String myMethod(Session session, String fromTable, String toTable, int count)
  {
    session.table(fromTable).limit(count).write().saveAsTable(toTable);
    return "Success";
  }
}

Accessing Other Classes and Resource Files

If your code depends on classes defined outside of the stored procedure (e.g. classes in a separate JAR file) or resource files, you must upload those files to a stage so that the files will be available when the stored procedure executes.

Later, when executing the CREATE PROCEDURE statement, use the IMPORTS clause to point to these files.

Preparing a Pre-compiled Stored Procedure

If you plan to create a pre-compiled stored procedure (rather than an in-line stored procedure), you must compile and package your classes in a JAR file, and you must upload the JAR file to a stage.

Compiling and Packaging Your Java Code

To make it easier to set up your stored procedure, build a JAR file that contains all of the dependencies needed for your stored procedure. Later, you’ll need to upload the JAR file to a stage and point to the JAR file from your CREATE PROCEDURE statement. This process is simpler if you have fewer JAR files to upload and point to.

The next sections provide some tips on creating a JAR file that contains all of the dependencies:

Using Maven to Build a JAR File With Dependencies

If you are using Maven to build and package your code, you can use the Maven Assembly Plugin to create a JAR file that contains all of the dependencies.

  1. In the directory for your project (e.g. hello-snowpark/), create a subdirectory named assembly/.

  2. In that directory, create an assembly descriptor file that specifies that you want to include dependencies in your JAR file.

    For an example, see jar-with-dependencies.

  3. In the assembly descriptor, add a <dependencySet> element that excludes the Snowpark library from your JAR file.

    For example:

    <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
      <id>jar-with-dependencies</id>
      <formats>
         <format>jar</format>
      </formats>
      <includeBaseDirectory>false</includeBaseDirectory>
      <dependencySets>
        <dependencySet>
          <outputDirectory>/</outputDirectory>
          <useProjectArtifact>false</useProjectArtifact>
          <unpack>true</unpack>
          <scope>provided</scope>
          <excludes>
            <exclude>com.snowflake:snowpark</exclude>
          </excludes>
        </dependencySet>
      </dependencySets>
    </assembly>
    

    For information about the elements in an assembly descriptor, see Assembly Descriptor Format.

  4. In your pom.xml file, under the <project> » <build> » <plugins>, add a <plugin> element for the Maven Assembly Plugin.

    In addition, under <configuration> » <descriptors>, add a <descriptor> that points to the assembly descriptor file that you created in the previous steps.

    For example:

    <project>
      [...]
      <build>
        [...]
        <plugins>
          <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
              <descriptors>
                <descriptor>src/assembly/jar-with-dependencies.xml</descriptor>
              </descriptors>
            </configuration>
            [...]
          </plugin>
          [...]
        </plugins>
        [...]
      </build>
      [...]
    </project>
    

Using Other Tools to Build a JAR File With Dependencies

If you are not using Maven, see the documentation for your build tool for instructions on building a JAR file with all of the dependencies.

For example, if you are using an IntelliJ IDEA project, see the instructions on setting up an artifact configuration.

Uploading Files to a Stage

Next, upload the files required for your stored procedure to a stage:

  1. Choose a stage for your files.

    You can use an external or named internal stage. If you plan to use the PUT command to upload the files, use a named internal stage. (The PUT command does not support uploading files to external stages.)

    You can use an existing stage, or you can create a new stage by executing CREATE STAGE. For example, the following command creates a new internal stage named mystage:

    CREATE STAGE mystage;
    

    Note

    The owner of the stored procedure must have the READ privilege to the stage.

  2. Use the PUT command to upload the following files to that stage:

    • The JAR file containing your compiled Java code.

    • Any other files that your stored procedure depends on.

    For example:

    PUT file:///Users/MyUserName/myjar.jar
            @mystage
            AUTO_COMPRESS = FALSE
            OVERWRITE = TRUE
            ;
    

    Note

    If you omit AUTO_COMPRESS = FALSE, the PUT command automatically compresses the file. The name of the compressed file on the stage will be myjar.jar.gz. Later, when you execute the CREATE PROCEDURE command, you will need to specify the filename with this .gz extension in the IMPORTS clause.

Note that if you delete or rename the JAR file containing your compiled Java code, you can no longer call the stored procedure.

If you need to update your JAR file, then:

  • Update it while no calls to the stored procedure can be made.

  • Add the clause OVERWRITE=TRUE to the PUT command.

Creating the Stored Procedure

Next, execute the CREATE PROCEDURE statement to create a stored procedure for your method. Set the parameters listed in the table below.

Parameter

Description

[ arg_name arg_data_type [, ... ] ]

  • Omit the argument for the Snowpark Session object. As mentioned earlier, this is not a formal parameter that you specify in CREATE PROCEDURE or CALL. When you call your stored procedure, Snowflake creates a Session object and passes it to your stored procedure.

  • For the data types of the rest of the arguments, use the Snowflake data types that correspond to the Java types of the arguments in your method.

RETURNS result_data_type

Specify RETURNS with the Snowflake data type of your return value.

LANGUAGE JAVA

You must specify this to indicate that your stored procedure code is written in Java.

RUNTIME_VERSION = '11'

You must specify this to indicate that your stored procedure uses Java 11.

PACKAGES = ( 'package_name' )

In this list, include the package for the version of the Snowpark library that you want to use.

Specify the fully qualified package name for the Snowpark library in the following format:

com.snowflake:snowpark:<version>

For example:

  • To use the latest version of Snowpark, use:

    PACKAGES = ('com.snowflake:snowpark:latest')
    
  • To use the 1.4.0 version of Snowpark, use:

    PACKAGES = ('com.snowflake:snowpark:1.4.0')

Note

When creating a new stored procedure, specify the version 1.3.0 or later.

For the list of supported packages and versions, query the INFORMATION_SCHEMA.PACKAGES view for rows with LANGUAGE = 'java'. For example:

select * from information_schema.packages where language = 'java';

IMPORTS = ( 'file' [, 'file' ... ] )

If your stored procedure depends on any JAR files that you uploaded to a stage location, include those files in this list.

If you are writing an in-line stored procedure, you can omit this clause, unless your code depends on classes defined outside the stored procedure or resource files.

If you are writing a pre-compiled stored procedure, you must also include the JAR file containing the definition of the stored procedure.

HANDLER = 'method_name'

Set this to the fully qualified name of your Java method.

TARGET_PATH = 'jar_file'

If you are writing an inline stored procedure and you do not want Snowflake to recompile your code on every call, you can set this to the JAR file that Snowflake should create for your compiled code.

This must be a path on a stage for which you have WRITE privileges.

EXECUTE AS CALLER

If you planned to set up the stored procedure to use caller’s rights, add this parameter.

Otherwise, if you want to use owner’s rights, omit this parameter.

The following examples create stored procedures in Java.

Example 1: In-line stored procedure:

CREATE OR REPLACE PROCEDURE myProc(fromTable STRING, toTable STRING, count INT)
RETURNS STRING
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES = ('com.snowflake:snowpark:latest')
HANDLER = 'MyClass.myMethod'
AS
$$
  import com.snowflake.snowpark_java.*;

  public class MyClass
  {
    public String myMethod(Session session, String fromTable, String toTable, int count)
    {
      session.table(fromTable).limit(count).write().saveAsTable(toTable);
      return "Success";
    }
  }
$$;

Example 2: Pre-compiled stored procedure that uses compiled code in the JAR file myjar.jar on the internal stage mystage:

CREATE OR REPLACE PROCEDURE myProc(fromTable STRING, toTable STRING, count INT)
RETURNS STRING
LANGUAGE JAVA
RUNTIME_VERSION = '11'
PACKAGES = ('com.snowflake:snowpark:latest')
IMPORTS = ('@mystage/myjar.jar')
HANDLER = 'MyClass.myMethod';

Calling Your Stored Procedure

In order for a user to call a stored procedure, the user’s role must have the USAGE privilege for the stored procedure.

Once you have the privileges to call the stored procedure, you can use the CALL statement to call the stored procedure. For example:

CALL myProc('table_a', 'table_b', 5);
Back to top