blob: e70aa87c0a6913133fefa34d25c9511ebdcd439b [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.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.nio.charset.StandardCharsets;
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 calendar) {
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:
throw notImplemented();
case Types.STRUCT:
return accessor.getStruct();
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.NULL:
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) {
slots[index] = wrap(ColumnMetaData.Rep.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(StandardCharsets.UTF_8);
}
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