feat(isthmus): configurable unquoted identifier casing via the ConverterProvider builder - #1037
Open
nielspardon wants to merge 3 commits into
Open
Conversation
Thread a single ConverterProvider through both the CREATE-statement parsing that builds the Calcite catalog and the query parsing, so one parser configuration (the provider's getSqlParserConfig()) is applied consistently end-to-end. Add a shared ConverterProvider.DEFAULT instance for the no-arg parsing entry points. The SqlParser.Config-based parsing entry points (public since v0.94.0) are removed in favour of passing a ConverterProvider: - SubstraitSqlStatementParser.parseStatements(String, SqlParser.Config) - SubstraitSqlToCalcite.convertQueries(..., SqlParser.Config) overloads SqlToSubstrait.convert(String, CatalogReader, SqlDialect) is deprecated: the SqlDialect argument no longer influences parsing and it delegates to convert(String, CatalogReader). Parser configuration is customised by subclassing ConverterProvider and overriding getSqlParserConfig().
Add ConverterProvider.builder() and a nested Builder that carries a full Calcite SqlParser.Config alongside extensions, typeFactory, the function converters, typeConverter and executionBehavior. getSqlParserConfig() now returns the stored config, and DEFAULT_SQL_PARSER_CONFIG (the isthmus default: DEFAULT + TO_UPPER + SqlDdlParserImpl.FACTORY + LENIENT) is exposed as the recommended base for customization. Make the builder the sole public construction path: a protected ConverterProvider(Builder) is the stable subclassing seam (build() and the delegating public constructors route through it), and all public constructors are @deprecated in favour of builder(). DEFAULT is now builder().build(). The two subclasses, DynamicConverterProvider and AutomaticDynamicFunctionMappingConverterProvider, gain a public (Builder) constructor calling super(builder) and deprecate their positional constructors. Callers, tests and examples are migrated to builder()/DEFAULT.
…builder Add Builder.unquotedCasing(Casing) as a convenience for the common case of overriding only the unquoted-identifier casing, layered on top of the current parser configuration. Route the isthmus CLI's --unquotedcasing option through it, use it in the FromSql example (Casing.UNCHANGED, matching the lower-case identifiers in the example's CREATE TABLE statements), and add UnquotedCasingTest covering the default casing, the convenience for each Casing, the full sqlParserConfig path (retaining LENIENT conformance), and end-to-end NamedScan naming (EMPLOYEES under TO_UPPER, employees under UNCHANGED).
This was referenced Jul 24, 2026
nielspardon
force-pushed
the
feat/converterprovider-builder-api
branch
from
July 25, 2026 06:41
b065c1c to
defa554
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Add
ConverterProvider.Builder.unquotedCasing(Casing)— a convenience for the common case ofoverriding only the unquoted-identifier casing, layered on top of the current parser configuration
(equivalent to
sqlParserConfig(current.withUnquotedCasing(casing))).Motivation
TO_UPPERis the ANSI/Calcite default, but a large class of engines fold unquoted identifiers tolower-case or preserve them (Presto/Trino, Spark, DuckDB, Postgres), so a plan generated from
SELECT … FROM orderscomes out with aNamedScannamedORDERSthat those engines can't resolve.This gives library and CLI users a one-liner to control it, while the full
sqlParserConfig(...)path (previous PR) remains for anything else.
Changes
ConverterProvider.Builder.unquotedCasing(Casing)convenience.IsthmusEntryPoint(CLI):--unquotedcasingnow maps tobuilder().unquotedCasing(...).FromSqlexample: usesbuilder().unquotedCasing(Casing.UNCHANGED)(matching the lower-caseidentifiers in its
CREATE TABLEstatements), replacing the deprecatedconvert(sql, catalog, SqlDialect)call.UnquotedCasingTest: default casing; the convenience for eachCasing; the fullsqlParserConfig(...)path (verifyingLENIENTconformance is retained); and end-to-end — a planbuilt from
CREATE TABLE employees … / SELECT … FROM employeesproduces aNamedScannamedEMPLOYEESunderTO_UPPERandemployeesunderUNCHANGED.Split of #983
Per @vbarua's request, #983 is split into three stacked PRs (review/merge bottom-up):
ConverterProviderinto the SQL statement parsersConverterProviderbuilderStacked on #1036 (and #1035) — review/merge those first.
🤖 Generated with AI