Important

New database features are no longer connected to engine versions; features are now enabled independently during scheduled update windows, and “major” and “minor” releases no longer exist in Helios. Please visit the release notes to view the latest features available in your database clusters.

Python UDFs

A Python User-Defined Function (UDF) is an external function that allows you to execute Python code outside of the SingleStore engine's process. It enables you to extend SingleStore with custom Python logic in a SingleStore Notebook. Python UDFs are especially useful when you need to integrate with AI applications, machine learning (ML) models and perform vector operations with libraries like NumPy, Pandas, Polars, or call external APIs.

Prerequisites

To enable Python UDFs in the SingleStore deployment, ensure the following:

  • SingleStore Version: SingleStore version 8.9 or later.

  • Environment: The SingleStore deployment must run in an AWS EKS IRSA-supported environment.

  • Cluster: Python UDFs require a cluster group that supports Python UDFs. The Cloud Portal validates support for the selected cluster group while publishing Python UDF.

After ensuring, contact SingleStore Support to enable this feature for your organization.

Publish a Python UDF

Create a Python UDF

Python UDFs can be created in Shared notebook only.

To create a new Python UDF, perform the following steps:

  1. In the left navigation, select Editor > Shared.

  2. Create or open a shared notebook.

  3. Select Publish (on the top right).

New Python UDF

After selecting Publish, a new dialog box appears.

Publish Settings

Publish as

Select Python UDF.

Name

Enter a name of the Python UDF.

Description

Enter the Python UDF description.

Notebook

Select a shared notebook to publish as a Python UDF. The shared notebook is pre-selected when the Python UDF is published through notebooks.

Deployment

Select the SingleStore deployment (cluster) the notebook connects to.

Selecting a cluster allows connecting to the SingleStore databases referenced in the notebook natively.

Runtime

Select an available runtime. The available options depend on your cluster configuration.

Note

This field is in preview.

Region

Select a region.

Idle Timeout

Select an idle timeout.

Select Next.

Select Publish to publish the notebook as Python UDF. Once the Python UDF is published, call the function using SQL Editor.

Note

A notebook can have one active Python UDF publication at a time. Function names registered in the selected database must not conflict with functions from another Python UDF. To modify an existing Python UDF, use Update.

Example Notebook

The following notebook shows how to publish your first Python UDF:

run-your-first-python-udf.ipynb

Manage an Existing Python UDF

To view an existing Python UDF, select Python UDFs in the left navigation. Existing Python UDFs can be managed by performing the following actions:

  • View

  • Update

  • Delete

View an Existing Python UDF

To view an existing Python UDF, select the Python UDF from the Name column. The following actions can be performed for a Python UDF from this page:

  • View Live Logs

  • Update

  • Delete

View Live Logs

To view live logs of the selected Python UDF, select View Live Logs from the ellipsis on the right side. A new window appears, where the Timestamp and the message in the Body column can be viewed. View the Log JSON by selecting the eye icon.

Update an Existing Python UDF

To update an existing Python UDF, select the ellipsis in the Actions column of the Python UDF, and select Update.

Delete an Existing Python UDF

To delete an existing Python UDF, select the ellipsis in the Actions column of the Python UDF, and select Delete.

Status of Python UDF

Status

Description

Initializing

The Python UDF is being created, updated, or restarted.

Active

The Python UDF is available and accepting requests.

Idle

The Python UDF is not currently running and may cold start on the next request.

Failed

The notebook code or UDF definition failed validation or execution. Use the snapshot and logs to debug.

Error

A service or infrastructure issue prevented the Python UDF from becoming available. Try again or contact Support if the issue persists.

Deleting

The Python UDF is being deleted.

Deleted

The Python UDF has been deleted.

Troubleshoot Python UDFs

SingleStore automatically saves a snapshot of the notebook for each failed execution. Open Python UDFs, select the Python UDF, then use the detail page to view logs or open the notebook snapshot for debugging.

Error

Solution

No functions found

Ensure the notebook defines at least one valid Python UDF function decorated with @udf.

Too many functions found

Reduce the number of Python UDF functions in the notebook to 10 or fewer and try again.

UDF server unreachable

Ensure the notebook starts the UDF app with connection_info = await apps.run_udf_app().

Duplicate UDF Function Name

Rename conflicting functions or remove the existing conflicting Python UDF.

Database Dropped

Update the Python UDF and select an existing database.

Old Database Detached

Reattach the previous database if stale functions need to be removed, or contact Support.

Cluster Deleted

Update the Python UDF and select a different deployment.

Cluster Suspended

Resume the cluster or create a new Python UDF with a different deployment.

Notebook Deleted/Not Present

Create a new Python UDF with a different notebook.

Internal Errors/Misc

Contact SingleStore Support or use the chat feature in the Cloud Portal.

Defining Python UDFs

Each Python UDF must meet the following requirements:

  1. The function's parameters and return types must be annotated.

  2. The function must be wrapped with the @udf decorator, which is located in singlestoredb.functions.

The @udf decorator is a critical component, as it automatically analyzes the type annotations to map Python data types to SingleStore data types. The mapping is subsequently used to generate the necessary CREATE EXTERNAL FUNCTION statement in the SingleStore database, ensuring a reliable connection between the Python code and the SQL queries. Refer to Equivalent Data Types for related information.

There are two main types of Python UDFs, defined by the type annotations:

  • Scalar

  • Vectorized

Scalar Python UDFs

Scalar Python UDFs are defined with standard Python type annotations, such as int, float, or str. When called from the database, the Python UDF server receives a batch of rows, but the Python UDF itself is invoked once for each individual row of data. This is useful in complex logic with individual records.

The following example demonstrates a scalar Python UDF:

from singlestoredb.functions import udf
import singlestoredb.apps as apps
@udf
async def multiply(x: float, y: float) -> float:
return x * y
# Start Python UDF server
connection_info = await apps.run_udf_app()
print("UDF server running. Connection info:", connection_info)

This creates the following external function:

CREATE OR REPLACE EXTERNAL FUNCTION `multiply`(
`x` DOUBLE NOT NULL,
`y` DOUBLE NOT NULL
)
RETURNS DOUBLE NOT NULL
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;

Note

The DEFINER value and AS MANAGED SERVICE URL are generated automatically when you publish the function. The DEFINER identifies the internal service identity that owns the published function. The URL points to the region-local Python UDF endpoint for the function. These values vary by function, publisher, and region.

Use async def for improved cancellation handling.

Invoke the scalar Python UDF using the following command:

SELECT multiply(5.0, 10.0) AS result;
+-----------------------+
| result                |
+-----------------------+
| 50.0                  |
+-----------------------+

Vectorized Python UDFs

Vectorized Python UDFs are defined with vector type annotations, such as numpy.ndarray, pandas.Series, polars.Series, or pyarrow.Array. The Python UDF is called only once for each batch of rows received from the SingleStore database. The entire batch is converted into vectorized inputs, where each column of data corresponds to a single vector object passed as a function parameter. This is useful in high-performance numerical processing.

The following example demonstrates a vectorized Python UDF.

import numpy as np
import numpy.typing as npt
from singlestoredb.functions import udf
@udf
async def vec_multiply(
x: npt.NDArray[np.float64],
y: npt.NDArray[np.float64]
) -> npt.NDArray[np.float64]:
return x * y
# Start Python UDF server
import singlestoredb.apps as apps
connection_info = await apps.run_udf_app()

This creates the following external function:

CREATE OR REPLACE EXTERNAL FUNCTION `vec_multiply`(
`x` DOUBLE NOT NULL,
`y` DOUBLE NOT NULL
)
RETURNS DOUBLE NOT NULL
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;

Invoke the vectorized Python UDF using the following command:

SELECT vec_multiply(vec_col1, vec_col2)AS result;
+-------------------+
| result            |
+-------------------+
| [4.0, 10.0, 18.0] |
+-------------------+

The function processes all rows in a batch simultaneously. Each row receives its own scalar result, for example, if col1 = 2.0 and col2 = 5.0 for a given row, the result for that row is 10.0.

Scalar Python TVFs

Scalar Python TVFs are defined in the same way as scalar Python UDFs, except that a scalar TVF uses a Table annotation to indicate that the function returns a table. The function must also return the final result wrapped in a Table object.

The following example demonstrates a scalar Python TVF:

from singlestoredb.functions import udf, Table
@udf
async def number_stats(n: int) -> Table[list[int], list[int], list[float]]:
numbers = list(range(1, n + 1))
squares = [x ** 2 for x in numbers]
roots = [round(x ** 0.5, 2) for x in numbers]
return Table(numbers, squares, roots)
# Start Python UDF server
import singlestoredb.apps as apps
connection_info = await apps.run_udf_app()

This creates the following external function:

CREATE OR REPLACE EXTERNAL FUNCTION `number_stats`(
`n` BIGINT NOT NULL
)
RETURNS TABLE(
`numbers` BIGINT NOT NULL,
`squares` BIGINT NOT NULL,
`roots` DOUBLE NOT NULL
)
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;

Invoke the scalar Python TVF using the following command:

SELECT * FROM number_stats([5]);
+---------+---------+-------+
| numbers | squares | roots |
+---------+---------+-------+
| 1       | 1       | 1.00  |
| 2       | 4       | 1.41  |
| 3       | 9       | 1.73  |
| 4       | 16      | 2.00  |
| 5       | 25      | 2.24  |
+---------+---------+-------+

SingleStore automatically generates generic column names if no names are associated with the return fields (numbers, squares, and roots in this example). Explicitly name result columns using overrides or schema classes. Refer to Overriding Parameters and Return Value Types for related information.

Vectorized Python TVFs

Vectorized Python TVFs return a table of results. Unlike Python UDFs, which are invoked for each row in a query, a TVF is invoked once with a set of parameters and returns multiple rows and columns. In a vectorized Python TVF, each returned vector represents a column in the output table. The length of each vector determines the number of rows returned.

The following example demonstrates a vectorized Python TVF:

import numpy as np
import numpy.typing as npt
from singlestoredb.functions import udf, Table
@udf
async def vec_table_function(
n: npt.NDArray[np.int_],
) -> Table[npt.NDArray[np.int_], npt.NDArray[np.float64], npt.NDArray[np.str_]]:
x = np.array([10] * n[0], dtype=np.int_)
y = np.array([10.0] * n[0], dtype=np.float64)
z = np.array(['ten'] * n[0], dtype=np.str_)
# Returns a tuple of vectors (each column of the output)
return Table(x, y, z)
# Start Python UDF server
import singlestoredb.apps as apps
connection_info = await apps.run_udf_app()

This creates the following external function:

CREATE OR REPLACE EXTERNAL FUNCTION `vec_table_function`(
`n` BIGINT NOT NULL
)
RETURNS TABLE(
`x` BIGINT NOT NULL,
`y` DOUBLE NOT NULL,
`z` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
)
DEFINER = '<function_owner>'@'%'
AS MANAGED SERVICE '<managed_service_url>'
FORMAT ROWDAT_1;

Invoke the vectorized Python TVF using the following command:

SELECT * FROM vec_table_function([1]);
+----+------+-----+
| x  | y    | z   |
+----+------+-----+
| 10 | 10.0 | ten |
+----+------+-----+

SingleStore automatically generates generic column names if no names are associated with the return fields (x, y, z). Explicitly name result columns using overrides or schema classes. Refer to Overriding Parameters and Return Value Types for related information.

In this section

Last modified:

Was this article helpful?

Verification instructions

Note: You must install cosign to verify the authenticity of the SingleStore file.

Use the following steps to verify the authenticity of singlestoredb-server, singlestoredb-toolbox, singlestoredb-studio, and singlestore-client SingleStore files that have been downloaded.

You may perform the following steps on any computer that can run cosign, such as the main deployment host of the cluster.

  1. (Optional) Run the following command to view the associated signature files.

    curl undefined
  2. Download the signature file from the SingleStore release server.

    • Option 1: Click the Download Signature button next to the SingleStore file.

    • Option 2: Copy and paste the following URL into the address bar of your browser and save the signature file.

    • Option 3: Run the following command to download the signature file.

      curl -O undefined
  3. After the signature file has been downloaded, run the following command to verify the authenticity of the SingleStore file.

    echo -n undefined |
    cosign verify-blob --certificate-oidc-issuer https://oidc.eks.us-east-1.amazonaws.com/id/CCDCDBA1379A5596AB5B2E46DCA385BC \
    --certificate-identity https://kubernetes.io/namespaces/freya-production/serviceaccounts/job-worker \
    --bundle undefined \
    --new-bundle-format -
    Verified OK

Try Out This Notebook to See What’s Possible in SingleStore

Get access to other groundbreaking datasets and engage with our community for expert advice.