All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.avatica.util.AbstractCursor Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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 com.hazelcast.com.liance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.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 com.hazelcast.org.apache.calcite.avatica.util;

import com.hazelcast.org.apache.calcite.avatica.AvaticaSite;
import com.hazelcast.org.apache.calcite.avatica.AvaticaUtils;
import com.hazelcast.org.apache.calcite.avatica.ColumnMetaData;

import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
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.SQLDataException;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.sql.Struct;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

/**
 * Base class for implementing a cursor.
 *
 * 

Derived class needs to provide {@link Getter} and can override * {@link com.hazelcast.org.apache.calcite.avatica.util.Cursor.Accessor} implementations if it * wishes.

*/ public abstract class AbstractCursor implements Cursor { /** * Slot into which each accessor should write whether the * value returned was null. */ protected final boolean[] wasNull = {false}; protected AbstractCursor() { } public boolean wasNull() { return wasNull[0]; } public List createAccessors(List types, Calendar localCalendar, ArrayImpl.Factory factory) { List accessors = new ArrayList<>(); for (ColumnMetaData type : types) { accessors.add( createAccessor(type, accessors.size(), localCalendar, factory)); } return accessors; } protected Accessor createAccessor(ColumnMetaData columnMetaData, int ordinal, Calendar localCalendar, ArrayImpl.Factory factory) { // Create an accessor appropriate to the underlying type; the accessor // can convert to any type in the same family. Getter getter = createGetter(ordinal); return createAccessor(columnMetaData, getter, localCalendar, factory); } protected Accessor createAccessor(ColumnMetaData columnMetaData, Getter getter, Calendar localCalendar, ArrayImpl.Factory factory) { switch (columnMetaData.type.rep) { case NUMBER: switch (columnMetaData.type.id) { case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.BIGINT: case Types.REAL: case Types.FLOAT: case Types.DOUBLE: case Types.NUMERIC: case Types.DECIMAL: return new NumberAccessor(getter, columnMetaData.scale); } } switch (columnMetaData.type.id) { case Types.TINYINT: return new ByteAccessor(getter); case Types.SMALLINT: return new ShortAccessor(getter); case Types.INTEGER: return new IntAccessor(getter); case Types.BIGINT: return new LongAccessor(getter); case Types.BOOLEAN: return new BooleanAccessor(getter); case Types.REAL: return new FloatAccessor(getter); case Types.FLOAT: case Types.DOUBLE: return new DoubleAccessor(getter); case Types.DECIMAL: return new BigDecimalAccessor(getter); case Types.CHAR: switch (columnMetaData.type.rep) { case PRIMITIVE_CHAR: case CHARACTER: return new StringFromCharAccessor(getter, columnMetaData.displaySize); default: return new FixedStringAccessor(getter, columnMetaData.displaySize); } case Types.VARCHAR: return new StringAccessor(getter); case Types.BINARY: case Types.VARBINARY: switch (columnMetaData.type.rep) { case STRING: return new BinaryFromStringAccessor(getter); default: return new BinaryAccessor(getter); } case Types.DATE: switch (columnMetaData.type.rep) { case PRIMITIVE_INT: case INTEGER: case NUMBER: return new DateFromNumberAccessor(getter, localCalendar); case JAVA_SQL_DATE: return new DateAccessor(getter); default: throw new AssertionError("bad " + columnMetaData.type.rep); } case Types.TIME: switch (columnMetaData.type.rep) { case PRIMITIVE_INT: case INTEGER: case NUMBER: return new TimeFromNumberAccessor(getter, localCalendar); case JAVA_SQL_TIME: return new TimeAccessor(getter); default: throw new AssertionError("bad " + columnMetaData.type.rep); } case Types.TIMESTAMP: switch (columnMetaData.type.rep) { case PRIMITIVE_LONG: case LONG: case NUMBER: return new TimestampFromNumberAccessor(getter, localCalendar); case JAVA_SQL_TIMESTAMP: return new TimestampAccessor(getter); case JAVA_UTIL_DATE: return new TimestampFromUtilDateAccessor(getter, localCalendar); default: throw new AssertionError("bad " + columnMetaData.type.rep); } case 2013: // TIME_WITH_TIMEZONE switch (columnMetaData.type.rep) { case STRING: return new StringAccessor(getter); default: throw new AssertionError("bad " + columnMetaData.type.rep); } case 2014: // TIMESTAMP_WITH_TIMEZONE switch (columnMetaData.type.rep) { case STRING: return new StringAccessor(getter); default: throw new AssertionError("bad " + columnMetaData.type.rep); } case Types.ARRAY: final ColumnMetaData.ArrayType arrayType = (ColumnMetaData.ArrayType) columnMetaData.type; final SlotGetter com.hazelcast.com.onentGetter = new SlotGetter(); final Accessor com.hazelcast.com.onentAccessor = createAccessor(ColumnMetaData.dummy(arrayType.getComponent(), true), com.hazelcast.com.onentGetter, localCalendar, factory); return new ArrayAccessor(getter, arrayType.getComponent(), com.hazelcast.com.onentAccessor, com.hazelcast.com.onentGetter, factory); case Types.STRUCT: switch (columnMetaData.type.rep) { case OBJECT: final ColumnMetaData.StructType structType = (ColumnMetaData.StructType) columnMetaData.type; List accessors = new ArrayList<>(); for (ColumnMetaData column : structType.columns) { accessors.add( createAccessor(column, new StructGetter(getter, column), localCalendar, factory)); } return new StructAccessor(getter, accessors); default: throw new AssertionError("bad " + columnMetaData.type.rep); } case Types.JAVA_OBJECT: case Types.OTHER: // e.g. map if (columnMetaData.type.getName().startsWith("INTERVAL_")) { int end = columnMetaData.type.getName().indexOf("("); if (end < 0) { end = columnMetaData.type.getName().length(); } TimeUnitRange range = TimeUnitRange.valueOf( columnMetaData.type.getName().substring("INTERVAL_".length(), end)); if (range.monthly()) { return new IntervalYearMonthAccessor(getter, range); } else { return new IntervalDayTimeAccessor(getter, range, columnMetaData.scale); } } return new ObjectAccessor(getter); default: throw new RuntimeException("unknown type " + columnMetaData.type.id); } } protected abstract Getter createGetter(int ordinal); public abstract boolean next(); /** Accesses a timestamp value as a string. * The timestamp is in SQL format (e.g. "2013-09-22 22:30:32"), * not Java format ("2013-09-22 22:30:32.123"). */ private static String timestampAsString(long v, Calendar calendar) { if (calendar != null) { v -= calendar.getTimeZone().getOffset(v); } return DateTimeUtils.unixTimestampToString(v); } /** Accesses a date value as a string, e.g. "2013-09-22". */ private static String dateAsString(int v, Calendar calendar) { AvaticaUtils.discard(calendar); // time zone shift doesn't make sense return DateTimeUtils.unixDateToString(v); } /** Accesses a time value as a string, e.g. "22:30:32". */ private static String timeAsString(int v, Calendar calendar) { if (calendar != null) { v -= calendar.getTimeZone().getOffset(v); } return DateTimeUtils.unixTimeToString(v); } private static Date longToDate(long v, Calendar calendar) { if (calendar != null) { v -= calendar.getTimeZone().getOffset(v); } return new Date(v); } static Time intToTime(int v, Calendar calendar) { if (calendar != null) { v -= calendar.getTimeZone().getOffset(v); } return new Time(v); } static Timestamp longToTimestamp(long v, Calendar calendar) { if (calendar != null) { v -= calendar.getTimeZone().getOffset(v); } return new Timestamp(v); } /** Implementation of {@link Cursor.Accessor}. */ static class AccessorImpl implements Accessor { protected final Getter getter; AccessorImpl(Getter getter) { assert getter != null; this.getter = getter; } public boolean wasNull() throws SQLException { return getter.wasNull(); } public String getString() throws SQLException { final Object o = getObject(); return o == null ? null : o.toString(); } public boolean getBoolean() throws SQLException { return getLong() != 0L; } public byte getByte() throws SQLException { return (byte) getLong(); } public short getShort() throws SQLException { return (short) getLong(); } public int getInt() throws SQLException { return (int) getLong(); } public long getLong() throws SQLException { throw cannotConvert("long"); } public float getFloat() throws SQLException { return (float) getDouble(); } public double getDouble() throws SQLException { throw cannotConvert("double"); } public BigDecimal getBigDecimal() throws SQLException { throw cannotConvert("BigDecimal"); } public BigDecimal getBigDecimal(int scale) throws SQLException { throw cannotConvert("BigDecimal with scale"); } public byte[] getBytes() throws SQLException { throw cannotConvert("byte[]"); } public InputStream getAsciiStream() throws SQLException { throw cannotConvert("InputStream (ascii)"); } public InputStream getUnicodeStream() throws SQLException { throw cannotConvert("InputStream (unicode)"); } public InputStream getBinaryStream() throws SQLException { throw cannotConvert("InputStream (binary)"); } public Object getObject() throws SQLException { return getter.getObject(); } public Reader getCharacterStream() throws SQLException { throw cannotConvert("Reader"); } private SQLException cannotConvert(String targetType) throws SQLException { return new SQLDataException("cannot convert to " + targetType + " (" + this + ")"); } public Object getObject(Map> map) throws SQLException { throw cannotConvert("Object (with map)"); } public Ref getRef() throws SQLException { throw cannotConvert("Ref"); } public Blob getBlob() throws SQLException { throw cannotConvert("Blob"); } public Clob getClob() throws SQLException { throw cannotConvert("Clob"); } public Array getArray() throws SQLException { throw cannotConvert("Array"); } public Struct getStruct() throws SQLException { throw cannotConvert("Struct"); } public Date getDate(Calendar calendar) throws SQLException { throw cannotConvert("Date"); } public Time getTime(Calendar calendar) throws SQLException { throw cannotConvert("Time"); } public Timestamp getTimestamp(Calendar calendar) throws SQLException { throw cannotConvert("Timestamp"); } public URL getURL() throws SQLException { throw cannotConvert("URL"); } public NClob getNClob() throws SQLException { throw cannotConvert("NClob"); } public SQLXML getSQLXML() throws SQLException { throw cannotConvert("SQLXML"); } public String getNString() throws SQLException { throw cannotConvert("NString"); } public Reader getNCharacterStream() throws SQLException { throw cannotConvert("NCharacterStream"); } public T getObject(Class type) throws SQLException { throw cannotConvert("Object (with type)"); } } /** * Accessor of exact numeric values. The subclass must implement the * {@link #getLong()} method. */ private abstract static class ExactNumericAccessor extends AccessorImpl { private ExactNumericAccessor(Getter getter) { super(getter); } public BigDecimal getBigDecimal(int scale) throws SQLException { final long v = getLong(); if (v == 0 && getter.wasNull()) { return null; } return BigDecimal.valueOf(v).setScale(scale, RoundingMode.DOWN); } public BigDecimal getBigDecimal() throws SQLException { final long val = getLong(); if (val == 0 && getter.wasNull()) { return null; } return BigDecimal.valueOf(val); } public double getDouble() throws SQLException { return getLong(); } public float getFloat() throws SQLException { return getLong(); } public abstract long getLong() throws SQLException; } /** * Accessor that assumes that the underlying value is a {@link Boolean}; * corresponds to {@link java.sql.Types#BOOLEAN}. */ private static class BooleanAccessor extends ExactNumericAccessor { private BooleanAccessor(Getter getter) { super(getter); } public boolean getBoolean() throws SQLException { Boolean o = (Boolean) getObject(); return o != null && o; } public long getLong() throws SQLException { return getBoolean() ? 1 : 0; } } /** * Accessor that assumes that the underlying value is a {@link Byte}; * corresponds to {@link java.sql.Types#TINYINT}. */ private static class ByteAccessor extends ExactNumericAccessor { private ByteAccessor(Getter getter) { super(getter); } public byte getByte() throws SQLException { Object obj = getObject(); if (null == obj) { return 0; } else if (obj instanceof Integer) { return ((Integer) obj).byteValue(); } return (Byte) obj; } public long getLong() throws SQLException { return getByte(); } } /** * Accessor that assumes that the underlying value is a {@link Short}; * corresponds to {@link java.sql.Types#SMALLINT}. */ private static class ShortAccessor extends ExactNumericAccessor { private ShortAccessor(Getter getter) { super(getter); } public short getShort() throws SQLException { Object obj = getObject(); if (null == obj) { return 0; } else if (obj instanceof Integer) { return ((Integer) obj).shortValue(); } return (Short) obj; } public long getLong() throws SQLException { return getShort(); } } /** * Accessor that assumes that the underlying value is an {@link Integer}; * corresponds to {@link java.sql.Types#INTEGER}. */ private static class IntAccessor extends ExactNumericAccessor { private IntAccessor(Getter getter) { super(getter); } public int getInt() throws SQLException { Integer o = (Integer) super.getObject(); return o == null ? 0 : o; } public long getLong() throws SQLException { return getInt(); } } /** * Accessor that assumes that the underlying value is a {@link Long}; * corresponds to {@link java.sql.Types#BIGINT}. */ private static class LongAccessor extends ExactNumericAccessor { private LongAccessor(Getter getter) { super(getter); } public long getLong() throws SQLException { Long o = (Long) super.getObject(); return o == null ? 0 : o; } } /** * Accessor of values that are {@link Double} or null. */ private abstract static class ApproximateNumericAccessor extends AccessorImpl { private ApproximateNumericAccessor(Getter getter) { super(getter); } public BigDecimal getBigDecimal(int scale) throws SQLException { final double v = getDouble(); if (v == 0d && getter.wasNull()) { return null; } return BigDecimal.valueOf(v).setScale(scale, RoundingMode.DOWN); } public BigDecimal getBigDecimal() throws SQLException { final double v = getDouble(); if (v == 0 && getter.wasNull()) { return null; } return BigDecimal.valueOf(v); } public abstract double getDouble() throws SQLException; public long getLong() throws SQLException { return (long) getDouble(); } } /** * Accessor that assumes that the underlying value is a {@link Float}; * corresponds to {@link java.sql.Types#FLOAT}. */ private static class FloatAccessor extends ApproximateNumericAccessor { private FloatAccessor(Getter getter) { super(getter); } public float getFloat() throws SQLException { Float o = (Float) getObject(); return o == null ? 0f : o; } public double getDouble() throws SQLException { return getFloat(); } } /** * Accessor that assumes that the underlying value is a {@link Double}; * corresponds to {@link java.sql.Types#DOUBLE}. */ private static class DoubleAccessor extends ApproximateNumericAccessor { private DoubleAccessor(Getter getter) { super(getter); } public double getDouble() throws SQLException { Object obj = getObject(); if (null == obj) { return 0d; } else if (obj instanceof BigDecimal) { return ((BigDecimal) obj).doubleValue(); } return (Double) obj; } } /** * Accessor of exact numeric values. The subclass must implement the * {@link #getLong()} method. */ private abstract static class BigNumberAccessor extends AccessorImpl { private BigNumberAccessor(Getter getter) { super(getter); } protected abstract Number getNumber() throws SQLException; public double getDouble() throws SQLException { Number number = getNumber(); return number == null ? 0d : number.doubleValue(); } public float getFloat() throws SQLException { Number number = getNumber(); return number == null ? 0f : number.floatValue(); } public long getLong() throws SQLException { Number number = getNumber(); return number == null ? 0L : number.longValue(); } public int getInt() throws SQLException { Number number = getNumber(); return number == null ? 0 : number.intValue(); } public short getShort() throws SQLException { Number number = getNumber(); return number == null ? 0 : number.shortValue(); } public byte getByte() throws SQLException { Number number = getNumber(); return number == null ? 0 : number.byteValue(); } public boolean getBoolean() throws SQLException { Number number = getNumber(); return number != null && number.doubleValue() != 0; } } /** * Accessor that assumes that the underlying value is a {@link BigDecimal}; * corresponds to {@link java.sql.Types#DECIMAL}. */ private static class BigDecimalAccessor extends BigNumberAccessor { private BigDecimalAccessor(Getter getter) { super(getter); } protected Number getNumber() throws SQLException { return (Number) getObject(); } public BigDecimal getBigDecimal(int scale) throws SQLException { return (BigDecimal) getObject(); } public BigDecimal getBigDecimal() throws SQLException { return (BigDecimal) getObject(); } } /** * Accessor that assumes that the underlying value is a {@link Number}; * corresponds to {@link java.sql.Types#NUMERIC}. * *

This is useful when numbers have been translated over JSON. JSON * converts a 0L (0 long) value to the string "0" and back to 0 (0 int). * So you cannot be sure that the source and target type are the same. */ static class NumberAccessor extends BigNumberAccessor { private final int scale; NumberAccessor(Getter getter, int scale) { super(getter); this.scale = scale; } protected Number getNumber() throws SQLException { return (Number) super.getObject(); } public BigDecimal getBigDecimal(int scale) throws SQLException { Number n = getNumber(); if (n == null) { return null; } BigDecimal decimal = AvaticaSite.toBigDecimal(n); if (0 != scale) { return decimal.setScale(scale, RoundingMode.UNNECESSARY); } return decimal; } public BigDecimal getBigDecimal() throws SQLException { return getBigDecimal(scale); } } /** * Accessor that assumes that the underlying value is a {@link String}; * corresponds to {@link java.sql.Types#CHAR} * and {@link java.sql.Types#VARCHAR}. */ private static class StringAccessor extends AccessorImpl { private StringAccessor(Getter getter) { super(getter); } public String getString() throws SQLException { final Object obj = getObject(); if (obj instanceof String) { return (String) obj; } return null == obj ? null : obj.toString(); } @Override public byte[] getBytes() throws SQLException { return super.getBytes(); } } /** * Accessor that assumes that the underlying value is a {@link String}; * corresponds to {@link java.sql.Types#CHAR}. */ private static class FixedStringAccessor extends StringAccessor { protected final Spacer spacer; private FixedStringAccessor(Getter getter, int length) { super(getter); this.spacer = new Spacer(length); } public String getString() throws SQLException { String s = super.getString(); if (s == null) { return null; } return spacer.padRight(s); } } /** * Accessor that assumes that the underlying value is a {@link String}; * corresponds to {@link java.sql.Types#CHAR}. */ private static class StringFromCharAccessor extends FixedStringAccessor { private StringFromCharAccessor(Getter getter, int length) { super(getter, length); } public String getString() throws SQLException { Character s = (Character) super.getObject(); if (s == null) { return null; } return spacer.padRight(s.toString()); } } /** * Accessor that assumes that the underlying value is an array of * {@link com.hazelcast.org.apache.calcite.avatica.util.ByteString} values; * corresponds to {@link java.sql.Types#BINARY} * and {@link java.sql.Types#VARBINARY}. */ private static class BinaryAccessor extends AccessorImpl { private BinaryAccessor(Getter getter) { super(getter); } //FIXME: Protobuf gets byte[] @Override public byte[] getBytes() throws SQLException { Object obj = getObject(); if (null == obj) { return null; } if (obj instanceof ByteString) { return ((ByteString) obj).getBytes(); } else if (obj instanceof String) { // Need to unwind the base64 for JSON return ByteString.parseBase64((String) obj); } else if (obj instanceof byte[]) { // Protobuf would have a byte array return (byte[]) obj; } else { throw new RuntimeException("Cannot handle " + obj.getClass() + " as bytes"); } } @Override public String getString() throws SQLException { Object o = getObject(); if (null == o) { return null; } if (o instanceof byte[]) { return new String((byte[]) o, StandardCharsets.UTF_8); } else if (o instanceof ByteString) { return ((ByteString) o).toString(); } throw new IllegalStateException("Unhandled value type: " + o.getClass()); } } /** * Accessor that assumes that the underlying value is a {@link String}, * encoding {@link java.sql.Types#BINARY} * and {@link java.sql.Types#VARBINARY} values in Base64 format. */ private static class BinaryFromStringAccessor extends StringAccessor { private BinaryFromStringAccessor(Getter getter) { super(getter); } @Override public Object getObject() throws SQLException { return super.getObject(); } @Override public byte[] getBytes() throws SQLException { // JSON sends this as a base64-enc string, protobuf can do binary. Object obj = getObject(); if (obj instanceof byte[]) { // If we already have bytes, just send them back. return (byte[]) obj; } return getBase64Decoded(); } private byte[] getBase64Decoded() throws SQLException { final String string = super.getString(); if (null == string) { return null; } // Need to base64 decode the string. return ByteString.parseBase64(string); } @Override public String getString() throws SQLException { final byte[] bytes = getBase64Decoded(); if (null == bytes) { return null; } // Need to base64 decode the string. return new String(bytes, StandardCharsets.UTF_8); } } /** * Accessor that assumes that the underlying value is a DATE, * in its default representation {@code int}; * corresponds to {@link java.sql.Types#DATE}. */ private static class DateFromNumberAccessor extends NumberAccessor { private final Calendar localCalendar; private DateFromNumberAccessor(Getter getter, Calendar localCalendar) { super(getter, 0); this.localCalendar = localCalendar; } @Override public Object getObject() throws SQLException { return getDate(localCalendar); } @Override public Date getDate(Calendar calendar) throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return longToDate(v.longValue() * DateTimeUtils.MILLIS_PER_DAY, calendar); } @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return longToTimestamp(v.longValue() * DateTimeUtils.MILLIS_PER_DAY, calendar); } @Override public String getString() throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return dateAsString(v.intValue(), null); } } /** * Accessor that assumes that the underlying value is a Time, * in its default representation {@code int}; * corresponds to {@link java.sql.Types#TIME}. */ private static class TimeFromNumberAccessor extends NumberAccessor { private final Calendar localCalendar; private TimeFromNumberAccessor(Getter getter, Calendar localCalendar) { super(getter, 0); this.localCalendar = localCalendar; } @Override public Object getObject() throws SQLException { return getTime(localCalendar); } @Override public Time getTime(Calendar calendar) throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return intToTime(v.intValue(), calendar); } @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return longToTimestamp(v.longValue(), calendar); } @Override public String getString() throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return timeAsString(v.intValue(), null); } } /** * Accessor that assumes that the underlying value is a TIMESTAMP, * in its default representation {@code long}; * corresponds to {@link java.sql.Types#TIMESTAMP}. */ private static class TimestampFromNumberAccessor extends NumberAccessor { private final Calendar localCalendar; private TimestampFromNumberAccessor(Getter getter, Calendar localCalendar) { super(getter, 0); this.localCalendar = localCalendar; } @Override public Object getObject() throws SQLException { return getTimestamp(localCalendar); } @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return longToTimestamp(v.longValue(), calendar); } @Override public Date getDate(Calendar calendar) throws SQLException { final Timestamp timestamp = getTimestamp(calendar); if (timestamp == null) { return null; } return new Date(timestamp.getTime()); } @Override public Time getTime(Calendar calendar) throws SQLException { final Timestamp timestamp = getTimestamp(calendar); if (timestamp == null) { return null; } return new Time( DateTimeUtils.floorMod(timestamp.getTime(), DateTimeUtils.MILLIS_PER_DAY)); } @Override public String getString() throws SQLException { final Number v = getNumber(); if (v == null) { return null; } return timestampAsString(v.longValue(), null); } } /** * Accessor that assumes that the underlying value is a DATE, * represented as a java.sql.Date; * corresponds to {@link java.sql.Types#DATE}. */ private static class DateAccessor extends ObjectAccessor { private DateAccessor(Getter getter) { super(getter); } @Override public Date getDate(Calendar calendar) throws SQLException { java.sql.Date date = (Date) getObject(); if (date == null) { return null; } if (calendar != null) { long v = date.getTime(); v -= calendar.getTimeZone().getOffset(v); date = new Date(v); } return date; } @Override public String getString() throws SQLException { final int v = getInt(); if (v == 0 && wasNull()) { return null; } return dateAsString(v, null); } @Override public long getLong() throws SQLException { Date date = getDate(null); return date == null ? 0L : (date.getTime() / DateTimeUtils.MILLIS_PER_DAY); } } /** * Accessor that assumes that the underlying value is a TIME, * represented as a java.sql.Time; * corresponds to {@link java.sql.Types#TIME}. */ private static class TimeAccessor extends ObjectAccessor { private TimeAccessor(Getter getter) { super(getter); } @Override public Time getTime(Calendar calendar) throws SQLException { Time date = (Time) getObject(); if (date == null) { return null; } if (calendar != null) { long v = date.getTime(); v -= calendar.getTimeZone().getOffset(v); date = new Time(v); } return date; } @Override public String getString() throws SQLException { final int v = getInt(); if (v == 0 && wasNull()) { return null; } return timeAsString(v, null); } @Override public long getLong() throws SQLException { Time time = getTime(null); return time == null ? 0L : (time.getTime() % DateTimeUtils.MILLIS_PER_DAY); } } /** * Accessor that assumes that the underlying value is a TIMESTAMP, * represented as a java.sql.Timestamp; * corresponds to {@link java.sql.Types#TIMESTAMP}. */ private static class TimestampAccessor extends ObjectAccessor { private TimestampAccessor(Getter getter) { super(getter); } @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { Timestamp timestamp = (Timestamp) getObject(); if (timestamp == null) { return null; } if (calendar != null) { long v = timestamp.getTime(); v -= calendar.getTimeZone().getOffset(v); timestamp = new Timestamp(v); } return timestamp; } @Override public Date getDate(Calendar calendar) throws SQLException { final Timestamp timestamp = getTimestamp(calendar); if (timestamp == null) { return null; } return new Date(timestamp.getTime()); } @Override public Time getTime(Calendar calendar) throws SQLException { final Timestamp timestamp = getTimestamp(calendar); if (timestamp == null) { return null; } return new Time( DateTimeUtils.floorMod(timestamp.getTime(), DateTimeUtils.MILLIS_PER_DAY)); } @Override public String getString() throws SQLException { final long v = getLong(); if (v == 0 && wasNull()) { return null; } return timestampAsString(v, null); } @Override public long getLong() throws SQLException { Timestamp timestamp = getTimestamp(null); return timestamp == null ? 0 : timestamp.getTime(); } } /** * Accessor that assumes that the underlying value is a TIMESTAMP, * represented as a java.util.Date; * corresponds to {@link java.sql.Types#TIMESTAMP}. */ private static class TimestampFromUtilDateAccessor extends ObjectAccessor { private final Calendar localCalendar; private TimestampFromUtilDateAccessor(Getter getter, Calendar localCalendar) { super(getter); this.localCalendar = localCalendar; } @Override public Timestamp getTimestamp(Calendar calendar) throws SQLException { java.util.Date date = (java.util.Date) getObject(); if (date == null) { return null; } long v = date.getTime(); if (calendar != null) { v -= calendar.getTimeZone().getOffset(v); } return new Timestamp(v); } @Override public Date getDate(Calendar calendar) throws SQLException { final Timestamp timestamp = getTimestamp(calendar); if (timestamp == null) { return null; } return new Date(timestamp.getTime()); } @Override public Time getTime(Calendar calendar) throws SQLException { final Timestamp timestamp = getTimestamp(calendar); if (timestamp == null) { return null; } return new Time( DateTimeUtils.floorMod(timestamp.getTime(), DateTimeUtils.MILLIS_PER_DAY)); } @Override public String getString() throws SQLException { java.util.Date date = (java.util.Date) getObject(); if (date == null) { return null; } return timestampAsString(date.getTime(), null); } @Override public long getLong() throws SQLException { Timestamp timestamp = getTimestamp(localCalendar); return timestamp == null ? 0 : timestamp.getTime(); } } /** * Accessor that assumes that the underlying value is a {@code int}; * corresponds to {@link java.sql.Types#OTHER}. */ private static class IntervalYearMonthAccessor extends IntAccessor { private final TimeUnitRange range; private IntervalYearMonthAccessor(Getter getter, TimeUnitRange range) { super(getter); this.range = range; } @Override public String getString() throws SQLException { final int v = getInt(); if (v == 0 && wasNull()) { return null; } return DateTimeUtils.intervalYearMonthToString(v, range); } } /** * Accessor that assumes that the underlying value is a {@code long}; * corresponds to {@link java.sql.Types#OTHER}. */ private static class IntervalDayTimeAccessor extends LongAccessor { private final TimeUnitRange range; private final int scale; private IntervalDayTimeAccessor(Getter getter, TimeUnitRange range, int scale) { super(getter); this.range = range; this.scale = scale; } @Override public String getString() throws SQLException { final long v = getLong(); if (v == 0 && wasNull()) { return null; } return DateTimeUtils.intervalDayTimeToString(v, range, scale); } } /** * Accessor that assumes that the underlying value is an ARRAY; * corresponds to {@link java.sql.Types#ARRAY}. */ public static class ArrayAccessor extends AccessorImpl { final ColumnMetaData.AvaticaType com.hazelcast.com.onentType; final Accessor com.hazelcast.com.onentAccessor; final SlotGetter com.hazelcast.com.onentSlotGetter; final ArrayImpl.Factory factory; public ArrayAccessor(Getter getter, ColumnMetaData.AvaticaType com.hazelcast.com.onentType, Accessor com.hazelcast.com.onentAccessor, SlotGetter com.hazelcast.com.onentSlotGetter, ArrayImpl.Factory factory) { super(getter); this.com.hazelcast.com.onentType = com.hazelcast.com.onentType; this.com.hazelcast.com.onentAccessor = com.hazelcast.com.onentAccessor; this.com.hazelcast.com.onentSlotGetter = com.hazelcast.com.onentSlotGetter; this.factory = factory; } @Override public Object getObject() throws SQLException { final Object object = super.getObject(); if (object == null || object instanceof ArrayImpl) { return object; } else if (object instanceof List) { List list = (List) object; // Run the array values through the com.hazelcast.com.onent accessor List convertedValues = new ArrayList<>(list.size()); for (Object val : list) { if (null == val) { convertedValues.add(null); } else { // Set the current value in the SlotGetter so we can use the Accessor to coerce it. com.hazelcast.com.onentSlotGetter.slot = val; convertedValues.add(convertValue()); } } return convertedValues; } // The object can be java array in case of user-provided class for row storage. return AvaticaUtils.primitiveList(object); } private Object convertValue() throws SQLException { switch (com.hazelcast.com.onentType.id) { case Types.BOOLEAN: case Types.BIT: return com.hazelcast.com.onentAccessor.getBoolean(); case Types.TINYINT: return com.hazelcast.com.onentAccessor.getByte(); case Types.SMALLINT: return com.hazelcast.com.onentAccessor.getShort(); case Types.INTEGER: return com.hazelcast.com.onentAccessor.getInt(); case Types.BIGINT: return com.hazelcast.com.onentAccessor.getLong(); case Types.FLOAT: return com.hazelcast.com.onentAccessor.getFloat(); case Types.DOUBLE: return com.hazelcast.com.onentAccessor.getDouble(); case Types.ARRAY: return com.hazelcast.com.onentAccessor.getArray(); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.NCHAR: case Types.LONGNVARCHAR: return com.hazelcast.com.onentAccessor.getString(); case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: return com.hazelcast.com.onentAccessor.getBytes(); case Types.DECIMAL: return com.hazelcast.com.onentAccessor.getBigDecimal(); case Types.DATE: case Types.TIME: case Types.TIMESTAMP: case Types.STRUCT: case Types.JAVA_OBJECT: return com.hazelcast.com.onentAccessor.getObject(); default: throw new IllegalStateException("Unhandled ARRAY com.hazelcast.com.onent type: " + com.hazelcast.com.onentType.rep + ", id: " + com.hazelcast.com.onentType.id); } } @SuppressWarnings("unchecked") @Override public Array getArray() throws SQLException { final Object o = getObject(); if (o == null) { return null; } if (o instanceof ArrayImpl) { return (ArrayImpl) o; } // If it's not an Array already, assume it is a List. return new ArrayImpl((List) o, this); } @Override public String getString() throws SQLException { final Array array = getArray(); return array == null ? null : array.toString(); } } /** * Accessor that assumes that the underlying value is a STRUCT; * corresponds to {@link java.sql.Types#STRUCT}. */ private static class StructAccessor extends AccessorImpl { private final List fieldAccessors; private StructAccessor(Getter getter, List fieldAccessors) { super(getter); this.fieldAccessors = fieldAccessors; } @Override public Object getObject() throws SQLException { return getStruct(); } @SuppressWarnings("unchecked") @Override public T getObject(Class clz) throws SQLException { // getStruct() is not exposed on Accessor, only AccessorImpl. getObject(Class) is exposed, // so we can make it do the right thing (call getStruct()). if (clz.equals(Struct.class)) { return (T) getStruct(); } return super.getObject(clz); } @Override public Struct getStruct() throws SQLException { final Object o = super.getObject(); if (o == null) { return null; } else if (o instanceof StructImpl) { return (StructImpl) o; } else if (o instanceof List) { return new StructImpl((List) o); } else { final List list = new ArrayList<>(); for (Accessor fieldAccessor : fieldAccessors) { list.add(fieldAccessor.getObject()); } return new StructImpl(list); } } } /** * Accessor that assumes that the underlying value is an OBJECT; * corresponds to {@link java.sql.Types#JAVA_OBJECT}. */ private static class ObjectAccessor extends AccessorImpl { private ObjectAccessor(Getter getter) { super(getter); } } /** Gets a value from a particular field of the current record of this * cursor. */ protected interface Getter { Object getObject() throws SQLException; boolean wasNull() throws SQLException; } /** Abstract implementation of {@link Getter}. */ protected abstract class AbstractGetter implements Getter { public boolean wasNull() throws SQLException { return wasNull[0]; } } /** Implementation of {@link Getter} that returns the current contents of * a mutable slot. */ public class SlotGetter implements Getter { public Object slot; public Object getObject() throws SQLException { return slot; } public boolean wasNull() throws SQLException { return slot == null; } } /** Implementation of {@link Getter} that returns the value of a given field * of the current contents of another getter. */ public class StructGetter implements Getter { public final Getter getter; private final ColumnMetaData columnMetaData; public StructGetter(Getter getter, ColumnMetaData columnMetaData) { this.getter = getter; this.columnMetaData = columnMetaData; } public Object getObject() throws SQLException { try { final Object o = getter.getObject(); if (o instanceof Object[]) { Object[] objects = (Object[]) o; return objects[columnMetaData.ordinal]; } final Field field = o.getClass().getField(columnMetaData.label); return field.get(getter.getObject()); } catch (IllegalAccessException | NoSuchFieldException e) { throw new SQLException(e); } } public boolean wasNull() throws SQLException { return getObject() == null; } } } // End AbstractCursor.java