blob: 9b889b922f82b09491958377775b0b0a775b2710 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
suite("test_pythonudf_concurrent") {
// Test multiple Python UDFs executing concurrently in the same SQL query
def runtime_version = "3.10.12"
try {
// Create test table
sql """ DROP TABLE IF EXISTS concurrent_udf_test; """
sql """
CREATE TABLE concurrent_udf_test (
id INT,
value1 INT,
value2 INT,
value3 DOUBLE,
value4 DOUBLE,
str1 STRING,
str2 STRING
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES("replication_num" = "1");
"""
sql """
INSERT INTO concurrent_udf_test VALUES
(1, 10, 20, 1.5, 2.5, 'hello', 'world'),
(2, 30, 40, 3.5, 4.5, 'foo', 'bar'),
(3, 50, 60, 5.5, 6.5, 'test', 'case'),
(4, 70, 80, 7.5, 8.5, 'python', 'udf'),
(5, 90, 100, 9.5, 10.5, 'doris', 'db');
"""
// Create multiple scalar UDFs with different operations
// UDF 1: Integer addition
sql """ DROP FUNCTION IF EXISTS py_add_int(INT, INT); """
sql """
CREATE FUNCTION py_add_int(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "add_int",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
def add_int(a, b):
if a is None or b is None:
return None
return a + b
\$\$;
"""
// UDF 2: Integer multiplication
sql """ DROP FUNCTION IF EXISTS py_multiply_int(INT, INT); """
sql """
CREATE FUNCTION py_multiply_int(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "multiply_int",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
def multiply_int(a, b):
if a is None or b is None:
return None
return a * b
\$\$;
"""
// UDF 3: Double division
sql """ DROP FUNCTION IF EXISTS py_divide_double(DOUBLE, DOUBLE); """
sql """
CREATE FUNCTION py_divide_double(DOUBLE, DOUBLE)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "divide_double",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
def divide_double(a, b):
if a is None or b is None or b == 0:
return None
return a / b
\$\$;
"""
// UDF 4: String concatenation
sql """ DROP FUNCTION IF EXISTS py_concat_str(STRING, STRING); """
sql """
CREATE FUNCTION py_concat_str(STRING, STRING)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "concat_str",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
def concat_str(s1, s2):
if s1 is None or s2 is None:
return None
return s1 + '_' + s2
\$\$;
"""
// UDF 5: String length
sql """ DROP FUNCTION IF EXISTS py_str_len(STRING); """
sql """
CREATE FUNCTION py_str_len(STRING)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "str_len",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
def str_len(s):
if s is None:
return None
return len(s)
\$\$;
"""
// Test 1: Multiple scalar UDFs in SELECT clause
qt_concurrent_scalar_1 """
SELECT
id,
py_add_int(value1, value2) AS add_result,
py_multiply_int(value1, value2) AS multiply_result,
py_divide_double(value3, value4) AS divide_result,
py_concat_str(str1, str2) AS concat_result,
py_str_len(str1) AS len_result
FROM concurrent_udf_test
ORDER BY id;
"""
// Test 2: Multiple scalar UDFs with nested calls
qt_concurrent_scalar_2 """
SELECT
id,
py_add_int(py_multiply_int(value1, 2), value2) AS nested_result1,
py_str_len(py_concat_str(str1, str2)) AS nested_result2
FROM concurrent_udf_test
ORDER BY id;
"""
// Test 3: Multiple scalar UDFs in WHERE clause
qt_concurrent_scalar_3 """
SELECT
id,
value1,
value2
FROM concurrent_udf_test
WHERE py_add_int(value1, value2) > 50
AND py_str_len(str1) > 3
ORDER BY id;
"""
// Test 4: Multiple vectorized UDFs (using pandas)
sql """ DROP FUNCTION IF EXISTS py_vec_add(INT, INT); """
sql """
CREATE FUNCTION py_vec_add(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "vec_add",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
import pandas as pd
def vec_add(a: pd.Series, b: pd.Series) -> pd.Series:
return a + b
\$\$;
"""
sql """ DROP FUNCTION IF EXISTS py_vec_sub(INT, INT); """
sql """
CREATE FUNCTION py_vec_sub(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "vec_sub",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
import pandas as pd
def vec_sub(a: pd.Series, b: pd.Series) -> pd.Series:
return a - b
\$\$;
"""
sql """ DROP FUNCTION IF EXISTS py_vec_max(INT, INT); """
sql """
CREATE FUNCTION py_vec_max(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "vec_max",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
import pandas as pd
import numpy as np
def vec_max(a: pd.Series, b: pd.Series) -> pd.Series:
return pd.Series(np.maximum(a, b))
\$\$;
"""
// Test 5: Multiple vectorized UDFs in the same query
qt_concurrent_vector_1 """
SELECT
id,
py_vec_add(value1, value2) AS vec_add_result,
py_vec_sub(value2, value1) AS vec_sub_result,
py_vec_max(value1, value2) AS vec_max_result
FROM concurrent_udf_test
ORDER BY id;
"""
// Test 6: Mix of scalar and vectorized UDFs
qt_concurrent_mixed_1 """
SELECT
id,
py_add_int(value1, value2) AS scalar_add,
py_vec_add(value1, value2) AS vector_add,
py_concat_str(str1, str2) AS scalar_concat,
py_multiply_int(value1, 10) AS scalar_mul
FROM concurrent_udf_test
ORDER BY id;
"""
// Test 7: Multiple UDFs with aggregation
qt_concurrent_with_agg """
SELECT
COUNT(*) AS total_count,
SUM(py_add_int(value1, value2)) AS sum_add,
AVG(py_multiply_int(value1, 2)) AS avg_mul,
MAX(py_str_len(str1)) AS max_len
FROM concurrent_udf_test;
"""
// Test 8: Multiple UDFs with GROUP BY
qt_concurrent_group_by """
SELECT
CASE WHEN id <= 3 THEN 'group1' ELSE 'group2' END AS grp,
COUNT(*) AS cnt,
SUM(py_add_int(value1, value2)) AS sum_result,
AVG(py_divide_double(value3, value4)) AS avg_result
FROM concurrent_udf_test
GROUP BY grp
ORDER BY grp;
"""
// Test 9: Multiple UDFs with different parameter types
sql """ DROP FUNCTION IF EXISTS py_int_to_str(INT); """
sql """
CREATE FUNCTION py_int_to_str(INT)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "int_to_str",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
)
AS \$\$
def int_to_str(n):
if n is None:
return None
return str(n)
\$\$;
"""
qt_concurrent_type_mix """
SELECT
id,
py_add_int(value1, value2) AS int_result,
py_divide_double(value3, value4) AS double_result,
py_concat_str(py_int_to_str(value1), str1) AS mixed_result
FROM concurrent_udf_test
ORDER BY id;
"""
// Test 10: Stress test - many UDFs in one query
qt_concurrent_stress """
SELECT
id,
py_add_int(value1, value2) AS r1,
py_multiply_int(value1, value2) AS r2,
py_add_int(value1, 100) AS r3,
py_multiply_int(value2, 5) AS r4,
py_divide_double(value3, value4) AS r5,
py_divide_double(value4, value3) AS r6,
py_concat_str(str1, str2) AS r7,
py_str_len(str1) AS r8,
py_str_len(str2) AS r9,
py_int_to_str(value1) AS r10
FROM concurrent_udf_test
ORDER BY id;
"""
} finally {
// Cleanup
try_sql("DROP FUNCTION IF EXISTS py_add_int(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_multiply_int(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_divide_double(DOUBLE, DOUBLE);")
try_sql("DROP FUNCTION IF EXISTS py_concat_str(STRING, STRING);")
try_sql("DROP FUNCTION IF EXISTS py_str_len(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_vec_add(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_vec_sub(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_vec_max(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_int_to_str(INT);")
try_sql("DROP TABLE IF EXISTS concurrent_udf_test;")
}
}