├── .gitignore ├── .travis.yml ├── README.md ├── build.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main ├── java │ └── com │ │ └── gerardnico │ │ └── calcite │ │ ├── CalciteCatalogs.java │ │ ├── CalciteConnections.java │ │ ├── CalciteDataContext.java │ │ ├── CalciteFramework.java │ │ ├── CalciteJdbc.java │ │ ├── CalciteModel.java │ │ ├── CalcitePlanner.java │ │ ├── CalciteProgram.java │ │ ├── CalciteRel.java │ │ ├── CalciteRelDataType.java │ │ ├── CalciteRelLogicalExpression.java │ │ ├── CalciteRelRunners.java │ │ ├── CalciteRelTrait.java │ │ ├── CalciteSchemaStatic.java │ │ ├── CalciteSql.java │ │ ├── CalciteSqlDialect.java │ │ ├── CalciteSqlNode.java │ │ ├── CalciteSqlParser.java │ │ ├── CalciteSqlSelect.java │ │ ├── CalciteSqlValidation.java │ │ ├── CalciteSqlValidatorCustom.java │ │ ├── CalciteSqlVisitor.java │ │ ├── CalciteTableFunctionCountries.java │ │ ├── adapter │ │ ├── AdapterContext.java │ │ └── package-info.java │ │ ├── demo │ │ ├── DatabaseConnectionSpec.java │ │ ├── DatabaseInstance.java │ │ ├── HelloWorld.java │ │ └── JdbcStore.java │ │ ├── mock │ │ ├── CompoundNameColumn.java │ │ ├── CompoundNameColumnResolver.java │ │ ├── CountingFactory.java │ │ ├── EmpInitializerExpressionFactory.java │ │ ├── Fixture.java │ │ ├── MockCatalogReaderDynamic.java │ │ ├── MockCatalogReaderExtended.java │ │ ├── MockCatalogReaderSimple.java │ │ ├── MockJdbcCatalogReader.java │ │ ├── MockRelOptCost.java │ │ ├── MockRelOptPlanner.java │ │ ├── MockSqlOperatorTable.java │ │ ├── MockViewExpander.java │ │ ├── Smalls.java │ │ └── VirtualColumnsExpressionFactory.java │ │ └── schema │ │ ├── BookstoreSchema.java │ │ ├── OrdersTableFactory.java │ │ ├── ScannableTableListingDetails.java │ │ ├── SchemaBuilder.java │ │ ├── StreamSchema.java │ │ ├── StreamableSchema.java │ │ ├── StreamableTableOrderDetails.java │ │ ├── foodmart │ │ └── FoodmartSchema.java │ │ ├── hr │ │ ├── HrDepartment.java │ │ ├── HrDependent.java │ │ ├── HrEmployee.java │ │ ├── HrLocation.java │ │ ├── HrSchema.java │ │ └── HrSchemaMin.java │ │ ├── lingual │ │ ├── LingualEmp.java │ │ └── LingualSchema.java │ │ ├── orderEntry │ │ └── OrderEntrySchema.java │ │ └── queries │ │ └── HrFoodmartQuery.java └── resources │ ├── hrAndFoodmart │ └── hrAndFoodmart.json │ └── sales │ ├── DEPTS.html │ ├── EMPS.html │ └── sales.json └── test └── java └── com └── gerardnico └── calcite ├── CalciteCatalogsTest.java ├── CalciteFrameworksTest.java ├── CalcitePlannerHepTest.java ├── CalcitePlannerVolcanoTest.java ├── CalciteRelJdbcTest.java ├── CalciteRelLogicalExpressionTest.java ├── CalciteSchemaTest.java ├── CalciteSqlSelectTest.java ├── CalciteSqlValidationTest.java ├── CalciteSqlVisitorTest.java ├── CalciteSqlWriterTest.java ├── HelloWorldTest.java └── adapter └── AdapterContextTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | .idea 4 | 5 | # Ignore Gradle build output directory 6 | build 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for Travis continuous integration. 2 | # See https://travis-ci.org/apache/calcite 3 | language: java 4 | matrix: 5 | fast_finish: true 6 | include: 7 | - jdk: openjdk8 8 | branches: 9 | only: 10 | - master 11 | install: true 12 | script: 13 | - ./gradlew --no-daemon build 14 | git: 15 | depth: 100 16 | cache: 17 | directories: 18 | - $HOME/.gradle/caches/ 19 | - $HOME/.gradle/wrapper/ 20 | 21 | before_install: 22 | - chmod +x gradlew 23 | before_cache: 24 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 25 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 26 | 27 | # End .travis.yml 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Calcite Demo's 2 | 3 | [![Build Status](https://travis-ci.org/gerardnico/calcite.svg?branch=master)](https://travis-ci.org/gerardnico/calcite) 4 | 5 | ## About 6 | 7 | A repository of [Calcite](https://calcite.apache.org/) demo's 8 | 9 | 10 | > Note: This demos are using a lot of [Reflective Schema](src/test/java/com/gerardnico/calcite/CalciteSchemaTest.java) 11 | > (ie schema in the code) but in practice, you would connect to a data store (database, adapter) to get a schema. 12 | 13 | 14 | ## Getting Started Demos 15 | 16 | * [Getting Started Article](https://gerardnico.com/db/calcite/getting_started) 17 | * [Getting Started Code](src/test/java/com/gerardnico/calcite/CalciteFrameworksTest.java) - parse sql, validate, transform to relation expression and execute with a [Planner](https://github.com/apache/calcite/blob/master/core/src/main/java/org/apache/calcite/tools/Planner.java) 18 | 19 | * [HelloWorld of the official documentation](https://calcite.apache.org/docs/index.html) 20 | * [HelloWorld Code](src/main/java/com/gerardnico/calcite/demo/HelloWorld.java) - A query (The first demo of the doc) 21 | 22 | ## Demo Schema and Catalog 23 | 24 | * [Reflective Schema](src/test/java/com/gerardnico/calcite/CalciteSchemaTest.java) - Shows how to create a schema from Java Object via reflexion 25 | * [Catalog Reader](src/test/java/com/gerardnico/calcite/CalciteCatalogsTest.java) - read the schema and tables from a catalog 26 | 27 | ## Demo Sql Parsing and validation 28 | 29 | Select Statement: 30 | * [Sql Select Info](src/test/java/com/gerardnico/calcite/CalciteSqlSelectTest.java) - Parse a Select Sql and extracts tokens 31 | * [Sql Select Visitor](src/test/java/com/gerardnico/calcite/CalciteSqlVisitorTest.java) - Parse a Select Sql and build the tree (SqlNode) and visit it 32 | * [Sql Pretty Print](src/test/java/com/gerardnico/calcite/CalciteSqlWriterTest.java) - Parse a SQL to SqlNode and print it pretty 33 | * [Sql Validation](src/test/java/com/gerardnico/calcite/CalciteSqlValidationTest.java) - Parse a SQL to SqlNode and validate it 34 | 35 | There is also other [node type](https://github.com/apache/calcite/tree/master/core/src/main/java/org/apache/calcite/sql): 36 | * [SqlCreate](https://github.com/apache/calcite/blob/master/core/src/main/java/org/apache/calcite/sql/SqlCreate.java) 37 | * [SqlInsert](https://github.com/apache/calcite/blob/master/core/src/main/java/org/apache/calcite/sql/SqlInsert.java) 38 | * ... 39 | 40 | 41 | ## Demo Logical Relational Expression 42 | 43 | * [Relational Logical Expression](src/test/java/com/gerardnico/calcite/CalciteRelLogicalExpressionTest.java) - shows how to create several relational logical expression, print the sql and execute them 44 | * [Relational Expression from Jdbc Schema](src/test/java/com/gerardnico/calcite/CalciteRelJdbcTest.java) From RelBuilder based on a Jdbc data store, build a relational expression and transform it to SQL. 45 | 46 | ## Demo Optimization / Cost 47 | 48 | * [Relational Expression Optimization with the HepPlanner](src/test/java/com/gerardnico/calcite/CalcitePlannerHepTest.java) - shows the filter early optimization 49 | * [Relational Expression Optimization with the Volcano Planner (and cost)](src/test/java/com/gerardnico/calcite/CalcitePlannerVolcanoTest.java) - optimization with the Volcano Planner 50 | 51 | 52 | 53 | ## FYI 54 | 55 | * This code is an extract and adaptation of the test core project of calcite. 56 | * The database used are `Hsqldb` and `H2` and the data files such as `SCOTT` are also in a jar dependency file. 57 | 58 | 59 | ## Doc / Reference 60 | 61 | * [Calcite demo in notebook](https://github.com/michaelmior/calcite-notebooks) 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This generated file contains a sample Java project to get you started. 5 | * For more details take a look at the Java Quickstart chapter in the Gradle 6 | * User Manual available at https://docs.gradle.org/5.6.4/userguide/tutorial_java_projects.html 7 | */ 8 | 9 | plugins { 10 | // Apply the java plugin to add support for Java 11 | java 12 | 13 | // Apply the application plugin to add support for building a CLI application 14 | application 15 | } 16 | 17 | repositories { 18 | // Use jcenter for resolving dependencies. 19 | // You can declare any Maven/Ivy/file repository here. 20 | jcenter() 21 | } 22 | 23 | dependencies { 24 | // This dependency is used by the application. 25 | implementation("com.google.guava:guava:28.0-jre") 26 | 27 | // Hsqldb is used in the code 28 | implementation("org.hsqldb:hsqldb:2.5.0") 29 | // The scott hsqldb files are in this jar 30 | implementation("net.hydromatic:scott-data-hsqldb:0.1") 31 | testImplementation("net.hydromatic:foodmart-data-hsqldb:0.4") 32 | 33 | implementation("com.h2database:h2:1.4.195") 34 | 35 | // https://mvnrepository.com/artifact/org.apache.calcite/calcite-core 36 | compile("org.apache.calcite:calcite-core:1.22.0") 37 | 38 | // Use JUnit test framework 39 | testImplementation("junit:junit:4.12") 40 | } 41 | 42 | application { 43 | // Define the main class for the application 44 | mainClassName = "com.gerardnico.calcite.App" 45 | } 46 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gerardnico/calcite/fd04b38a1c9448ee75f73285d2015248848b1080/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/5.6.4/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = "calcite" 11 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteCatalogs.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.mock.MockCatalogReaderSimple; 4 | import org.apache.calcite.prepare.Prepare; 5 | import org.apache.calcite.rel.type.RelDataTypeFactory; 6 | 7 | /** 8 | * Static info on catalog 9 | */ 10 | public class CalciteCatalogs { 11 | 12 | 13 | /** 14 | * Print info in a catalogReader 15 | * @param catalogReader 16 | */ 17 | static public void print(Prepare.CatalogReader catalogReader) { 18 | System.out.println("Schemas:"); 19 | catalogReader.getSchemaPaths().forEach(s -> System.out.println(" * Schema Paths: " + String.join(",", s))); 20 | System.out.println(); 21 | System.out.println("Root Schema: " + catalogReader.getRootSchema().name); 22 | System.out.println(" Sub Schema: "); 23 | catalogReader.getRootSchema().getSubSchemaMap() 24 | .values() 25 | .forEach(s -> { 26 | System.out.println(" * " + s.name); 27 | s.getTableNames().forEach(t->System.out.println(" * "+t)); 28 | }); 29 | } 30 | 31 | /** 32 | * 33 | * @return a mock catalog with a list of schema: 34 | * * order entry 35 | * * hr 36 | * To see the schema, you can {@link #printMock() print it} 37 | */ 38 | public static Prepare.CatalogReader createMock() { 39 | RelDataTypeFactory sqlTypeFactory = CalciteRelDataType.createSqlTypeFactory(); 40 | return new MockCatalogReaderSimple(sqlTypeFactory, true).init(); 41 | } 42 | 43 | public static void printMock() { 44 | print(createMock()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteConnections.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.config.CalciteConnectionConfigImpl; 4 | import org.apache.calcite.config.CalciteConnectionProperty; 5 | import org.apache.calcite.config.NullCollation; 6 | import org.apache.calcite.jdbc.CalciteConnection; 7 | import org.apache.calcite.plan.Context; 8 | import org.apache.calcite.plan.Contexts; 9 | 10 | import java.sql.Connection; 11 | import java.sql.DriverManager; 12 | import java.sql.SQLException; 13 | import java.util.Properties; 14 | 15 | /** 16 | * Static method on {@link org.apache.calcite.jdbc.CalciteConnection} 17 | */ 18 | public class CalciteConnections { 19 | 20 | /** 21 | * 22 | * @param connection 23 | * @return a {@link CalciteConnection} 24 | */ 25 | public static CalciteConnection unwrap(Connection connection) { 26 | try { 27 | return connection.unwrap(CalciteConnection.class); 28 | } catch (SQLException e) { 29 | throw new RuntimeException(e); 30 | } 31 | } 32 | 33 | /** 34 | * Return a connection without any model 35 | * @return 36 | */ 37 | public static CalciteConnection getConnectionWithoutModel() { 38 | try { 39 | Class.forName("org.apache.calcite.jdbc.Driver"); 40 | Properties info = new Properties(); 41 | /** 42 | * The lex property gives a lot of default lexical property 43 | * {@link CalciteConnectionProperty.LEX} 44 | */ 45 | info.setProperty("lex", "JAVA"); 46 | /** 47 | * You can define your with extra connection properties 48 | */ 49 | info.setProperty(CalciteConnectionProperty.CASE_SENSITIVE.name(),"false"); 50 | return unwrap(DriverManager.getConnection("jdbc:calcite:", info)); 51 | } catch (ClassNotFoundException | SQLException e) { 52 | throw new RuntimeException(e); 53 | } 54 | } 55 | 56 | /** 57 | * 58 | * @param modelPath - the path de json model file 59 | * @return a connection configured with the model file 60 | */ 61 | public static CalciteConnection getConnectionWithModel(String modelPath) { 62 | return CalciteModel.getConnectionFromModel(modelPath); 63 | } 64 | 65 | /** 66 | * @return a context from a connection :) 67 | */ 68 | public static Context getContext() { 69 | Properties properties = new Properties(); 70 | properties.setProperty( 71 | CalciteConnectionProperty.DEFAULT_NULL_COLLATION.camelName(), 72 | NullCollation.LOW.name() 73 | ); 74 | CalciteConnectionConfigImpl connectionConfig = new CalciteConnectionConfigImpl(properties); 75 | return Contexts.of(connectionConfig); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteDataContext.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.DataContext; 4 | import org.apache.calcite.schema.SchemaPlus; 5 | 6 | /** 7 | * DataContext is a runtime context allowing access to the tables in a database. 8 | */ 9 | public class CalciteDataContext { 10 | 11 | static SchemaPlus getRootSchema(DataContext dataContext) { 12 | return dataContext.getRootSchema(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteFramework.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.schema.SchemaBuilder; 4 | import com.gerardnico.calcite.schema.hr.HrSchema; 5 | import com.gerardnico.calcite.schema.orderEntry.OrderEntrySchema; 6 | import org.apache.calcite.adapter.java.ReflectiveSchema; 7 | import org.apache.calcite.plan.Contexts; 8 | import org.apache.calcite.plan.RelTraitDef; 9 | import org.apache.calcite.schema.SchemaPlus; 10 | import org.apache.calcite.sql.parser.SqlParser; 11 | import org.apache.calcite.tools.*; 12 | 13 | import javax.sql.DataSource; 14 | import java.util.List; 15 | 16 | /** 17 | * {@link Frameworks Frameworks} is the second entry point of calcite after JDBC 18 | */ 19 | public class CalciteFramework { 20 | 21 | 22 | private static FrameworkConfig createFrameworkConfig(SchemaPlus schema) { 23 | 24 | return Frameworks.newConfigBuilder() 25 | .parserConfig(CalciteSqlParser.createMySqlConfig()) 26 | .defaultSchema(schema) 27 | .typeSystem(CalciteRelDataType.getDefaultSystem()) 28 | .costFactory(null) 29 | .ruleSets(RuleSets.ofList()) 30 | .traitDefs(CalciteRelTrait.getRelTraits()) 31 | .context(Contexts.EMPTY_CONTEXT) // Context provides a way to store data within the planner session that can be accessed in planner rules. 32 | .build(); 33 | 34 | } 35 | 36 | public static FrameworkConfig getDefaultConfig(){ 37 | return Frameworks.newConfigBuilder().build(); 38 | } 39 | 40 | /** 41 | * Get a default config from a data store 42 | * @param dataStore 43 | * @return 44 | */ 45 | public static FrameworkConfig getNewConfig(DataSource dataStore){ 46 | SchemaPlus schemaPlus = CalciteSchemaStatic.getCurrentSchema(dataStore); 47 | return Frameworks.newConfigBuilder() 48 | .defaultSchema(schemaPlus) 49 | .parserConfig(CalciteSqlParser.getDefault()) 50 | .build(); 51 | } 52 | 53 | 54 | /** 55 | * Creates a config based on the SCOTT schema, with tables EMP and DEPT. 56 | * @return 57 | */ 58 | public static FrameworkConfig configScottSchemaBased() { 59 | return Frameworks.newConfigBuilder() 60 | .defaultSchema(SchemaBuilder.getSchema(SchemaBuilder.SchemaSpec.SCOTT_WITH_TEMPORAL)) 61 | .build(); 62 | } 63 | 64 | public static FrameworkConfig configOrderEntrySchemaBased() { 65 | SchemaPlus rootSchema = Frameworks.createRootSchema(true); 66 | ReflectiveSchema schema = new ReflectiveSchema(new OrderEntrySchema()); 67 | SchemaPlus orderEntry = rootSchema.add("OE", schema); 68 | return Frameworks.newConfigBuilder() 69 | .parserConfig( 70 | SqlParser.configBuilder() 71 | .setCaseSensitive(false) 72 | .build()) 73 | .defaultSchema(orderEntry) 74 | .traitDefs((List) null) 75 | .programs(Programs.heuristicJoinOrder(Programs.RULE_SET, true, 2)) 76 | .build(); 77 | } 78 | 79 | /** 80 | * A config based on the HR schema 81 | * @return 82 | */ 83 | public static FrameworkConfig configHrSchemaBased() { 84 | SchemaPlus rootSchema = Frameworks.createRootSchema(true); 85 | ReflectiveSchema schema = new ReflectiveSchema(new HrSchema()); 86 | SchemaPlus hr = rootSchema.add("HR", schema); 87 | 88 | return Frameworks.newConfigBuilder() 89 | .parserConfig(CalciteSqlParser.getInsensitiveConfig()) 90 | .defaultSchema(hr) 91 | .build(); 92 | } 93 | 94 | /** 95 | * 96 | * @return a planner based on the {@link HrSchema} 97 | */ 98 | public static Planner getPlannerHrSchemaBased(){ 99 | // Build the schema 100 | SchemaPlus rootSchema = Frameworks.createRootSchema(true); 101 | ReflectiveSchema schema = new ReflectiveSchema(new HrSchema()); 102 | SchemaPlus hr = rootSchema.add("HR", schema); 103 | 104 | // Get a non-sensitive parser 105 | SqlParser.Config insensitiveParser = SqlParser.configBuilder() 106 | .setCaseSensitive(false) 107 | .build(); 108 | 109 | // Build a global configuration object 110 | FrameworkConfig config = Frameworks.newConfigBuilder() 111 | .parserConfig(insensitiveParser) 112 | .defaultSchema(hr) 113 | .build(); 114 | 115 | // Get the planner tool 116 | return Frameworks.getPlanner(config); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteJdbc.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.adapter.jdbc.JdbcSchema; 4 | import org.apache.calcite.schema.SchemaPlus; 5 | import org.apache.calcite.tools.Frameworks; 6 | import org.apache.calcite.tools.RelBuilder; 7 | 8 | import javax.sql.DataSource; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | 12 | /** 13 | * Static method that are related to Calcite and JDBC 14 | */ 15 | public class CalciteJdbc { 16 | 17 | 18 | /** 19 | * 20 | * @param dataSource 21 | * @return the calcite schema representation of the Jdbc schema 22 | */ 23 | public static SchemaPlus getSchema(DataSource dataSource) { 24 | try { 25 | SchemaPlus rootSchema = Frameworks.createRootSchema(true); 26 | String schemaName = dataSource.getConnection().getSchema(); 27 | rootSchema.add( 28 | schemaName, 29 | JdbcSchema.create(rootSchema, schemaName, dataSource, null, null) 30 | ); 31 | return rootSchema.getSubSchema(schemaName); 32 | } catch (SQLException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | /** 38 | * Return a RelBuilder based on the schema of a data store 39 | * @param dataSource 40 | * @return 41 | */ 42 | public static RelBuilder getBuilder(DataSource dataSource){ 43 | return CalciteRel.createDataStoreBasedRelBuilder(dataSource); 44 | } 45 | 46 | 47 | /** 48 | * An utility function to print a resultSet 49 | * 50 | * @param resultSet 51 | */ 52 | public static void printResultSet(ResultSet resultSet) { 53 | try { 54 | StringBuilder stringBuilder = new StringBuilder(); 55 | while (resultSet.next()) { 56 | for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { 57 | stringBuilder 58 | .append(resultSet.getObject(i)) 59 | .append(","); 60 | } 61 | stringBuilder.append("\n"); 62 | } 63 | System.out.println(); 64 | System.out.println("Result:"); 65 | System.out.println(stringBuilder.toString()); 66 | } catch (SQLException e) { 67 | throw new RuntimeException(e); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteModel.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.config.CalciteConnectionProperty; 4 | import org.apache.calcite.jdbc.CalciteConnection; 5 | 6 | import java.sql.Connection; 7 | import java.sql.DriverManager; 8 | import java.sql.SQLException; 9 | import java.util.Properties; 10 | 11 | /** 12 | * Snippet of code with the model 13 | */ 14 | public class CalciteModel { 15 | 16 | /** 17 | * The model path can be given as a property 18 | * 19 | * @throws ClassNotFoundException 20 | */ 21 | public static CalciteConnection getConnectionFromModel(String modelPath) { 22 | try { 23 | 24 | Properties properties = new Properties(); 25 | properties.setProperty("model", modelPath); 26 | properties.setProperty(CalciteConnectionProperty.CASE_SENSITIVE.name(),"false"); 27 | Class.forName("org.apache.calcite.jdbc.Driver"); 28 | Connection connection = DriverManager.getConnection("jdbc:calcite:", properties); 29 | 30 | return connection.unwrap(CalciteConnection.class); 31 | } catch (SQLException | ClassNotFoundException e) { 32 | throw new RuntimeException(e); 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalcitePlanner.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.mock.MockRelOptPlanner; 4 | import org.apache.calcite.interpreter.InterpretableConvention; 5 | import org.apache.calcite.plan.Convention; 6 | import org.apache.calcite.plan.RelOptPlanner; 7 | import org.apache.calcite.plan.RelTraitSet; 8 | import org.apache.calcite.plan.hep.HepPlanner; 9 | import org.apache.calcite.plan.hep.HepProgram; 10 | import org.apache.calcite.plan.volcano.VolcanoPlanner; 11 | import org.apache.calcite.rel.RelNode; 12 | import org.apache.calcite.rel.RelRoot; 13 | import org.apache.calcite.rel.core.RelFactories; 14 | import org.apache.calcite.rel.logical.LogicalIntersect; 15 | import org.apache.calcite.rel.logical.LogicalUnion; 16 | import org.apache.calcite.rel.rules.*; 17 | import org.apache.calcite.tools.*; 18 | 19 | /** 20 | * 21 | */ 22 | public class CalcitePlanner { 23 | 24 | /** 25 | * See org.apache.calcite.test.HepPlannerTest 26 | * 27 | * @return 28 | */ 29 | static public HepPlanner createHepPlanner() { 30 | HepProgram hepProgram = HepProgram.builder().build(); 31 | HepPlanner hepPlanner = new HepPlanner(hepProgram); 32 | hepPlanner.addRule(JoinToMultiJoinRule.INSTANCE); 33 | hepPlanner.addRule(LoptOptimizeJoinRule.INSTANCE); 34 | hepPlanner.addRule(MultiJoinOptimizeBushyRule.INSTANCE); 35 | hepPlanner.addRule(JoinPushThroughJoinRule.LEFT); 36 | hepPlanner.addRule(new CoerceInputsRule(LogicalUnion.class, false, RelFactories.LOGICAL_BUILDER)); 37 | hepPlanner.addRule(new CoerceInputsRule(LogicalIntersect.class, false, RelFactories.LOGICAL_BUILDER)); 38 | return hepPlanner; 39 | } 40 | 41 | static public final RelOptPlanner getMockRelOptPlanner() { 42 | 43 | return new MockRelOptPlanner(CalciteConnections.getContext()); 44 | 45 | } 46 | 47 | static public final Planner getPlannerFromFrameworkConfig(FrameworkConfig frameworkConfig) { 48 | return Frameworks.getPlanner(frameworkConfig); 49 | } 50 | 51 | public static VolcanoPlanner createVolcanoPlanner() { 52 | return new VolcanoPlanner(); 53 | } 54 | 55 | /** 56 | * A sample code to show how you can optimize with Volcano 57 | * @param relRoot 58 | * @return 59 | */ 60 | public static RelNode optimizeWithVolcano(RelRoot relRoot) { 61 | 62 | // Create the volcano planner 63 | VolcanoPlanner volcanoPlanner = createVolcanoPlanner(); 64 | 65 | // A set of rules to apply 66 | Program program = Programs.ofRules( 67 | FilterProjectTransposeRule.INSTANCE, 68 | ProjectMergeRule.INSTANCE, 69 | FilterMergeRule.INSTANCE, 70 | LoptOptimizeJoinRule.INSTANCE 71 | ); 72 | 73 | // Create the desired output traits 74 | // No idea what I'm doing 75 | RelTraitSet relTraits = volcanoPlanner.emptyTraitSet() 76 | .replace(Convention.NONE) 77 | .replace(InterpretableConvention.INSTANCE); 78 | 79 | return program.run(volcanoPlanner,relRoot.rel,relTraits, null,null); 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteProgram.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.tools.Program; 4 | import org.apache.calcite.tools.Programs; 5 | 6 | public class CalciteProgram { 7 | 8 | 9 | public static Program createHeuristicJoinOrderProgram() { 10 | return Programs.heuristicJoinOrder( 11 | Programs.RULE_SET, 12 | true, 13 | 2); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteRel.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.demo.JdbcStore; 4 | import org.apache.calcite.plan.RelOptUtil; 5 | import org.apache.calcite.rel.RelNode; 6 | import org.apache.calcite.rel.RelRoot; 7 | import org.apache.calcite.rel.RelWriter; 8 | import org.apache.calcite.rel.externalize.RelWriterImpl; 9 | import org.apache.calcite.schema.SchemaPlus; 10 | import org.apache.calcite.sql.SqlDialect; 11 | import org.apache.calcite.sql.SqlExplainLevel; 12 | import org.apache.calcite.sql.parser.SqlParser; 13 | import org.apache.calcite.tools.*; 14 | 15 | import javax.sql.DataSource; 16 | import java.io.PrintWriter; 17 | import java.sql.PreparedStatement; 18 | import java.sql.ResultSet; 19 | import java.sql.SQLException; 20 | 21 | public class CalciteRel { 22 | 23 | /** 24 | * @return a {@link RelBuilder} with a schema mapped to the SCOTT database, with tables EMP and DEPT. 25 | */ 26 | public static RelBuilder createHrScottBasedRelBuilder() { 27 | final FrameworkConfig config = CalciteFramework.configScottSchemaBased(); 28 | return RelBuilder.create(config); 29 | } 30 | 31 | 32 | /** 33 | * Create a RelBuilder based on the JDBC data source 34 | * 35 | * @param dataSource 36 | * @return 37 | */ 38 | public static RelBuilder createDataStoreBasedRelBuilder(DataSource dataSource) { 39 | SchemaPlus schemaPlus = CalciteJdbc.getSchema(dataSource); 40 | FrameworkConfig config = Frameworks.newConfigBuilder() 41 | .parserConfig(SqlParser.Config.DEFAULT) 42 | .defaultSchema(schemaPlus) 43 | .programs(CalciteProgram.createHeuristicJoinOrderProgram()) 44 | .build(); 45 | return RelBuilder.create(config); 46 | } 47 | 48 | /** 49 | * Print a relational expression (ie sane as {@link #explain(RelNode)} 50 | * 51 | * @param relNode 52 | */ 53 | public static void print(RelNode relNode) { 54 | System.out.println(toString(relNode)); 55 | } 56 | 57 | /** 58 | * Explain is just a {@link #print(RelNode)} 59 | * 60 | * @param relNode 61 | */ 62 | public static void explain(RelNode relNode) { 63 | RelWriter rw = new RelWriterImpl(new PrintWriter(System.out, true)); 64 | relNode.explain(rw); 65 | } 66 | 67 | /** 68 | * Explain is just a {@link #print(RelNode)} 69 | * 70 | * @param relNode 71 | */ 72 | public static void explainAll(RelNode relNode) { 73 | System.out.println(RelOptUtil.toString(relNode, SqlExplainLevel.ALL_ATTRIBUTES)); 74 | } 75 | 76 | 77 | /** 78 | * @param relNode 79 | * @return the string representation of a relNode 80 | */ 81 | public static String toString(RelNode relNode) { 82 | return RelOptUtil.toString(relNode); 83 | } 84 | 85 | 86 | /** 87 | * Execute a logical relNode and return the result set 88 | * 89 | * @param relNode 90 | * @return 91 | */ 92 | public static ResultSet executeQuery(RelNode relNode) { 93 | return executeQuery(relNode, null); 94 | } 95 | 96 | /** 97 | * 98 | * @param relNode 99 | * @param defaultSchema - the schema only if the relNode is physical 100 | * @return 101 | */ 102 | public static ResultSet executeQuery(RelNode relNode, SchemaPlus defaultSchema) { 103 | PreparedStatement run = CalciteRelRunners.run(relNode, defaultSchema); 104 | try { 105 | return run.executeQuery(); 106 | } catch (SQLException e) { 107 | throw new RuntimeException(e); 108 | } 109 | } 110 | 111 | /** 112 | * {@link #executeQuery(RelNode) Execute} and print the result set in one pass 113 | * 114 | * @param relNode 115 | */ 116 | public static void executeAndPrint(RelNode relNode) { 117 | executeAndPrint(relNode, null); 118 | } 119 | 120 | /** 121 | * After having create a {@link RelNode regular expression} with a builder. 122 | * Example: {@link JdbcStore#getRelBuilder() builder}, 123 | *

124 | * You can transform it into sql 125 | * 126 | * @param relNode 127 | * @param sqlDialect - the dialect 128 | * @return the sql representation of the relNode 129 | */ 130 | public static String fromRelNodeToSql(RelNode relNode, SqlDialect sqlDialect) { 131 | return CalciteSql.fromRelNodeToSql(relNode, sqlDialect); 132 | } 133 | 134 | 135 | public static RelBuilder createOrderEntryBasedRelBuilder() { 136 | final FrameworkConfig config = CalciteFramework.configOrderEntrySchemaBased(); 137 | return RelBuilder.create(config); 138 | } 139 | 140 | /** 141 | * @param relRoot 142 | * @return a logical plan 143 | */ 144 | public static RelNode getLogicalPlan(RelRoot relRoot) { 145 | return relRoot.project(); 146 | } 147 | 148 | public static void executeAndPrint(RelNode relNode, SchemaPlus defaultSchema) { 149 | 150 | try (ResultSet resultSet = executeQuery(relNode, defaultSchema)) { 151 | CalciteJdbc.printResultSet(resultSet); 152 | } catch (SQLException e) { 153 | throw new RuntimeException(e); 154 | } 155 | 156 | 157 | } 158 | 159 | /** 160 | * Utility function to: 161 | * * print a relational expression 162 | * * print the SQL equivalent 163 | * * to run it 164 | * @param relNode 165 | */ 166 | public static void showExplainSqlAndRun(RelNode relNode){ 167 | System.out.println("Print the relational expression"); 168 | CalciteRel.print(relNode); 169 | System.out.println(); 170 | 171 | System.out.println("Print the SQL"); 172 | String sql = CalciteRel.fromRelNodeToSql(relNode, CalciteSqlDialect.getDialect(CalciteSqlDialect.DIALECT.ANSI)); 173 | System.out.println(sql); 174 | 175 | System.out.println("Execute the relational expression"); 176 | CalciteRel.executeAndPrint(relNode); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteRelDataType.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.rel.type.RelDataType; 4 | import org.apache.calcite.rel.type.RelDataTypeFactory; 5 | import org.apache.calcite.rel.type.RelDataTypeSystem; 6 | import org.apache.calcite.sql.type.SqlTypeFactoryImpl; 7 | 8 | import java.sql.Date; 9 | 10 | public class CalciteRelDataType { 11 | 12 | 13 | public static RelDataTypeSystem getDefaultSystem(){ 14 | return RelDataTypeSystem.DEFAULT; 15 | } 16 | 17 | /** 18 | * Example how you would define a row in a table such as {@link org.apache.calcite.schema.StreamableTable#getRowType(RelDataTypeFactory)} 19 | * @param typeFactory 20 | * @return 21 | */ 22 | public static RelDataType getRowType(RelDataTypeFactory typeFactory){ 23 | return new RelDataTypeFactory.Builder(typeFactory) 24 | .add("DATE_ID", typeFactory.createJavaType(Date.class)) 25 | .add("ITEM_ID", typeFactory.createJavaType(Long.class)) 26 | .add("ITEM_PRICE", typeFactory.createJavaType(Double.class)) 27 | .add("BUYER_NAME", typeFactory.createJavaType(String.class)) 28 | .add("QUANTITY", typeFactory.createJavaType(Integer.class)) 29 | .build(); 30 | } 31 | 32 | /** 33 | * Sql Type factory 34 | * @return 35 | */ 36 | public static RelDataTypeFactory createSqlTypeFactory() { 37 | return new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteRelLogicalExpression.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.rel.RelNode; 4 | import org.apache.calcite.rel.core.JoinRelType; 5 | import org.apache.calcite.sql.fun.SqlStdOperatorTable; 6 | import org.apache.calcite.tools.RelBuilder; 7 | 8 | /** 9 | * Example that uses {@link org.apache.calcite.tools.RelBuilder} with an HR schema 10 | * (Example: {@link CalciteRel#createHrScottBasedRelBuilder()}) 11 | * to create various relational expressions 12 | * 13 | * API summary 14 | */ 15 | public class CalciteRelLogicalExpression { 16 | 17 | /** 18 | * Example from the doc Filter and Aggregate 19 | */ 20 | public static RelNode filterAndCountSumAggregate(RelBuilder builder) { 21 | return builder 22 | .scan("EMP") 23 | .aggregate(builder.groupKey("DEPTNO"), 24 | builder.count(false, "C"), 25 | builder.sum(false, "S", builder.field("SAL"))) 26 | .filter( 27 | builder.call(SqlStdOperatorTable.GREATER_THAN, 28 | builder.field("C"), 29 | builder.literal(3))) 30 | .build(); 31 | } 32 | 33 | /** 34 | * Example modified of Push and pop 35 | * The first join has not the good key 36 | *

37 | * There is also example code at example4 38 | *

39 | * Sometimes the stack becomes so deeply nested it gets confusing. To keep 40 | * things straight, you can remove expressions from the stack. For example, 41 | * here we are building a bushy join: 42 | * 43 | *

 44 |      *                join
 45 |      *              /      \
 46 |      *         join          join
 47 |      *       /      \      /      \
 48 |      * CUSTOMERS ORDERS LINE_ITEMS PRODUCTS
 49 |      * 
50 | * 51 | *

We build it in three stages. Store the intermediate results in variables 52 | * `left` and `right`, and use `push()` to put them back on the stack when it 53 | * is time to create the final `Join`. 54 | */ 55 | public static RelNode bushyJoin(RelBuilder relBuilder) { 56 | RelBuilder builder = CalciteRel.createOrderEntryBasedRelBuilder(); 57 | final RelNode left = builder 58 | .scan("CUSTOMERS") 59 | .scan("ORDERS") 60 | .join(JoinRelType.INNER, "CUSTOMER_ID") 61 | .build(); 62 | 63 | System.out.println("Print the Customers/Orders SQL"); 64 | String sql = CalciteRel.fromRelNodeToSql(left, CalciteSqlDialect.getDialect(CalciteSqlDialect.DIALECT.ANSI)); 65 | System.out.println(sql); 66 | 67 | System.out.println("Execute the relational expression"); 68 | CalciteRel.executeAndPrint(left); 69 | 70 | final RelNode right = builder 71 | .scan("ORDER_ITEMS") 72 | .scan("PRODUCTS") 73 | .join(JoinRelType.INNER, "PRODUCT_ID") 74 | .build(); 75 | 76 | System.out.println("Print the OrderItems/PRODUCTS SQL"); 77 | sql = CalciteRel.fromRelNodeToSql(right, CalciteSqlDialect.getDialect(CalciteSqlDialect.DIALECT.ANSI)); 78 | System.out.println(sql); 79 | 80 | System.out.println("Execute the relational expression"); 81 | CalciteRel.executeAndPrint(right); 82 | 83 | System.out.println("bushy join"); 84 | return builder 85 | .push(left) 86 | .push(right) 87 | .join(JoinRelType.INNER, "ORDER_ID") 88 | .build(); 89 | } 90 | 91 | /** 92 | * Creates a relational expression for a table scan. 93 | * It is equivalent to 94 | * 95 | *

SELECT *
 96 |      * FROM emp
97 | * 98 | * @return 99 | */ 100 | public static RelNode tableAsValues(RelBuilder builder) { 101 | return builder 102 | .values(new String[]{"a", "b"}, 1, true, null, false) 103 | .build(); 104 | } 105 | 106 | 107 | /** 108 | * Creates a relational expression for a table scan. 109 | * It is equivalent to 110 | * 111 | *
SELECT *
112 |      * FROM emp
113 | */ 114 | public static RelNode tableScan(RelBuilder builder) { 115 | return builder 116 | .scan("EMP") 117 | .build(); 118 | } 119 | 120 | /** 121 | * Creates a relational expression for a table scan and project. 122 | * It is equivalent to 123 | * 124 | *
SELECT deptno, ename
125 |      * FROM emp
126 | * 127 | * @return 128 | */ 129 | public static RelNode project(RelBuilder builder) { 130 | return builder 131 | .scan("EMP") 132 | .project(builder.field("DEPTNO"), builder.field("ENAME")) 133 | .build(); 134 | } 135 | 136 | public static RelNode recursiveQuery(RelBuilder builder) { 137 | return builder 138 | .values(new String[]{"i"}, 1) 139 | .transientScan("aux") 140 | .filter( 141 | builder.call( 142 | SqlStdOperatorTable.LESS_THAN, 143 | builder.field(0), 144 | builder.literal(10))) 145 | .project( 146 | builder.call( 147 | SqlStdOperatorTable.PLUS, 148 | builder.field(0), 149 | builder.literal(1))) 150 | .repeatUnion("aux", true) 151 | .build(); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteRelRunners.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.schema.hr.HrSchema; 4 | import org.apache.calcite.adapter.clone.CloneSchema; 5 | import org.apache.calcite.adapter.java.ReflectiveSchema; 6 | import org.apache.calcite.interpreter.Bindables; 7 | import org.apache.calcite.jdbc.CalciteConnection; 8 | import org.apache.calcite.jdbc.CalciteSchema; 9 | import org.apache.calcite.plan.RelOptTable; 10 | import org.apache.calcite.rel.RelHomogeneousShuttle; 11 | import org.apache.calcite.rel.RelNode; 12 | import org.apache.calcite.rel.RelShuttle; 13 | import org.apache.calcite.rel.core.TableScan; 14 | import org.apache.calcite.rel.logical.LogicalTableScan; 15 | import org.apache.calcite.schema.Schema; 16 | import org.apache.calcite.schema.SchemaPlus; 17 | import org.apache.calcite.tools.RelRunner; 18 | 19 | import java.sql.Connection; 20 | import java.sql.DriverManager; 21 | import java.sql.PreparedStatement; 22 | import java.sql.SQLException; 23 | 24 | /** 25 | * Copy and modification of the {@link org.apache.calcite.tools.RelRunners}. 26 | * 27 | * to prevent 28 | * java.sql.SQLException: exception while executing query: null 29 | * at 30 | * org.apache.calcite.jdbc.CalcitePrepare$CalciteSignature.enumerable(CalcitePrepare.java:355) 31 | * 32 | * because the schema is not provided 33 | * 34 | */ 35 | public class CalciteRelRunners { 36 | 37 | 38 | private CalciteRelRunners() { 39 | } 40 | 41 | public static PreparedStatement run(RelNode rel) { 42 | return run(rel, null); 43 | } 44 | 45 | /** 46 | * Runs a relational expression by creating a JDBC connection. 47 | */ 48 | public static PreparedStatement run(RelNode rel, SchemaPlus schemaPlus) { 49 | final RelShuttle shuttle = new RelHomogeneousShuttle() { 50 | @Override 51 | public RelNode visit(TableScan scan) { 52 | final RelOptTable table = scan.getTable(); 53 | if (scan instanceof LogicalTableScan 54 | && Bindables.BindableTableScan.canHandle(table)) { 55 | // Always replace the LogicalTableScan with BindableTableScan 56 | // because it's implementation does not require a "schema" as context. 57 | return Bindables.BindableTableScan.create(scan.getCluster(), table); 58 | } 59 | return super.visit(scan); 60 | } 61 | }; 62 | rel = rel.accept(shuttle); 63 | try (Connection connection = DriverManager.getConnection("jdbc:calcite:")) { 64 | /** 65 | * To prevent java.sql.SQLException: exception while executing query: null 66 | * we build the schema 67 | */ 68 | if (schemaPlus != null) { 69 | CalciteConnection calciteConnection = (CalciteConnection) connection; 70 | SchemaPlus rootSchema = calciteConnection.getRootSchema(); 71 | Schema reflectiveSchema = schemaPlus.unwrap(ReflectiveSchema.class); 72 | rootSchema.add(schemaPlus.getName(), reflectiveSchema); 73 | } 74 | final RelRunner runner = connection.unwrap(RelRunner.class); 75 | return runner.prepare(rel); 76 | } catch (SQLException e) { 77 | throw new RuntimeException(e); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteRelTrait.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.plan.ConventionTraitDef; 4 | import org.apache.calcite.plan.RelTraitDef; 5 | import org.apache.calcite.rel.RelCollationTraitDef; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class CalciteRelTrait { 11 | 12 | 13 | public static List getRelTraits(){ 14 | final List traitDefs = new ArrayList(); 15 | traitDefs.add(ConventionTraitDef.INSTANCE); 16 | traitDefs.add(RelCollationTraitDef.INSTANCE); 17 | return traitDefs; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSchemaStatic.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.schema.foodmart.FoodmartSchema; 4 | import com.gerardnico.calcite.schema.hr.HrSchema; 5 | import org.apache.calcite.DataContext; 6 | import org.apache.calcite.adapter.java.ReflectiveSchema; 7 | import org.apache.calcite.jdbc.CalciteConnection; 8 | import org.apache.calcite.schema.SchemaPlus; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.Connection; 12 | import java.sql.SQLException; 13 | 14 | /** 15 | * Static schema function 16 | */ 17 | public class CalciteSchemaStatic { 18 | 19 | /** 20 | * 21 | * @param calciteConnection - A {@link CalciteConnection} that you can get in {@link CalciteConnections} 22 | * @return 23 | */ 24 | static SchemaPlus getCurrentSchema(CalciteConnection calciteConnection){ 25 | final String schemaName; 26 | try { 27 | schemaName = calciteConnection.getSchema(); 28 | return calciteConnection.getRootSchema().getSubSchema(schemaName); 29 | } catch (SQLException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } 33 | 34 | /** 35 | * 36 | * @param dataSource 37 | * @return the current schema of a data source 38 | */ 39 | static SchemaPlus getCurrentSchema(DataSource dataSource){ 40 | 41 | return CalciteJdbc.getSchema(dataSource); 42 | } 43 | 44 | public static SchemaPlus getRootSchemaFromCalciteConnection(CalciteConnection calciteConnection) { 45 | return calciteConnection.getRootSchema(); 46 | } 47 | 48 | public static SchemaPlus getRootSchemaFromSqlConnection(Connection connection) { 49 | try { 50 | CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class); 51 | return calciteConnection.getRootSchema(); 52 | } catch (SQLException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | /** 58 | * Add the HR and Foodmart Schema 59 | * @param calciteConnection - a calcite connection (Example {@link CalciteConnections#getConnectionWithoutModel()} 60 | * @return - the same connection 61 | */ 62 | public static void addReflectiveSchemaToConnection(CalciteConnection calciteConnection) { 63 | 64 | SchemaPlus rootSchema = calciteConnection.getRootSchema(); 65 | rootSchema.add("hr", new ReflectiveSchema(new HrSchema())); 66 | rootSchema.add("foodmart", new ReflectiveSchema(new FoodmartSchema())); 67 | 68 | } 69 | 70 | 71 | public static CalciteConnection addReflectiveSchemaToConnectionViaModel() { 72 | return CalciteConnections.getConnectionWithModel("src/main/resources/hrAndFoodmart/hrAndFoodmart.json"); 73 | } 74 | 75 | /** 76 | * Get a root schema from a data context 77 | * @return 78 | */ 79 | public static SchemaPlus getRootSchemaFromDataContext(DataContext dataContext){ 80 | return dataContext.getRootSchema(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSql.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.demo.JdbcStore; 4 | import com.gerardnico.calcite.mock.MockCatalogReaderSimple; 5 | import com.gerardnico.calcite.mock.MockViewExpander; 6 | import org.apache.calcite.plan.RelOptCluster; 7 | import org.apache.calcite.plan.volcano.VolcanoPlanner; 8 | import org.apache.calcite.prepare.PlannerImpl; 9 | import org.apache.calcite.rel.RelNode; 10 | import org.apache.calcite.rel.RelRoot; 11 | import org.apache.calcite.rel.rel2sql.RelToSqlConverter; 12 | import org.apache.calcite.rel.type.RelDataTypeFactory; 13 | import org.apache.calcite.rex.RexBuilder; 14 | import org.apache.calcite.sql.*; 15 | import org.apache.calcite.sql.dialect.*; 16 | import org.apache.calcite.sql.parser.SqlParseException; 17 | import org.apache.calcite.sql.parser.SqlParser; 18 | import org.apache.calcite.sql.pretty.SqlPrettyWriter; 19 | import org.apache.calcite.sql2rel.SqlToRelConverter; 20 | import org.apache.calcite.tools.Planner; 21 | 22 | public class CalciteSql { 23 | 24 | /** 25 | * Transform a SQL Node to SQL back 26 | * 27 | * @param sqlNode 28 | * @return 29 | */ 30 | public static String fromSqlNodeToSql(SqlNode sqlNode) { 31 | SqlPrettyWriter sqlWriter = new SqlPrettyWriter(); 32 | sqlNode.unparse(sqlWriter, 0, 0); 33 | return sqlWriter.toString(); 34 | } 35 | 36 | /** 37 | * @param sql - A sql statement (query, insert, ...) 38 | * @return - the tree 39 | */ 40 | public static SqlNode fromSqlToSqlNode(String sql) { 41 | try { 42 | SqlParser parser = CalciteSqlParser.create(sql); 43 | return parser.parseStmt(); 44 | } catch (SqlParseException e) { 45 | throw new RuntimeException(e); 46 | } 47 | 48 | } 49 | 50 | /** 51 | * @param sql - A sql query statement 52 | * @return - the tree 53 | */ 54 | public static SqlNode fromSqlQueryToSqlNode(String sql) { 55 | try { 56 | SqlParser parser = CalciteSqlParser.create(sql); 57 | return parser.parseQuery(); 58 | } catch (SqlParseException e) { 59 | throw new RuntimeException(e); 60 | } 61 | 62 | } 63 | 64 | /** 65 | * @param sql - A query 66 | * @param config - The Sql parser config 67 | * @return - the tree 68 | */ 69 | public static SqlNode fromSqlToSqlNode(String sql, SqlParser.Config config) { 70 | try { 71 | SqlParser parser = CalciteSqlParser.create( 72 | sql, 73 | config 74 | ); 75 | return parser.parseQuery(); 76 | } catch (SqlParseException e) { 77 | throw new RuntimeException(e); 78 | } 79 | 80 | } 81 | 82 | public static SqlNode fromSqlToSqlNodeMySql(String sql){ 83 | return fromSqlToSqlNode(sql,CalciteSqlParser.createMySqlConfig()); 84 | } 85 | 86 | /** 87 | * 88 | * @param sql 89 | * @return a relNode 90 | */ 91 | public static RelRoot fromSqlToRelNode(String sql) { 92 | 93 | SqlNode sqlNode = fromSqlToSqlNode(sql); 94 | return CalciteSqlNode.fromSqlNodeToRelRoot(sqlNode); 95 | 96 | } 97 | 98 | 99 | 100 | 101 | /** 102 | * After having create a {@link RelNode regular expression} with for instance the {@link JdbcStore#getRelBuilder() builder}, 103 | * you can transform it into sql 104 | * 105 | * @param relNode 106 | * @param sqlDialect - A sql dialect (one of {@link CalciteSqlDialect#getDialect(CalciteSqlDialect.DIALECT)}) 107 | * @return the sql representation of the relNode 108 | */ 109 | static public String fromRelNodeToSql(RelNode relNode, SqlDialect sqlDialect) { 110 | SqlPrettyWriter sqlWriter = new SqlPrettyWriter(); 111 | RelToSqlConverter relToSqlConverter = new RelToSqlConverter(sqlDialect); 112 | SqlSelect sqlSelect = relToSqlConverter.visitChild(0, relNode).asSelect(); 113 | return sqlWriter.format(sqlSelect); 114 | } 115 | 116 | /** 117 | * @param relNode 118 | * @return a sql in the ANSI dialect 119 | */ 120 | static public String fromRelNodeToSql(RelNode relNode) { 121 | SqlDialect dialect = AnsiSqlDialect.DEFAULT; 122 | return fromRelNodeToSql(relNode, dialect); 123 | } 124 | 125 | 126 | 127 | /** 128 | * Print the tree 129 | */ 130 | public static void print(SqlNode sqlNode) { 131 | SqlWriterConfig config = SqlPrettyWriter.config(); 132 | print(sqlNode,config); 133 | } 134 | 135 | /** 136 | * Print the tree 137 | */ 138 | public static void print(SqlNode sqlNode, SqlWriterConfig config) { 139 | System.out.println(new SqlPrettyWriter(config).format(sqlNode)); 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlDialect.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.sql.SqlDialect; 4 | import org.apache.calcite.sql.SqlDialectFactoryImpl; 5 | import org.apache.calcite.sql.dialect.*; 6 | 7 | import java.sql.Connection; 8 | import java.sql.SQLException; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * An SQL dialect 16 | * 17 | */ 18 | 19 | public class CalciteSqlDialect { 20 | 21 | public enum DIALECT { 22 | ANSI, 23 | SNOWFLAKE, 24 | ACCESS, 25 | BIGQUERY, 26 | CALCITE, 27 | DERBY 28 | } 29 | 30 | static Map dialects = new HashMap<>(); 31 | static { 32 | dialects.put(DIALECT.ANSI,AnsiSqlDialect.DEFAULT); 33 | dialects.put(DIALECT.SNOWFLAKE,SnowflakeSqlDialect.DEFAULT); 34 | dialects.put(DIALECT.ACCESS,AccessSqlDialect.DEFAULT); 35 | dialects.put(DIALECT.BIGQUERY,BigQuerySqlDialect.DEFAULT); 36 | dialects.put(DIALECT.CALCITE,org.apache.calcite.sql.dialect.CalciteSqlDialect.DEFAULT); 37 | dialects.put(DIALECT.DERBY,DerbySqlDialect.DEFAULT); 38 | } 39 | 40 | /** 41 | * Get dialects as demo 42 | * All dialects are at {@link org.apache.calcite.sql.dialect} 43 | */ 44 | static public List getDialects() { 45 | 46 | return new ArrayList<>(dialects.values()); 47 | } 48 | 49 | /** 50 | * Get dialects as demo 51 | * All dialects are at {@link org.apache.calcite.sql.dialect} 52 | */ 53 | static public SqlDialect getDialect(DIALECT dialect) { 54 | return dialects.get(dialect); 55 | } 56 | 57 | /** 58 | * Get a dialect from a JDBC connection 59 | */ 60 | static public SqlDialect getDialectFromSqlConnection(Connection connection) { 61 | try { 62 | return SqlDialectFactoryImpl.INSTANCE.create(connection.getMetaData()); 63 | } catch (SQLException e) { 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlNode.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.mock.MockViewExpander; 4 | import org.apache.calcite.plan.RelOptCluster; 5 | import org.apache.calcite.plan.RelOptTable; 6 | import org.apache.calcite.prepare.Prepare; 7 | import org.apache.calcite.rel.RelRoot; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.rel.type.RelDataTypeSystem; 10 | import org.apache.calcite.rex.RexBuilder; 11 | import org.apache.calcite.sql.SqlNode; 12 | import org.apache.calcite.sql.type.SqlTypeFactoryImpl; 13 | import org.apache.calcite.sql2rel.SqlToRelConverter; 14 | import org.apache.calcite.sql2rel.StandardConvertletTable; 15 | import org.apache.calcite.tools.FrameworkConfig; 16 | import org.apache.calcite.tools.Planner; 17 | import org.apache.calcite.tools.RelConversionException; 18 | import org.apache.calcite.tools.ValidationException; 19 | 20 | import static com.gerardnico.calcite.CalcitePlanner.getMockRelOptPlanner; 21 | 22 | public class CalciteSqlNode { 23 | 24 | /** 25 | * Validate the SQl Node against the schema with a planner object 26 | * 27 | * @return the validated node 28 | */ 29 | public static SqlNode validate(Planner planner, SqlNode sqlNode) throws ValidationException { 30 | return CalciteSqlValidation.validateFromPlanner(planner, sqlNode); 31 | } 32 | 33 | /** 34 | * From SqlToRelRoot 35 | * 36 | * @return 37 | */ 38 | public static RelRoot fromSqlNodeToRelRoot(SqlNode sqlNode) { 39 | 40 | final CalciteSqlValidatorCustom sqlValidator = CalciteSqlValidation.createCustomSqlValidator(); 41 | 42 | // SqlToRelConverter.Config localConfig = SqlToRelConverter.Config.DEFAULT; 43 | SqlToRelConverter.Config sqlToRelConfig = SqlToRelConverter.configBuilder() 44 | .withTrimUnusedFields(true) 45 | .withExpand(true) 46 | .build(); 47 | 48 | 49 | final RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); 50 | final RexBuilder rexBuilder = new RexBuilder(typeFactory); 51 | final RelOptCluster cluster = RelOptCluster.create(getMockRelOptPlanner(), rexBuilder); 52 | 53 | // Can expand a view into relational expressions. 54 | final RelOptTable.ViewExpander viewExpander = new MockViewExpander( 55 | sqlValidator, 56 | (Prepare.CatalogReader) sqlValidator.getCatalogReader(), 57 | cluster, 58 | sqlToRelConfig); 59 | 60 | final SqlToRelConverter converter = new SqlToRelConverter( 61 | viewExpander, 62 | sqlValidator, 63 | (Prepare.CatalogReader) sqlValidator.getCatalogReader(), 64 | cluster, 65 | StandardConvertletTable.INSTANCE, 66 | sqlToRelConfig 67 | ); 68 | 69 | RelRoot relRoot = converter.convertQuery(sqlNode, false, true); 70 | assert relRoot != null; 71 | relRoot = relRoot 72 | .withRel(converter.flattenTypes(relRoot.rel, true)) 73 | .withRel(converter.decorrelate(sqlNode, relRoot.rel)) 74 | .withRel(converter.trimUnusedFields(true, relRoot.rel)); 75 | 76 | return relRoot; 77 | } 78 | 79 | /** 80 | * From SqlToRelRoot 81 | * 82 | * @param planner - A planner utility. See {@link CalcitePlanner#getPlannerFromFrameworkConfig(FrameworkConfig)} 83 | * @return a relRoot 84 | */ 85 | public static RelRoot fromSqlNodeToRelRootViaPlanner(Planner planner, SqlNode sqlNode) { 86 | try { 87 | return planner.rel(sqlNode); 88 | } catch (RelConversionException e) { 89 | throw new RuntimeException(e); 90 | } 91 | } 92 | 93 | /** 94 | * @param sql 95 | * @return a SqlNode from a Sql 96 | */ 97 | public static SqlNode fromSqlToSqlNode(String sql) { 98 | return CalciteSql.fromSqlToSqlNode(sql); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlParser.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.avatica.util.Casing; 4 | import org.apache.calcite.avatica.util.Quoting; 5 | import org.apache.calcite.config.Lex; 6 | import org.apache.calcite.sql.parser.SqlParser; 7 | import org.apache.calcite.sql.validate.SqlConformanceEnum; 8 | 9 | public class CalciteSqlParser { 10 | 11 | /** 12 | * The parser config set the Lexical configuration 13 | * Ie 14 | * * how identifiers are quoted, 15 | * * whether they are converted to upper or lower 16 | * 17 | * This is the default and it can also be set on a {@link CalciteConnections#getConnectionWithoutModel connection level} 18 | * 19 | * @return a parser config with a MySql lexicon 20 | */ 21 | public static SqlParser.Config createMySqlConfig() { 22 | return SqlParser.configBuilder() 23 | .setCaseSensitive(false) 24 | .setLex(Lex.MYSQL) 25 | .setConformance(SqlConformanceEnum.DEFAULT) 26 | .setQuoting(Quoting.DOUBLE_QUOTE) 27 | .setConformance(SqlConformanceEnum.LENIENT) 28 | .setUnquotedCasing(Casing.UNCHANGED) 29 | .setQuotedCasing(Casing.UNCHANGED) 30 | .build(); 31 | } 32 | 33 | 34 | /** 35 | * @return a default parser config 36 | */ 37 | public static SqlParser.Config getDefault() { 38 | return SqlParser.Config.DEFAULT; 39 | } 40 | 41 | /** 42 | * Create a default parser 43 | * @param sql 44 | * @return 45 | */ 46 | public static SqlParser create(String sql) { 47 | 48 | return SqlParser.create(sql); 49 | } 50 | 51 | /** 52 | * 53 | * @param sql 54 | * @param config 55 | * @return 56 | */ 57 | public static SqlParser create(String sql, SqlParser.Config config) { 58 | 59 | return SqlParser.create(sql, config); 60 | } 61 | 62 | /** 63 | * 64 | * @return a parser config with case-insensivity 65 | */ 66 | public static SqlParser.Config getInsensitiveConfig() { 67 | return SqlParser.configBuilder() 68 | .setCaseSensitive(false) 69 | .build(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlSelect.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.sql.SqlNode; 4 | import org.apache.calcite.sql.SqlSelect; 5 | 6 | public class CalciteSqlSelect { 7 | 8 | static public void printInfo(SqlNode sqlNode) { 9 | if (sqlNode instanceof SqlSelect) { 10 | SqlSelect select = (SqlSelect) sqlNode; 11 | 12 | System.out.println("Select list is: " + select.getSelectList()); 13 | System.out.println("From clause is: " + select.getFrom()); 14 | System.out.println("Where clause is: " + select.getWhere()); 15 | System.out.println("Group clause is: " + select.getGroup()); 16 | 17 | } else { 18 | throw new RuntimeException("This is not a select statement. The class of this SqlNode is " + sqlNode.getClass().toString()); 19 | } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlValidation.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.mock.MockCatalogReaderSimple; 4 | import com.gerardnico.calcite.mock.MockJdbcCatalogReader; 5 | import com.gerardnico.calcite.mock.MockSqlOperatorTable; 6 | import org.apache.calcite.config.CalciteConnectionConfig; 7 | import org.apache.calcite.prepare.Prepare; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.rel.type.RelDataTypeFactoryImpl; 10 | import org.apache.calcite.rel.type.RelDataTypeSystem; 11 | import org.apache.calcite.sql.SqlNode; 12 | import org.apache.calcite.sql.fun.SqlStdOperatorTable; 13 | import org.apache.calcite.sql.type.SqlTypeFactoryImpl; 14 | import org.apache.calcite.sql.validate.*; 15 | import org.apache.calcite.tools.Planner; 16 | import org.apache.calcite.tools.ValidationException; 17 | 18 | public class CalciteSqlValidation { 19 | 20 | 21 | /** 22 | * 23 | * @param planner 24 | * @param sqlNode 25 | * @return the validated node 26 | */ 27 | static SqlNode validateFromPlanner(Planner planner, SqlNode sqlNode) { 28 | try { 29 | return planner.validate(sqlNode); 30 | } catch (ValidationException e) { 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | 35 | /** 36 | * 37 | * @param sqlNode 38 | * @return the validated node 39 | */ 40 | static SqlNode validateFromCustomValidator(SqlNode sqlNode) { 41 | return createCustomSqlValidator().validate(sqlNode); 42 | } 43 | 44 | /** 45 | * Sample code that returns a custom sql validator 46 | * @return 47 | */ 48 | static public CalciteSqlValidatorCustom createCustomSqlValidator() { 49 | 50 | RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT); 51 | MockJdbcCatalogReader catalogReader = new MockCatalogReaderSimple(typeFactory, true).init(); 52 | MockSqlOperatorTable sqlOperatorTable = new MockSqlOperatorTable(SqlStdOperatorTable.instance()); 53 | MockSqlOperatorTable.addRamp(sqlOperatorTable); 54 | 55 | CalciteSqlValidatorCustom sqlValidator = new CalciteSqlValidatorCustom( 56 | sqlOperatorTable, 57 | catalogReader, 58 | typeFactory, 59 | SqlConformanceEnum.DEFAULT); 60 | 61 | sqlValidator.setEnableTypeCoercion(true); 62 | 63 | final CalciteConnectionConfig calciteConnectionConfig = CalciteConnections.getContext().unwrap(CalciteConnectionConfig.class); 64 | if (calciteConnectionConfig != null) { 65 | sqlValidator.setDefaultNullCollation(calciteConnectionConfig.defaultNullCollation()); 66 | } 67 | 68 | return sqlValidator; 69 | } 70 | 71 | /** 72 | * Build a validator with: 73 | * * the {@link MockCatalogReaderSimple} 74 | * * a {@link SqlConformanceEnum#DEFAULT} 75 | * * and 76 | * @param sqlNode 77 | * @return the validated node 78 | */ 79 | static SqlNode validateFromUtilValidatorPlanner(Prepare.CatalogReader catalogReader, SqlNode sqlNode) { 80 | 81 | SqlStdOperatorTable operatorTable = SqlStdOperatorTable.instance(); 82 | SqlValidatorWithHints sqlValidator = SqlValidatorUtil.newValidator( 83 | operatorTable, 84 | catalogReader, 85 | catalogReader.getTypeFactory(), 86 | SqlConformanceEnum.DEFAULT 87 | ); 88 | return sqlValidator.validate(sqlNode); 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlValidatorCustom.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.mock.MockJdbcCatalogReader; 4 | import com.gerardnico.calcite.mock.MockCatalogReaderSimple; 5 | import com.gerardnico.calcite.mock.MockSqlOperatorTable; 6 | import org.apache.calcite.config.CalciteConnectionConfig; 7 | import org.apache.calcite.rel.type.RelDataTypeFactory; 8 | import org.apache.calcite.rel.type.RelDataTypeSystem; 9 | import org.apache.calcite.sql.SqlOperatorTable; 10 | import org.apache.calcite.sql.fun.SqlStdOperatorTable; 11 | import org.apache.calcite.sql.type.SqlTypeFactoryImpl; 12 | import org.apache.calcite.sql.validate.SqlConformance; 13 | import org.apache.calcite.sql.validate.SqlConformanceEnum; 14 | import org.apache.calcite.sql.validate.SqlValidatorCatalogReader; 15 | import org.apache.calcite.sql.validate.SqlValidatorImpl; 16 | 17 | /** 18 | * A validator is an object that will validate a {@link org.apache.calcite.rel.RelRoot} 19 | * against the schema (do we have the table, ...) 20 | * 21 | * Factory method is {@link #createSqlValidator()} 22 | * 23 | * This class that originates from the FarragoTestValidator 24 | */ 25 | public class CalciteSqlValidatorCustom extends SqlValidatorImpl { 26 | 27 | CalciteSqlValidatorCustom( 28 | SqlOperatorTable sqlOperatorTable, 29 | SqlValidatorCatalogReader catalogReader, 30 | RelDataTypeFactory typeFactory, 31 | SqlConformance conformance) { 32 | super(sqlOperatorTable, catalogReader, typeFactory, conformance); 33 | } 34 | 35 | @Override 36 | public boolean shouldExpandIdentifiers() { 37 | return true; 38 | } 39 | 40 | 41 | 42 | @Override 43 | public SqlValidatorCatalogReader getCatalogReader() { 44 | return super.getCatalogReader(); 45 | } 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/CalciteSqlVisitor.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | 4 | import org.apache.calcite.sql.*; 5 | import org.apache.calcite.sql.util.SqlBasicVisitor; 6 | 7 | public class CalciteSqlVisitor extends SqlBasicVisitor { 8 | 9 | @Override 10 | public Void visit(SqlLiteral literal) { 11 | System.out.println("Literal: " + literal); 12 | System.out.println("-------------------------------------"); 13 | return super.visit(literal); 14 | } 15 | 16 | @Override 17 | public Void visit(SqlCall call) { 18 | System.out.println("SqlCall: " + call); 19 | System.out.println("-------------------------------------"); 20 | return super.visit(call); 21 | } 22 | 23 | @Override 24 | public Void visit(SqlIdentifier id) { 25 | System.out.println("Identifier: " + id); 26 | System.out.println("-------------------------------------"); 27 | return super.visit(id); 28 | } 29 | 30 | @Override 31 | public Void visit(SqlNodeList nodeList) { 32 | if (nodeList.size()!=0) { 33 | System.out.println("NodeList: " + nodeList); 34 | System.out.println("-------------------------------------"); 35 | } 36 | return super.visit(nodeList); 37 | } 38 | 39 | @Override 40 | public Void visit(SqlDataTypeSpec type) { 41 | System.out.println("DataType: " + type); 42 | System.out.println("-------------------------------------"); 43 | return super.visit(type); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/adapter/AdapterContext.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.adapter; 2 | 3 | import com.gerardnico.calcite.schema.SchemaBuilder; 4 | import org.apache.calcite.DataContext; 5 | import org.apache.calcite.adapter.java.JavaTypeFactory; 6 | import org.apache.calcite.config.CalciteConnectionConfig; 7 | import org.apache.calcite.jdbc.CalcitePrepare; 8 | import org.apache.calcite.jdbc.CalciteSchema; 9 | import org.apache.calcite.schema.SchemaPlus; 10 | import org.apache.calcite.tools.RelRunner; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * 16 | * New adapters can be created by implementing {@link org.apache.calcite.jdbc.CalcitePrepare#convert(org.apache.calcite.jdbc.CalcitePrepare.Context, java.lang.String)} 17 | */ 18 | public class AdapterContext implements CalcitePrepare.Context { 19 | @Override 20 | public JavaTypeFactory getTypeFactory() { 21 | // adapter implementation 22 | return null; 23 | } 24 | 25 | @Override 26 | public CalciteSchema getRootSchema() { 27 | // adapter implementation 28 | final SchemaPlus jdbcFoodMart = SchemaBuilder.get() 29 | .addSchema(SchemaBuilder.SchemaSpec.JDBC_FOODMART) 30 | .getSchema(); 31 | return jdbcFoodMart.unwrap(CalciteSchema.class); 32 | } 33 | 34 | @Override 35 | public CalciteSchema getMutableRootSchema() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public List getDefaultSchemaPath() { 41 | return null; 42 | } 43 | 44 | @Override 45 | public CalciteConnectionConfig config() { 46 | return null; 47 | } 48 | 49 | @Override 50 | public CalcitePrepare.SparkHandler spark() { 51 | return null; 52 | } 53 | 54 | @Override 55 | public DataContext getDataContext() { 56 | return null; 57 | } 58 | 59 | @Override 60 | public List getObjectPath() { 61 | return null; 62 | } 63 | 64 | @Override 65 | public RelRunner getRelRunner() { 66 | return null; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/adapter/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Doc 3 | * 4 | * See {@link com.gerardnico.calcite.adapter.AdapterContext} 5 | */ 6 | package com.gerardnico.calcite.adapter; -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/demo/DatabaseConnectionSpec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.demo; 18 | 19 | /** Information necessary to create a JDBC connection. 20 | * 21 | *

Specify one to run tests against a different database. */ 22 | public class DatabaseConnectionSpec { 23 | public final String url; 24 | public final String username; 25 | public final String password; 26 | public final String driver; 27 | public final String schema; 28 | public final String catalog; 29 | 30 | public DatabaseConnectionSpec(String url, String username, String password, 31 | String driver, String schema) { 32 | this.url = url; 33 | this.username = username; 34 | this.password = password; 35 | this.driver = driver; 36 | this.schema = schema; 37 | this.catalog = null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/demo/DatabaseInstance.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.demo; 2 | 3 | import org.apache.calcite.config.CalciteSystemProperty; 4 | 5 | 6 | /** 7 | * Information necessary to create a JDBC connection. Specify one to run 8 | * tests against a different database. (hsqldb is the default.) 9 | */ 10 | public enum DatabaseInstance { 11 | HSQLDB( 12 | new DatabaseConnectionSpec("jdbc:hsqldb:res:foodmart", "FOODMART", "FOODMART", 13 | "org.hsqldb.jdbcDriver", "foodmart"), 14 | new DatabaseConnectionSpec("jdbc:hsqldb:res:scott", "SCOTT", 15 | "TIGER", "org.hsqldb.jdbcDriver", "SCOTT")), 16 | H2( 17 | new DatabaseConnectionSpec("jdbc:h2:" + CalciteSystemProperty.TEST_DATASET_PATH.value() 18 | + "/h2/target/foodmart;user=foodmart;password=foodmart", 19 | "foodmart", "foodmart", "org.h2.Driver", "foodmart"), null), 20 | MYSQL( 21 | new DatabaseConnectionSpec("jdbc:mysql://localhost/foodmart", "foodmart", 22 | "foodmart", "com.mysql.jdbc.Driver", "foodmart"), null), 23 | ORACLE( 24 | new DatabaseConnectionSpec("jdbc:oracle:thin:@localhost:1521:XE", "foodmart", 25 | "foodmart", "oracle.jdbc.OracleDriver", "FOODMART"), null), 26 | POSTGRESQL( 27 | new DatabaseConnectionSpec( 28 | "jdbc:postgresql://localhost/foodmart?user=foodmart&password=foodmart&searchpath=foodmart", 29 | "foodmart", "foodmart", "org.postgresql.Driver", "foodmart"), null); 30 | 31 | public final DatabaseConnectionSpec foodmart; 32 | public final DatabaseConnectionSpec scott; 33 | 34 | DatabaseInstance(DatabaseConnectionSpec foodmart, DatabaseConnectionSpec scott) { 35 | this.foodmart = foodmart; 36 | this.scott = scott; 37 | } 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/demo/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.demo; 2 | 3 | import com.gerardnico.calcite.CalciteConnections; 4 | import com.gerardnico.calcite.CalciteJdbc; 5 | import com.gerardnico.calcite.CalciteSchemaStatic; 6 | import com.gerardnico.calcite.schema.hr.HrDepartment; 7 | import com.gerardnico.calcite.schema.hr.HrEmployee; 8 | import com.gerardnico.calcite.schema.hr.HrSchema; 9 | import com.google.common.collect.ImmutableList; 10 | import org.apache.calcite.adapter.java.ReflectiveSchema; 11 | import org.apache.calcite.jdbc.CalciteConnection; 12 | import org.apache.calcite.schema.SchemaPlus; 13 | 14 | import java.sql.*; 15 | import java.util.Arrays; 16 | import java.util.Collections; 17 | 18 | /** 19 | * Code was adapted from Background 20 | */ 21 | public class HelloWorld { 22 | 23 | 24 | public static void main(String[] args) throws ClassNotFoundException, SQLException { 25 | 26 | // Create the connection 27 | try ( 28 | CalciteConnection calciteConnection = CalciteConnections.getConnectionWithoutModel(); 29 | ) { 30 | 31 | // Calcite - Add the Hr schema to the connection 32 | addHrSchema(calciteConnection); 33 | 34 | // Jdbc basic query 35 | try ( 36 | Statement statement = calciteConnection.createStatement(); 37 | ResultSet resultSet = statement.executeQuery( 38 | "select d.deptno, min(e.empid)\n" 39 | + "from hr.emps as e\n" 40 | + "join hr.depts as d\n" 41 | + " on e.deptno = d.deptno\n" 42 | + "group by d.deptno\n" 43 | + "having count(*) > 1"); 44 | ) { 45 | CalciteJdbc.printResultSet(resultSet); 46 | } 47 | } 48 | } 49 | 50 | /** 51 | * Add the {@link HrSchema} 52 | * 53 | * @param connection 54 | */ 55 | private static void addHrSchema(CalciteConnection connection) { 56 | 57 | SchemaPlus rootSchema = CalciteSchemaStatic.getRootSchemaFromCalciteConnection(connection); 58 | rootSchema.add("hr", new ReflectiveSchema(new HrSchema())); 59 | 60 | } 61 | 62 | public static class HrSchemaBig { 63 | @Override public String toString() { 64 | return "HrSchema"; 65 | } 66 | 67 | public final HrEmployee[] emps = { 68 | new HrEmployee(1, 10, "Bill", 10000, 1000), 69 | new HrEmployee(2, 20, "Eric", 8000, 500), 70 | new HrEmployee(3, 10, "Sebastian", 7000, null), 71 | new HrEmployee(4, 10, "Theodore", 11500, 250), 72 | new HrEmployee(5, 10, "Marjorie", 10000, 1000), 73 | new HrEmployee(6, 20, "Guy", 8000, 500), 74 | new HrEmployee(7, 10, "Dieudonne", 7000, null), 75 | new HrEmployee(8, 10, "Haroun", 11500, 250), 76 | new HrEmployee(9, 10, "Sarah", 10000, 1000), 77 | new HrEmployee(10, 20, "Gabriel", 8000, 500), 78 | new HrEmployee(11, 10, "Pierre", 7000, null), 79 | new HrEmployee(12, 10, "Paul", 11500, 250), 80 | new HrEmployee(13, 10, "Jacques", 100, 1000), 81 | new HrEmployee(14, 20, "Khawla", 8000, 500), 82 | new HrEmployee(15, 10, "Brielle", 7000, null), 83 | new HrEmployee(16, 10, "Hyuna", 11500, 250), 84 | new HrEmployee(17, 10, "Ahmed", 10000, 1000), 85 | new HrEmployee(18, 20, "Lara", 8000, 500), 86 | new HrEmployee(19, 10, "Capucine", 7000, null), 87 | new HrEmployee(20, 10, "Michelle", 11500, 250), 88 | new HrEmployee(21, 10, "Cerise", 10000, 1000), 89 | new HrEmployee(22, 80, "Travis", 8000, 500), 90 | new HrEmployee(23, 10, "Taylor", 7000, null), 91 | new HrEmployee(24, 10, "Seohyun", 11500, 250), 92 | new HrEmployee(25, 70, "Helen", 10000, 1000), 93 | new HrEmployee(26, 50, "Patric", 8000, 500), 94 | new HrEmployee(27, 10, "Clara", 7000, null), 95 | new HrEmployee(28, 10, "Catherine", 11500, 250), 96 | new HrEmployee(29, 10, "Anibal", 10000, 1000), 97 | new HrEmployee(30, 30, "Ursula", 8000, 500), 98 | new HrEmployee(31, 10, "Arturito", 7000, null), 99 | new HrEmployee(32, 70, "Diane", 11500, 250), 100 | new HrEmployee(33, 10, "Phoebe", 10000, 1000), 101 | new HrEmployee(34, 20, "Maria", 8000, 500), 102 | new HrEmployee(35, 10, "Edouard", 7000, null), 103 | new HrEmployee(36, 110, "Isabelle", 11500, 250), 104 | new HrEmployee(37, 120, "Olivier", 10000, 1000), 105 | new HrEmployee(38, 20, "Yann", 8000, 500), 106 | new HrEmployee(39, 60, "Ralf", 7000, null), 107 | new HrEmployee(40, 60, "Emmanuel", 11500, 250), 108 | new HrEmployee(41, 10, "Berenice", 10000, 1000), 109 | new HrEmployee(42, 20, "Kylie", 8000, 500), 110 | new HrEmployee(43, 80, "Natacha", 7000, null), 111 | new HrEmployee(44, 100, "Henri", 11500, 250), 112 | new HrEmployee(45, 90, "Pascal", 10000, 1000), 113 | new HrEmployee(46, 90, "Sabrina", 8000, 500), 114 | new HrEmployee(47, 8, "Riyad", 7000, null), 115 | new HrEmployee(48, 5, "Andy", 11500, 250), 116 | }; 117 | public final HrDepartment[] depts = { 118 | new HrDepartment(10, "Sales", Arrays.asList(emps[0], emps[2]),null), 119 | new HrDepartment(20, "Marketing", ImmutableList.of(), null), 120 | new HrDepartment(30, "HR", Collections.singletonList(emps[1]), null), 121 | new HrDepartment(40, "Administration", Arrays.asList(emps[0], emps[2]), 122 | null), 123 | new HrDepartment(50, "Design", ImmutableList.of(), null), 124 | new HrDepartment(60, "IT", Collections.singletonList(emps[1]), null), 125 | new HrDepartment(70, "Production", Arrays.asList(emps[0], emps[2]), 126 | null), 127 | new HrDepartment(80, "Finance", ImmutableList.of(), null), 128 | new HrDepartment(90, "Accounting", Collections.singletonList(emps[1]), null), 129 | new HrDepartment(100, "Research", Arrays.asList(emps[0], emps[2]), 130 | null), 131 | new HrDepartment(110, "Maintenance", ImmutableList.of(), null), 132 | new HrDepartment(120, "Client Support", Collections.singletonList(emps[1]), null), 133 | }; 134 | } 135 | 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/demo/JdbcStore.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.demo; 2 | 3 | import com.gerardnico.calcite.CalciteJdbc; 4 | import com.gerardnico.calcite.CalciteRel; 5 | import com.gerardnico.calcite.CalciteSql; 6 | import com.gerardnico.calcite.CalciteSqlDialect; 7 | import org.apache.calcite.adapter.jdbc.JdbcSchema; 8 | import org.apache.calcite.plan.RelTraitDef; 9 | import org.apache.calcite.rel.RelNode; 10 | import org.apache.calcite.schema.SchemaPlus; 11 | import org.apache.calcite.sql.SqlDialect; 12 | import org.apache.calcite.sql.SqlDialectFactoryImpl; 13 | import org.apache.calcite.sql.parser.SqlParser; 14 | import org.apache.calcite.tools.Frameworks; 15 | import org.apache.calcite.tools.Programs; 16 | import org.apache.calcite.tools.RelBuilder; 17 | 18 | import javax.sql.DataSource; 19 | import java.sql.Connection; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.List; 23 | 24 | /** 25 | * Main entry for every Jdbc related operation inside a Calcite environment 26 | */ 27 | public class JdbcStore { 28 | 29 | 30 | private final String driverClassName; 31 | private final String userName; 32 | private final String url; 33 | private final String password; 34 | private DataSource dataSource; 35 | private SchemaPlus defaultSchema; 36 | 37 | /** 38 | * @param url 39 | * @param driverClassName 40 | * @param username 41 | * @param password 42 | */ 43 | public JdbcStore(String url, String driverClassName, 44 | String username, String password) { 45 | this.url = (url != null ? url : "jdbc:h2:mem:test"); 46 | this.driverClassName = (driverClassName != null ? driverClassName : "org.h2.Driver"); 47 | this.userName = username; 48 | this.password = password; 49 | } 50 | 51 | public static JdbcStore createDefault() { 52 | return new JdbcStore(null, null, null, null); 53 | } 54 | 55 | DataSource getDataSource() { 56 | if (dataSource == null) { 57 | dataSource = JdbcSchema.dataSource(url, driverClassName, userName, password); 58 | } 59 | return dataSource; 60 | } 61 | 62 | 63 | public RelBuilder getRelBuilder() { 64 | return CalciteRel.createDataStoreBasedRelBuilder(dataSource); 65 | } 66 | 67 | /** 68 | * Create the calcite schema from the data source 69 | * 70 | * @return 71 | */ 72 | private SchemaPlus getOrCreateDefaultSchema() { 73 | if (defaultSchema == null) { 74 | defaultSchema = CalciteJdbc.getSchema(dataSource); 75 | } 76 | return defaultSchema; 77 | } 78 | 79 | public Connection getConnection() { 80 | try { 81 | return getDataSource().getConnection(); 82 | } catch (SQLException e) { 83 | throw new RuntimeException(e); 84 | } 85 | } 86 | 87 | 88 | /** 89 | * Execute a print a sql query 90 | * 91 | * @param sqlQuery 92 | */ 93 | public void executeAndPrintQuery(String sqlQuery) { 94 | try (ResultSet resultSet = getConnection().createStatement().executeQuery(sqlQuery)) { 95 | CalciteJdbc.printResultSet(resultSet); 96 | } catch (SQLException e) { 97 | System.out.println("FAILED! - " + e.getMessage()); 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | /** 103 | * @return the dialect for this connection 104 | */ 105 | public SqlDialect getDialect() { 106 | return CalciteSqlDialect.getDialectFromSqlConnection(getConnection()); 107 | } 108 | 109 | 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/CompoundNameColumn.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.rel.type.RelDataType; 20 | 21 | /** Column having names with multiple parts. */ 22 | final class CompoundNameColumn { 23 | final String first; 24 | final String second; 25 | final RelDataType type; 26 | 27 | CompoundNameColumn(String first, String second, RelDataType type) { 28 | this.first = first; 29 | this.second = second; 30 | this.type = type; 31 | } 32 | 33 | String getName() { 34 | return (first.isEmpty() ? "" : ("\"" + first + "\".")) 35 | + ("\"" + second + "\""); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/CompoundNameColumnResolver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.linq4j.Ord; 20 | import org.apache.calcite.rel.type.*; 21 | import org.apache.calcite.util.Pair; 22 | 23 | import java.util.*; 24 | 25 | /** ColumnResolver implementation that resolves CompoundNameColumn by simulating 26 | * Phoenix behaviors. */ 27 | final class CompoundNameColumnResolver implements MockJdbcCatalogReader.ColumnResolver { 28 | private final Map nameMap = new HashMap<>(); 29 | private final Map> groupMap = new HashMap<>(); 30 | private final String defaultColumnGroup; 31 | 32 | CompoundNameColumnResolver( 33 | List columns, String defaultColumnGroup) { 34 | this.defaultColumnGroup = defaultColumnGroup; 35 | for (Ord column : Ord.zip(columns)) { 36 | nameMap.put(column.e.getName(), column.i); 37 | Map subMap = 38 | groupMap.computeIfAbsent(column.e.first, k -> new HashMap<>()); 39 | subMap.put(column.e.second, column.i); 40 | } 41 | } 42 | 43 | @Override public List>> resolveColumn( 44 | RelDataType rowType, RelDataTypeFactory typeFactory, List names) { 45 | List>> ret = new ArrayList<>(); 46 | if (names.size() >= 2) { 47 | Map subMap = groupMap.get(names.get(0)); 48 | if (subMap != null) { 49 | Integer index = subMap.get(names.get(1)); 50 | if (index != null) { 51 | ret.add( 52 | new Pair>( 53 | rowType.getFieldList().get(index), 54 | names.subList(2, names.size()))); 55 | } 56 | } 57 | } 58 | 59 | final String columnName = names.get(0); 60 | final List remainder = names.subList(1, names.size()); 61 | Integer index = nameMap.get(columnName); 62 | if (index != null) { 63 | ret.add( 64 | new Pair>( 65 | rowType.getFieldList().get(index), remainder)); 66 | return ret; 67 | } 68 | 69 | final List priorityGroups = Arrays.asList("", defaultColumnGroup); 70 | for (String group : priorityGroups) { 71 | Map subMap = groupMap.get(group); 72 | if (subMap != null) { 73 | index = subMap.get(columnName); 74 | if (index != null) { 75 | ret.add( 76 | new Pair>( 77 | rowType.getFieldList().get(index), remainder)); 78 | return ret; 79 | } 80 | } 81 | } 82 | for (Map.Entry> entry : groupMap.entrySet()) { 83 | if (priorityGroups.contains(entry.getKey())) { 84 | continue; 85 | } 86 | index = entry.getValue().get(columnName); 87 | if (index != null) { 88 | ret.add( 89 | new Pair>( 90 | rowType.getFieldList().get(index), remainder)); 91 | } 92 | } 93 | 94 | if (ret.isEmpty() && names.size() == 1) { 95 | Map subMap = groupMap.get(columnName); 96 | if (subMap != null) { 97 | List> entries = 98 | new ArrayList<>(subMap.entrySet()); 99 | entries.sort((o1, o2) -> o1.getValue() - o2.getValue()); 100 | ret.add( 101 | new Pair>( 102 | new RelDataTypeFieldImpl( 103 | columnName, -1, 104 | createStructType( 105 | rowType, 106 | typeFactory, 107 | entries)), 108 | remainder)); 109 | } 110 | } 111 | 112 | return ret; 113 | } 114 | 115 | private static RelDataType createStructType( 116 | final RelDataType rowType, 117 | RelDataTypeFactory typeFactory, 118 | final List> entries) { 119 | return typeFactory.createStructType( 120 | StructKind.PEEK_FIELDS, 121 | new AbstractList() { 122 | @Override public RelDataType get(int index) { 123 | final int i = entries.get(index).getValue(); 124 | return rowType.getFieldList().get(i).getType(); 125 | } 126 | @Override public int size() { 127 | return entries.size(); 128 | } 129 | }, 130 | new AbstractList() { 131 | @Override public String get(int index) { 132 | return entries.get(index).getKey(); 133 | } 134 | @Override public int size() { 135 | return entries.size(); 136 | } 137 | }); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/CountingFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import com.google.common.collect.ImmutableList; 20 | import org.apache.calcite.plan.RelOptTable; 21 | import org.apache.calcite.rel.type.RelDataType; 22 | import org.apache.calcite.rel.type.RelDataTypeField; 23 | import org.apache.calcite.rex.RexBuilder; 24 | import org.apache.calcite.rex.RexNode; 25 | import org.apache.calcite.schema.ColumnStrategy; 26 | import org.apache.calcite.sql.SqlFunction; 27 | import org.apache.calcite.sql2rel.InitializerContext; 28 | import org.apache.calcite.sql2rel.InitializerExpressionFactory; 29 | import org.apache.calcite.sql2rel.NullInitializerExpressionFactory; 30 | 31 | import java.math.BigDecimal; 32 | import java.util.List; 33 | import java.util.concurrent.atomic.AtomicInteger; 34 | 35 | /** To check whether 36 | * {@link InitializerExpressionFactory#newColumnDefaultValue} is called. 37 | * 38 | *

If a column is in {@code defaultColumns}, returns 1 as the default 39 | * value. */ 40 | public class CountingFactory extends NullInitializerExpressionFactory { 41 | public static final ThreadLocal THREAD_CALL_COUNT = 42 | ThreadLocal.withInitial(AtomicInteger::new); 43 | 44 | private final List defaultColumns; 45 | 46 | CountingFactory(List defaultColumns) { 47 | this.defaultColumns = ImmutableList.copyOf(defaultColumns); 48 | } 49 | 50 | @Override public ColumnStrategy generationStrategy(RelOptTable table, 51 | int iColumn) { 52 | final RelDataTypeField field = 53 | table.getRowType().getFieldList().get(iColumn); 54 | if (defaultColumns.contains(field.getName())) { 55 | return ColumnStrategy.DEFAULT; 56 | } 57 | return super.generationStrategy(table, iColumn); 58 | } 59 | 60 | @Override public RexNode newColumnDefaultValue(RelOptTable table, 61 | int iColumn, InitializerContext context) { 62 | THREAD_CALL_COUNT.get().incrementAndGet(); 63 | final RelDataTypeField field = 64 | table.getRowType().getFieldList().get(iColumn); 65 | if (defaultColumns.contains(field.getName())) { 66 | final RexBuilder rexBuilder = context.getRexBuilder(); 67 | return rexBuilder.makeExactLiteral(BigDecimal.ONE); 68 | } 69 | return super.newColumnDefaultValue(table, iColumn, context); 70 | } 71 | 72 | @Override public RexNode newAttributeInitializer(RelDataType type, 73 | SqlFunction constructor, int iAttribute, 74 | List constructorArgs, InitializerContext context) { 75 | THREAD_CALL_COUNT.get().incrementAndGet(); 76 | return super.newAttributeInitializer(type, constructor, iAttribute, 77 | constructorArgs, context); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/EmpInitializerExpressionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.plan.RelOptTable; 20 | import org.apache.calcite.rel.type.RelDataTypeFactory; 21 | import org.apache.calcite.rex.RexBuilder; 22 | import org.apache.calcite.rex.RexNode; 23 | import org.apache.calcite.schema.ColumnStrategy; 24 | import org.apache.calcite.sql.type.SqlTypeName; 25 | import org.apache.calcite.sql2rel.InitializerContext; 26 | import org.apache.calcite.sql2rel.NullInitializerExpressionFactory; 27 | 28 | import java.math.BigDecimal; 29 | 30 | /** Default values for the "EMPDEFAULTS" table. */ 31 | class EmpInitializerExpressionFactory 32 | extends NullInitializerExpressionFactory { 33 | @Override public ColumnStrategy generationStrategy(RelOptTable table, 34 | int iColumn) { 35 | switch (iColumn) { 36 | case 0: 37 | case 1: 38 | case 5: 39 | return ColumnStrategy.DEFAULT; 40 | default: 41 | return super.generationStrategy(table, iColumn); 42 | } 43 | } 44 | 45 | @Override public RexNode newColumnDefaultValue(RelOptTable table, 46 | int iColumn, InitializerContext context) { 47 | final RexBuilder rexBuilder = context.getRexBuilder(); 48 | final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory(); 49 | switch (iColumn) { 50 | case 0: 51 | return rexBuilder.makeExactLiteral(new BigDecimal(123), 52 | typeFactory.createSqlType(SqlTypeName.INTEGER)); 53 | case 1: 54 | return rexBuilder.makeLiteral("Bob"); 55 | case 5: 56 | return rexBuilder.makeExactLiteral(new BigDecimal(555), 57 | typeFactory.createSqlType(SqlTypeName.INTEGER)); 58 | default: 59 | return super.newColumnDefaultValue(table, iColumn, context); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/Fixture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.rel.type.*; 20 | import org.apache.calcite.sql.SqlIdentifier; 21 | import org.apache.calcite.sql.parser.SqlParserPos; 22 | import org.apache.calcite.sql.type.ObjectSqlType; 23 | import org.apache.calcite.sql.type.SqlTypeName; 24 | 25 | import java.util.Arrays; 26 | 27 | /** Types used during initialization. */ 28 | final class Fixture extends AbstractFixture { 29 | final RelDataType intType = sqlType(SqlTypeName.INTEGER); 30 | final RelDataType intTypeNull = nullable(intType); 31 | final RelDataType bigintType = sqlType(SqlTypeName.BIGINT); 32 | final RelDataType decimalType = sqlType(SqlTypeName.DECIMAL); 33 | final RelDataType varcharType = sqlType(SqlTypeName.VARCHAR); 34 | final RelDataType varcharTypeNull = sqlType(SqlTypeName.VARCHAR); 35 | final RelDataType varchar5Type = sqlType(SqlTypeName.VARCHAR, 5); 36 | final RelDataType varchar10Type = sqlType(SqlTypeName.VARCHAR, 10); 37 | final RelDataType varchar10TypeNull = nullable(varchar10Type); 38 | final RelDataType varchar20Type = sqlType(SqlTypeName.VARCHAR, 20); 39 | final RelDataType varchar20TypeNull = nullable(varchar20Type); 40 | final RelDataType timestampType = sqlType(SqlTypeName.TIMESTAMP); 41 | final RelDataType timestampTypeNull = nullable(timestampType); 42 | final RelDataType dateType = sqlType(SqlTypeName.DATE); 43 | final RelDataType booleanType = sqlType(SqlTypeName.BOOLEAN); 44 | final RelDataType booleanTypeNull = nullable(booleanType); 45 | final RelDataType rectilinearCoordType = typeFactory.builder() 46 | .add("X", intType) 47 | .add("Y", intType) 48 | .build(); 49 | final RelDataType rectilinearPeekCoordType = typeFactory.builder() 50 | .add("X", intType) 51 | .add("Y", intType) 52 | .add("unit", varchar20Type) 53 | .kind(StructKind.PEEK_FIELDS) 54 | .build(); 55 | final RelDataType rectilinearPeekNoExpandCoordType = typeFactory.builder() 56 | .add("M", intType) 57 | .add("SUB", 58 | typeFactory.builder() 59 | .add("A", intType) 60 | .add("B", intType) 61 | .kind(StructKind.PEEK_FIELDS_NO_EXPAND) 62 | .build()) 63 | .kind(StructKind.PEEK_FIELDS_NO_EXPAND) 64 | .build(); 65 | final RelDataType abRecordType = typeFactory.builder() 66 | .add("A", varchar10Type) 67 | .add("B", varchar10Type) 68 | .build();; 69 | final RelDataType skillRecordType = typeFactory.builder() 70 | .add("TYPE", varchar10Type) 71 | .add("DESC", varchar20Type) 72 | .add("OTHERS", abRecordType) 73 | .build(); 74 | final RelDataType empRecordType = typeFactory.builder() 75 | .add("EMPNO", intType) 76 | .add("ENAME", varchar10Type) 77 | .add("DETAIL", typeFactory.builder() 78 | .add("SKILLS", array(skillRecordType)).build()) 79 | .build(); 80 | final RelDataType empListType = array(empRecordType); 81 | final ObjectSqlType addressType = new ObjectSqlType(SqlTypeName.STRUCTURED, 82 | new SqlIdentifier("ADDRESS", SqlParserPos.ZERO), 83 | false, 84 | Arrays.asList( 85 | new RelDataTypeFieldImpl("STREET", 0, varchar20Type), 86 | new RelDataTypeFieldImpl("CITY", 1, varchar20Type), 87 | new RelDataTypeFieldImpl("ZIP", 2, intType), 88 | new RelDataTypeFieldImpl("STATE", 3, varchar20Type)), 89 | RelDataTypeComparability.NONE); 90 | // Row(f0 int, f1 varchar) 91 | final RelDataType recordType1 = typeFactory.createStructType( 92 | Arrays.asList(intType, varcharType), 93 | Arrays.asList("f0", "f1")); 94 | // Row(f0 int not null, f1 varchar null) 95 | final RelDataType recordType2 = typeFactory.createStructType( 96 | Arrays.asList(intType, nullable(varcharType)), 97 | Arrays.asList("f0", "f1")); 98 | // Row(f0 Row(ff0 int not null, ff1 varchar null) null, f1 timestamp not null) 99 | final RelDataType recordType3 = typeFactory.createStructType( 100 | Arrays.asList( 101 | nullable( 102 | typeFactory.createStructType(Arrays.asList(intType, varcharTypeNull), 103 | Arrays.asList("ff0", "ff1"))), timestampType), Arrays.asList("f0", "f1")); 104 | // Row(f0 bigint not null, f1 decimal null) array 105 | final RelDataType recordType4 = array( 106 | typeFactory.createStructType( 107 | Arrays.asList(bigintType, nullable(decimalType)), 108 | Arrays.asList("f0", "f1"))); 109 | // Row(f0 varchar not null, f1 timestamp null) multiset 110 | final RelDataType recordType5 = typeFactory.createMultisetType( 111 | typeFactory.createStructType( 112 | Arrays.asList(varcharType, timestampTypeNull), 113 | Arrays.asList("f0", "f1")), 114 | -1); 115 | final RelDataType intArrayType = array(intType); 116 | final RelDataType varchar5ArrayType = array(varchar5Type); 117 | final RelDataType intArrayArrayType = array(intArrayType); 118 | final RelDataType varchar5ArrayArrayType = array(varchar5ArrayType); 119 | final RelDataType intMultisetType = typeFactory.createMultisetType(intType, -1); 120 | final RelDataType varchar5MultisetType = typeFactory.createMultisetType(varchar5Type, -1); 121 | final RelDataType intMultisetArrayType = array(intMultisetType); 122 | final RelDataType varchar5MultisetArrayType = array(varchar5MultisetType); 123 | final RelDataType intArrayMultisetType = typeFactory.createMultisetType(intArrayType, -1); 124 | // Row(f0 int array multiset, f1 varchar(5) array) array multiset 125 | final RelDataType rowArrayMultisetType = typeFactory.createMultisetType( 126 | array( 127 | typeFactory.createStructType( 128 | Arrays.asList(intArrayMultisetType, varchar5ArrayType), 129 | Arrays.asList("f0", "f1"))), 130 | -1); 131 | 132 | Fixture(RelDataTypeFactory typeFactory) { 133 | super(typeFactory); 134 | } 135 | 136 | private RelDataType nullable(RelDataType type) { 137 | return typeFactory.createTypeWithNullability(type, true); 138 | } 139 | 140 | private RelDataType sqlType(SqlTypeName typeName, int... args) { 141 | assert args.length < 3 : "unknown size of additional int args"; 142 | return args.length == 2 ? typeFactory.createSqlType(typeName, args[0], args[1]) 143 | : args.length == 1 ? typeFactory.createSqlType(typeName, args[0]) 144 | : typeFactory.createSqlType(typeName); 145 | } 146 | 147 | private RelDataType array(RelDataType type) { 148 | return typeFactory.createArrayType(type, -1); 149 | } 150 | } 151 | 152 | /** 153 | * Just a little trick to store factory ref before field init in fixture 154 | */ 155 | abstract class AbstractFixture { 156 | final RelDataTypeFactory typeFactory; 157 | 158 | AbstractFixture(RelDataTypeFactory typeFactory) { 159 | this.typeFactory = typeFactory; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/MockCatalogReaderDynamic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.rel.type.RelDataType; 20 | import org.apache.calcite.rel.type.RelDataTypeFactory; 21 | import org.apache.calcite.schema.TableMacro; 22 | import org.apache.calcite.schema.TranslatableTable; 23 | import org.apache.calcite.schema.impl.ViewTable; 24 | import org.apache.calcite.sql.type.SqlTypeName; 25 | 26 | import java.util.Arrays; 27 | import java.util.Collections; 28 | import java.util.List; 29 | import java.util.function.Supplier; 30 | 31 | /** 32 | * Registers dynamic tables. 33 | * 34 | *

Not thread-safe. 35 | */ 36 | public class MockCatalogReaderDynamic extends MockJdbcCatalogReader { 37 | /** 38 | * Creates a MockCatalogReader. 39 | * 40 | *

Caller must then call {@link #init} to populate with data.

41 | * 42 | * @param typeFactory Type factory 43 | * @param caseSensitive case sensitivity 44 | */ 45 | public MockCatalogReaderDynamic(RelDataTypeFactory typeFactory, 46 | boolean caseSensitive) { 47 | super(typeFactory, caseSensitive); 48 | } 49 | 50 | @Override public MockJdbcCatalogReader init() { 51 | // Register "DYNAMIC" schema. 52 | MockSchema schema = new MockSchema("SALES"); 53 | registerSchema(schema); 54 | 55 | MockDynamicTable nationTable = 56 | new MockDynamicTable(schema.getCatalogName(), 57 | schema.getName(), "NATION"); 58 | registerTable(nationTable); 59 | 60 | Supplier customerTableSupplier = () -> 61 | new MockDynamicTable(schema.getCatalogName(), schema.getName(), "CUSTOMER"); 62 | 63 | MockDynamicTable customerTable = customerTableSupplier.get(); 64 | registerTable(customerTable); 65 | 66 | // CREATE TABLE "REGION" - static table with known schema. 67 | final RelDataType intType = 68 | typeFactory.createSqlType(SqlTypeName.INTEGER); 69 | final RelDataType varcharType = 70 | typeFactory.createSqlType(SqlTypeName.VARCHAR); 71 | 72 | MockTable regionTable = 73 | MockTable.create(this, schema, "REGION", false, 100); 74 | regionTable.addColumn("R_REGIONKEY", intType); 75 | regionTable.addColumn("R_NAME", varcharType); 76 | regionTable.addColumn("R_COMMENT", varcharType); 77 | registerTable(regionTable); 78 | 79 | List custModifiableViewNames = Arrays.asList( 80 | schema.getCatalogName(), schema.getName(), "CUSTOMER_MODIFIABLEVIEW"); 81 | TableMacro custModifiableViewMacro = MockModifiableViewRelOptTable.viewMacro(rootSchema, 82 | "select n_name from SALES.CUSTOMER", custModifiableViewNames.subList(0, 2), 83 | Collections.singletonList(custModifiableViewNames.get(2)), true); 84 | TranslatableTable empModifiableView = custModifiableViewMacro.apply(Collections.emptyList()); 85 | MockTable mockCustViewTable = MockRelViewTable.create( 86 | (ViewTable) empModifiableView, this, 87 | custModifiableViewNames.get(0), custModifiableViewNames.get(1), 88 | custModifiableViewNames.get(2), false, 20, null); 89 | registerTable(mockCustViewTable); 90 | 91 | // re-registers customer table to clear its row type after view registration 92 | reregisterTable(customerTableSupplier.get()); 93 | 94 | return this; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/MockCatalogReaderExtended.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import com.google.common.collect.ImmutableList; 20 | import org.apache.calcite.rel.type.RelDataTypeFactory; 21 | import org.apache.calcite.schema.TableMacro; 22 | import org.apache.calcite.schema.TranslatableTable; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | 28 | /** Adds some extra tables to the mock catalog. These increase the time and 29 | * complexity of initializing the catalog (because they contain views whose 30 | * SQL needs to be parsed) and so are not used for all tests. */ 31 | public class MockCatalogReaderExtended extends MockCatalogReaderSimple { 32 | /** 33 | * Creates a MockCatalogReader. 34 | * 35 | *

Caller must then call {@link #init} to populate with data.

36 | * 37 | * @param typeFactory Type factory 38 | * @param caseSensitive case sensitivity 39 | */ 40 | public MockCatalogReaderExtended(RelDataTypeFactory typeFactory, 41 | boolean caseSensitive) { 42 | super(typeFactory, caseSensitive); 43 | } 44 | 45 | @Override public MockJdbcCatalogReader init() { 46 | super.init(); 47 | 48 | MockSchema salesSchema = new MockSchema("SALES"); 49 | // Same as "EMP_20" except it uses ModifiableViewTable which populates 50 | // constrained columns with default values on INSERT and has a single constraint on DEPTNO. 51 | List empModifiableViewNames = ImmutableList.of( 52 | salesSchema.getCatalogName(), salesSchema.getName(), "EMP_MODIFIABLEVIEW"); 53 | TableMacro empModifiableViewMacro = MockModifiableViewRelOptTable.viewMacro(rootSchema, 54 | "select EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, SLACKER from EMPDEFAULTS" 55 | + " where DEPTNO = 20", empModifiableViewNames.subList(0, 2), 56 | ImmutableList.of(empModifiableViewNames.get(2)), true); 57 | TranslatableTable empModifiableView = empModifiableViewMacro.apply(ImmutableList.of()); 58 | MockModifiableViewRelOptTable mockEmpViewTable = MockModifiableViewRelOptTable.create( 59 | (MockModifiableViewRelOptTable.MockModifiableViewTable) empModifiableView, this, 60 | empModifiableViewNames.get(0), empModifiableViewNames.get(1), 61 | empModifiableViewNames.get(2), false, 20, null); 62 | registerTable(mockEmpViewTable); 63 | 64 | // Same as "EMP_MODIFIABLEVIEW" except that all columns are in the view, columns are reordered, 65 | // and there is an `extra` extended column. 66 | List empModifiableViewNames2 = ImmutableList.of( 67 | salesSchema.getCatalogName(), salesSchema.getName(), "EMP_MODIFIABLEVIEW2"); 68 | TableMacro empModifiableViewMacro2 = MockModifiableViewRelOptTable.viewMacro(rootSchema, 69 | "select ENAME, EMPNO, JOB, DEPTNO, SLACKER, SAL, EXTRA, HIREDATE, MGR, COMM" 70 | + " from EMPDEFAULTS extend (EXTRA boolean)" 71 | + " where DEPTNO = 20", empModifiableViewNames2.subList(0, 2), 72 | ImmutableList.of(empModifiableViewNames.get(2)), true); 73 | TranslatableTable empModifiableView2 = empModifiableViewMacro2.apply(ImmutableList.of()); 74 | MockModifiableViewRelOptTable mockEmpViewTable2 = MockModifiableViewRelOptTable.create( 75 | (MockModifiableViewRelOptTable.MockModifiableViewTable) empModifiableView2, this, 76 | empModifiableViewNames2.get(0), empModifiableViewNames2.get(1), 77 | empModifiableViewNames2.get(2), false, 20, null); 78 | registerTable(mockEmpViewTable2); 79 | 80 | // Same as "EMP_MODIFIABLEVIEW" except that comm is not in the view. 81 | List empModifiableViewNames3 = ImmutableList.of( 82 | salesSchema.getCatalogName(), salesSchema.getName(), "EMP_MODIFIABLEVIEW3"); 83 | TableMacro empModifiableViewMacro3 = MockModifiableViewRelOptTable.viewMacro(rootSchema, 84 | "select EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, SLACKER from EMPDEFAULTS" 85 | + " where DEPTNO = 20", empModifiableViewNames3.subList(0, 2), 86 | ImmutableList.of(empModifiableViewNames3.get(2)), true); 87 | TranslatableTable empModifiableView3 = empModifiableViewMacro3.apply(ImmutableList.of()); 88 | MockModifiableViewRelOptTable mockEmpViewTable3 = MockModifiableViewRelOptTable.create( 89 | (MockModifiableViewRelOptTable.MockModifiableViewTable) empModifiableView3, this, 90 | empModifiableViewNames3.get(0), empModifiableViewNames3.get(1), 91 | empModifiableViewNames3.get(2), false, 20, null); 92 | registerTable(mockEmpViewTable3); 93 | 94 | MockSchema structTypeSchema = new MockSchema("STRUCT"); 95 | registerSchema(structTypeSchema); 96 | final Fixture f = new Fixture(typeFactory); 97 | final List columnsExtended = Arrays.asList( 98 | new CompoundNameColumn("", "K0", f.varchar20TypeNull), 99 | new CompoundNameColumn("", "C1", f.varchar20TypeNull), 100 | new CompoundNameColumn("F0", "C0", f.intType), 101 | new CompoundNameColumn("F1", "C1", f.intTypeNull)); 102 | final List extendedColumns = 103 | new ArrayList(columnsExtended); 104 | extendedColumns.add(new CompoundNameColumn("F2", "C2", f.varchar20Type)); 105 | final CompoundNameColumnResolver structExtendedTableResolver = 106 | new CompoundNameColumnResolver(extendedColumns, "F0"); 107 | final MockTable structExtendedTypeTable = 108 | MockTable.create(this, structTypeSchema, "T_EXTEND", false, 100, 109 | structExtendedTableResolver); 110 | for (CompoundNameColumn column : columnsExtended) { 111 | structExtendedTypeTable.addColumn(column.getName(), column.type); 112 | } 113 | registerTable(structExtendedTypeTable); 114 | 115 | // Defines a table with 116 | // schema(A int, B bigint, C varchar(10), D as a + 1 stored, E as b * 3 virtual). 117 | MockSchema virtualColumnsSchema = new MockSchema("VIRTUALCOLUMNS"); 118 | registerSchema(virtualColumnsSchema); 119 | final MockTable virtualColumnsTable1 = 120 | MockTable.create(this, virtualColumnsSchema, "VC_T1", false, 100, 121 | null, new VirtualColumnsExpressionFactory(), false); 122 | virtualColumnsTable1.addColumn("A", f.intTypeNull); 123 | virtualColumnsTable1.addColumn("B", f.bigintType); 124 | virtualColumnsTable1.addColumn("C", f.varchar10Type); 125 | virtualColumnsTable1.addColumn("D", f.intTypeNull); 126 | virtualColumnsTable1.addColumn("E", f.bigintType); 127 | // Same schema with VC_T1 but with different table name. 128 | final MockTable virtualColumnsTable2 = 129 | MockTable.create(this, virtualColumnsSchema, "VC_T2", false, 100, 130 | null, new VirtualColumnsExpressionFactory(), false); 131 | virtualColumnsTable2.addColumn("A", f.intTypeNull); 132 | virtualColumnsTable2.addColumn("B", f.bigintType); 133 | virtualColumnsTable2.addColumn("C", f.varchar10Type); 134 | virtualColumnsTable2.addColumn("D", f.intTypeNull); 135 | virtualColumnsTable2.addColumn("E", f.bigintType); 136 | registerTable(virtualColumnsTable1); 137 | registerTable(virtualColumnsTable2); 138 | 139 | // Register table with complex data type rows. 140 | MockSchema complexTypeColumnsSchema = new MockSchema("COMPLEXTYPES"); 141 | registerSchema(complexTypeColumnsSchema); 142 | final MockTable complexTypeColumnsTable = 143 | MockTable.create(this, complexTypeColumnsSchema, "CTC_T1", 144 | false, 100); 145 | complexTypeColumnsTable.addColumn("A", f.recordType1); 146 | complexTypeColumnsTable.addColumn("B", f.recordType2); 147 | complexTypeColumnsTable.addColumn("C", f.recordType3); 148 | complexTypeColumnsTable.addColumn("D", f.recordType4); 149 | complexTypeColumnsTable.addColumn("E", f.recordType5); 150 | complexTypeColumnsTable.addColumn("intArrayType", f.intArrayType); 151 | complexTypeColumnsTable.addColumn("varchar5ArrayType", f.varchar5ArrayType); 152 | complexTypeColumnsTable.addColumn("intArrayArrayType", f.intArrayArrayType); 153 | complexTypeColumnsTable.addColumn("varchar5ArrayArrayType", f.varchar5ArrayArrayType); 154 | complexTypeColumnsTable.addColumn("intMultisetType", f.intMultisetType); 155 | complexTypeColumnsTable.addColumn("varchar5MultisetType", f.varchar5MultisetType); 156 | complexTypeColumnsTable.addColumn("intMultisetArrayType", f.intMultisetArrayType); 157 | complexTypeColumnsTable.addColumn("varchar5MultisetArrayType", 158 | f.varchar5MultisetArrayType); 159 | complexTypeColumnsTable.addColumn("intArrayMultisetType", f.intArrayMultisetType); 160 | complexTypeColumnsTable.addColumn("rowArrayMultisetType", f.rowArrayMultisetType); 161 | registerTable(complexTypeColumnsTable); 162 | 163 | return this; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/MockRelOptCost.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.plan.RelOptCost; 20 | 21 | /** 22 | * MockRelOptCost is a mock implementation of the {@link RelOptCost} interface. 23 | * TODO: constructors for various scenarios 24 | */ 25 | public class MockRelOptCost implements RelOptCost { 26 | //~ Methods ---------------------------------------------------------------- 27 | 28 | @Override public boolean equals(Object obj) { 29 | return this == obj 30 | || obj instanceof MockRelOptCost 31 | && equals((MockRelOptCost) obj); 32 | } 33 | 34 | @Override public int hashCode() { 35 | return 1; 36 | } 37 | 38 | public double getCpu() { 39 | return 0; 40 | } 41 | 42 | public boolean isInfinite() { 43 | return false; 44 | } 45 | 46 | public double getIo() { 47 | return 0; 48 | } 49 | 50 | public boolean isLe(RelOptCost cost) { 51 | return true; 52 | } 53 | 54 | public boolean isLt(RelOptCost cost) { 55 | return false; 56 | } 57 | 58 | public double getRows() { 59 | return 0; 60 | } 61 | 62 | public boolean equals(RelOptCost cost) { 63 | return true; 64 | } 65 | 66 | public boolean isEqWithEpsilon(RelOptCost cost) { 67 | return true; 68 | } 69 | 70 | public RelOptCost minus(RelOptCost cost) { 71 | return this; 72 | } 73 | 74 | public RelOptCost multiplyBy(double factor) { 75 | return this; 76 | } 77 | 78 | public double divideBy(RelOptCost cost) { 79 | return 1; 80 | } 81 | 82 | public RelOptCost plus(RelOptCost cost) { 83 | return this; 84 | } 85 | 86 | public String toString() { 87 | return "MockRelOptCost(0)"; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/MockRelOptPlanner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import com.google.common.collect.ImmutableList; 20 | import org.apache.calcite.plan.*; 21 | import org.apache.calcite.rel.RelNode; 22 | import org.apache.calcite.rex.RexExecutorImpl; 23 | import org.apache.calcite.schema.Schemas; 24 | import org.apache.calcite.util.Pair; 25 | 26 | import java.util.ArrayList; 27 | import java.util.Collections; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | /** 32 | * MockRelOptPlanner is a mock implementation of the {@link RelOptPlanner} 33 | * interface. 34 | */ 35 | public class MockRelOptPlanner extends AbstractRelOptPlanner { 36 | //~ Instance fields -------------------------------------------------------- 37 | 38 | private RelNode root; 39 | 40 | private RelOptRule rule; 41 | 42 | private RelNode transformationResult; 43 | 44 | private long metadataTimestamp = 0L; 45 | 46 | //~ Methods ---------------------------------------------------------------- 47 | 48 | /** Creates MockRelOptPlanner. */ 49 | public MockRelOptPlanner(Context context) { 50 | super(RelOptCostImpl.FACTORY, context); 51 | setExecutor(new RexExecutorImpl(Schemas.createDataContext(null, null))); 52 | } 53 | 54 | // implement RelOptPlanner 55 | public void setRoot(RelNode rel) { 56 | this.root = rel; 57 | } 58 | 59 | // implement RelOptPlanner 60 | public RelNode getRoot() { 61 | return root; 62 | } 63 | 64 | @Override public void clear() { 65 | super.clear(); 66 | this.rule = null; 67 | } 68 | 69 | public List getRules() { 70 | return rule == null 71 | ? ImmutableList.of() : ImmutableList.of(rule); 72 | } 73 | 74 | public boolean addRule(RelOptRule rule) { 75 | assert this.rule == null 76 | : "MockRelOptPlanner only supports a single rule"; 77 | this.rule = rule; 78 | 79 | return false; 80 | } 81 | 82 | public boolean removeRule(RelOptRule rule) { 83 | return false; 84 | } 85 | 86 | // implement RelOptPlanner 87 | public RelNode changeTraits(RelNode rel, RelTraitSet toTraits) { 88 | return rel; 89 | } 90 | 91 | // implement RelOptPlanner 92 | public RelNode findBestExp() { 93 | if (rule != null) { 94 | matchRecursive(root, null, -1); 95 | } 96 | return root; 97 | } 98 | 99 | /** 100 | * Recursively matches a rule. 101 | * 102 | * @param rel Relational expression 103 | * @param parent Parent relational expression 104 | * @param ordinalInParent Ordinal of relational expression among its 105 | * siblings 106 | * @return whether match occurred 107 | */ 108 | private boolean matchRecursive( 109 | RelNode rel, 110 | RelNode parent, 111 | int ordinalInParent) { 112 | List bindings = new ArrayList(); 113 | if (match( 114 | rule.getOperand(), 115 | rel, 116 | bindings)) { 117 | MockRuleCall call = 118 | new MockRuleCall( 119 | this, 120 | rule.getOperand(), 121 | bindings.toArray(new RelNode[0])); 122 | if (rule.matches(call)) { 123 | rule.onMatch(call); 124 | } 125 | } 126 | 127 | if (transformationResult != null) { 128 | if (parent == null) { 129 | root = transformationResult; 130 | } else { 131 | parent.replaceInput(ordinalInParent, transformationResult); 132 | } 133 | return true; 134 | } 135 | 136 | List children = rel.getInputs(); 137 | for (int i = 0; i < children.size(); ++i) { 138 | if (matchRecursive(children.get(i), rel, i)) { 139 | return true; 140 | } 141 | } 142 | return false; 143 | } 144 | 145 | /** 146 | * Matches a relational expression to a rule. 147 | * 148 | * @param operand Root operand of rule 149 | * @param rel Relational expression 150 | * @param bindings Bindings, populated on successful match 151 | * @return whether relational expression matched rule 152 | */ 153 | private boolean match( 154 | RelOptRuleOperand operand, 155 | RelNode rel, 156 | List bindings) { 157 | if (!operand.matches(rel)) { 158 | return false; 159 | } 160 | bindings.add(rel); 161 | switch (operand.childPolicy) { 162 | case ANY: 163 | return true; 164 | } 165 | List childOperands = operand.getChildOperands(); 166 | List childRels = rel.getInputs(); 167 | if (childOperands.size() != childRels.size()) { 168 | return false; 169 | } 170 | for (Pair pair 171 | : Pair.zip(childOperands, childRels)) { 172 | if (!match(pair.left, pair.right, bindings)) { 173 | return false; 174 | } 175 | } 176 | return true; 177 | } 178 | 179 | // implement RelOptPlanner 180 | public RelNode register( 181 | RelNode rel, 182 | RelNode equivRel) { 183 | return rel; 184 | } 185 | 186 | // implement RelOptPlanner 187 | public RelNode ensureRegistered(RelNode rel, RelNode equivRel) { 188 | return rel; 189 | } 190 | 191 | // implement RelOptPlanner 192 | public boolean isRegistered(RelNode rel) { 193 | return true; 194 | } 195 | 196 | @Override public long getRelMetadataTimestamp(RelNode rel) { 197 | return metadataTimestamp; 198 | } 199 | 200 | /** Allow tests to tweak the timestamp. */ 201 | public void setRelMetadataTimestamp(long metadataTimestamp) { 202 | this.metadataTimestamp = metadataTimestamp; 203 | } 204 | 205 | //~ Inner Classes ---------------------------------------------------------- 206 | 207 | /** Mock call to a planner rule. */ 208 | private class MockRuleCall extends RelOptRuleCall { 209 | /** 210 | * Creates a MockRuleCall. 211 | * 212 | * @param planner Planner 213 | * @param operand Operand 214 | * @param rels List of matched relational expressions 215 | */ 216 | MockRuleCall( 217 | RelOptPlanner planner, 218 | RelOptRuleOperand operand, 219 | RelNode[] rels) { 220 | super( 221 | planner, 222 | operand, 223 | rels, 224 | Collections.emptyMap()); 225 | } 226 | 227 | // implement RelOptRuleCall 228 | public void transformTo(RelNode rel, Map equiv, 229 | RelHintsPropagator handler) { 230 | transformationResult = rel; 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/MockSqlOperatorTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import com.google.common.collect.ImmutableList; 20 | import org.apache.calcite.rel.type.RelDataType; 21 | import org.apache.calcite.rel.type.RelDataTypeFactory; 22 | import org.apache.calcite.sql.*; 23 | import org.apache.calcite.sql.parser.SqlParserPos; 24 | import org.apache.calcite.sql.type.OperandTypes; 25 | import org.apache.calcite.sql.type.ReturnTypes; 26 | import org.apache.calcite.sql.type.SqlTypeFamily; 27 | import org.apache.calcite.sql.type.SqlTypeName; 28 | import org.apache.calcite.sql.util.ChainedSqlOperatorTable; 29 | import org.apache.calcite.sql.util.ListSqlOperatorTable; 30 | import org.apache.calcite.util.Optionality; 31 | 32 | /** 33 | * Mock operator table for testing purposes. Contains the standard SQL operator 34 | * table, plus a list of operators. 35 | */ 36 | public class MockSqlOperatorTable extends ChainedSqlOperatorTable { 37 | private final ListSqlOperatorTable listOpTab; 38 | 39 | public MockSqlOperatorTable(SqlOperatorTable parentTable) { 40 | super(ImmutableList.of(parentTable, new ListSqlOperatorTable())); 41 | listOpTab = (ListSqlOperatorTable) tableList.get(1); 42 | } 43 | 44 | /** 45 | * Adds an operator to this table. 46 | */ 47 | public void addOperator(SqlOperator op) { 48 | listOpTab.add(op); 49 | } 50 | 51 | public static void addRamp(MockSqlOperatorTable opTab) { 52 | // Don't use anonymous inner classes. They can't be instantiated 53 | // using reflection when we are deserializing from JSON. 54 | opTab.addOperator(new RampFunction()); 55 | opTab.addOperator(new DedupFunction()); 56 | opTab.addOperator(new MyFunction()); 57 | opTab.addOperator(new MyAvgAggFunction()); 58 | } 59 | 60 | /** "RAMP" user-defined function. */ 61 | public static class RampFunction extends SqlFunction { 62 | public RampFunction() { 63 | super("RAMP", 64 | SqlKind.OTHER_FUNCTION, 65 | null, 66 | null, 67 | OperandTypes.NUMERIC, 68 | SqlFunctionCategory.USER_DEFINED_FUNCTION); 69 | } 70 | 71 | public RelDataType inferReturnType(SqlOperatorBinding opBinding) { 72 | final RelDataTypeFactory typeFactory = 73 | opBinding.getTypeFactory(); 74 | return typeFactory.builder() 75 | .add("I", SqlTypeName.INTEGER) 76 | .build(); 77 | } 78 | } 79 | 80 | /** "DEDUP" user-defined function. */ 81 | public static class DedupFunction extends SqlFunction { 82 | public DedupFunction() { 83 | super("DEDUP", 84 | SqlKind.OTHER_FUNCTION, 85 | null, 86 | null, 87 | OperandTypes.VARIADIC, 88 | SqlFunctionCategory.USER_DEFINED_FUNCTION); 89 | } 90 | 91 | public RelDataType inferReturnType(SqlOperatorBinding opBinding) { 92 | final RelDataTypeFactory typeFactory = 93 | opBinding.getTypeFactory(); 94 | return typeFactory.builder() 95 | .add("NAME", SqlTypeName.VARCHAR, 1024) 96 | .build(); 97 | } 98 | } 99 | 100 | /** "MYFUN" user-defined scalar function. */ 101 | public static class MyFunction extends SqlFunction { 102 | public MyFunction() { 103 | super("MYFUN", 104 | new SqlIdentifier("MYFUN", SqlParserPos.ZERO), 105 | SqlKind.OTHER_FUNCTION, 106 | null, 107 | null, 108 | OperandTypes.NUMERIC, 109 | null, 110 | SqlFunctionCategory.USER_DEFINED_FUNCTION); 111 | } 112 | 113 | public RelDataType inferReturnType(SqlOperatorBinding opBinding) { 114 | final RelDataTypeFactory typeFactory = 115 | opBinding.getTypeFactory(); 116 | return typeFactory.createSqlType(SqlTypeName.BIGINT); 117 | } 118 | } 119 | 120 | /** "MYAGG" user-defined aggregate function. This agg function accept two numeric arguments 121 | * in order to reproduce the throws of CALCITE-2744. */ 122 | public static class MyAvgAggFunction extends SqlAggFunction { 123 | public MyAvgAggFunction() { 124 | super("MYAGG", null, SqlKind.AVG, ReturnTypes.AVG_AGG_FUNCTION, 125 | null, OperandTypes.family(SqlTypeFamily.NUMERIC, SqlTypeFamily.NUMERIC), 126 | SqlFunctionCategory.NUMERIC, false, false, Optionality.FORBIDDEN); 127 | } 128 | 129 | @Override public boolean isDeterministic() { 130 | return false; 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/MockViewExpander.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.mock; 2 | 3 | import org.apache.calcite.plan.RelOptCluster; 4 | import org.apache.calcite.plan.RelOptTable; 5 | import org.apache.calcite.prepare.Prepare; 6 | import org.apache.calcite.rel.RelRoot; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.sql.SqlNode; 9 | import org.apache.calcite.sql.parser.SqlParseException; 10 | import org.apache.calcite.sql.parser.SqlParser; 11 | import org.apache.calcite.sql.validate.SqlValidator; 12 | import org.apache.calcite.sql2rel.SqlToRelConverter; 13 | import org.apache.calcite.sql2rel.StandardConvertletTable; 14 | 15 | import java.util.List; 16 | 17 | 18 | /** 19 | * Can expand a view into relational expressions. 20 | * {@link RelOptTable.ViewExpander} implementation for testing usage. 21 | */ 22 | public class MockViewExpander implements RelOptTable.ViewExpander { 23 | private final SqlValidator validator; 24 | private final Prepare.CatalogReader catalogReader; 25 | private final RelOptCluster cluster; 26 | private final SqlToRelConverter.Config config; 27 | 28 | public MockViewExpander( 29 | SqlValidator validator, 30 | Prepare.CatalogReader catalogReader, 31 | RelOptCluster cluster, 32 | SqlToRelConverter.Config config) { 33 | this.validator = validator; 34 | this.catalogReader = catalogReader; 35 | this.cluster = cluster; 36 | this.config = config; 37 | } 38 | 39 | @Override public RelRoot expandView(RelDataType rowType, String queryString, 40 | List schemaPath, List viewPath) { 41 | try { 42 | SqlNode parsedNode = SqlParser.create(queryString).parseStmt(); 43 | SqlNode validatedNode = validator.validate(parsedNode); 44 | SqlToRelConverter converter = new SqlToRelConverter( 45 | this, validator, catalogReader, cluster, 46 | StandardConvertletTable.INSTANCE, config); 47 | return converter.convertQuery(validatedNode, false, true); 48 | } catch (SqlParseException e) { 49 | throw new RuntimeException("Error happened while expanding view.", e); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/mock/VirtualColumnsExpressionFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.mock; 18 | 19 | import org.apache.calcite.plan.RelOptTable; 20 | import org.apache.calcite.schema.ColumnStrategy; 21 | import org.apache.calcite.sql2rel.NullInitializerExpressionFactory; 22 | 23 | /** Define column strategies for the "VIRTUALCOLUMNS" table. */ 24 | public class VirtualColumnsExpressionFactory extends NullInitializerExpressionFactory { 25 | @Override public ColumnStrategy generationStrategy(RelOptTable table, int iColumn) { 26 | switch (iColumn) { 27 | case 3: 28 | return ColumnStrategy.STORED; 29 | case 4: 30 | return ColumnStrategy.VIRTUAL; 31 | default: 32 | return super.generationStrategy(table, iColumn); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/BookstoreSchema.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.schema; 18 | 19 | import java.math.BigDecimal; 20 | import java.util.Arrays; 21 | import java.util.Collections; 22 | import java.util.List; 23 | 24 | /** 25 | * A Schema representing a bookstore. 26 | * 27 | *

It contains a single table with various levels/types of nesting, 28 | * and is used mainly for testing parts of code that rely on nested 29 | * structures. 30 | * 31 | *

New authors can be added but attention should be made to update 32 | * appropriately tests that might fail. 33 | * 34 | *

The Schema is meant to be used with 35 | * {@link org.apache.calcite.adapter.java.ReflectiveSchema} thus all 36 | * fields, and methods, should be public. 37 | */ 38 | public final class BookstoreSchema { 39 | 40 | public final Author[] authors = { 41 | new Author(1, 42 | "Victor Hugo", 43 | new Place( 44 | new Coordinate(BigDecimal.valueOf(47.24), BigDecimal.valueOf(6.02)), 45 | "Besançon", 46 | "France"), 47 | Collections.singletonList( 48 | new Book("Les Misérables", 49 | 1862, 50 | Collections.singletonList(new Page(1, "Contents"))))), 51 | new Author(2, 52 | "Nikos Kazantzakis", 53 | new Place( 54 | new Coordinate(BigDecimal.valueOf(35.3387), BigDecimal.valueOf(25.1442)), 55 | "Heraklion", 56 | "Greece"), 57 | Arrays.asList( 58 | new Book("Zorba the Greek", 59 | 1946, 60 | Arrays.asList(new Page(1, "Contents"), 61 | new Page(2, "Acknowledgements"))), 62 | new Book("The Last Temptation of Christ", 63 | 1955, 64 | Collections.singletonList(new Page(1, "Contents"))))), 65 | new Author(3, 66 | "Homer", 67 | new Place(null, 68 | "Ionia", 69 | "Greece"), 70 | Collections.emptyList()) 71 | }; 72 | 73 | /** 74 | */ 75 | public static class Author { 76 | public final int aid; 77 | public final String name; 78 | public final Place birthPlace; 79 | @org.apache.calcite.adapter.java.Array(component = Book.class) 80 | public final List books; 81 | 82 | public Author(int aid, String name, Place birthPlace, List books) { 83 | this.aid = aid; 84 | this.name = name; 85 | this.birthPlace = birthPlace; 86 | this.books = books; 87 | } 88 | } 89 | 90 | /** 91 | */ 92 | public static class Place { 93 | public final Coordinate coords; 94 | public final String city; 95 | public final String country; 96 | 97 | public Place(Coordinate coords, String city, String country) { 98 | this.coords = coords; 99 | this.city = city; 100 | this.country = country; 101 | } 102 | 103 | } 104 | 105 | /** 106 | */ 107 | public static class Coordinate { 108 | public final BigDecimal latitude; 109 | public final BigDecimal longtitude; 110 | 111 | public Coordinate(BigDecimal latitude, BigDecimal longtitude) { 112 | this.latitude = latitude; 113 | this.longtitude = longtitude; 114 | } 115 | } 116 | 117 | /** 118 | */ 119 | public static class Book { 120 | public final String title; 121 | public final int publishYear; 122 | @org.apache.calcite.adapter.java.Array(component = Page.class) 123 | public final List pages; 124 | 125 | public Book(String title, int publishYear, List pages) { 126 | this.title = title; 127 | this.publishYear = publishYear; 128 | this.pages = pages; 129 | } 130 | } 131 | 132 | /** 133 | */ 134 | public static class Page { 135 | public final int pageNo; 136 | public final String contentType; 137 | 138 | public Page(int pageNo, String contentType) { 139 | this.pageNo = pageNo; 140 | this.contentType = contentType; 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/OrdersTableFactory.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.DataContext; 5 | import org.apache.calcite.config.CalciteConnectionConfig; 6 | import org.apache.calcite.linq4j.Enumerable; 7 | import org.apache.calcite.linq4j.Linq4j; 8 | import org.apache.calcite.rel.type.RelDataType; 9 | import org.apache.calcite.rel.type.RelDataTypeFactory; 10 | import org.apache.calcite.rel.type.RelProtoDataType; 11 | import org.apache.calcite.schema.*; 12 | import org.apache.calcite.sql.SqlCall; 13 | import org.apache.calcite.sql.SqlNode; 14 | import org.apache.calcite.sql.type.SqlTypeName; 15 | 16 | import java.util.Map; 17 | 18 | public class OrdersTableFactory implements TableFactory { 19 | 20 | @Override 21 | public Table create(SchemaPlus schema, String name, Map operand, RelDataType rowType) { 22 | final Object[][] rows = { 23 | {1, "paint", 10}, 24 | {2, "paper", 5}, 25 | {3, "brush", 12}, 26 | {4, "paint", 3}, 27 | {5, "paint", 3} 28 | }; 29 | return new OrdersTable(ImmutableList.copyOf(rows)); 30 | } 31 | 32 | public static class OrdersTable implements ScannableTable { 33 | protected final RelProtoDataType protoRowType = new RelProtoDataType() { 34 | public RelDataType apply(RelDataTypeFactory a0) { 35 | return a0.builder() 36 | .add("id", SqlTypeName.INTEGER) 37 | .add("product", SqlTypeName.VARCHAR, 10) 38 | .add("units", SqlTypeName.INTEGER) 39 | .build(); 40 | } 41 | }; 42 | 43 | private final ImmutableList rows; 44 | 45 | public OrdersTable(ImmutableList rows) { 46 | this.rows = rows; 47 | } 48 | 49 | public Enumerable scan(DataContext root) { 50 | return Linq4j.asEnumerable(rows); 51 | } 52 | 53 | @Override 54 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 55 | return protoRowType.apply(typeFactory); 56 | } 57 | 58 | @Override 59 | public Statistic getStatistic() { 60 | return Statistics.UNKNOWN; 61 | } 62 | 63 | @Override 64 | public Schema.TableType getJdbcTableType() { 65 | return Schema.TableType.TABLE; 66 | } 67 | 68 | @Override 69 | public boolean isRolledUp(String column) { 70 | return false; 71 | } 72 | 73 | @Override 74 | public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 75 | return false; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/ScannableTableListingDetails.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.DataContext; 5 | import org.apache.calcite.config.CalciteConnectionConfig; 6 | import org.apache.calcite.linq4j.Enumerable; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.schema.ScannableTable; 10 | import org.apache.calcite.schema.Schema; 11 | import org.apache.calcite.schema.Statistic; 12 | import org.apache.calcite.schema.Statistics; 13 | import org.apache.calcite.sql.SqlCall; 14 | import org.apache.calcite.sql.SqlNode; 15 | 16 | 17 | public class ScannableTableListingDetails implements ScannableTable { 18 | 19 | @Override 20 | public Enumerable scan(DataContext root) { 21 | return null; 22 | } 23 | 24 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 25 | return new RelDataTypeFactory.Builder(typeFactory) 26 | .add("LIST_ID", typeFactory.createJavaType(Long.class)) 27 | .add("ITEM_PRICE", typeFactory.createJavaType(Double.class)) 28 | .add("SELLER_NAME", typeFactory.createJavaType(String.class)) 29 | .add("AVAIL_QUANTITY", typeFactory.createJavaType(Integer.class)) 30 | .build(); 31 | } 32 | 33 | public Statistic getStatistic() { 34 | return Statistics.of(100, ImmutableList.of()); 35 | } 36 | 37 | public Schema.TableType getJdbcTableType() { 38 | return Schema.TableType.TABLE; 39 | } 40 | 41 | @Override 42 | public boolean isRolledUp(String column) { 43 | return false; 44 | } 45 | 46 | @Override 47 | public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 48 | return false; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/StreamSchema.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.DataContext; 5 | import org.apache.calcite.avatica.util.DateTimeUtils; 6 | import org.apache.calcite.config.CalciteConnectionConfig; 7 | import org.apache.calcite.linq4j.Enumerable; 8 | import org.apache.calcite.linq4j.Linq4j; 9 | import org.apache.calcite.rel.RelCollations; 10 | import org.apache.calcite.rel.type.RelDataType; 11 | import org.apache.calcite.rel.type.RelDataTypeFactory; 12 | import org.apache.calcite.rel.type.RelProtoDataType; 13 | import org.apache.calcite.schema.*; 14 | import org.apache.calcite.sql.SqlCall; 15 | import org.apache.calcite.sql.SqlNode; 16 | import org.apache.calcite.sql.type.SqlTypeName; 17 | 18 | import java.util.Iterator; 19 | import java.util.Map; 20 | 21 | /** 22 | * Extract of org.apache.calcite.test.StreamTest to get only the tables 23 | * and not the test 24 | */ 25 | public class StreamSchema { 26 | /** 27 | * Base table for the Orders table. Manages the base schema used for the test tables and common 28 | * functions. 29 | */ 30 | private abstract static class BaseOrderStreamTable implements ScannableTable { 31 | protected final RelProtoDataType protoRowType = a0 -> a0.builder() 32 | .add("ROWTIME", SqlTypeName.TIMESTAMP) 33 | .add("ID", SqlTypeName.INTEGER) 34 | .add("PRODUCT", SqlTypeName.VARCHAR, 10) 35 | .add("UNITS", SqlTypeName.INTEGER) 36 | .build(); 37 | 38 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 39 | return protoRowType.apply(typeFactory); 40 | } 41 | 42 | public Statistic getStatistic() { 43 | return Statistics.of(100d, ImmutableList.of(), 44 | RelCollations.createSingleton(0)); 45 | } 46 | 47 | public Schema.TableType getJdbcTableType() { 48 | return Schema.TableType.TABLE; 49 | } 50 | 51 | @Override 52 | public boolean isRolledUp(String column) { 53 | return false; 54 | } 55 | 56 | @Override 57 | public boolean rolledUpColumnValidInsideAgg(String column, 58 | SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 59 | return false; 60 | } 61 | } 62 | 63 | /** 64 | * Mock table that returns a stream of orders from a fixed array. 65 | */ 66 | @SuppressWarnings("UnusedDeclaration") 67 | public static class OrdersStreamTableFactory implements TableFactory
{ 68 | // public constructor, per factory contract 69 | public OrdersStreamTableFactory() { 70 | } 71 | 72 | public Table create(SchemaPlus schema, String name, 73 | Map operand, RelDataType rowType) { 74 | return new OrdersTable(getRowList()); 75 | } 76 | 77 | public static ImmutableList getRowList() { 78 | final Object[][] rows = { 79 | {ts(10, 15, 0), 1, "paint", 10}, 80 | {ts(10, 24, 15), 2, "paper", 5}, 81 | {ts(10, 24, 45), 3, "brush", 12}, 82 | {ts(10, 58, 0), 4, "paint", 3}, 83 | {ts(11, 10, 0), 5, "paint", 3} 84 | }; 85 | return ImmutableList.copyOf(rows); 86 | } 87 | 88 | private static Object ts(int h, int m, int s) { 89 | return DateTimeUtils.unixTimestamp(2015, 2, 15, h, m, s); 90 | } 91 | } 92 | 93 | /** 94 | * Table representing the ORDERS stream. 95 | */ 96 | public static class OrdersTable extends BaseOrderStreamTable 97 | implements StreamableTable { 98 | private final ImmutableList rows; 99 | 100 | public OrdersTable(ImmutableList rows) { 101 | this.rows = rows; 102 | } 103 | 104 | public Enumerable scan(DataContext root) { 105 | return Linq4j.asEnumerable(rows); 106 | } 107 | 108 | @Override 109 | public Table stream() { 110 | return new OrdersTable(rows); 111 | } 112 | 113 | @Override 114 | public boolean isRolledUp(String column) { 115 | return false; 116 | } 117 | 118 | @Override 119 | public boolean rolledUpColumnValidInsideAgg(String column, 120 | SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 121 | return false; 122 | } 123 | } 124 | 125 | /** 126 | * Mock table that returns a stream of orders from a fixed array. 127 | */ 128 | @SuppressWarnings("UnusedDeclaration") 129 | public static class InfiniteOrdersStreamTableFactory implements TableFactory
{ 130 | // public constructor, per factory contract 131 | public InfiniteOrdersStreamTableFactory() { 132 | } 133 | 134 | public Table create(SchemaPlus schema, String name, 135 | Map operand, RelDataType rowType) { 136 | return new InfiniteOrdersTable(); 137 | } 138 | } 139 | 140 | /** 141 | * Table representing an infinitely larger ORDERS stream. 142 | */ 143 | public static class InfiniteOrdersTable extends BaseOrderStreamTable 144 | implements StreamableTable { 145 | public Enumerable scan(DataContext root) { 146 | return Linq4j.asEnumerable(() -> new Iterator() { 147 | private final String[] items = {"paint", "paper", "brush"}; 148 | private int counter = 0; 149 | 150 | public boolean hasNext() { 151 | return true; 152 | } 153 | 154 | public Object[] next() { 155 | final int index = counter++; 156 | return new Object[]{ 157 | System.currentTimeMillis(), index, items[index % items.length], 10}; 158 | } 159 | 160 | public void remove() { 161 | throw new UnsupportedOperationException(); 162 | } 163 | }); 164 | } 165 | 166 | public Table stream() { 167 | return this; 168 | } 169 | } 170 | 171 | /** 172 | * Table representing the history of the ORDERS stream. 173 | */ 174 | public static class OrdersHistoryTable extends BaseOrderStreamTable { 175 | private final ImmutableList rows; 176 | 177 | public OrdersHistoryTable(ImmutableList rows) { 178 | this.rows = rows; 179 | } 180 | 181 | public Enumerable scan(DataContext root) { 182 | return Linq4j.asEnumerable(rows); 183 | } 184 | } 185 | 186 | /** 187 | * Mocks a simple relation to use for stream joining test. 188 | */ 189 | public static class ProductsTableFactory implements TableFactory
{ 190 | public Table create(SchemaPlus schema, String name, 191 | Map operand, RelDataType rowType) { 192 | final Object[][] rows = { 193 | {"paint", 1}, 194 | {"paper", 0}, 195 | {"brush", 1} 196 | }; 197 | return new ProductsTable(ImmutableList.copyOf(rows)); 198 | } 199 | } 200 | 201 | /** 202 | * Table representing the PRODUCTS relation. 203 | */ 204 | public static class ProductsTable implements ScannableTable { 205 | private final ImmutableList rows; 206 | 207 | public ProductsTable(ImmutableList rows) { 208 | this.rows = rows; 209 | } 210 | 211 | private final RelProtoDataType protoRowType = a0 -> a0.builder() 212 | .add("ID", SqlTypeName.VARCHAR, 32) 213 | .add("SUPPLIER", SqlTypeName.INTEGER) 214 | .build(); 215 | 216 | public Enumerable scan(DataContext root) { 217 | return Linq4j.asEnumerable(rows); 218 | } 219 | 220 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 221 | return protoRowType.apply(typeFactory); 222 | } 223 | 224 | public Statistic getStatistic() { 225 | return Statistics.of(200d, ImmutableList.of()); 226 | } 227 | 228 | public Schema.TableType getJdbcTableType() { 229 | return Schema.TableType.TABLE; 230 | } 231 | 232 | @Override 233 | public boolean isRolledUp(String column) { 234 | return false; 235 | } 236 | 237 | @Override 238 | public boolean rolledUpColumnValidInsideAgg(String column, 239 | SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 240 | return false; 241 | } 242 | } 243 | 244 | /** 245 | * Table representing the PRODUCTS_TEMPORAL temporal table. 246 | */ 247 | public static class ProductsTemporalTable implements TemporalTable { 248 | 249 | private final RelProtoDataType protoRowType = a0 -> a0.builder() 250 | .add("ID", SqlTypeName.VARCHAR, 32) 251 | .add("SUPPLIER", SqlTypeName.INTEGER) 252 | .add("SYS_START", SqlTypeName.TIMESTAMP) 253 | .add("SYS_END", SqlTypeName.TIMESTAMP) 254 | .build(); 255 | 256 | @Override 257 | public String getSysStartFieldName() { 258 | return "SYS_START"; 259 | } 260 | 261 | @Override 262 | public String getSysEndFieldName() { 263 | return "SYS_END"; 264 | } 265 | 266 | @Override 267 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 268 | return protoRowType.apply(typeFactory); 269 | } 270 | 271 | @Override 272 | public Statistic getStatistic() { 273 | return Statistics.of(200d, ImmutableList.of()); 274 | } 275 | 276 | @Override 277 | public Schema.TableType getJdbcTableType() { 278 | return Schema.TableType.TABLE; 279 | } 280 | 281 | @Override 282 | public boolean isRolledUp(String column) { 283 | return false; 284 | } 285 | 286 | @Override 287 | public boolean rolledUpColumnValidInsideAgg(String column, 288 | SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 289 | return false; 290 | } 291 | } 292 | 293 | } 294 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/StreamableSchema.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema; 2 | 3 | import org.apache.calcite.schema.SchemaPlus; 4 | import org.apache.calcite.tools.Frameworks; 5 | 6 | /** 7 | * Adapted from QUERY EXPLAIN WITH CALCITE 8 | */ 9 | public class StreamableSchema { 10 | 11 | static public SchemaPlus getSchema() { 12 | 13 | final SchemaPlus rootSchema = Frameworks.createRootSchema(true); 14 | 15 | rootSchema.add("LISTING_DETAILS", new ScannableTableListingDetails()); 16 | rootSchema.add("ORDER_DETAILS", new StreamableTableOrderDetails()); 17 | return rootSchema; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/StreamableTableOrderDetails.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import org.apache.calcite.config.CalciteConnectionConfig; 5 | import org.apache.calcite.rel.RelCollations; 6 | import org.apache.calcite.rel.RelFieldCollation; 7 | import org.apache.calcite.rel.type.RelDataType; 8 | import org.apache.calcite.rel.type.RelDataTypeFactory; 9 | import org.apache.calcite.schema.*; 10 | import org.apache.calcite.sql.SqlCall; 11 | import org.apache.calcite.sql.SqlNode; 12 | import org.apache.calcite.util.ImmutableBitSet; 13 | 14 | import java.sql.Date; 15 | 16 | public class StreamableTableOrderDetails implements StreamableTable { 17 | 18 | @Override 19 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 20 | return new RelDataTypeFactory.Builder(typeFactory) 21 | .add("CK_TIME", typeFactory.createJavaType(Date.class)) 22 | .add("ITEM_ID", typeFactory.createJavaType(Long.class)) 23 | .add("ITEM_PRICE", typeFactory.createJavaType(Double.class)) 24 | .add("BUYER_NAME", typeFactory.createJavaType(String.class)) 25 | .add("QUANTITY", typeFactory.createJavaType(Integer.class)) 26 | .build(); 27 | } 28 | 29 | @Override 30 | public Statistic getStatistic() { 31 | RelFieldCollation.Direction dir = RelFieldCollation.Direction.ASCENDING; 32 | RelFieldCollation collation = new RelFieldCollation(0, dir, RelFieldCollation.NullDirection.UNSPECIFIED); 33 | return Statistics.of(5, ImmutableList.of(ImmutableBitSet.of(0)), 34 | ImmutableList.of(RelCollations.of(collation))); 35 | } 36 | 37 | @Override 38 | public Schema.TableType getJdbcTableType() { 39 | return Schema.TableType.STREAM; 40 | } 41 | 42 | @Override 43 | public boolean isRolledUp(String column) { 44 | return false; 45 | } 46 | 47 | @Override 48 | public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, SqlNode parent, CalciteConnectionConfig config) { 49 | return false; 50 | } 51 | 52 | public Table stream() { 53 | return null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/foodmart/FoodmartSchema.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.foodmart; 2 | 3 | /** 4 | * Object that will be used via reflection to create the "foodmart" 5 | * schema. 6 | * Extract of org.apache.calcite.test.JdbcTest 7 | */ 8 | public class FoodmartSchema { 9 | 10 | /** 11 | * The static class for the ` 12 | * @return 13 | */ 14 | public static FoodmartSchema createFoodmartSchema(){ 15 | return new FoodmartSchema(); 16 | } 17 | 18 | public final SalesFact[] sales_fact_1997 = { 19 | new SalesFact(100, 10), 20 | new SalesFact(150, 20), 21 | }; 22 | 23 | 24 | /** 25 | * Object that will be used via reflection to create the 26 | * "sales_fact_1997" fact table. 27 | */ 28 | public static class SalesFact { 29 | public final int cust_id; 30 | public final int prod_id; 31 | 32 | public SalesFact(int cust_id, int prod_id) { 33 | this.cust_id = cust_id; 34 | this.prod_id = prod_id; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/hr/HrDepartment.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.hr; 2 | 3 | 4 | import java.io.Serializable; 5 | import java.util.List; 6 | 7 | public class HrDepartment implements Serializable { 8 | 9 | public final int deptno; 10 | public final String name; 11 | 12 | @org.apache.calcite.adapter.java.Array(component = HrEmployee.class) 13 | public final List hrEmployees; 14 | private HrLocation location = new HrLocation(0,"default",1,2); 15 | 16 | 17 | public HrDepartment(int deptno, String name, List hrEmployees, 18 | HrLocation location) { 19 | this.deptno = deptno; 20 | this.name = name; 21 | this.hrEmployees = hrEmployees; 22 | this.location = location; 23 | } 24 | 25 | public HrDepartment(int deptno, String name, List hrEmployees) { 26 | this.deptno = deptno; 27 | this.name = name; 28 | this.hrEmployees = hrEmployees; 29 | } 30 | 31 | 32 | @Override 33 | public String toString() { 34 | return "Department [deptno: " + deptno + ", name: " + name 35 | + ", employees: " + hrEmployees + ", location: " + location + "]"; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) { 40 | return obj == this 41 | || obj instanceof HrDepartment 42 | && deptno == ((HrDepartment) obj).deptno; 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/hr/HrDependent.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.hr; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | 6 | public class HrDependent implements Serializable { 7 | public final int empid; 8 | public final String name; 9 | 10 | public HrDependent(int empid, String name) { 11 | this.empid = empid; 12 | this.name = name; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return "Dependent [empid: " + empid + ", name: " + name + "]"; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object obj) { 22 | return obj == this 23 | || obj instanceof HrDependent 24 | && empid == ((HrDependent) obj).empid 25 | && Objects.equals(name, ((HrDependent) obj).name); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/hr/HrEmployee.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.hr; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Object that will be used via reflection to create the "emps" table. 7 | */ 8 | public class HrEmployee implements Serializable { 9 | 10 | public final int empid; 11 | public final int deptno; 12 | public final String name; 13 | public final float salary; 14 | public final Integer commission; 15 | 16 | public HrEmployee(int empid, int deptno, String name, float salary, 17 | Integer commission) { 18 | this.empid = empid; 19 | this.deptno = deptno; 20 | this.name = name; 21 | this.salary = salary; 22 | this.commission = commission; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "Employee [empid: " + empid + ", deptno: " + deptno 28 | + ", name: " + name + "]"; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object obj) { 33 | return obj == this 34 | || obj instanceof HrEmployee 35 | && empid == ((HrEmployee) obj).empid; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/hr/HrLocation.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.hr; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | public class HrLocation implements Serializable { 7 | public final int x; 8 | public final int y; 9 | public final int locationId; 10 | public final String name; 11 | 12 | public HrLocation(int locationId, String name, int x, int y) { 13 | 14 | this.locationId = locationId; 15 | this.name = name; 16 | this.x = x; 17 | this.y = y; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "Location [x: " + x + ", y: " + y + "]"; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object obj) { 27 | return obj == this 28 | || obj instanceof HrLocation 29 | && x == ((HrLocation) obj).x 30 | && y == ((HrLocation) obj).y; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/hr/HrSchema.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.hr; 2 | 3 | import com.gerardnico.calcite.mock.Smalls; 4 | import com.google.common.collect.ImmutableList; 5 | import org.apache.calcite.schema.QueryableTable; 6 | import org.apache.calcite.schema.Schema; 7 | import org.apache.calcite.schema.TranslatableTable; 8 | 9 | import java.util.Arrays; 10 | import java.util.Collections; 11 | import java.util.logging.Level; 12 | 13 | /** 14 | * Object that will be used via reflection to create the "hr" schema. 15 | * A schema that contains two tables by reflection. 16 | * 17 | *

Here is the SQL to create equivalent tables in Oracle: 18 | * 19 | *

20 | *
 21 |  * CREATE TABLE "emps" (
 22 |  *   "empid" INTEGER NOT NULL,
 23 |  *   "deptno" INTEGER NOT NULL,
 24 |  *   "name" VARCHAR2(10) NOT NULL,
 25 |  *   "salary" NUMBER(6, 2) NOT NULL,
 26 |  *   "commission" INTEGER);
 27 |  * INSERT INTO "emps" VALUES (100, 10, 'Bill', 10000, 1000);
 28 |  * INSERT INTO "emps" VALUES (200, 20, 'Eric', 8000, 500);
 29 |  * INSERT INTO "emps" VALUES (150, 10, 'Sebastian', 7000, null);
 30 |  * INSERT INTO "emps" VALUES (110, 10, 'Theodore', 11500, 250);
 31 |  *
 32 |  * CREATE TABLE "depts" (
 33 |  *   "deptno" INTEGER NOT NULL,
 34 |  *   "name" VARCHAR2(10) NOT NULL,
 35 |  *   "employees" ARRAY OF "Employee",
 36 |  *   "location" "Location");
 37 |  * INSERT INTO "depts" VALUES (10, 'Sales', null, (-122, 38));
 38 |  * INSERT INTO "depts" VALUES (30, 'Marketing', null, (0, 52));
 39 |  * INSERT INTO "depts" VALUES (40, 'HR', null, null);
 40 |  * 
41 | *
42 | */ 43 | 44 | public class HrSchema { 45 | 46 | @Override 47 | public String toString() { 48 | return "HrSchema"; 49 | } 50 | 51 | public final HrEmployee[] emps = { 52 | new HrEmployee(1, 10, "Bill", 10000, 1000), 53 | new HrEmployee(2, 20, "Eric", 8000, 500), 54 | new HrEmployee(3, 10, "Sebastian", 7000, null), 55 | new HrEmployee(4, 10, "Theodore", 11500, 250), 56 | new HrEmployee(5, 10, "Marjorie", 10000, 1000), 57 | new HrEmployee(6, 20, "Guy", 8000, 500), 58 | new HrEmployee(7, 10, "Dieudonne", 7000, null), 59 | new HrEmployee(8, 10, "Haroun", 11500, 250), 60 | new HrEmployee(9, 10, "Sarah", 10000, 1000), 61 | new HrEmployee(10, 20, "Gabriel", 8000, 500), 62 | new HrEmployee(11, 10, "Pierre", 7000, null), 63 | new HrEmployee(12, 10, "Paul", 11500, 250), 64 | new HrEmployee(13, 10, "Jacques", 100, 1000), 65 | new HrEmployee(14, 20, "Khawla", 8000, 500), 66 | new HrEmployee(15, 10, "Brielle", 7000, null), 67 | new HrEmployee(16, 10, "Hyuna", 11500, 250), 68 | new HrEmployee(17, 10, "Ahmed", 10000, 1000), 69 | new HrEmployee(18, 20, "Lara", 8000, 500), 70 | new HrEmployee(19, 10, "Capucine", 7000, null), 71 | new HrEmployee(20, 10, "Michelle", 11500, 250), 72 | new HrEmployee(21, 10, "Cerise", 10000, 1000), 73 | new HrEmployee(22, 80, "Travis", 8000, 500), 74 | new HrEmployee(23, 10, "Taylor", 7000, null), 75 | new HrEmployee(24, 10, "Seohyun", 11500, 250), 76 | new HrEmployee(25, 70, "Helen", 10000, 1000), 77 | new HrEmployee(26, 50, "Patric", 8000, 500), 78 | new HrEmployee(27, 10, "Clara", 7000, null), 79 | new HrEmployee(28, 10, "Catherine", 11500, 250), 80 | new HrEmployee(29, 10, "Anibal", 10000, 1000), 81 | new HrEmployee(30, 30, "Ursula", 8000, 500), 82 | new HrEmployee(31, 10, "Arturito", 7000, null), 83 | new HrEmployee(32, 70, "Diane", 11500, 250), 84 | new HrEmployee(33, 10, "Phoebe", 10000, 1000), 85 | new HrEmployee(34, 20, "Maria", 8000, 500), 86 | new HrEmployee(35, 10, "Edouard", 7000, null), 87 | new HrEmployee(36, 110, "Isabelle", 11500, 250), 88 | new HrEmployee(37, 120, "Olivier", 10000, 1000), 89 | new HrEmployee(38, 20, "Yann", 8000, 500), 90 | new HrEmployee(39, 60, "Ralf", 7000, null), 91 | new HrEmployee(40, 60, "Emmanuel", 11500, 250), 92 | new HrEmployee(41, 10, "Berenice", 10000, 1000), 93 | new HrEmployee(42, 20, "Kylie", 8000, 500), 94 | new HrEmployee(43, 80, "Natacha", 7000, null), 95 | new HrEmployee(44, 100, "Henri", 11500, 250), 96 | new HrEmployee(45, 90, "Pascal", 10000, 1000), 97 | new HrEmployee(46, 90, "Sabrina", 8000, 500), 98 | new HrEmployee(47, 8, "Riyad", 7000, null), 99 | new HrEmployee(48, 5, "Andy", 11500, 250), 100 | new HrEmployee(100, 10, "Bill", 10000, 1000), 101 | new HrEmployee(200, 20, "Eric", 8000, 500), 102 | new HrEmployee(150, 10, "Sebastian", 7000, null), 103 | new HrEmployee(110, 10, "Theodore", 11500, 250), 104 | }; 105 | public final HrDepartment[] depts = { 106 | new HrDepartment(10, "Sales", Arrays.asList(emps[0], emps[2]),null), 107 | new HrDepartment(20, "Marketing", ImmutableList.of(), null), 108 | new HrDepartment(30, "HR", Collections.singletonList(emps[1]), null), 109 | new HrDepartment(40, "Administration", Arrays.asList(emps[0], emps[2]), 110 | null), 111 | new HrDepartment(50, "Design", ImmutableList.of(), null), 112 | new HrDepartment(60, "IT", Collections.singletonList(emps[1]), null), 113 | new HrDepartment(70, "Production", Arrays.asList(emps[0], emps[2]), 114 | null), 115 | new HrDepartment(80, "Finance", ImmutableList.of(), null), 116 | new HrDepartment(90, "Accounting", Collections.singletonList(emps[1]), null), 117 | new HrDepartment(100, "Research", Arrays.asList(emps[0], emps[2]), 118 | null), 119 | new HrDepartment(110, "Maintenance", ImmutableList.of(), null), 120 | new HrDepartment(120, "Client Support", Collections.singletonList(emps[1]), null), 121 | }; 122 | 123 | public final HrDependent[] dependents = { 124 | new HrDependent(10, "Michael"), 125 | new HrDependent(10, "Jane"), 126 | }; 127 | 128 | public final HrDependent[] locations = { 129 | new HrDependent(10, "San Francisco"), 130 | new HrDependent(20, "San Diego"), 131 | }; 132 | 133 | public QueryableTable foo(int count) { 134 | return Smalls.generateStrings(count); 135 | } 136 | 137 | public TranslatableTable view(String s) { 138 | return Smalls.view(s); 139 | } 140 | 141 | public static HrSchema createHrSchema() { 142 | return new HrSchema(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/hr/HrSchemaMin.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.hr; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | 5 | import java.util.Arrays; 6 | 7 | public class HrSchemaMin { 8 | 9 | public final HrEmployee[] emps = { 10 | new HrEmployee(1, 10, "Bill", 10000, 1000), 11 | new HrEmployee(2, 20, "Eric", 8000, 500), 12 | new HrEmployee(3, 10, "Sebastian", 7000, null), 13 | new HrEmployee(4, 10, "Theodore", 11500, 250), 14 | new HrEmployee(5, 10, "Marjorie", 10000, 1000), 15 | new HrEmployee(6, 20, "Guy", 8000, 500), 16 | new HrEmployee(7, 10, "Dieudonne", 7000, null), 17 | 18 | }; 19 | public final HrDepartment[] depts = { 20 | new HrDepartment(10, "Sales", Arrays.asList(emps[0], emps[2]),null), 21 | new HrDepartment(20, "Marketing", ImmutableList.of(), null), 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/lingual/LingualEmp.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.lingual; 2 | 3 | public class LingualEmp { 4 | 5 | public final int EMPNO; 6 | public final int DEPTNO; 7 | 8 | public LingualEmp(int EMPNO, int DEPTNO) { 9 | this.EMPNO = EMPNO; 10 | this.DEPTNO = DEPTNO; 11 | } 12 | 13 | @Override 14 | public boolean equals(Object obj) { 15 | return obj == this 16 | || obj instanceof LingualEmp 17 | && EMPNO == ((LingualEmp) obj).EMPNO; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/lingual/LingualSchema.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.lingual; 2 | 3 | public class LingualSchema { 4 | 5 | public final LingualEmp[] EMPS = { 6 | new LingualEmp(1, 10), 7 | new LingualEmp(2, 30) 8 | }; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/orderEntry/OrderEntrySchema.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to you under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.gerardnico.calcite.schema.orderEntry; 18 | 19 | /** 20 | * A Schema representing OrderEntry. 21 | * 22 | *

The Schema is meant to be used with 23 | * {@link org.apache.calcite.adapter.java.ReflectiveSchema} thus all 24 | * fields, and methods, should be public. 25 | */ 26 | public final class OrderEntrySchema { 27 | 28 | public final Customers[] CUSTOMERS = { 29 | new Customers(1), 30 | new Customers(2) 31 | }; 32 | 33 | public final OrderItems[] ORDER_ITEMS = { 34 | new OrderItems(1, 2), 35 | new OrderItems(2, 1) 36 | }; 37 | 38 | public final Orders[] ORDERS = { 39 | new Orders(1, 1), 40 | new Orders(2, 1) 41 | }; 42 | 43 | public final Products[] PRODUCTS = { 44 | new Products(1), 45 | new Products(2) 46 | }; 47 | 48 | /** 49 | * Orders 50 | */ 51 | public static class OrderItems { 52 | public final int ORDER_ID; 53 | public final int PRODUCT_ID; 54 | 55 | public OrderItems(int orderId, int productId) { 56 | ORDER_ID = orderId; 57 | PRODUCT_ID = productId; 58 | } 59 | } 60 | 61 | /** 62 | * Orders 63 | */ 64 | public static class Orders { 65 | public final int ORDER_ID; 66 | public final int CUSTOMER_ID; 67 | 68 | 69 | public Orders(int orderId, int customerId) { 70 | ORDER_ID = orderId; 71 | CUSTOMER_ID = customerId; 72 | } 73 | } 74 | 75 | /** 76 | * Customers 77 | */ 78 | public static class Customers { 79 | public final int CUSTOMER_ID; 80 | 81 | 82 | public Customers(int customerId) { 83 | CUSTOMER_ID = customerId; 84 | } 85 | } 86 | 87 | /** 88 | * Products 89 | */ 90 | public static class Products { 91 | public final int PRODUCT_ID; 92 | 93 | 94 | public Products(int productId) { 95 | PRODUCT_ID = productId; 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/gerardnico/calcite/schema/queries/HrFoodmartQuery.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.schema.queries; 2 | 3 | import com.gerardnico.calcite.CalciteJdbc; 4 | import com.gerardnico.calcite.schema.hr.HrSchema; 5 | import org.apache.calcite.jdbc.CalciteConnection; 6 | 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | 11 | public class HrFoodmartQuery { 12 | 13 | /** 14 | * Execute a sample query agains the {@link HrSchema#createHrSchema()} 15 | * 16 | * @param connection 17 | */ 18 | public static void executeSampleQuery(CalciteConnection connection) { 19 | try (Statement statement = connection.createStatement()) { 20 | ResultSet resultSet = statement.executeQuery("select *\n" 21 | + "from foodmart.sales_fact_1997 as s\n" 22 | + "join hr.emps as e\n" 23 | + "on e.empid = s.cust_id"); 24 | CalciteJdbc.printResultSet(resultSet); 25 | } catch (SQLException e) { 26 | throw new RuntimeException(e); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/hrAndFoodmart/hrAndFoodmart.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultSchema": "hr", 4 | "schemas": [ 5 | { 6 | "name": "hr", 7 | "type": "custom", 8 | "factory": "org.apache.calcite.adapter.java.ReflectiveSchema$Factory", 9 | "operand": { 10 | "class": "com.gerardnico.calcite.schema.hr.HrSchema", 11 | "staticMethod": "createHrSchema" 12 | } 13 | }, 14 | { 15 | "name": "foodmart", 16 | "type": "custom", 17 | "factory": "org.apache.calcite.adapter.java.ReflectiveSchema$Factory", 18 | "operand": { 19 | "class": "com.gerardnico.calcite.schema.foodmart.FoodmartSchema", 20 | "staticMethod": "createFoodmartSchema" 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /src/main/resources/sales/DEPTS.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 |

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
DEPTNONAME
10Sales
20Marketing
30Accounts
41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/resources/sales/EMPS.html: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
EMPNONAMEDEPTNO
100Fred30
110Eric20
110John40
120Wilma20
130Alice40
55 | 56 | 57 | -------------------------------------------------------------------------------- /src/main/resources/sales/sales.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultSchema": "SALES", 4 | "schemas": [ { 5 | "name": "SALES", 6 | "type": "custom", 7 | "factory": "org.apache.calcite.adapter.file.FileSchemaFactory", 8 | "operand": { 9 | "tables": [ { 10 | "name": "EMPS", 11 | "url": "file:EMPS.html" 12 | }, { 13 | "name": "DEPTS", 14 | "url": "file:DEPTS.html" 15 | } ] 16 | } 17 | } ] 18 | } -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteCatalogsTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * A catalog in Calcite is a JDBC catalog 7 | * they returns {@link org.apache.calcite.adapter.jdbc.JdbcSchema} 8 | */ 9 | public class CalciteCatalogsTest { 10 | 11 | 12 | /** 13 | * Show how to read and print catalog 14 | */ 15 | @Test 16 | public void catalogReaderTest() { 17 | 18 | CalciteCatalogs.printMock(); 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteFrameworksTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.schema.hr.HrSchemaMin; 4 | import org.apache.calcite.adapter.java.ReflectiveSchema; 5 | import org.apache.calcite.rel.RelNode; 6 | import org.apache.calcite.rel.RelRoot; 7 | import org.apache.calcite.rel.RelWriter; 8 | import org.apache.calcite.rel.externalize.RelWriterImpl; 9 | import org.apache.calcite.schema.SchemaPlus; 10 | import org.apache.calcite.sql.SqlExplainLevel; 11 | import org.apache.calcite.sql.SqlNode; 12 | import org.apache.calcite.sql.dialect.OracleSqlDialect; 13 | import org.apache.calcite.sql.parser.SqlParseException; 14 | import org.apache.calcite.sql.parser.SqlParser; 15 | import org.apache.calcite.tools.*; 16 | import org.junit.Test; 17 | 18 | import java.io.PrintWriter; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | 23 | /** 24 | * This is class that demonstrate the query process planning: 25 | * * parse 26 | * * validate 27 | * * convert to a query plan 28 | * * execute. 29 | * See Query planning process example for more info 30 | */ 31 | public class CalciteFrameworksTest { 32 | 33 | 34 | 35 | @Test 36 | public void parseValidateAndLogicalPlanTest() throws SqlParseException, RelConversionException, ValidationException, SQLException { 37 | 38 | // Build the schema 39 | SchemaPlus rootSchema = Frameworks.createRootSchema(true); 40 | ReflectiveSchema schema = new ReflectiveSchema(new HrSchemaMin()); 41 | SchemaPlus hr = rootSchema.add("HR", schema); 42 | 43 | // Get a non-sensitive parser 44 | SqlParser.Config insensitiveParser = SqlParser.configBuilder() 45 | .setCaseSensitive(false) 46 | .build(); 47 | 48 | // Build a global configuration object 49 | FrameworkConfig config = Frameworks.newConfigBuilder() 50 | .parserConfig(insensitiveParser) 51 | .defaultSchema(hr) 52 | .build(); 53 | 54 | // Get the planner tool 55 | Planner planner = Frameworks.getPlanner(config); 56 | 57 | // Parse the sql to a tree 58 | SqlNode sqlNode = planner.parse("select depts.name, count(emps.empid) from emps inner join depts on emps.deptno = depts.deptno group by depts.deptno, depts.name order by depts.name"); 59 | 60 | // Print it 61 | System.out.println(sqlNode.toSqlString(OracleSqlDialect.DEFAULT)); 62 | 63 | // Validate the tree 64 | SqlNode sqlNodeValidated = planner.validate(sqlNode); 65 | 66 | // Convert the sql tree to a relation expression 67 | RelRoot relRoot = planner.rel(sqlNodeValidated); 68 | 69 | // Explain, print the relational expression 70 | RelNode relNode = relRoot.project(); 71 | final RelWriter relWriter = new RelWriterImpl(new PrintWriter(System.out), SqlExplainLevel.ALL_ATTRIBUTES, false); 72 | relNode.explain(relWriter); 73 | 74 | // Run it 75 | PreparedStatement run = RelRunners.run(relNode); 76 | ResultSet resultSet = run.executeQuery(); 77 | 78 | // Print it 79 | System.out.println("Result:"); 80 | while (resultSet.next()) { 81 | for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { 82 | System.out.print(resultSet.getObject(i)+","); 83 | } 84 | System.out.println(); 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalcitePlannerHepTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.plan.hep.HepPlanner; 4 | import org.apache.calcite.plan.hep.HepProgram; 5 | import org.apache.calcite.rel.RelNode; 6 | import org.apache.calcite.rel.core.JoinRelType; 7 | import org.apache.calcite.tools.FrameworkConfig; 8 | import org.apache.calcite.tools.RelBuilder; 9 | import org.junit.Test; 10 | import org.apache.calcite.rel.rules.FilterJoinRule; 11 | /** 12 | * 13 | * Demo the filter early optimization. 14 | * 15 | * Example based 16 | * on query-optimization.ipynb 17 | */ 18 | public class CalcitePlannerHepTest { 19 | 20 | @Test 21 | public void basePlannerTest() { 22 | 23 | /** 24 | * Create a {@link RelBuilder} with a Hr reflective database 25 | */ 26 | FrameworkConfig config = CalciteFramework.configHrSchemaBased(); 27 | RelBuilder relBuilder = RelBuilder.create(config); 28 | 29 | /** 30 | * Use the {@link RelBuilder} to create an relational expression tree 31 | * equivalent to the below SQL 32 | * 33 | * select * from emps inner join depts on deptno 34 | * where empid = 100 35 | */ 36 | RelNode relNode = relBuilder 37 | .scan("emps") 38 | .scan("depts") 39 | .join(JoinRelType.INNER, "deptno") 40 | .filter(relBuilder.equals(relBuilder.field("empid"), relBuilder.literal(100))) 41 | .build(); 42 | 43 | /** 44 | * Print the tree 45 | */ 46 | System.out.println("----------------------------"); 47 | System.out.println("Build relational expression"); 48 | System.out.println("----------------------------"); 49 | CalciteRel.explain(relNode); 50 | /** 51 | * 3:LogicalFilter(condition=[=($0, 100)]) 52 | * 2:LogicalJoin(condition=[=($1, $5)], joinType=[inner]) 53 | * 0:LogicalTableScan(table=[[hrAndFoodmart, emps]]) 54 | * 1:LogicalTableScan(table=[[hrAndFoodmart, depts]]) 55 | */ 56 | 57 | /** 58 | * Optimize: Push the filter (Filter Early) is implemented by the {@link FilterJoinRule} 59 | * 60 | */ 61 | HepProgram hepProgram = HepProgram.builder() 62 | .addRuleInstance(FilterJoinRule.FILTER_ON_JOIN) 63 | .build(); 64 | 65 | HepPlanner hepPlanner = new HepPlanner(hepProgram); 66 | hepPlanner.setRoot(relNode); 67 | RelNode bestRelNode = hepPlanner.findBestExp(); 68 | System.out.println("----------------------------"); 69 | System.out.println("Best relational expression"); 70 | System.out.println("----------------------------"); 71 | CalciteRel.explain(bestRelNode); 72 | 73 | /** 74 | * Execute 75 | */ 76 | CalciteRel.executeQuery(bestRelNode); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalcitePlannerVolcanoTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.adapter.enumerable.EnumerableConvention; 4 | import org.apache.calcite.plan.RelOptCluster; 5 | import org.apache.calcite.plan.RelOptCost; 6 | import org.apache.calcite.plan.RelTraitSet; 7 | import org.apache.calcite.plan.hep.HepPlanner; 8 | import org.apache.calcite.plan.hep.HepProgram; 9 | import org.apache.calcite.plan.volcano.VolcanoPlanner; 10 | import org.apache.calcite.rel.RelNode; 11 | import org.apache.calcite.rel.core.JoinRelType; 12 | import org.apache.calcite.rel.rules.FilterJoinRule; 13 | import org.apache.calcite.tools.FrameworkConfig; 14 | import org.apache.calcite.tools.RelBuilder; 15 | import org.apache.calcite.tools.RelRunners; 16 | import org.junit.Test; 17 | 18 | import java.sql.ResultSet; 19 | import java.sql.SQLException; 20 | 21 | /** 22 | * Demo the filter early optimization. 23 | *

24 | * Example based 25 | * on query-optimization.ipynb 26 | *

27 | * The VolcanoPlanner attempts to apply a set of rules to minimize the cost of executing a query (Cost Based Optimizer) 28 | * VolcanoPlanner is the default planner used by Calcite. 29 | * Note that all the operators in the tree we constructed above are logical operators meaning 30 | * that they don't have a specific implementation. 31 | * All logical operators must be converted to physical operators before a query can actually be implemented, 32 | * which is required for VolcanoPlanner to provide an estimated cost. 33 | * Each physical operator in Calcite has a calling convention which specifies how the query will actually be executed. 34 | * Since we're not working with an actual database we'll use EnumerableConvention which simply implements queries over 35 | * collections implementing the Enumerable interface. 36 | */ 37 | public class CalcitePlannerVolcanoTest { 38 | 39 | @Test 40 | public void basePlannerTest() throws SQLException { 41 | 42 | /** 43 | * Create a {@link RelBuilder} with a Hr reflective database 44 | */ 45 | FrameworkConfig config = CalciteFramework.configHrSchemaBased(); 46 | RelBuilder relBuilder = RelBuilder.create(config); 47 | 48 | /** 49 | * Use the {@link RelBuilder} to create an relational expression tree 50 | * equivalent to the below SQL 51 | * 52 | * select * from emps inner join depts on deptno 53 | * where empid = 100 54 | */ 55 | RelNode sqlNode = relBuilder 56 | .scan("emps") 57 | // .scan("depts") 58 | // .join(JoinRelType.INNER, "deptno") 59 | // .filter(relBuilder.equals(relBuilder.field("empid"), relBuilder.literal(100))) 60 | // .project(relBuilder.field("name")) 61 | .aggregate(relBuilder.groupKey(), 62 | relBuilder.count(false, "C")) 63 | .build(); 64 | 65 | /** 66 | * Print the tree 67 | */ 68 | System.out.println("----------------------------"); 69 | System.out.println("Build relational expression"); 70 | System.out.println("----------------------------"); 71 | CalciteRel.explain(sqlNode); 72 | /** 73 | * 3:LogicalFilter(condition=[=($0, 100)]) 74 | * 2:LogicalJoin(condition=[=($1, $5)], joinType=[inner]) 75 | * 0:LogicalTableScan(table=[[hrAndFoodmart, emps]]) 76 | * 1:LogicalTableScan(table=[[hrAndFoodmart, depts]]) 77 | */ 78 | 79 | 80 | /** 81 | * Optimize: Push the filter (Filter Early) is implemented by the {@link FilterJoinRule} 82 | * 83 | * VolcanoPlanner has a default set of rules which includes {@link FilterJoinRule} 84 | */ 85 | RelOptCluster cluster = sqlNode.getCluster(); 86 | VolcanoPlanner planner = (VolcanoPlanner) cluster.getPlanner(); 87 | 88 | RelTraitSet desiredTraits = cluster.traitSet().replace(EnumerableConvention.INSTANCE); 89 | RelNode newRoot = planner.changeTraits(sqlNode, desiredTraits); 90 | planner.setRoot(newRoot); 91 | 92 | RelNode optimized = planner.findBestExp(); 93 | System.out.println("----------------------------"); 94 | System.out.println("Best relational expression"); 95 | System.out.println("----------------------------"); 96 | CalciteRel.explain(optimized); 97 | 98 | System.out.println("----------------------------"); 99 | System.out.println("Logical operators have been replaced with enumerable operators."); 100 | System.out.println("This means we can now execute the optimized query and get a result."); 101 | System.out.println("----------------------------"); 102 | 103 | /** 104 | * Cost 105 | */ 106 | RelOptCost cost = planner.getCost(optimized); 107 | double rows = cost.getRows(); 108 | double cpu = cost.getCpu(); 109 | double io = cost.getIo(); 110 | System.out.println("----------------------------"); 111 | System.out.println("Cost"); 112 | System.out.println("----------------------------"); 113 | System.out.println("Rows: "+rows); 114 | System.out.println("Cpu: "+cpu); 115 | System.out.println("Io: "+io); 116 | 117 | /** 118 | * Run 119 | * The runner use a JDBC connection, we need then to build 120 | * the schema, that's why we pass the schema 121 | */ 122 | CalciteRel.executeAndPrint(optimized, config.getDefaultSchema()); 123 | 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteRelJdbcTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.demo.JdbcStore; 4 | import org.apache.calcite.rel.RelNode; 5 | import org.apache.calcite.tools.RelBuilder; 6 | import org.junit.Test; 7 | 8 | import java.sql.Connection; 9 | import java.sql.PreparedStatement; 10 | import java.sql.SQLException; 11 | 12 | public class CalciteRelJdbcTest { 13 | 14 | @Test 15 | public void relToSqlTest() { 16 | 17 | // Create a data store wrapper object 18 | JdbcStore jdbcStore = JdbcStore.createDefault(); 19 | 20 | // Build the schema 21 | try { 22 | Connection connection = jdbcStore.getConnection(); 23 | connection.createStatement().execute("CREATE TABLE TEST (TEST_VALUE INT)"); 24 | String insertStatement = "INSERT INTO TEST (TEST_VALUE) VALUES(?)"; 25 | for (int i = 0; i < 20; i++) { 26 | PreparedStatement preparedStatement = connection.prepareStatement(insertStatement); 27 | preparedStatement.setInt(1, i); 28 | preparedStatement.execute(); 29 | } 30 | System.out.println("Records inserted"); 31 | System.out.println(); 32 | } catch (SQLException e) { 33 | throw new RuntimeException(e); 34 | } 35 | 36 | // Get the builder 37 | RelBuilder builder = jdbcStore.getRelBuilder(); 38 | 39 | // Build a relational expression and execute 40 | RelNode relNode = builder.scan("TEST") 41 | .project(builder.field("TEST_VALUE")) 42 | .build(); 43 | String sql = CalciteRel.fromRelNodeToSql(relNode,jdbcStore.getDialect()); 44 | System.out.println(sql); 45 | System.out.println("Execute the relational expression gives a pointer exception"); 46 | // Null pointer exception: CalciteRel.executeAndPrint(relNode); 47 | 48 | // Project Fetch 49 | int offset = 0; 50 | int fetch = 2; 51 | relNode = builder.scan("TEST") 52 | .project(builder.field("TEST_VALUE")) 53 | .sortLimit(offset, fetch, builder.field("TEST_VALUE")) 54 | .build(); 55 | sql = CalciteRel.fromRelNodeToSql(relNode,jdbcStore.getDialect()); 56 | System.out.println(sql); 57 | System.out.println("Execute the SQL"); 58 | jdbcStore.executeAndPrintQuery(sql); 59 | 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteRelLogicalExpressionTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.rel.RelNode; 4 | import org.apache.calcite.sql.fun.SqlStdOperatorTable; 5 | import org.apache.calcite.tools.RelBuilder; 6 | import org.junit.Test; 7 | 8 | public class CalciteRelLogicalExpressionTest { 9 | 10 | @Test 11 | public void tablesAsValuesTest() { 12 | RelBuilder builder = CalciteRel.createOrderEntryBasedRelBuilder(); 13 | RelNode relNode = CalciteRelLogicalExpression.tableAsValues(builder); 14 | CalciteRel.showExplainSqlAndRun(relNode); 15 | } 16 | 17 | @Test 18 | public void tableScanTest() { 19 | RelBuilder builder = CalciteRel.createHrScottBasedRelBuilder(); 20 | RelNode relNode = CalciteRelLogicalExpression.tableScan(builder); 21 | CalciteRel.showExplainSqlAndRun(relNode); 22 | } 23 | 24 | /** 25 | * Example modified of Push and pop 26 | * The first join has not the good key 27 | *

28 | * There is also example code at example4 29 | */ 30 | @Test 31 | public void pushAndPopBushyJoinTest() { 32 | RelBuilder builder = CalciteRel.createOrderEntryBasedRelBuilder(); 33 | RelNode relNode = CalciteRelLogicalExpression.bushyJoin(builder); 34 | CalciteRel.showExplainSqlAndRun(relNode); 35 | } 36 | 37 | @Test 38 | public void recursiveQueryTest() { 39 | RelBuilder builder = CalciteRel.createHrScottBasedRelBuilder(); 40 | RelNode relNode = CalciteRelLogicalExpression.recursiveQuery(builder); 41 | CalciteRel.explain(relNode); 42 | // To print the Sql, an operator should be implemented 43 | CalciteRel.executeAndPrint(relNode); 44 | } 45 | 46 | 47 | @Test 48 | public void filterAndAggregateTest() { 49 | RelBuilder relBuilder = CalciteRel.createHrScottBasedRelBuilder(); 50 | RelNode relNode = CalciteRelLogicalExpression.filterAndCountSumAggregate(relBuilder); 51 | CalciteRel.showExplainSqlAndRun(relNode); 52 | } 53 | 54 | @Test 55 | public void projectTest() { 56 | RelBuilder relBuilder = CalciteRel.createHrScottBasedRelBuilder(); 57 | RelNode relNode = CalciteRelLogicalExpression.project(relBuilder); 58 | CalciteRel.showExplainSqlAndRun(relNode); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteSchemaTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.schema.queries.HrFoodmartQuery; 4 | import org.apache.calcite.jdbc.CalciteConnection; 5 | import org.junit.Test; 6 | 7 | public class CalciteSchemaTest { 8 | 9 | /** 10 | * Example of using Calcite via JDBC. 11 | * 12 | *

Schema is specified programmatically via reflection on class.

13 | *

14 | * Copy of JdbcExample 15 | */ 16 | @Test 17 | public void reflectiveWithCode() { 18 | 19 | CalciteConnection calciteConnection = CalciteConnections.getConnectionWithoutModel(); 20 | CalciteSchemaStatic.addReflectiveSchemaToConnection(calciteConnection); 21 | HrFoodmartQuery.executeSampleQuery(calciteConnection); 22 | 23 | } 24 | 25 | @Test 26 | public void reflectiveWithModel() { 27 | 28 | CalciteConnection calciteConnection = CalciteSchemaStatic.addReflectiveSchemaToConnectionViaModel(); 29 | HrFoodmartQuery.executeSampleQuery(calciteConnection); 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteSqlSelectTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.sql.SqlNode; 4 | import org.apache.calcite.sql.parser.SqlParseException; 5 | import org.apache.calcite.tools.FrameworkConfig; 6 | import org.apache.calcite.tools.Planner; 7 | import org.junit.Test; 8 | 9 | public class CalciteSqlSelectTest { 10 | 11 | 12 | @Test 13 | public void sqlSelect() throws SqlParseException { 14 | FrameworkConfig config = CalciteFramework.configScottSchemaBased(); 15 | Planner planner = CalcitePlanner.getPlannerFromFrameworkConfig(config); 16 | SqlNode sqlNode = planner.parse("select deptno, count(1) from emp where SAL between 1000 and 2000 group by deptno"); 17 | CalciteSqlSelect.printInfo(sqlNode); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteSqlValidationTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.prepare.Prepare; 4 | import org.apache.calcite.runtime.CalciteContextException; 5 | import org.apache.calcite.sql.SqlNode; 6 | import org.apache.calcite.sql.parser.SqlParseException; 7 | import org.apache.calcite.sql.parser.SqlParser; 8 | import org.apache.calcite.tools.FrameworkConfig; 9 | import org.apache.calcite.tools.Planner; 10 | import org.apache.calcite.tools.ValidationException; 11 | import org.junit.Test; 12 | 13 | public class CalciteSqlValidationTest { 14 | 15 | @Test 16 | public void parseValidationGoodTest() { 17 | final String sql = "select * from SALES.EMP"; 18 | SqlNode sqlNode = CalciteSql.fromSqlToSqlNode(sql); 19 | CalciteSqlValidation.createCustomSqlValidator().validate(sqlNode); 20 | } 21 | 22 | @Test(expected = CalciteContextException.class) 23 | public void parseValidationBadTest() { 24 | final String sql = "select * from YOLO"; // Yolo is not a table 25 | SqlNode sqlNode = CalciteSql.fromSqlToSqlNode(sql); 26 | CalciteSqlValidation.createCustomSqlValidator().validate(sqlNode); 27 | } 28 | 29 | @Test 30 | public void validateFromPlanner() throws ValidationException { 31 | FrameworkConfig config = CalciteFramework.configScottSchemaBased(); 32 | Planner planner = CalcitePlanner.getPlannerFromFrameworkConfig(config); 33 | final String sql = "select * from EMP"; 34 | SqlNode sqlNode = null; 35 | try { 36 | sqlNode = planner.parse(sql); 37 | } catch (SqlParseException e) { 38 | throw new RuntimeException(e); 39 | } 40 | planner.validate(sqlNode); 41 | 42 | } 43 | 44 | @Test 45 | public void validateFromSqlValidatorUtil() throws SqlParseException { 46 | 47 | // A query parsed 48 | SqlParser sqlParser = CalciteSqlParser.create("select * from EMP"); 49 | SqlNode sqlNode = sqlParser.parseStmt(); 50 | 51 | // A catalog with the EMP table 52 | Prepare.CatalogReader catalog = CalciteCatalogs.createMock(); 53 | 54 | // Validate with a default SQL conformance 55 | SqlNode sqlNodeValidated = CalciteSqlValidation.validateFromUtilValidatorPlanner(catalog,sqlNode); 56 | 57 | // Print 58 | CalciteSql.print(sqlNodeValidated); 59 | 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteSqlVisitorTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.sql.SqlNode; 4 | import org.apache.calcite.sql.parser.SqlParseException; 5 | import org.apache.calcite.tools.FrameworkConfig; 6 | import org.apache.calcite.tools.Planner; 7 | import org.junit.Test; 8 | 9 | public class CalciteSqlVisitorTest { 10 | 11 | @Test 12 | public void sqlVisitor() throws SqlParseException { 13 | FrameworkConfig config = CalciteFramework.configScottSchemaBased(); 14 | Planner planner = CalcitePlanner.getPlannerFromFrameworkConfig(config); 15 | SqlNode sqlNode = planner.parse("select cast(deptno as varchar), count(1) from emp where SAL between 1000 and 2000 group by deptno"); 16 | sqlNode.accept(new CalciteSqlVisitor()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/CalciteSqlWriterTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import org.apache.calcite.runtime.CalciteContextException; 4 | import org.apache.calcite.sql.SqlNode; 5 | import org.apache.calcite.sql.SqlWriterConfig; 6 | import org.apache.calcite.sql.pretty.SqlPrettyWriter; 7 | import org.junit.Test; 8 | 9 | /** 10 | * Parser is used to: 11 | * * pretty print 12 | * * validate 13 | */ 14 | public class CalciteSqlWriterTest { 15 | 16 | @Test 17 | public void prettyPrintDemoTest() { 18 | final String sql = "select x as a, b as b, c as c, d," 19 | + " 'mixed-Case string'," 20 | + " unquotedCamelCaseId," 21 | + " \"quoted id\" " 22 | + "from" 23 | + " (select *" 24 | + " from t" 25 | + " where x = y and a > 5" 26 | + " group by z, zz" 27 | + " window w as (partition by c)," 28 | + " w1 as (partition by c,d order by a, b" 29 | + " range between interval '2:2' hour to minute preceding" 30 | + " and interval '1' day following)) " 31 | + "order by gg desc nulls last, hh asc"; 32 | SqlNode sqlNode = CalciteSql.fromSqlToSqlNode(sql); 33 | SqlWriterConfig config = SqlPrettyWriter.config() 34 | .withLineFolding(SqlWriterConfig.LineFolding.STEP) 35 | .withSelectFolding(SqlWriterConfig.LineFolding.TALL) 36 | .withFromFolding(SqlWriterConfig.LineFolding.TALL) 37 | .withWhereFolding(SqlWriterConfig.LineFolding.TALL) 38 | .withHavingFolding(SqlWriterConfig.LineFolding.TALL) 39 | .withIndentation(4) 40 | .withClauseEndsLine(true); 41 | CalciteSql.print(sqlNode,config); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/HelloWorldTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite; 2 | 3 | import com.gerardnico.calcite.demo.HelloWorld; 4 | import org.junit.Test; 5 | 6 | import java.sql.SQLException; 7 | 8 | public class HelloWorldTest { 9 | 10 | @Test 11 | public void helloWorldTest() throws SQLException, ClassNotFoundException { 12 | String[] args = {}; 13 | HelloWorld.main(args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/com/gerardnico/calcite/adapter/AdapterContextTest.java: -------------------------------------------------------------------------------- 1 | package com.gerardnico.calcite.adapter; 2 | 3 | import org.apache.calcite.jdbc.CalcitePrepare; 4 | import org.apache.calcite.jdbc.CalciteSchema; 5 | import org.apache.calcite.jdbc.Driver; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import java.sql.Connection; 10 | import java.sql.SQLException; 11 | import java.util.List; 12 | 13 | /** 14 | * Not really up to date information 15 | * Testing adapter in Java 16 | */ 17 | public class AdapterContextTest { 18 | 19 | @Test 20 | @Ignore 21 | public void testSelectAllFromTable() throws SQLException { 22 | AdapterContext ctx = new AdapterContext(); 23 | List defaultSchemaPath = ctx.getDefaultSchemaPath(); 24 | CalciteSchema rootSchema = ctx.getRootSchema(); 25 | 26 | String sql = "SELECT * FROM TABLENAME"; 27 | CalcitePrepare.Query query = CalcitePrepare.Query.of(sql); 28 | Class elementType = Object[].class; 29 | //CalcitePrepare.CalciteSignature prepared = new CalcitePrepareImpl().prepareSql(ctx, query, elementType, -1); 30 | 31 | Driver driver = new Driver(); 32 | Connection connection = driver.connect("url",null); 33 | // connection.create 34 | // prepared.enumerable(ctx); 35 | // Object enumerable = prepared.getExecutable(); 36 | // etc. 37 | } 38 | } 39 | --------------------------------------------------------------------------------