From 129b1c7aef257d018835e4ee43be68c0235c8a68 Mon Sep 17 00:00:00 2001 From: Kirill Tkalenko Date: Wed, 2 Sep 2026 21:34:32 +0300 Subject: [PATCH] IGNITE-29034 Wip --- .../query/calcite/schema/IgniteSchema.java | 18 +++++ .../OperatorsExtensionIntegrationTest.java | 68 ++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteSchema.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteSchema.java index 6d22ac2d51d33..1882b1bb96f69 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteSchema.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteSchema.java @@ -28,6 +28,7 @@ import org.apache.calcite.schema.FunctionParameter; import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.TableMacro; import org.apache.calcite.schema.impl.AbstractSchema; import org.apache.calcite.tools.FrameworkConfig; import org.apache.ignite.IgniteException; @@ -37,6 +38,12 @@ * Ignite schema. */ public class IgniteSchema extends AbstractSchema { + /** */ + private static final String DUAL_TBL_NAME = "DUAL"; + + /** */ + private static final String DUAL_TBL_VIEW = "SELECT 'X' AS DUMMY"; + /** */ private final String schemaName; @@ -143,6 +150,17 @@ public SchemaPlus register(SchemaPlus parent, FrameworkConfig frameworkCfg) { viewMap.forEach((name, sql) -> newSchema.add(name, new ViewTableMacroImpl(sql, newSchema, frameworkCfg))); + registerDualTableIfSupported(newSchema, frameworkCfg); + return newSchema; } + + /** */ + private static void registerDualTableIfSupported(SchemaPlus schema, FrameworkConfig frameworkCfg) { + if (frameworkCfg != null && frameworkCfg.getParserConfig().conformance().isSupportedDualTable() + && schema.tables().get(DUAL_TBL_NAME) == null + && schema.getFunctions(DUAL_TBL_NAME).stream().noneMatch(TableMacro.class::isInstance)) { + schema.add(DUAL_TBL_NAME, new ViewTableMacroImpl(DUAL_TBL_VIEW, schema, frameworkCfg)); + } + } } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java index 35a6c1bf96cb9..0b697edde2cd0 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/OperatorsExtensionIntegrationTest.java @@ -50,6 +50,8 @@ import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable; import org.apache.calcite.sql.util.SqlOperatorTables; +import org.apache.calcite.sql.validate.SqlConformance; +import org.apache.calcite.sql.validate.SqlDelegatingConformance; import org.apache.calcite.sql.validate.SqlValidator; import org.apache.calcite.sql2rel.SqlRexContext; import org.apache.calcite.sql2rel.SqlRexConvertlet; @@ -57,6 +59,7 @@ import org.apache.calcite.tools.Frameworks; import org.apache.calcite.util.BuiltInMethod; import org.apache.calcite.util.Optionality; +import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor; import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext; @@ -79,6 +82,15 @@ * Tests SQL engine extension with plugin. */ public class OperatorsExtensionIntegrationTest extends AbstractBasicIntegrationTest { + /** */ + private static final SqlConformance TEST_CONFORMANCE = new SqlDelegatingConformance( + CalciteQueryProcessor.FRAMEWORK_CONFIG.getParserConfig().conformance()) { + /** {@inheritDoc} */ + @Override public boolean isSupportedDualTable() { + return true; + } + }; + /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { return super.getConfiguration(igniteInstanceName) @@ -90,12 +102,15 @@ public class OperatorsExtensionIntegrationTest extends AbstractBasicIntegrationT @Override public @Nullable T createComponent(PluginContext ctx, Class cls) { if (FrameworkConfig.class.equals(cls)) { FrameworkConfig cfg = Frameworks.newConfigBuilder(CalciteQueryProcessor.FRAMEWORK_CONFIG) + .parserConfig(CalciteQueryProcessor.FRAMEWORK_CONFIG.getParserConfig() + .withConformance(TEST_CONFORMANCE)) .convertletTable(new ConvertletTable()) .operatorTable(SqlOperatorTables.chain( new OperatorTable().init(), CalciteQueryProcessor.FRAMEWORK_CONFIG.getOperatorTable())) .sqlValidatorConfig( ((IgniteSqlValidator.Config)CalciteQueryProcessor.FRAMEWORK_CONFIG.getSqlValidatorConfig()) - .withSqlNodeRewriter(new SqlRewriter())) + .withSqlNodeRewriter(new SqlRewriter()) + .withConformance(TEST_CONFORMANCE)) .context(Contexts.chain( CalciteQueryProcessor.FRAMEWORK_CONFIG.getContext(), Contexts.of(IgniteSqlSemantics.builder() @@ -238,6 +253,57 @@ public void testPaginationRoundingPolicy() { .check(); } + /** */ + @Test + public void testDualTable() { + assertQuery("SELECT 1 + 1 FROM dual").returns(2).check(); + + assertQuery("SELECT * FROM DUAL") + .columnNames("DUMMY") + .returns("X") + .check(); + + assertQuery("SELECT DUMMY FROM DUAL") + .columnNames("DUMMY") + .returns("X") + .check(); + + assertQuery("SELECT LAG(rate, 1, rate) OVER (ORDER BY period) FROM " + + "(SELECT 1 AS rate, 1 AS period FROM dual)") + .returns(1) + .check(); + } + + /** */ + @Test + public void testDualTableInNewSchema() { + client.getOrCreateCache(new CacheConfiguration() + .setName("CUSTOM_SCHEMA_MARKER") + .setSqlSchema("CUSTOM_SCHEMA") + .setIndexedTypes(Integer.class, Integer.class)); + + assertQuery("SELECT DUMMY FROM CUSTOM_SCHEMA.DUAL") + .columnNames("DUMMY") + .returns("X") + .check(); + } + + /** */ + @Test + public void testUserDefinedDualView() { + sql("CREATE VIEW PUBLIC.DUAL AS SELECT 'USER' AS DUMMY"); + + try { + assertQuery("SELECT DUMMY FROM PUBLIC.DUAL") + .columnNames("DUMMY") + .returns("USER") + .check(); + } + finally { + sql("DROP VIEW IF EXISTS PUBLIC.DUAL"); + } + } + /** Rewrites LTRIM with 2 parameters. */ public static SqlCall rewriteLtrim(SqlValidator validator, SqlCall call) { if (call.operandCount() != 2)