http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java new file mode 100644 index 0000000..478723f --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java @@ -0,0 +1,1046 @@ +/* + * 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; + +import org.apache.calcite.avatica.remote.TypedValue; +import org.apache.calcite.avatica.util.ArrayImpl; +import org.apache.calcite.avatica.util.Cursor; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +/** + * Implementation of {@link java.sql.ResultSet} + * for the Avatica engine. + */ +public class AvaticaResultSet implements ResultSet, ArrayImpl.Factory { + protected final AvaticaStatement statement; + protected final QueryState state; + protected final Meta.Signature signature; + protected final Meta.Frame firstFrame; + protected final List<ColumnMetaData> columnMetaDataList; + protected final ResultSetMetaData resultSetMetaData; + protected final Calendar localCalendar; + + protected Cursor cursor; + protected List<Cursor.Accessor> accessorList; + private int row; + private boolean afterLast; + private int fetchDirection; + private int fetchSize; + private int type; + private int concurrency; + private int holdability; + private boolean closed; + private long timeoutMillis; + private Cursor timeoutCursor; + + public AvaticaResultSet(AvaticaStatement statement, + QueryState state, + Meta.Signature signature, + ResultSetMetaData resultSetMetaData, + TimeZone timeZone, + Meta.Frame firstFrame) { + this.statement = statement; + this.state = state; + this.signature = signature; + this.firstFrame = firstFrame; + this.columnMetaDataList = signature.columns; + this.type = statement.resultSetType; + this.concurrency = statement.resultSetConcurrency; + this.holdability = statement.resultSetHoldability; + this.fetchSize = statement.getFetchSize(); + this.fetchDirection = statement.getFetchDirection(); + this.resultSetMetaData = resultSetMetaData; + this.localCalendar = Calendar.getInstance(timeZone); + } + + private int findColumn0(String columnLabel) throws SQLException { + for (ColumnMetaData columnMetaData : columnMetaDataList) { + // Per JDBC 3.0 specification, match is case-insensitive and if there is + // more than one column with a particular name, take the first. + if (columnMetaData.label.equalsIgnoreCase(columnLabel)) { + return columnMetaData.ordinal; // 0-based + } + } + throw new SQLException("column '" + columnLabel + "' not found"); + } + + /** + * Returns the accessor for column with a given index. + * + * @param columnIndex 1-based column index + * @return Accessor + * @throws SQLException if index is not valid + */ + private Cursor.Accessor getAccessor(int columnIndex) throws SQLException { + try { + return accessorList.get(columnIndex - 1); + } catch (IndexOutOfBoundsException e) { + throw new SQLException("invalid column ordinal: " + columnIndex); + } + } + + /** + * Returns the accessor for column with a given label. + * + * @param columnLabel Column label + * @return Accessor + * @throws SQLException if there is no column with that label + */ + private Cursor.Accessor getAccessor(String columnLabel) throws SQLException { + return accessorList.get(findColumn0(columnLabel)); + } + + public void close() { + closed = true; + final Cursor cursor = this.cursor; + if (cursor != null) { + this.cursor = null; + cursor.close(); + } + statement.onResultSetClose(this); + // TODO: for timeout, see IteratorResultSet.close +/* + if (timeoutCursor != null) { + final long noTimeout = 0; + timeoutCursor.close(noTimeout); + timeoutCursor = null; + } +*/ + } + + /** + * Sets the timeout that this result set will wait for a row from the + * underlying iterator. + * + * <p>Not a JDBC method.</p> + * + * @param timeoutMillis Timeout in milliseconds. Must be greater than zero. + */ + void setTimeout(long timeoutMillis) { + assert timeoutMillis > 0; + assert this.timeoutMillis == 0; + this.timeoutMillis = timeoutMillis; + assert timeoutCursor == null; + timeoutCursor = cursor; + + // TODO: for timeout, see IteratorResultSet.setTimeout +/* + timeoutCursor = new TimeoutCursor(timeoutMillis); + timeoutCursor.start(); +*/ + } + + // not JDBC + protected void cancel() { + throw new UnsupportedOperationException(); // TODO: + } + + /** + * Executes this result set. (Not a JDBC method.) + * + * <p>Note that execute cannot occur in the constructor, because the + * constructor occurs while the statement is locked, to make sure that + * execute/cancel don't happen at the same time.</p> + * + * @see org.apache.calcite.avatica.AvaticaConnection.Trojan#execute(AvaticaResultSet) + * + * @throws SQLException if execute fails for some reason. + */ + protected AvaticaResultSet execute() throws SQLException { + final List<TypedValue> parameterValues = statement.getBoundParameterValues(); + final Iterable<Object> iterable1 = + statement.connection.meta.createIterable(statement.handle, state, signature, + parameterValues, firstFrame); + this.cursor = MetaImpl.createCursor(signature.cursorFactory, iterable1); + this.accessorList = + cursor.createAccessors(columnMetaDataList, localCalendar, this); + this.row = -1; + this.afterLast = false; + return this; + } + + protected AvaticaResultSet execute2(Cursor cursor, + List<ColumnMetaData> columnMetaDataList) { + this.cursor = cursor; + this.accessorList = + cursor.createAccessors(columnMetaDataList, localCalendar, this); + this.row = -1; + this.afterLast = false; + return this; + } + + public ResultSet create(ColumnMetaData.AvaticaType elementType, + Iterable<Object> iterable) { + throw new UnsupportedOperationException(); + } + + public boolean next() throws SQLException { + // TODO: for timeout, see IteratorResultSet.next + if (isClosed()) { + throw new SQLException("next() called on closed cursor"); + } + if (cursor.next()) { + ++row; + return true; + } else { + afterLast = true; + return false; + } + } + + public int findColumn(String columnLabel) throws SQLException { + return findColumn0(columnLabel) + 1; + } + + public boolean wasNull() throws SQLException { + return cursor.wasNull(); + } + + public String getString(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getString(); + } + + public boolean getBoolean(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getBoolean(); + } + + public byte getByte(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getByte(); + } + + public short getShort(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getShort(); + } + + public int getInt(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getInt(); + } + + public long getLong(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getLong(); + } + + public float getFloat(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getFloat(); + } + + public double getDouble(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getDouble(); + } + + public BigDecimal getBigDecimal( + int columnIndex, int scale) throws SQLException { + return getAccessor(columnIndex).getBigDecimal(scale); + } + + public byte[] getBytes(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getBytes(); + } + + public Date getDate(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getDate(localCalendar); + } + + public Time getTime(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getTime(localCalendar); + } + + public Timestamp getTimestamp(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getTimestamp(localCalendar); + } + + public InputStream getAsciiStream(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getAsciiStream(); + } + + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getUnicodeStream(); + } + + public InputStream getBinaryStream(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getBinaryStream(); + } + + public String getString(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getString(); + } + + public boolean getBoolean(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getBoolean(); + } + + public byte getByte(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getByte(); + } + + public short getShort(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getShort(); + } + + public int getInt(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getInt(); + } + + public long getLong(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getLong(); + } + + public float getFloat(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getFloat(); + } + + public double getDouble(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getDouble(); + } + + public BigDecimal getBigDecimal( + String columnLabel, int scale) throws SQLException { + return getAccessor(columnLabel).getBigDecimal(scale); + } + + public byte[] getBytes(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getBytes(); + } + + public Date getDate(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getDate(localCalendar); + } + + public Time getTime(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getTime(localCalendar); + } + + public Timestamp getTimestamp(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getTimestamp(localCalendar); + } + + public InputStream getAsciiStream(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getAsciiStream(); + } + + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getUnicodeStream(); + } + + public InputStream getBinaryStream(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getBinaryStream(); + } + + public SQLWarning getWarnings() throws SQLException { + return null; // no warnings, since warnings are not supported + } + + public void clearWarnings() throws SQLException { + // no-op since warnings are not supported + } + + public String getCursorName() throws SQLException { + throw new UnsupportedOperationException(); + } + + public ResultSetMetaData getMetaData() throws SQLException { + return resultSetMetaData; + } + + public Object getObject(int columnIndex) throws SQLException { + final Cursor.Accessor accessor = getAccessor(columnIndex); + final ColumnMetaData metaData = columnMetaDataList.get(columnIndex - 1); + return AvaticaSite.get(accessor, metaData.type.id, localCalendar); + } + + public Object getObject(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getObject(); + } + + public Reader getCharacterStream(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getCharacterStream(); + } + + public Reader getCharacterStream(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getCharacterStream(); + } + + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getBigDecimal(); + } + + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getBigDecimal(); + } + + public boolean isBeforeFirst() throws SQLException { + return row < 0; + } + + public boolean isAfterLast() throws SQLException { + return afterLast; + } + + public boolean isFirst() throws SQLException { + return row == 0; + } + + public boolean isLast() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void beforeFirst() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void afterLast() throws SQLException { + throw new UnsupportedOperationException(); + } + + public boolean first() throws SQLException { + throw new UnsupportedOperationException(); + } + + public boolean last() throws SQLException { + throw new UnsupportedOperationException(); + } + + public int getRow() throws SQLException { + return row; + } + + public boolean absolute(int row) throws SQLException { + throw new UnsupportedOperationException(); + } + + public boolean relative(int rows) throws SQLException { + throw new UnsupportedOperationException(); + } + + public boolean previous() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void setFetchDirection(int direction) throws SQLException { + this.fetchDirection = direction; + } + + public int getFetchDirection() throws SQLException { + return fetchDirection; + } + + public void setFetchSize(int fetchSize) throws SQLException { + this.fetchSize = fetchSize; + } + + public int getFetchSize() throws SQLException { + return fetchSize; + } + + public int getType() throws SQLException { + return type; + } + + public int getConcurrency() throws SQLException { + return concurrency; + } + + public boolean rowUpdated() throws SQLException { + return false; + } + + public boolean rowInserted() throws SQLException { + return false; + } + + public boolean rowDeleted() throws SQLException { + return false; + } + + public void updateNull(int columnIndex) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateByte(int columnIndex, byte x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateShort(int columnIndex, short x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateInt(int columnIndex, int x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateLong(int columnIndex, long x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateFloat(int columnIndex, float x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateDouble(int columnIndex, double x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBigDecimal( + int columnIndex, BigDecimal x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateString(int columnIndex, String x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateDate(int columnIndex, Date x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateTime(int columnIndex, Time x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateTimestamp( + int columnIndex, Timestamp x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateAsciiStream( + int columnIndex, InputStream x, int length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBinaryStream( + int columnIndex, InputStream x, int length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateCharacterStream( + int columnIndex, Reader x, int length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateObject( + int columnIndex, Object x, int scaleOrLength) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateObject(int columnIndex, Object x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNull(String columnLabel) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBoolean( + String columnLabel, boolean x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateByte(String columnLabel, byte x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateShort(String columnLabel, short x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateInt(String columnLabel, int x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateLong(String columnLabel, long x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateFloat(String columnLabel, float x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateDouble(String columnLabel, double x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBigDecimal( + String columnLabel, BigDecimal x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateString(String columnLabel, String x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateDate(String columnLabel, Date x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateTime(String columnLabel, Time x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateTimestamp( + String columnLabel, Timestamp x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateAsciiStream( + String columnLabel, InputStream x, int length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBinaryStream( + String columnLabel, InputStream x, int length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateCharacterStream( + String columnLabel, Reader reader, int length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateObject( + String columnLabel, Object x, int scaleOrLength) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateObject(String columnLabel, Object x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void insertRow() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateRow() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void deleteRow() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void refreshRow() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void cancelRowUpdates() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void moveToInsertRow() throws SQLException { + throw new UnsupportedOperationException(); + } + + public void moveToCurrentRow() throws SQLException { + throw new UnsupportedOperationException(); + } + + public AvaticaStatement getStatement() { + return statement; + } + + public Object getObject( + int columnIndex, Map<String, Class<?>> map) throws SQLException { + return getAccessor(columnIndex).getObject(map); + } + + public Ref getRef(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getRef(); + } + + public Blob getBlob(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getBlob(); + } + + public Clob getClob(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getClob(); + } + + public Array getArray(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getArray(); + } + + public Object getObject( + String columnLabel, Map<String, Class<?>> map) throws SQLException { + return getAccessor(columnLabel).getObject(map); + } + + public Ref getRef(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getRef(); + } + + public Blob getBlob(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getBlob(); + } + + public Clob getClob(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getClob(); + } + + public Array getArray(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getArray(); + } + + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + return getAccessor(columnIndex).getDate(cal); + } + + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + return getAccessor(columnLabel).getDate(cal); + } + + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + return getAccessor(columnIndex).getTime(cal); + } + + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + return getAccessor(columnLabel).getTime(cal); + } + + public Timestamp getTimestamp( + int columnIndex, Calendar cal) throws SQLException { + return getAccessor(columnIndex).getTimestamp(cal); + } + + public Timestamp getTimestamp( + String columnLabel, Calendar cal) throws SQLException { + return getAccessor(columnLabel).getTimestamp(cal); + } + + public URL getURL(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getURL(); + } + + public URL getURL(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getURL(); + } + + public void updateRef(int columnIndex, Ref x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateRef(String columnLabel, Ref x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBlob(int columnIndex, Blob x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBlob(String columnLabel, Blob x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateClob(int columnIndex, Clob x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateClob(String columnLabel, Clob x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateArray(int columnIndex, Array x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateArray(String columnLabel, Array x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public RowId getRowId(int columnIndex) throws SQLException { + throw new UnsupportedOperationException(); + } + + public RowId getRowId(String columnLabel) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateRowId(int columnIndex, RowId x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateRowId(String columnLabel, RowId x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public int getHoldability() throws SQLException { + return holdability; + } + + public boolean isClosed() throws SQLException { + return closed; + } + + public void updateNString( + int columnIndex, String nString) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNString( + String columnLabel, String nString) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNClob( + String columnLabel, NClob nClob) throws SQLException { + throw new UnsupportedOperationException(); + } + + public NClob getNClob(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getNClob(); + } + + public NClob getNClob(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getNClob(); + } + + public SQLXML getSQLXML(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getSQLXML(); + } + + public SQLXML getSQLXML(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getSQLXML(); + } + + public void updateSQLXML( + int columnIndex, SQLXML xmlObject) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateSQLXML( + String columnLabel, SQLXML xmlObject) throws SQLException { + throw new UnsupportedOperationException(); + } + + public String getNString(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getNString(); + } + + public String getNString(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getNString(); + } + + public Reader getNCharacterStream(int columnIndex) throws SQLException { + return getAccessor(columnIndex).getNCharacterStream(); + } + + public Reader getNCharacterStream(String columnLabel) throws SQLException { + return getAccessor(columnLabel).getNCharacterStream(); + } + + public void updateNCharacterStream( + int columnIndex, Reader x, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNCharacterStream( + String columnLabel, Reader reader, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateAsciiStream( + int columnIndex, InputStream x, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBinaryStream( + int columnIndex, InputStream x, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateCharacterStream( + int columnIndex, Reader x, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateAsciiStream( + String columnLabel, InputStream x, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBinaryStream( + String columnLabel, InputStream x, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateCharacterStream( + String columnLabel, Reader reader, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBlob( + int columnIndex, + InputStream inputStream, + long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBlob( + String columnLabel, + InputStream inputStream, + long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateClob( + int columnIndex, Reader reader, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateClob( + String columnLabel, Reader reader, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNClob( + int columnIndex, Reader reader, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNClob( + String columnLabel, Reader reader, long length) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNCharacterStream( + int columnIndex, Reader x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNCharacterStream( + String columnLabel, Reader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateAsciiStream( + int columnIndex, InputStream x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBinaryStream( + int columnIndex, InputStream x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateCharacterStream( + int columnIndex, Reader x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateAsciiStream( + String columnLabel, InputStream x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBinaryStream( + String columnLabel, InputStream x) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateCharacterStream( + String columnLabel, Reader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBlob( + int columnIndex, InputStream inputStream) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateBlob( + String columnLabel, InputStream inputStream) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateClob(int columnIndex, Reader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateClob( + String columnLabel, Reader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNClob( + int columnIndex, Reader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + public void updateNClob( + String columnLabel, Reader reader) throws SQLException { + throw new UnsupportedOperationException(); + } + + public <T> T getObject(int columnIndex, Class<T> type) throws SQLException { + return getAccessor(columnIndex).getObject(type); + } + + public <T> T getObject( + String columnLabel, Class<T> type) throws SQLException { + return getAccessor(columnLabel).getObject(type); + } + + public <T> T unwrap(Class<T> iface) throws SQLException { + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw statement.connection.helper.createException( + "does not implement '" + iface + "'"); + } + + public boolean isWrapperFor(Class<?> iface) throws SQLException { + return iface.isInstance(this); + } +} + +// End AvaticaResultSet.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java new file mode 100644 index 0000000..b4e8892 --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSetMetaData.java @@ -0,0 +1,145 @@ +/* + * 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; + +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +/** + * Implementation of {@link ResultSetMetaData} + * for the Avatica framework. + */ +public class AvaticaResultSetMetaData implements ResultSetMetaData { + final AvaticaStatement statement; + final Object query; // reserved for future use + final Meta.Signature signature; + + public AvaticaResultSetMetaData( + AvaticaStatement statement, + Object query, + Meta.Signature signature) { + this.statement = statement; + this.query = query; + this.signature = signature; + } + + private ColumnMetaData getColumnMetaData(int column) { + return signature.columns.get(column - 1); + } + + // implement ResultSetMetaData + + public int getColumnCount() throws SQLException { + return signature.columns.size(); + } + + public boolean isAutoIncrement(int column) throws SQLException { + return getColumnMetaData(column).autoIncrement; + } + + public boolean isCaseSensitive(int column) throws SQLException { + return getColumnMetaData(column).caseSensitive; + } + + public boolean isSearchable(int column) throws SQLException { + return getColumnMetaData(column).searchable; + } + + public boolean isCurrency(int column) throws SQLException { + return getColumnMetaData(column).currency; + } + + public int isNullable(int column) throws SQLException { + return getColumnMetaData(column).nullable; + } + + public boolean isSigned(int column) throws SQLException { + return getColumnMetaData(column).signed; + } + + public int getColumnDisplaySize(int column) throws SQLException { + return getColumnMetaData(column).displaySize; + } + + public String getColumnLabel(int column) throws SQLException { + return getColumnMetaData(column).label; + } + + public String getColumnName(int column) throws SQLException { + return getColumnMetaData(column).columnName; + } + + public String getSchemaName(int column) throws SQLException { + return getColumnMetaData(column).schemaName; + } + + public int getPrecision(int column) throws SQLException { + return getColumnMetaData(column).precision; + } + + public int getScale(int column) throws SQLException { + return getColumnMetaData(column).scale; + } + + public String getTableName(int column) throws SQLException { + return getColumnMetaData(column).tableName; + } + + public String getCatalogName(int column) throws SQLException { + return getColumnMetaData(column).catalogName; + } + + public int getColumnType(int column) throws SQLException { + return getColumnMetaData(column).type.id; + } + + public String getColumnTypeName(int column) throws SQLException { + return getColumnMetaData(column).type.name; + } + + public boolean isReadOnly(int column) throws SQLException { + return getColumnMetaData(column).readOnly; + } + + public boolean isWritable(int column) throws SQLException { + return getColumnMetaData(column).writable; + } + + public boolean isDefinitelyWritable(int column) throws SQLException { + return getColumnMetaData(column).definitelyWritable; + } + + public String getColumnClassName(int column) throws SQLException { + return getColumnMetaData(column).columnClassName; + } + + // implement Wrapper + + public <T> T unwrap(Class<T> iface) throws SQLException { + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw statement.connection.helper.createException( + "does not implement '" + iface + "'"); + } + + public boolean isWrapperFor(Class<?> iface) throws SQLException { + return iface.isInstance(this); + } +} + +// End AvaticaResultSetMetaData.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java new file mode 100644 index 0000000..675c8af --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSeverity.java @@ -0,0 +1,90 @@ +/* + * 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; + +import org.apache.calcite.avatica.proto.Common; + +import java.util.Objects; + +/** + * An enumeration that denotes the severity of a given unexpected state. + */ +public enum AvaticaSeverity { + /** + * The severity of the outcome of some unexpected state is unknown. + */ + UNKNOWN(0), + + /** + * The system has been left in an unrecoverable state as a result of an operation. + */ + FATAL(1), + + /** + * The result of an action resulted in an error which the current operation cannot recover + * from. Clients can attempt to execute the operation again. + */ + ERROR(2), + + /** + * The operation completed successfully but a message was generated to warn the client about + * some unexpected state or action. + */ + WARNING(3); + + private final int value; + + AvaticaSeverity(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public Common.Severity toProto() { + switch (this) { + case UNKNOWN: + return Common.Severity.UNKNOWN_SEVERITY; + case FATAL: + return Common.Severity.FATAL_SEVERITY; + case ERROR: + return Common.Severity.ERROR_SEVERITY; + case WARNING: + return Common.Severity.WARNING_SEVERITY; + default: + throw new RuntimeException("Unhandled Severity level: " + this); + } + } + + public static AvaticaSeverity fromProto(Common.Severity proto) { + switch (Objects.requireNonNull(proto)) { + case UNKNOWN_SEVERITY: + return AvaticaSeverity.UNKNOWN; + case FATAL_SEVERITY: + return AvaticaSeverity.FATAL; + case ERROR_SEVERITY: + return AvaticaSeverity.ERROR; + case WARNING_SEVERITY: + return AvaticaSeverity.WARNING; + default: + throw new RuntimeException("Unhandled protobuf Severity level: " + proto); + } + } +} + +// End AvaticaSeverity.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java new file mode 100644 index 0000000..7fd6947 --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java @@ -0,0 +1,587 @@ +/* + * 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; + +import org.apache.calcite.avatica.remote.TypedValue; +import org.apache.calcite.avatica.util.Cursor; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.NClob; +import java.sql.Ref; +import java.sql.RowId; +import java.sql.SQLException; +import java.sql.SQLXML; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Calendar; + +/** + * A location that a value can be written to or read from. + */ +public class AvaticaSite { + final AvaticaParameter parameter; + + /** Calendar is not thread-safe. But calendar is only used from within one + * thread, and we have to trust that clients are not modifying calendars + * that they pass to us in a method such as + * {@link java.sql.PreparedStatement#setTime(int, Time, Calendar)}, so we do + * not need to synchronize access. */ + final Calendar calendar; + private final int index; + final TypedValue[] slots; + + /** Value that means the parameter has been set to null. + * If value is null, parameter has not been set. */ + public static final Object DUMMY_VALUE = Dummy.INSTANCE; + + public AvaticaSite(AvaticaParameter parameter, Calendar calendar, int index, + TypedValue[] slots) { + assert calendar != null; + assert parameter != null; + assert slots != null; + this.parameter = parameter; + this.calendar = calendar; + this.index = index; + this.slots = slots; + } + + private TypedValue wrap(ColumnMetaData.Rep rep, Object o, + Calendar calendar) { + return TypedValue.ofJdbc(rep, o, calendar); + } + + private TypedValue wrap(ColumnMetaData.Rep rep, Object o) { + return TypedValue.ofJdbc(rep, o, calendar); + } + + public boolean isSet(int index) { + return slots[index] != null; + } + + public void setByte(byte o) { + slots[index] = wrap(ColumnMetaData.Rep.BYTE, o); + } + + public void setChar(char o) { + slots[index] = wrap(ColumnMetaData.Rep.CHARACTER, o); + } + + public void setShort(short o) { + slots[index] = wrap(ColumnMetaData.Rep.SHORT, o); + } + + public void setInt(int o) { + slots[index] = wrap(ColumnMetaData.Rep.INTEGER, o); + } + + public void setLong(long o) { + slots[index] = wrap(ColumnMetaData.Rep.LONG, o); + } + + public void setBoolean(boolean o) { + slots[index] = wrap(ColumnMetaData.Rep.BOOLEAN, o); + } + + public void setRowId(RowId x) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, x); + } + + public void setNString(String o) { + slots[index] = wrap(ColumnMetaData.Rep.STRING, o); + } + + public void setNCharacterStream(Reader value, long length) { + } + + public void setNClob(NClob value) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, value); + } + + public void setClob(Reader reader, long length) { + } + + public void setBlob(InputStream inputStream, long length) { + } + + public void setNClob(Reader reader, long length) { + } + + public void setSQLXML(SQLXML xmlObject) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, xmlObject); + } + + public void setAsciiStream(InputStream x, long length) { + } + + public void setBinaryStream(InputStream x, long length) { + } + + public void setCharacterStream(Reader reader, long length) { + } + + public void setAsciiStream(InputStream x) { + } + + public void setBinaryStream(InputStream x) { + } + + public void setCharacterStream(Reader reader) { + } + + public void setNCharacterStream(Reader value) { + } + + public void setClob(Reader reader) { + } + + public void setBlob(InputStream inputStream) { + } + + public void setNClob(Reader reader) { + } + + public void setUnicodeStream(InputStream x, int length) { + } + + public void setFloat(float x) { + slots[index] = wrap(ColumnMetaData.Rep.FLOAT, x); + } + + public void setDouble(double x) { + slots[index] = wrap(ColumnMetaData.Rep.DOUBLE, x); + } + + public void setBigDecimal(BigDecimal x) { + slots[index] = wrap(ColumnMetaData.Rep.NUMBER, x); + } + + public void setString(String x) { + slots[index] = wrap(ColumnMetaData.Rep.STRING, x); + } + + public void setBytes(byte[] x) { + slots[index] = wrap(ColumnMetaData.Rep.BYTE_STRING, x); + } + + public void setTimestamp(Timestamp x, Calendar calendar) { + slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP, x, calendar); + } + + public void setTime(Time x, Calendar calendar) { + slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_TIME, x, calendar); + } + + public void setDate(Date x, Calendar cal) { + slots[index] = wrap(ColumnMetaData.Rep.JAVA_SQL_DATE, x, calendar); + } + + public void setObject(Object x, int targetSqlType) { + if (x == null || Types.NULL == targetSqlType) { + setNull(targetSqlType); + return; + } + switch (targetSqlType) { + case Types.CLOB: + case Types.DATALINK: + case Types.NCLOB: + case Types.OTHER: + case Types.REF: + case Types.SQLXML: + case Types.STRUCT: + throw notImplemented(); + case Types.ARRAY: + setArray(toArray(x)); + break; + case Types.BIGINT: + setLong(toLong(x)); + break; + case Types.BINARY: + case Types.LONGVARBINARY: + case Types.VARBINARY: + setBytes(toBytes(x)); + break; + case Types.BIT: + case Types.BOOLEAN: + setBoolean(toBoolean(x)); + break; + case Types.BLOB: + if (x instanceof Blob) { + setBlob((Blob) x); + break; + } else if (x instanceof InputStream) { + setBlob((InputStream) x); + } + throw unsupportedCast(x.getClass(), Blob.class); + case Types.DATE: + setDate(toDate(x), calendar); + break; + case Types.DECIMAL: + case Types.NUMERIC: + setBigDecimal(toBigDecimal(x)); + break; + case Types.DISTINCT: + throw notImplemented(); + case Types.DOUBLE: + case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes + setDouble(toDouble(x)); + break; + case Types.INTEGER: + setInt(toInt(x)); + break; + case Types.JAVA_OBJECT: + setObject(x); + break; + case Types.LONGNVARCHAR: + case Types.LONGVARCHAR: + case Types.NVARCHAR: + case Types.VARCHAR: + case Types.CHAR: + case Types.NCHAR: + setString(toString(x)); + break; + case Types.REAL: + setFloat(toFloat(x)); + break; + case Types.ROWID: + if (x instanceof RowId) { + setRowId((RowId) x); + break; + } + throw unsupportedCast(x.getClass(), RowId.class); + case Types.SMALLINT: + setShort(toShort(x)); + break; + case Types.TIME: + setTime(toTime(x), calendar); + break; + case Types.TIMESTAMP: + setTimestamp(toTimestamp(x), calendar); + break; + case Types.TINYINT: + setByte(toByte(x)); + break; + default: + throw notImplemented(); + } + } + + /** Similar logic to {@link #setObject}. */ + public static Object get(Cursor.Accessor accessor, int targetSqlType, + Calendar localCalendar) throws SQLException { + switch (targetSqlType) { + case Types.CLOB: + case Types.DATALINK: + case Types.NCLOB: + case Types.REF: + case Types.SQLXML: + case Types.STRUCT: + throw notImplemented(); + case Types.ARRAY: + return accessor.getArray(); + case Types.BIGINT: + final long aLong = accessor.getLong(); + if (aLong == 0 && accessor.wasNull()) { + return null; + } + return aLong; + case Types.BINARY: + case Types.LONGVARBINARY: + case Types.VARBINARY: + return accessor.getBytes(); + case Types.BIT: + case Types.BOOLEAN: + final boolean aBoolean = accessor.getBoolean(); + if (!aBoolean && accessor.wasNull()) { + return null; + } + return aBoolean; + case Types.BLOB: + return accessor.getBlob(); + case Types.DATE: + return accessor.getDate(localCalendar); + case Types.DECIMAL: + case Types.NUMERIC: + return accessor.getBigDecimal(); + case Types.DISTINCT: + throw notImplemented(); + case Types.DOUBLE: + case Types.FLOAT: // yes really; SQL FLOAT is up to 8 bytes + final double aDouble = accessor.getDouble(); + if (aDouble == 0 && accessor.wasNull()) { + return null; + } + return aDouble; + case Types.INTEGER: + final int anInt = accessor.getInt(); + if (anInt == 0 && accessor.wasNull()) { + return null; + } + return anInt; + case Types.JAVA_OBJECT: + case Types.OTHER: + return accessor.getObject(); + case Types.LONGNVARCHAR: + case Types.LONGVARCHAR: + case Types.NVARCHAR: + case Types.VARCHAR: + case Types.CHAR: + case Types.NCHAR: + return accessor.getString(); + case Types.REAL: + final float aFloat = accessor.getFloat(); + if (aFloat == 0 && accessor.wasNull()) { + return null; + } + return aFloat; + case Types.ROWID: + throw notImplemented(); + case Types.SMALLINT: + final short aShort = accessor.getShort(); + if (aShort == 0 && accessor.wasNull()) { + return null; + } + return aShort; + case Types.TIME: + return accessor.getTime(localCalendar); + case Types.TIMESTAMP: + return accessor.getTimestamp(localCalendar); + case Types.TINYINT: + final byte aByte = accessor.getByte(); + if (aByte == 0 && accessor.wasNull()) { + return null; + } + return aByte; + default: + throw notImplemented(); + } + } + + public void setObject(Object x) { + slots[index] = TypedValue.ofJdbc(x, calendar); + } + + public void setNull(int sqlType) { + slots[index] = wrap(ColumnMetaData.Rep.OBJECT, null); + } + + public void setRef(Ref x) { + } + + public void setBlob(Blob x) { + } + + public void setClob(Clob x) { + } + + public void setArray(Array x) { + } + + public void setNull(int sqlType, String typeName) { + } + + public void setURL(URL x) { + } + + public void setObject(Object x, int targetSqlType, + int scaleOrLength) { + } + + private static RuntimeException unsupportedCast(Class<?> from, Class<?> to) { + return new UnsupportedOperationException("Cannot convert from " + + from.getCanonicalName() + " to " + to.getCanonicalName()); + } + + private static RuntimeException notImplemented() { + return new RuntimeException("not implemented"); + } + + private static Array toArray(Object x) { + if (x instanceof Array) { + return (Array) x; + } + throw unsupportedCast(x.getClass(), Array.class); + } + + public static BigDecimal toBigDecimal(Object x) { + if (x instanceof BigDecimal) { + return (BigDecimal) x; + } else if (x instanceof BigInteger) { + return new BigDecimal((BigInteger) x); + } else if (x instanceof Number) { + if (x instanceof Double || x instanceof Float) { + return new BigDecimal(((Number) x).doubleValue()); + } else { + return new BigDecimal(((Number) x).longValue()); + } + } else if (x instanceof Boolean) { + return (Boolean) x ? BigDecimal.ONE : BigDecimal.ZERO; + } else if (x instanceof String) { + return new BigDecimal((String) x); + } + throw unsupportedCast(x.getClass(), BigDecimal.class); + } + + private static boolean toBoolean(Object x) { + if (x instanceof Boolean) { + return (Boolean) x; + } else if (x instanceof Number) { + return ((Number) x).intValue() != 0; + } else if (x instanceof String) { + String s = (String) x; + if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes")) { + return true; + } else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("no")) { + return false; + } + } + throw unsupportedCast(x.getClass(), Boolean.TYPE); + } + + private static byte toByte(Object x) { + if (x instanceof Number) { + return ((Number) x).byteValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? (byte) 1 : (byte) 0; + } else if (x instanceof String) { + return Byte.parseByte((String) x); + } else { + throw unsupportedCast(x.getClass(), Byte.TYPE); + } + } + + private static byte[] toBytes(Object x) { + if (x instanceof byte[]) { + return (byte[]) x; + } + if (x instanceof String) { + return ((String) x).getBytes(); + } + throw unsupportedCast(x.getClass(), byte[].class); + } + + private static Date toDate(Object x) { + if (x instanceof String) { + return Date.valueOf((String) x); + } + return new Date(toLong(x)); + } + + private static Time toTime(Object x) { + if (x instanceof String) { + return Time.valueOf((String) x); + } + return new Time(toLong(x)); + } + + private static Timestamp toTimestamp(Object x) { + if (x instanceof String) { + return Timestamp.valueOf((String) x); + } + return new Timestamp(toLong(x)); + } + + private static double toDouble(Object x) { + if (x instanceof Number) { + return ((Number) x).doubleValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1D : 0D; + } else if (x instanceof String) { + return Double.parseDouble((String) x); + } else { + throw unsupportedCast(x.getClass(), Double.TYPE); + } + } + + private static float toFloat(Object x) { + if (x instanceof Number) { + return ((Number) x).floatValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1F : 0F; + } else if (x instanceof String) { + return Float.parseFloat((String) x); + } else { + throw unsupportedCast(x.getClass(), Float.TYPE); + } + } + + private static int toInt(Object x) { + if (x instanceof Number) { + return ((Number) x).intValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1 : 0; + } else if (x instanceof String) { + return Integer.parseInt((String) x); + } else { + throw unsupportedCast(x.getClass(), Integer.TYPE); + } + } + + private static long toLong(Object x) { + if (x instanceof Number) { + return ((Number) x).longValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? 1L : 0L; + } else if (x instanceof String) { + return Long.parseLong((String) x); + } else { + throw unsupportedCast(x.getClass(), Long.TYPE); + } + } + + private static short toShort(Object x) { + if (x instanceof Number) { + return ((Number) x).shortValue(); + } else if (x instanceof Boolean) { + return (Boolean) x ? (short) 1 : (short) 0; + } else if (x instanceof String) { + return Short.parseShort((String) x); + } else { + throw unsupportedCast(x.getClass(), Short.TYPE); + } + } + + private static String toString(Object x) { + if (x instanceof String) { + return (String) x; + } else if (x instanceof Character + || x instanceof Boolean) { + return x.toString(); + } + throw unsupportedCast(x.getClass(), String.class); + } + + /** Singleton value to denote parameters that have been set to null (as + * opposed to not set). + * + * <p>Not a valid value for a parameter. + * + * <p>As an enum, it is serializable by Jackson. */ + private enum Dummy { + INSTANCE + } +} + +// End AvaticaSite.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java new file mode 100644 index 0000000..9408a7b --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaSqlException.java @@ -0,0 +1,138 @@ +/* + * 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; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; + +/** + * A client-facing {@link SQLException} which encapsulates errors from the remote Avatica server. + */ +public class AvaticaSqlException extends SQLException { + + private static final long serialVersionUID = 1L; + + private final String errorMessage; + private final List<String> stackTraces; + private final String remoteServer; + + /** + * Construct the Exception with information from the server. + * + * @param errorMessage A human-readable error message. + * @param errorCode An integer corresponding to a known error. + * @param stackTraces Server-side stacktrace. + * @param remoteServer The host:port where the Avatica server is located + */ + public AvaticaSqlException(String errorMessage, String sqlState, int errorCode, + List<String> stackTraces, String remoteServer) { + super("Error " + errorCode + " (" + sqlState + ") : " + errorMessage, sqlState, errorCode); + this.errorMessage = errorMessage; + this.stackTraces = Objects.requireNonNull(stackTraces); + this.remoteServer = remoteServer; + } + + public String getErrorMessage() { + return errorMessage; + } + + /** + * @return The stacktraces for exceptions thrown on the Avatica server. + */ + public List<String> getStackTraces() { + return stackTraces; + } + + /** + * @return The host:port for the remote Avatica server. May be null. + */ + public String getRemoteServer() { + return remoteServer; + } + + // printStackTrace() will get redirected to printStackTrace(PrintStream), don't need to override. + + @Override public void printStackTrace(PrintStream stream) { + super.printStackTrace(stream); + stream.flush(); + printServerStackTrace(new PrintStreamOrWriter(stream)); + } + + @Override public void printStackTrace(PrintWriter writer) { + super.printStackTrace(writer); + writer.flush(); + printServerStackTrace(new PrintStreamOrWriter(writer)); + } + + void printServerStackTrace(PrintStreamOrWriter streamOrWriter) { + for (String serverStackTrace : this.stackTraces) { + streamOrWriter.println(serverStackTrace); + } + } + + /** + * A class that encapsulates either a PrintStream or a PrintWriter. + */ + private static class PrintStreamOrWriter { + /** + * Enumeration to differentiate between a PrintStream and a PrintWriter. + */ + private enum Type { + STREAM, + WRITER + } + + private PrintStream stream; + private PrintWriter writer; + private final Type type; + + public PrintStreamOrWriter(PrintStream stream) { + this.stream = stream; + type = Type.STREAM; + } + + public PrintStreamOrWriter(PrintWriter writer) { + this.writer = writer; + type = Type.WRITER; + } + + /** + * Prints the given string to the the provided stream or writer. + * + * @param string The string to print + */ + public void println(String string) { + switch (type) { + case STREAM: + stream.println(string); + stream.flush(); + return; + case WRITER: + writer.println(string); + writer.flush(); + return; + default: + throw new IllegalStateException(); + } + } + } +} + +// End AvaticaSqlException.java http://git-wip-us.apache.org/repos/asf/calcite/blob/5cee486f/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java ---------------------------------------------------------------------- diff --git a/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java new file mode 100644 index 0000000..cfd1d45 --- /dev/null +++ b/avatica/core/src/main/java/org/apache/calcite/avatica/AvaticaStatement.java @@ -0,0 +1,527 @@ +/* + * 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; + +import org.apache.calcite.avatica.remote.TypedValue; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Implementation of {@link java.sql.Statement} + * for the Avatica engine. + */ +public abstract class AvaticaStatement + implements Statement { + /** The default value for {@link Statement#getFetchSize()}. */ + public static final int DEFAULT_FETCH_SIZE = 100; + + public final AvaticaConnection connection; + /** Statement id; unique within connection. */ + public Meta.StatementHandle handle; + protected boolean closed; + + /** + * Support for {@link #closeOnCompletion()} method. + */ + protected boolean closeOnCompletion; + + /** + * Current result set, or null if the statement is not executing anything. + * Any method which modifies this member must synchronize + * on the AvaticaStatement. + */ + protected AvaticaResultSet openResultSet; + + /** Current update count. Same lifecycle as {@link #openResultSet}. */ + protected long updateCount; + + private int queryTimeoutMillis; + final int resultSetType; + final int resultSetConcurrency; + final int resultSetHoldability; + private int fetchSize = DEFAULT_FETCH_SIZE; + private int fetchDirection; + protected long maxRowCount = 0; + + private Meta.Signature signature; + + protected void setSignature(Meta.Signature signature) { + this.signature = signature; + } + + protected Meta.Signature getSignature() { + return signature; + } + + public Meta.StatementType getStatementType() { + return signature.statementType; + } + + /** + * Creates an AvaticaStatement. + * + * @param connection Connection + * @param h Statement handle + * @param resultSetType Result set type + * @param resultSetConcurrency Result set concurrency + * @param resultSetHoldability Result set holdability + */ + protected AvaticaStatement(AvaticaConnection connection, + Meta.StatementHandle h, int resultSetType, int resultSetConcurrency, + int resultSetHoldability) { + this(connection, h, resultSetType, resultSetConcurrency, resultSetHoldability, null); + } + + protected AvaticaStatement(AvaticaConnection connection, + Meta.StatementHandle h, int resultSetType, int resultSetConcurrency, + int resultSetHoldability, Meta.Signature signature) { + this.connection = Objects.requireNonNull(connection); + this.resultSetType = resultSetType; + this.resultSetConcurrency = resultSetConcurrency; + this.resultSetHoldability = resultSetHoldability; + this.signature = signature; + this.closed = false; + if (h == null) { + final Meta.ConnectionHandle ch = connection.handle; + h = connection.meta.createStatement(ch); + } + connection.statementMap.put(h.id, this); + this.handle = h; + } + + /** Returns the identifier of the statement, unique within its connection. */ + public int getId() { + return handle.id; + } + + private void checkNotPreparedOrCallable(String s) throws SQLException { + if (this instanceof PreparedStatement + || this instanceof CallableStatement) { + throw connection.helper.createException("Cannot call " + s + + " on prepared or callable statement"); + } + } + + protected void executeInternal(String sql) throws SQLException { + // reset previous state before moving forward. + this.updateCount = -1; + try { + // In JDBC, maxRowCount = 0 means no limit; in prepare it means LIMIT 0 + final long maxRowCount1 = maxRowCount <= 0 ? -1 : maxRowCount; + for (int i = 0; i < connection.maxRetriesPerExecute; i++) { + try { + Meta.ExecuteResult x = + connection.prepareAndExecuteInternal(this, sql, maxRowCount1); + return; + } catch (NoSuchStatementException e) { + resetStatement(); + } + } + } catch (RuntimeException e) { + throw connection.helper.createException("Error while executing SQL \"" + sql + "\": " + + e.getMessage(), e); + } + + throw new RuntimeException("Failed to successfully execute query after " + + connection.maxRetriesPerExecute + " attempts."); + } + + protected void resetStatement() { + // Invalidate the old statement + connection.statementMap.remove(handle.id); + // Get a new one + final Meta.ConnectionHandle ch = new Meta.ConnectionHandle(connection.id); + Meta.StatementHandle h = connection.meta.createStatement(ch); + // Cache it in the connection + connection.statementMap.put(h.id, this); + // Update the local state and try again + this.handle = h; + } + + /** + * Re-initialize the ResultSet on the server with the given state. + * @param state The ResultSet's state. + * @param offset Offset into the desired ResultSet + * @return True if the ResultSet has more results, false if there are no more results. + */ + protected boolean syncResults(QueryState state, long offset) throws NoSuchStatementException { + return connection.meta.syncResults(handle, state, offset); + } + + // implement Statement + + public boolean execute(String sql) throws SQLException { + checkNotPreparedOrCallable("execute(String)"); + executeInternal(sql); + // Result set is null for DML or DDL. + // Result set is closed if user cancelled the query. + return openResultSet != null && !openResultSet.isClosed(); + } + + public ResultSet executeQuery(String sql) throws SQLException { + checkNotPreparedOrCallable("executeQuery(String)"); + try { + executeInternal(sql); + if (openResultSet == null) { + throw connection.helper.createException( + "Statement did not return a result set"); + } + return openResultSet; + } catch (RuntimeException e) { + throw connection.helper.createException("Error while executing SQL \"" + sql + "\": " + + e.getMessage(), e); + } + } + + public final int executeUpdate(String sql) throws SQLException { + return (int) executeLargeUpdate(sql); + } + + public long executeLargeUpdate(String sql) throws SQLException { + checkNotPreparedOrCallable("executeUpdate(String)"); + executeInternal(sql); + return updateCount; + } + + public synchronized void close() throws SQLException { + try { + close_(); + } catch (RuntimeException e) { + throw connection.helper.createException("While closing statement", e); + } + } + + protected void close_() { + if (!closed) { + closed = true; + if (openResultSet != null) { + AvaticaResultSet c = openResultSet; + openResultSet = null; + c.close(); + } + try { + // inform the server to close the resource + connection.meta.closeStatement(handle); + } finally { + // make sure we don't leak on our side + connection.statementMap.remove(handle.id); + } + // If onStatementClose throws, this method will throw an exception (later + // converted to SQLException), but this statement still gets closed. + connection.driver.handler.onStatementClose(this); + } + } + + public int getMaxFieldSize() throws SQLException { + throw connection.helper.unsupported(); + } + + public void setMaxFieldSize(int max) throws SQLException { + throw connection.helper.unsupported(); + } + + public final int getMaxRows() { + return (int) getLargeMaxRows(); + } + + public long getLargeMaxRows() { + return maxRowCount; + } + + public final void setMaxRows(int maxRowCount) throws SQLException { + setLargeMaxRows(maxRowCount); + } + + public void setLargeMaxRows(long maxRowCount) throws SQLException { + if (maxRowCount < 0) { + throw connection.helper.createException( + "illegal maxRows value: " + maxRowCount); + } + this.maxRowCount = maxRowCount; + } + + public void setEscapeProcessing(boolean enable) throws SQLException { + throw connection.helper.unsupported(); + } + + public int getQueryTimeout() throws SQLException { + long timeoutSeconds = getQueryTimeoutMillis() / 1000; + if (timeoutSeconds > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + if (timeoutSeconds == 0 && getQueryTimeoutMillis() > 0) { + // Don't return timeout=0 if e.g. timeoutMillis=500. 0 is special. + return 1; + } + return (int) timeoutSeconds; + } + + int getQueryTimeoutMillis() { + return queryTimeoutMillis; + } + + public void setQueryTimeout(int seconds) throws SQLException { + if (seconds < 0) { + throw connection.helper.createException( + "illegal timeout value " + seconds); + } + setQueryTimeoutMillis(seconds * 1000); + } + + void setQueryTimeoutMillis(int millis) { + this.queryTimeoutMillis = millis; + } + + public synchronized void cancel() throws SQLException { + if (openResultSet != null) { + openResultSet.cancel(); + } + } + + public SQLWarning getWarnings() throws SQLException { + return null; // no warnings, since warnings are not supported + } + + public void clearWarnings() throws SQLException { + // no-op since warnings are not supported + } + + public void setCursorName(String name) throws SQLException { + throw connection.helper.unsupported(); + } + + public ResultSet getResultSet() throws SQLException { + // NOTE: result set becomes visible in this member while + // executeQueryInternal is still in progress, and before it has + // finished executing. Its internal state may not be ready for API + // calls. JDBC never claims to be thread-safe! (Except for calls to the + // cancel method.) It is not possible to synchronize, because it would + // block 'cancel'. + return openResultSet; + } + + public int getUpdateCount() throws SQLException { + return (int) updateCount; + } + + public long getLargeUpdateCount() throws SQLException { + return updateCount; + } + + public boolean getMoreResults() throws SQLException { + throw connection.helper.unsupported(); + } + + public void setFetchDirection(int direction) throws SQLException { + this.fetchDirection = direction; + } + + public int getFetchDirection() { + return fetchDirection; + } + + public void setFetchSize(int rows) throws SQLException { + this.fetchSize = rows; + } + + public int getFetchSize() { + return fetchSize; + } + + public int getResultSetConcurrency() throws SQLException { + throw connection.helper.unsupported(); + } + + public int getResultSetType() throws SQLException { + throw connection.helper.unsupported(); + } + + public void addBatch(String sql) throws SQLException { + throw connection.helper.unsupported(); + } + + public void clearBatch() throws SQLException { + throw connection.helper.unsupported(); + } + + public int[] executeBatch() throws SQLException { + throw connection.helper.unsupported(); + } + + public AvaticaConnection getConnection() { + return connection; + } + + public boolean getMoreResults(int current) throws SQLException { + throw connection.helper.unsupported(); + } + + public ResultSet getGeneratedKeys() throws SQLException { + throw connection.helper.unsupported(); + } + + public int executeUpdate( + String sql, int autoGeneratedKeys) throws SQLException { + throw connection.helper.unsupported(); + } + + public int executeUpdate( + String sql, int[] columnIndexes) throws SQLException { + throw connection.helper.unsupported(); + } + + public int executeUpdate( + String sql, String[] columnNames) throws SQLException { + throw connection.helper.unsupported(); + } + + public boolean execute( + String sql, int autoGeneratedKeys) throws SQLException { + throw connection.helper.unsupported(); + } + + public boolean execute( + String sql, int[] columnIndexes) throws SQLException { + throw connection.helper.unsupported(); + } + + public boolean execute( + String sql, String[] columnNames) throws SQLException { + throw connection.helper.unsupported(); + } + + public int getResultSetHoldability() throws SQLException { + throw connection.helper.unsupported(); + } + + public boolean isClosed() throws SQLException { + return closed; + } + + public void setPoolable(boolean poolable) throws SQLException { + throw connection.helper.unsupported(); + } + + public boolean isPoolable() throws SQLException { + throw connection.helper.unsupported(); + } + + // implements java.sql.Statement.closeOnCompletion (added in JDK 1.7) + public void closeOnCompletion() throws SQLException { + closeOnCompletion = true; + } + + // implements java.sql.Statement.isCloseOnCompletion (added in JDK 1.7) + public boolean isCloseOnCompletion() throws SQLException { + return closeOnCompletion; + } + + // implement Wrapper + + public <T> T unwrap(Class<T> iface) throws SQLException { + if (iface.isInstance(this)) { + return iface.cast(this); + } + throw connection.helper.createException( + "does not implement '" + iface + "'"); + } + + public boolean isWrapperFor(Class<?> iface) throws SQLException { + return iface.isInstance(this); + } + + /** + * Executes a prepared statement. + * + * @param signature Parsed statement + * @param isUpdate if the execute is for an update + * + * @return as specified by {@link java.sql.Statement#execute(String)} + * @throws java.sql.SQLException if a database error occurs + */ + protected boolean executeInternal(Meta.Signature signature, boolean isUpdate) + throws SQLException { + ResultSet resultSet = executeQueryInternal(signature, isUpdate); + // user may have cancelled the query + if (resultSet.isClosed()) { + return false; + } + return true; + } + + /** + * Executes a prepared query, closing any previously open result set. + * + * @param signature Parsed query + * @param isUpdate If the execute is for an update + * @return Result set + * @throws java.sql.SQLException if a database error occurs + */ + protected ResultSet executeQueryInternal(Meta.Signature signature, boolean isUpdate) + throws SQLException { + return connection.executeQueryInternal(this, signature, null, null, isUpdate); + } + + /** + * Called by each child result set when it is closed. + * + * @param resultSet Result set or cell set + */ + void onResultSetClose(ResultSet resultSet) { + if (closeOnCompletion) { + close_(); + } + } + + /** Returns the list of values of this statement's parameters. + * + * <p>Called at execute time. Not a public API.</p> + * + * <p>The default implementation returns the empty list, because non-prepared + * statements have no parameters.</p> + * + * @see org.apache.calcite.avatica.AvaticaConnection.Trojan#getParameterValues(AvaticaStatement) + */ + protected List<TypedValue> 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<TypedValue> getBoundParameterValues() throws SQLException { + final List<TypedValue> parameterValues = getParameterValues(); + for (Object parameterValue : parameterValues) { + if (parameterValue == null) { + throw new SQLException("unbound parameter"); + } + } + return parameterValues; + } +} + +// End AvaticaStatement.java
