├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src └── main ├── java └── com │ └── liangbo │ └── xing │ └── calcite │ ├── App.java │ ├── CalciteApplication.java │ ├── DateFormat.java │ ├── MemoryData.java │ ├── MemoryEnumerator.java │ ├── MemorySchema.java │ ├── MemorySchemaFactory.java │ ├── function │ ├── TimeFunction.java │ ├── TimeOperator.java │ └── TimeParameter.java │ └── table │ └── MemoryTable.java └── resources ├── School.json └── application.properties /README.md: -------------------------------------------------------------------------------- 1 | # calcite 2 | sql 解析引擎 示列 3 | ## 最大功能通过sql 访问任意结构数据 网上的一个示列 4 | 5 | ### 几个重要的组件 6 | >> 1 AbstractSchema 通过schema实现从数据源自己的概念(DataBase及Table)向Calcite的概念(MemoryTable)进行转换的过程 7 | >> 2 AbstractTable 实际上就是对Java 类的封装,将原始数据源中的Column概念(列数据类型等)转换为Calcite中的Column概念(RelDataType) 8 | >> 3 ScannableTable 调用scan函数构建emurator用于读取数据,使用Enumerator来读取数据。再Enumerator中会实现从原始数据源的数据到实际我们需要的数据之间的转换过程。 9 | >> 4 Enumerator 数据迭代器,用于将从数据源中查询出来的数据进行迭代取出。 10 | 11 | ```java 12 | 生成一些内存数据 13 | student.data.add(Arrays.asList("fengysh","A000001", "1", "1989-06-10", "anhui")); 14 | student.data.add(Arrays.asList("wyshz","A000002", "1", "1989-03-04", "henan")); 15 | student.data.add(Arrays.asList("hesk","A000003", "1", "1992-02-10", "anhui")); 16 | student.data.add(Arrays.asList("whst","A000004", "2", "1993-04-08", "hebei")); 17 | student.data.add(Arrays.asList("wush","B000005", "2", "1998-02-26", "beijing")); 18 | student.data.add(Arrays.asList("ehsn","C000006", "3", "1990-06-18", "sichuan")); 19 | student.data.add(Arrays.asList("wisyh","D000007", "3", "1991-03-06", "zhejiang")); 20 | student.data.add(Arrays.asList("helsj","D000008", "4", "1993-09-10", "jiangsu")); 21 | 22 | ``` 23 | 24 | ### 通过将内存的数据 转换为calcite datasource, table cloumn 25 | 26 | ```java 27 | //通过标准的sql语句访问 28 | result = st.executeQuery("select S.\"id\", SUM(S.\"classId\") from \"Student\" as S group by S.\"id\""); 29 | while(result.next()) { 30 | System.out.println(result.getString(1) + "\t" + result.getString(2)); 31 | } 32 | 33 | ``` 34 | 35 | ##### 功能很强大的 包括hive都在使用 36 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.liangbo.xing 7 | calcite 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | calcite 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.9.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-actuator 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | 44 | com.google.guava 45 | guava 46 | 23.5-jre 47 | 48 | 49 | joda-time 50 | joda-time 51 | 2.9.9 52 | 53 | 54 | mysql 55 | mysql-connector-java 56 | 6.0.6 57 | 58 | 59 | com.github.shyiko 60 | mysql-binlog-connector-java 61 | 0.13.0 62 | 63 | 64 | com.zaxxer 65 | HikariCP 66 | 2.7.4 67 | 68 | 69 | org.apache.calcite 70 | calcite-core 71 | 1.15.0 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-maven-plugin 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/App.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import com.liangbo.xing.calcite.function.TimeOperator; 4 | import org.apache.calcite.jdbc.CalciteConnection; 5 | import org.apache.calcite.schema.impl.ScalarFunctionImpl; 6 | 7 | import java.sql.*; 8 | import java.util.Properties; 9 | 10 | /** 11 | * Hello world! 12 | * 13 | */ 14 | public class App 15 | { 16 | public static void main( String[] args ) throws NoSuchMethodException, SecurityException{ 17 | try { 18 | Class.forName("org.apache.calcite.jdbc.Driver"); 19 | } catch (ClassNotFoundException e1) { 20 | e1.printStackTrace(); 21 | } 22 | Properties info = new Properties(); 23 | try { 24 | /** 25 | * 本步骤中,使用DriverManager 建立连接,步骤如下: 26 | * 27 | */ 28 | Connection connection = DriverManager.getConnection("jdbc:calcite:model=" + "/Users/xingliangbo/Documents/workspace_bill/calcite/src/main/resources/School.json", info); 29 | CalciteConnection calciteConn = connection.unwrap(CalciteConnection.class); 30 | calciteConn.getRootSchema().add("YEAR", ScalarFunctionImpl.create(TimeOperator.class.getMethod("YEAR", Date.class))); 31 | calciteConn.getRootSchema().add("COM", ScalarFunctionImpl.create(TimeOperator.class.getMethod("COM", String.class, String.class))); 32 | /** 33 | * getTables 操作会将数据源原始的table概念转换为calcite中的Table概念,还要将操作函数类中的所有操作函数 34 | * 读取出来。 35 | */ 36 | ResultSet result = connection.getMetaData().getTables(null, null, null, null); 37 | while(result.next()) { 38 | System.out.println("Catalog : " + result.getString(1) + ",Database : " + result.getString(2) + ",Table : " + result.getString(3)); 39 | } 40 | result.close(); 41 | /** 42 | * getColumns 操作会将原始数据源中的Column概念(列数据类型等)转换为Calcite中的Column概念(RelDataType) 43 | */ 44 | result = connection.getMetaData().getColumns(null, null, "Student", null); 45 | while(result.next()) { 46 | System.out.println("name : " + result.getString(4) + ", type : " + result.getString(5) + ", typename : " + result.getString(6)); 47 | } 48 | result.close(); 49 | 50 | Statement st = connection.createStatement(); 51 | /** 52 | * 调用scan函数读取数据库,在执行query的过程中,会调用Calcite的MemoryTable概念的scan来获取表的迭代器,这个迭代器是我们自己定义的 53 | * 用于对表数据进行迭代处理。这个处理过程就是实现从原始数据源的数据到我们所需要的数据之间的转换过程,要调用迭代器的功能来完成实现。 54 | */ 55 | result = st.executeQuery("select S.\"id\", SUM(S.\"classId\") from \"Student\" as S group by S.\"id\""); 56 | while(result.next()) { 57 | System.out.println(result.getString(1) + "\t" + result.getString(2)); 58 | } 59 | result.close(); 60 | connection.close(); 61 | } catch (SQLException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/CalciteApplication.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class CalciteApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(CalciteApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/DateFormat.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import java.sql.Date; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Map; 6 | import java.util.TimeZone; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * 工具类 11 | * 12 | * @author hdfs 13 | */ 14 | public class DateFormat { 15 | 16 | public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd"; 17 | public static final String DEFAULT_DATETIME_PATTERN_WITHOUT_MILLISECONDS = "yyyy-MM-dd HH:mm:ss"; 18 | public static final String DEFAULT_DATETIME_PATTERN_WITH_MILLISECONDS = "yyyy-MM-dd HH:mm:ss.SSS"; 19 | 20 | static final private Map> threadLocalMap = new ConcurrentHashMap>(); 21 | 22 | public static SimpleDateFormat getDateFormat(String datePattern) { 23 | ThreadLocal formatThreadLocal = threadLocalMap.get(datePattern); 24 | if (formatThreadLocal == null) { 25 | threadLocalMap.put(datePattern, formatThreadLocal = new ThreadLocal()); 26 | } 27 | SimpleDateFormat format = formatThreadLocal.get(); 28 | if (format == null) { 29 | format = new SimpleDateFormat(datePattern); 30 | format.setTimeZone(TimeZone.getTimeZone("GMT")); // NOTE: this must be GMT to calculate epoch date correctly 31 | formatThreadLocal.set(format); 32 | } 33 | return format; 34 | } 35 | 36 | public static String formatToDateStr(long millis) { 37 | return getDateFormat(DEFAULT_DATE_PATTERN).format(new Date(millis)); 38 | } 39 | 40 | public static String formatToTimeStr(long millis) { 41 | return getDateFormat(DEFAULT_DATETIME_PATTERN_WITH_MILLISECONDS).format(new Date(millis)); 42 | } 43 | 44 | public static String dateToString(Date date) { 45 | return dateToString(date, DEFAULT_DATETIME_PATTERN_WITHOUT_MILLISECONDS); 46 | } 47 | 48 | public static String dateToString(Date date, String pattern) { 49 | return getDateFormat(pattern).format(date); 50 | } 51 | 52 | public static Date stringToDate(String str) { 53 | return stringToDate(str, DEFAULT_DATE_PATTERN); 54 | } 55 | 56 | public static Date stringToDate(String str, String pattern) { 57 | Date date = null; 58 | try { 59 | date = Date.valueOf(str); 60 | } catch (Exception e) { 61 | throw new IllegalArgumentException("'" + str + "' is not a valid date of pattern '" + pattern + "'", e); 62 | } 63 | return date; 64 | } 65 | 66 | public static long stringToMillis(String str) { 67 | if (isAllDigits(str)) { 68 | return Long.parseLong(str); 69 | } else if (str.length() == 10) { 70 | return stringToDate(str, DEFAULT_DATE_PATTERN).getTime(); 71 | } else if (str.length() == 19) { 72 | return stringToDate(str, DEFAULT_DATETIME_PATTERN_WITHOUT_MILLISECONDS).getTime(); 73 | } else if (str.length() == 23) { 74 | return stringToDate(str, DEFAULT_DATETIME_PATTERN_WITH_MILLISECONDS).getTime(); 75 | } else { 76 | throw new IllegalArgumentException("there is no valid date pattern for:" + str); 77 | } 78 | } 79 | 80 | private static boolean isAllDigits(String str) { 81 | for (int i = 0, n = str.length(); i < n; i++) { 82 | if (Character.isDigit(str.charAt(i)) == false) 83 | return false; 84 | } 85 | return true; 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/MemoryData.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import org.apache.calcite.sql.type.SqlTypeName; 4 | 5 | import java.sql.Date; 6 | import java.util.*; 7 | 8 | /** 9 | * 内存数据源, 提供数据 10 | * 11 | * @author hdfs 12 | */ 13 | public class MemoryData { 14 | public static final Map MAP = new HashMap(); 15 | public static Map SQLTYPE_MAPPING = new HashMap(); 16 | @SuppressWarnings("rawtypes") 17 | public static Map JAVATYPE_MAPPING = new HashMap(); 18 | 19 | /** 20 | * 准备数据源中的数据 21 | */ 22 | static { 23 | initRowType(); 24 | Database school = new Database(); 25 | Table student = new Table(); 26 | initStudentTable(student); 27 | 28 | Table classs = new Table(); 29 | initClassTable(classs); 30 | 31 | school.tables.add(student); 32 | school.tables.add(classs); 33 | MAP.put("school", school); 34 | } 35 | 36 | public static void initRowType() { 37 | SQLTYPE_MAPPING.put("char", SqlTypeName.CHAR); 38 | JAVATYPE_MAPPING.put("char", Character.class); 39 | SQLTYPE_MAPPING.put("varchar", SqlTypeName.VARCHAR); 40 | JAVATYPE_MAPPING.put("varchar", String.class); 41 | SQLTYPE_MAPPING.put("boolean", SqlTypeName.BOOLEAN); 42 | SQLTYPE_MAPPING.put("integer", SqlTypeName.INTEGER); 43 | JAVATYPE_MAPPING.put("integer", Integer.class); 44 | SQLTYPE_MAPPING.put("tinyint", SqlTypeName.TINYINT); 45 | SQLTYPE_MAPPING.put("smallint", SqlTypeName.SMALLINT); 46 | SQLTYPE_MAPPING.put("bigint", SqlTypeName.BIGINT); 47 | SQLTYPE_MAPPING.put("decimal", SqlTypeName.DECIMAL); 48 | SQLTYPE_MAPPING.put("numeric", SqlTypeName.DECIMAL); 49 | SQLTYPE_MAPPING.put("float", SqlTypeName.FLOAT); 50 | SQLTYPE_MAPPING.put("real", SqlTypeName.REAL); 51 | SQLTYPE_MAPPING.put("double", SqlTypeName.DOUBLE); 52 | SQLTYPE_MAPPING.put("date", SqlTypeName.DATE); 53 | JAVATYPE_MAPPING.put("date", Date.class); 54 | SQLTYPE_MAPPING.put("time", SqlTypeName.TIME); 55 | SQLTYPE_MAPPING.put("timestamp", SqlTypeName.TIMESTAMP); 56 | SQLTYPE_MAPPING.put("any", SqlTypeName.ANY); 57 | } 58 | 59 | public static void initClassTable(Table cl) { 60 | cl.tableName = "Class"; 61 | Column name = new Column(); 62 | name.name = "name"; 63 | name.type = "varchar"; 64 | cl.columns.add(name); 65 | 66 | Column id = new Column(); 67 | id.name = "id"; 68 | id.type = "integer"; 69 | cl.columns.add(id); 70 | 71 | Column teacher = new Column(); 72 | teacher.name = "teacher"; 73 | teacher.type = "varchar"; 74 | cl.columns.add(teacher); 75 | 76 | cl.data.add(Arrays.asList("3-1", "1", "fengsu")); 77 | cl.data.add(Arrays.asList("3-2", "2", "sunshue")); 78 | cl.data.add(Arrays.asList("3-3", "3", "sunshdh")); 79 | cl.data.add(Arrays.asList("3-4", "4", "shwud")); 80 | } 81 | 82 | public static void initStudentTable(Table student) { 83 | student.tableName = "Student"; 84 | Column name = new Column(); 85 | name.name = "name"; 86 | name.type = "varchar"; 87 | student.columns.add(name); 88 | 89 | Column id = new Column(); 90 | id.name = "id"; 91 | id.type = "varchar"; 92 | student.columns.add(id); 93 | 94 | Column classId = new Column(); 95 | classId.name = "classId"; 96 | classId.type = "integer"; 97 | student.columns.add(classId); 98 | 99 | Column birth = new Column(); 100 | birth.name = "birthday"; 101 | birth.type = "date"; 102 | student.columns.add(birth); 103 | 104 | Column home = new Column(); 105 | home.name = "home"; 106 | home.type = "varchar"; 107 | student.columns.add(home); 108 | 109 | student.data.add(Arrays.asList("fengysh","A000001", "1", "1989-06-10", "anhui")); 110 | student.data.add(Arrays.asList("wyshz","A000002", "1", "1989-03-04", "henan")); 111 | student.data.add(Arrays.asList("hesk","A000003", "1", "1992-02-10", "anhui")); 112 | student.data.add(Arrays.asList("whst","A000004", "2", "1993-04-08", "hebei")); 113 | student.data.add(Arrays.asList("wush","B000005", "2", "1998-02-26", "beijing")); 114 | student.data.add(Arrays.asList("ehsn","C000006", "3", "1990-06-18", "sichuan")); 115 | student.data.add(Arrays.asList("wisyh","D000007", "3", "1991-03-06", "zhejiang")); 116 | student.data.add(Arrays.asList("helsj","D000008", "4", "1993-09-10", "jiangsu")); 117 | } 118 | 119 | public static class Database{ 120 | public List tables = new LinkedList
(); 121 | } 122 | 123 | 124 | public static class Table{ 125 | public String tableName; 126 | public List columns = new LinkedList(); 127 | public List> data = new LinkedList>(); 128 | } 129 | 130 | public static class Column{ 131 | public String name; 132 | public String type; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/MemoryEnumerator.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import org.apache.calcite.linq4j.Enumerator; 4 | 5 | import java.math.BigDecimal; 6 | import java.util.List; 7 | 8 | /** 9 | * 数据迭代器,用于将从数据源中查询出来的数据进行迭代取出。 10 | * 11 | * @author hdfs 12 | */ 13 | public class MemoryEnumerator implements Enumerator { 14 | private List> data = null; 15 | private int currentIndex = -1; 16 | private RowConverter rowConvert; 17 | private List columnTypes; 18 | 19 | /** 20 | * data 是所有的数据内容 21 | * @param fields 数据坐标 22 | * @param types 数据列类型 23 | * @param data 所有数据内容,从数据库中查询到的数据内容 24 | */ 25 | public MemoryEnumerator(int[] fields, List types, List> data) { 26 | this.data = data; 27 | this.columnTypes = types; 28 | rowConvert = (RowConverter) new ArrayRowConverter(fields); 29 | } 30 | 31 | /** 32 | * 行数据转换器,用于将一行数据按照列数据类型转换为对象的形式 33 | * @author hdfs 34 | * 35 | * @param 36 | */ 37 | abstract static class RowConverter{ 38 | abstract E convertRow(List rows, List columnTypes); 39 | } 40 | 41 | static class ArrayRowConverter extends RowConverter { 42 | private int[] fields; 43 | 44 | public ArrayRowConverter(int[] fields) { 45 | this.fields = fields; 46 | } 47 | 48 | /** 49 | * 用于将从数据源中查询获得的数据转换为预先设定的类型,这里转换为数组的形式 50 | */ 51 | @Override 52 | Object[] convertRow(List rows, List columnTypes) { 53 | Object[] objects = new Object[fields.length]; 54 | int i = 0 ; 55 | for(int field : this.fields) { 56 | objects[i ++] = convertOptiqCellValue(rows.get(field), columnTypes.get(field)); 57 | } 58 | return objects; 59 | } 60 | } 61 | 62 | public void close() { 63 | // TODO Auto-generated method stub 64 | 65 | } 66 | 67 | public E current() { 68 | List line = data.get(currentIndex); 69 | return rowConvert.convertRow(line, this.columnTypes); 70 | } 71 | 72 | public boolean moveNext() { 73 | return ++ currentIndex < data.size(); 74 | } 75 | 76 | public void reset() { 77 | // TODO Auto-generated method stub 78 | 79 | } 80 | 81 | 82 | public static Object convertOptiqCellValue(String strValue, String dataType) { 83 | if (strValue == null) 84 | return null; 85 | 86 | if ((strValue.equals("") || strValue.equals("\\N")) && !dataType.equals("string")) 87 | return null; 88 | 89 | // TODO use data type enum instead of string comparison 90 | if ("date".equals(dataType)) { 91 | // convert epoch time 92 | /* 93 | Date dateValue = DateFormat.stringToDate(strValue); // NOTE: forces GMT timezone 94 | long millis = dateValue.getTime(); 95 | long days = millis / (1000 * 3600 * 24); 96 | return Integer.valueOf((int) days); // Optiq expects Integer instead of Long. by honma 97 | */ 98 | return DateFormat.stringToDate(strValue); 99 | } else if ("tinyint".equals(dataType)) { 100 | return Byte.valueOf(strValue); 101 | } else if ("short".equals(dataType) || "smallint".equals(dataType)) { 102 | return Short.valueOf(strValue); 103 | } else if ("integer".equals(dataType)) { 104 | return Integer.valueOf(strValue); 105 | } else if ("long".equals(dataType) || "bigint".equals(dataType)) { 106 | return Long.valueOf(strValue); 107 | } else if ("double".equals(dataType)) { 108 | return Double.valueOf(strValue); 109 | } else if ("decimal".equals(dataType)) { 110 | return new BigDecimal(strValue); 111 | } else if ("timestamp".equals(dataType)) { 112 | return Long.valueOf(DateFormat.stringToMillis(strValue)); 113 | } else if ("float".equals(dataType)) { 114 | return Float.valueOf(strValue); 115 | } else if ("boolean".equals(dataType)) { 116 | return Boolean.valueOf(strValue); 117 | } else { 118 | return strValue; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/MemorySchema.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import org.apache.calcite.schema.Function; 7 | import org.apache.calcite.schema.ScalarFunction; 8 | import org.apache.calcite.schema.Table; 9 | import org.apache.calcite.schema.impl.AbstractSchema; 10 | import org.apache.calcite.schema.impl.ScalarFunctionImpl; 11 | 12 | import com.liangbo.xing.calcite.MemoryData.Database; 13 | import com.liangbo.xing.calcite.function.TimeOperator; 14 | import com.liangbo.xing.calcite.table.MemoryTable; 15 | import com.google.common.collect.HashMultimap; 16 | import com.google.common.collect.ImmutableMultimap; 17 | import com.google.common.collect.Multimap; 18 | 19 | /** 20 | * 数据源的schema,DataSource的元数据 21 | * 22 | * @author hdfs 23 | */ 24 | public class MemorySchema extends AbstractSchema { 25 | private String dbName; 26 | public MemorySchema(String dbName) { 27 | this.dbName = dbName; 28 | } 29 | 30 | /** 31 | * 通过schema实现从数据源自己的概念(DataBase及Table)向Calcite的概念(MemoryTable)进行转换的过程。 32 | */ 33 | @Override 34 | public Map getTableMap() { 35 | Map tables = new HashMap(); 36 | Database database = MemoryData.MAP.get(this.dbName); 37 | if(database == null) 38 | return tables; 39 | for(MemoryData.Table table : database.tables) { 40 | tables.put(table.tableName, new MemoryTable(table)); 41 | } 42 | return tables; 43 | } 44 | 45 | /** 46 | * 获取操作函数中所有操作函数,将这些函数转换为Calcite中的概念。 47 | */ 48 | protected Multimap getFunctionMultimap() { 49 | ImmutableMultimap funcs = ScalarFunctionImpl.createAll(TimeOperator.class); 50 | Multimap functions = HashMultimap.create(); 51 | for(String key : funcs.keySet()) { 52 | for(ScalarFunction func : funcs.get(key)) { 53 | functions.put(key, func); 54 | } 55 | } 56 | return functions; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/MemorySchemaFactory.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite; 2 | 3 | import org.apache.calcite.schema.Schema; 4 | import org.apache.calcite.schema.SchemaFactory; 5 | import org.apache.calcite.schema.SchemaPlus; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * SchemaFactory , 用于构建数据源的Schema 11 | * 12 | * @author hdfs 13 | */ 14 | public class MemorySchemaFactory implements SchemaFactory { 15 | /** 16 | * 建立Connection的时候,即通过读取定义的元数据文件,获取指定的SchemaFatory 17 | * 然后通过这个SchemaFactory来创建Schema。 18 | */ 19 | public Schema create(SchemaPlus parentSchema, String name, 20 | Map operand) { 21 | System.out.println("param1 : " + operand.get("param1")); 22 | System.out.println("param2 : " + operand.get("param2")); 23 | System.out.println("Get database " + name); 24 | return new MemorySchema(name); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/function/TimeFunction.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite.function; 2 | 3 | import org.apache.calcite.schema.Function; 4 | import org.apache.calcite.schema.FunctionParameter; 5 | 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | 9 | public class TimeFunction implements Function { 10 | 11 | @Override 12 | public List getParameters() { 13 | List params = new LinkedList(); 14 | params.add(new TimeParameter("year", 1)); 15 | return params; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/function/TimeOperator.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite.function; 2 | 3 | import java.sql.Date; 4 | import java.util.Calendar; 5 | 6 | /** 7 | * 自定义函数操作 8 | * 9 | * @author hdfs 10 | */ 11 | public class TimeOperator { 12 | public String YEAR(Date date) { 13 | Calendar cal = Calendar.getInstance(); 14 | cal.setTime(date); 15 | return "sdf"; 16 | } 17 | 18 | /* public int THE_YEAR(int date) { 19 | long mills = (long) date * (1000 * 3600 * 24); 20 | Date dt = Date.valueOf(DateFormat.formatToDateStr(mills)); 21 | Calendar cal = Calendar.getInstance(); 22 | cal.setTime(dt); 23 | return cal.get(Calendar.YEAR); 24 | }*/ 25 | 26 | public Integer THE_MONTH(Date date) { 27 | return 6; 28 | } 29 | 30 | public Integer THE_DAY(Date date) { 31 | return 16; 32 | } 33 | 34 | public int THE_SYEAR(Date date, String year) { 35 | return 18; 36 | } 37 | 38 | public String COM(String str1, String str2) { 39 | return str1 + str2; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/function/TimeParameter.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite.function; 2 | 3 | import org.apache.calcite.rel.type.RelDataType; 4 | import org.apache.calcite.rel.type.RelDataTypeFactory; 5 | import org.apache.calcite.schema.FunctionParameter; 6 | import org.apache.calcite.sql.type.SqlTypeName; 7 | 8 | public class TimeParameter implements FunctionParameter { 9 | private int orinal = 0; 10 | private String name ; 11 | 12 | public TimeParameter(String name, int ordinal) { 13 | this.orinal = ordinal; 14 | this.name = name; 15 | } 16 | 17 | @Override 18 | public int getOrdinal() { 19 | return this.orinal; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return this.name; 25 | } 26 | 27 | @Override 28 | public RelDataType getType(RelDataTypeFactory typeFactory) { 29 | return typeFactory.createSqlType(SqlTypeName.INTEGER); 30 | } 31 | 32 | @Override 33 | public boolean isOptional() { 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/liangbo/xing/calcite/table/MemoryTable.java: -------------------------------------------------------------------------------- 1 | package com.liangbo.xing.calcite.table; 2 | 3 | import com.liangbo.xing.calcite.MemoryData; 4 | import com.liangbo.xing.calcite.MemoryEnumerator; 5 | import org.apache.calcite.DataContext; 6 | import org.apache.calcite.linq4j.AbstractEnumerable; 7 | import org.apache.calcite.linq4j.Enumerable; 8 | import org.apache.calcite.linq4j.Enumerator; 9 | import org.apache.calcite.rel.type.RelDataType; 10 | import org.apache.calcite.rel.type.RelDataTypeFactory; 11 | import org.apache.calcite.schema.ScannableTable; 12 | import org.apache.calcite.schema.impl.AbstractTable; 13 | import org.apache.calcite.sql.type.SqlTypeUtil; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * Calcite中表的抽象,实际上就是实现从原始数据源中的Table概念到Calcite中Table概念之间的转换过程。 20 | * 1、原始数据源列类型转换为Calcite中的类型。 21 | * 2、原始数据源中数据转换为我们所需要的数据 22 | * 23 | * @author hdfs 24 | */ 25 | public class MemoryTable extends AbstractTable implements ScannableTable { 26 | // 自定义的数据表,就是我们所定义的数据源的表 27 | private MemoryData.Table sourceTable; 28 | // 引用数据类型 29 | private RelDataType dataType; 30 | 31 | public MemoryTable(MemoryData.Table table) { 32 | this.sourceTable = table; 33 | dataType = null; 34 | } 35 | 36 | 37 | private static int[] identityList(int n) { 38 | int[] integers = new int[n]; 39 | for (int i = 0; i < n; i++) { 40 | integers[i] = i; 41 | } 42 | return integers; 43 | } 44 | 45 | // 实际上就是对Java 类的封装,将原始数据源中的Column概念(列数据类型等)转换为Calcite中的Column概念(RelDataType) 46 | public RelDataType getRowType(RelDataTypeFactory typeFactory) { 47 | if(dataType == null) { 48 | RelDataTypeFactory.FieldInfoBuilder fieldInfo = typeFactory.builder(); 49 | for (MemoryData.Column column : this.sourceTable.columns) { // 保存每列列名与该列数据类型之间映射关系 50 | RelDataType sqlType = typeFactory.createJavaType( 51 | MemoryData.JAVATYPE_MAPPING.get(column.type)); 52 | sqlType = SqlTypeUtil.addCharsetAndCollation(sqlType, typeFactory); 53 | fieldInfo.add(column.name, sqlType); // 保存每一个列列名与这个列的数据类型之间的映射关系 54 | } 55 | this.dataType = typeFactory.createStructType(fieldInfo); 56 | } 57 | return this.dataType; // 将所有列的数据类型合并为一种数据类型的数据结构 58 | } 59 | 60 | /** 61 | * 调用scan函数构建emurator用于读取数据,使用Enumerator来读取数据。再Enumerator中会实现从原始数据源的数据到实际我们需要的数据之间的转换过程。 62 | */ 63 | public Enumerable scan(DataContext root) { 64 | final List types = new ArrayList(sourceTable.columns.size()); 65 | for(MemoryData.Column column : sourceTable.columns) { 66 | types.add(column.type); 67 | } 68 | final int[] fields = identityList(this.dataType.getFieldCount()); 69 | return new AbstractEnumerable() { 70 | public Enumerator enumerator() { 71 | // 这里的data就是从数据源中查询获得的数据,因此对于其他数据源,数据的接入应该是在这里进行的。 72 | return new MemoryEnumerator(fields, types, sourceTable.data); 73 | } 74 | }; 75 | } 76 | 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/resources/School.json: -------------------------------------------------------------------------------- 1 | { 2 | version: '1.0', 3 | defaultSchema: 'school', 4 | schemas: [ 5 | { 6 | type: 'custom', 7 | name: 'school', 8 | factory: 'com.liangbo.xing.calcite.MemorySchemaFactory', 9 | operand: { 10 | param1: 'hello', 11 | param2: 'world' 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartxing/calcite/912b4ff62f6583c4effea3849ab7a1e569f4c376/src/main/resources/application.properties --------------------------------------------------------------------------------