diff --git a/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java b/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java index db50908a2..b65632fc8 100644 --- a/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java +++ b/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java @@ -104,6 +104,11 @@ public class SelectResultSet implements ResultSet { private static final DateTimeFormatter TEXT_LOCAL_DATE_TIME; private static final DateTimeFormatter TEXT_OFFSET_DATE_TIME; private static final DateTimeFormatter TEXT_ZONED_DATE_TIME; + + private static final int BIT_LAST_FIELD_NOT_NULL = 0b000000; + private static final int BIT_LAST_FIELD_NULL = 0b000001; + private static final int BIT_LAST_ZERO_DATE = 0b000010; + public static final int TINYINT1_IS_BIT = 1; public static final int YEAR_IS_DATE_TYPE = 2; private static final ColumnInformation[] INSERT_ID_COLUMNS; @@ -156,8 +161,7 @@ public class SelectResultSet implements ResultSet { private int resultSetScrollType; private int rowPointer; private ColumnNameMap columnNameMap; - private boolean lastGetWasNull; - private boolean lastValueNull; + private int lastValueNull; private int lastRowPointer = -1; private int dataTypeMappingFlags; private boolean returnTableAlias; @@ -666,7 +670,11 @@ private void checkObjectRange(int position) throws SQLException { row.resetRow(data[rowPointer]); lastRowPointer = rowPointer; } - this.lastValueNull = row.setPosition(position - 1); + this.lastValueNull = row.setPosition(position - 1) ? BIT_LAST_FIELD_NULL : BIT_LAST_FIELD_NOT_NULL; + } + + private boolean lastValueWasNull() { + return (lastValueNull & BIT_LAST_FIELD_NULL) != 0; } @Override @@ -953,7 +961,8 @@ public void setStatement(MariaDbStatement statement) { * {inheritDoc}. */ public boolean wasNull() throws SQLException { - return lastValueNull || lastGetWasNull; + return (lastValueNull & BIT_LAST_FIELD_NULL) != 0 + || (lastValueNull & BIT_LAST_ZERO_DATE) != 0; } /** @@ -969,7 +978,7 @@ public InputStream getAsciiStream(String columnLabel) throws SQLException { */ public InputStream getAsciiStream(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return new ByteArrayInputStream(new String(row.buf, row.pos, row.getLengthMaxFieldSize(), StandardCharsets.UTF_8).getBytes()); } @@ -993,7 +1002,7 @@ private String getInternalString(ColumnInformation columnInfo) throws SQLExcepti } private String getInternalString(ColumnInformation columnInfo, Calendar cal) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; switch (columnInfo.getColumnType()) { case STRING: @@ -1041,7 +1050,12 @@ private String getInternalString(ColumnInformation columnInfo, Calendar cal) thr case DATE: if (isBinaryEncoded) { Date date = getInternalDate(columnInfo, cal); - return (date == null) ? null : date.toString(); + if (date == null) { + //specific for "zero-date", getString will return "zero-date" value -> wasNull() must then return false + lastValueNull ^= BIT_LAST_ZERO_DATE; + return new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8); + } + return date.toString(); } break; case YEAR: @@ -1057,10 +1071,9 @@ private String getInternalString(ColumnInformation columnInfo, Calendar cal) thr case DATETIME: Timestamp timestamp = getInternalTimestamp(columnInfo, cal); if (timestamp == null) { - if (!lastValueNull && !this.isBinaryEncoded) { - return new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8); - } - return null; + //specific for "zero-date", getString will return "zero-date" value -> wasNull() must then return false + lastValueNull ^= BIT_LAST_ZERO_DATE; + return new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8); } return timestamp.toString(); case DECIMAL: @@ -1097,7 +1110,7 @@ private String zeroFillingIfNeeded(String value, ColumnInformation columnInforma */ public InputStream getBinaryStream(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return new ByteArrayInputStream(row.buf, row.pos, row.getLengthMaxFieldSize()); } @@ -1131,7 +1144,7 @@ public int getInt(String columnLabel) throws SQLException { * @return int */ private int getInternalInt(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; if (!this.isBinaryEncoded) { return parseInt(columnInfo); @@ -1199,7 +1212,7 @@ public long getLong(int columnIndex) throws SQLException { * @throws SQLException if any error occur */ private long getInternalLong(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; if (!this.isBinaryEncoded) { return parseLong(columnInfo); @@ -1287,7 +1300,7 @@ public float getFloat(int columnIndex) throws SQLException { */ @SuppressWarnings("UnnecessaryInitCause") private float getInternalFloat(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; if (!this.isBinaryEncoded) { switch (columnInfo.getColumnType()) { @@ -1412,7 +1425,7 @@ public double getDouble(int columnIndex) throws SQLException { * @throws SQLException id any error occur */ private double getInternalDouble(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; if (!this.isBinaryEncoded) { switch (columnInfo.getColumnType()) { case BIT: @@ -1545,7 +1558,7 @@ public BigDecimal getBigDecimal(String columnLabel) throws SQLException { * @throws SQLException id any error occur */ private BigDecimal getInternalBigDecimal(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (!this.isBinaryEncoded) { return new BigDecimal(new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8)); @@ -1602,7 +1615,7 @@ public byte[] getBytes(String columnLabel) throws SQLException { */ public byte[] getBytes(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; byte[] data = new byte[row.getLengthMaxFieldSize()]; System.arraycopy(row.buf, row.pos, data, 0, row.getLengthMaxFieldSize()); return data; @@ -1648,15 +1661,10 @@ public Date getDate(String columnLabel, Calendar cal) throws SQLException { * @throws SQLException if raw data cannot be parse */ private Date getInternalDate(ColumnInformation columnInfo, Calendar cal) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (!this.isBinaryEncoded) { String rawValue = new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8); - if ("0000-00-00".equals(rawValue)) { - lastGetWasNull = true; - return null; - } - switch (columnInfo.getColumnType()) { case TIMESTAMP: case DATETIME: @@ -1668,6 +1676,11 @@ private Date getInternalDate(ColumnInformation columnInfo, Calendar cal) throws throw new SQLException("Cannot read DATE using a Types.TIME field"); case DATE: + if ("0000-00-00".equals(rawValue)) { + lastValueNull |= BIT_LAST_ZERO_DATE; + return null; + } + return new Date( Integer.parseInt(rawValue.substring(0, 4)) - 1900, Integer.parseInt(rawValue.substring(5, 7)) - 1, @@ -1745,7 +1758,7 @@ public Time getTime(String columnLabel, Calendar cal) throws SQLException { * @throws SQLException if raw data cannot be parse */ private Time getInternalTime(ColumnInformation columnInfo, Calendar cal) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (!this.isBinaryEncoded) { if (columnInfo.getColumnType() == ColumnType.TIMESTAMP || columnInfo.getColumnType() == ColumnType.DATETIME) { @@ -1758,10 +1771,6 @@ private Time getInternalTime(ColumnInformation columnInfo, Calendar cal) throws } else { String raw = new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8); - if ("0000-00-00".equals(raw)) { - lastGetWasNull = true; - return null; - } if (!options.useLegacyDatetimeCode && (raw.startsWith("-") || raw.split(":").length != 3 || raw.indexOf(":") > 3)) { throw new SQLException("Time format \"" + raw + "\" incorrect, must be HH:mm:ss"); } @@ -1834,12 +1843,12 @@ public Timestamp getTimestamp(int columnIndex) throws SQLException { */ @SuppressWarnings("ConstantConditions") private Timestamp getInternalTimestamp(ColumnInformation columnInfo, Calendar userCalendar) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (!this.isBinaryEncoded) { String rawValue = new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8); if (rawValue.startsWith("0000-00-00 00:00:00")) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_ZERO_DATE; return null; } @@ -1910,7 +1919,7 @@ public InputStream getUnicodeStream(String columnLabel) throws SQLException { */ public InputStream getUnicodeStream(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return new ByteArrayInputStream(new String(row.buf, row.pos, row.getLengthMaxFieldSize(), StandardCharsets.UTF_8).getBytes()); } @@ -1973,27 +1982,27 @@ public T getObject(int columnIndex, Class type) throws SQLException { return (T) getInternalString(col, null); } else if (type.equals(Integer.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) (Integer) getInternalInt(col); } else if (type.equals(Long.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) (Long) getInternalLong(col); } else if (type.equals(Short.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) (Short) getInternalShort(col); } else if (type.equals(Double.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) (Double) getInternalDouble(col); } else if (type.equals(Float.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) (Float) getInternalFloat(col); } else if (type.equals(Byte.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) (Byte) getInternalByte(col); } else if (type.equals(byte[].class)) { @@ -2021,14 +2030,14 @@ public T getObject(int columnIndex, Class type) throws SQLException { return type.cast(calendar); } else if (type.equals(Clob.class) || type.equals(NClob.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; //TODO rewrite Blob to use buffer directly (using offset + length) byte[] data = new byte[row.getLengthMaxFieldSize()]; System.arraycopy(row.buf, row.pos, data, 0, row.getLengthMaxFieldSize()); return (T) new MariaDbClob(data); } else if (type.equals(InputStream.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return (T) new ByteArrayInputStream(row.buf, row.pos, row.getLengthMaxFieldSize()); } else if (type.equals(Reader.class)) { @@ -2045,33 +2054,33 @@ public T getObject(int columnIndex, Class type) throws SQLException { return (T) getInternalBigDecimal(col); } else if (type.equals(LocalDateTime.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; ZonedDateTime zonedDateTime = getZonedDateTime(col, LocalDateTime.class); return zonedDateTime == null ? null : type.cast(zonedDateTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()); } else if (type.equals(ZonedDateTime.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return type.cast(getZonedDateTime(col, ZonedDateTime.class)); } else if (type.equals(OffsetDateTime.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; ZonedDateTime tmpZonedDateTime = getZonedDateTime(col, OffsetDateTime.class); return tmpZonedDateTime == null ? null : type.cast(tmpZonedDateTime.toOffsetDateTime()); } else if (type.equals(OffsetDateTime.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return type.cast(getLocalDate(col)); } else if (type.equals(LocalDate.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return type.cast(getLocalDate(col)); } else if (type.equals(LocalTime.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return type.cast(getLocalTime(col)); } else if (type.equals(OffsetTime.class)) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; return type.cast(getOffsetTime(col)); } @@ -2094,7 +2103,7 @@ public T getObject(String columnLabel, Class type) throws SQLException { */ private Object getInternalObject(ColumnInformation columnInfo, int dataTypeMappingFlags) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; switch (columnInfo.getColumnType()) { case BIT: @@ -2248,7 +2257,7 @@ public Ref getRef(String columnLabel) throws SQLException { */ public Blob getBlob(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; //TODO implement MariaDbBlob with offset byte[] data = new byte[row.getLengthMaxFieldSize()]; System.arraycopy(row.buf, row.pos, data, 0, row.getLengthMaxFieldSize()); @@ -2267,7 +2276,7 @@ public Blob getBlob(String columnLabel) throws SQLException { */ public Clob getClob(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; //TODO implement MariaDbClob with offset byte[] data = new byte[row.getLengthMaxFieldSize()]; System.arraycopy(row.buf, row.pos, data, 0, row.getLengthMaxFieldSize()); @@ -2301,7 +2310,7 @@ public Array getArray(String columnLabel) throws SQLException { @Override public URL getURL(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; try { return new URL(getInternalString(columnsInformation[columnIndex - 1])); } catch (MalformedURLException e) { @@ -2337,7 +2346,7 @@ public RowId getRowId(String columnLabel) throws SQLException { */ public NClob getNClob(int columnIndex) throws SQLException { checkObjectRange(columnIndex); - if (lastValueNull) return null; + if (lastValueWasNull()) return null; //TODO implement MariaDbBlob with offset byte[] data = new byte[row.getLengthMaxFieldSize()]; System.arraycopy(row.buf, row.pos, data, 0, row.getLengthMaxFieldSize()); @@ -3090,7 +3099,7 @@ public int getHoldability() throws SQLException { * @throws SQLException id any error occur */ private boolean getInternalBoolean(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return false; + if (lastValueWasNull()) return false; if (!this.isBinaryEncoded) { if (row.length == 1 && row.buf[row.pos] == 0) { @@ -3131,7 +3140,7 @@ private boolean getInternalBoolean(ColumnInformation columnInfo) throws SQLExcep * @throws SQLException id any error occur */ private byte getInternalByte(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; if (!this.isBinaryEncoded) { if (columnInfo.getColumnType() == ColumnType.BIT) { @@ -3179,7 +3188,7 @@ private byte getInternalByte(ColumnInformation columnInfo) throws SQLException { * @throws SQLException id any error occur */ private short getInternalShort(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; if (!this.isBinaryEncoded) { return parseShort(columnInfo); @@ -3247,7 +3256,7 @@ public void setReturnTableAlias(boolean returnTableAlias) { } private String getTimeString(ColumnInformation columnInfo) { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (row.length == 0) { // binary send 00:00:00 as 0. if (columnInfo.getDecimals() == 0) { @@ -3323,7 +3332,7 @@ private void rangeCheck(Object className, long minValue, long maxValue, long val } private int getInternalTinyInt(ColumnInformation columnInfo) { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; int value = row.buf[row.pos]; if (!columnInfo.isSigned()) { value = (row.buf[row.pos] & 0xff); @@ -3332,7 +3341,7 @@ private int getInternalTinyInt(ColumnInformation columnInfo) { } private int getInternalSmallInt(ColumnInformation columnInfo) { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; int value = ((row.buf[row.pos] & 0xff) + ((row.buf[row.pos + 1] & 0xff) << 8)); if (!columnInfo.isSigned()) { return value & 0xffff; @@ -3342,7 +3351,7 @@ private int getInternalSmallInt(ColumnInformation columnInfo) { } private long getInternalMediumInt(ColumnInformation columnInfo) { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; long value = ((row.buf[row.pos] & 0xff) + ((row.buf[row.pos + 1] & 0xff) << 8) + ((row.buf[row.pos + 2] & 0xff) << 16) @@ -3355,7 +3364,7 @@ private long getInternalMediumInt(ColumnInformation columnInfo) { private byte parseByte(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; try { switch (columnInfo.getColumnType()) { case FLOAT: @@ -3414,7 +3423,7 @@ private byte parseByte(ColumnInformation columnInfo) throws SQLException { } private short parseShort(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return 0; + if (lastValueWasNull()) return 0; try { switch (columnInfo.getColumnType()) { case FLOAT: @@ -3611,7 +3620,7 @@ private long parseLong(ColumnInformation columnInfo) throws SQLException { * @throws SQLException exception */ private BigInteger getInternalBigInteger(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (!this.isBinaryEncoded) { return new BigInteger(new String(row.buf, row.pos, row.length, StandardCharsets.UTF_8)); } else { @@ -3671,7 +3680,7 @@ private Date binaryDate(ColumnInformation columnInfo, Calendar cal) throws SQLEx throw new SQLException("Cannot read Date using a Types.TIME field"); default: if (row.length == 0) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_FIELD_NULL; return null; } @@ -3761,7 +3770,7 @@ private Time binaryTime(ColumnInformation columnInfo, Calendar cal) throws SQLEx private Timestamp binaryTimestamp(ColumnInformation columnInfo, Calendar userCalendar) { if (row.length == 0) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_FIELD_NULL; return null; } @@ -3877,9 +3886,9 @@ private int extractNanos(String timestring) throws SQLException { * @throws SQLException if any read error occur */ private ZonedDateTime getZonedDateTime(ColumnInformation columnInfo, Class clazz) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (row.length == 0) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_FIELD_NULL; return null; } @@ -3973,9 +3982,9 @@ private ZonedDateTime getZonedDateTime(ColumnInformation columnInfo, Class clazz * @throws SQLException if any read error occur */ private OffsetTime getOffsetTime(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (row.length == 0) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_FIELD_NULL; return null; } @@ -4112,9 +4121,9 @@ private OffsetTime getOffsetTime(ColumnInformation columnInfo) throws SQLExcepti * @throws SQLException if any read error occur */ private LocalTime getLocalTime(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (row.length == 0) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_FIELD_NULL; return null; } @@ -4210,9 +4219,9 @@ private LocalTime getLocalTime(ColumnInformation columnInfo) throws SQLException * @throws SQLException if any read error occur */ private LocalDate getLocalDate(ColumnInformation columnInfo) throws SQLException { - if (lastValueNull) return null; + if (lastValueWasNull()) return null; if (row.length == 0) { - lastGetWasNull = true; + lastValueNull |= BIT_LAST_FIELD_NULL; return null; } diff --git a/src/test/java/org/mariadb/jdbc/ResultSetTest.java b/src/test/java/org/mariadb/jdbc/ResultSetTest.java index 2c2975afa..3d1baa21d 100644 --- a/src/test/java/org/mariadb/jdbc/ResultSetTest.java +++ b/src/test/java/org/mariadb/jdbc/ResultSetTest.java @@ -52,13 +52,11 @@ package org.mariadb.jdbc; +import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; +import java.sql.*; import static org.junit.Assert.*; @@ -872,4 +870,85 @@ public void numericTestWithDecimal() throws SQLException { assertTrue(rs.getLong("test") == -1); assertTrue(rs.getFloat("test") == -1.3333F); } + + @Test + public void nullField() throws SQLException { + Assume.assumeTrue(isMariadbServer()); //'0000-00-00' doesn't work anymore on mysql 5.7. + createTable("nullField", "t1 varchar(50), t2 timestamp NULL, t3 date, t4 year(4)"); + Statement stmt = sharedConnection.createStatement(); + stmt.execute("INSERT INTO nullField(t1,t2,t3,t4) values " + + "(null, '0000-00-00 00:00:00', '0000-00-00', '0000'), " + + "(null, null, null, null)," + + "('aa', now(), now(), '2017')"); + + ResultSet rs = stmt.executeQuery("SELECT * FROM nullField"); + assertTrue(rs.next()); + if (!sharedOptions().useServerPrepStmts) { + assertNull(rs.getString(1)); + assertTrue(rs.wasNull()); + + assertNull(rs.getTimestamp(2)); + assertTrue(rs.wasNull()); + + assertEquals("0000-00-00 00:00:00", rs.getString(2)); + assertFalse(rs.wasNull()); + + assertNull(rs.getDate(3)); + assertTrue(rs.wasNull()); + + assertEquals("0000-00-00", rs.getString(3)); + assertFalse(rs.wasNull()); + + assertEquals(Date.valueOf("0000-01-01"), rs.getDate(4)); + assertFalse(rs.wasNull()); + + assertEquals(0, rs.getInt(4)); + assertFalse(rs.wasNull()); + + assertEquals("0001-01-01", rs.getString(4)); + assertFalse(rs.wasNull()); + + } + + assertTrue(rs.next()); + + assertNull(rs.getTimestamp(2)); + assertTrue(rs.wasNull()); + + assertNull(rs.getString(2)); + assertTrue(rs.wasNull()); + + assertNull(rs.getDate(3)); + assertTrue(rs.wasNull()); + + assertNull(rs.getString(3)); + assertTrue(rs.wasNull()); + + assertNull(rs.getDate(4)); + assertTrue(rs.wasNull()); + + assertNull(rs.getString(2)); + assertTrue(rs.wasNull()); + + assertTrue(rs.next()); + + assertNotNull(rs.getTimestamp(2)); + assertFalse(rs.wasNull()); + + assertNotNull(rs.getString(2)); + assertFalse(rs.wasNull()); + + assertNotNull(rs.getDate(3)); + assertFalse(rs.wasNull()); + + assertNotNull(rs.getString(3)); + assertFalse(rs.wasNull()); + + assertNotNull(rs.getDate(4)); + assertFalse(rs.wasNull()); + + assertNotNull(rs.getString(2)); + assertFalse(rs.wasNull()); + + } }