Skip to content

isthmus: REVERSE and INITCAP have no FunctionMappings entry, so neither converts from Calcite #1222

Description

@nielspardon

REVERSE and INITCAP exist on both sides of the Calcite ⇄ Substrait boundary, yet neither converts:

SELECT REVERSE(l_comment) FROM lineitem
  -> IllegalArgumentException: Unable to convert call REVERSE(varchar<44>?).
SELECT INITCAP(l_comment) FROM lineitem
  -> IllegalArgumentException: Unable to convert call INITCAP(varchar<44>?).

Both are declared in functions_string.yaml (spec v0.102.0) with character implementations, and both have a Calcite operator — SqlLibraryOperators.REVERSE and SqlStdOperatorTable.INITCAP. What is missing is the entry that joins them: FunctionConverter builds its signatures map exclusively from the FunctionMappings sig list, with no fall-back that matches an operator to a same-named Substrait function, so an operator absent from that table simply has no FunctionFinder and every call to it fails.

AutomaticDynamicFunctionMappingConverterProvider does not cover this. It synthesises Calcite operators for unmapped Substrait functions, which is the Substrait → Calcite direction; a real Calcite operator going the other way still needs a finder, and both queries above fail identically under it.

The fix is two entries:

s(SqlLibraryOperators.REVERSE, "reverse"),
s(SqlStdOperatorTable.INITCAP, "initcap"),

Passing exactly those to ScalarFunctionConverter as additionalSignatures makes both queries convert, so nothing beyond the mapping is missing — the yaml signatures already match a varchar argument.

Two notes for whoever picks this up. 42 of the 126 scalar function names have no FunctionFinder, so this is a sample of a wider gap rather than the whole of it; reverse and initcap are called out because both were confirmed to have a Calcite counterpart whose signature already lines up, which is not true of every name on that list. And if you take an inventory yourself, trim/ltrim/rtrim will look unbound when they are not: all three map onto the single Calcite TRIM operator and signatures is keyed by operator, so two of the three are overwritten — inert here, because TrimFunctionMapper handles TRIM ahead of the finder path.

#1012 would restructure this mapping wholesale; these two entries are worth adding independently of it. #781 is the earlier instance of the same shape, for ANY_VALUE.

Found while reviewing #1169. Checked on main @ 85c03c7.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions