[
https://issues.apache.org/jira/browse/CALCITE-7724?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18105397#comment-18105397
]
Mihai Budiu commented on CALCITE-7724:
--------------------------------------
This is exactly the problem I was mentioning in CALCITE-7685
> SqlUtil#lookupSubjectRoutines rejects a valid operator when getFunctionKind()
> remaps its kind and two candidates share a name
> -----------------------------------------------------------------------------------------------------------------------------
>
> Key: CALCITE-7724
> URL: https://issues.apache.org/jira/browse/CALCITE-7724
> Project: Calcite
> Issue Type: Bug
> Components: core
> Affects Versions: 1.38.0
> Reporter: Sean Broeder
> Assignee: Sean Broeder
> Priority: Major
>
> Summary:
> Any call to a function whose SqlKind is remapped by SqlKind.getFunctionKind()
> — POSITION and the newly-dedicated CHAR_LENGTH among roughly 90 others — can
> fail validation with “No match found for function signature”, even though the
> exact operator being called is registered and otherwise perfectly valid. The
> failure only appears once a second candidate for the same operator name
> reaches the routine-resolution pipeline’s fourth pass, which happens whenever
> an operator table (or a chain of them) contributes more than one entry for
> that name.
> Reproduction:
> Minimal repro: chain the standard operator table with itself, which is enough
> to produce the two-candidate precondition, then validate an ordinary call to
> POSITION or CHAR_LENGTH.
> {code:java}
> // SqlValidatorTest.java
> final SqlOperatorTable duplicated =
> SqlOperatorTables.chain(SqlStdOperatorTable.instance(),
> SqlStdOperatorTable.instance());
> expr("position('mouse' in 'house')").withOperatorTable(duplicated).ok();
> expr("char_length('string')").withOperatorTable(duplicated).ok();
> {code}
> Results:
> {code:java}
> org.opentest4j.AssertionFailedError: Validator threw unexpected exception;
> query [values (position('mouse' in 'house'))];
> exception [No match found for function signature POSITION(<CHARACTER>,
> <CHARACTER>)]
> {code}
> The same query against a single, non-duplicated operator table validates
> without issue — matching the fact that Calcite’s own existing
> testPosition()/testCharLength() tests still pass today. The bug is real but
> silent until a second contributor for the name is present, which is exactly
> what happens in any composite operator table assembled from more than one
> source (the situation that surfaced it).
> Root Cause:
> SqlUtil.lookupSubjectRoutines narrows candidates in four passes: by name, by
> parameter count, by parameter type, and finally — only once at least two
> candidates remain — by SqlKind:
> {code:java}
> private static Iterator<SqlOperator> filterOperatorRoutinesByKind(
> Iterator<SqlOperator> routines, final SqlKind sqlKind) {
> return Iterators.filter(routines,
> operator -> requireNonNull(operator, "operator")
> .getKind().getFunctionKind() == sqlKind);
> }
> {code}
> The candidate’s kind is passed through getFunctionKind() — a 1.38 addition
> whose own doc comment says: “If this kind represents a non-standard function,
> return OTHER_FUNCTION, otherwise return this.” The requested kind, sqlKind,
> is compared as-is — it is the call’s own, already-bound kind, never run
> through the same mapping. For POSITION, that requested kind is
> SqlKind.POSITION (its x IN y syntax forces the parser to bind the real
> operator eagerly, before any type-driven resolution runs) — so the comparison
> reduces to OTHER_FUNCTION == POSITION. Always false. The operator fails to
> match itself.
> The asymmetry is the whole bug: one side of the comparison is normalized, the
> other isn’
> Proposed Fix
> Map both sides of the comparison through getFunctionKind(). For every kind
> the switch doesn’t remap, this is a no-op — identical to today’s behavior and
> to every version before 1.38. For the ~90 kinds it does remap, both sides now
> land in the same bucket, restoring the ability for an operator to match
> itself.
> {code:java}
> @@ SqlUtil.java
> private static Iterator<SqlOperator> filterOperatorRoutinesByKind(
> Iterator<SqlOperator> routines, final SqlKind sqlKind) {
> + final SqlKind sqlFunctionKind = sqlKind.getFunctionKind();
> return Iterators.filter(routines,
> - operator -> requireNonNull(operator,
> "operator").getKind().getFunctionKind() == sqlKind);
> + operator -> requireNonNull(operator,
> "operator").getKind().getFunctionKind() == sqlFunctionKind);
> }
> {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)