This is an automated email from the ASF dual-hosted git repository.

tkalkirill pushed a commit to branch ignite-29034
in repository https://gitbox.apache.org/repos/asf/ignite.git

commit 129b1c7aef257d018835e4ee43be68c0235c8a68
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Wed Sep 2 21:34:32 2026 +0300

    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 6d22ac2d51d..1882b1bb96f 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.Function;
 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 @@ import 
org.apache.ignite.internal.processors.query.calcite.util.Commons;
  * 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 class IgniteSchema extends AbstractSchema {
 
         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 35a6c1bf96c..0b697edde2c 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.SqlTypeFamily;
 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.FrameworkConfig;
 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 @@ import org.junit.Test;
  * 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 <T> @Nullable T createComponent(PluginContext 
ctx, Class<T> 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 class OperatorsExtensionIntegrationTest extends 
AbstractBasicIntegrationT
             .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<Integer, Integer>()
+            .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)

Reply via email to