Перейти к содержимому
Последнее обновление

CREATE FUNCTION

Описание​

Этот оператор используется для создания пользовательской функции.

Синтаксис​

CREATE [ GLOBAL ] 
[{AGGREGATE | TABLES | ALIAS }] FUNCTION <function_name>
(<arg_type> [, ...])
[ RETURNS <ret_type> ]
[ INTERMEDIATE <inter_type> ]
[ WITH PARAMETER(<param> [,...]) AS <origin_function> ]
[ PROPERTIES ("<key>" = "<value>" [, ...]) ]

Обязательные параметры​

1. <function_name>

Если function_name включает имя базы данных, например db1.my_func, пользовательская функция будет создана в соответствующей базе данных. В противном случае функция будет создана в базе данных текущего сеанса. Имя и параметры новой функции не должны одновременно совпадать с именем и параметрами существующей функции в текущем пространстве имён; иначе создание завершится ошибкой.

2. <arg_type>

Типы входных параметров функции. Можно объявить один или несколько фиксированных аргументов. Начиная с версии 4.1.5, объявление функций с переменным числом аргументов с помощью ... больше не поддерживается. Для передачи переменного числа значений используйте параметр типа ARRAY.

3. <ret_type>

Тип возвращаемого значения функции. Это обязательный параметр при создании новой функции. При создании псевдонима для существующей функции этот параметр не требуется.

Необязательные параметры​

1. GLOBAL

Если указан, создаваемая функция доступна глобально.

2. AGGREGATE

Если указан, создаваемая функция является агрегатной функцией.

3. TABLES

Если указан, создаваемая функция является табличной функцией.

4. ALIAS

Если указан, создаваемая функция является функцией-псевдонимом.

Если ни один из перечисленных выше параметров, определяющих тип функции, не указан, создаваемая функция является скалярной.

5. <inter_type>

Указывает тип данных на промежуточном этапе вычисления агрегатной функции.

6. <param>

Указывает параметры функции-псевдонима. Необходимо указать хотя бы один параметр.

7. <origin_function>

Указывает исходную функцию, которой соответствует функция-псевдоним.

8. <properties>

  • file: Указывает JAR-пакет, содержащий пользовательскую функцию (UDF). В среде из нескольких машин его также можно загрузить по HTTP. Этот параметр обязателен.
  • symbol: Указывает имя класса, в котором содержится UDF. Этот параметр обязателен.
  • type: Указывает тип вызова UDF. Значение по умолчанию — Native. При использовании Java UDF укажите JAVA_UDF.
  • always_nullable: Указывает, может ли результат UDF содержать значения NULL. Это необязательный параметр со значением по умолчанию true.
  • volatility: Указывает степень стабильности результата функции. Этот параметр поддерживается с версии 4.1.2. Он необязателен. Значение по умолчанию — volatile для скалярных UDF и immutable для UDAF/UDTF. Допустимые значения:
    • immutable: При одинаковых входных данных всегда возвращается одинаковый результат. Большинство детерминированных UDF следует помечать как immutable, чтобы оптимизатор мог выполнять дополнительные оптимизации плана.
    • stable: Результат стабилен в пределах одного оператора SQL, но может меняться между операторами, как у now(). Для функций этого типа отключены кэш SQL и переписывание запросов с использованием материализованных представлений.
    • volatile: Результат может меняться при каждом вызове, как у random(). Для функций этого типа отключены кэш SQL, переписывание запросов с использованием материализованных представлений и многие правила переписывания запросов оптимизатора.

Требования к правам доступа​

Для выполнения этой команды пользователь должен обладать привилегией ADMIN_PRIV.

Примеры​

  1. Создайте пользовательскую функцию UDF. Подробнее см. JAVA-UDF.

    CREATE FUNCTION java_udf_add_one(int) RETURNS int PROPERTIES (
    "file"="file:///path/to/java-udf-demo-jar-with-dependencies.jar",
    "symbol"="org.apache.doris.udf.AddOne",
    "always_nullable"="true",
    "type"="JAVA_UDF",
    "volatility"="immutable"
    );
  2. Создайте пользовательскую функцию UDAF.

    CREATE AGGREGATE FUNCTION simple_sum(INT) RETURNS INT PROPERTIES (
    "file"="file:///pathTo/java-udaf.jar",
    "symbol"="org.apache.doris.udf.demo.SimpleDemo",
    "always_nullable"="true",
    "type"="JAVA_UDF"
    );
  3. Создайте пользовательскую функцию UDTF.

    CREATE TABLES FUNCTION java_udtf(string, string) RETURNS array<string> PROPERTIES (
    "file"="file:///pathTo/java-udaf.jar",
    "symbol"="org.apache.doris.udf.demo.UDTFStringTest",
    "always_nullable"="true",
    "type"="JAVA_UDF"
    );
  4. Создайте пользовательскую функцию-псевдоним. Подробнее см. Функция-псевдоним.

    CREATE ALIAS FUNCTION id_masking(INT) WITH PARAMETER(id) AS CONCAT(LEFT(id, 3), '****', RIGHT(id, 4));
  5. Создайте глобальную пользовательскую функцию-псевдоним.

    CREATE GLOBAL ALIAS FUNCTION id_masking(INT) WITH PARAMETER(id) AS CONCAT(LEFT(id, 3), '****', RIGHT(id, 4));
  6. Создайте Python UDF с характеристикой volatile. Для функций, зависящих от случайных значений, таких как uuid.uuid4(), следует сохранять значение по умолчанию volatility = volatile; их нельзя ошибочно помечать как immutable.

    CREATE TABLE cte_uuid_seed (id INT) ENGINE=OLAP DUPLICATE KEY(id)
    DISTRIBUTED BY HASH(id) BUCKETS 1 PROPERTIES ("replication_num" = "1");
    INSERT INTO cte_uuid_seed VALUES (1),(2),(3);

    DROP FUNCTION IF EXISTS py_uuid_token(INT);
    CREATE FUNCTION py_uuid_token(INT)
    RETURNS STRING
    PROPERTIES (
    "type" = "PYTHON_UDF",
    "symbol" = "py_uuid_token_impl",
    "always_nullable" = "false",
    "runtime_version" = "3.12.11",
    "volatility" = "volatile"
    )
    AS $$
    import uuid
    def py_uuid_token_impl(x):
    return f"{x}-{uuid.uuid4()}"
    $$;

    SET enable_cte_materialize = true;
    SET inline_cte_referenced_threshold = 10;

    WITH cte AS (SELECT id, py_uuid_token(id) AS token FROM cte_uuid_seed)
    SELECT id, COUNT(DISTINCT token) AS distinct_tokens
    FROM (SELECT id, token FROM cte UNION ALL SELECT id, token FROM cte) u
    GROUP BY id ORDER BY id;

    Правильный результат:

    +------+-----------------+
    | id | distinct_tokens |
    +------+-----------------+
    | 1 | 1 |
    | 2 | 1 |
    | 3 | 1 |
    +------+-----------------+

    Для этой функции следующее определение является неправильным:

    DROP FUNCTION IF EXISTS py_uuid_token(INT);
    CREATE FUNCTION py_uuid_token(INT)
    RETURNS STRING
    PROPERTIES (
    "type" = "PYTHON_UDF",
    "symbol" = "py_uuid_token_impl",
    "always_nullable" = "false",
    "runtime_version" = "3.12.11",
    "volatility" = "immutable"
    )
    AS $$
    import uuid
    def py_uuid_token_impl(x):
    return f"{x}-{uuid.uuid4()}"
    $$;

    Выполните тот же запрос ещё раз:

    WITH cte AS (SELECT id, py_uuid_token(id) AS token FROM cte_uuid_seed)
    SELECT id, COUNT(DISTINCT token) AS distinct_tokens
    FROM (SELECT id, token FROM cte UNION ALL SELECT id, token FROM cte) u
    GROUP BY id ORDER BY id;

    Неправильный результат:

    +------+-----------------+
    | id | distinct_tokens |
    +------+-----------------+
    | 1 | 2 |
    | 2 | 2 |
    | 3 | 2 |
    +------+-----------------+

    Почему это неправильно: Поскольку py_uuid_token имеет характеристику volatile, каждый вызов uuid.uuid4() генерирует новое значение. Если функцию ошибочно пометить как volatility = immutable, оптимизатор может счесть переписывание повторных обращений безопасным и выбрать план, в котором UDF вычисляется отдельно с каждой стороны UNION ALL. В результате одно и то же значение id может давать два разных значения token, а результат COUNT(DISTINCT token) меняется с 1 на 2.