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

mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new f2ec11fe7e [CALCITE-6678] Calcite should support dual table query when 
db provides this feature
f2ec11fe7e is described below

commit f2ec11fe7e23ecf2db903bc02c40609242993aad
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Fri Nov 8 20:40:16 2024 +0800

    [CALCITE-6678] Calcite should support dual table query when db provides 
this feature
---
 .../apache/calcite/jdbc/CalciteConnectionImpl.java | 30 ++++++++++++
 .../sql/validate/SqlAbstractConformance.java       |  4 ++
 .../calcite/sql/validate/SqlConformance.java       | 15 ++++++
 .../calcite/sql/validate/SqlConformanceEnum.java   | 10 ++++
 .../sql/validate/SqlDelegatingConformance.java     |  4 ++
 .../org/apache/calcite/test/CoreQuidemTest.java    |  9 ++++
 core/src/test/resources/sql/dummy.iq               | 54 ++++++++++++++++++++++
 7 files changed, 126 insertions(+)

diff --git 
a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java 
b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
index 7b69e2c3ed..b01b6ad951 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
@@ -18,6 +18,7 @@ package org.apache.calcite.jdbc;
 
 import org.apache.calcite.DataContext;
 import org.apache.calcite.DataContexts;
+import org.apache.calcite.adapter.java.AbstractQueryableTable;
 import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.avatica.AvaticaConnection;
 import org.apache.calcite.avatica.AvaticaFactory;
@@ -36,6 +37,7 @@ import org.apache.calcite.jdbc.CalcitePrepare.Context;
 import org.apache.calcite.linq4j.BaseQueryable;
 import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.Linq4j;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.QueryProvider;
 import org.apache.calcite.linq4j.Queryable;
@@ -46,6 +48,8 @@ import org.apache.calcite.materialize.MaterializationService;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.prepare.CalciteCatalogReader;
 import org.apache.calcite.rel.type.DelegatingTypeSystem;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rel.type.TimeFrameSet;
 import org.apache.calcite.rel.type.TimeFrames;
@@ -76,6 +80,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 import java.lang.reflect.Type;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -149,6 +154,10 @@ abstract class CalciteConnectionImpl
         requireNonNull(rootSchema != null
             ? rootSchema
             : CalciteSchema.createRootSchema(true));
+    // Add dual table metadata when isSupportedDualTable return true
+    if (cfg.conformance().isSupportedDualTable()) {
+      this.rootSchema.add("DUAL", new DualTable(String.class));
+    }
     checkArgument(this.rootSchema.isRoot(), "must be root schema");
     this.properties.put(InternalProperty.CASE_SENSITIVE, cfg.caseSensitive());
     this.properties.put(InternalProperty.UNQUOTED_CASING, 
cfg.unquotedCasing());
@@ -621,4 +630,25 @@ abstract class CalciteConnectionImpl
     }
   }
 
+  /** Implementation of {@link AbstractQueryableTable} for dual table. */
+  private static class DualTable extends AbstractQueryableTable {
+
+    DualTable(Class<String> clazz) {
+      super(clazz);
+    }
+
+    @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+      return typeFactory.createStructType(
+          // Dual table has one column DUMMY, and defined to be VARCHAR2(1)
+          Collections.singletonList(typeFactory.createJavaType(String.class)),
+          Collections.singletonList("DUMMY"));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override public Queryable<String> asQueryable(QueryProvider queryProvider,
+        SchemaPlus schema, String tableName) {
+      // Dual table contains one row with a value X
+      return Linq4j.asEnumerable(Collections.singletonList("X")).asQueryable();
+    }
+  }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
index 3205a48b40..ce750242fc 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java
@@ -33,6 +33,10 @@ public abstract class SqlAbstractConformance implements 
SqlConformance {
     return SqlConformanceEnum.DEFAULT.allowCharLiteralAlias();
   }
 
+  @Override public boolean isSupportedDualTable() {
+    return SqlConformanceEnum.DEFAULT.isSupportedDualTable();
+  }
+
   @Override public boolean isGroupByAlias() {
     return SqlConformanceEnum.DEFAULT.isGroupByAlias();
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
index b8501f02f1..2517e4577b 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java
@@ -177,6 +177,21 @@ public interface SqlConformance {
    */
   boolean isSortByAliasObscures();
 
+  /**
+   * Whether this dialect supports dual table.
+   *
+   * <p>For example,
+   *
+   * <blockquote><pre>SELECT 1 + 1 FROM DUAL</pre></blockquote>
+   *
+   * <p>Among the built-in conformance levels, true in
+   * {@link SqlConformanceEnum#MYSQL_5},
+   * {@link SqlConformanceEnum#ORACLE_10},
+   * {@link SqlConformanceEnum#ORACLE_12},
+   * false otherwise.
+   */
+  boolean isSupportedDualTable();
+
   /**
    * Whether {@code FROM} clause is required in a {@code SELECT} statement.
    *
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
index a610f19f16..66358f9685 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java
@@ -103,6 +103,16 @@ public enum SqlConformanceEnum implements SqlConformance {
     }
   }
 
+  @Override public boolean isSupportedDualTable() {
+    switch (this) {
+    case MYSQL_5:
+    case ORACLE_10:
+    case ORACLE_12:
+      return true;
+    default:
+      return false;
+    }
+  }
 
   @Override public boolean isGroupByAlias() {
     switch (this) {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
index c8e2f7cdd2..5cac7afca7 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlDelegatingConformance.java
@@ -39,6 +39,10 @@ public class SqlDelegatingConformance implements 
SqlConformance {
     return delegate.allowCharLiteralAlias();
   }
 
+  @Override public boolean isSupportedDualTable() {
+    return delegate.isSupportedDualTable();
+  }
+
   @Override public boolean isGroupByAlias() {
     return delegate.isGroupByAlias();
   }
diff --git a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java 
b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
index e7a3c636ab..be327c89a8 100644
--- a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
+++ b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
@@ -121,6 +121,15 @@ class CoreQuidemTest extends QuidemTest {
                   SqlConformanceEnum.MYSQL_5)
               .with(CalciteAssert.Config.SCOTT)
               .connect();
+        case "scott-oracle":
+          // Same as "scott", but uses Oracle conformance.
+          return CalciteAssert.that()
+              .with(CalciteConnectionProperty.PARSER_FACTORY,
+                  ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
+              .with(CalciteConnectionProperty.CONFORMANCE,
+                  SqlConformanceEnum.ORACLE_10)
+              .with(CalciteAssert.Config.SCOTT)
+              .connect();
         case "steelwheels":
           return CalciteAssert.that()
               .with(CalciteConnectionProperty.PARSER_FACTORY,
diff --git a/core/src/test/resources/sql/dummy.iq 
b/core/src/test/resources/sql/dummy.iq
index 166e1b06d8..7647c72dd6 100644
--- a/core/src/test/resources/sql/dummy.iq
+++ b/core/src/test/resources/sql/dummy.iq
@@ -20,4 +20,58 @@ values 1;
 EXPR$0
 1
 !ok
+
+# [CALCITE-6678] Support dual table query (enabled in MySQL, Oracle libraries)
+!set outputformat mysql
+!use scott-mysql
+
+# MySQL supports users to specify the dual table, and also supports users not 
to specify the dual table.
+SELECT 1 + 1 FROM DUAL;
++--------+
+| EXPR$0 |
++--------+
+|      2 |
++--------+
+(1 row)
+
+!ok
+
+SELECT 1 + 1;
++--------+
+| EXPR$0 |
++--------+
+|      2 |
++--------+
+(1 row)
+
+!ok
+
+# Oracle supports users to specify the dual table, but not supports users not 
to specify the dual table.
+!use scott-oracle
+
+SELECT 1 + 1 FROM DUAL;
++--------+
+| EXPR$0 |
++--------+
+|      2 |
++--------+
+(1 row)
+
+!ok
+
+SELECT 1 + 1;
+java.sql.SQLException: Error while executing SQL "SELECT 1 + 1": From line 1, 
column 1 to line 1, column 12: SELECT must have a FROM clause
+
+!error
+
+SELECT * FROM DUAL;
++-------+
+| DUMMY |
++-------+
+| X     |
++-------+
+(1 row)
+
+!ok
+
 # End dummy.iq

Reply via email to