| /* |
| * 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.Meta.Signature; |
| import org.apache.calcite.avatica.remote.TypedValue; |
| |
| 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.ParameterMetaData; |
| import java.sql.PreparedStatement; |
| import java.sql.Ref; |
| import java.sql.ResultSet; |
| import java.sql.ResultSetMetaData; |
| import java.sql.RowId; |
| import java.sql.SQLException; |
| import java.sql.SQLXML; |
| import java.sql.Time; |
| import java.sql.Timestamp; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.List; |
| import java.util.Locale; |
| |
| /** |
| * Implementation of {@link java.sql.PreparedStatement} |
| * for the Avatica engine. |
| * |
| * <p>This class has sub-classes which implement JDBC 3.0 and JDBC 4.0 APIs; |
| * it is instantiated using {@link AvaticaFactory#newPreparedStatement}.</p> |
| */ |
| public abstract class AvaticaPreparedStatement |
| extends AvaticaStatement |
| implements PreparedStatement, ParameterMetaData { |
| private final ResultSetMetaData resultSetMetaData; |
| private Calendar calendar; |
| protected final TypedValue[] slots; |
| protected final List<List<TypedValue>> parameterValueBatch; |
| |
| /** |
| * Creates an AvaticaPreparedStatement. |
| * |
| * @param connection Connection |
| * @param h Statement handle |
| * @param signature Result of preparing statement |
| * @param resultSetType Result set type |
| * @param resultSetConcurrency Result set concurrency |
| * @param resultSetHoldability Result set holdability |
| * @throws SQLException If fails due to underlying implementation reasons. |
| */ |
| protected AvaticaPreparedStatement(AvaticaConnection connection, |
| Meta.StatementHandle h, |
| Meta.Signature signature, |
| int resultSetType, |
| int resultSetConcurrency, |
| int resultSetHoldability) throws SQLException { |
| super(connection, h, resultSetType, resultSetConcurrency, |
| resultSetHoldability, signature); |
| this.slots = new TypedValue[signature.parameters.size()]; |
| this.resultSetMetaData = |
| connection.factory.newResultSetMetaData(this, signature); |
| this.parameterValueBatch = new ArrayList<>(); |
| } |
| |
| @Override protected List<TypedValue> getParameterValues() { |
| return Arrays.asList(slots); |
| } |
| |
| /** Returns a copy of the current parameter values. |
| * @return A copied list of the parameter values |
| */ |
| protected List<TypedValue> copyParameterValues() { |
| // For implementing batch update, we need to make a copy of slots, not just a thin reference |
| // to it as as list. Otherwise, subsequent setFoo(..) calls will alter the underlying array |
| // and modify our cached TypedValue list. |
| List<TypedValue> copy = new ArrayList<>(slots.length); |
| for (TypedValue value : slots) { |
| copy.add(value); |
| } |
| return copy; |
| } |
| |
| /** Returns a calendar in the connection's time zone, creating one the first |
| * time this method is called. |
| * |
| * <p>Uses the calendar to offset date-time values when calling methods such |
| * as {@link #setDate(int, Date)}. |
| * |
| * <p>A note on thread-safety. This method does not strictly need to be |
| * {@code synchronized}, because JDBC does not promise thread safety if |
| * different threads are accessing the same statement, or even different |
| * objects within a particular connection. |
| * |
| * <p>The calendar returned is to be used only within this statement, and |
| * JDBC only allows access to a statement from within one thread, so |
| * therefore does not need to be synchronized when accessed. |
| */ |
| protected synchronized Calendar getCalendar() { |
| if (calendar == null) { |
| calendar = Calendar.getInstance(connection.getTimeZone(), Locale.ROOT); |
| } |
| return calendar; |
| } |
| |
| protected List<List<TypedValue>> getParameterValueBatch() { |
| return this.parameterValueBatch; |
| } |
| |
| // implement PreparedStatement |
| |
| public ResultSet executeQuery() throws SQLException { |
| checkOpen(); |
| this.updateCount = -1; |
| final Signature sig = getSignature(); |
| return getConnection().executeQueryInternal(this, sig, null, |
| new QueryState(sig.sql), false); |
| } |
| |
| public ParameterMetaData getParameterMetaData() throws SQLException { |
| checkOpen(); |
| return this; |
| } |
| |
| public final int executeUpdate() throws SQLException { |
| return AvaticaUtils.toSaturatedInt(executeLargeUpdate()); |
| } |
| |
| public long executeLargeUpdate() throws SQLException { |
| checkOpen(); |
| getConnection().executeQueryInternal(this, null, null, |
| new QueryState(getSignature().sql), true); |
| return updateCount; |
| } |
| |
| public void setNull(int parameterIndex, int sqlType) throws SQLException { |
| getSite(parameterIndex).setNull(sqlType); |
| } |
| |
| public void setBoolean(int parameterIndex, boolean x) throws SQLException { |
| getSite(parameterIndex).setBoolean(x); |
| } |
| |
| public void setByte(int parameterIndex, byte x) throws SQLException { |
| getSite(parameterIndex).setByte(x); |
| } |
| |
| public void setShort(int parameterIndex, short x) throws SQLException { |
| getSite(parameterIndex).setShort(x); |
| } |
| |
| public void setInt(int parameterIndex, int x) throws SQLException { |
| getSite(parameterIndex).setInt(x); |
| } |
| |
| public void setLong(int parameterIndex, long x) throws SQLException { |
| getSite(parameterIndex).setLong(x); |
| } |
| |
| public void setFloat(int parameterIndex, float x) throws SQLException { |
| getSite(parameterIndex).setFloat(x); |
| } |
| |
| public void setDouble(int parameterIndex, double x) throws SQLException { |
| getSite(parameterIndex).setDouble(x); |
| } |
| |
| public void setBigDecimal(int parameterIndex, BigDecimal x) |
| throws SQLException { |
| getSite(parameterIndex).setBigDecimal(x); |
| } |
| |
| public void setString(int parameterIndex, String x) throws SQLException { |
| getSite(parameterIndex).setString(x); |
| } |
| |
| public void setBytes(int parameterIndex, byte[] x) throws SQLException { |
| getSite(parameterIndex).setBytes(x); |
| } |
| |
| public void setAsciiStream(int parameterIndex, InputStream x, int length) |
| throws SQLException { |
| getSite(parameterIndex).setAsciiStream(x, length); |
| } |
| |
| @SuppressWarnings("deprecation") |
| public void setUnicodeStream(int parameterIndex, InputStream x, int length) |
| throws SQLException { |
| getSite(parameterIndex).setUnicodeStream(x, length); |
| } |
| |
| public void setBinaryStream(int parameterIndex, InputStream x, int length) |
| throws SQLException { |
| getSite(parameterIndex).setBinaryStream(x, length); |
| } |
| |
| public void clearParameters() throws SQLException { |
| checkOpen(); |
| for (int i = 0; i < slots.length; i++) { |
| slots[i] = null; |
| } |
| } |
| |
| public void setObject(int parameterIndex, Object x, int targetSqlType) |
| throws SQLException { |
| getSite(parameterIndex).setObject(x, targetSqlType); |
| } |
| |
| public void setObject(int parameterIndex, Object x) throws SQLException { |
| getSite(parameterIndex).setObject(x); |
| } |
| |
| public boolean execute() throws SQLException { |
| checkOpen(); |
| this.updateCount = -1; |
| // We don't know if this is actually an update or a query, so call it a query so we pass the |
| // Signature to the server. |
| getConnection().executeQueryInternal(this, getSignature(), null, |
| new QueryState(getSignature().sql), false); |
| // Result set is null for DML or DDL. |
| // Result set is closed if user cancelled the query. |
| return openResultSet != null && !openResultSet.isClosed(); |
| } |
| |
| public void addBatch() throws SQLException { |
| checkOpen(); |
| // Need to copy the parameterValues into a new list, not wrap the array in a list |
| // as getParameterValues does. |
| this.parameterValueBatch.add(copyParameterValues()); |
| } |
| |
| @Override public void clearBatch() throws SQLException { |
| checkOpen(); |
| this.parameterValueBatch.clear(); |
| } |
| |
| @Override public int[] executeBatch() throws SQLException { |
| return AvaticaUtils.toSaturatedInts(executeLargeBatch()); |
| } |
| |
| public long[] executeLargeBatch() throws SQLException { |
| checkOpen(); |
| // Overriding the implementation in AvaticaStatement. |
| try { |
| return getConnection().executeBatchUpdateInternal(this); |
| } finally { |
| // If we failed to send this batch, that's a problem for the user to handle, not us. |
| // Make sure we always clear the statements we collected to submit in one RPC. |
| this.parameterValueBatch.clear(); |
| } |
| } |
| |
| public void setCharacterStream(int parameterIndex, Reader reader, int length) |
| throws SQLException { |
| getSite(parameterIndex).setCharacterStream(reader, length); |
| } |
| |
| public void setRef(int parameterIndex, Ref x) throws SQLException { |
| getSite(parameterIndex).setRef(x); |
| } |
| |
| public void setBlob(int parameterIndex, Blob x) throws SQLException { |
| getSite(parameterIndex).setBlob(x); |
| } |
| |
| public void setClob(int parameterIndex, Clob x) throws SQLException { |
| getSite(parameterIndex).setClob(x); |
| } |
| |
| public void setArray(int parameterIndex, Array x) throws SQLException { |
| getSite(parameterIndex).setArray(x); |
| } |
| |
| public ResultSetMetaData getMetaData() throws SQLException { |
| checkOpen(); |
| return resultSetMetaData; |
| } |
| |
| public void setDate(int parameterIndex, Date x, Calendar calendar) |
| throws SQLException { |
| getSite(parameterIndex).setDate(x, calendar); |
| } |
| |
| public void setDate(int parameterIndex, Date x) throws SQLException { |
| setDate(parameterIndex, x, getCalendar()); |
| } |
| |
| public void setTime(int parameterIndex, Time x, Calendar calendar) |
| throws SQLException { |
| getSite(parameterIndex).setTime(x, calendar); |
| } |
| |
| public void setTime(int parameterIndex, Time x) throws SQLException { |
| setTime(parameterIndex, x, getCalendar()); |
| } |
| |
| public void setTimestamp(int parameterIndex, Timestamp x, Calendar calendar) |
| throws SQLException { |
| getSite(parameterIndex).setTimestamp(x, calendar); |
| } |
| |
| public void setTimestamp(int parameterIndex, Timestamp x) |
| throws SQLException { |
| setTimestamp(parameterIndex, x, getCalendar()); |
| } |
| |
| public void setNull(int parameterIndex, int sqlType, String typeName) |
| throws SQLException { |
| getSite(parameterIndex).setNull(sqlType, typeName); |
| } |
| |
| public void setURL(int parameterIndex, URL x) throws SQLException { |
| getSite(parameterIndex).setURL(x); |
| } |
| |
| public void setObject(int parameterIndex, Object x, int targetSqlType, |
| int scaleOrLength) throws SQLException { |
| getSite(parameterIndex).setObject(x, targetSqlType, scaleOrLength); |
| } |
| |
| public void setRowId(int parameterIndex, RowId x) throws SQLException { |
| getSite(parameterIndex).setRowId(x); |
| } |
| |
| public void setNString(int parameterIndex, String value) throws SQLException { |
| getSite(parameterIndex).setNString(value); |
| } |
| |
| public void setNCharacterStream(int parameterIndex, Reader value, long length) |
| throws SQLException { |
| getSite(parameterIndex).setNCharacterStream(value, length); |
| } |
| |
| public void setNClob(int parameterIndex, NClob value) throws SQLException { |
| getSite(parameterIndex).setNClob(value); |
| } |
| |
| public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { |
| getSite(parameterIndex).setClob(reader, length); |
| } |
| |
| public void setBlob(int parameterIndex, InputStream inputStream, long length) |
| throws SQLException { |
| getSite(parameterIndex).setBlob(inputStream, length); |
| } |
| |
| public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { |
| getSite(parameterIndex).setNClob(reader, length); |
| } |
| |
| public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { |
| getSite(parameterIndex).setSQLXML(xmlObject); |
| } |
| |
| public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { |
| getSite(parameterIndex).setAsciiStream(x, length); |
| } |
| |
| public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { |
| getSite(parameterIndex).setBinaryStream(x, length); |
| } |
| |
| public void setCharacterStream(int parameterIndex, Reader reader, long length) |
| throws SQLException { |
| getSite(parameterIndex).setCharacterStream(reader, length); |
| } |
| |
| public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { |
| getSite(parameterIndex).setAsciiStream(x); |
| } |
| |
| public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { |
| getSite(parameterIndex).setBinaryStream(x); |
| } |
| |
| public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { |
| getSite(parameterIndex).setCharacterStream(reader); |
| } |
| |
| public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { |
| getSite(parameterIndex).setNCharacterStream(value); |
| } |
| |
| public void setClob(int parameterIndex, Reader reader) throws SQLException { |
| getSite(parameterIndex).setClob(reader); |
| } |
| |
| public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { |
| getSite(parameterIndex).setBlob(inputStream); |
| } |
| |
| public void setNClob(int parameterIndex, Reader reader) throws SQLException { |
| getSite(parameterIndex).setNClob(reader); |
| } |
| |
| // implement ParameterMetaData |
| |
| |
| |
| protected AvaticaParameter getParameter(int param) throws SQLException { |
| try { |
| return getSignature().parameters.get(param - 1); |
| } catch (IndexOutOfBoundsException e) { |
| //noinspection ThrowableResultOfMethodCallIgnored |
| throw AvaticaConnection.HELPER.toSQLException( |
| AvaticaConnection.HELPER.createException( |
| "parameter ordinal " + param + " out of range")); |
| } |
| } |
| |
| protected AvaticaSite getSite(int param) throws SQLException { |
| checkOpen(); |
| final AvaticaParameter parameter = getParameter(param); |
| return new AvaticaSite(parameter, getCalendar(), param - 1, slots); |
| } |
| |
| public int getParameterCount() { |
| return getSignature().parameters.size(); |
| } |
| |
| public int isNullable(int param) throws SQLException { |
| return ParameterMetaData.parameterNullableUnknown; |
| } |
| |
| public boolean isSigned(int index) throws SQLException { |
| return getParameter(index).signed; |
| } |
| |
| public int getPrecision(int index) throws SQLException { |
| return getParameter(index).precision; |
| } |
| |
| public int getScale(int index) throws SQLException { |
| return getParameter(index).scale; |
| } |
| |
| public int getParameterType(int index) throws SQLException { |
| return getParameter(index).parameterType; |
| } |
| |
| public String getParameterTypeName(int index) throws SQLException { |
| return getParameter(index).typeName; |
| } |
| |
| public String getParameterClassName(int index) throws SQLException { |
| return getParameter(index).className; |
| } |
| |
| public int getParameterMode(int param) throws SQLException { |
| //noinspection UnusedDeclaration |
| AvaticaParameter paramDef = getParameter(param); // forces param range check |
| return ParameterMetaData.parameterModeIn; |
| } |
| } |
| |
| // End AvaticaPreparedStatement.java |