Skip to content

feat(isthmus)!: inject ConverterProvider into the SQL statement parsers#1035

Merged
nielspardon merged 3 commits into
mainfrom
feat/inject-converterprovider-parsers
Jul 25, 2026
Merged

feat(isthmus)!: inject ConverterProvider into the SQL statement parsers#1035
nielspardon merged 3 commits into
mainfrom
feat/inject-converterprovider-parsers

Conversation

@nielspardon

@nielspardon nielspardon commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

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. Previously the CREATE-statement path and
the query path could apply different parser settings, so the identifier casing used to build a
NamedScan name could disagree with the casing used to resolve it in a query.

Also adds a shared ConverterProvider.DEFAULT instance, used as the default for the no-arg parsing
entry points.

Breaking changes

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(String, CatalogReader, SqlParser.Config) and the
    5-arg SqlParser.Config overload

SqlToSubstrait.convert(String, CatalogReader, SqlDialect) is deprecated: the SqlDialect
argument no longer influences parsing and it delegates to convert(String, CatalogReader). Until the
builder arrives (next PR in the stack), parser configuration is customised by subclassing
ConverterProvider and overriding getSqlParserConfig().

Notable changes

Class Change
SubstraitSqlStatementParser parseStatements(String, ConverterProvider) owns the SqlParser instantiation; SqlParser.Config overload removed
SubstraitSqlToCalcite New convertQueries(sql, catalog, ConverterProvider[, operatorTable]); internal overloads route through ConverterProvider; SqlParser.Config overloads removed
SubstraitCreateStatementParser New processCreateStatements(ConverterProvider, …) / processCreateStatementsToCatalog(ConverterProvider, …) overloads — same parser config as query parsing
SqlToSubstrait convert(sql, catalog) uses the ConverterProvider path; convert(sql, catalog, SqlDialect) @Deprecated (arg ignored)
SqlExpressionToSubstrait Routes CREATE parsing through the provider
ConverterProvider Adds the shared DEFAULT instance
IsthmusEntryPoint (CLI) Threads its provider through catalog and query parsing consistently

Split of #983

Per @vbarua's request, #983 is split into three stacked PRs (review/merge bottom-up):

  1. feat(isthmus)!: inject ConverterProvider into the SQL statement parsers #1035 — inject ConverterProvider into the SQL statement parsers (this PR)
  2. feat(isthmus): introduce a ConverterProvider builder #1036 — introduce the ConverterProvider builder
  3. feat(isthmus): configurable unquoted identifier casing via the ConverterProvider builder #1037 — configurable unquoted identifier casing via the builder

Replaces #983.

🤖 Generated with AI

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().
ConverterProvider converterProvider,
SqlOperatorTable operatorTable)
throws SqlParseException {
SqlValidator validator = new SubstraitSqlValidator(catalogReader, operatorTable);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Now that we're injecting the ConverterProvider, should we use the operatorTable available in ConverterProvider#getSqlOperator here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

good catch. pushed a commit that refactors this part

Comment thread isthmus/src/main/java/io/substrait/isthmus/SqlToSubstrait.java
* @return list of {@link SubstraitTable}s generated from the CREATE statements
* @throws SqlParseException if parsing fails or statements are invalid
*/
public static List<SubstraitTable> processCreateStatements(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

minor: would it make sense to move this under the original processCreateStatement from which it was extracted? Would also have made the diff easier to review.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

pushed a commit that moves the method as suggested

rootSchema,
defaultSchema,
SubstraitTypeSystem.TYPE_FACTORY,
SqlConverterBase.CONNECTION_CONFIG);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(not for this PR) I wonder if it would make sense to get the RelTypeFactory and SqlConverterBase from the ConverterProvider as well. The former is already configured in the ConverterProvider.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

can probably include that in the builder PR

…ibling

Relocate processCreateStatements(ConverterProvider, String) to sit directly
beneath the single-argument processCreateStatements(String) that delegates to
it, grouping the two overloads together. Pure code move; no behavior change.
The SQL statement converters now derive the operator table from the injected
ConverterProvider rather than accepting it as a separate parameter, so one
provider configures both parsing and the valid operators end-to-end:

- convertQuery/convertQueries build the validator with
  converterProvider.getSqlOperatorTable(), dropping the redundant
  convertQueries(ConverterProvider, SqlOperatorTable) overload and the
  SqlToSubstrait.operatorTable field that fed it.
- Add convertQuery(String, CatalogReader, ConverterProvider) mirroring the
  plural overload, and migrate callers off passing the operator table by hand.
- SqlExpressionToSubstrait's extended-expression validator now honors the
  provider's operator table too, matching the query path.
- Memoize DynamicConverterProvider.getSqlOperatorTable() so reusing a converter
  across conversions no longer rebuilds the dynamic operator table each call.
- Deprecate the raw-SqlOperatorTable convertQuery/convertQueries overloads in
  favour of the ConverterProvider ones.
@nielspardon
nielspardon requested a review from vbarua July 24, 2026 18:18

@vbarua vbarua left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

I'll leave the merging to you. The PR message is a bit of mouthful for a commit message, especially because we'll try and generate the release notes from it as well.

@nielspardon
nielspardon merged commit e018342 into main Jul 25, 2026
12 checks passed
@nielspardon
nielspardon deleted the feat/inject-converterprovider-parsers branch July 25, 2026 06:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants