You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/03/23 18:26:18 UTC
[4/9] incubator-calcite git commit: [CALCITE-618] Add Avatica support
for getTables (Julian Hyde and Nick Dimiduk)
[CALCITE-618] Add Avatica support for getTables (Julian Hyde and Nick Dimiduk)
Also add getSchemas.
Remove dependencies on Guava. (Avatica depends only on JDK and Jackson.)
Add JdbcMeta; implement remote execute query
Rename some fields of AvaticaType to prevent clash between JSON "type" and type field.
Add FetchRequest; if FetchRequest has parameters, executes first.
Document Avatica
Move class ConnectionSpec to top-level
Use scott-data-hsqldb rather than foodmart-data-hsqldb for testing Avatica; it is smaller and therefore loads faster
Work around JSON's mangling of small longs into ints
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/4d2f647a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/4d2f647a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/4d2f647a
Branch: refs/heads/master
Commit: 4d2f647a6d397a4a7c99fcc2d3d6359baf937943
Parents: 2b07a9e
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Mar 12 14:51:14 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Mar 23 01:44:36 2015 -0700
----------------------------------------------------------------------
avatica/pom.xml | 11 +
.../calcite/avatica/AvaticaConnection.java | 30 +-
.../apache/calcite/avatica/AvaticaFactory.java | 11 +-
.../calcite/avatica/AvaticaJdbc41Factory.java | 4 +-
.../avatica/AvaticaPreparedStatement.java | 12 +-
.../calcite/avatica/AvaticaResultSet.java | 12 +-
.../avatica/AvaticaResultSetMetaData.java | 4 +-
.../calcite/avatica/AvaticaStatement.java | 33 +-
.../apache/calcite/avatica/ColumnMetaData.java | 46 +-
.../java/org/apache/calcite/avatica/Meta.java | 90 +++-
.../org/apache/calcite/avatica/MetaImpl.java | 131 ++++--
.../calcite/avatica/UnregisteredDriver.java | 2 +-
.../apache/calcite/avatica/jdbc/JdbcMeta.java | 442 +++++++++++++++++++
.../calcite/avatica/jdbc/JdbcResultSet.java | 105 +++++
.../calcite/avatica/jdbc/package-info.java | 22 +
.../calcite/avatica/remote/JsonService.java | 23 +-
.../calcite/avatica/remote/LocalService.java | 78 +++-
.../calcite/avatica/remote/MockJsonService.java | 44 +-
.../calcite/avatica/remote/RemoteMeta.java | 45 +-
.../apache/calcite/avatica/remote/Service.java | 75 +++-
.../calcite/avatica/util/AbstractCursor.java | 75 +++-
.../apache/calcite/avatica/util/ArrayImpl.java | 8 +-
.../calcite/avatica/test/RemoteDriverTest.java | 216 ++++++++-
core/pom.xml | 5 +
.../calcite/jdbc/CalciteJdbc41Factory.java | 4 +-
.../apache/calcite/jdbc/CalciteMetaImpl.java | 12 +-
.../apache/calcite/jdbc/CalciteResultSet.java | 7 +-
.../materialize/MaterializationService.java | 2 +-
.../org/apache/calcite/test/CalciteAssert.java | 47 +-
.../org/apache/calcite/test/ConnectionSpec.java | 37 ++
.../apache/calcite/test/JdbcAdapterTest.java | 6 +-
.../java/org/apache/calcite/test/JdbcTest.java | 25 +-
doc/avatica.md | 81 ++++
pom.xml | 37 +-
34 files changed, 1566 insertions(+), 216 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/pom.xml
----------------------------------------------------------------------
diff --git a/avatica/pom.xml b/avatica/pom.xml
index 58aec3c..3e6040c 100644
--- a/avatica/pom.xml
+++ b/avatica/pom.xml
@@ -45,6 +45,17 @@ limitations under the License.
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>net.hydromatic</groupId>
+ <artifactId>scott-data-hsqldb</artifactId>
+ <version>0.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hsqldb</groupId>
+ <artifactId>hsqldb</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
index 492ec5f..bc8c3eb 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaConnection.java
@@ -269,7 +269,17 @@ public abstract class AvaticaConnection implements Connection {
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
- throw new UnsupportedOperationException(); // TODO:
+ try {
+ // TODO: cut out round-trip to create a statement handle
+ final Meta.ConnectionHandle ch = new Meta.ConnectionHandle(id);
+ final Meta.StatementHandle h = meta.createStatement(ch);
+
+ final Meta.Signature x = meta.prepare(h, sql, -1);
+ return factory.newPreparedStatement(this, h, x, resultSetType,
+ resultSetConcurrency, resultSetHoldability);
+ } catch (RuntimeException e) {
+ throw helper.createException("while preparing SQL: " + sql, e);
+ }
}
public CallableStatement prepareCall(
@@ -391,12 +401,12 @@ public abstract class AvaticaConnection implements Connection {
*
* @param statement Statement
* @param signature Prepared query
- * @param iterable List of rows, or null if we need to execute
+ * @param firstFrame First frame of rows, or null if we need to execute
* @return Result set
* @throws java.sql.SQLException if a database error occurs
*/
protected ResultSet executeQueryInternal(AvaticaStatement statement,
- Meta.Signature signature, Iterable<Object> iterable) throws SQLException {
+ Meta.Signature signature, Meta.Frame firstFrame) throws SQLException {
// Close the previous open result set, if there is one.
synchronized (statement) {
if (statement.openResultSet != null) {
@@ -412,7 +422,7 @@ public abstract class AvaticaConnection implements Connection {
final TimeZone timeZone = getTimeZone();
statement.openResultSet =
- factory.newResultSet(statement, signature, timeZone, iterable);
+ factory.newResultSet(statement, signature, timeZone, firstFrame);
}
// Release the monitor before executing, to give another thread the
// opportunity to call cancel.
@@ -447,11 +457,12 @@ public abstract class AvaticaConnection implements Connection {
}
}
- public void assign(Meta.Signature signature,
- Iterable<Object> iterable) throws SQLException {
+ public void assign(Meta.Signature signature, Meta.Frame firstFrame)
+ throws SQLException {
final TimeZone timeZone = getTimeZone();
statement.openResultSet =
- factory.newResultSet(statement, signature, timeZone, iterable);
+ factory.newResultSet(statement, signature, timeZone,
+ firstFrame);
}
public void execute() throws SQLException {
@@ -467,9 +478,8 @@ public abstract class AvaticaConnection implements Connection {
final Meta.StatementHandle h =
new Meta.StatementHandle(metaResultSet.statementId);
final AvaticaStatement statement = lookupStatement(h);
- return executeQueryInternal(statement,
- metaResultSet.signature,
- metaResultSet.iterable);
+ return executeQueryInternal(statement, metaResultSet.signature.sanitize(),
+ metaResultSet.firstFrame);
}
/** Creates a statement wrapper around an existing handle. */
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaFactory.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaFactory.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaFactory.java
index 0139ef3..c6d2ede 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaFactory.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaFactory.java
@@ -50,20 +50,15 @@ public interface AvaticaFactory {
* Creates a result set. You will then need to call
* {@link AvaticaResultSet#execute()} on it.
*
- * <p>If {@code signature} implements
- * {@link org.apache.calcite.avatica.MetaImpl.WithIterable} we do not need
- * to execute; we can use pre-canned data. This mechanism is used for
- * metadata requests such as {@code getTables}.
- *
* @param statement Statement
* @param signature Prepared statement
* @param timeZone Time zone
- * @param iterable Iterable over rows in result, or null if an execute/fetch
- * is required
+ * @param firstFrame Frame containing the first (or perhaps only) rows in the
+ * result, or null if an execute/fetch is required
* @return Result set
*/
AvaticaResultSet newResultSet(AvaticaStatement statement,
- Meta.Signature signature, TimeZone timeZone, Iterable<Object> iterable)
+ Meta.Signature signature, TimeZone timeZone, Meta.Frame firstFrame)
throws SQLException;
/**
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
index 4b2f721..6a70a26 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaJdbc41Factory.java
@@ -84,11 +84,11 @@ class AvaticaJdbc41Factory implements AvaticaFactory {
}
public AvaticaResultSet newResultSet(AvaticaStatement statement,
- Meta.Signature signature, TimeZone timeZone, Iterable<Object> iterable) {
+ Meta.Signature signature, TimeZone timeZone, Meta.Frame firstFrame) {
final ResultSetMetaData metaData =
newResultSetMetaData(statement, signature);
return new AvaticaResultSet(statement, signature, metaData, timeZone,
- iterable);
+ firstFrame);
}
public AvaticaResultSetMetaData newResultSetMetaData(
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
index a40e8d1..9324db7 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaPreparedStatement.java
@@ -32,7 +32,7 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
@@ -76,11 +76,7 @@ public abstract class AvaticaPreparedStatement
}
@Override protected List<Object> getParameterValues() {
- final List<Object> list = new ArrayList<Object>();
- for (Object o : slots) {
- list.add(o == AvaticaParameter.DUMMY_VALUE ? null : o);
- }
- return list;
+ return Arrays.asList(slots);
}
// implement PreparedStatement
@@ -174,7 +170,9 @@ public abstract class AvaticaPreparedStatement
}
public void clearParameters() throws SQLException {
- throw new UnsupportedOperationException();
+ for (int i = 0; i < slots.length; i++) {
+ slots[i] = null;
+ }
}
public void setObject(int parameterIndex, Object x, int targetSqlType)
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
index 8d2fbee..fca227f 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java
@@ -49,7 +49,7 @@ import java.util.TimeZone;
public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory {
protected final AvaticaStatement statement;
protected final Meta.Signature signature;
- protected final Iterable<Object> iterable;
+ protected final Meta.Frame firstFrame;
protected final List<ColumnMetaData> columnMetaDataList;
protected final ResultSetMetaData resultSetMetaData;
protected final Calendar localCalendar;
@@ -71,10 +71,10 @@ public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory {
Meta.Signature signature,
ResultSetMetaData resultSetMetaData,
TimeZone timeZone,
- Iterable<Object> iterable) {
+ Meta.Frame firstFrame) {
this.statement = statement;
this.signature = signature;
- this.iterable = iterable;
+ this.firstFrame = firstFrame;
this.columnMetaDataList = signature.columns;
this.type = statement.resultSetType;
this.concurrency = statement.resultSetConcurrency;
@@ -179,9 +179,11 @@ public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory {
* @throws SQLException if execute fails for some reason.
*/
protected AvaticaResultSet execute() throws SQLException {
- this.cursor = MetaImpl.createCursor(signature.cursorFactory,
+ final List<Object> parameterValues = statement.getBoundParameterValues();
+ final Iterable<Object> iterable1 =
statement.connection.meta.createIterable(statement.handle, signature,
- iterable));
+ parameterValues, firstFrame);
+ this.cursor = MetaImpl.createCursor(signature.cursorFactory, iterable1);
this.accessorList =
cursor.createAccessors(columnMetaDataList, localCalendar, this);
this.row = -1;
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
index cb67a89..b4e8892 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java
@@ -104,11 +104,11 @@ public class AvaticaResultSetMetaData implements ResultSetMetaData {
}
public int getColumnType(int column) throws SQLException {
- return getColumnMetaData(column).type.type;
+ return getColumnMetaData(column).type.id;
}
public String getColumnTypeName(int column) throws SQLException {
- return getColumnMetaData(column).type.typeName;
+ return getColumnMetaData(column).type.name;
}
public boolean isReadOnly(int column) throws SQLException {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
index fb0a6f5..6df3528 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java
@@ -20,6 +20,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -53,7 +54,7 @@ public abstract class AvaticaStatement
final int resultSetHoldability;
private int fetchSize;
private int fetchDirection;
- protected int maxRowCount;
+ protected int maxRowCount = 0;
/**
* Creates an AvaticaStatement.
@@ -98,17 +99,6 @@ public abstract class AvaticaStatement
}
}
- public ResultSet executeQueryOld(String sql) throws SQLException {
- try {
- final int maxRowCount1 = maxRowCount <= 0 ? -1 : maxRowCount;
- Meta.Signature x = connection.meta.prepare(handle, sql, maxRowCount1);
- return executeQueryInternal(x);
- } catch (RuntimeException e) {
- throw connection.helper.createException(
- "error while executing SQL \"" + sql + "\": " + e.getMessage(), e);
- }
- }
-
public ResultSet executeQuery(String sql) throws SQLException {
try {
// In JDBC, maxRowCount = 0 means no limit; in prepare it means LIMIT 0
@@ -417,6 +407,25 @@ public abstract class AvaticaStatement
protected List<Object> getParameterValues() {
return Collections.emptyList();
}
+
+ /** Returns a list of bound parameter values.
+ *
+ * <p>If any of the parameters have not been bound, throws.
+ * If parameters have been bound to null, the value in the list is null.
+ */
+ protected List<Object> getBoundParameterValues() throws SQLException {
+ final List<Object> list = new ArrayList<>();
+ for (Object parameterValue : getParameterValues()) {
+ if (parameterValue == null) {
+ throw new SQLException("unbound parameter");
+ }
+ if (parameterValue == AvaticaParameter.DUMMY_VALUE) {
+ parameterValue = null;
+ }
+ list.add(parameterValue);
+ }
+ return list;
+ }
}
// End AvaticaStatement.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java b/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
index 0b6856d..8e9930f 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java
@@ -160,6 +160,13 @@ public class ColumnMetaData {
type.columnClassName());
}
+ public ColumnMetaData setTypeId(int typeId) {
+ return new ColumnMetaData(ordinal, autoIncrement, caseSensitive, searchable,
+ currency, nullable, signed, displaySize, label, columnName, schemaName,
+ precision, scale, tableName, catalogName, type.setId(typeId), readOnly,
+ writable, definitelyWritable, columnClassName);
+ }
+
/** Description of the type used to internally represent a value. For example,
* a {@link java.sql.Date} might be represented as a {@link #PRIMITIVE_INT}
* if not nullable, or a {@link #JAVA_SQL_DATE}. */
@@ -192,7 +199,7 @@ public class ColumnMetaData {
public static final Map<Class, Rep> VALUE_MAP;
static {
- Map<Class, Rep> builder = new HashMap<Class, Rep>();
+ Map<Class, Rep> builder = new HashMap<>();
for (Rep rep : values()) {
builder.put(rep.clazz, rep);
}
@@ -220,31 +227,39 @@ public class ColumnMetaData {
@JsonSubTypes.Type(value = StructType.class, name = "struct"),
@JsonSubTypes.Type(value = ArrayType.class, name = "array") })
public static class AvaticaType {
- public final int type;
- public final String typeName;
+ public final int id;
+ public final String name;
/** The type of the field that holds the value. Not a JDBC property. */
- public final Rep representation;
+ public final Rep rep;
- protected AvaticaType(int type, String typeName, Rep representation) {
- this.type = type;
- this.typeName = typeName;
- this.representation = representation;
- assert representation != null;
+ protected AvaticaType(int id, String name, Rep rep) {
+ this.id = id;
+ this.name = name;
+ this.rep = rep;
+ assert rep != null;
}
public String columnClassName() {
- return SqlType.valueOf(type).clazz.getName();
+ return SqlType.valueOf(id).clazz.getName();
+ }
+
+ public AvaticaType setId(int rep) {
+ throw new UnsupportedOperationException();
}
}
/** Scalar type. */
public static class ScalarType extends AvaticaType {
@JsonCreator
- public ScalarType(@JsonProperty("type") int type,
- @JsonProperty("typeName") String typeName,
- @JsonProperty("representation") Rep representation) {
- super(type, typeName, representation);
+ public ScalarType(@JsonProperty("id") int id,
+ @JsonProperty("name") String name,
+ @JsonProperty("rep") Rep rep) {
+ super(id, name, rep);
+ }
+
+ @Override public AvaticaType setId(int id) {
+ return new ScalarType(id, name, rep);
}
}
@@ -306,8 +321,7 @@ public class ColumnMetaData {
private final int type;
private final Class clazz;
- private static final Map<Integer, SqlType> BY_ID =
- new HashMap<Integer, SqlType>();
+ private static final Map<Integer, SqlType> BY_ID = new HashMap<>();
static {
for (SqlType sqlType : values()) {
BY_ID.put(sqlType.type, sqlType);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java b/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
index 107c4c9..f7f3b80 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/Meta.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Command handler for getting various metadata. Should be implemented by each
@@ -148,7 +149,7 @@ public interface Meta {
* requires to be not null; derived classes may instead choose to execute the
* relational expression in {@code signature}. */
Iterable<Object> createIterable(StatementHandle handle, Signature signature,
- Iterable<Object> iterable);
+ List<Object> parameterValues, Frame firstFrame);
/** Prepares a statement.
*
@@ -170,6 +171,25 @@ public interface Meta {
MetaResultSet prepareAndExecute(StatementHandle h, String sql,
int maxRowCount, PrepareCallback callback);
+ /** Returns a frame of rows.
+ *
+ * <p>The frame describes whether there may be another frame. If there is not
+ * another frame, the current iteration is done when we have finished the
+ * rows in the this frame.
+ *
+ * <p>The default implementation always returns null.
+ *
+ * @param h Statement handle
+ * @param parameterValues A list of parameter values, if statement is to be
+ * executed; otherwise null
+ * @param offset Zero-based offset of first row in the requested frame
+ * @param fetchMaxRowCount Maximum number of rows to return; negative means
+ * no limit
+ * @return Frame, or null if there are no more
+ */
+ Frame fetch(StatementHandle h, List<Object> parameterValues, int offset,
+ int fetchMaxRowCount);
+
/** Called during the creation of a statement to allocate a new handle.
*
* @param ch Connection handle
@@ -201,15 +221,15 @@ public interface Meta {
class MetaResultSet {
public final int statementId;
public final boolean ownStatement;
- public final Iterable<Object> iterable;
+ public final Frame firstFrame;
public final Signature signature;
public MetaResultSet(int statementId, boolean ownStatement,
- Signature signature, Iterable<Object> iterable) {
- this.signature = signature;
+ Signature signature, Frame firstFrame) {
+ this.signature = Objects.requireNonNull(signature);
this.statementId = statementId;
this.ownStatement = ownStatement;
- this.iterable = iterable;
+ this.firstFrame = firstFrame; // may be null
}
}
@@ -227,7 +247,7 @@ public interface Meta {
assert (fieldNames != null)
== (style == Style.RECORD_PROJECTION || style == Style.MAP);
assert (fields != null) == (style == Style.RECORD_PROJECTION);
- this.style = style;
+ this.style = Objects.requireNonNull(style);
this.clazz = clazz;
this.fields = fields;
this.fieldNames = fieldNames;
@@ -271,7 +291,7 @@ public interface Meta {
public static CursorFactory record(Class resultClass, List<Field> fields,
List<String> fieldNames) {
if (fields == null) {
- fields = new ArrayList<Field>();
+ fields = new ArrayList<>();
for (String fieldName : fieldNames) {
try {
fields.add(resultClass.getField(fieldName));
@@ -348,6 +368,60 @@ public interface Meta {
return new Signature(columns, sql, parameters, internalParameters,
cursorFactory);
}
+
+ /** Creates a copy of this Signature with null lists and maps converted to
+ * empty. */
+ public Signature sanitize() {
+ if (columns == null || parameters == null || internalParameters == null) {
+ return new Signature(sanitize(columns), sql, sanitize(parameters),
+ sanitize(internalParameters), cursorFactory);
+ }
+ return this;
+ }
+
+ private <E> List<E> sanitize(List<E> list) {
+ return list == null ? Collections.<E>emptyList() : list;
+ }
+
+ private <K, V> Map<K, V> sanitize(Map<K, V> map) {
+ return map == null ? Collections.<K, V>emptyMap() : map;
+ }
+ }
+
+ /** A collection of rows. */
+ class Frame {
+ /** Frame that has zero rows and is the last frame. */
+ public static final Frame EMPTY =
+ new Frame(0, true, Collections.emptyList());
+
+ /** Frame that has zero rows but may have another frame. */
+ public static final Frame MORE =
+ new Frame(0, false, Collections.emptyList());
+
+ /** Zero-based offset of first row. */
+ public final int offset;
+ /** Whether this is definitely the last frame of rows.
+ * If true, there are no more rows.
+ * If false, there may or may not be more rows. */
+ public final boolean done;
+ /** The rows. */
+ public final Iterable<Object> rows;
+
+ public Frame(int offset, boolean done, Iterable<Object> rows) {
+ this.offset = offset;
+ this.done = done;
+ this.rows = rows;
+ }
+
+ @JsonCreator
+ public static Frame create(@JsonProperty("offset") int offset,
+ @JsonProperty("done") boolean done,
+ @JsonProperty("rows") List<Object> rows) {
+ if (offset == 0 && done && rows.isEmpty()) {
+ return EMPTY;
+ }
+ return new Frame(offset, done, rows);
+ }
}
/** Connection handle. */
@@ -383,7 +457,7 @@ public interface Meta {
interface PrepareCallback {
Object getMonitor();
void clear() throws SQLException;
- void assign(Signature signature, Iterable<Object> iterable)
+ void assign(Signature signature, Frame firstFrame)
throws SQLException;
void execute() throws SQLException;
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java b/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
index 9b2be07..a6c5f3c 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/MetaImpl.java
@@ -36,8 +36,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
/**
* Basic implementation of {@link Meta}.
@@ -171,7 +173,7 @@ public abstract class MetaImpl implements Meta {
return createResultSet(Collections.<String, Object>emptyMap(),
fieldMetaData(clazz).columns,
CursorFactory.deduce(fieldMetaData(clazz).columns, null),
- Collections.emptyList());
+ Frame.EMPTY);
}
protected static ColumnMetaData columnMetaData(String name, int index,
@@ -207,14 +209,13 @@ public abstract class MetaImpl implements Meta {
protected MetaResultSet createResultSet(
Map<String, Object> internalParameters, List<ColumnMetaData> columns,
- CursorFactory cursorFactory, Iterable<Object> iterable) {
+ CursorFactory cursorFactory, Frame firstFrame) {
try {
final AvaticaStatement statement = connection.createStatement();
- final SignatureWithIterable signature =
- new SignatureWithIterable(internalParameters, columns, "",
- Collections.<AvaticaParameter>emptyList(), cursorFactory,
- iterable);
- return new MetaResultSet(statement.getId(), true, signature, iterable);
+ final Signature signature =
+ new Signature(columns, "", Collections.<AvaticaParameter>emptyList(),
+ internalParameters, cursorFactory);
+ return new MetaResultSet(statement.getId(), true, signature, firstFrame);
} catch (SQLException e) {
throw new RuntimeException(e);
}
@@ -646,8 +647,16 @@ public abstract class MetaImpl implements Meta {
}
public Iterable<Object> createIterable(StatementHandle handle,
- Signature signature, Iterable<Object> iterable) {
- return iterable;
+ Signature signature, List<Object> parameterValues, Frame firstFrame) {
+ if (firstFrame != null && firstFrame.done) {
+ return firstFrame.rows;
+ }
+ return new FetchIterable(handle, firstFrame, parameterValues);
+ }
+
+ public Frame fetch(StatementHandle h, List<Object> parameterValues,
+ int offset, int fetchMaxRowCount) {
+ return null;
}
/** Information about a type. */
@@ -691,27 +700,99 @@ public abstract class MetaImpl implements Meta {
}
}
- /** Prepare result with a iterable. Use this for simple statements
- * that have a canned response and don't need to be executed. */
- protected interface WithIterable {
- Iterable getIterable();
+ /** Iterator that never returns any elements. */
+ private static class EmptyIterator implements Iterator<Object> {
+ public static final Iterator<Object> INSTANCE = new EmptyIterator();
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public Object next() {
+ throw new NoSuchElementException();
+ }
+ }
+
+ /** Iterable that yields an iterator over rows coming from a sequence of
+ * {@link Frame}s. */
+ private class FetchIterable implements Iterable<Object> {
+ private final StatementHandle handle;
+ private final Frame firstFrame;
+ private final List<Object> parameterValues;
+
+ public FetchIterable(StatementHandle handle, Frame firstFrame,
+ List<Object> parameterValues) {
+ this.handle = handle;
+ this.firstFrame = firstFrame;
+ this.parameterValues = parameterValues;
+ }
+
+ public Iterator<Object> iterator() {
+ return new FetchIterator(handle, firstFrame, parameterValues);
+ }
}
- /** Prepare result that contains an iterable. */
- protected static class SignatureWithIterable extends Signature
- implements WithIterable {
- private final Iterable iterable;
+ /** Iterator over rows coming from a sequence of {@link Frame}s. */
+ private class FetchIterator implements Iterator<Object> {
+ private final StatementHandle handle;
+ private Frame frame;
+ private Iterator<Object> rows;
+ private List<Object> parameterValues;
+
+ public FetchIterator(StatementHandle handle, Frame firstFrame,
+ List<Object> parameterValues) {
+ this.handle = handle;
+ this.parameterValues = parameterValues;
+ if (firstFrame == null) {
+ frame = Frame.MORE;
+ rows = EmptyIterator.INSTANCE;
+ } else {
+ frame = firstFrame;
+ rows = firstFrame.rows.iterator();
+ }
+ moveNext();
+ }
- public SignatureWithIterable(Map<String, Object> internalParameters,
- List<ColumnMetaData> columns, String sql,
- List<AvaticaParameter> parameters, CursorFactory cursorFactory,
- Iterable<Object> iterable) {
- super(columns, sql, parameters, internalParameters, cursorFactory);
- this.iterable = iterable;
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
}
- public Iterable getIterable() {
- return iterable;
+ public boolean hasNext() {
+ return rows != null;
+ }
+
+ public Object next() {
+ if (rows == null) {
+ throw new NoSuchElementException();
+ }
+ final Object o = rows.next();
+ moveNext();
+ return o;
+ }
+
+ private void moveNext() {
+ for (;;) {
+ if (rows.hasNext()) {
+ break;
+ }
+ if (frame.done) {
+ rows = null;
+ break;
+ }
+ frame = fetch(handle, parameterValues, frame.offset, 100);
+ parameterValues = null; // don't execute next time
+ if (frame == null) {
+ rows = null;
+ break;
+ }
+ // It is valid for rows to be empty, so we go around the loop again to
+ // check
+ rows = frame.rows.iterator();
+ }
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/UnregisteredDriver.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/UnregisteredDriver.java b/avatica/src/main/java/org/apache/calcite/avatica/UnregisteredDriver.java
index 07883f6..fefd5f4 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/UnregisteredDriver.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/UnregisteredDriver.java
@@ -43,7 +43,7 @@ import java.util.logging.Logger;
* <p>The provider must implement:</p>
* <ul>
* <li>{@link Meta#prepare(org.apache.calcite.avatica.Meta.StatementHandle, String, int)}
- * <li>{@link Meta#createIterable(org.apache.calcite.avatica.Meta.StatementHandle, org.apache.calcite.avatica.Meta.Signature, Iterable)}
+ * <li>{@link Meta#createIterable(org.apache.calcite.avatica.Meta.StatementHandle, org.apache.calcite.avatica.Meta.Signature, java.util.List, Meta.Frame)}
* </ul>
*/
public abstract class UnregisteredDriver implements java.sql.Driver {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java b/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
new file mode 100644
index 0000000..56c20c4
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcMeta.java
@@ -0,0 +1,442 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.avatica.jdbc;
+
+import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.calcite.avatica.Meta;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/** Implementation of {@link Meta} upon an existing JDBC data source. */
+public class JdbcMeta implements Meta {
+ /**
+ * JDBC Types Mapped to Java Types
+ *
+ * @see <a href="https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html#1051555">JDBC Types Mapped to Java Types</a>
+ */
+ protected static final Map<Integer, Type> SQL_TYPE_TO_JAVA_TYPE =
+ new HashMap<>();
+ static {
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.CHAR, String.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.VARCHAR, String.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.LONGNVARCHAR, String.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.NUMERIC, BigDecimal.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.DECIMAL, BigDecimal.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.BIT, Boolean.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.TINYINT, Byte.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.SMALLINT, Short.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.INTEGER, Integer.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.BIGINT, Long.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.REAL, Float.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.FLOAT, Double.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.DOUBLE, Double.TYPE);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.BINARY, byte[].class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.VARBINARY, byte[].class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.LONGVARBINARY, byte[].class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.DATE, java.sql.Date.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.TIME, java.sql.Time.class);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.TIMESTAMP, java.sql.Timestamp.class);
+ //put(Types.CLOB, Clob);
+ //put(Types.BLOB, Blob);
+ SQL_TYPE_TO_JAVA_TYPE.put(Types.ARRAY, Array.class);
+ }
+
+ private final Map<Integer, StatementInfo> statementMap = new HashMap<>();
+
+ /**
+ * Convert from JDBC metadata to Avatica columns.
+ */
+ protected static List<ColumnMetaData>
+ columns(ResultSetMetaData metaData) throws SQLException {
+ final List<ColumnMetaData> columns = new ArrayList<>();
+ for (int i = 1; i <= metaData.getColumnCount(); i++) {
+ final Type javaType =
+ SQL_TYPE_TO_JAVA_TYPE.get(metaData.getColumnType(i));
+ ColumnMetaData.AvaticaType t =
+ ColumnMetaData.scalar(metaData.getColumnType(i),
+ metaData.getColumnTypeName(i), ColumnMetaData.Rep.of(javaType));
+ ColumnMetaData md =
+ new ColumnMetaData(i - 1, metaData.isAutoIncrement(i),
+ metaData.isCaseSensitive(i), metaData.isSearchable(i),
+ metaData.isCurrency(i), metaData.isNullable(i),
+ metaData.isSigned(i), metaData.getColumnDisplaySize(i),
+ metaData.getColumnLabel(i), metaData.getColumnName(i),
+ metaData.getSchemaName(i), metaData.getPrecision(i),
+ metaData.getScale(i), metaData.getTableName(i),
+ metaData.getCatalogName(i), t, metaData.isReadOnly(i),
+ metaData.isWritable(i), metaData.isDefinitelyWritable(i),
+ metaData.getColumnClassName(i));
+ columns.add(md);
+ }
+ return columns;
+ }
+
+ /**
+ * Converts from JDBC metadata to AvaticaParameters
+ */
+ protected static List<AvaticaParameter> parameters(ParameterMetaData metaData)
+ throws SQLException {
+ if (metaData == null) {
+ return Collections.emptyList();
+ }
+ final List<AvaticaParameter> params = new ArrayList<>();
+ for (int i = 1; i <= metaData.getParameterCount(); i++) {
+ params.add(
+ new AvaticaParameter(metaData.isSigned(i), metaData.getPrecision(i),
+ metaData.getScale(i), metaData.getParameterType(i),
+ metaData.getParameterTypeName(i),
+ metaData.getParameterClassName(i), "?" + i));
+ }
+ return params;
+ }
+
+ protected static Signature signature(ResultSetMetaData metaData,
+ ParameterMetaData parameterMetaData, String sql) throws SQLException {
+ return new Signature(columns(metaData), sql, parameters(parameterMetaData),
+ null, CursorFactory.ARRAY);
+ }
+
+ protected static Signature signature(ResultSetMetaData metaData)
+ throws SQLException {
+ return signature(metaData, null, null);
+ }
+
+ protected final Connection connection;
+
+ public JdbcMeta(Connection connection) {
+ this.connection = connection;
+ }
+
+ public String getSqlKeywords() {
+ try {
+ return connection.getMetaData().getSQLKeywords();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getNumericFunctions() {
+ try {
+ return connection.getMetaData().getNumericFunctions();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getStringFunctions() {
+ return null;
+ }
+
+ public String getSystemFunctions() {
+ return null;
+ }
+
+ public String getTimeDateFunctions() {
+ return null;
+ }
+
+ public MetaResultSet getTables(String catalog, Pat schemaPattern,
+ Pat tableNamePattern, List<String> typeList) {
+ try {
+ String[] types = new String[typeList == null ? 0 : typeList.size()];
+ return JdbcResultSet.create(
+ connection.getMetaData().getTables(catalog, schemaPattern.s,
+ tableNamePattern.s,
+ typeList == null ? types : typeList.toArray(types)));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getColumns(String catalog, Pat schemaPattern,
+ Pat tableNamePattern, Pat columnNamePattern) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getColumns(catalog, schemaPattern.s,
+ tableNamePattern.s, columnNamePattern.s));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getSchemas(String catalog, Pat schemaPattern) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getSchemas(catalog, schemaPattern.s));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getCatalogs() {
+ try {
+ return JdbcResultSet.create(connection.getMetaData().getCatalogs());
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getTableTypes() {
+ try {
+ return JdbcResultSet.create(connection.getMetaData().getTableTypes());
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getProcedures(String catalog, Pat schemaPattern,
+ Pat procedureNamePattern) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getProcedures(catalog, schemaPattern.s,
+ procedureNamePattern.s));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getProcedureColumns(String catalog, Pat schemaPattern,
+ Pat procedureNamePattern, Pat columnNamePattern) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getProcedureColumns(catalog,
+ schemaPattern.s, procedureNamePattern.s, columnNamePattern.s));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getColumnPrivileges(String catalog, String schema,
+ String table, Pat columnNamePattern) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getColumnPrivileges(catalog, schema,
+ table, columnNamePattern.s));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getTablePrivileges(String catalog, Pat schemaPattern,
+ Pat tableNamePattern) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getTablePrivileges(catalog,
+ schemaPattern.s, tableNamePattern.s));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getBestRowIdentifier(String catalog, String schema,
+ String table, int scope, boolean nullable) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getBestRowIdentifier(catalog, schema,
+ table, scope, nullable));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getVersionColumns(String catalog, String schema,
+ String table) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getVersionColumns(catalog, schema, table));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getPrimaryKeys(String catalog, String schema,
+ String table) {
+ try {
+ return JdbcResultSet.create(
+ connection.getMetaData().getPrimaryKeys(catalog, schema, table));
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public MetaResultSet getImportedKeys(String catalog, String schema,
+ String table) {
+ return null;
+ }
+
+ public MetaResultSet getExportedKeys(String catalog, String schema,
+ String table) {
+ return null;
+ }
+
+ public MetaResultSet getCrossReference(String parentCatalog,
+ String parentSchema, String parentTable, String foreignCatalog,
+ String foreignSchema, String foreignTable) {
+ return null;
+ }
+
+ public MetaResultSet getTypeInfo() {
+ return null;
+ }
+
+ public MetaResultSet getIndexInfo(String catalog, String schema, String table,
+ boolean unique, boolean approximate) {
+ return null;
+ }
+
+ public MetaResultSet getUDTs(String catalog, Pat schemaPattern,
+ Pat typeNamePattern, int[] types) {
+ return null;
+ }
+
+ public MetaResultSet getSuperTypes(String catalog, Pat schemaPattern,
+ Pat typeNamePattern) {
+ return null;
+ }
+
+ public MetaResultSet getSuperTables(String catalog, Pat schemaPattern,
+ Pat tableNamePattern) {
+ return null;
+ }
+
+ public MetaResultSet getAttributes(String catalog, Pat schemaPattern,
+ Pat typeNamePattern, Pat attributeNamePattern) {
+ return null;
+ }
+
+ public MetaResultSet getClientInfoProperties() {
+ return null;
+ }
+
+ public MetaResultSet getFunctions(String catalog, Pat schemaPattern,
+ Pat functionNamePattern) {
+ return null;
+ }
+
+ public MetaResultSet getFunctionColumns(String catalog, Pat schemaPattern,
+ Pat functionNamePattern, Pat columnNamePattern) {
+ return null;
+ }
+
+ public MetaResultSet getPseudoColumns(String catalog, Pat schemaPattern,
+ Pat tableNamePattern, Pat columnNamePattern) {
+ return null;
+ }
+
+ public Iterable<Object> createIterable(StatementHandle handle,
+ Signature signature, List<Object> parameterValues, Frame firstFrame) {
+ return null;
+ }
+
+ public StatementHandle createStatement(ConnectionHandle ch) {
+ try {
+ final Statement statement = connection.createStatement();
+ final int id = statementMap.size();
+ statementMap.put(id, new StatementInfo(statement));
+ return new StatementHandle(id);
+ } catch (SQLException e) {
+ throw propagate(e);
+ }
+ }
+
+ private RuntimeException propagate(Throwable e) {
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Signature prepare(StatementHandle h, String sql, int maxRowCount) {
+ // TODO: can't actually prepare an existing statement...
+ try {
+ PreparedStatement statement = connection.prepareStatement(sql);
+ statementMap.put(h.id, new StatementInfo(statement));
+ return signature(statement.getMetaData(),
+ statement.getParameterMetaData(), sql);
+ } catch (SQLException e) {
+ throw propagate(e);
+ }
+ }
+
+ public MetaResultSet prepareAndExecute(StatementHandle h, String sql,
+ int maxRowCount, PrepareCallback callback) {
+ final StatementInfo statementInfo = statementMap.get(h.id);
+ try {
+ statementInfo.resultSet = statementInfo.statement.executeQuery(sql);
+ return JdbcResultSet.create(statementInfo.resultSet);
+ } catch (SQLException e) {
+ throw propagate(e);
+ }
+ }
+
+ public Frame fetch(StatementHandle h, List<Object> parameterValues,
+ int offset, int fetchMaxRowCount) {
+ final StatementInfo statementInfo = statementMap.get(h.id);
+ try {
+ if (statementInfo.resultSet == null || parameterValues != null) {
+ if (statementInfo.resultSet != null) {
+ statementInfo.resultSet.close();
+ }
+ final PreparedStatement preparedStatement =
+ (PreparedStatement) statementInfo.statement;
+ if (parameterValues != null) {
+ for (int i = 0; i < parameterValues.size(); i++) {
+ Object o = parameterValues.get(i);
+ preparedStatement.setObject(i + 1, o);
+ }
+ }
+ statementInfo.resultSet = preparedStatement.executeQuery();
+ }
+ return JdbcResultSet.frame(statementInfo.resultSet, offset,
+ fetchMaxRowCount);
+ } catch (SQLException e) {
+ throw propagate(e);
+ }
+ }
+
+ /** All we know about a statement. */
+ private static class StatementInfo {
+ final Statement statement; // sometimes a PreparedStatement
+ ResultSet resultSet;
+
+ private StatementInfo(Statement statement) {
+ this.statement = Objects.requireNonNull(statement);
+ }
+ }
+}
+
+// End JdbcMeta.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java b/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
new file mode 100644
index 0000000..ac4aeb5
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/jdbc/JdbcResultSet.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.avatica.jdbc;
+
+import org.apache.calcite.avatica.Meta;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Implementation of {@link org.apache.calcite.avatica.Meta.MetaResultSet}
+ * upon a JDBC {@link java.sql.ResultSet}.
+ *
+ * @see org.apache.calcite.avatica.jdbc.JdbcMeta */
+class JdbcResultSet extends Meta.MetaResultSet {
+ protected JdbcResultSet(int statementId, boolean ownStatement,
+ Meta.Signature signature, Meta.Frame firstFrame) {
+ super(statementId, ownStatement, signature, firstFrame);
+ }
+
+ /** Creates a result set. */
+ public static JdbcResultSet create(ResultSet resultSet) {
+ try {
+ int id = resultSet.getStatement().hashCode();
+ Meta.Signature sig = JdbcMeta.signature(resultSet.getMetaData());
+ final Meta.Frame firstFrame = frame(resultSet, 0, -1);
+ resultSet.close();
+ return new JdbcResultSet(id, true, sig, firstFrame);
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Creates a frame containing a given number or unlimited number of rows
+ * from a result set. */
+ static Meta.Frame frame(ResultSet resultSet, int offset,
+ int fetchMaxRowCount) throws SQLException {
+ final ResultSetMetaData metaData = resultSet.getMetaData();
+ final int columnCount = metaData.getColumnCount();
+ final int[] types = new int[columnCount];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = metaData.getColumnType(i + 1);
+ }
+ final List<Object> rows = new ArrayList<>();
+ boolean done = false;
+ for (int i = 0; fetchMaxRowCount < 0 || i < fetchMaxRowCount; i++) {
+ if (!resultSet.next()) {
+ done = true;
+ break;
+ }
+ Object[] columns = new Object[columnCount];
+ for (int j = 0; j < columnCount; j++) {
+ columns[j] = getValue(resultSet, types[j], j);
+ }
+ rows.add(columns);
+ }
+ return new Meta.Frame(offset, done, rows);
+ }
+
+ private static Object getValue(ResultSet resultSet, int type, int j)
+ throws SQLException {
+ switch (type) {
+ case Types.BIGINT:
+ final long aLong = resultSet.getLong(j + 1);
+ return aLong == 0 && resultSet.wasNull() ? null : aLong;
+ case Types.INTEGER:
+ final int anInt = resultSet.getInt(j + 1);
+ return anInt == 0 && resultSet.wasNull() ? null : anInt;
+ case Types.SMALLINT:
+ final short aShort = resultSet.getShort(j + 1);
+ return aShort == 0 && resultSet.wasNull() ? null : aShort;
+ case Types.TINYINT:
+ final byte aByte = resultSet.getByte(j + 1);
+ return aByte == 0 && resultSet.wasNull() ? null : aByte;
+ case Types.DOUBLE:
+ case Types.FLOAT:
+ final double aDouble = resultSet.getDouble(j + 1);
+ return aDouble == 0D && resultSet.wasNull() ? null : aDouble;
+ case Types.REAL:
+ final float aFloat = resultSet.getFloat(j + 1);
+ return aFloat == 0D && resultSet.wasNull() ? null : aFloat;
+ default:
+ return resultSet.getObject(j + 1);
+ }
+ }
+}
+
+// End JdbcResultSet.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/jdbc/package-info.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/jdbc/package-info.java b/avatica/src/main/java/org/apache/calcite/avatica/jdbc/package-info.java
new file mode 100644
index 0000000..8b8fb76
--- /dev/null
+++ b/avatica/src/main/java/org/apache/calcite/avatica/jdbc/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Implements an Avatica provider on top of an existing JDBC data source. */
+package org.apache.calcite.avatica.jdbc;
+
+
+// End package-info.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonService.java
index 3de4ef0..7e110ff 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/JsonService.java
@@ -41,11 +41,14 @@ public abstract class JsonService implements Service {
* responses to and from the peer service. */
public abstract String apply(String request);
- private <T> T decode(String response, Class<T> valueType) throws IOException {
+ //@VisibleForTesting
+ protected static <T> T decode(String response, Class<T> valueType)
+ throws IOException {
return MAPPER.readValue(response, valueType);
}
- private <T> String encode(T request) throws IOException {
+ //@VisibleForTesting
+ protected static <T> String encode(T request) throws IOException {
final StringWriter w = new StringWriter();
MAPPER.writeValue(w, request);
return w.toString();
@@ -71,6 +74,14 @@ public abstract class JsonService implements Service {
}
}
+ public ResultSetResponse apply(TablesRequest request) {
+ try {
+ return decode(apply(encode(request)), ResultSetResponse.class);
+ } catch (IOException e) {
+ throw handle(e);
+ }
+ }
+
public PrepareResponse apply(PrepareRequest request) {
try {
return decode(apply(encode(request)), PrepareResponse.class);
@@ -87,6 +98,14 @@ public abstract class JsonService implements Service {
}
}
+ public FetchResponse apply(FetchRequest request) {
+ try {
+ return decode(apply(encode(request)), FetchResponse.class);
+ } catch (IOException e) {
+ throw handle(e);
+ }
+ }
+
public CreateStatementResponse apply(CreateStatementRequest request) {
try {
return decode(apply(encode(request)), CreateStatementResponse.class);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
index 5ee3d16..faf60b8 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/LocalService.java
@@ -16,10 +16,13 @@
*/
package org.apache.calcite.avatica.remote;
+import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.MetaImpl;
+import java.sql.Types;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -27,6 +30,8 @@ import java.util.List;
*/
public class LocalService implements Service {
final Meta meta;
+ /** Whether output is going to JSON. */
+ private final boolean json = true;
public LocalService(Meta meta) {
this.meta = meta;
@@ -47,8 +52,8 @@ public class LocalService implements Service {
public ResultSetResponse toResponse(Meta.MetaResultSet resultSet) {
Meta.CursorFactory cursorFactory = resultSet.signature.cursorFactory;
final List<Object> list;
- if (resultSet.iterable != null) {
- list = list(resultSet.iterable);
+ if (resultSet.firstFrame != null) {
+ list = list(resultSet.firstFrame.rows);
switch (cursorFactory.style) {
case ARRAY:
cursorFactory = Meta.CursorFactory.LIST;
@@ -68,16 +73,16 @@ public class LocalService implements Service {
signature = signature.setCursorFactory(cursorFactory);
}
return new ResultSetResponse(resultSet.statementId, resultSet.ownStatement,
- signature, list);
+ signature, new Meta.Frame(0, true, list));
}
private List<List<Object>> list2(Meta.MetaResultSet resultSet) {
- List<List<Object>> list = new ArrayList<List<Object>>();
- return MetaImpl.collect(resultSet.signature.cursorFactory,
- meta.createIterable(new Meta.StatementHandle(resultSet.statementId),
- resultSet.signature,
- resultSet.iterable),
- list);
+ final Meta.StatementHandle h =
+ new Meta.StatementHandle(resultSet.statementId);
+ final Iterable<Object> iterable = meta.createIterable(h,
+ resultSet.signature, Collections.emptyList(), resultSet.firstFrame);
+ final List<List<Object>> list = new ArrayList<>();
+ return MetaImpl.collect(resultSet.signature.cursorFactory, iterable, list);
}
public ResultSetResponse apply(CatalogsRequest request) {
@@ -91,11 +96,43 @@ public class LocalService implements Service {
return toResponse(resultSet);
}
+ public ResultSetResponse apply(TablesRequest request) {
+ final Meta.MetaResultSet resultSet =
+ meta.getTables(request.catalog, Meta.Pat.of(request.schemaPattern),
+ Meta.Pat.of(request.tableNamePattern), request.typeList);
+ return toResponse(resultSet);
+ }
+
public PrepareResponse apply(PrepareRequest request) {
final Meta.StatementHandle h =
new Meta.StatementHandle(request.statementId);
- final Meta.Signature signature =
- meta.prepare(h, request.sql, request.maxRowCount);
+ Meta.Signature signature =
+ meta.prepare(h, request.sql, request.maxRowCount)
+ .setCursorFactory(Meta.CursorFactory.LIST);
+ if (json) {
+ final List<ColumnMetaData> columns = new ArrayList<>();
+ for (ColumnMetaData column : signature.columns) {
+ switch (column.type.rep) {
+ case BYTE:
+ case PRIMITIVE_BYTE:
+ case DOUBLE:
+ case PRIMITIVE_DOUBLE:
+ case FLOAT:
+ case PRIMITIVE_FLOAT:
+ case INTEGER:
+ case PRIMITIVE_INT:
+ case SHORT:
+ case PRIMITIVE_SHORT:
+ case LONG:
+ case PRIMITIVE_LONG:
+ column = column.setTypeId(Types.NUMERIC);
+ }
+ columns.add(column);
+ }
+ signature = new Meta.Signature(columns, signature.sql,
+ signature.parameters, signature.internalParameters,
+ signature.cursorFactory);
+ }
return new PrepareResponse(signature);
}
@@ -105,20 +142,29 @@ public class LocalService implements Service {
final Meta.MetaResultSet resultSet =
meta.prepareAndExecute(h, request.sql, request.maxRowCount,
new Meta.PrepareCallback() {
- public Object getMonitor() {
+ @Override public Object getMonitor() {
return LocalService.class;
}
- public void clear() {}
+ @Override public void clear() {}
- public void assign(Meta.Signature signature,
- Iterable<Object> iterable) {}
+ @Override public void assign(Meta.Signature signature,
+ Meta.Frame firstFrame) {}
- public void execute() {}
+ @Override public void execute() {}
});
return toResponse(resultSet);
}
+ public FetchResponse apply(FetchRequest request) {
+ final Meta.StatementHandle h =
+ new Meta.StatementHandle(request.statementId);
+ final Meta.Frame frame =
+ meta.fetch(h, request.parameterValues, request.offset,
+ request.fetchMaxRowCount);
+ return new FetchResponse(frame);
+ }
+
public CreateStatementResponse apply(CreateStatementRequest request) {
final Meta.StatementHandle h =
meta.createStatement(new Meta.ConnectionHandle(request.connectionId));
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/remote/MockJsonService.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/MockJsonService.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/MockJsonService.java
index 7fe7ea8..19e59e3 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/MockJsonService.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/MockJsonService.java
@@ -18,6 +18,8 @@ package org.apache.calcite.avatica.remote;
import org.apache.calcite.avatica.AvaticaConnection;
+import java.io.IOException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -44,10 +46,44 @@ public class MockJsonService extends JsonService {
/** Factory that creates a {@code MockJsonService}. */
public static class Factory implements Service.Factory {
public Service create(AvaticaConnection connection) {
- final Map<String, String> map1 = new HashMap<String, String>();
- map1.put(
- "{\"request\":\"getSchemas\",\"catalog\":null,\"schemaPattern\":{\"s\":null}}",
- "{\"response\":\"resultSet\", rows: []}");
+ final Map<String, String> map1 = new HashMap<>();
+ try {
+ map1.put(
+ "{\"request\":\"getSchemas\",\"catalog\":null,\"schemaPattern\":{\"s\":null}}",
+ "{\"response\":\"resultSet\", firstFrame: {offset: 0, done: true, rows: []}}");
+ map1.put(
+ JsonService.encode(new SchemasRequest(null, null)),
+ "{\"response\":\"resultSet\", firstFrame: {offset: 0, done: true, rows: []}}");
+ map1.put(
+ JsonService.encode(
+ new TablesRequest(null, null, null, Arrays.<String>asList())),
+ "{\"response\":\"resultSet\", firstFrame: {offset: 0, done: true, rows: []}}");
+ map1.put(
+ "{\"request\":\"createStatement\",\"connectionId\":0}",
+ "{\"response\":\"createStatement\",\"id\":0}");
+ map1.put(
+ "{\"request\":\"prepareAndExecute\",\"statementId\":0,"
+ + "\"sql\":\"select * from (\\n values (1, 'a'), (null, 'b'), (3, 'c')) as t (c1, c2)\",\"maxRowCount\":-1}",
+ "{\"response\":\"resultSet\",\"signature\": {\n"
+ + " \"columns\": [\n"
+ + " {\"columnName\": \"C1\", \"type\": {type: \"scalar\", id: 4, rep: \"INTEGER\"}},\n"
+ + " {\"columnName\": \"C2\", \"type\": {type: \"scalar\", id: 12, rep: \"STRING\"}}\n"
+ + " ], \"cursorFactory\": {\"style\": \"ARRAY\"}\n"
+ + "}, \"rows\": [[1, \"a\"], [null, \"b\"], [3, \"c\"]]}");
+ map1.put(
+ "{\"request\":\"prepare\",\"statementId\":0,"
+ + "\"sql\":\"select * from (\\n values (1, 'a'), (null, 'b'), (3, 'c')) as t (c1, c2)\",\"maxRowCount\":-1}",
+ "{\"response\":\"prepare\",\"signature\": {\n"
+ + " \"columns\": [\n"
+ + " {\"columnName\": \"C1\", \"type\": {type: \"scalar\", id: 4, rep: \"INTEGER\"}},\n"
+ + " {\"columnName\": \"C2\", \"type\": {type: \"scalar\", id: 12, rep: \"STRING\"}}\n"
+ + " ],\n"
+ + " \"parameters\": [],\n"
+ + " \"cursorFactory\": {\"style\": \"ARRAY\"}\n"
+ + "}}");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
return new MockJsonService(map1);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
index 89ef490..83dd28c 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/RemoteMeta.java
@@ -17,10 +17,14 @@
package org.apache.calcite.avatica.remote;
import org.apache.calcite.avatica.AvaticaConnection;
+import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.MetaImpl;
import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
/**
* Implementation of {@link Meta} for the remote driver.
@@ -33,10 +37,19 @@ class RemoteMeta extends MetaImpl {
this.service = service;
}
- private MetaResultSet toResultSet(Service.ResultSetResponse response) {
- final Signature signature0 = response.signature;
+ private MetaResultSet toResultSet(Class clazz,
+ Service.ResultSetResponse response) {
+ Signature signature0 = response.signature;
+ if (signature0 == null) {
+ final List<ColumnMetaData> columns =
+ clazz == null
+ ? Collections.<ColumnMetaData>emptyList()
+ : fieldMetaData(clazz).columns;
+ signature0 = Signature.create(columns,
+ "?", Collections.<AvaticaParameter>emptyList(), CursorFactory.ARRAY);
+ }
return new MetaResultSet(response.statementId, response.ownStatement,
- signature0, response.rows);
+ signature0, response.firstFrame);
}
@Override public StatementHandle createStatement(ConnectionHandle ch) {
@@ -48,13 +61,22 @@ class RemoteMeta extends MetaImpl {
@Override public MetaResultSet getCatalogs() {
final Service.ResultSetResponse response =
service.apply(new Service.CatalogsRequest());
- return toResultSet(response);
+ return toResultSet(MetaCatalog.class, response);
}
@Override public MetaResultSet getSchemas(String catalog, Pat schemaPattern) {
final Service.ResultSetResponse response =
service.apply(new Service.SchemasRequest(catalog, schemaPattern.s));
- return toResultSet(response);
+ return toResultSet(MetaSchema.class, response);
+ }
+
+ @Override public MetaResultSet getTables(String catalog, Pat schemaPattern,
+ Pat tableNamePattern, List<String> typeList) {
+ final Service.ResultSetResponse response =
+ service.apply(
+ new Service.TablesRequest(catalog, schemaPattern.s,
+ tableNamePattern.s, typeList));
+ return toResultSet(MetaTable.class, response);
}
@Override public Signature prepare(StatementHandle h, String sql,
@@ -72,14 +94,23 @@ class RemoteMeta extends MetaImpl {
callback.clear();
response = service.apply(
new Service.PrepareAndExecuteRequest(h.id, sql, maxRowCount));
- callback.assign(response.signature, response.rows);
+ callback.assign(response.signature, response.firstFrame);
}
callback.execute();
- return toResultSet(response);
+ return toResultSet(null, response);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
+
+ @Override public Frame fetch(StatementHandle h, List<Object> parameterValues,
+ int offset, int fetchMaxRowCount) {
+ final Service.FetchResponse response =
+ service.apply(
+ new Service.FetchRequest(h.id, parameterValues, offset,
+ fetchMaxRowCount));
+ return response.frame;
+ }
}
// End RemoteMeta.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java b/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
index cad38dd..cf73b49 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/remote/Service.java
@@ -32,8 +32,10 @@ import java.util.List;
public interface Service {
ResultSetResponse apply(CatalogsRequest request);
ResultSetResponse apply(SchemasRequest request);
+ ResultSetResponse apply(TablesRequest request);
PrepareResponse apply(PrepareRequest request);
ResultSetResponse apply(PrepareAndExecuteRequest request);
+ FetchResponse apply(FetchRequest request);
CreateStatementResponse apply(CreateStatementRequest request);
/** Factory that creates a {@code Service}. */
@@ -49,9 +51,11 @@ public interface Service {
@JsonSubTypes({
@JsonSubTypes.Type(value = CatalogsRequest.class, name = "getCatalogs"),
@JsonSubTypes.Type(value = SchemasRequest.class, name = "getSchemas"),
+ @JsonSubTypes.Type(value = TablesRequest.class, name = "getTables"),
@JsonSubTypes.Type(value = PrepareRequest.class, name = "prepare"),
@JsonSubTypes.Type(value = PrepareAndExecuteRequest.class,
name = "prepareAndExecute"),
+ @JsonSubTypes.Type(value = FetchRequest.class, name = "fetch"),
@JsonSubTypes.Type(value = CreateStatementRequest.class,
name = "createStatement") })
abstract class Request {
@@ -66,6 +70,7 @@ public interface Service {
@JsonSubTypes({
@JsonSubTypes.Type(value = ResultSetResponse.class, name = "resultSet"),
@JsonSubTypes.Type(value = PrepareResponse.class, name = "prepare"),
+ @JsonSubTypes.Type(value = FetchResponse.class, name = "fetch"),
@JsonSubTypes.Type(value = CreateStatementResponse.class,
name = "createStatement") })
abstract class Response {
@@ -97,6 +102,31 @@ public interface Service {
}
}
+ /** Request for
+ * {@link Meta#getTables(String, org.apache.calcite.avatica.Meta.Pat, org.apache.calcite.avatica.Meta.Pat, java.util.List)}
+ */
+ class TablesRequest extends Request {
+ public final String catalog;
+ public final String schemaPattern;
+ public final String tableNamePattern;
+ public final List<String> typeList;
+
+ @JsonCreator
+ public TablesRequest(@JsonProperty("catalog") String catalog,
+ @JsonProperty("schemaPattern") String schemaPattern,
+ @JsonProperty("tableNamePattern") String tableNamePattern,
+ @JsonProperty("typeList") List<String> typeList) {
+ this.catalog = catalog;
+ this.schemaPattern = schemaPattern;
+ this.tableNamePattern = tableNamePattern;
+ this.typeList = typeList;
+ }
+
+ @Override Response accept(Service service) {
+ return service.apply(this);
+ }
+ }
+
/** Response that contains a result set.
*
* <p>Several types of request, including
@@ -107,17 +137,17 @@ public interface Service {
public final int statementId;
public final boolean ownStatement;
public final Meta.Signature signature;
- public final List<Object> rows;
+ public final Meta.Frame firstFrame;
@JsonCreator
public ResultSetResponse(@JsonProperty("statementId") int statementId,
@JsonProperty("ownStatement") boolean ownStatement,
@JsonProperty("signature") Meta.Signature signature,
- @JsonProperty("rows") List<Object> rows) {
+ @JsonProperty("firstFrame") Meta.Frame firstFrame) {
this.statementId = statementId;
this.ownStatement = ownStatement;
this.signature = signature;
- this.rows = rows;
+ this.firstFrame = firstFrame;
}
}
@@ -177,6 +207,45 @@ public interface Service {
}
/** Request for
+ * {@link org.apache.calcite.avatica.Meta#fetch(Meta.StatementHandle, List, int, int)}. */
+ class FetchRequest extends Request {
+ public final int statementId;
+ public final int offset;
+ /** Maximum number of rows to be returned in the frame. Negative means no
+ * limit. */
+ public final int fetchMaxRowCount;
+ /** A list of parameter values, if statement is to be executed; otherwise
+ * null. */
+ public final List<Object> parameterValues;
+
+ @JsonCreator
+ public FetchRequest(@JsonProperty("statementId") int statementId,
+ @JsonProperty("parameterValues") List<Object> parameterValues,
+ @JsonProperty("offset") int offset,
+ @JsonProperty("fetchMaxRowCount") int fetchMaxRowCount) {
+ this.statementId = statementId;
+ this.parameterValues = parameterValues;
+ this.offset = offset;
+ this.fetchMaxRowCount = fetchMaxRowCount;
+ }
+
+ @Override FetchResponse accept(Service service) {
+ return service.apply(this);
+ }
+ }
+
+ /** Response from
+ * {@link org.apache.calcite.avatica.remote.Service.FetchRequest}. */
+ class FetchResponse extends Response {
+ public final Meta.Frame frame;
+
+ @JsonCreator
+ public FetchResponse(@JsonProperty("frame") Meta.Frame frame) {
+ this.frame = frame;
+ }
+ }
+
+ /** Request for
* {@link org.apache.calcite.avatica.Meta#createStatement(org.apache.calcite.avatica.Meta.ConnectionHandle)}. */
class CreateStatementRequest extends Request {
public final int connectionId;
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java b/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
index 301352f..42731ba 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java
@@ -22,6 +22,7 @@ import org.apache.calcite.avatica.ColumnMetaData;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
+import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.URL;
import java.sql.Array;
@@ -71,12 +72,12 @@ public abstract class AbstractCursor implements Cursor {
return accessors;
}
- protected Accessor createAccessor(ColumnMetaData type, int ordinal,
+ protected Accessor createAccessor(ColumnMetaData columnMetaData, int ordinal,
Calendar localCalendar, ArrayImpl.Factory factory) {
// Create an accessor appropriate to the underlying type; the accessor
// can convert to any type in the same family.
Getter getter = createGetter(ordinal);
- switch (type.type.type) {
+ switch (columnMetaData.type.id) {
case Types.TINYINT:
return new ByteAccessor(getter);
case Types.SMALLINT:
@@ -92,15 +93,17 @@ public abstract class AbstractCursor implements Cursor {
return new FloatAccessor(getter);
case Types.DOUBLE:
return new DoubleAccessor(getter);
+ case Types.NUMERIC:
+ return new NumberAccessor(getter);
case Types.DECIMAL:
return new BigDecimalAccessor(getter);
case Types.CHAR:
- switch (type.type.representation) {
+ switch (columnMetaData.type.rep) {
case PRIMITIVE_CHAR:
case CHARACTER:
- return new StringFromCharAccessor(getter, type.displaySize);
+ return new StringFromCharAccessor(getter, columnMetaData.displaySize);
default:
- return new FixedStringAccessor(getter, type.displaySize);
+ return new FixedStringAccessor(getter, columnMetaData.displaySize);
}
case Types.VARCHAR:
return new StringAccessor(getter);
@@ -108,27 +111,27 @@ public abstract class AbstractCursor implements Cursor {
case Types.VARBINARY:
return new BinaryAccessor(getter);
case Types.DATE:
- switch (type.type.representation) {
+ switch (columnMetaData.type.rep) {
case PRIMITIVE_INT:
case INTEGER:
return new DateFromIntAccessor(getter, localCalendar);
case JAVA_SQL_DATE:
return new DateAccessor(getter, localCalendar);
default:
- throw new AssertionError("bad " + type.type.representation);
+ throw new AssertionError("bad " + columnMetaData.type.rep);
}
case Types.TIME:
- switch (type.type.representation) {
+ switch (columnMetaData.type.rep) {
case PRIMITIVE_INT:
case INTEGER:
return new TimeFromIntAccessor(getter, localCalendar);
case JAVA_SQL_TIME:
return new TimeAccessor(getter, localCalendar);
default:
- throw new AssertionError("bad " + type.type.representation);
+ throw new AssertionError("bad " + columnMetaData.type.rep);
}
case Types.TIMESTAMP:
- switch (type.type.representation) {
+ switch (columnMetaData.type.rep) {
case PRIMITIVE_LONG:
case LONG:
return new TimestampFromLongAccessor(getter, localCalendar);
@@ -137,31 +140,32 @@ public abstract class AbstractCursor implements Cursor {
case JAVA_UTIL_DATE:
return new TimestampFromUtilDateAccessor(getter, localCalendar);
default:
- throw new AssertionError("bad " + type.type.representation);
+ throw new AssertionError("bad " + columnMetaData.type.rep);
}
case Types.ARRAY:
return new ArrayAccessor(getter,
- ((ColumnMetaData.ArrayType) type.type).component, factory);
+ ((ColumnMetaData.ArrayType) columnMetaData.type).component, factory);
case Types.JAVA_OBJECT:
case Types.STRUCT:
case Types.OTHER: // e.g. map
- if (type.type.typeName.startsWith("INTERVAL_")) {
- int end = type.type.typeName.indexOf("(");
+ if (columnMetaData.type.name.startsWith("INTERVAL_")) {
+ int end = columnMetaData.type.name.indexOf("(");
if (end < 0) {
- end = type.type.typeName.length();
+ end = columnMetaData.type.name.length();
}
TimeUnitRange range =
TimeUnitRange.valueOf(
- type.type.typeName.substring("INTERVAL_".length(), end));
+ columnMetaData.type.name.substring("INTERVAL_".length(), end));
if (range.monthly()) {
return new IntervalYearMonthAccessor(getter, range);
} else {
- return new IntervalDayTimeAccessor(getter, range, type.scale);
+ return new IntervalDayTimeAccessor(getter, range,
+ columnMetaData.scale);
}
}
return new ObjectAccessor(getter);
default:
- throw new RuntimeException("unknown type " + type.type.type);
+ throw new RuntimeException("unknown type " + columnMetaData.type.id);
}
}
@@ -617,6 +621,41 @@ public abstract class AbstractCursor implements Cursor {
}
/**
+ * Accessor that assumes that the underlying value is a {@link Number};
+ * corresponds to {@link java.sql.Types#NUMERIC}.
+ *
+ * <p>This is useful when numbers have been translated over JSON. JSON
+ * converts a 0L (0 long) value to the string "0" and back to 0 (0 int).
+ * So you cannot be sure that the source and target type are the same.
+ */
+ private static class NumberAccessor extends BigNumberAccessor {
+ public NumberAccessor(Getter getter) {
+ super(getter);
+ }
+
+ protected Number getNumber() {
+ return (Number) getObject();
+ }
+
+ public BigDecimal getBigDecimal(int scale) {
+ return getBigDecimal();
+ }
+
+ public BigDecimal getBigDecimal() {
+ Number n = getNumber();
+ return n == null ? null
+ : n instanceof BigDecimal ? (BigDecimal) n
+ : n instanceof BigInteger ? new BigDecimal((BigInteger) n)
+ : n instanceof Long ? new BigDecimal((long) n)
+ : n instanceof Integer ? new BigDecimal((int) n)
+ : n instanceof Short ? new BigDecimal((short) n)
+ : n instanceof Byte ? new BigDecimal((byte) n)
+ : n instanceof Double ? new BigDecimal((double) n)
+ : new BigDecimal((float) n);
+ }
+ }
+
+ /**
* Accessor that assumes that the underlying value is a {@link String};
* corresponds to {@link java.sql.Types#CHAR}
* and {@link java.sql.Types#VARCHAR}.
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4d2f647a/avatica/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java b/avatica/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
index e953e6a..385f733 100644
--- a/avatica/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
+++ b/avatica/src/main/java/org/apache/calcite/avatica/util/ArrayImpl.java
@@ -39,11 +39,11 @@ public class ArrayImpl implements Array {
}
public String getBaseTypeName() throws SQLException {
- return elementType.typeName;
+ return elementType.name;
}
public int getBaseType() throws SQLException {
- return elementType.type;
+ return elementType.id;
}
public Object getArray() throws SQLException {
@@ -65,7 +65,7 @@ public class ArrayImpl implements Array {
@SuppressWarnings("unchecked")
protected Object getArray(List list) throws SQLException {
int i = 0;
- switch (elementType.representation) {
+ switch (elementType.rep) {
case PRIMITIVE_DOUBLE:
final double[] doubles = new double[list.size()];
for (double v : (List<Double>) list) {
@@ -118,7 +118,7 @@ public class ArrayImpl implements Array {
// fall through
}
final Object[] objects = list.toArray();
- switch (elementType.type) {
+ switch (elementType.id) {
case Types.ARRAY:
final ColumnMetaData.ArrayType arrayType =
(ColumnMetaData.ArrayType) elementType;