<a id="flink-sql-udfs"></a>

# User-defined Functions in Confluent Cloud for Apache Flink

Confluent Cloud for Apache Flink® supports user-defined functions (UDFs), which are extension points
for running custom logic that you can’t express in the system-provided
Flink SQL [queries](../reference/queries/overview.md#flink-sql-queries) or with the
[Table API](../reference/table-api.md#flink-table-api).

You can implement user-defined functions in Java or Python, and you can use
third-party libraries within a UDF. Confluent Cloud for Apache Flink supports scalar functions (UDFs),
which map scalar values to a new scalar value, and table functions (UDTFs),
which map multiple scalar values to multiple output rows (Java only).

- **Create an example UDF:** [Create a User Defined Function](../how-to-guides/create-udf.md#flink-sql-create-udf)
- **Add logging to your UDFs:** [Enable Logging in a User Defined Function](../how-to-guides/enable-udf-logging.md#flink-sql-enable-udf-logging)
- **Availability:** [UDF regional availability](#flink-sql-udfs-availability)
- **Limitations:** [UDF limitations](#flink-sql-udfs-limitations)
- **Example code:** [Flink UDF Java Examples](https://github.com/confluentinc/flink-udf-java-examples)
  and [Flink UDF Python Examples](https://github.com/confluentinc/flink-udf-python-examples)
- **Permanent and in-line UDFs:** [Permanent and in-line UDFs](#flink-sql-udfs-permanent-and-inline-udfs)
- **External connections:** [External connectivity](#flink-sql-udfs-external-connectivity)

## Artifacts

Artifacts are JAR files for Java, and ZIP files for Python, that contain
user-defined functions and all of the required dependencies. You upload
artifacts to Confluent Cloud and scope them to a specific region in a Confluent Cloud
environment. To use them with UDFs, artifacts must follow a few common
implementation principles, which the following sections describe.

To use a UDF, you must register one or more functions that reference the
artifact.

## Functions

Functions are SQL objects that reference a class in an artifact and that
you can use in any SQL statement or Table API program.

After you upload an artifact, you register a function by using the
[CREATE FUNCTION](../reference/statements/create-function.md#flink-sql-create-function) statement.

After you register a function, you can invoke it from any SQL statement or
Table API program.

The following example shows how to register a `TShirtSizingIsSmaller`
function and invoke it in a SQL statement.

```sql
-- Register the function.
CREATE FUNCTION is_smaller
  AS 'com.example.my.TShirtSizingIsSmaller'
  USING JAR 'confluent-artifact://<artifact-id>';

-- Invoke the function.
SELECT IS_SMALLER ('L', 'M');
```

To build and upload a UDF to Confluent Cloud for Apache Flink for use in Flink SQL or the Table API,
see [Create a UDF](../how-to-guides/create-udf.md#flink-sql-create-udf).

## RBAC

To upload artifacts, register functions, and invoke functions, you must have the
FlinkDeveloper role or higher. For more information, see
[Grant Role-Based Access](../operate-and-deploy/flink-rbac.md#flink-rbac).

## Shared responsibility

Confluent supports the UDF infrastructure in Confluent Cloud only. Troubleshooting
custom UDF issues for functions you build, or that others provide to you, is
your responsibility. The following provides additional details about shared
support responsibilities.

* **Customer Managed**: You are responsible for function logic. Confluent
  does not provide any support for debugging services and features within UDFs.
* **Confluent Managed**: Confluent is responsible for managing the Flink
  services and custom compute platform, and provides support for these.

<a id="flink-sql-udfs-scalar-functions"></a>

## Scalar functions

A user-defined scalar function maps zero, one, or multiple scalar values to a
new scalar value. You can use any data type listed in [Data Types](../reference/datatypes.md#flink-sql-datatypes)
as a parameter or return type of an evaluation method.

To define a scalar function, extend the `ScalarFunction` base class in
`org.apache.flink.table.functions` and implement one or more evaluation
methods named `eval(...)`.

The following code example shows how to define your own hash code function in
Java.


```java
import org.apache.flink.table.annotation.InputGroup;
import org.apache.flink.table.api.*;
import org.apache.flink.table.functions.ScalarFunction;
import static org.apache.flink.table.api.Expressions.*;

public static class HashFunction extends ScalarFunction {

  // take any data type and return INT
  public int eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o) {
    return o.hashCode();
  }
}
```

The following example shows how to call the `HashFunction` UDF in a
Flink SQL statement.

```sql
SELECT HashFunction(myField) FROM MyTable;
```





To build and upload a UDF to Confluent Cloud for Apache Flink for use in Flink SQL, see
[Create a User Defined Function](../how-to-guides/create-udf.md#flink-sql-create-udf).

<a id="flink-sql-udfs-vectorized-scalar-functions"></a>

### Vectorized scalar functions

A *vectorized* scalar function is a Python scalar UDF that processes a batch of
rows at once instead of one row at a time. Instead of receiving individual
scalar values, the function receives each argument as a `pandas.Series` and
returns a `pandas.Series` of the same length. Under the hood, Flink transfers
the batch to Python in [Apache Arrow](https://arrow.apache.org/) columnar
format, so Flink amortizes the per-row overhead of the Python interpreter
across the whole batch.

Use a vectorized function when you process high-throughput streams and you can
express your logic as element-wise operations over a batch, for example with
[NumPy](https://numpy.org/) or [pandas](https://pandas.pydata.org/). For
this kind of workload, a vectorized function can be significantly faster than
the equivalent row-by-row scalar function.

To make a Python scalar function vectorized, add `func_type="pandas"` to the
`udf` definition. No other changes are required: the function signature,
artifact packaging, and `CREATE FUNCTION` registration are the same as for any
other Python UDF. The following example computes the great-circle distance
between two coordinates over a batch by using NumPy.

```python
import numpy as np
import pandas as pd
from pyflink.table import DataTypes
from pyflink.table.udf import udf

_EARTH_RADIUS_KM = 6371.0


def _great_circle_km_vec(lat1, lon1, lat2, lon2):
    lat1_r = np.radians(lat1)
    lat2_r = np.radians(lat2)
    dlat = np.radians(lat2 - lat1)
    dlon = np.radians(lon2 - lon1)
    a = (np.sin(dlat / 2) ** 2 +
         np.cos(lat1_r) * np.cos(lat2_r) * np.sin(dlon / 2) ** 2)
    a = np.clip(a, 0.0, 1.0)
    return pd.Series(_EARTH_RADIUS_KM * 2 * np.arcsin(np.sqrt(a)))


great_circle_km_vec = udf(
    _great_circle_km_vec,
    input_types=[DataTypes.DOUBLE()] * 4,
    result_type=DataTypes.DOUBLE(),
    func_type="pandas",
)
```

When you implement a vectorized function, keep the following in mind:

- The returned `pandas.Series` must have the same length as the input batch.
- SQL `NULL` values arrive as `NaN` in the input `Series`, and Flink
  converts `NaN` values in the returned `Series` back to SQL `NULL`.
- `pandas` and `numpy` are available through the Flink Python runtime, so you
  don’t need to add them as explicit dependencies.

To build, upload, and register a Python UDF, see
[Create a User-Defined Function](../how-to-guides/create-udf.md#flink-sql-create-udf).

<a id="flink-sql-udfs-permanent-and-inline-udfs"></a>

## Permanent and in-line UDFs

Starting with Confluent Table API plugin version 2.1-8, you can simplify the
process of creating and managing UDFs.

- **Permanent UDFs**: Flink registers them automatically, and you can use
  them in any Flink SQL or Table API program. The Table API creates a
  temporary JAR file containing all transitive classes required to run the
  function, uploads it to Confluent Cloud, and registers the function using the
  previously uploaded artifact.
- **In-line UDFs**: You define and use them in the same Table API program.
  In-line functions must be serializable by class name, so use a top-level
  class or a static nested class, not an anonymous inner class.

The following example shows how to create and call a permanent UDF and an
in-line UDF.

For the full code listing, see
[Example_09_Functions.java](https://github.com/confluentinc/flink-table-api-java-examples/blob/master/src/main/java/io/confluent/flink/examples/table/Example_09_Functions.java)
in the
[flink-table-api-java-examples](https://github.com/confluentinc/flink-table-api-java-examples/)
repository.

### Implement a permanent and in-line UDF

```java
package io.confluent.flink.examples.table;

import io.confluent.flink.plugin.ConfluentSettings;

import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;
import org.apache.flink.table.functions.ScalarFunction;
import org.apache.flink.table.functions.TableFunction;

import java.util.List;

import static org.apache.flink.table.api.Expressions.$;
import static org.apache.flink.table.api.Expressions.array;
import static org.apache.flink.table.api.Expressions.call;
import static org.apache.flink.table.api.Expressions.row;

/**
* A table program example showing how to use User-Defined Functions
* (UDFs) in the Flink Table API.
*
* <p>The Flink Table API simplifies the process of creating and managing UDFs.
*
* <ul>
*   <li>It helps creating a JAR file containing all required dependencies for a given UDF.
*   <li>Uploads the JAR to Confluent artifact API.
*   <li>Creates SQL functions for given artifacts.
* </ul>
*/
public class Example_09_Functions {

   // Fill this with an environment you have write access to
   static final String TARGET_CATALOG = "";

   // Fill this with a Kafka cluster you have write access to
   static final String TARGET_DATABASE = "";

   // All logic is defined in a main() method. It can run both in an IDE or CI/CD system.
   public static void main(String[] args) {
      // Setup connection properties to Confluent Cloud
      EnvironmentSettings settings = ConfluentSettings.fromResource("/cloud.properties");

      // Initialize the session context to get started
      TableEnvironment env = TableEnvironment.create(settings);

      // Set default catalog and database
      env.useCatalog(TARGET_CATALOG);
      env.useDatabase(TARGET_DATABASE);

      System.out.println("Registering a scalar function...");
      // The Table API underneath creates a temporary JAR file containing all transitive classes
      // required to run the function, uploads it to Confluent Cloud, and registers the function
      // using the previously uploaded artifact.
      env.createFunction("CustomTax", CustomTax.class, true);

      // As of now, Scalar and Table functions are supported.
      System.out.println("Registering a table function...");
      env.createFunction("Explode", Explode.class, true);

      // Once registered, the functions can be used in Table API and SQL queries.
      System.out.println("Executing registered UDFs...");
      env.fromValues(row("Apple", "USA", 2), row("Apple", "EU", 3))
               .select(
                        $("f0").as("product"),
                        $("f1").as("location"),
                        $("f2").times(call("CustomTax", $("f1"))).as("tax"))
               .execute()
               .print();

      env.fromValues(
                        row(1L, "Ann", array("Apples", "Bananas")),
                        row(2L, "Peter", array("Apples", "Pears")))
               .joinLateral(call("Explode", $("f2")).as("fruit"))
               .select($("f0").as("id"), $("f1").as("name"), $("fruit"))
               .execute()
               .print();

      // Instead of registering functions permanently, you can embed UDFs directly into queries
      // without registering them first. This will upload all the functions of the query as a
      // single artifact to Confluent Cloud. Moreover, the functions lifecycle will be bound to
      // the lifecycle of the query.
      System.out.println("Executing inline UDFs...");
      env.fromValues(row("Apple", "USA", 2), row("Apple", "EU", 3))
               .select(
                        $("f0").as("product"),
                        $("f1").as("location"),
                        $("f2").times(call(CustomTax.class, $("f1"))).as("tax"))
               .execute()
               .print();

      env.fromValues(
                        row(1L, "Ann", array("Apples", "Bananas")),
                        row(2L, "Peter", array("Apples", "Pears")))
               .joinLateral(call(Explode.class, $("f2")).as("fruit"))
               .select($("f0").as("id"), $("f1").as("name"), $("fruit"))
               .execute()
               .print();
   }

   /** A scalar function that calculates a custom tax based on the provided location. */
   public static class CustomTax extends ScalarFunction {
      public int eval(String location) {
            if (location.equals("USA")) {
               return 10;
            }
            if (location.equals("EU")) {
               return 5;
            }
            return 0;
      }
   }

   /** A table function that explodes an array of string into multiple rows. */
   public static class Explode extends TableFunction<String> {
      public void eval(List<String> arr) {
            for (String i : arr) {
               collect(i);
            }
      }
   }
}
```

<a id="flink-sql-udfs-table-functions"></a>

## Table functions

Confluent Cloud for Apache Flink also supports user-defined table functions (UDTFs), which take
multiple scalar values as input arguments and return multiple rows as output,
instead of a single value.

To create a user-defined table function, extend the `TableFunction` base
class in `org.apache.flink.table.functions` and implement one or more
of the evaluation methods, which are named `eval(...)`. Input and output data
types are inferred automatically by using reflection, including the generic
argument `T` of the class, for determining the output data type. Unlike
scalar functions, the evaluation method itself doesn’t have a return type.
Instead, a table function provides a `collect(T)` method that’s called within
every evaluation method to emit zero, one, or more records.

In the [Table API](../reference/table-api.md#flink-table-api), a table function is used with the
`.joinLateral(...)` or `.leftOuterJoinLateral(...)` operators. The
`joinLateral` operator cross-joins each row from the outer table (the table
on the left of the operator) with all rows produced by the table-valued
function (on the right side of the operator). The `leftOuterJoinLateral`
operator joins each row from the outer table with all rows produced by the
table-valued function and preserves outer rows, for which the table function
returns an empty table.

#### NOTE
User-defined table functions are distinct from the
[Table API](../reference/table-api.md#flink-table-api) but can be used in Table API code.

In SQL, use `LATERAL TABLE(<TableFunction>)` with `JOIN` or `LEFT JOIN`
with an `ON TRUE` join condition.

The following code example shows how to implement a simple string splitting
function in Java.


```java
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.FunctionHint;
import org.apache.flink.table.api.*;
import org.apache.flink.table.functions.TableFunction;
import org.apache.flink.types.Row;
import static org.apache.flink.table.api.Expressions.*;

@FunctionHint(output = @DataTypeHint("ROW<word STRING, length INT>"))
public static class SplitFunction extends TableFunction<Row> {

  public void eval(String str) {
    for (String s : str.split(" ")) {
      // use collect(...) to emit a row
      collect(Row.of(s, s.length()));
    }
  }
}
```

The following example shows how to call the `SplitFunction` UDTF in a
Flink SQL statement.

```sql
SELECT myField, word, length
FROM MyTable
LEFT JOIN LATERAL TABLE(SplitFunction(myField)) ON TRUE;
```





To build and upload a user-defined table function to Confluent Cloud for Apache Flink for use in
Flink SQL, see
[Create a User Defined Table Function](../how-to-guides/create-udf.md#flink-sql-implement-udtf-function).

## Implementation considerations

All UDFs adhere to a few common implementation principles, which are described
in the following sections.

- [Function class](#flink-sql-udfs-function-class)
- [Evaluation methods](#flink-sql-udfs-evaluation-methods)
- [Type inference](#flink-sql-udfs-type-inference)
- [Named parameters](#flink-sql-udfs-named-parameters)
- [Scalar functions](#flink-sql-udfs-scalar-functions)
- [Table functions](#flink-sql-udfs-table-functions)

The following code example shows how to implement a simple scalar function and
how to call it in Flink SQL.



For the [Table API](../reference/table-api.md#flink-table-api), you can register the function in
code and invoke it.



For SQL queries, your UDF must be registered by using the
[CREATE FUNCTION](../reference/statements/create-function.md#flink-sql-create-function) statement. For more
information, see [Create a User-defined Function](../how-to-guides/create-udf.md#flink-sql-create-udf).

```java
import org.apache.flink.table.api.*;
import org.apache.flink.table.functions.ScalarFunction;
import static org.apache.flink.table.api.Expressions.*;

// define function logic
public static class SubstringFunction extends ScalarFunction {
  public String eval(String s, Integer begin, Integer end) {
    return s.substring(begin, end);
  }
}
```

The following example shows how to call the `SubstringFunction` UDF in a
Flink SQL statement.

```sql
SELECT SubstringFunction('test string', 2, 5);
```





<a id="flink-sql-udfs-function-class"></a>

### Function class

Your implementation class must extend one of the system-provided base classes.

- Scalar functions extend the `org.apache.flink.table.functions.ScalarFunction`
  class.
- Table functions extend the `org.apache.flink.table.functions.TableFunction`
  class.

The class must be declared public, not abstract, and must be accessible
globally. Non-static inner or anonymous classes are not supported.



<a id="flink-sql-udfs-evaluation-methods"></a>

### Evaluation methods

You define the behavior of a scalar function by implementing a custom
evaluation method, named `eval`, which must be declared `public`.
You can overload evaluation methods by implementing multiple methods named
`eval`.

The evaluation method is called by code-generated operators during runtime.

Regular JVM method-calling semantics apply, so these implementation options
are available:

- You can implement overloaded methods, like `eval(Integer)` and
  `eval(LocalDateTime)`.
- You can use var-args, like `eval(Integer...)`.
- You can use object inheritance, like `eval(Object)` that takes both
  `LocalDateTime` and `Integer`.
- You can use combinations of these, like `eval(Object...)` that takes all
  kinds of arguments.

The `ScalarFunction` base class provides a set of optional methods that you
can override,  `open()`, `close()`, `isDeterministic()`, and
`supportsConstantFolding()`. You can use the `open()` method for
initialization work and the `close()` method for cleanup work.

Internally, Table API and SQL code generation works with primitive values where
possible. To reduce overhead during runtime, a user-defined scalar function
should declare parameters and result types as primitive types instead of their
boxed classes. For example, DATE/TIME is equal to `int`, and TIMESTAMP is
equal to `long`.

The following code example shows a user-defined function that has overloaded
`eval` methods.

```java
import org.apache.flink.table.functions.ScalarFunction;

// function with overloaded evaluation methods
public static class SumFunction extends ScalarFunction {

  public Integer eval(Integer a, Integer b) {
    return a + b;
  }

  public Integer eval(String a, String b) {
    return Integer.valueOf(a) + Integer.valueOf(b);
  }

  public Integer eval(Double... d) {
    double result = 0;
    for (double value : d)
      result += value;
    return (int) result;
  }
}
```

<a id="flink-sql-udfs-type-inference"></a>

### Type inference

The Table API is strongly typed, so both function parameters and return types
must be mapped to a data type.

The Flink planner needs information about expected types, precision, and scale.
Also it needs information about how internal data structures are represented
as JVM objects when calling a user-defined function.

*Type inference* is the process of validating input arguments and deriving data
types for both the parameters and the result of a function.

User-defined functions in Flink implement automatic type-inference extraction
that derives data types from the function’s class and its evaluation methods
by using reflection. If this implicit extraction approach with reflection
fails, you can help the extraction process by annotating affected parameters,
classes, or methods with `@DataTypeHint` and `@FunctionHint`.








#### Automatic type inference

Automatic type inference inspects the function’s class and evaluation methods
to derive data types for the arguments and return value of a function. The
`@DataTypeHint` and `@FunctionHint` annotations support automatic
extraction.

For a list of classes that implicitly map to a data type, see
[Data type extraction](../reference/datatypes.md#flink-sql-data-type-extraction).

<a id="flink-sql-udfs-type-inference-data-type-hints"></a>

#### Data type hints

In some situations, you might need to support automatic extraction inline
for parameters and return types of a function. In these cases you can use
data type hints and the `@DataTypeHint` annotation to define data types.

The following code example shows how to use data type hints.

```java
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.InputGroup;
import org.apache.flink.table.functions.ScalarFunction;
import org.apache.flink.types.Row;

// user-defined function that has overloaded evaluation methods.
public static class OverloadedFunction extends ScalarFunction {

  // No hint required for type inference.
  public Long eval(long a, long b) {
    return a + b;
  }

  // Define the precision and scale of a decimal.
  public @DataTypeHint("DECIMAL(12, 3)") BigDecimal eval(double a, double b) {
    return BigDecimal.valueOf(a + b);
  }

  // Define a nested data type.
  @DataTypeHint("ROW<s STRING, t TIMESTAMP_LTZ(3)>")
  public Row eval(int i) {
    return Row.of(String.valueOf(i), Instant.ofEpochSecond(i));
  }

  // Enable wildcard input and custom serialized output.
  @DataTypeHint(value = "RAW", bridgedTo = ByteBuffer.class)
  public ByteBuffer eval(@DataTypeHint(inputGroup = InputGroup.ANY) Object o) {
    return MyUtils.serializeToByteBuffer(o);
  }
}
```

#### Function hints

In some situations, you might want one evaluation method to handle multiple
different data types, or you might have overloaded evaluation methods with
a common result type to declare only once.

The `@FunctionHint` annotation provides a mapping from argument data types
to a result data type. It enables annotating entire function classes or
evaluation methods for input, accumulator, and result data types. You can
declare one or more annotations on a class or individually for each evaluation
method for overloading function signatures.

All hint parameters are optional. If a parameter is not defined, the default
reflection-based extraction is used. Hint parameters defined on a function
class are inherited by all evaluation methods.

The following code example shows how to use function hints.

```java
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.annotation.FunctionHint;
import org.apache.flink.table.functions.TableFunction;
import org.apache.flink.types.Row;

// User-defined function with overloaded evaluation methods
// but globally defined output type.
@FunctionHint(output = @DataTypeHint("ROW<s STRING, i INT>"))
public static class OverloadedFunction extends ScalarFunction<Row> {

  public void eval(int a, int b) {
    collect(Row.of("Sum", a + b));
  }

  // Overloading arguments is still possible.
  public void eval() {
    collect(Row.of("Empty args", -1));
  }
}

// Decouples the type inference from evaluation methods.
// The type inference is entirely determined by the function hints.
@FunctionHint(
  input = {@DataTypeHint("INT"), @DataTypeHint("INT")},
  output = @DataTypeHint("INT")
)
@FunctionHint(
  input = {@DataTypeHint("BIGINT"), @DataTypeHint("BIGINT")},
  output = @DataTypeHint("BIGINT")
)
@FunctionHint(
  input = {},
  output = @DataTypeHint("BOOLEAN")
)

public static class OverloadedFunction extends ScalarFunction<Object> {

  // Ensure a method exists that the JVM can call.
  public void eval(Object... o) {
    if (o.length == 0) {
      collect(false);
    }
    collect(o[0]);
  }
}
```















<a id="flink-sql-udfs-named-parameters"></a>

### Named parameters

When you call a user-defined function, you can use parameter names to specify
the values of the parameters. Named parameters enable passing both the
parameter name and value to a function. This approach avoids confusion caused
by incorrect parameter order, and it improves code readability and
maintainability. Also, named parameters can omit optional parameters, which
are filled with `null` by default. Use the `@ArgumentHint` annotation to
specify the name, type, and whether a parameter is required or not.

The following code examples demonstrate how to use `@ArgumentHint` in
different scopes.

1. Use the `@ArgumentHint` annotation on the parameters of the `eval` method
   of the function:
   ```java
   import com.sun.tracing.dtrace.ArgsAttributes;
   import org.apache.flink.table.annotation.ArgumentHint;
   import org.apache.flink.table.functions.ScalarFunction;

   public static class NamedParameterClass extends ScalarFunction {

       // Use the @ArgumentHint annotation to specify the name, type, and whether a parameter is required.
       public String eval(@ArgumentHint(name = "param1", isOptional = false, type = @DataTypeHint("STRING")) String s1,
                         @ArgumentHint(name = "param2", isOptional = true, type = @DataTypeHint("INT")) Integer s2) {
           return s1 + ", " + s2;
       }
   }
   ```
2. Use the `@ArgumentHint` annotation on the `eval` method of the function.
   ```java
   import org.apache.flink.table.annotation.ArgumentHint;
   import org.apache.flink.table.functions.ScalarFunction;

   public static class NamedParameterClass extends ScalarFunction {

     // Use the @ArgumentHint annotation to specify the name, type, and whether a parameter is required.
     @FunctionHint(
             argument = {@ArgumentHint(name = "param1", isOptional = false, type = @DataTypeHint("STRING")),
                     @ArgumentHint(name = "param2", isOptional = true, type = @DataTypeHint("INTEGER"))}
     )
     public String eval(String s1, Integer s2) {
       return s1 + ", " + s2;
     }
   }
   ```
3. Use the `@ArgumentHint` annotation on the class of the function.
   ```java
   import org.apache.flink.table.annotation.ArgumentHint;
   import org.apache.flink.table.functions.ScalarFunction;

   // Use the @ArgumentHint annotation to specify the name, type, and whether a parameter is required.
   @FunctionHint(
           argument = {@ArgumentHint(name = "param1", isOptional = false, type = @DataTypeHint("STRING")),
                   @ArgumentHint(name = "param2", isOptional = true, type = @DataTypeHint("INTEGER"))}
   )
   public static class NamedParameterClass extends ScalarFunction {

     public String eval(String s1, Integer s2) {
       return s1 + ", " + s2;
     }
   }
   ```

The `@ArgumentHint` annotation already contains the `@DataTypeHint`
annotation, so you can’t use it with `@DataTypeHint` in `@FunctionHint`.
When applied to function parameters, `@ArgumentHint` can’t be used with
`@DataTypeHint` at the same time, so you should use `@ArgumentHint`
instead.

Named parameters take effect only when the corresponding class doesn’t
contain overloaded functions and variable parameter functions, otherwise
using named parameters causes an error.

<a id="flink-sql-udfs-determinism"></a>

### Determinism

Every user-defined function class can declare whether it produces deterministic
results or not by overriding the `isDeterministic()` method. If the function
is not purely functional, like `random()`, `date()`, or `now()`, the
method must return `false`. By default, `isDeterministic()` returns
`true`.

Also, the `isDeterministic()` method can influence the runtime behavior.
A runtime implementation might be called at two different stages.

#### During planning

During planning, in the so-called *pre-flight* phase, if a function is called
with constant expressions, or if constant expressions can be derived from the
given statement, a function is pre-evaluated for constant expression reduction
and might not be executed on the cluster. In these cases, you can use the
`isDeterministic()` method to disable constant expression reduction. For
example, the following calls to ABS are executed during planning:

```sql
SELECT ABS(-1) FROM t;
SELECT ABS(field) FROM t WHERE field = -1;
```

But the following call to ABS is not executed during planning:

```sql
SELECT ABS(field) FROM t;
```

#### During runtime

If a function is called with non-constant expressions or `isDeterministic()`
returns `false`, the function is executed on the cluster.

#### System function determinism

The determinism of system (built-in) functions is immutable. According to
Apache Calcite’s `SqlOperator` definition, there are two kinds of functions
which are not deterministic: *dynamic* functions and *non-deterministic*
functions.

```java
/**
 * Returns whether a call to this operator is guaranteed to always return
 * the same result given the same operands; true is assumed by default.
 */
public boolean isDeterministic() {
  return true;
}

/**
 * Returns whether it is unsafe to cache query plans referencing this
 * operator; false is assumed by default.
 */
public boolean isDynamicFunction() {
  return false;
}
```

The `isDeterministic()` method indicates the determinism of a function is
evaluated per-record during runtime if it returns `false`.

The `isDynamicFunction()` method implies the function can be evaluated only
at query-start if it returns `true`. It will be pre-evaluated during planning
only for batch mode. For streaming mode, it is equivalent to a
non-deterministic function, because the query is executed continuously under
the abstraction of a continuous query over [dynamic tables](dynamic-tables.md#flink-sql-dynamic-tables),
so the dynamic functions are also re-evaluated for each query execution, which
is equivalent to per-record in the current implementation.

The `isDynamicFunction` method applies only to system functions.

The following system functions are always non-deterministic, which means they
are evaluated per-record during runtime, both in batch and streaming mode.

- CURRENT_ROW_TIMESTAMP
- RAND
- RAND_INTEGER
- UNIX_TIMESTAMP
- UUID

The following system temporal functions are dynamic and are pre-evaluated
during planning (query-start) for batch mode and evaluated per-record for
streaming mode.

- CURRENT_DATE
- CURRENT_TIME
- CURRENT_TIMESTAMP
- LOCALTIME
- LOCALTIMESTAMP
- NOW

### Use the `open()` and `close()` methods of a Java UDF

Manage resources that are shared across UDFs by using the `open()` and
`close()` methods.

Implement the `open()` method to run expensive operations, like reading from
a YAML file or initializing underlying data structures to process in the UDF.

```java
public void open(FunctionContext context) {
  yaml = new Yaml();
  configs = yaml.load(inputStream);
  expensiveResource = new ExpensiveResource();
  // Initialize other resources...
}
```

Implement the `close()` method to release resources that were created during
the `open()` call.

```java
public void close() {
  expensiveResource.close();
  // Release other resources...
}
```

### Improve performance of the `eval()` method in Java UDFs

In the Java-based `eval()` method of a UDF, you can improve performance by
using the following techniques:

- Use the `eval()` method only for essential per-row logic. Any
  initialization work belongs in the `open()` method.
- If your code involves heavy string operations, use `java.util.StringBuilder`
  to concatenate strings.
- For parsing records with a single delimiter, like records delimited by commas,
  spaces, or semicolons, prefer the `java.util.StringTokenizer` class over the
  `java.util.Pattern` class.
- Avoid using the `String.split` method that uses a regular expression as the
  first parameter.
- For string operations, prefer initializing a `java.util.Pattern` statically
  and using methods in the `Pattern` class.
- Whenever possible, use well-tested and high-performance third-party Java
  libraries.
- Third-party libraries bundled in your UDF JAR might conflict with versions
  on the Confluent Cloud for Apache Flink runtime classpath. The runtime version takes
  precedence. For more information, see
  [Dependency version conflicts](#flink-sql-udfs-dependency-conflicts).
























<a id="flink-sql-udfs-external-connectivity"></a>

## External connectivity

External connectivity enables Flink UDFs to interact with external systems and
services directly from your UDF code, by using any of the
[connection types](../reference/statements/create-connection.md#flink-sql-create-connection) supported by
`CREATE CONNECTION` — for example, REST APIs, databases, and AI services. This
enables real-time data enrichment and integration with third-party services or
internal applications.

External connectivity provides the following benefits:

- Secure Connections: Use
  [Confluent Cloud Connection objects](../../integrations/connections/overview.md#connections-overview)
  to manage endpoints and secrets, like API keys, securely, without hardcoding
  them in your UDF artifact.
- Runtime Injection: Connection details are injected into the UDF runtime and
  accessed by using the `FunctionContext`, similarly to the Confluent Cloud for Apache Flink®
  implementation.

To enable external connectivity in your UDFs, follow these steps:

1. Define a connection: Create a Connection object in Confluent Cloud that contains
   the endpoint URL and secure credentials, by using the
   [Cloud Console, CLI, or SQL](../../integrations/connections/manage-connections.md#manage-connections).
2. Access in code: In your Java UDF `open()` method, use
   `FunctionContext.getJobParameter()` to retrieve the connection secrets and
   endpoints.
3. Bind in SQL: Register the UDF with
   [CREATE FUNCTION](../reference/statements/create-function.md#flink-sql-create-function) and bind one or more
   connections explicitly by using the `USING CONNECTIONS` clause.

### Endpoint availability

External connectivity supports public endpoints on all clouds where UDFs are
available. Private endpoints are available on AWS only.

#### External connectivity endpoint availability by cloud

| Cloud        | Public endpoints   | Private endpoints   |
|--------------|--------------------|---------------------|
| AWS          | ✓ Available        | ✓ Available         |
| Azure        | ✓ Available        | Not available       |
| Google Cloud | ✓ Available        | Not available       |

#### NOTE
**Billing.** External connectivity is billed on external data transfer — the
requests and responses exchanged with external systems — not on networking,
endpoints, or runtime. Data transfer is tracked as two line items:
*External Data Transfer – Outbound* (requests sent from Flink to external
systems) and *External Data Transfer – Inbound* (responses returned to
Flink). The rate is $0.025 per GB for each line item, the same for public and
private networking, with no separate per-endpoint or per-job charges.

External data transfer for external connectivity is not currently metered,
so no data transfer charges apply at this time.

<a id="flink-sql-udfs-external-connectivity-latency"></a>

### External connectivity call latency and throughput

External endpoint calls are synchronous: each call blocks the task thread until
the endpoint responds, so per-task throughput depends on endpoint response
time. A faster endpoint lets each task process more records per second.

Flink processes records in batches, and the platform applies a timeout to the
batched invocation rather than to an individual endpoint call, so a single slow
call consumes part of the budget shared by the other records in its batch. A
batch that doesn’t complete before the timeout is terminated without returning
values, and the statement error reports the limit that applied.

To increase total throughput, scale the compute pool so Flink can run more
parallel tasks, and design the upstream table so the workload partitions across
them.

For patterns that handle terminated calls, 5xx responses, and network errors,
see [Handle external-call failures explicitly](../how-to-guides/create-udf.md#flink-sql-udf-external-call-failure-modes).

<a id="flink-sql-udfs-availability"></a>

## UDF regional availability

The following tables show which UDF capabilities are available in each cloud
region. Select a cloud provider tab to see the region-by-region matrix.

Legend:

- **Java UDFs** — Generally Available.
- **Python UDFs** — Generally Available. Supports scalar functions, including
  vectorized (`func_type="pandas"`) scalar functions. See
  [Create a User-Defined Function](../how-to-guides/create-udf.md#flink-sql-create-udf).
- **External connectivity** — Generally Available. Java-only. Public endpoints
  are supported on all clouds. Private endpoints are available on AWS only.
  See [External connectivity](#flink-sql-udfs-external-connectivity).
- `✓` indicates the capability is supported in that region.
- A blank cell or `—` indicates the capability is not yet available.

### AWS

#### Flink UDF availability on AWS

| AWS Region     | Location                | Java UDFs   | Python UDFs   | External connectivity   |
|----------------|-------------------------|-------------|---------------|-------------------------|
| af-south-1     | Cape Town, South Africa | ✓           | ✓             | ✓                       |
| ap-east-1      | Hong Kong               | ✓           | ✓             | ✓                       |
| ap-northeast-1 | Tokyo, Japan            | ✓           | ✓             | ✓                       |
| ap-northeast-2 | Seoul, South Korea      | ✓           | ✓             | ✓                       |
| ap-south-1     | Mumbai, India           | ✓           | ✓             | ✓                       |
| ap-southeast-1 | Singapore               | ✓           | ✓             | ✓                       |
| ap-southeast-2 | Sydney, Australia       | ✓           | ✓             | ✓                       |
| ap-southeast-3 | Jakarta, Indonesia      | ✓           | ✓             | ✓                       |
| ca-central-1   | Canada Central          | ✓           | ✓             | ✓                       |
| eu-central-1   | Frankfurt, Germany      | ✓           | ✓             | ✓                       |
| eu-central-2   | Zurich, Switzerland     | ✓           | ✓             | ✓                       |
| eu-north-1     | Stockholm, Sweden       | ✓           | ✓             | ✓                       |
| eu-south-1     | Milan, Italy            | ✓           | ✓             | ✓                       |
| eu-west-1      | Ireland                 | ✓           | ✓             | ✓                       |
| eu-west-2      | London, UK              | ✓           | ✓             | ✓                       |
| eu-west-3      | Paris, France           | ✓           | ✓             | ✓                       |
| me-south-1     | Bahrain                 | ✓           | ✓             | ✓                       |
| sa-east-1      | São Paulo, Brazil       | ✓           | ✓             | ✓                       |
| us-east-1      | 1. Virginia, USA        | ✓           | ✓             | ✓                       |
| us-east-2      | Ohio, USA               | ✓           | ✓             | ✓                       |
| us-west-2      | Oregon, USA             | ✓           | ✓             | ✓                       |

### Azure

#### Flink UDF availability on Azure

| Azure Region       | Location                   | Java UDFs   | Python UDFs   | External connectivity   |
|--------------------|----------------------------|-------------|---------------|-------------------------|
| australiaeast      | New South Wales, Australia | ✓           |               | ✓                       |
| brazilsouth        | São Paulo state, Brazil    | ✓           |               | ✓                       |
| canadacentral      | Toronto, Canada            | ✓           |               | ✓                       |
| centralindia       | Pune, India                | ✓           |               | ✓                       |
| centralus          | Iowa, USA                  | ✓           |               | ✓                       |
| eastasia           | Hong Kong                  | ✓           |               | ✓                       |
| eastus             | Virginia, USA              | ✓           |               | ✓                       |
| eastus2            | Virginia, USA              | ✓           |               | ✓                       |
| francecentral      | Paris, France              | ✓           |               | ✓                       |
| germanywestcentral | Frankfurt, Germany         | ✓           |               | ✓                       |
| japaneast          | Tokyo, Japan               | ✓           |               | ✓                       |
| newzealandnorth    | Auckland, New Zealand      | ✓           |               | ✓                       |
| northeurope        | Ireland                    | ✓           |               | ✓                       |
| norwayeast         | Oslo, Norway               | ✓           |               | ✓                       |
| southafricanorth   | Johannesburg, South Africa | ✓           |               | ✓                       |
| southcentralus     | Texas, USA                 | ✓           |               | ✓                       |
| southeastasia      | Singapore                  | ✓           |               | ✓                       |
| spaincentral       | Spain                      | ✓           |               | ✓                       |
| swedencentral      | Gävle, Sweden              | ✓           |               | ✓                       |
| switzerlandnorth   | Zurich, Switzerland        | ✓           |               | ✓                       |
| uaenorth           | Dubai, UAE                 | ✓           |               | ✓                       |
| uksouth            | London, UK                 | ✓           |               | ✓                       |
| westeurope         | Netherlands                | ✓           |               | ✓                       |
| westus2            | Washington, USA            | ✓           |               | ✓                       |
| westus3            | Phoenix, USA               | ✓           |               | ✓                       |

### Google Cloud

#### NOTE
Java UDFs and external connectivity for UDFs are now available in select
Google Cloud regions, listed in the following table. Python UDFs are not yet
available on Google Cloud. For the full list of Google Cloud regions where Confluent Cloud for Apache Flink itself
is supported, see [Google Cloud region availability](../../get-started/regions.md#regions-gcp).

#### Flink UDF availability on Google Cloud

| Google Cloud Region     | Location                     | Java UDFs   | Python UDFs   | External connectivity   |
|-------------------------|------------------------------|-------------|---------------|-------------------------|
| asia-northeast1         | Tokyo, Japan                 | ✓           |               | ✓                       |
| asia-south1             | Mumbai, India                | ✓           |               | ✓                       |
| asia-south2             | Delhi, India                 | ✓           |               | ✓                       |
| asia-southeast1         | Singapore                    | ✓           |               | ✓                       |
| australia-southeast1    | Sydney, Australia            | ✓           |               | ✓                       |
| australia-southeast2    | Melbourne, Australia         | ✓           |               | ✓                       |
| europe-north1           | Hamina, Finland              | ✓           |               | ✓                       |
| europe-southwest1       | Madrid, Spain                | ✓           |               | ✓                       |
| europe-west1            | Belgium                      | ✓           |               | ✓                       |
| europe-west2            | London, UK                   | ✓           |               | ✓                       |
| europe-west3            | Frankfurt, Germany           | ✓           |               | ✓                       |
| europe-west4            | Eemshaven, Netherlands       | ✓           |               | ✓                       |
| europe-west8            | Milan, Italy                 | ✓           |               | ✓                       |
| northamerica-northeast2 | Toronto, Canada              | ✓           |               | ✓                       |
| southamerica-east1      | São Paulo, Brazil            | ✓           |               | ✓                       |
| southamerica-west1      | Santiago, Chile              | ✓           |               | ✓                       |
| us-central1             | Iowa, USA                    | ✓           |               | ✓                       |
| us-east1                | 1. Carolina, USA             | ✓           |               | ✓                       |
| us-east4                | 1. Virginia, USA             | ✓           |               | ✓                       |
| us-west1                | Oregon, USA                  | ✓           |               | ✓                       |
| us-west2                | Los Angeles, California, USA | ✓           |               | ✓                       |
| us-west4                | Las Vegas, Nevada, USA       | ✓           |               | ✓                       |

For the full list of regions where Confluent Cloud for Apache Flink itself is supported (including
regions without UDF support), see [Supported regions](../reference/overview.md#flink-cloud-regions).

<a id="flink-sql-udfs-limitations"></a>

## UDF limitations

User-defined functions have the following limitations.

- Confluent CLI version 4.13.0 or later is required.
- UDFs can’t make external network calls directly. To call an external service
  from a UDF, use the
  [External connectivity](#flink-sql-udfs-external-connectivity) feature,
  which routes calls through a registered Connection object.
- JDK 21 is the latest supported Java version for uploaded JAR files.
- Each Flink statement can have no more than 10 UDFs.
- Each organization/cloud/region/environment can have no more than 100 Flink
  artifacts.
- The size limit of each artifact is 100 MB.
- Flink doesn’t support aggregates.
- Flink doesn’t support table aggregates.
- Flink doesn’t support temporary functions.
- Flink doesn’t support the ALTER FUNCTION statement.
- You can’t use UDFs in combination with
  [MATCH_RECOGNIZE](../reference/queries/match_recognize.md#flink-sql-pattern-recognition).
- Flink doesn’t support vararg functions.
- Flink doesn’t support user-defined structured types.
- Both inputs and outputs of the UDF have a row-size limit of 4 MB.
- Flink doesn’t support custom type inference.
- Flink doesn’t support constant expression reduction.
- The UDF feature targets streaming processing, so the first invocation of a
  UDF might be slow, but subsequent invocations run with low latency.

### File system access limitations

The file system is read-only in the runtime environment. UDFs can’t create,
write, or modify files on the file system. This includes temporary files, model
files, or any other file operations. Libraries that require file system write
access, like those using JNI/native binaries that extract files from JARs, are
not supported.

### JNI and native binary limitations

Libraries that use Java Native Interface (JNI) or require native binaries are
not supported due to filesystem restrictions and potential architecture
compatibility issues.

<a id="flink-sql-udfs-dependency-conflicts"></a>

### Dependency version conflicts

When you declare Flink dependencies with `provided` scope in your Maven or
Gradle build, their transitive dependencies are still present on the Confluent Cloud for Apache Flink
runtime classpath. If your UDF JAR bundles a different version of
one of those transitive dependencies, the runtime version takes precedence.

This can cause `NoSuchMethodError` or `NoClassDefFoundError` exceptions at
runtime, even though your UDF compiles and passes local tests. A common example
is `commons-lang3`: the `flink-table-api-java` artifact pulls in a specific
version, and if your JAR bundles a different version, the runtime version wins.

To detect and resolve dependency conflicts:

1. Run `mvn dependency:tree -Dverbose` to identify conflicting versions in
   your project.
2. Add the Maven Enforcer Plugin with the `<dependencyConvergence/>` rule to
   fail the build when version conflicts exist:
   ```xml
   <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-enforcer-plugin</artifactId>
     <version>3.5.0</version>
     <executions>
       <execution>
         <id>enforce</id>
         <goals>
           <goal>enforce</goal>
         </goals>
         <configuration>
           <rules>
             <dependencyConvergence/>
           </rules>
         </configuration>
       </execution>
     </executions>
   </plugin>
   ```
3. Resolve each conflict by aligning your dependency version to the runtime
   version, removing the dependency from your JAR, or switching to an
   alternative library that doesn’t conflict.

### Troubleshoot UDF runtime errors

Exceptions thrown during the `open()` method of a UDF produce a generic error
message:

```none
Your statement encountered an error during execution.
```

Because the actual exception is not surfaced, debugging `open()` failures
requires adding explicit logging. Wrap the body of your `open()` method in a
try/catch block and log the exception:

```java
@Override
public void open(FunctionContext context) throws Exception {
    try {
        // your initialization code
    } catch (Exception e) {
        LOG.error("UDF open() failed", e);
        throw e;
    }
}
```

For instructions on enabling and viewing UDF logs, see
[Enable UDF logging](../how-to-guides/enable-udf-logging.md#flink-sql-enable-udf-logging).

If the stack trace shows `NoSuchMethodError` or `NoClassDefFoundError`,
the cause is likely a [dependency version conflict](#flink-sql-udfs-dependency-conflicts).

<a id="flink-sql-udfs-limitations-external-connectivity"></a>

### External connectivity limitations

- Synchronous execution: External calls are synchronous. To maintain stream
  processing performance, the UDF waits for the response before processing the
  next record in the thread.
- Latency constraint: High latency in external systems directly impacts the
  throughput of your Flink job, and slow calls can exhaust the timeout that
  applies to the batch they belong to. For latency and throughput detail, see
  [External connectivity call latency and throughput](#flink-sql-udfs-external-connectivity-latency).
- Stateless functions only: Flink supports external connectivity only for Java
  stateless scalar and table functions (`ScalarFunction` and
  `TableFunction`). It isn’t supported for `ProcessTableFunction`.
- Networking: Private endpoints are available on AWS only. Public endpoints
  are supported on all clouds. For details, see the endpoint availability table
  in [External connectivity](#flink-sql-udfs-external-connectivity).

## UDF logging limitations

- **Log4j logging only**: You can compose external UDF loggers only with the
  Apache Log4j logging framework.
- **Burst rate to 1000/s**: UDF logging supports up to 1000 log events per
  second for each UDF during a short burst of high activity. This optimizes
  performance and reduces noise in the logs. Flink drops events that exceed
  the maximum rate.

## Related content

- [CREATE FUNCTION](../reference/statements/create-function.md#flink-sql-create-function)
- [Create a User-defined Function](../how-to-guides/create-udf.md#flink-sql-create-udf)
- [Flink SQL Queries](../reference/queries/overview.md#flink-sql-queries)
- [Flink UDF Java Examples](https://github.com/confluentinc/flink-udf-java-examples)
- [Flink UDF Python Examples](https://github.com/confluentinc/flink-udf-python-examples)

#### NOTE
This website includes content developed at the [Apache Software Foundation](https://www.apache.org/)
under the terms of the [Apache License v2](https://www.apache.org/licenses/LICENSE-2.0.html).
