Skip to content

Commit

Permalink
[CALCITE-6678] Calcite should support dual table query when db provid…
Browse files Browse the repository at this point in the history
…es this feature
  • Loading branch information
strongduanmu authored and mihaibudiu committed Nov 18, 2024
1 parent e20a9a5 commit f2ec11f
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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;
Expand All @@ -36,6 +37,7 @@
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;
Expand All @@ -46,6 +48,8 @@
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;
Expand Down Expand Up @@ -76,6 +80,7 @@
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;
Expand Down Expand Up @@ -149,6 +154,10 @@ protected CalciteConnectionImpl(Driver driver, AvaticaFactory factory,
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());
Expand Down Expand Up @@ -621,4 +630,25 @@ static class CalciteServerStatementImpl
}
}

/** 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ protected SqlDelegatingConformance(SqlConformance delegate) {
return delegate.allowCharLiteralAlias();
}

@Override public boolean isSupportedDualTable() {
return delegate.isSupportedDualTable();
}

@Override public boolean isGroupByAlias() {
return delegate.isGroupByAlias();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ public static void main(String[] args) throws Exception {
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,
Expand Down
54 changes: 54 additions & 0 deletions core/src/test/resources/sql/dummy.iq
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit f2ec11f

Please sign in to comment.