blob: abb0603422196f616d0ba4750699912d33dfc430 [file] [log] [blame]
/*
* 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