Skip to content

[fix](udf) Reject variadic user-defined functions - #67373

Merged
HappenLee merged 1 commit into
apache:masterfrom
linrrzqqq:reject-var-args-udf
Sep 4, 2026
Merged

[fix](udf) Reject variadic user-defined functions#67373
HappenLee merged 1 commit into
apache:masterfrom
linrrzqqq:reject-var-args-udf

Conversation

@linrrzqqq

@linrrzqqqlinrrzqqq commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

What problem does this PR solve?

Issue Number: None

Problem Summary:

User-defined functions exposed variadic DDL metadata without reliable end-to-end support. Reject variadic declarations during CREATE FUNCTION analysis for scalar, aggregate, table, and alias functions while retaining variadic signature parsing for DROP, SHOW, and historical metadata compatibility.

before:

CREATEFUNCTIONpy_add(INT, INT, ...)
RETURNS INT
PROPERTIES (
"type"="PYTHON_UDF",
"symbol"="evaluate",
"runtime_version"="3.12.11",
"volatility"="immutable"
)
AS $$
def evaluate(a, b, c):
return a + b + c
$$;
SELECT py_add(1, 2, 3);
-- ERROR 1105 (HY000): errCode = 2, detailMessage = Index 2 out of bounds for length 2

In PythonUdfBuilder.java:82:

publicPair<PythonUdf, PythonUdf> build(Stringname, List<?> arguments) {
// exprs = (1, 2, 3), size = 3// argTypes = [INT, INT], size = 2List<Expression> exprs = arguments.stream().map(Expression.class::cast).collect(Collectors.toList());
List<DataType> argTypes = udf.getSignatures().get(0).argumentsTypes;
List<Expression> processedExprs = Lists.newArrayList();
for (inti = 0; i < exprs.size(); ++i) {
// when i = 2, argTypes.get(2), err occur!processedExprs.add(TypeCoercionUtils.castIfNotSameType(exprs.get(i), argTypes.get(i)));
}
returnPair.ofSame(udf.withFreshVolatileIdentity().withChildren(processedExprs));
}

now:

CREATEFUNCTIONpy_add(INT, INT, ...)
RETURNS INT
PROPERTIES (
"type"="PYTHON_UDF",
"symbol"="evaluate",
"runtime_version"="3.12.11",
"volatility"="immutable"
)
AS $$
def evaluate(a, b, c):
return a + b + c
$$;
-- ERROR 1105 (HY000): errCode = 2, detailMessage = mismatched input ',' expecting ')'(line 1, pos 31)

Release note

Reject variadic declarations for user-defined functions.

Check List (For Author)

@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@linrrzqqq

Copy link
Copy Markdown
CollaboratorAuthor

/review

github-actions[bot]
github-actionsBot previously requested changes Sep 1, 2026

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated code review: request changes

The fixed-arity implementation is otherwise coherent, but the exact reviewed head cannot pass the mandatory FE style gate because the newly added arity test uses forbidden static Mockito imports. I left one inline comment with the concrete fix.

Critical checkpoint conclusions:

  • Goal and proof: CREATE now rejects variadic declarations for scalar, aggregate, table, and alias functions; all new catalog objects persist fixed arity; historical Java/Python metadata is exact-gated before unsafe builder work. The parser and arity tests target those boundaries.
  • Scope and clarity: the production change is small and consistently covers the two CREATE visitors, four catalog-construction branches, and all six Java/Python scalar/aggregate/table builders.
  • Concurrency: no new thread, shared mutable state, lock, blocking operation, or lock-order change is introduced.
  • Lifecycle: no resource ownership, static initialization, or special release lifecycle is changed.
  • Configuration: no configuration item is added or changed.
  • Compatibility: catalog/Thrift formats are unchanged; DROP and SHOW retain variadic signature parsing so historical entries remain addressable, while replay preserves their stored metadata.
  • Parallel paths: scalar/UDAF/UDTF/alias creation, Java/Python/RPC metadata construction, database/global scopes, alias's pre-existing exact gate, and table-function normal/outer registration were traced. No parallel variable-arity creation path remains.
  • Conditional checks: the exact-size check is the direct fixed-arity invariant and runs during candidate filtering before signature indexing, cast loops, child reconstruction, or FE-to-BE translation.
  • Test coverage and results: the added tests cover the main CREATE/DROP/SHOW parser boundary and under/exact/over calls for all six changed builders. Per the review bundle, no local builds or tests were run. The exact-head GitHub CheckStyle job did run and failed on the added test imports.
  • Observability: parser/analysis rejection does not require new logs or metrics; failures remain explicit analysis errors.
  • Persistence and transactions: new function metadata and table companions journal the fixed bit; historical replay remains unchanged. No user-data transaction, visible-version, or data-write path is involved.
  • FE/BE transport: no new transmitted variable or symbol/storage-format change is introduced; existing metadata fields remain intact.
  • Performance: the O(1) arity check rejects invalid calls earlier and adds no meaningful allocation or hot-path cost.
  • Other issues: no additional correctness, lifecycle, compatibility, performance, or coverage bug remained after the final changed-file and unresolved-risk sweep.

User focus: no additional user-provided focus was supplied.

Review completion: complete after one converged round (two full reviewers plus a separate historical-variadic risk review all returned NO_NEW_VALUABLE_FINDINGS). Reviewed head: 8ce33b9.

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.mockito.Mockito.mock;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Use the repository's non-static Mockito import

This added test currently makes the mandatory CheckStyle job fail: this line is in the wrong custom import group and violates AvoidStaticImport, while the following when import also violates AvoidStaticImport. Import org.mockito.Mockito normally and call Mockito.mock(...) / Mockito.when(...) so this head passes the FE style gate.

### What problem does this PR solve?
Issue Number: None
Problem Summary:
CREATE FUNCTION accepted variadic signatures even though user-defined function implementations are fixed-arity. Remove variadic syntax from CREATE FUNCTION and CREATE ALIAS FUNCTION, and always create fixed-arity UDF metadata.
Java and Python UDF, UDAF, and UDTF builders now also require the actual argument count to exactly match the declared arity. This prevents historical variadic metadata from accepting calls that the fixed-length builder and runtime paths cannot safely construct. Variadic signature parsing remains available for DROP FUNCTION and SHOW CREATE FUNCTION so historical catalog entries remain addressable.
### Release note
CREATE FUNCTION no longer accepts variadic argument declarations, and user-defined function calls require an exact argument count.
### Check List (For Author)
- Test
- [x] Unit Test
- ./run-fe-ut.sh --run org.apache.doris.nereids.parser.NereidsParserTest,org.apache.doris.catalog.CreateFunctionTest,org.apache.doris.nereids.trees.expressions.functions.udf.UdfBuilderArityTest,org.apache.doris.nereids.trees.expressions.functions.udf.UdfVolatilityTest
- Behavior changed:
- [x] Yes. CREATE FUNCTION rejects variadic declarations and UDF builders enforce fixed arity.
- Does this need documentation?
- [x] No.
@linrrzqqq

Copy link
Copy Markdown
CollaboratorAuthor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 75.00% (12/16) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 17071 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 360056b408e357aab9e046172a7b8637746855b4, data reload: false
------ Round 1 ----------------------------------
============================================
q1	17601	3093	3087	3087
q2	2073	264	228	228
q3	10250	884	524	524
q4	4675	246	201	201
q5	7678	751	390	390
q6	133	117	92	92
q7	528	514	385	385
q8	9233	896	931	896
q9	3449	2406	2407	2406
q10	6515	834	747	747
q11	403	199	177	177
q12	613	257	198	198
q13	18143	1560	1144	1144
q14	159	149	140	140
q15	q16	433	395	363	363
q17	1421	844	846	844
q18	3094	2293	2265	2265
q19	1293	909	770	770
q20	381	289	202	202
q21	5595	1782	1859	1782
q22	329	274	230	230
Total cold run time: 93999 ms
Total hot run time: 17071 ms
----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3464	3400	3369	3369
q2	519	402	369	369
q3	2263	2307	2165	2165
q4	1211	1171	895	895
q5	2174	2135	2113	2113
q6	167	118	89	89
q7	1043	937	908	908
q8	1625	1440	1429	1429
q9	3149	3127	3109	3109
q10	1884	1799	1663	1663
q11	361	274	252	252
q12	460	434	343	343
q13	1484	1543	1160	1160
q14	167	183	155	155
q15	q16	410	402	363	363
q17	3560	3306	3175	3175
q18	4811	4449	4759	4449
q19	967	880	851	851
q20	994	949	845	845
q21	3851	3265	3246	3246
q22	403	365	316	316
Total cold run time: 34967 ms
Total hot run time: 31264 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 82324 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 360056b408e357aab9e046172a7b8637746855b4, data reload: false
query5	4267	401	336	336
query6	386	138	149	138
query7	4931	437	233	233
query8	296	126	121	121
query9	8730	2945	2941	2941
query10	419	222	191	191
query11	5389	1037	889	889
query12	120	70	73	70
query13	1185	419	334	334
query14	6115	2236	2111	2111
query14_1	1981	1969	1966	1966
query15	173	123	116	116
query16	930	425	371	371
query17	817	468	393	393
query18	2348	329	250	250
query19	184	149	111	111
query20	75	71	74	71
query21	208	106	90	90
query22	5332	5278	5264	5264
query23	6963	6292	6006	6006
query23_1	6243	6086	6146	6086
query24	7334	1122	782	782
query24_1	790	799	789	789
query25	446	310	270	270
query26	1241	241	131	131
query27	2775	378	252	252
query28	4735	1507	1504	1504
query29	951	487	346	346
query30	249	153	128	128
query31	828	396	322	322
query32	128	73	70	70
query33	461	231	164	164
query34	997	828	497	497
query35	403	399	351	351
query36	562	531	525	525
query37	114	77	69	69
query38	995	838	786	786
query39	494	485	474	474
query39_1	478	475	454	454
query40	209	88	74	74
query41	56	52	53	52
query42	76	74	70	70
query43	243	239	214	214
query44	1033	547	559	547
query45	111	108	99	99
query46	780	811	500	500
query47	784	761	704	704
query48	301	307	242	242
query49	534	238	178	178
query50	721	264	195	195
query51	7870	7947	8049	7947
query52	76	69	61	61
query53	196	200	144	144
query54	223	171	149	149
query55	71	62	54	54
query56	195	187	172	172
query57	781	674	605	605
query58	208	154	165	154
query59	1213	1241	1130	1130
query60	246	178	171	171
query61	125	121	112	112
query62	352	205	177	177
query63	168	141	144	141
query64	2710	768	606	606
query65	1563	1605	1603	1603
query66	1879	277	204	204
query67	10060	9830	9822	9822
query68	3042	1142	753	753
query69	358	221	192	192
query70	674	625	614	614
query71	255	172	164	164
query72	2407	1766	1608	1608
query73	675	613	343	343
query74	2006	1213	1142	1142
query75	1195	1099	971	971
query76	2389	741	553	553
query77	262	263	220	220
query78	3767	3608	3183	3183
query79	2758	850	576	576
query80	1611	334	281	281
query81	507	154	138	138
query82	649	124	100	100
query83	320	211	190	190
query84	295	112	91	91
query85	831	349	300	300
query86	400	174	171	171
query87	1028	951	904	904
query88	2887	2105	2113	2105
query89	283	204	177	177
query90	2013	132	135	132
query91	133	121	103	103
query92	81	70	74	70
query93	1858	1077	708	708
query94	668	277	230	230
query95	542	268	292	268
query96	836	599	258	258
query97	1057	1088	1015	1015
query98	157	139	133	133
query99	420	350	306	306
Total cold run time: 179633 ms
Total hot run time: 82324 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 14.71 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 360056b408e357aab9e046172a7b8637746855b4, data reload: false
query1	0.01	0.01	0.00
query2	0.08	0.04	0.03
query3	0.24	0.11	0.11
query4	1.60	0.10	0.09
query5	0.18	0.16	0.16
query6	1.26	0.66	0.71
query7	0.04	0.00	0.00
query8	0.05	0.03	0.02
query9	0.28	0.21	0.23
query10	0.37	0.35	0.35
query11	0.16	0.11	0.12
query12	0.15	0.13	0.12
query13	0.30	0.30	0.32
query14	0.46	0.45	0.47
query15	0.36	0.35	0.35
query16	0.22	0.20	0.24
query17	0.65	0.70	0.73
query18	0.19	0.16	0.18
query19	1.19	1.19	1.17
query20	0.02	0.01	0.01
query21	15.44	0.15	0.12
query22	5.07	0.04	0.04
query23	16.18	0.25	0.11
query24	2.97	0.32	0.23
query25	0.12	0.05	0.03
query26	0.74	0.19	0.14
query27	0.03	0.04	0.03
query28	3.56	0.58	0.27
query29	12.45	3.19	2.57
query30	0.25	0.11	0.10
query31	2.76	0.37	0.16
query32	3.52	0.32	0.23
query33	1.43	1.42	1.42
query34	15.38	2.18	1.80
query35	1.75	1.73	1.75
query36	0.46	0.30	0.29
query37	0.06	0.04	0.04
query38	0.04	0.04	0.04
query39	0.03	0.02	0.02
query40	0.11	0.08	0.07
query41	0.07	0.03	0.03
query42	0.03	0.02	0.02
query43	0.04	0.02	0.03
Total cold run time: 90.3 s
Total hot run time: 14.71 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 81.25% (13/16) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 54.17% (13/24) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 3.68% (7/190) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 3.47% (7/202) 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 2.79% (7/251) 🎉
Increment coverage report
Complete coverage report

@HappenLee
HappenLee merged commit de9ed8e into apache:masterSep 4, 2026
36 of 37 checks passed
@linrrzqqq
linrrzqqq deleted the reject-var-args-udf branch September 4, 2026 04:18
github-actionsBot pushed a commit that referenced this pull request Sep 4, 2026
User-defined functions exposed variadic DDL metadata without reliable
end-to-end support. Reject variadic declarations during `CREATE
FUNCTION` analysis for scalar, aggregate, table, and alias functions
while retaining variadic signature parsing for `DROP`, `SHOW`, and
historical metadata compatibility.
before:
```sql
CREATE FUNCTION py_add(INT, INT, ...)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "evaluate",
"runtime_version" = "3.12.11",
"volatility" = "immutable"
)
AS $$
def evaluate(a, b, c):
return a + b + c
$$;
SELECT py_add(1, 2, 3);
-- ERROR 1105 (HY000): errCode = 2, detailMessage = Index 2 out of bounds for length 2
```
In `PythonUdfBuilder.java:82`:
```java
public Pair<PythonUdf, PythonUdf> build(String name, List<?> arguments) {
// exprs = (1, 2, 3), size = 3
// argTypes = [INT, INT], size = 2
List<Expression> exprs = arguments.stream().map(Expression.class::cast).collect(Collectors.toList());
List<DataType> argTypes = udf.getSignatures().get(0).argumentsTypes;
List<Expression> processedExprs = Lists.newArrayList();
for (int i = 0; i < exprs.size(); ++i) {
// when i = 2, argTypes.get(2), err occur!
processedExprs.add(TypeCoercionUtils.castIfNotSameType(exprs.get(i), argTypes.get(i)));
}
return Pair.ofSame(udf.withFreshVolatileIdentity().withChildren(processedExprs));
}
```
now:
```sql
CREATE FUNCTION py_add(INT, INT, ...)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "evaluate",
"runtime_version" = "3.12.11",
"volatility" = "immutable"
)
AS $$
def evaluate(a, b, c):
return a + b + c
$$;
-- ERROR 1105 (HY000): errCode = 2, detailMessage = mismatched input ',' expecting ')'(line 1, pos 31)
```
### Release note
Reject variadic declarations for user-defined functions.
### Check List (For Author)
- Test: Unit Test
- ./run-fe-ut.sh --run org.apache.doris.catalog.CreateFunctionTest
- Behavior changed: Yes. Variadic user-defined function declarations are
rejected during analysis.
- Does this need documentation:
apache/doris-website#4103
yiguolei pushed a commit that referenced this pull request Sep 5, 2026
User-defined functions exposed variadic DDL metadata without reliable
end-to-end support. Reject variadic declarations during `CREATE
FUNCTION` analysis for scalar, aggregate, table, and alias functions
while retaining variadic signature parsing for `DROP`, `SHOW`, and
historical metadata compatibility.
before:
```sql
CREATE FUNCTION py_add(INT, INT, ...)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "evaluate",
"runtime_version" = "3.12.11",
"volatility" = "immutable"
)
AS $$
def evaluate(a, b, c):
return a + b + c
$$;
SELECT py_add(1, 2, 3);
-- ERROR 1105 (HY000): errCode = 2, detailMessage = Index 2 out of bounds for length 2
```
In `PythonUdfBuilder.java:82`:
```java
public Pair<PythonUdf, PythonUdf> build(String name, List<?> arguments) {
// exprs = (1, 2, 3), size = 3
// argTypes = [INT, INT], size = 2
List<Expression> exprs = arguments.stream().map(Expression.class::cast).collect(Collectors.toList());
List<DataType> argTypes = udf.getSignatures().get(0).argumentsTypes;
List<Expression> processedExprs = Lists.newArrayList();
for (int i = 0; i < exprs.size(); ++i) {
// when i = 2, argTypes.get(2), err occur!
processedExprs.add(TypeCoercionUtils.castIfNotSameType(exprs.get(i), argTypes.get(i)));
}
return Pair.ofSame(udf.withFreshVolatileIdentity().withChildren(processedExprs));
}
```
now:
```sql
CREATE FUNCTION py_add(INT, INT, ...)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"symbol" = "evaluate",
"runtime_version" = "3.12.11",
"volatility" = "immutable"
)
AS $$
def evaluate(a, b, c):
return a + b + c
$$;
-- ERROR 1105 (HY000): errCode = 2, detailMessage = mismatched input ',' expecting ')'(line 1, pos 31)
```
### Release note
Reject variadic declarations for user-defined functions.
### Check List (For Author)
- Test: Unit Test
- ./run-fe-ut.sh --run org.apache.doris.catalog.CreateFunctionTest
- Behavior changed: Yes. Variadic user-defined function declarations are
rejected during analysis.
- Does this need documentation:
apache/doris-website#4103
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@linrrzqqq@hello-stephen@starocean999@zhangstar333@HappenLee