diff --git a/examples/isthmus-api/src/main/java/io/substrait/examples/FromSql.java b/examples/isthmus-api/src/main/java/io/substrait/examples/FromSql.java index 17973b683..15f179f60 100644 --- a/examples/isthmus-api/src/main/java/io/substrait/examples/FromSql.java +++ b/examples/isthmus-api/src/main/java/io/substrait/examples/FromSql.java @@ -1,6 +1,7 @@ package io.substrait.examples; import io.substrait.examples.IsthmusAppExamples.Action; +import io.substrait.isthmus.ConverterProvider; import io.substrait.isthmus.SqlToSubstrait; import io.substrait.isthmus.sql.SubstraitCreateStatementParser; import io.substrait.plan.Plan; @@ -10,8 +11,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import org.apache.calcite.avatica.util.Casing; import org.apache.calcite.prepare.CalciteCatalogReader; -import org.apache.calcite.sql.SqlDialect; import org.apache.calcite.sql.parser.SqlParseException; /** @@ -22,7 +23,7 @@ *
1. Create a fully typed schema for the inputs. Within a SQL context this represents the CREATE * TABLE commands, which need to be converted to a Calcite Schema. * - *
2. Parse the SQL query to convert (in the source SQL dialect). + *
2. Parse the SQL query to convert. * *
3. Convert the SQL query to Calcite Relations. * @@ -49,22 +50,27 @@ public void run(final String[] args) { "test_result" varchar(15),"test_mileage" int, "postcode_area" varchar(15)); """); + // The unquoted identifier casing applied while parsing is configurable via a + // ConverterProvider. The same provider is used for both the schema and the query so that + // identifier casing stays consistent end-to-end. Casing.UNCHANGED preserves identifiers as + // written, matching the lower-case names used in the CREATE TABLE statements above. + final ConverterProvider converterProvider = + ConverterProvider.builder().unquotedCasing(Casing.UNCHANGED).build(); + final CalciteCatalogReader catalogReader = - SubstraitCreateStatementParser.processCreateStatementsToCatalog(createSqlStatements); + SubstraitCreateStatementParser.processCreateStatementsToCatalog( + converterProvider, createSqlStatements); - // Query that needs to be converted; again this could be in a variety of SQL - // dialects + // Query that needs to be converted final String sqlQuery = """ SELECT vehicles.colour, count(*) as colourcount FROM vehicles INNER JOIN tests ON vehicles.vehicle_id=tests.vehicle_id WHERE tests.test_result = 'P' GROUP BY vehicles.colour ORDER BY count(*) """; - final SqlToSubstrait sqlToSubstrait = new SqlToSubstrait(); - // choose DuckDB as an example dialect - final SqlDialect dialect = SqlDialect.DatabaseProduct.DUCKDB.getDialect(); - final Plan substraitPlan = sqlToSubstrait.convert(sqlQuery, catalogReader, dialect); + final SqlToSubstrait sqlToSubstrait = new SqlToSubstrait(converterProvider); + final Plan substraitPlan = sqlToSubstrait.convert(sqlQuery, catalogReader); // Create the proto plan to display to stdout - as it has a better format final PlanProtoConverter planToProto = new PlanProtoConverter(); diff --git a/isthmus-cli/src/main/java/io/substrait/isthmus/cli/IsthmusEntryPoint.java b/isthmus-cli/src/main/java/io/substrait/isthmus/cli/IsthmusEntryPoint.java index 03d4fef2b..8c4a40098 100644 --- a/isthmus-cli/src/main/java/io/substrait/isthmus/cli/IsthmusEntryPoint.java +++ b/isthmus-cli/src/main/java/io/substrait/isthmus/cli/IsthmusEntryPoint.java @@ -15,7 +15,6 @@ import java.util.concurrent.Callable; import org.apache.calcite.avatica.util.Casing; import org.apache.calcite.prepare.Prepare; -import org.apache.calcite.sql.parser.SqlParser; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.Option; @@ -60,15 +59,6 @@ enum OutputFormat { description = "Calcite's casing policy for unquoted identifiers: ${COMPLETION-CANDIDATES}") private Casing unquotedCasing = Casing.TO_UPPER; - private ConverterProvider converterProvider() { - return new ConverterProvider() { - @Override - public SqlParser.Config getSqlParserConfig() { - return super.getSqlParserConfig().withUnquotedCasing(unquotedCasing); - } - }; - } - /** * Standard Java Main method invoked by the isthmus CLI command. * @@ -96,16 +86,17 @@ public static void main(String... args) { @Override public Integer call() throws Exception { + ConverterProvider provider = ConverterProvider.builder().unquotedCasing(unquotedCasing).build(); // Isthmus image is parsing SQL Expression if that argument is defined if (sqlExpressions != null) { - SqlExpressionToSubstrait converter = new SqlExpressionToSubstrait(converterProvider()); + SqlExpressionToSubstrait converter = new SqlExpressionToSubstrait(provider); ExtendedExpression extendedExpression = converter.convert(sqlExpressions, createStatements); printMessage(extendedExpression); } else { // by default Isthmus image are parsing SQL Query - SqlToSubstrait converter = new SqlToSubstrait(converterProvider()); + SqlToSubstrait converter = new SqlToSubstrait(provider); Prepare.CatalogReader catalog = SubstraitCreateStatementParser.processCreateStatementsToCatalog( - createStatements.toArray(String[]::new)); + provider, createStatements); Plan plan = new PlanProtoConverter().toProto(converter.convert(sql, catalog)); printMessage(plan); } diff --git a/isthmus/src/main/java/io/substrait/isthmus/ConverterProvider.java b/isthmus/src/main/java/io/substrait/isthmus/ConverterProvider.java index 8cd3731b3..95c602880 100644 --- a/isthmus/src/main/java/io/substrait/isthmus/ConverterProvider.java +++ b/isthmus/src/main/java/io/substrait/isthmus/ConverterProvider.java @@ -40,21 +40,52 @@ * *
It is consumed by all conversion classes as their primary source of configuration. * - *
The no argument constructor {@link #ConverterProvider()} provides reasonable system defaults. + *
The no argument constructor {@link #ConverterProvider()} provides reasonable system defaults, + * and {@link #DEFAULT} is a shared instance of it. * - *
Other constructors allow for further customization of conversion behaviours. + *
For customized conversion behaviour — including supplying a full Calcite {@link + * SqlParser.Config} for SQL parsing — use the {@link #builder()}. * *
More in-depth customization can be achieved by extending this class, as is done in {@link * DynamicConverterProvider}. */ public class ConverterProvider { + /** + * The default Calcite {@link SqlParser.Config} used by isthmus: {@link SqlParser.Config#DEFAULT} + * with {@link Casing#TO_UPPER} unquoted-identifier casing, the {@link SqlDdlParserImpl} parser + * factory (so {@code CREATE TABLE} statements parse), and {@link SqlConformanceEnum#LENIENT} + * conformance. + * + *
This is the recommended starting point for a customized parser configuration: derive from it + * with Calcite's {@code withXxx} methods and pass the result to {@link + * Builder#sqlParserConfig(SqlParser.Config)}, e.g. {@code + * DEFAULT_SQL_PARSER_CONFIG.withUnquotedCasing(Casing.UNCHANGED)}. + */ + public static final SqlParser.Config DEFAULT_SQL_PARSER_CONFIG = + SqlParser.Config.DEFAULT + .withUnquotedCasing(Casing.TO_UPPER) + .withParserFactory(SqlDdlParserImpl.FACTORY) + .withConformance(SqlConformanceEnum.LENIENT); + + /** + * A shared default {@link ConverterProvider} instance using all system defaults. Equivalent to + * {@code new ConverterProvider()} but avoids redundant construction at every call site. + * + *
This instance is safe to share because {@link ConverterProvider} is effectively immutable + * after construction — all fields are set only in constructors. + */ + public static final ConverterProvider DEFAULT = new ConverterProvider(); + /** The Calcite type factory used for creating and managing data types. */ protected RelDataTypeFactory typeFactory; /** The collection of Substrait extensions (functions and types) available for conversion. */ protected final SimpleExtension.ExtensionCollection extensions; + /** The Calcite SQL parser configuration, controlling parsing behaviour like identifier casing. */ + protected final SqlParser.Config sqlParserConfig; + /** Converter for Substrait scalar functions. */ protected ScalarFunctionConverter scalarFunctionConverter; @@ -101,7 +132,9 @@ public ConverterProvider( new ScalarFunctionConverter(extensions.scalarFunctions(), typeFactory), new AggregateFunctionConverter(extensions.aggregateFunctions(), typeFactory), new WindowFunctionConverter(extensions.windowFunctions(), typeFactory), - TypeConverter.DEFAULT); + TypeConverter.DEFAULT, + createDefaultExecutionBehavior(), + DEFAULT_SQL_PARSER_CONFIG); } /** @@ -113,7 +146,10 @@ public ConverterProvider( * @param afc the aggregate function converter to use * @param wfc the window function converter to use * @param tc the type converter to use + * @deprecated Use {@link #builder()} instead; the growing set of components is more readably + * configured through the builder than through this positional constructor. */ + @Deprecated public ConverterProvider( RelDataTypeFactory typeFactory, SimpleExtension.ExtensionCollection extensions, @@ -121,7 +157,15 @@ public ConverterProvider( AggregateFunctionConverter afc, WindowFunctionConverter wfc, TypeConverter tc) { - this(typeFactory, extensions, sfc, afc, wfc, tc, createDefaultExecutionBehavior()); + this( + typeFactory, + extensions, + sfc, + afc, + wfc, + tc, + createDefaultExecutionBehavior(), + DEFAULT_SQL_PARSER_CONFIG); } /** @@ -134,7 +178,10 @@ public ConverterProvider( * @param wfc the window function converter to use * @param tc the type converter to use * @param executionBehavior the execution behavior to use for plans + * @deprecated Use {@link #builder()} instead; the growing set of components is more readably + * configured through the builder than through this positional constructor. */ + @Deprecated public ConverterProvider( RelDataTypeFactory typeFactory, SimpleExtension.ExtensionCollection extensions, @@ -143,6 +190,31 @@ public ConverterProvider( WindowFunctionConverter wfc, TypeConverter tc, Plan.ExecutionBehavior executionBehavior) { + this(typeFactory, extensions, sfc, afc, wfc, tc, executionBehavior, DEFAULT_SQL_PARSER_CONFIG); + } + + /** + * Master constructor: assigns all components directly. Used by the {@link Builder} and the + * delegating public constructors. + * + * @param typeFactory the Calcite type factory to use + * @param extensions the Substrait extension collection to use + * @param sfc the scalar function converter to use + * @param afc the aggregate function converter to use + * @param wfc the window function converter to use + * @param tc the type converter to use + * @param executionBehavior the execution behavior to use for plans + * @param sqlParserConfig the Calcite SQL parser configuration to use + */ + private ConverterProvider( + RelDataTypeFactory typeFactory, + SimpleExtension.ExtensionCollection extensions, + ScalarFunctionConverter sfc, + AggregateFunctionConverter afc, + WindowFunctionConverter wfc, + TypeConverter tc, + Plan.ExecutionBehavior executionBehavior, + SqlParser.Config sqlParserConfig) { this.typeFactory = typeFactory; this.extensions = extensions; this.scalarFunctionConverter = sfc; @@ -150,6 +222,7 @@ public ConverterProvider( this.windowFunctionConverter = wfc; this.typeConverter = tc; this.executionBehavior = executionBehavior; + this.sqlParserConfig = sqlParserConfig; } /** @@ -169,13 +242,14 @@ private static Plan.ExecutionBehavior createDefaultExecutionBehavior() { * {@link SqlParser.Config} is a Calcite class which controls SQL parsing behaviour like * identifier casing. * + *
Defaults to {@link #DEFAULT_SQL_PARSER_CONFIG}. Provide a custom configuration via {@link + * Builder#sqlParserConfig(SqlParser.Config)} (or the {@link Builder#unquotedCasing(Casing)} + * convenience), or override this method in a subclass for fully dynamic behaviour. + * * @return the SQL parser configuration */ public SqlParser.Config getSqlParserConfig() { - return SqlParser.Config.DEFAULT - .withUnquotedCasing(Casing.TO_UPPER) - .withParserFactory(SqlDdlParserImpl.FACTORY) - .withConformance(SqlConformanceEnum.LENIENT); + return sqlParserConfig; } /** @@ -415,12 +489,189 @@ public TypeConverter getTypeConverter() { * *
The default execution behavior uses {@link * Plan.ExecutionBehavior.VariableEvaluationMode#PER_PLAN}, which evaluates variables once per - * plan execution. This can be customized by providing a different execution behavior through the - * constructor. + * plan execution. This can be customized via {@link + * Builder#executionBehavior(Plan.ExecutionBehavior)}. * * @return the execution behavior to use when creating plans */ public Plan.ExecutionBehavior getExecutionBehavior() { return executionBehavior; } + + /** + * Creates a new {@link Builder} for configuring a {@link ConverterProvider}. + * + *
The builder starts from the same system defaults as {@link #ConverterProvider()} and lets + * callers override individual components — most notably the Calcite {@link SqlParser.Config} used + * for SQL parsing, via {@link Builder#sqlParserConfig(SqlParser.Config)} for full control or + * {@link Builder#unquotedCasing(Casing)} for the common casing-only case. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Fluent builder for {@link ConverterProvider}. + * + *
Unset components fall back to the same system defaults as {@link + * ConverterProvider#ConverterProvider()}. The scalar, aggregate and window function converters, + * if not set explicitly, are derived from the configured {@link + * #extensions(SimpleExtension.ExtensionCollection) extensions} and {@link + * #typeFactory(RelDataTypeFactory) type factory} when {@link #build()} is called. + */ + public static class Builder { + private SimpleExtension.ExtensionCollection extensions = + DefaultExtensionCatalog.DEFAULT_COLLECTION; + private RelDataTypeFactory typeFactory = SubstraitTypeSystem.TYPE_FACTORY; + private ScalarFunctionConverter scalarFunctionConverter; + private AggregateFunctionConverter aggregateFunctionConverter; + private WindowFunctionConverter windowFunctionConverter; + private TypeConverter typeConverter = TypeConverter.DEFAULT; + private Plan.ExecutionBehavior executionBehavior = createDefaultExecutionBehavior(); + private SqlParser.Config sqlParserConfig = DEFAULT_SQL_PARSER_CONFIG; + + /** + * Sets the Substrait extension collection to use. + * + * @param extensions the extension collection + * @return this builder + */ + public Builder extensions(SimpleExtension.ExtensionCollection extensions) { + this.extensions = extensions; + return this; + } + + /** + * Sets the Calcite type factory to use. + * + * @param typeFactory the type factory + * @return this builder + */ + public Builder typeFactory(RelDataTypeFactory typeFactory) { + this.typeFactory = typeFactory; + return this; + } + + /** + * Sets the scalar function converter. When left unset, it is derived from the configured + * extensions and type factory. + * + * @param scalarFunctionConverter the scalar function converter + * @return this builder + */ + public Builder scalarFunctionConverter(ScalarFunctionConverter scalarFunctionConverter) { + this.scalarFunctionConverter = scalarFunctionConverter; + return this; + } + + /** + * Sets the aggregate function converter. When left unset, it is derived from the configured + * extensions and type factory. + * + * @param aggregateFunctionConverter the aggregate function converter + * @return this builder + */ + public Builder aggregateFunctionConverter( + AggregateFunctionConverter aggregateFunctionConverter) { + this.aggregateFunctionConverter = aggregateFunctionConverter; + return this; + } + + /** + * Sets the window function converter. When left unset, it is derived from the configured + * extensions and type factory. + * + * @param windowFunctionConverter the window function converter + * @return this builder + */ + public Builder windowFunctionConverter(WindowFunctionConverter windowFunctionConverter) { + this.windowFunctionConverter = windowFunctionConverter; + return this; + } + + /** + * Sets the type converter. + * + * @param typeConverter the type converter + * @return this builder + */ + public Builder typeConverter(TypeConverter typeConverter) { + this.typeConverter = typeConverter; + return this; + } + + /** + * Sets the execution behavior for plans created by the resulting converter. + * + * @param executionBehavior the execution behavior + * @return this builder + */ + public Builder executionBehavior(Plan.ExecutionBehavior executionBehavior) { + this.executionBehavior = executionBehavior; + return this; + } + + /** + * Sets the full Calcite {@link SqlParser.Config} used for SQL parsing, replacing the default. + * + *
Use {@link ConverterProvider#DEFAULT_SQL_PARSER_CONFIG} as a starting point to retain + * isthmus' DDL parser factory and conformance while overriding individual settings. + * + * @param sqlParserConfig the parser configuration + * @return this builder + */ + public Builder sqlParserConfig(SqlParser.Config sqlParserConfig) { + this.sqlParserConfig = sqlParserConfig; + return this; + } + + /** + * Convenience for the common case of overriding only the unquoted-identifier casing, applied on + * top of the current {@link #sqlParserConfig(SqlParser.Config) parser configuration}. + * + *
Equivalent to {@code sqlParserConfig(currentConfig.withUnquotedCasing(unquotedCasing))}.
+ * Because it layers onto the current configuration, a subsequent {@link
+ * #sqlParserConfig(SqlParser.Config)} call replaces the whole configuration and discards the
+ * casing set here; set the full config first, then apply this convenience.
+ *
+ * @param unquotedCasing the casing to apply to unquoted SQL identifiers during parsing
+ * @return this builder
+ */
+ public Builder unquotedCasing(Casing unquotedCasing) {
+ this.sqlParserConfig = this.sqlParserConfig.withUnquotedCasing(unquotedCasing);
+ return this;
+ }
+
+ /**
+ * Builds a {@link ConverterProvider} from the configured components, deriving any unset
+ * function converters from the configured extensions and type factory.
+ *
+ * @return a new {@link ConverterProvider}
+ */
+ public ConverterProvider build() {
+ ScalarFunctionConverter sfc =
+ scalarFunctionConverter != null
+ ? scalarFunctionConverter
+ : new ScalarFunctionConverter(extensions.scalarFunctions(), typeFactory);
+ AggregateFunctionConverter afc =
+ aggregateFunctionConverter != null
+ ? aggregateFunctionConverter
+ : new AggregateFunctionConverter(extensions.aggregateFunctions(), typeFactory);
+ WindowFunctionConverter wfc =
+ windowFunctionConverter != null
+ ? windowFunctionConverter
+ : new WindowFunctionConverter(extensions.windowFunctions(), typeFactory);
+ return new ConverterProvider(
+ typeFactory,
+ extensions,
+ sfc,
+ afc,
+ wfc,
+ typeConverter,
+ executionBehavior,
+ sqlParserConfig);
+ }
+ }
}
diff --git a/isthmus/src/main/java/io/substrait/isthmus/SqlExpressionToSubstrait.java b/isthmus/src/main/java/io/substrait/isthmus/SqlExpressionToSubstrait.java
index 5c020d5d1..9637cd6fd 100644
--- a/isthmus/src/main/java/io/substrait/isthmus/SqlExpressionToSubstrait.java
+++ b/isthmus/src/main/java/io/substrait/isthmus/SqlExpressionToSubstrait.java
@@ -40,7 +40,7 @@ public class SqlExpressionToSubstrait extends SqlConverterBase {
/** Creates a converter with default configuration. */
public SqlExpressionToSubstrait() {
- this(new ConverterProvider());
+ this(ConverterProvider.DEFAULT);
}
/**
@@ -202,7 +202,7 @@ private Result registerCreateTablesForExtendedExpression(List The {@code sqlDialect} parameter was previously used to influence identifier casing during
+ * parsing. This is now configurable via {@link ConverterProvider#builder()} (for example {@code
+ * ConverterProvider.builder().unquotedCasing(...)}), or for fully custom parser behaviour, by
+ * subclassing {@link ConverterProvider} and overriding {@link
+ * ConverterProvider#getSqlParserConfig()}.
+ *
* @param sqlStatements a string containing one more SQL statements
* @param catalogReader the {@link Prepare.CatalogReader} for finding tables/views referenced in
* the SQL statements
* @param sqlDialect The sql dialect to use for parsing.
* @return the Substrait {@link Plan}
* @throws SqlParseException if there is an error while parsing the SQL statements
+ * @deprecated Prefer constructing {@link SqlToSubstrait} with a {@link ConverterProvider}
+ * configured for the desired casing (via {@link ConverterProvider#builder()}) and calling
+ * {@link #convert(String, Prepare.CatalogReader)}. For fully custom parser behaviour,
+ * subclass {@link ConverterProvider} and override {@link
+ * ConverterProvider#getSqlParserConfig()}.
*/
+ @Deprecated
public Plan convert(
final String sqlStatements,
final Prepare.CatalogReader catalogReader,
final SqlDialect sqlDialect)
throws SqlParseException {
- Builder builder = io.substrait.plan.Plan.builder();
- builder.version(Version.builder().from(Version.DEFAULT_VERSION).producer("isthmus").build());
- builder.executionBehavior(converterProvider.getExecutionBehavior());
-
- final SqlParser.Config sqlParserConfig =
- sqlDialect.configureParser(
- SqlParser.config().withParserFactory(ServerDdlExecutor.PARSER_FACTORY));
-
- // TODO: consider case in which one sql passes conversion while others don't
- SubstraitSqlToCalcite.convertQueries(sqlStatements, catalogReader, sqlParserConfig).stream()
- .map(root -> SubstraitRelVisitor.convert(root, converterProvider))
- .forEach(root -> builder.addRoots(root));
-
- return builder.build();
+ return convert(sqlStatements, catalogReader);
}
}
diff --git a/isthmus/src/main/java/io/substrait/isthmus/SubstraitToSql.java b/isthmus/src/main/java/io/substrait/isthmus/SubstraitToSql.java
index 690bd5774..ac07d8d65 100644
--- a/isthmus/src/main/java/io/substrait/isthmus/SubstraitToSql.java
+++ b/isthmus/src/main/java/io/substrait/isthmus/SubstraitToSql.java
@@ -23,7 +23,7 @@ public class SubstraitToSql extends SqlConverterBase {
/** Creates a SubstraitToSql converter with default configuration and extensions. */
public SubstraitToSql() {
- this(new ConverterProvider());
+ this(ConverterProvider.DEFAULT);
}
/**
diff --git a/isthmus/src/main/java/io/substrait/isthmus/sql/SubstraitCreateStatementParser.java b/isthmus/src/main/java/io/substrait/isthmus/sql/SubstraitCreateStatementParser.java
index b008d577d..f8179e397 100644
--- a/isthmus/src/main/java/io/substrait/isthmus/sql/SubstraitCreateStatementParser.java
+++ b/isthmus/src/main/java/io/substrait/isthmus/sql/SubstraitCreateStatementParser.java
@@ -1,5 +1,6 @@
package io.substrait.isthmus.sql;
+import io.substrait.isthmus.ConverterProvider;
import io.substrait.isthmus.SqlConverterBase;
import io.substrait.isthmus.SubstraitTypeSystem;
import io.substrait.isthmus.Utils;
@@ -50,28 +51,7 @@ public class SubstraitCreateStatementParser {
*/
public static List This method expects the use of fully qualified table names in the CREATE statements.
+ *
+ * @param converterProvider the converter provider whose parser config controls identifier casing
+ * and other parser settings
+ * @param createStatements List of SQL strings containing only CREATE statements, must not be null
+ * @return a {@link CalciteCatalogReader} generated from the CREATE statements
+ * @throws SqlParseException if there is an error parsing the SQL statements
+ */
+ public static CalciteCatalogReader processCreateStatementsToCatalog(
+ @NonNull final ConverterProvider converterProvider,
+ @NonNull final List This method expects the use of fully qualified table names in the CREATE statements.
+ *
+ * @param converterProvider the converter provider whose parser config controls identifier casing
+ * and other parser settings
+ * @param createStatements a SQL string containing only CREATE statements, must not be null
+ * @return a {@link CalciteCatalogReader} generated from the CREATE statements
+ * @throws SqlParseException if parsing fails or statements are invalid
+ */
+ public static CalciteCatalogReader processCreateStatementsToCatalog(
+ @NonNull final ConverterProvider converterProvider, @NonNull final String... createStatements)
+ throws SqlParseException {
+ final CalciteSchema rootSchema =
+ processCreateStatementsToSchema(converterProvider, createStatements);
final List This method only supports simple table names without any additional qualifiers. Only used
+ * with {@link io.substrait.isthmus.SqlExpressionToSubstrait}.
*
- * @param createStatements one or more SQL strings containing only CREATE statements; must not be
- * null
- * @return a {@link CalciteSchema} generated from the CREATE statements
+ * @param converterProvider the converter provider whose parser config controls identifier casing
+ * and other parser settings
+ * @param createStatements a SQL string containing only CREATE statements; must not be null
+ * @return list of {@link SubstraitTable}s generated from the CREATE statements
* @throws SqlParseException if parsing fails or statements are invalid
*/
+ public static List To use a custom parser configuration, build the {@link ConverterProvider} via {@link
+ * ConverterProvider#builder()} and its {@code sqlParserConfig(...)}; for fully dynamic behaviour,
+ * subclass {@link ConverterProvider} and override {@link ConverterProvider#getSqlParserConfig()}.
*
* @param sqlStatements a string containing one or more SQL statements
- * @param parserConfig Calcite SqlParser.Config to control the parser
+ * @param converterProvider the converter provider whose parser config controls identifier casing
+ * and other parser settings
* @return a list of {@link SqlNode}s corresponding to the given statements
* @throws SqlParseException if there is an error while parsing the SQL statements
*/
- public static List