/* * Copyright 2014 The Board of Trustees of The Leland Stanford Junior University. * * Licensed 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 com.github.susom.database; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Timestamp; import java.time.LocalDate; import java.util.Date; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * Safely wrap a ResultSet and provide access to the data it contains. * * @author garricko */ class RowsAdaptor implements Rows { private final ResultSet rs; private final Options options; private int column = 1; public RowsAdaptor(ResultSet rs, Options options) { this.rs = rs; this.options = options; } @Override public boolean next() { try { column = 1; return rs.next(); } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public String[] getColumnLabels() { try { ResultSetMetaData metaData = rs.getMetaData(); String[] names = new String[metaData.getColumnCount()]; for (int i = 0; i < names.length; i++) { names[i] = metaData.getColumnLabel(i + 1); } return names; } catch (SQLException e) { throw new DatabaseException("Unable to retrieve metadata from ResultSet", e); } } @Nonnull @Override public ResultSetMetaData getMetadata() { try { return rs.getMetaData(); } catch (SQLException e) { throw new DatabaseException("Unable to retrieve metadata from ResultSet", e); } } @Nullable @Override public Boolean getBooleanOrNull() { return getBooleanOrNull(column++); } @Nullable @Override public Boolean getBooleanOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toBoolean(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Nullable @Override public Boolean getBooleanOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toBoolean(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public boolean getBooleanOrFalse() { return getBooleanOrFalse(column++); } @Override public boolean getBooleanOrFalse(int columnOneBased) { Boolean result = getBooleanOrNull(columnOneBased); if (result == null) { result = Boolean.FALSE; } return result; } @Override public boolean getBooleanOrFalse(String columnName) { Boolean result = getBooleanOrNull(columnName); if (result == null) { result = Boolean.FALSE; } return result; } @Override public boolean getBooleanOrTrue() { return getBooleanOrTrue(column++); } @Override public boolean getBooleanOrTrue(int columnOneBased) { Boolean result = getBooleanOrNull(columnOneBased); if (result == null) { result = Boolean.TRUE; } return result; } @Override public boolean getBooleanOrTrue(String columnName) { Boolean result = getBooleanOrNull(columnName); if (result == null) { result = Boolean.TRUE; } return result; } @Nullable @Override public Integer getIntegerOrNull() { return getIntegerOrNull(column++); } @Override public Integer getIntegerOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toInteger(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public Integer getIntegerOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toInteger(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public int getIntegerOrZero() { return getIntegerOrZero(column++); } @Override public int getIntegerOrZero(int columnOneBased) { Integer result = getIntegerOrNull(columnOneBased); if (result == null) { result = 0; } return result; } @Override public int getIntegerOrZero(String columnName) { Integer result = getIntegerOrNull(columnName); if (result == null) { result = 0; } return result; } @Nullable @Override public Long getLongOrNull() { return getLongOrNull(column++); } @Override public Long getLongOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toLong(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public Long getLongOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toLong(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public long getLongOrZero() { return getLongOrZero(column++); } @Override public long getLongOrZero(int columnOneBased) { Long result = getLongOrNull(columnOneBased); if (result == null) { result = 0L; } return result; } @Override public long getLongOrZero(String columnName) { Long result = getLongOrNull(columnName); if (result == null) { result = 0L; } return result; } @Nullable @Override public Float getFloatOrNull() { return getFloatOrNull(column++); } @Override public Float getFloatOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toFloat(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public Float getFloatOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toFloat(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public float getFloatOrZero() { return getFloatOrZero(column++); } @Override public float getFloatOrZero(int columnOneBased) { Float result = getFloatOrNull(columnOneBased); if (result == null) { result = 0f; } return result; } @Override public float getFloatOrZero(String columnName) { Float result = getFloatOrNull(columnName); if (result == null) { result = 0f; } return result; } @Nullable @Override public Double getDoubleOrNull() { return getDoubleOrNull(column++); } @Override public Double getDoubleOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toDouble(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public Double getDoubleOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toDouble(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public double getDoubleOrZero() { return getDoubleOrZero(column++); } @Override public double getDoubleOrZero(int columnOneBased) { Double result = getDoubleOrNull(columnOneBased); if (result == null) { result = 0d; } return result; } @Override public double getDoubleOrZero(String columnName) { Double result = getDoubleOrNull(columnName); if (result == null) { result = 0d; } return result; } @Nullable @Override public BigDecimal getBigDecimalOrNull() { return getBigDecimalOrNull(column++); } @Override public BigDecimal getBigDecimalOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toBigDecimal(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public BigDecimal getBigDecimalOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toBigDecimal(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public BigDecimal getBigDecimalOrZero() { return getBigDecimalOrZero(column++); } @Nonnull @Override public BigDecimal getBigDecimalOrZero(int columnOneBased) { BigDecimal result = getBigDecimalOrNull(columnOneBased); if (result == null) { result = BigDecimal.ZERO; } return result; } @Nonnull @Override public BigDecimal getBigDecimalOrZero(String columnName) { BigDecimal result = getBigDecimalOrNull(columnName); if (result == null) { result = BigDecimal.ZERO; } return result; } @Nullable @Override public String getStringOrNull() { return getStringOrNull(column++); } @Override public String getStringOrNull(int columnOneBased) { try { column = columnOneBased + 1; String result = rs.getString(columnOneBased); if (result != null && result.length() == 0) { result = null; } return result; } catch (SQLException e) { throw new DatabaseException(e); } } @Override public String getStringOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; String result = rs.getString(columnName); if (result != null && result.length() == 0) { result = null; } return result; } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public String getStringOrEmpty() { return getStringOrEmpty(column++); } @Nonnull @Override public String getStringOrEmpty(int columnOneBased) { String result = getStringOrNull(columnOneBased); if (result == null) { result = ""; } return result; } @Nonnull @Override public String getStringOrEmpty(String columnName) { String result = getStringOrNull(columnName); if (result == null) { result = ""; } return result; } @Nullable @Override public String getClobStringOrNull() { return getClobStringOrNull(column++); } @Override public String getClobStringOrNull(int columnOneBased) { try { column = columnOneBased + 1; String result = rs.getString(columnOneBased); if (result != null && result.length() == 0) { result = null; } return result; } catch (SQLException e) { throw new DatabaseException(e); } } @Override public String getClobStringOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; String result = rs.getString(columnName); if (result != null && result.length() == 0) { result = null; } return result; } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public String getClobStringOrEmpty() { return getClobStringOrEmpty(column++); } @Nonnull @Override public String getClobStringOrEmpty(int columnOneBased) { String result = getClobStringOrNull(columnOneBased); if (result == null) { result = ""; } return result; } @Nonnull @Override public String getClobStringOrEmpty(String columnName) { String result = getClobStringOrNull(columnName); if (result == null) { result = ""; } return result; } @Nullable @Override public Reader getClobReaderOrNull() { return getClobReaderOrNull(column++); } @Override public Reader getClobReaderOrNull(int columnOneBased) { try { column = columnOneBased + 1; return rs.getCharacterStream(columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public Reader getClobReaderOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return rs.getCharacterStream(columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public Reader getClobReaderOrEmpty() { return getClobReaderOrEmpty(column++); } @Nonnull @Override public Reader getClobReaderOrEmpty(int columnOneBased) { Reader result = getClobReaderOrNull(columnOneBased); if (result == null) { result = new StringReader(""); } return result; } @Nonnull @Override public Reader getClobReaderOrEmpty(String columnName) { Reader result = getClobReaderOrNull(columnName); if (result == null) { result = new StringReader(""); } return result; } @Nullable @Override public byte[] getBlobBytesOrNull() { return getBlobBytesOrNull(column++); } @Override public byte[] getBlobBytesOrNull(int columnOneBased) { try { column = columnOneBased + 1; return rs.getBytes(columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public byte[] getBlobBytesOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return rs.getBytes(columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public byte[] getBlobBytesOrZeroLen() { return getBlobBytesOrZeroLen(column++); } @Nonnull @Override public byte[] getBlobBytesOrZeroLen(int columnOneBased) { byte[] result = getBlobBytesOrNull(columnOneBased); if (result == null) { result = new byte[0]; } return result; } @Nonnull @Override public byte[] getBlobBytesOrZeroLen(String columnName) { byte[] result = getBlobBytesOrNull(columnName); if (result == null) { result = new byte[0]; } return result; } @Nullable @Override public InputStream getBlobInputStreamOrNull() { return getBlobInputStreamOrNull(column++); } @Override public InputStream getBlobInputStreamOrNull(int columnOneBased) { try { column = columnOneBased + 1; return rs.getBinaryStream(columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Override public InputStream getBlobInputStreamOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return rs.getBinaryStream(columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Nonnull @Override public InputStream getBlobInputStreamOrEmpty() { return getBlobInputStreamOrEmpty(column++); } @Nonnull @Override public InputStream getBlobInputStreamOrEmpty(int columnOneBased) { InputStream result = getBlobInputStreamOrNull(columnOneBased); if (result == null) { result = new ByteArrayInputStream(new byte[0]); } return result; } @Nonnull @Override public InputStream getBlobInputStreamOrEmpty(String columnName) { InputStream result = getBlobInputStreamOrNull(columnName); if (result == null) { result = new ByteArrayInputStream(new byte[0]); } return result; } @Nullable @Override public Date getDateOrNull() { return getDateOrNull(column++); } @Nullable @Override public Date getDateOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toDate(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Nullable @Override public Date getDateOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toDate(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } @Nullable @Override public LocalDate getLocalDateOrNull() { return getLocalDateOrNull(column++); } @Nullable @Override public LocalDate getLocalDateOrNull(int columnOneBased) { try { column = columnOneBased + 1; return toLocalDate(rs, columnOneBased); } catch (SQLException e) { throw new DatabaseException(e); } } @Nullable @Override public LocalDate getLocalDateOrNull(String columnName) { try { column = rs.findColumn(columnName) + 1; return toLocalDate(rs, columnName); } catch (SQLException e) { throw new DatabaseException(e); } } /** * Make sure the Timestamp will return getTime() accurate to the millisecond * (if possible) and truncate away nanoseconds. */ private Date timestampToDate(Timestamp ts) { long millis = ts.getTime(); int nanos = ts.getNanos(); return new Date(millis / 1000 * 1000 + nanos / 1000000); } private Date toDate(ResultSet rs, int col) throws SQLException { Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps()); return val == null ? null : timestampToDate(val); } private Date toDate(ResultSet rs, String col) throws SQLException { Timestamp val = rs.getTimestamp(col, options.calendarForTimestamps()); return val == null ? null : timestampToDate(val); } private LocalDate toLocalDate(ResultSet rs, int col) throws SQLException { java.sql.Date val = rs.getDate(col); return val == null ? null : val.toLocalDate(); } private LocalDate toLocalDate(ResultSet rs, String col) throws SQLException { java.sql.Date val = rs.getDate(col); return val == null ? null : val.toLocalDate(); } private Boolean toBoolean(ResultSet rs, int col) throws SQLException { String val = rs.getString(col); if (val == null) { return null; } else if (val.equals("Y") || val.equals("1")) { return Boolean.TRUE; } else if (val.equals("N") || val.equals("0")) { return Boolean.FALSE; } else { throw new DatabaseException("Reading boolean from column " + col + " but the value was not 'Y' or 'N'"); } } private Boolean toBoolean(ResultSet rs, String col) throws SQLException { String val = rs.getString(col); if (val == null) { return null; } else if (val.equals("Y") || val.equals("1")) { return Boolean.TRUE; } else if (val.equals("N") || val.equals("0")) { return Boolean.FALSE; } else { throw new DatabaseException("Reading boolean from column \"" + col + "\" but the value was not 'Y' or 'N'"); } } private Integer toInteger(ResultSet rs, int col) throws SQLException { int val = rs.getInt(col); return rs.wasNull() ? null : val; } private Integer toInteger(ResultSet rs, String col) throws SQLException { int val = rs.getInt(col); return rs.wasNull() ? null : val; } private Long toLong(ResultSet rs, int col) throws SQLException { long val = rs.getLong(col); return rs.wasNull() ? null : val; } private Long toLong(ResultSet rs, String col) throws SQLException { long val = rs.getLong(col); return rs.wasNull() ? null : val; } private Float toFloat(ResultSet rs, int col) throws SQLException { float val = rs.getFloat(col); return rs.wasNull() ? null : val; } private Float toFloat(ResultSet rs, String col) throws SQLException { float val = rs.getFloat(col); return rs.wasNull() ? null : val; } private Double toDouble(ResultSet rs, int col) throws SQLException { double val = rs.getDouble(col); return rs.wasNull() ? null : val; } private Double toDouble(ResultSet rs, String col) throws SQLException { double val = rs.getDouble(col); return rs.wasNull() ? null : val; } private BigDecimal fixBigDecimal(BigDecimal val) { if (val.scale() > 0) { val = val.stripTrailingZeros(); if (val.scale() < 0) { val = val.setScale(0); } } return val; } private BigDecimal toBigDecimal(ResultSet rs, int col) throws SQLException { BigDecimal val = rs.getBigDecimal(col); return val == null ? null : fixBigDecimal(val); } private BigDecimal toBigDecimal(ResultSet rs, String col) throws SQLException { BigDecimal val = rs.getBigDecimal(col); return val == null ? null : fixBigDecimal(val); } }