embulk-filter-calcite 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +3 -0
  5. data/README.md +61 -0
  6. data/build.gradle +112 -0
  7. data/config/checkstyle/checkstyle.xml +128 -0
  8. data/config/checkstyle/default.xml +108 -0
  9. data/gradle/wrapper/gradle-wrapper.jar +0 -0
  10. data/gradle/wrapper/gradle-wrapper.properties +6 -0
  11. data/gradlew +160 -0
  12. data/gradlew.bat +90 -0
  13. data/lib/embulk/filter/calcite.rb +3 -0
  14. data/src/main/java/org/embulk/filter/calcite/CalciteFilterPlugin.java +309 -0
  15. data/src/main/java/org/embulk/filter/calcite/PageConverter.java +119 -0
  16. data/src/main/java/org/embulk/filter/calcite/adapter/page/PageEnumerator.java +56 -0
  17. data/src/main/java/org/embulk/filter/calcite/adapter/page/PageFieldType.java +44 -0
  18. data/src/main/java/org/embulk/filter/calcite/adapter/page/PageSchema.java +26 -0
  19. data/src/main/java/org/embulk/filter/calcite/adapter/page/PageSchemaFactory.java +26 -0
  20. data/src/main/java/org/embulk/filter/calcite/adapter/page/PageTable.java +69 -0
  21. data/src/main/java/org/embulk/filter/calcite/getter/FilterColumnGetterFactory.java +45 -0
  22. data/src/main/java/org/embulk/filter/calcite/getter/UTCTimestampColumnGetter.java +41 -0
  23. data/src/test/java/org/embulk/filter/calcite/TestCalciteFilterPlugin.java +96 -0
  24. data/src/test/resources/org/embulk/filter/calcite/test/test_int_ops_expected.csv +4 -0
  25. data/src/test/resources/org/embulk/filter/calcite/test/test_int_ops_filter.yml +2 -0
  26. data/src/test/resources/org/embulk/filter/calcite/test/test_int_ops_in.yml +18 -0
  27. data/src/test/resources/org/embulk/filter/calcite/test/test_int_ops_source.csv +5 -0
  28. data/src/test/resources/org/embulk/filter/calcite/test/test_simple_expected.csv +4 -0
  29. data/src/test/resources/org/embulk/filter/calcite/test/test_simple_filter.yml +2 -0
  30. data/src/test/resources/org/embulk/filter/calcite/test/test_simple_in.yml +18 -0
  31. data/src/test/resources/org/embulk/filter/calcite/test/test_simple_source.csv +5 -0
  32. data/src/test/resources/org/embulk/filter/calcite/test/test_string_ops_expected.csv +4 -0
  33. data/src/test/resources/org/embulk/filter/calcite/test/test_string_ops_filter.yml +2 -0
  34. data/src/test/resources/org/embulk/filter/calcite/test/test_string_ops_in.yml +18 -0
  35. data/src/test/resources/org/embulk/filter/calcite/test/test_string_ops_source.csv +5 -0
  36. data/src/test/resources/org/embulk/filter/calcite/test/test_where_int_cond_expected.csv +2 -0
  37. data/src/test/resources/org/embulk/filter/calcite/test/test_where_int_cond_filter.yml +2 -0
  38. data/src/test/resources/org/embulk/filter/calcite/test/test_where_int_cond_in.yml +18 -0
  39. data/src/test/resources/org/embulk/filter/calcite/test/test_where_int_cond_source.csv +5 -0
  40. data/src/test/resources/org/embulk/filter/calcite/test/test_where_string_cond_expected.csv +2 -0
  41. data/src/test/resources/org/embulk/filter/calcite/test/test_where_string_cond_filter.yml +2 -0
  42. data/src/test/resources/org/embulk/filter/calcite/test/test_where_string_cond_in.yml +18 -0
  43. data/src/test/resources/org/embulk/filter/calcite/test/test_where_string_cond_source.csv +5 -0
  44. metadata +137 -0
@@ -0,0 +1,6 @@
1
+ #Wed Nov 02 10:15:26 ICT 2016
2
+ distributionBase=GRADLE_USER_HOME
3
+ distributionPath=wrapper/dists
4
+ zipStoreBase=GRADLE_USER_HOME
5
+ zipStorePath=wrapper/dists
6
+ distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
data/gradlew ADDED
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ##############################################################################
4
+ ##
5
+ ## Gradle start up script for UN*X
6
+ ##
7
+ ##############################################################################
8
+
9
+ # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10
+ DEFAULT_JVM_OPTS=""
11
+
12
+ APP_NAME="Gradle"
13
+ APP_BASE_NAME=`basename "$0"`
14
+
15
+ # Use the maximum available, or set MAX_FD != -1 to use that value.
16
+ MAX_FD="maximum"
17
+
18
+ warn ( ) {
19
+ echo "$*"
20
+ }
21
+
22
+ die ( ) {
23
+ echo
24
+ echo "$*"
25
+ echo
26
+ exit 1
27
+ }
28
+
29
+ # OS specific support (must be 'true' or 'false').
30
+ cygwin=false
31
+ msys=false
32
+ darwin=false
33
+ case "`uname`" in
34
+ CYGWIN* )
35
+ cygwin=true
36
+ ;;
37
+ Darwin* )
38
+ darwin=true
39
+ ;;
40
+ MINGW* )
41
+ msys=true
42
+ ;;
43
+ esac
44
+
45
+ # Attempt to set APP_HOME
46
+ # Resolve links: $0 may be a link
47
+ PRG="$0"
48
+ # Need this for relative symlinks.
49
+ while [ -h "$PRG" ] ; do
50
+ ls=`ls -ld "$PRG"`
51
+ link=`expr "$ls" : '.*-> \(.*\)$'`
52
+ if expr "$link" : '/.*' > /dev/null; then
53
+ PRG="$link"
54
+ else
55
+ PRG=`dirname "$PRG"`"/$link"
56
+ fi
57
+ done
58
+ SAVED="`pwd`"
59
+ cd "`dirname \"$PRG\"`/" >/dev/null
60
+ APP_HOME="`pwd -P`"
61
+ cd "$SAVED" >/dev/null
62
+
63
+ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64
+
65
+ # Determine the Java command to use to start the JVM.
66
+ if [ -n "$JAVA_HOME" ] ; then
67
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68
+ # IBM's JDK on AIX uses strange locations for the executables
69
+ JAVACMD="$JAVA_HOME/jre/sh/java"
70
+ else
71
+ JAVACMD="$JAVA_HOME/bin/java"
72
+ fi
73
+ if [ ! -x "$JAVACMD" ] ; then
74
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75
+
76
+ Please set the JAVA_HOME variable in your environment to match the
77
+ location of your Java installation."
78
+ fi
79
+ else
80
+ JAVACMD="java"
81
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82
+
83
+ Please set the JAVA_HOME variable in your environment to match the
84
+ location of your Java installation."
85
+ fi
86
+
87
+ # Increase the maximum file descriptors if we can.
88
+ if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89
+ MAX_FD_LIMIT=`ulimit -H -n`
90
+ if [ $? -eq 0 ] ; then
91
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92
+ MAX_FD="$MAX_FD_LIMIT"
93
+ fi
94
+ ulimit -n $MAX_FD
95
+ if [ $? -ne 0 ] ; then
96
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
97
+ fi
98
+ else
99
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100
+ fi
101
+ fi
102
+
103
+ # For Darwin, add options to specify how the application appears in the dock
104
+ if $darwin; then
105
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106
+ fi
107
+
108
+ # For Cygwin, switch paths to Windows format before running java
109
+ if $cygwin ; then
110
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112
+ JAVACMD=`cygpath --unix "$JAVACMD"`
113
+
114
+ # We build the pattern for arguments to be converted via cygpath
115
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116
+ SEP=""
117
+ for dir in $ROOTDIRSRAW ; do
118
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
119
+ SEP="|"
120
+ done
121
+ OURCYGPATTERN="(^($ROOTDIRS))"
122
+ # Add a user-defined pattern to the cygpath arguments
123
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125
+ fi
126
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
127
+ i=0
128
+ for arg in "$@" ; do
129
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131
+
132
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134
+ else
135
+ eval `echo args$i`="\"$arg\""
136
+ fi
137
+ i=$((i+1))
138
+ done
139
+ case $i in
140
+ (0) set -- ;;
141
+ (1) set -- "$args0" ;;
142
+ (2) set -- "$args0" "$args1" ;;
143
+ (3) set -- "$args0" "$args1" "$args2" ;;
144
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150
+ esac
151
+ fi
152
+
153
+ # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154
+ function splitJvmOpts() {
155
+ JVM_OPTS=("$@")
156
+ }
157
+ eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158
+ JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159
+
160
+ exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@@ -0,0 +1,90 @@
1
+ @if "%DEBUG%" == "" @echo off
2
+ @rem ##########################################################################
3
+ @rem
4
+ @rem Gradle startup script for Windows
5
+ @rem
6
+ @rem ##########################################################################
7
+
8
+ @rem Set local scope for the variables with windows NT shell
9
+ if "%OS%"=="Windows_NT" setlocal
10
+
11
+ @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12
+ set DEFAULT_JVM_OPTS=
13
+
14
+ set DIRNAME=%~dp0
15
+ if "%DIRNAME%" == "" set DIRNAME=.
16
+ set APP_BASE_NAME=%~n0
17
+ set APP_HOME=%DIRNAME%
18
+
19
+ @rem Find java.exe
20
+ if defined JAVA_HOME goto findJavaFromJavaHome
21
+
22
+ set JAVA_EXE=java.exe
23
+ %JAVA_EXE% -version >NUL 2>&1
24
+ if "%ERRORLEVEL%" == "0" goto init
25
+
26
+ echo.
27
+ echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28
+ echo.
29
+ echo Please set the JAVA_HOME variable in your environment to match the
30
+ echo location of your Java installation.
31
+
32
+ goto fail
33
+
34
+ :findJavaFromJavaHome
35
+ set JAVA_HOME=%JAVA_HOME:"=%
36
+ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37
+
38
+ if exist "%JAVA_EXE%" goto init
39
+
40
+ echo.
41
+ echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42
+ echo.
43
+ echo Please set the JAVA_HOME variable in your environment to match the
44
+ echo location of your Java installation.
45
+
46
+ goto fail
47
+
48
+ :init
49
+ @rem Get command-line arguments, handling Windowz variants
50
+
51
+ if not "%OS%" == "Windows_NT" goto win9xME_args
52
+ if "%@eval[2+2]" == "4" goto 4NT_args
53
+
54
+ :win9xME_args
55
+ @rem Slurp the command line arguments.
56
+ set CMD_LINE_ARGS=
57
+ set _SKIP=2
58
+
59
+ :win9xME_args_slurp
60
+ if "x%~1" == "x" goto execute
61
+
62
+ set CMD_LINE_ARGS=%*
63
+ goto execute
64
+
65
+ :4NT_args
66
+ @rem Get arguments from the 4NT Shell from JP Software
67
+ set CMD_LINE_ARGS=%$
68
+
69
+ :execute
70
+ @rem Setup the command line
71
+
72
+ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73
+
74
+ @rem Execute Gradle
75
+ "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76
+
77
+ :end
78
+ @rem End local scope for the variables with windows NT shell
79
+ if "%ERRORLEVEL%"=="0" goto mainEnd
80
+
81
+ :fail
82
+ rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83
+ rem the _cmd.exe /c_ return code!
84
+ if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85
+ exit /b 1
86
+
87
+ :mainEnd
88
+ if "%OS%"=="Windows_NT" endlocal
89
+
90
+ :omega
@@ -0,0 +1,3 @@
1
+ Embulk::JavaPlugin.register_filter(
2
+ "calcite", "org.embulk.filter.calcite.CalciteFilterPlugin",
3
+ File.expand_path('../../../../classpath', __FILE__))
@@ -0,0 +1,309 @@
1
+ package org.embulk.filter.calcite;
2
+
3
+ import com.google.common.base.Optional;
4
+ import com.google.common.base.Throwables;
5
+ import com.google.common.collect.ImmutableList;
6
+ import com.google.common.collect.ImmutableMap;
7
+ import com.google.inject.Inject;
8
+ import org.apache.calcite.jdbc.Driver;
9
+ import org.embulk.config.Config;
10
+ import org.embulk.config.ConfigDefault;
11
+ import org.embulk.config.ConfigException;
12
+ import org.embulk.config.ConfigInject;
13
+ import org.embulk.config.ConfigSource;
14
+ import org.embulk.config.Task;
15
+ import org.embulk.config.TaskSource;
16
+ import org.embulk.filter.calcite.adapter.page.PageSchema;
17
+ import org.embulk.filter.calcite.adapter.page.PageSchemaFactory;
18
+ import org.embulk.filter.calcite.adapter.page.PageTable;
19
+ import org.embulk.filter.calcite.getter.FilterColumnGetterFactory;
20
+ import org.embulk.input.jdbc.JdbcColumn;
21
+ import org.embulk.input.jdbc.JdbcColumnOption;
22
+ import org.embulk.input.jdbc.JdbcSchema;
23
+ import org.embulk.input.jdbc.getter.ColumnGetter;
24
+ import org.embulk.input.jdbc.getter.ColumnGetterFactory;
25
+ import org.embulk.spi.BufferAllocator;
26
+ import org.embulk.spi.FilterPlugin;
27
+ import org.embulk.spi.Page;
28
+ import org.embulk.spi.PageBuilder;
29
+ import org.embulk.spi.PageOutput;
30
+ import org.embulk.spi.Schema;
31
+ import org.joda.time.DateTimeZone;
32
+ import org.slf4j.Logger;
33
+
34
+ import java.sql.Connection;
35
+ import java.sql.ResultSet;
36
+ import java.sql.ResultSetMetaData;
37
+ import java.sql.SQLException;
38
+ import java.sql.Statement;
39
+ import java.util.List;
40
+ import java.util.Map;
41
+ import java.util.Properties;
42
+
43
+ import static java.lang.String.format;
44
+ import static java.lang.Thread.currentThread;
45
+ import static java.util.Locale.ENGLISH;
46
+ import static org.embulk.spi.Exec.getLogger;
47
+ import static org.embulk.spi.Exec.getModelManager;
48
+ import static org.embulk.spi.Exec.newConfigSource;
49
+
50
+ public class CalciteFilterPlugin
51
+ implements FilterPlugin
52
+ {
53
+ public interface PluginTask
54
+ extends Task
55
+ {
56
+ @Config("query")
57
+ public String getQuery();
58
+
59
+ @Config("default_timezone")
60
+ @ConfigDefault("\"UTC\"")
61
+ public DateTimeZone getDefaultTimeZone();
62
+
63
+ public JdbcSchema getQuerySchema();
64
+ public void setQuerySchema(JdbcSchema querySchema);
65
+
66
+ // TODO support jdbc Url properties
67
+ // TODO support column_options: option
68
+ // TODO support options: option
69
+
70
+ @ConfigInject
71
+ public BufferAllocator getBufferAllocator();
72
+ }
73
+
74
+ private final Logger log;
75
+
76
+ @Inject
77
+ public CalciteFilterPlugin()
78
+ {
79
+ this.log = getLogger(getClass());
80
+ }
81
+
82
+ @Override
83
+ public void transaction(ConfigSource config, Schema inputSchema, FilterPlugin.Control control)
84
+ {
85
+ PluginTask task = config.loadConfig(PluginTask.class);
86
+ Properties props = System.getProperties(); // TODO should be configured as config option
87
+
88
+ // Set input schema in PageSchema
89
+ PageSchema.schema = inputSchema;
90
+
91
+ // Set page converter as TLS variable in PageTable
92
+ PageTable.pageConverter.set(newPageConverter(task, inputSchema));
93
+ try {
94
+ JdbcSchema querySchema;
95
+ try (Connection conn = newConnection(props)) { // SQLException thrown by conn.close()
96
+ querySchema = getQuerySchema(task, conn);
97
+ task.setQuerySchema(querySchema);
98
+ }
99
+ catch (SQLException e) {
100
+ throw Throwables.propagate(e);
101
+ }
102
+
103
+ control.run(task.dump(), buildOutputSchema(task, querySchema));
104
+ }
105
+ finally {
106
+ PageTable.pageConverter.remove();
107
+ }
108
+ }
109
+
110
+ private PageConverter newPageConverter(PluginTask task, Schema inputSchema)
111
+ {
112
+ return new PageConverter(inputSchema, task.getDefaultTimeZone().toTimeZone());
113
+ }
114
+
115
+ private Connection newConnection(Properties props)
116
+ {
117
+ String jdbcUrl = buildJdbcUrl();
118
+ try {
119
+ // Relax case-sensitive
120
+ // @see https://calcite.apache.org/docs/adapter.html#jdbc-connect-string-parameters
121
+ props.setProperty("caseSensitive", "false");
122
+ return new Driver().connect(jdbcUrl, props);
123
+ }
124
+ catch (SQLException e) {
125
+ String message = format(ENGLISH, "Cannot create connections by Jdbc URL: %s", jdbcUrl);
126
+ throw new IllegalStateException(message, e);
127
+ }
128
+ }
129
+
130
+ private String buildJdbcUrl()
131
+ {
132
+ // build a json model to apply Page storage adaptor
133
+ // @see https://github.com/apache/calcite/blob/master/example/csv/src/test/resources/model.json
134
+ ImmutableMap.Builder<String, Object> map = ImmutableMap.builder();
135
+ map.put("version", "1.0");
136
+ map.put("defaultSchema", "page");
137
+ map.put("schemas", ImmutableList.<Map<String, String>>of(
138
+ ImmutableMap.of(
139
+ "name", "page",
140
+ "type", "custom",
141
+ "factory", PageSchemaFactory.class.getName()
142
+ )
143
+ ));
144
+ String jsonModel = getModelManager().writeObject(map.build());
145
+
146
+ // build Jdbc URL
147
+ String jdbcUrl = format(ENGLISH, "jdbc:calcite:model=inline:%s", jsonModel);
148
+ log.info(format(ENGLISH, "Generated Jdbc URL: %s", jdbcUrl));
149
+ return jdbcUrl;
150
+ }
151
+
152
+ private JdbcSchema getQuerySchema(PluginTask task, Connection conn)
153
+ throws SQLException
154
+ {
155
+ try (Statement stat = conn.createStatement(); // SQLException thrown by conn.close()
156
+ ResultSet result = executeQuery(stat, task.getQuery())) { // SQLException thrown by rs.close()
157
+ return getQuerySchema(result.getMetaData());
158
+ }
159
+ }
160
+
161
+ private ResultSet executeQuery(Statement stat, String query)
162
+ {
163
+ // This is a workaround to avoid NPE caused by commons-compiler v2.7.6
164
+ ClassLoader cl = currentThread().getContextClassLoader();
165
+ currentThread().setContextClassLoader(getClass().getClassLoader());
166
+ try {
167
+ return stat.executeQuery(query);
168
+ }
169
+ catch (SQLException e) {
170
+ throw new ConfigException("Cannot execute a query: " + query, e);
171
+ }
172
+ finally {
173
+ currentThread().setContextClassLoader(cl);
174
+ }
175
+ }
176
+
177
+ private JdbcSchema getQuerySchema(ResultSetMetaData metadata)
178
+ throws SQLException
179
+ {
180
+ ImmutableList.Builder<JdbcColumn> columns = ImmutableList.builder();
181
+ for (int i = 0; i < metadata.getColumnCount(); i++) {
182
+ int index = i + 1; // JDBC column index begins from 1
183
+ columns.add(new JdbcColumn(
184
+ metadata.getColumnLabel(index),
185
+ metadata.getColumnTypeName(index),
186
+ metadata.getColumnType(index),
187
+ metadata.getPrecision(index),
188
+ metadata.getScale(index)));
189
+ }
190
+ return new JdbcSchema(columns.build());
191
+ }
192
+
193
+ private Schema buildOutputSchema(PluginTask task, JdbcSchema querySchema)
194
+ {
195
+ ColumnGetterFactory factory = newColumnGetterFactory(task, Optional.<PageBuilder>absent());
196
+ List<ColumnGetter> getters = newColumnGetters(factory, querySchema);
197
+
198
+ Schema.Builder schema = Schema.builder();
199
+ for (int i = 0; i < querySchema.getColumns().size(); i++) {
200
+ schema.add(querySchema.getColumn(i).getName(), getters.get(i).getToType());
201
+ }
202
+ return schema.build();
203
+ }
204
+
205
+ private ColumnGetterFactory newColumnGetterFactory(PluginTask task, Optional<PageBuilder> pageBuilder)
206
+ {
207
+ if (pageBuilder.isPresent()) {
208
+ return new FilterColumnGetterFactory(pageBuilder.get(), task.getDefaultTimeZone());
209
+ }
210
+ else {
211
+ return new FilterColumnGetterFactory(null, task.getDefaultTimeZone());
212
+ }
213
+ }
214
+
215
+ private List<ColumnGetter> newColumnGetters(ColumnGetterFactory factory, JdbcSchema querySchema)
216
+ {
217
+ ImmutableList.Builder<ColumnGetter> getters = ImmutableList.builder();
218
+ for (JdbcColumn column : querySchema.getColumns()) {
219
+ getters.add(factory.newColumnGetter(null, null, column, newJdbcColumnOption()));
220
+ }
221
+ return getters.build();
222
+ }
223
+
224
+ private JdbcColumnOption newJdbcColumnOption()
225
+ {
226
+ // TODO need to improve for supporting column_options: option
227
+ return newConfigSource().loadConfig(JdbcColumnOption.class);
228
+ }
229
+
230
+ @Override
231
+ public PageOutput open(TaskSource taskSource, Schema inputSchema, Schema outputSchema, PageOutput output)
232
+ {
233
+ PluginTask task = taskSource.loadTask(PluginTask.class);
234
+
235
+ // Set input schema in PageSchema for various types of executor plugins
236
+ PageSchema.schema = inputSchema;
237
+
238
+ PageBuilder pageBuilder = new PageBuilder(task.getBufferAllocator(), outputSchema, output);
239
+ PageConverter pageConverter = newPageConverter(task, inputSchema);
240
+ ColumnGetterFactory factory = newColumnGetterFactory(task, Optional.of(pageBuilder));
241
+ List<ColumnGetter> getters = newColumnGetters(factory, task.getQuerySchema());
242
+ Properties props = System.getProperties(); // TODO should be configured as config option
243
+ return new FilterPageOutput(outputSchema, task.getQuery(), pageBuilder, pageConverter, getters, props);
244
+ }
245
+
246
+ private class FilterPageOutput
247
+ implements PageOutput
248
+ {
249
+ private final Schema outputSchema;
250
+ private final String query;
251
+ private final PageBuilder pageBuilder;
252
+ private final PageConverter pageConverter;
253
+ private final List<ColumnGetter> getters;
254
+ private final Properties props;
255
+
256
+ private FilterPageOutput(Schema outputSchema, String query, PageBuilder pageBuilder, PageConverter pageConverter,
257
+ List<ColumnGetter> getters, Properties props)
258
+ {
259
+ this.outputSchema = outputSchema;
260
+ this.query = query;
261
+ this.pageBuilder = pageBuilder;
262
+ this.pageConverter = pageConverter;
263
+ this.getters = getters;
264
+ this.props = props;
265
+ }
266
+
267
+ @Override
268
+ public void add(Page page)
269
+ {
270
+ // Set page converter as TLS variable in PageTable
271
+ PageTable.pageConverter.set(pageConverter);
272
+
273
+ // Set page as TLS variable in PageTable
274
+ PageTable.page.set(page);
275
+
276
+ try (Connection conn = newConnection(props);
277
+ Statement stat = conn.createStatement();
278
+ ResultSet result = executeQuery(stat, query)) {
279
+
280
+ while (result.next()) {
281
+ for (int i = 0; i < getters.size(); i++) {
282
+ int index = i + 1; // JDBC column index begins from 1
283
+ getters.get(i).getAndSet(result, index, outputSchema.getColumn(i));
284
+ }
285
+ pageBuilder.addRecord();
286
+ }
287
+ }
288
+ catch (SQLException e) {
289
+ throw Throwables.propagate(e); // TODO better exception handling? error messages?
290
+ }
291
+ finally {
292
+ PageTable.pageConverter.remove();
293
+ PageTable.page.remove();
294
+ }
295
+ }
296
+
297
+ @Override
298
+ public void finish()
299
+ {
300
+ pageBuilder.finish();
301
+ }
302
+
303
+ @Override
304
+ public void close()
305
+ {
306
+ pageBuilder.close();
307
+ }
308
+ }
309
+ }