Mercurial > hg > monetdb-java
changeset 120:02f560eb3cf2 embedded
Major change in the Datablock response. Removed the arrays creation in the Embedded connection, so it will run much faster now. It can be possible to do it as well in the MAPI connection, but in the way the Old Mapi Protocol is designed, it will be very complicated (ByteBuffers are designed to work with binary data instead of textual data :S). I think it's better to wait for the new protocol, which will be much faster and easier to parse.
line wrap: on
line diff
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetConnection.java @@ -1559,14 +1559,10 @@ public abstract class MonetConnection ex res = protocol.getNextResultSetResponse(MonetConnection.this, ResponseList.this, this.seqnr, this.maxrows); ResultSetResponse rsreponse = (ResultSetResponse) res; - // only add this resultset to the hashmap if it can possibly - // have an additional datablock - if (rsreponse.getRowcount() < rsreponse.getTuplecount()) { - if (rsresponses == null) { - rsresponses = new HashMap<>(); - } - rsresponses.put(rsreponse.getId(), rsreponse); + if (rsresponses == null) { + rsresponses = new HashMap<>(); } + rsresponses.put(rsreponse.getId(), rsreponse); } break; case StarterHeaders.Q_UPDATE: @@ -1586,7 +1582,7 @@ public abstract class MonetConnection ex MonetConnection.this.autoCommit = isAutoCommit; break; case StarterHeaders.Q_BLOCK: { - DataBlockResponse next = protocol.getNextDatablockResponse(rsresponses); + AbstractDataBlockResponse next = protocol.getNextDatablockResponse(rsresponses); if (next == null) { error = "M0M12!No ResultSetResponse for a DataBlock found"; break; @@ -1636,7 +1632,7 @@ public abstract class MonetConnection ex // it is of no use to store DataBlockResponses, you never want to retrieve them directly // anyway - if (!(res instanceof DataBlockResponse)) { + if (!(res instanceof AbstractDataBlockResponse)) { responses.add(res); } // read the next line (can be prompt, new result, error, etc.) before we start the loop over
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in @@ -48,7 +48,7 @@ import java.util.logging.Logger; * @author Fabian Groffen, Pedro Ferreira * @version @JDBC_MAJOR@.@JDBC_MINOR@ (@JDBC_VER_SUFFIX@) */ -final public class MonetDriver implements Driver { +public final class MonetDriver implements Driver { // the url kind will be jdbc:monetdb://<host>[:<port>]/<database> (in a MAPI connection) // the url kind will be jdbc:monetdb:embedded:<directory> (in an Embedded connection) // Chapter 9.2.1 from Sun JDBC 3.0 specification
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @@ -8,37 +8,23 @@ package nl.cwi.monetdb.jdbc; -import nl.cwi.monetdb.mcl.responses.DataBlockResponse; +import nl.cwi.monetdb.mcl.protocol.ProtocolException; +import nl.cwi.monetdb.mcl.responses.AbstractDataBlockResponse; import nl.cwi.monetdb.mcl.responses.ResultSetResponse; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.Date; -import java.sql.NClob; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.RowId; -import java.sql.SQLData; -import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.sql.SQLInput; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Statement; -import java.sql.Time; -import java.sql.Timestamp; -import java.sql.Types; -import java.util.*; +import java.sql.*; +import java.util.Calendar; +import java.util.Map; +import java.util.UUID; /** * A ResultSet suitable for the MonetDB database. @@ -84,12 +70,10 @@ public class MonetResultSet extends Mone private int concurrency = CONCUR_READ_ONLY; /** The warnings for this ResultSet object */ private SQLWarning warnings; - /** whether the last read field (via some getXyz() method) was NULL */ - private boolean lastReadWasNull = true; /** Just a dummy variable to keep store the fetchsize set. */ private int fetchSize; /** The current row's values */ - private DataBlockResponse currentBlock; + private AbstractDataBlockResponse currentBlock; /** * Main constructor backed by the given Header. @@ -152,11 +136,6 @@ public class MonetResultSet extends Mone this.JdbcSQLTypes = JdbcSQLTypes; } - private boolean setLastNullValue(int columnIndex) { - this.lastReadWasNull = currentBlock.checkValueIsNull(columnIndex); - return this.lastReadWasNull; - } - //== methods of interface ResultSet // Chapter 14.2.2 Sun JDBC 3.0 Specification @@ -303,23 +282,29 @@ public class MonetResultSet extends Mone @Override public InputStream getAsciiStream(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } + InputStream res = null; switch (JdbcSQLTypes[columnIndex - 1]) { + case Types.CLOB: + Clob cl = getClob(columnIndex); + if(cl != null) { + res = cl.getAsciiStream(); + } + break; case Types.BLOB: - return getClob(columnIndex).getAsciiStream(); - case Types.CLOB: - return getClob(columnIndex).getAsciiStream(); case Types.LONGVARBINARY: case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: - return new ByteArrayInputStream(getBytes(columnIndex)); + byte[] bytes = getBytes(columnIndex); + if(bytes != null) { + res = new ByteArrayInputStream(getBytes(columnIndex)); + } + break; default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to ascii stream not supported", "M1M05"); } + return res; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -362,18 +347,25 @@ public class MonetResultSet extends Mone @Override public InputStream getBinaryStream(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } + InputStream res = null; switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: - return getBlob(columnIndex).getBinaryStream(); + Blob cl = getBlob(columnIndex); + if(cl != null) { + res = cl.getBinaryStream(); + } + break; case Types.LONGVARBINARY: - return new ByteArrayInputStream(getBytes(columnIndex)); + byte[] bytes = getBytes(columnIndex); + if(bytes != null) { + res = new ByteArrayInputStream(getBytes(columnIndex)); + } + break; default: throw new SQLException("Conversion from " + types[columnIndex - 1] + " to binary stream not supported", "M1M05"); } + return res; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -414,13 +406,13 @@ public class MonetResultSet extends Mone @Override public Reader getCharacterStream(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } - return new StringReader(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return (ss == null) ? null : new StringReader(ss); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -481,13 +473,12 @@ public class MonetResultSet extends Mone @Override public Blob getBlob(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } return (MonetBlob) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -516,13 +507,12 @@ public class MonetResultSet extends Mone @Override public Clob getClob(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } return (MonetClob) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -580,52 +570,48 @@ public class MonetResultSet extends Mone @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } - BigDecimal val; switch (JdbcSQLTypes[columnIndex - 1]) { case Types.NUMERIC: case Types.DECIMAL: - val = (BigDecimal) currentBlock.getObjectValue(columnIndex - 1); - break; + return (BigDecimal) currentBlock.getObjectValue(columnIndex - 1); case Types.BOOLEAN: - val = new BigDecimal(currentBlock.getBooleanValue(columnIndex - 1) ? (byte) 1 : (byte) 0); - break; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(bol ? 1 : 0); case Types.TINYINT: - val = new BigDecimal(currentBlock.getByteValue(columnIndex - 1)); - break; + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(bb); case Types.SMALLINT: - val = new BigDecimal(currentBlock.getShortValue(columnIndex - 1)); - break; + short sh = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(sh); case Types.INTEGER: - val = new BigDecimal(currentBlock.getIntValue(columnIndex - 1)); - break; + int in = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(in); case Types.BIGINT: - val = new BigDecimal(currentBlock.getLongValue(columnIndex - 1)); - break; + long lon = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(lon); case Types.REAL: - val = new BigDecimal(currentBlock.getFloatValue(columnIndex - 1)); - break; + float floa = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(floa); case Types.DOUBLE: - val = new BigDecimal(currentBlock.getDoubleValue(columnIndex - 1)); - break; + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? null : new BigDecimal(dou); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - val = new BigDecimal(currentBlock.getValueAsString(columnIndex - 1)); - break; + String ss = currentBlock.getValueAsString(columnIndex - 1); + return (ss == null) ? null : new BigDecimal(ss); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05"); } - return val; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -644,11 +630,10 @@ public class MonetResultSet extends Mone @Deprecated public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; + BigDecimal val = getBigDecimal(columnIndex); + if(val != null) { + val.setScale(scale); } - BigDecimal val = getBigDecimal(columnIndex); - val.setScale(scale); return val; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); @@ -699,25 +684,29 @@ public class MonetResultSet extends Mone @Override public boolean getBoolean(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return false; // if the value is SQL NULL, the value returned is false - } // match type specific values switch (JdbcSQLTypes[columnIndex - 1]) { - case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1); - case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1) != 0; - case Types.SMALLINT: - return currentBlock.getShortValue(columnIndex - 1) != 0; - case Types.INTEGER: - return currentBlock.getIntValue(columnIndex - 1) != 0; - case Types.BIGINT: - return currentBlock.getLongValue(columnIndex - 1) != 0L; - case Types.REAL: - return currentBlock.getFloatValue(columnIndex - 1) != 0.0f; - case Types.DOUBLE: - return currentBlock.getDoubleValue(columnIndex - 1) != 0.0d; + case Types.BOOLEAN: + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && bol; + case Types.TINYINT: + byte bb = currentBlock.getByteValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && bb != 0; + case Types.SMALLINT: + short sh = currentBlock.getShortValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && sh != 0; + case Types.INTEGER: + int in = currentBlock.getIntValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && in != 0; + case Types.BIGINT: + long lon = currentBlock.getLongValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && lon != 0; + case Types.REAL: + float floa = currentBlock.getFloatValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && floa != 0.0f; + case Types.DOUBLE: + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return !currentBlock.isLastReadWasNull() && dou != 0.0d; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: @@ -725,18 +714,20 @@ public class MonetResultSet extends Mone case Types.BLOB: case Types.LONGVARBINARY: String val = currentBlock.getValueAsString(columnIndex - 1); - return !"0".equals(val) && ("1".equals(val) || Boolean.parseBoolean(val)); + return val != null && !"0".equals(val) && ("1".equals(val) || Boolean.parseBoolean(val)); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.compareTo(BigDecimal.ZERO) != 0; + return bigdec != null && bigdec.compareTo(BigDecimal.ZERO) != 0; default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + " to boolean type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -765,42 +756,49 @@ public class MonetResultSet extends Mone @Override public byte getByte(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return 0; - } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1); + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : bb; case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1) ? (byte) 1 : (byte) 0; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (bol ? (byte) 1 : (byte) 0); case Types.SMALLINT: - return (byte) currentBlock.getShortValue(columnIndex - 1); + short sh = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (byte) sh; case Types.INTEGER: - return (byte) currentBlock.getIntValue(columnIndex - 1); + int in = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (byte) in; case Types.BIGINT: - return (byte) currentBlock.getLongValue(columnIndex - 1); + long lon = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (byte) lon; case Types.REAL: - return (byte) Math.round(currentBlock.getFloatValue(columnIndex - 1)); + float floa = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (byte) Math.round(floa); case Types.DOUBLE: - return (byte) Math.round(currentBlock.getDoubleValue(columnIndex - 1)); + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (byte) Math.round(dou); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return Byte.parseByte(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? 0 : Byte.parseByte(ss); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.byteValue(); + return bigdec == null ? 0 : bigdec.byteValue(); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); + " to byte type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -831,13 +829,11 @@ public class MonetResultSet extends Mone @Override public byte[] getBytes(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } // According to Table B-6, getBytes() only operates on BINARY types switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: - return ((MonetBlob) currentBlock.getObjectValue(columnIndex - 1)).getBuffer(); + MonetBlob mb = (MonetBlob) currentBlock.getObjectValue(columnIndex - 1); + return mb == null ? null : mb.getBuffer(); case Types.LONGVARBINARY: // unpack the HEX (BLOB) notation to real bytes return (byte[]) currentBlock.getObjectValue(columnIndex - 1); @@ -845,13 +841,16 @@ public class MonetResultSet extends Mone case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: - return currentBlock.getValueAsString(columnIndex - 1).getBytes(); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? null : ss.getBytes(); default: throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -919,42 +918,49 @@ public class MonetResultSet extends Mone @Override public double getDouble(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return 0.0d; - } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.DOUBLE: - return currentBlock.getDoubleValue(columnIndex - 1); + double res = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : res; case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1) ? 1.0d : 0.0d; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : (bol ? 1.0d : 0.0d); case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1); + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : (double) bb; case Types.SMALLINT: - return currentBlock.getShortValue(columnIndex - 1); + short sh = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : (double) sh; case Types.INTEGER: - return currentBlock.getIntValue(columnIndex - 1); + int in = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : (double) in; case Types.BIGINT: - return currentBlock.getLongValue(columnIndex - 1); + long lon = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : (double) lon; case Types.REAL: - return currentBlock.getFloatValue(columnIndex - 1); + float floa = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0d : floa; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return Double.parseDouble(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? 0.0d : Double.parseDouble(ss); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.doubleValue(); + return bigdec == null ? 0.0d : bigdec.doubleValue(); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); + " to double type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1062,42 +1068,49 @@ public class MonetResultSet extends Mone @Override public float getFloat(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return 0.0f; - } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.REAL: - return currentBlock.getFloatValue(columnIndex - 1); + float res = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : res; case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1) ? 1.0f : 0.0f; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : (bol ? 1.0f : 0.0f); case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1); + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : (float) bb; case Types.SMALLINT: - return currentBlock.getShortValue(columnIndex - 1); + short sh = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : (float) sh; case Types.INTEGER: - return currentBlock.getIntValue(columnIndex - 1); + int in = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : (float) in; case Types.BIGINT: - return currentBlock.getLongValue(columnIndex - 1); + long lon = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : (float) lon; case Types.DOUBLE: - return (float) currentBlock.getDoubleValue(columnIndex - 1); + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0.0f : (float) dou; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return Float.parseFloat(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? 0.0f : Float.parseFloat(ss); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.floatValue(); + return bigdec == null ? 0.0f : bigdec.floatValue(); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); + " to float type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1126,43 +1139,49 @@ public class MonetResultSet extends Mone @Override public int getInt(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return 0; - } - // match type specific values switch (JdbcSQLTypes[columnIndex - 1]) { case Types.INTEGER: - return currentBlock.getIntValue(columnIndex - 1); + int res = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : res; case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1) ? 1 : 0; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (bol ? 1 : 0); case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1); + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : bb; case Types.SMALLINT: - return currentBlock.getShortValue(columnIndex - 1); + short sh = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : sh; case Types.BIGINT: - return (int) currentBlock.getLongValue(columnIndex - 1); + long lon = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (int) lon; case Types.REAL: - return Math.round(currentBlock.getFloatValue(columnIndex - 1)); + float floa = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : Math.round(floa); case Types.DOUBLE: - return (int) Math.round(currentBlock.getDoubleValue(columnIndex - 1)); + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (int) Math.round(dou); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return Integer.parseInt(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? 0 : Integer.parseInt(ss); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.intValue(); + return bigdec == null ? 0 : bigdec.intValue(); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); + " to integer type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1191,42 +1210,49 @@ public class MonetResultSet extends Mone @Override public long getLong(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return 0L; - } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BIGINT: - return currentBlock.getLongValue(columnIndex - 1); + long res = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : res; case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1) ? 1 : 0; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (bol ? 1 : 0); case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1); + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : bb; case Types.SMALLINT: - return currentBlock.getShortValue(columnIndex - 1); + short sh = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : sh; case Types.INTEGER: - return currentBlock.getIntValue(columnIndex - 1); + int in = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : in; case Types.REAL: - return Math.round(currentBlock.getFloatValue(columnIndex - 1)); + float floa = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : Math.round(floa); case Types.DOUBLE: - return Math.round(currentBlock.getDoubleValue(columnIndex - 1)); + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : Math.round(dou); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return Long.parseLong(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? 0 : Long.parseLong(ss); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.longValue(); + return bigdec == null ? 0 : bigdec.longValue(); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); + " to long type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -1785,9 +1811,6 @@ public class MonetResultSet extends Mone // implementation should be as fast as possible, so avoid method calls (by inlining code) where possible final int JdbcType; try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } JdbcType = JdbcSQLTypes[columnIndex - 1]; switch(JdbcType) { case Types.BOOLEAN: @@ -1817,6 +1840,9 @@ public class MonetResultSet extends Mone // For these MonetDB types (except json, see comments below) we try to create objects of the corresponding class. String MonetDBType = types[columnIndex - 1]; String val = currentBlock.getValueAsString(columnIndex - 1); + if (val == null) { + return null; + } switch (MonetDBType.length()) { case 3: if ("url".equals(MonetDBType)) { @@ -1862,7 +1888,9 @@ public class MonetResultSet extends Mone } } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); - } + } catch (ProtocolException e) { + throw new SQLException(e); + } } private boolean classImplementsSQLData(Class<?> cl) { @@ -1876,194 +1904,196 @@ public class MonetResultSet extends Mone @SuppressWarnings("unchecked") private Object getObjectFromClass(int columnIndex, Class<?> type) throws SQLException { - if(setLastNullValue(columnIndex - 1)) { - return null; - } - if (type == null) { - // fallback to the standard Class mappings - type = getClassForType(JdbcSQLTypes[columnIndex - 1]); - } - if (type == null || type == String.class) { - return currentBlock.getValueAsString(columnIndex - 1); - } else if (type == BigDecimal.class) { - return getBigDecimal(columnIndex); - } else if (type == Boolean.class) { - return getBoolean(columnIndex); - } else if (type == Byte.class) { - return getByte(columnIndex); - } else if (type == Short.class) { - return getShort(columnIndex); - } else if (type == Integer.class) { - return getInt(columnIndex); - } else if (type == Long.class) { - return getLong(columnIndex); - } else if (type == Float.class) { - return getFloat(columnIndex); - } else if (type == Double.class) { - return getDouble(columnIndex); - } else if (type == byte[].class) { - return getBytes(columnIndex); - } else if (type == Date.class) { - return getDate(columnIndex); - } else if (type == Time.class) { - return getTime(columnIndex); - } else if (type == Timestamp.class) { - return getTimestamp(columnIndex); - } else if (type == Clob.class) { - return getClob(columnIndex); - } else if (type == Blob.class) { - return getBlob(columnIndex); - } else if (classImplementsSQLData(type)) { - SQLData x; - try { - Constructor<? extends SQLData> ctor = ((Class)type).getConstructor(); - x = ctor.newInstance(); - } catch (NoSuchMethodException | InstantiationException | InvocationTargetException - | SecurityException | IllegalAccessException nsme) { - throw new SQLException(nsme.getMessage(), "M0M27"); + try { + + if (type == null) { + // fallback to the standard Class mappings + type = getClassForType(JdbcSQLTypes[columnIndex - 1]); } - final int colnum = columnIndex; - final boolean valwasnull = wasNull(); - SQLInput input = new SQLInput() { - @Override - public String readString() throws SQLException { - return getString(colnum); + if (type == null || type == String.class) { + return currentBlock.getValueAsString(columnIndex - 1); + } else if (type == BigDecimal.class) { + return getBigDecimal(columnIndex); + } else if (type == Boolean.class) { + return getBoolean(columnIndex); + } else if (type == Byte.class) { + return getByte(columnIndex); + } else if (type == Short.class) { + return getShort(columnIndex); + } else if (type == Integer.class) { + return getInt(columnIndex); + } else if (type == Long.class) { + return getLong(columnIndex); + } else if (type == Float.class) { + return getFloat(columnIndex); + } else if (type == Double.class) { + return getDouble(columnIndex); + } else if (type == byte[].class) { + return getBytes(columnIndex); + } else if (type == Date.class) { + return getDate(columnIndex); + } else if (type == Time.class) { + return getTime(columnIndex); + } else if (type == Timestamp.class) { + return getTimestamp(columnIndex); + } else if (type == Clob.class) { + return getClob(columnIndex); + } else if (type == Blob.class) { + return getBlob(columnIndex); + } else if (classImplementsSQLData(type)) { + SQLData x; + try { + Constructor<? extends SQLData> ctor = ((Class) type).getConstructor(); + x = ctor.newInstance(); + } catch (NoSuchMethodException | InstantiationException | InvocationTargetException + | SecurityException | IllegalAccessException nsme) { + throw new SQLException(nsme.getMessage(), "M0M27"); } - - @Override - public boolean readBoolean() throws SQLException { - return getBoolean(colnum); - } - - @Override - public byte readByte() throws SQLException { - return getByte(colnum); - } - - @Override - public short readShort() throws SQLException { - return getShort(colnum); - } + final int colnum = columnIndex; + final boolean valwasnull = wasNull(); + SQLInput input = new SQLInput() { + @Override + public String readString() throws SQLException { + return getString(colnum); + } - @Override - public int readInt() throws SQLException { - return getInt(colnum); - } + @Override + public boolean readBoolean() throws SQLException { + return getBoolean(colnum); + } + + @Override + public byte readByte() throws SQLException { + return getByte(colnum); + } - @Override - public long readLong() throws SQLException { - return getLong(colnum); - } + @Override + public short readShort() throws SQLException { + return getShort(colnum); + } + + @Override + public int readInt() throws SQLException { + return getInt(colnum); + } - @Override - public float readFloat() throws SQLException { - return getFloat(colnum); - } + @Override + public long readLong() throws SQLException { + return getLong(colnum); + } + + @Override + public float readFloat() throws SQLException { + return getFloat(colnum); + } - @Override - public double readDouble() throws SQLException { - return getDouble(colnum); - } + @Override + public double readDouble() throws SQLException { + return getDouble(colnum); + } - @Override - public BigDecimal readBigDecimal() throws SQLException { - return getBigDecimal(colnum); - } + @Override + public BigDecimal readBigDecimal() throws SQLException { + return getBigDecimal(colnum); + } - @Override - public byte[] readBytes() throws SQLException { - return getBytes(colnum); - } + @Override + public byte[] readBytes() throws SQLException { + return getBytes(colnum); + } - @Override - public Date readDate() throws SQLException { - return getDate(colnum); - } + @Override + public Date readDate() throws SQLException { + return getDate(colnum); + } - @Override - public Time readTime() throws SQLException { - return getTime(colnum); - } + @Override + public Time readTime() throws SQLException { + return getTime(colnum); + } - @Override - public Timestamp readTimestamp() throws SQLException { - return getTimestamp(colnum); - } + @Override + public Timestamp readTimestamp() throws SQLException { + return getTimestamp(colnum); + } - @Override - public Reader readCharacterStream() throws SQLException { - return getCharacterStream(colnum); - } + @Override + public Reader readCharacterStream() throws SQLException { + return getCharacterStream(colnum); + } - @Override - public InputStream readAsciiStream() throws SQLException { - return getAsciiStream(colnum); - } + @Override + public InputStream readAsciiStream() throws SQLException { + return getAsciiStream(colnum); + } - @Override - public InputStream readBinaryStream() throws SQLException { - return getBinaryStream(colnum); - } + @Override + public InputStream readBinaryStream() throws SQLException { + return getBinaryStream(colnum); + } - @Override - public Object readObject() throws SQLException { - return getObject(colnum); - } + @Override + public Object readObject() throws SQLException { + return getObject(colnum); + } - @Override - public Ref readRef() throws SQLException { - return getRef(colnum); - } + @Override + public Ref readRef() throws SQLException { + return getRef(colnum); + } - @Override - public Blob readBlob() throws SQLException { - return getBlob(colnum); - } + @Override + public Blob readBlob() throws SQLException { + return getBlob(colnum); + } - @Override - public Clob readClob() throws SQLException { - return getClob(colnum); - } + @Override + public Clob readClob() throws SQLException { + return getClob(colnum); + } - @Override - public Array readArray() throws SQLException { - return getArray(colnum); - } + @Override + public Array readArray() throws SQLException { + return getArray(colnum); + } - @Override - public boolean wasNull() throws SQLException { - return valwasnull; - } + @Override + public boolean wasNull() throws SQLException { + return valwasnull; + } - @Override - public URL readURL() throws SQLException { - return getURL(colnum); - } + @Override + public URL readURL() throws SQLException { + return getURL(colnum); + } - @Override - public NClob readNClob() throws SQLException { - return getNClob(colnum); - } + @Override + public NClob readNClob() throws SQLException { + return getNClob(colnum); + } - @Override - public String readNString() throws SQLException { - return getNString(colnum); - } + @Override + public String readNString() throws SQLException { + return getNString(colnum); + } - @Override - public SQLXML readSQLXML() throws SQLException { - return getSQLXML(colnum); - } + @Override + public SQLXML readSQLXML() throws SQLException { + return getSQLXML(colnum); + } - @Override - public RowId readRowId() throws SQLException { - return getRowId(colnum); - } - }; - x.readSQL(input, types[columnIndex - 1]); - return x; - } else { - return currentBlock.getObjectValue(columnIndex - 1); + @Override + public RowId readRowId() throws SQLException { + return getRowId(colnum); + } + }; + x.readSQL(input, types[columnIndex - 1]); + return x; + } else { + return currentBlock.getObjectValue(columnIndex - 1); + } + } catch (ProtocolException e) { + throw new SQLException(e); } } @@ -2303,41 +2333,48 @@ public class MonetResultSet extends Mone @Override public short getShort(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return 0; - } switch (JdbcSQLTypes[columnIndex - 1]) { case Types.SMALLINT: - return currentBlock.getShortValue(columnIndex - 1); + short res = currentBlock.getShortValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : res; case Types.BOOLEAN: - return currentBlock.getBooleanValue(columnIndex - 1) ? (short) 1 : (short) 0; + boolean bol = currentBlock.getBooleanValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? (short) 0 : (bol ? (short) 1 : (short) 0); case Types.TINYINT: - return currentBlock.getByteValue(columnIndex - 1); + byte bb = currentBlock.getByteValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : bb; case Types.INTEGER: - return (short) currentBlock.getIntValue(columnIndex - 1); + int in = currentBlock.getIntValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (short) in; case Types.BIGINT: - return (short) currentBlock.getLongValue(columnIndex - 1); + long lon = currentBlock.getLongValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (short) lon; case Types.REAL: - return (short) Math.round(currentBlock.getFloatValue(columnIndex - 1)); + float floa = currentBlock.getFloatValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (short) Math.round(floa); case Types.DOUBLE: - return (short) Math.round(currentBlock.getDoubleValue(columnIndex - 1)); + double dou = currentBlock.getDoubleValue(columnIndex - 1); + return currentBlock.isLastReadWasNull() ? 0 : (short) Math.round(dou); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return Short.parseShort(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? 0 : Short.parseShort(ss); case Types.NUMERIC: case Types.DECIMAL: BigDecimal bigdec = (BigDecimal) currentBlock.getValueAsObject(columnIndex - 1); - return bigdec.shortValue(); + return bigdec == null ? 0 : bigdec.shortValue(); default: //OTHERS, BLOB, LONGVARBINARY, TIME... throw new SQLException("Conversion from " + types[columnIndex - 1] + - " to boolean type not supported", "M1M05"); + " to short type not supported", "M1M05"); } } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); + } catch (ProtocolException e) { + throw new SQLException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } @@ -2379,12 +2416,11 @@ public class MonetResultSet extends Mone @Override public String getString(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } return currentBlock.getValueAsString(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); + } catch (ProtocolException e) { + throw new SQLException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } @@ -2494,9 +2530,6 @@ public class MonetResultSet extends Mone @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } Calendar res; long millis; switch (JdbcSQLTypes[columnIndex - 1]) { @@ -2504,11 +2537,17 @@ public class MonetResultSet extends Mone case Types.TIME: case Types.TIMESTAMP: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; case Types.TIME_WITH_TIMEZONE: case Types.TIMESTAMP_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } millis = res.getTimeInMillis(); break; case Types.CHAR: @@ -2518,6 +2557,9 @@ public class MonetResultSet extends Mone case Types.BLOB: case Types.LONGVARBINARY: res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.DATE); + if(res == null) { + return null; + } millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; default: @@ -2528,6 +2570,8 @@ public class MonetResultSet extends Mone return new Date(millis); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); + } catch (ProtocolException e) { + throw new SQLException(e); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } @@ -2593,9 +2637,6 @@ public class MonetResultSet extends Mone @Override public Time getTime(int columnIndex, Calendar cal) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } Calendar res; long millis; switch (JdbcSQLTypes[columnIndex - 1]) { @@ -2603,11 +2644,17 @@ public class MonetResultSet extends Mone case Types.TIME: case Types.TIMESTAMP: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; case Types.TIME_WITH_TIMEZONE: case Types.TIMESTAMP_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } millis = res.getTimeInMillis(); break; case Types.CHAR: @@ -2617,7 +2664,10 @@ public class MonetResultSet extends Mone case Types.BLOB: case Types.LONGVARBINARY: res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.TIME); - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + if(res == null) { + return null; + } + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; default: this.addWarning("unsupported data type", "01M03"); @@ -2627,7 +2677,9 @@ public class MonetResultSet extends Mone return new Time(millis); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -2692,9 +2744,6 @@ public class MonetResultSet extends Mone @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } Calendar res; long millis; int nanos = 0; @@ -2702,20 +2751,32 @@ public class MonetResultSet extends Mone case Types.DATE: case Types.TIME: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; case Types.TIME_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); + if(res == null) { + return null; + } millis = res.getTimeInMillis(); break; case Types.TIMESTAMP: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); - nanos = currentBlock.getNanos(columnIndex - 1); + if(res == null) { + return null; + } + nanos = currentBlock.getLastNanos(); millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; case Types.TIMESTAMP_WITH_TIMEZONE: res = (Calendar) currentBlock.getValueAsObject(columnIndex - 1); - nanos = currentBlock.getNanos(columnIndex - 1); + if(res == null) { + return null; + } + nanos = currentBlock.getLastNanos(); millis = res.getTimeInMillis(); break; case Types.CHAR: @@ -2725,7 +2786,10 @@ public class MonetResultSet extends Mone case Types.BLOB: case Types.LONGVARBINARY: res = currentBlock.getDateValueFromString(this, columnIndex - 1, Types.TIMESTAMP); - millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); + if(res == null) { + return null; + } + millis = res.getTimeInMillis() - res.getTimeZone().getRawOffset() + cal.getTimeZone().getRawOffset(); break; default: this.addWarning("unsupported data type", "01M03"); @@ -2737,7 +2801,9 @@ public class MonetResultSet extends Mone return result; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); - } catch (IndexOutOfBoundsException e) { + } catch (ProtocolException e) { + throw new SQLException(e); + } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } } @@ -2796,13 +2862,11 @@ public class MonetResultSet extends Mone @Override public URL getURL(int columnIndex) throws SQLException { try { - if(setLastNullValue(columnIndex - 1)) { - return null; - } switch(JdbcSQLTypes[columnIndex - 1]) { //if it's a string type, will attempt the conversion case Types.OTHER: if("url".equals(types[columnIndex - 1])) { - return new URL(currentBlock.getValueAsString(columnIndex - 1)); + String ss = currentBlock.getValueAsString(columnIndex - 1); + return ss == null ? null : new URL(ss); } break; case Types.CHAR: @@ -2811,11 +2875,14 @@ public class MonetResultSet extends Mone case Types.CLOB: case Types.BLOB: case Types.LONGVARBINARY: - return new URL(currentBlock.getValueAsString(columnIndex - 1)); + String sss = currentBlock.getValueAsString(columnIndex - 1); + return sss == null ? null : new URL(sss); } throw new SQLException("Cannot convert " + types[columnIndex - 1] + " to an url", "M1M05"); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); + } catch (ProtocolException e) { + throw new SQLException(e); } catch (MalformedURLException e) { throw new SQLException(e.getMessage(), "M1M05"); } @@ -3506,7 +3573,7 @@ public class MonetResultSet extends Mone */ @Override public boolean wasNull() { - return lastReadWasNull; + return currentBlock.isLastReadWasNull(); } //== end methods of interface ResultSet
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java @@ -9,9 +9,9 @@ package nl.cwi.monetdb.mcl.connection.mapi; import nl.cwi.monetdb.jdbc.MonetConnection; -import nl.cwi.monetdb.mcl.connection.helpers.ChannelSecurity; import nl.cwi.monetdb.mcl.connection.ControlCommands; import nl.cwi.monetdb.mcl.connection.MCLException; +import nl.cwi.monetdb.mcl.connection.helpers.ChannelSecurity; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.ServerResponses; import nl.cwi.monetdb.mcl.protocol.oldmapi.OldMapiProtocol; @@ -110,7 +110,7 @@ public class MapiConnection extends Mone } /** - * Set the SO_TIMEOUT on the underlying Socket. When for some reason the connection to the database hangs, this + * Set the SO_TIMEOUT on the underlying Socket. When for some reason the connection to the database hangs, this * setting can be useful to break out of this indefinite wait. This option must be enabled prior to entering the * blocking operation to have effect. *
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java @@ -236,6 +236,10 @@ public class OldMapiSocket extends Abstr break; } } + if(size == -1) { //When nothing could be read, throw the exception + throw new IOException("Read from " + connection.getHostname() + ":" + + connection.getPort() + ": Incomplete block read from stream"); + } b.position(size); b.flip(); return size;
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java @@ -183,7 +183,7 @@ public abstract class AbstractProtocol { /** * Gets the next UpdateResponse response from the server. * - * @return The UpdateResponse instance + * @return An UpdateResponse instance * @throws ProtocolException If an error in the underlying connection happened. */ public abstract UpdateResponse getNextUpdateResponse() throws ProtocolException; @@ -191,7 +191,7 @@ public abstract class AbstractProtocol { /** * Gets the next SchemaResponse response from the server. * - * @return The SchemaResponse instance + * @return A SchemaResponse instance */ public SchemaResponse getNextSchemaResponse() { return new SchemaResponse(); @@ -200,12 +200,25 @@ public abstract class AbstractProtocol { /** * Gets the next AutoCommitResponse response from the server. * - * @return The AutoCommitResponse instance + * @return An AutoCommitResponse instance * @throws ProtocolException If an error in the underlying connection happened. */ public abstract AutoCommitResponse getNextAutoCommitResponse() throws ProtocolException; /** + * Get an empty DataBlockResponse from the server. + * + * @param rowcount - Number of tuples + * @param columncount - Number of tuples + * @param protocol - This protocol + * @param JdbcSQLTypes - the types array + * @return An AbstractDataBlockResponse instance + */ + public abstract AbstractDataBlockResponse getAnEmptyDataBlockResponse(int rowcount, int columncount, + AbstractProtocol protocol, + int[] JdbcSQLTypes); + + /** * Gets the next DataBlockResponse response from the server, belonging to a ResultSetResponse * * @param rsresponses A map of ResultSetResponse, in which this Block will belong to one of them, by checking its id @@ -213,7 +226,7 @@ public abstract class AbstractProtocol { * @return The DataBlockResponse instance * @throws ProtocolException If an error in the underlying connection happened. */ - public abstract DataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) + public abstract AbstractDataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) throws ProtocolException; /** @@ -230,18 +243,6 @@ public abstract class AbstractProtocol { String[] tableNames) throws ProtocolException; /** - * Retrieves the next values in a DataBlockResponse from the underlying connection, starting at a specific line - * number. - * - * @param firstLineNumber The first line number in the response to retrieve - * @param typesMap The JDBC types mapping array for every column in the ResultSetResponse of the DataBlock - * @param values An array of columns to fill the values - * @return The number of lines parsed from the underlying connection - * @throws ProtocolException If an error in the underlying connection happened. - */ - public abstract int parseTupleLines(int firstLineNumber, int[] typesMap, Object[] values) throws ProtocolException; - - /** * Gets the remaining response line from the underlying connection as a Java String. This method is mostly used to * retrieve error Strings, when they are detected while parsing a response line. *
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/ServerResponses.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/ServerResponses.java @@ -15,6 +15,8 @@ package nl.cwi.monetdb.mcl.protocol; */ public final class ServerResponses { + private ServerResponses() {} + /* Please don't change the order or the values */ /** "there is currently no line", or the the type is unknown is represented by UNKNOWN */
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/StarterHeaders.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/StarterHeaders.java @@ -16,6 +16,8 @@ package nl.cwi.monetdb.mcl.protocol; */ public final class StarterHeaders { + private StarterHeaders() {} + /* Please don't change the order or the values */ /** A parse response (not handled) */
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java @@ -16,6 +16,8 @@ package nl.cwi.monetdb.mcl.protocol; */ public final class TableResultHeaders { + private TableResultHeaders() {} + /* Please don't change the order or the values */ /** When an unknown table header is returned on a MAPI connection */
new file mode 100644 --- /dev/null +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiDataBlockResponse.java @@ -0,0 +1,298 @@ +package nl.cwi.monetdb.mcl.protocol.oldmapi; + +import nl.cwi.monetdb.jdbc.MonetBlob; +import nl.cwi.monetdb.jdbc.MonetClob; +import nl.cwi.monetdb.mcl.connection.helpers.TimestampHelper; +import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; +import nl.cwi.monetdb.mcl.protocol.ProtocolException; +import nl.cwi.monetdb.mcl.protocol.ServerResponses; +import nl.cwi.monetdb.mcl.responses.AbstractDataBlockResponse; + +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Arrays; +import java.util.Calendar; + +/** + * DataBlockResponse for an Old MAPI connection. + * + * @author Fabin Groffen, Pedro Ferreira + */ +public class OldMapiDataBlockResponse extends AbstractDataBlockResponse { + + /** The array to keep the data in */ + private Object[] data; + /** The counter which keeps the current position in the lines array */ + private int pos; + /** The last parsed nanos values for timestamps */ + private int lastNanos; + + OldMapiDataBlockResponse(int rowcount, int columncount, AbstractProtocol protocol, int[] JdbcSQLTypes) { + super(rowcount, protocol, JdbcSQLTypes); + this.pos = -1; + this.data = new Object[columncount]; + } + + @Override + public void addLines(AbstractProtocol protocol) throws ProtocolException { + int csrh = protocol.getCurrentServerResponse(); + if (csrh != ServerResponses.RESULT) { + throw new ProtocolException("protocol violation: unexpected line in data block: " + + protocol.getRemainingStringLine(0)); + } + if(this.pos == -1) { //if it's the first line, initialize the matrix + int numberOfColumns = this.data.length; + for (int i = 0 ; i < numberOfColumns ; i++) { + switch (this.jdbcSQLTypes[i]) { + case Types.INTEGER: + this.data[i] = new int[this.rowcount]; + break; + case Types.BOOLEAN: + case Types.TINYINT: + this.data[i] = new byte[this.rowcount]; + break; + case Types.SMALLINT: + this.data[i] = new short[this.rowcount]; + break; + case Types.REAL: + this.data[i] = new float[this.rowcount]; + break; + case Types.DOUBLE: + this.data[i] = new double[this.rowcount]; + break; + case Types.BIGINT: + this.data[i] = new long[this.rowcount]; + break; + case Types.DATE: + case Types.TIME: + case Types.TIME_WITH_TIMEZONE: + this.data[i] = new Calendar[this.rowcount]; + break; + case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: + this.data[i] = new TimestampHelper[this.rowcount]; + break; + case Types.NUMERIC: + case Types.DECIMAL: + this.data[i] = new BigDecimal[this.rowcount]; + break; + case Types.BLOB: + this.data[i] = new MonetBlob[this.rowcount]; + break; + case Types.CLOB: + this.data[i] = new MonetClob[this.rowcount]; + break; + case Types.LONGVARBINARY: + this.data[i] = new byte[this.rowcount][]; + break; + default: //CHAR, VARCHAR, OTHER + this.data[i] = new String[this.rowcount]; + } + } + } + // add to the backing array + int nextPos = this.pos + 1; + this.pos = ((OldMapiProtocol)this.protocol).parseTupleLines(nextPos, this.jdbcSQLTypes, this.data); + } + + /** + * Returns whether this Response expects more lines to be added to it. + * + * @return true if a next line should be added, false otherwise + */ + @Override + public boolean wantsMore() { + // remember: pos is the value already stored + return (this.pos + 1) < this.rowcount; + } + + /** + * Instructs the Response implementation to close and do the necessary clean up procedures. + */ + @Override + public void close() { + // feed all rows to the garbage collector + int numberOfColumns = this.data.length; + for (int i = 0; i < numberOfColumns; i++) { + data[i] = null; + } + data = null; + } + + /** + * Checks if a value in the current row is null. + * + * @param column The column index starting from 0 + * @return If the value is null or not. + */ + private boolean checkValueIsNull(int column) { + switch (this.jdbcSQLTypes[column]) { + case Types.BOOLEAN: + case Types.TINYINT: + this.lastReadWasNull = ((byte[]) this.data[column])[this.blockLine] == Byte.MIN_VALUE; + break; + case Types.SMALLINT: + this.lastReadWasNull = ((short[]) this.data[column])[this.blockLine] == Short.MIN_VALUE; + break; + case Types.INTEGER: + this.lastReadWasNull = ((int[]) this.data[column])[this.blockLine] == Integer.MIN_VALUE; + break; + case Types.BIGINT: + this.lastReadWasNull = ((long[]) this.data[column])[this.blockLine] == Long.MIN_VALUE; + break; + case Types.REAL: + this.lastReadWasNull = ((float[]) this.data[column])[this.blockLine] == Float.MIN_VALUE; + break; + case Types.DOUBLE: + this.lastReadWasNull = ((double[]) this.data[column])[this.blockLine] == Double.MIN_VALUE; + break; + default: + this.lastReadWasNull = ((Object[]) this.data[column])[this.blockLine] == null; + } + return this.lastReadWasNull; + } + + @Override + public boolean getBooleanValue(int column) { + return !this.checkValueIsNull(column) && ((byte[]) this.data[column])[this.blockLine] == 1; + } + + @Override + public byte getByteValue(int column) { + if(this.checkValueIsNull(column)) { + return 0; + } + return ((byte[]) this.data[column])[this.blockLine]; + } + + @Override + public short getShortValue(int column) { + if(this.checkValueIsNull(column)) { + return 0; + } + return ((short[]) this.data[column])[this.blockLine]; + } + + @Override + public int getIntValue(int column) { + if(this.checkValueIsNull(column)) { + return 0; + } + return ((int[]) this.data[column])[this.blockLine]; + } + + @Override + public long getLongValue(int column) { + if(this.checkValueIsNull(column)) { + return 0; + } + return ((long[]) this.data[column])[this.blockLine]; + } + + @Override + public float getFloatValue(int column) { + if(this.checkValueIsNull(column)) { + return 0.0f; + } + return ((float[]) this.data[column])[this.blockLine]; + } + + @Override + public double getDoubleValue(int column) { + if(this.checkValueIsNull(column)) { + return 0.0f; + } + return ((double[]) this.data[column])[this.blockLine]; + } + + @Override + public Object getObjectValue(int column) { + if(this.checkValueIsNull(column)) { + return null; + } + return ((Object[]) this.data[column])[this.blockLine]; + } + + @Override + public String getValueAsString(int column) { + switch (this.jdbcSQLTypes[column]) { + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.OTHER: + return ((String[]) this.data[column])[this.blockLine]; + case Types.LONGVARBINARY: + return Arrays.toString(((byte[][]) this.data[column])[this.blockLine]); + case Types.BOOLEAN: + return ((byte[]) this.data[column])[this.blockLine] == 1 ? "true" : "false"; + case Types.TINYINT: + return Byte.toString(((byte[]) this.data[column])[this.blockLine]); + case Types.SMALLINT: + return Short.toString(((short[]) this.data[column])[this.blockLine]); + case Types.INTEGER: + return Integer.toString(((int[]) this.data[column])[this.blockLine]); + case Types.BIGINT: + return Long.toString(((long[]) this.data[column])[this.blockLine]); + case Types.REAL: + return Float.toString(((float[]) this.data[column])[this.blockLine]); + case Types.DOUBLE: + return Double.toString(((double[]) this.data[column])[this.blockLine]); + case Types.DATE: + Date aux1 = new Date(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); + return protocol.getMonetDate().format(aux1); + case Types.TIME: + Time aux2 = new Time(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); + return protocol.getMonetTimePrinter().format(aux2); + case Types.TIME_WITH_TIMEZONE: + Time aux3 = new Time(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); + return protocol.getMonetTimeTzPrinter().format(aux3); + case Types.TIMESTAMP: + TimestampHelper thel = ((TimestampHelper[]) this.data[column])[this.blockLine]; + Timestamp aux4 = thel.getTimestamp(); + this.lastNanos = thel.getNanoseconds(); + return protocol.getMonetTimestampPrinter().format(aux4); + case Types.TIMESTAMP_WITH_TIMEZONE: + TimestampHelper thelper = ((TimestampHelper[]) this.data[column])[this.blockLine]; + Timestamp aux5 = thelper.getTimestamp(); + this.lastNanos = thelper.getNanoseconds(); + return protocol.getMonetTimestampTzPrinter().format(aux5); + default: //BLOB, CLOB, BigDecimal + return ((Object[]) this.data[column])[this.blockLine].toString(); + } + } + + @Override + public Object getValueAsObject(int column) { + switch (this.jdbcSQLTypes[column]) { + case Types.BOOLEAN: + return ((byte[]) this.data[column])[this.blockLine] == 1; + case Types.TINYINT: + return ((byte[]) this.data[column])[this.blockLine]; + case Types.SMALLINT: + return ((short[]) this.data[column])[this.blockLine]; + case Types.INTEGER: + return ((int[]) this.data[column])[this.blockLine]; + case Types.BIGINT: + return ((long[]) this.data[column])[this.blockLine]; + case Types.REAL: + return ((float[]) this.data[column])[this.blockLine]; + case Types.DOUBLE: + return ((double[]) this.data[column])[this.blockLine]; + case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: + TimestampHelper thelper = ((TimestampHelper[]) this.data[column])[this.blockLine]; + this.lastNanos = thelper.getNanoseconds(); + return thelper.getCalendar(); + default: + return ((Object[]) this.data[column])[this.blockLine]; + } + } + + @Override + public int getLastNanos() { + return this.lastNanos; + } +}
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java @@ -14,8 +14,8 @@ import nl.cwi.monetdb.mcl.protocol.Abstr import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.ServerResponses; import nl.cwi.monetdb.mcl.protocol.StarterHeaders; +import nl.cwi.monetdb.mcl.responses.AbstractDataBlockResponse; import nl.cwi.monetdb.mcl.responses.AutoCommitResponse; -import nl.cwi.monetdb.mcl.responses.DataBlockResponse; import nl.cwi.monetdb.mcl.responses.ResultSetResponse; import nl.cwi.monetdb.mcl.responses.UpdateResponse; @@ -168,7 +168,7 @@ public class OldMapiProtocol extends Abs /** * Gets the next UpdateResponse response from the server. * - * @return The UpdateResponse instance + * @return An UpdateResponse instance * @throws ProtocolException If an error in the underlying connection happened. */ @Override @@ -181,7 +181,7 @@ public class OldMapiProtocol extends Abs /** * Gets the next AutoCommitResponse response from the server. * - * @return The AutoCommitResponse instance + * @return An AutoCommitResponse instance * @throws ProtocolException If an error in the underlying connection happened. */ @Override @@ -190,6 +190,12 @@ public class OldMapiProtocol extends Abs return new AutoCommitResponse(ac); } + @Override + public AbstractDataBlockResponse getAnEmptyDataBlockResponse(int rowcount, int columncount, + AbstractProtocol protocol, int[] JdbcSQLTypes) { + return new OldMapiDataBlockResponse(rowcount, columncount, protocol, JdbcSQLTypes); + } + /** * Gets the next DataBlockResponse response from the server, belonging to a ResultSetResponse * @@ -199,7 +205,7 @@ public class OldMapiProtocol extends Abs * @throws ProtocolException If an error in the underlying connection happened. */ @Override - public DataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) + public AbstractDataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) throws ProtocolException { int id = OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this); //The order cannot be switched!! OldMapiStartOfHeaderParser.GetNextResponseDataAsInt(this); //column count @@ -240,8 +246,7 @@ public class OldMapiProtocol extends Abs * @return The number of lines parsed from the underlying connection * @throws ProtocolException If an error in the underlying connection happened. */ - @Override - public int parseTupleLines(int firstLineNumber, int[] typesMap, Object[] values) throws ProtocolException { + int parseTupleLines(int firstLineNumber, int[] typesMap, Object[] values) throws ProtocolException { OldMapiTupleLineParser.OldMapiParseTupleLine(this, firstLineNumber, typesMap, values); return firstLineNumber; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiServerResponseParser.java @@ -17,6 +17,8 @@ import nl.cwi.monetdb.mcl.protocol.Serve */ final class OldMapiServerResponseParser { + private OldMapiServerResponseParser() {} + /** * Retrieves the next server response from an old MAPI protocol instance. *
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiStartOfHeaderParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiStartOfHeaderParser.java @@ -19,6 +19,8 @@ import nl.cwi.monetdb.mcl.protocol.Start */ final class OldMapiStartOfHeaderParser { + private OldMapiStartOfHeaderParser() {} + static int GetNextStartHeaderOnOldMapi(OldMapiProtocol protocol) { int res; switch (protocol.lineBuffer.get()) {
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java @@ -21,6 +21,8 @@ import java.nio.CharBuffer; */ final class OldMapiTableHeaderParser { + private OldMapiTableHeaderParser() {} + /** * Retrieves the next table result set header and fills the respective array of values. *
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java @@ -29,6 +29,8 @@ import java.util.Calendar; */ final class OldMapiTupleLineParser { + private OldMapiTupleLineParser() {} + /** * The character array representation of a NULL value found in a tuple. */ @@ -175,7 +177,6 @@ final class OldMapiTupleLineParser { break; } } - protocol.tupleLineBuffer = tupleLineBuffer; // check if this result is of the size we expected it to be if (column != typesMap.length)
deleted file mode 100644 --- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParserHelper.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V. - */ - -package nl.cwi.monetdb.mcl.protocol.oldmapi; - -import nl.cwi.monetdb.mcl.protocol.ProtocolException; - -/** - * This is a helper Class for the OldMapiTupleLineParser. The main objective of this class is to parse primitive types - * without any memory allocation for performance reasons. The code may seem to be boilerplate, but it has to be done - * this way to due to poor typing of the Java programming language. - * - * @author Pedro Ferreira - */ -final class OldMapiTupleLineParserHelper { - - /** - * Checks if a char[] (target) is inside on another (source), retrieving the first index on the source, where the - * target is, if found. In other words this a Java implementation of the strstr function from the C standard. - * As we search always from the beginning of the source, the start parameter is not used. - * - * @param source The source char[] to search - * @param sourceCount The number of characters in the source array to search - * @param target The target char[] to be found - * @param targetCount The result set column SQL types - * @return The integer representation of the Table Result Header retrieved - */ - static int CharIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, - int targetCount, int fromIndex) { - if (fromIndex >= sourceCount) { - return (targetCount == 0 ? sourceCount : -1); - } - if (fromIndex < 0) { - fromIndex = 0; - } - if (targetCount == 0) { - return fromIndex; - } - - char first = target[targetOffset]; - int max = sourceOffset + (sourceCount - targetCount); - - for (int i = sourceOffset + fromIndex; i <= max; i++) { - /* Look for first character. */ - if (source[i] != first) { - while (++i <= max && source[i] != first); - } - if (i <= max) { - int j = i + 1; - int end = j + targetCount - 1; - for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++); - - if (j == end) { - /* Found whole string. */ - return i - sourceOffset; - } - } - } - return -1; - } - - /** - * The character array representation of a TRUE value. - */ - private static final char[] TrueConstant = new char[]{'t','r','u','e'}; - - /** - * Converts a segment of a CharBuffer's backing array into a Java boolean. - * - * @param start The first position in the array to parse - * @param data The CharBuffer's backing array to parse - * @return 1 it's a true value, 0 if false - */ - static byte CharArrayToBoolean(char[] data, int start) { - return CharIndexOf(data, 0, data.length, TrueConstant, 0, 4, start) == start ? (byte)1 : (byte)0; - } - - /** - * Converts a segment of a CharBuffer's backing array into a Java byte. - * - * @param data The CharBuffer's backing array to parse - * @param start The first position in the array to parse - * @param count The number of characters to read from the starter position - * @return The parsed byte value - */ - static byte CharArrayToByte(char[] data, int start, int count) throws ProtocolException { - byte tmp = 0; - int limit = start + count; - boolean positive = true; - char chr = data[start++]; - - if (chr >= '0' && chr <= '9') { - tmp = (byte)(chr - '0'); - } else if(chr == '-') { - positive = false; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - while (start < limit) { - chr = data[start++]; - if(chr == ' ') { - break; - } - tmp *= 10; - if (chr >= '0' && chr <= '9') { - tmp += chr - '0'; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - } - return positive ? tmp : (byte) -tmp; - } - - /** - * Converts a segment of a CharBuffer's backing array into a Java short. - * - * @param data The CharBuffer's backing array to parse - * @param start The first position in the array to parse - * @param count The number of characters to read from the starter position - * @return The parsed short value - */ - static short CharArrayToShort(char[] data, int start, int count) throws ProtocolException { - short tmp = 0; - int limit = start + count; - boolean positive = true; - char chr = data[start++]; - - if (chr >= '0' && chr <= '9') { - tmp = (short)(chr - '0'); - } else if(chr == '-') { - positive = false; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - while (start < limit) { - chr = data[start++]; - if(chr == ' ') { - break; - } - tmp *= 10; - if (chr >= '0' && chr <= '9') { - tmp += chr - '0'; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - } - return positive ? tmp : (short) -tmp; - } - - /** - * Converts a segment of a CharBuffer's backing array into a Java int. - * - * @param data The CharBuffer's backing array to parse - * @param start The first position in the array to parse - * @param count The number of characters to read from the starter position - * @return The parsed int value - */ - static int CharArrayToInt(char[] data, int start, int count) throws ProtocolException { - int tmp = 0, limit = start + count; - boolean positive = true; - char chr = data[start++]; - - if (chr >= '0' && chr <= '9') { - tmp = chr - '0'; - } else if(chr == '-') { - positive = false; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - while (start < limit) { - chr = data[start++]; - if(chr == ' ') { - break; - } else if(chr == '.') { //for intervals - continue; - } - tmp *= 10; - if (chr >= '0' && chr <= '9') { - tmp += (int)chr - (int)'0'; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - } - return positive ? tmp : -tmp; - } - - /** - * Converts a segment of a CharBuffer's backing array into a Java long. - * - * @param data The CharBuffer's backing array to parse - * @param start The first position in the array to parse - * @param count The number of characters to read from the starter position - * @return The parsed long value - */ - static long CharArrayToLong(char[] data, int start, int count) throws ProtocolException { - long tmp = 0; - int limit = start + count; - boolean positive = true; - char chr = data[start++]; - - if (chr >= '0' && chr <= '9') { - tmp = chr - '0'; - } else if(chr == '-') { - positive = false; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - while (start < limit) { - chr = data[start++]; - if(chr == ' ') { - break; - } else if(chr == '.') { //for intervals - continue; - } - tmp *= 10; - if (chr >= '0' && chr <= '9') { - tmp += chr - '0'; - } else { - throw new ProtocolException("Expected a digit at the position " + (start - 1)); - } - } - return positive ? tmp : -tmp; - } -}
new file mode 100644 --- /dev/null +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/AbstractDataBlockResponse.java @@ -0,0 +1,212 @@ +package nl.cwi.monetdb.mcl.responses; + +import nl.cwi.monetdb.jdbc.MonetResultSet; +import nl.cwi.monetdb.mcl.connection.helpers.GregorianCalendarParser; +import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; +import nl.cwi.monetdb.mcl.protocol.ProtocolException; + +import java.sql.SQLException; +import java.sql.Types; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +/** + * The DataBlockResponse is tabular data belonging to a ResultSetResponse. On a MAPI connection, tabular data from the + * server typically looks like: + * <pre> + * [ "value", 56 ] + * </pre> + * where each column is separated by ",\t" and each tuple surrounded by brackets ("[" and "]"). A DataBlockResponse + * object holds the raw data as read from the server, in a parsed manner, ready for easy retrieval. Meanwhile on an + * Embedded connection, the data is automatically parsed. + * + * This object is not intended to be queried by multiple threads synchronously. It is designed to work for one thread + * retrieving rows from it. When multiple threads will retrieve rows from this object, it is possible for threads to + * get the same data. + * + * @author Fabian Groffen, Pedro Ferreira + */ +public abstract class AbstractDataBlockResponse implements IIncompleteResponse { + + /** The connection protocol to parse the tuple lines */ + public final AbstractProtocol protocol; + /** A 'pointer' to the current line */ + public int blockLine; + /** The number of rows in the block */ + public final int rowcount; + /** The JdbcSQLTypes mapping */ + public final int[] jdbcSQLTypes; + /** whether the last read field (via some getXyz() method) was NULL */ + public boolean lastReadWasNull = true; + + /** + * Constructs an AbstractDataBlockResponse object. + * + * @param rowcount the number of rows + * @param protocol the underlying protocol + * @param JdbcSQLTypes an array of the JDBC mappings of the columns + */ + public AbstractDataBlockResponse(int rowcount, AbstractProtocol protocol, int[] JdbcSQLTypes) { + this.rowcount = rowcount; + this.protocol = protocol; + this.jdbcSQLTypes = JdbcSQLTypes; + } + + /** + * Returns whether this Response expects more lines to be added to it. + * + * @return true if a next line should be added, false otherwise + */ + @Override + public abstract boolean wantsMore(); + + /** + * addLines adds a batch of rows to the block. Before adding the first line, the column arrays are allocated. + * + * @param protocol The connection's protocol to fetch data from + * @throws ProtocolException If the result line is not expected + */ + @Override + public abstract void addLines(AbstractProtocol protocol) throws ProtocolException; + + /* Methods to be called after the block construction has been completed */ + + /** + * Sets the current line number on the block. + * + * @param blockLine the block line number + */ + void setBlockLine(int blockLine) { + this.blockLine = blockLine; + } + + /** + * Returns if the last value read was null or not. + * + * @return If the last value read was null or not + */ + public boolean isLastReadWasNull() { + return lastReadWasNull; + } + + /** + * Gets the current row value as a Java Boolean. + * + * @param column The column index starting from 0 + * @return A Java Boolean if the column is a boolean, otherwise a ClassCastException is thrown + */ + public abstract boolean getBooleanValue(int column); + + /** + * Gets the current row value as a Java Byte. + * + * @param column The column index starting from 0 + * @return A Java Byte if the column is a tinyint, otherwise a ClassCastException is thrown + */ + public abstract byte getByteValue(int column); + + /** + * Gets the current row value as a Java Short. + * + * @param column The column index starting from 0 + * @return A Java Short if the column is a smallint, otherwise a ClassCastException is thrown + */ + public abstract short getShortValue(int column); + + /** + * Gets the current row value as a Java Integer. + * + * @param column The column index starting from 0 + * @return A Java Integer if the column is an integer or month_interval, otherwise a ClassCastException is thrown + */ + public abstract int getIntValue(int column); + + /** + * Gets the current row value as a Java Long. + * + * @param column The column index starting from 0 + * @return A Java Long if the column is a bigint or second_interval, otherwise a ClassCastException is thrown + */ + public abstract long getLongValue(int column); + + /** + * Gets the current row value as a Java Float. + * + * @param column The column index starting from 0 + * @return A Java Float if the column is a real, otherwise a ClassCastException is thrown + */ + public abstract float getFloatValue(int column); + + /** + * Gets the current row value as a Java Double. + * + * @param column The column index starting from 0 + * @return A Java Double if the column is a double, otherwise a ClassCastException is thrown + */ + public abstract double getDoubleValue(int column); + + /** + * Gets the current row value as a Java Object. + * + * @param column The column index starting from 0 + * @return A Java Object if the column is not a primitive type, otherwise a ClassCastException is thrown + */ + public abstract Object getObjectValue(int column) throws ProtocolException; + + /** + * Gets the current row value as a Java String. + * + * @param column The column index starting from 0 + * @return The String representation of the data type + */ + public abstract String getValueAsString(int column) throws ProtocolException; + + /** + * Gets the current row value as a Java Object. + * + * @param column The column index starting from 0 + * @return The Object representation of the data type + */ + public abstract Object getValueAsObject(int column) throws ProtocolException; + + /** + * To parse a String column in a date, time or timestamp instance, this method is used. + * + * @param mrs A MonetResultSet instance where warning can be added + * @param column The column index starting from 0 + * @param jdbcType The JDBC type of the column desired to convert + * @return A {@link Calendar} instance of the parsed date + * @throws SQLException If the conversation cannot be performed + */ + public Calendar getDateValueFromString(MonetResultSet mrs, int column, int jdbcType) throws SQLException { + try { + String value = this.getObjectValue(column).toString(); + SimpleDateFormat aux; + switch (jdbcType) { + case Types.DATE: + aux = protocol.getMonetDate(); + break; + case Types.TIME: + case Types.TIME_WITH_TIMEZONE: + aux = protocol.getMonetTimePrinter(); + break; + case Types.TIMESTAMP: + case Types.TIMESTAMP_WITH_TIMEZONE: + aux = protocol.getMonetTimestampPrinter(); + break; + default: + throw new SQLException("Internal error!", "M1M05"); + } + return GregorianCalendarParser.ParseDateString(mrs, value, protocol.getMonetParserPosition(), aux, jdbcType); + } catch (ProtocolException e) { + throw new SQLException(e); + } + } + + /** + * Get the last parsed nanoseconds value. + * + * @return The last parsed nanoseconds value + */ + public abstract int getLastNanos(); +}
deleted file mode 100644 --- a/src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Copyright 1997 - July 2008 CWI, August 2008 - 2017 MonetDB B.V. - */ - -package nl.cwi.monetdb.mcl.responses; - -import nl.cwi.monetdb.jdbc.MonetBlob; -import nl.cwi.monetdb.jdbc.MonetClob; -import nl.cwi.monetdb.jdbc.MonetConnection; -import nl.cwi.monetdb.jdbc.MonetResultSet; -import nl.cwi.monetdb.mcl.connection.helpers.GregorianCalendarParser; -import nl.cwi.monetdb.mcl.connection.helpers.TimestampHelper; -import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; -import nl.cwi.monetdb.mcl.protocol.ProtocolException; -import nl.cwi.monetdb.mcl.protocol.ServerResponses; - -import java.math.BigDecimal; -import java.sql.*; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Calendar; - -/** - * The DataBlockResponse is tabular data belonging to a ResultSetResponse. On a MAPI connection, tabular data from the - * server typically looks like: - * <pre> - * [ "value", 56 ] - * </pre> - * where each column is separated by ",\t" and each tuple surrounded by brackets ("[" and "]"). A DataBlockResponse - * object holds the raw data as read from the server, in a parsed manner, ready for easy retrieval. Meanwhile on an - * Embedded connection, the data is automatically parsed. - * - * This object is not intended to be queried by multiple threads synchronously. It is designed to work for one thread - * retrieving rows from it. When multiple threads will retrieve rows from this object, it is possible for threads to - * get the same data. - * - * @author Fabian Groffen, Pedro Ferreira - */ -public class DataBlockResponse implements IIncompleteResponse { - - /** The array to keep the data in */ - private Object[] data; - /** The counter which keeps the current position in the data array */ - private int pos; - /** If the underlying connection is embedded or not */ - private final boolean isEmbedded; - /** The connection protocol to parse the tuple lines */ - private final AbstractProtocol protocol; - /** The JdbcSQLTypes mapping */ - private final int[] jdbcSQLTypes; - /** A 'pointer' to the current line */ - private int blockLine; - /** The number of rows in the block */ - private final int rowcount; - - /** - * Constructs a DataBlockResponse object. - * - * @param rowcount the number of rows - * @param columncount the number of columns - * @param protocol the underlying protocol - * @param JdbcSQLTypes an array of the JDBC mappings of the columns - */ - DataBlockResponse(int rowcount, int columncount, MonetConnection connection, AbstractProtocol protocol, - int[] JdbcSQLTypes) { - this.pos = -1; - this.rowcount = rowcount; - this.data = new Object[columncount]; - this.isEmbedded = connection.isEmbedded(); - this.protocol = protocol; - this.jdbcSQLTypes = JdbcSQLTypes; - } - - /** - * addLines adds a batch of rows to the block. Before adding the first line, the column arrays are allocated. - * - * @param protocol The connection's protocol to fetch data from - * @throws ProtocolException If the result line is not expected - */ - @Override - public void addLines(AbstractProtocol protocol) throws ProtocolException { - int csrh = protocol.getCurrentServerResponse(); - if (csrh != ServerResponses.RESULT) { - throw new ProtocolException("protocol violation: unexpected line in data block: " + - protocol.getRemainingStringLine(0)); - } - - if(this.pos == -1) { //if it's the first line, initialize the matrix - int numberOfColumns = this.data.length; - for (int i = 0 ; i < numberOfColumns ; i++) { - switch (this.jdbcSQLTypes[i]) { - case Types.INTEGER: - this.data[i] = new int[this.rowcount]; - break; - case Types.BOOLEAN: - case Types.TINYINT: - this.data[i] = new byte[this.rowcount]; - break; - case Types.SMALLINT: - this.data[i] = new short[this.rowcount]; - break; - case Types.REAL: - this.data[i] = new float[this.rowcount]; - break; - case Types.DOUBLE: - this.data[i] = new double[this.rowcount]; - break; - case Types.BIGINT: - this.data[i] = new long[this.rowcount]; - break; - case Types.DATE: - case Types.TIME: - case Types.TIME_WITH_TIMEZONE: - this.data[i] = new Calendar[this.rowcount]; - break; - case Types.TIMESTAMP: - case Types.TIMESTAMP_WITH_TIMEZONE: - if(this.isEmbedded) { - this.data[i] = new Calendar[this.rowcount]; - } else { - this.data[i] = new TimestampHelper[this.rowcount]; - } - break; - case Types.NUMERIC: - case Types.DECIMAL: - this.data[i] = new BigDecimal[this.rowcount]; - break; - case Types.BLOB: - this.data[i] = new MonetBlob[this.rowcount]; - break; - case Types.CLOB: - this.data[i] = new MonetClob[this.rowcount]; - break; - case Types.LONGVARBINARY: - this.data[i] = new byte[this.rowcount][]; - break; - default: //CHAR, VARCHAR, OTHER - this.data[i] = new String[this.rowcount]; - } - } - } - - // add to the backing array - int nextPos = this.pos + 1; - this.pos = this.protocol.parseTupleLines(nextPos, this.jdbcSQLTypes, this.data); - } - - /** - * Returns whether this Response expects more lines to be added to it. - * - * @return true if a next line should be added, false otherwise - */ - @Override - public boolean wantsMore() { - // remember: pos is the value already stored - return (this.pos + 1) < this.rowcount; - } - - /** - * Instructs the Response implementation to close and do the necessary clean up procedures. - */ - @Override - public void close() { - // feed all rows to the garbage collector - int numberOfColumns = this.data.length; - for (int i = 0; i < numberOfColumns; i++) { - data[i] = null; - } - data = null; - } - - /* Methods to be called after the block construction has been completed */ - - /** - * Sets the current line number on the block. - * - * @param blockLine the block line number - */ - void setBlockLine(int blockLine) { - this.blockLine = blockLine; - } - - /** - * Checks if a value in the current row is null. - * - * @param column The column index starting from 0 - * @return If the value is null or not. - */ - public boolean checkValueIsNull(int column) { - switch (this.jdbcSQLTypes[column]) { - case Types.BOOLEAN: - case Types.TINYINT: - return ((byte[]) this.data[column])[this.blockLine] == Byte.MIN_VALUE; - case Types.SMALLINT: - return ((short[]) this.data[column])[this.blockLine] == Short.MIN_VALUE; - case Types.INTEGER: - return ((int[]) this.data[column])[this.blockLine] == Integer.MIN_VALUE; - case Types.BIGINT: - return ((long[]) this.data[column])[this.blockLine] == Long.MIN_VALUE; - case Types.REAL: - return ((float[]) this.data[column])[this.blockLine] == Float.MIN_VALUE; - case Types.DOUBLE: - return ((double[]) this.data[column])[this.blockLine] == Double.MIN_VALUE; - default: - return ((Object[]) this.data[column])[this.blockLine] == null; - } - } - - /** - * Gets the current row value as a Java Boolean. - * - * @param column The column index starting from 0 - * @return A Java Boolean if the column is a boolean, otherwise a ClassCastException is thrown - */ - public boolean getBooleanValue(int column) { - return ((byte[]) this.data[column])[this.blockLine] == 1; - } - - /** - * Gets the current row value as a Java Byte. - * - * @param column The column index starting from 0 - * @return A Java Byte if the column is a tinyint, otherwise a ClassCastException is thrown - */ - public byte getByteValue(int column) { - return ((byte[]) this.data[column])[this.blockLine]; - } - - /** - * Gets the current row value as a Java Short. - * - * @param column The column index starting from 0 - * @return A Java Short if the column is a smallint, otherwise a ClassCastException is thrown - */ - public short getShortValue(int column) { - return ((short[]) this.data[column])[this.blockLine]; - } - - /** - * Gets the current row value as a Java Integer. - * - * @param column The column index starting from 0 - * @return A Java Integer if the column is an integer or month_interval, otherwise a ClassCastException is thrown - */ - public int getIntValue(int column) { - return ((int[]) this.data[column])[this.blockLine]; - } - - /** - * Gets the current row value as a Java Long. - * - * @param column The column index starting from 0 - * @return A Java Long if the column is a bigint or second_interval, otherwise a ClassCastException is thrown - */ - public long getLongValue(int column) { - return ((long[]) this.data[column])[this.blockLine]; - } - - /** - * Gets the current row value as a Java Float. - * - * @param column The column index starting from 0 - * @return A Java Float if the column is a real, otherwise a ClassCastException is thrown - */ - public float getFloatValue(int column) { - return ((float[]) this.data[column])[this.blockLine]; - } - - /** - * Gets the current row value as a Java Double. - * - * @param column The column index starting from 0 - * @return A Java Double if the column is a double, otherwise a ClassCastException is thrown - */ - public double getDoubleValue(int column) { - return ((double[]) this.data[column])[this.blockLine]; - } - - /** - * Gets the current row value as a Java Object. - * - * @param column The column index starting from 0 - * @return A Java Object if the column is not a primitive type, otherwise a ClassCastException is thrown - */ - public Object getObjectValue(int column) { - return ((Object[]) this.data[column])[this.blockLine]; - } - - /** - * To parse a String column in a date, time or timestamp instance, this method is used. - - * @param mrs A MonetResultSet instance where warning can be added * - * @param column The column index starting from 0 - * @param jdbcType The JDBC type of the column desired to convert - * @return A {@link Calendar} instance of the parsed date - * @throws SQLException If the conversation cannot be performed - */ - public Calendar getDateValueFromString(MonetResultSet mrs, int column, int jdbcType) throws SQLException { - String value = ((Object[]) this.data[column])[this.blockLine].toString(); - SimpleDateFormat aux; - switch (jdbcType) { - case Types.DATE: - aux = protocol.getMonetDate(); - break; - case Types.TIME: - case Types.TIME_WITH_TIMEZONE: - aux = protocol.getMonetTimePrinter(); - break; - case Types.TIMESTAMP: - case Types.TIMESTAMP_WITH_TIMEZONE: - aux = protocol.getMonetTimestampPrinter(); - break; - default: - throw new SQLException("Internal error!", "M1M05"); - } - return GregorianCalendarParser.ParseDateString(mrs, value, protocol.getMonetParserPosition(), aux, jdbcType); - } - - public int getNanos(int column) { - if(isEmbedded) { - return 0; - } else { - return ((TimestampHelper[]) this.data[column])[this.blockLine].getTimestamp().getNanos(); - } - } - - /** - * Gets the current row value as a Java String. - * - * @param column The column index starting from 0 - * @return The String representation of the data type - */ - public String getValueAsString(int column) { - switch (this.jdbcSQLTypes[column]) { - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.OTHER: - return ((String[]) this.data[column])[this.blockLine]; - case Types.LONGVARBINARY: - return Arrays.toString(((byte[][]) this.data[column])[this.blockLine]); - case Types.BOOLEAN: - return ((byte[]) this.data[column])[this.blockLine] == 1 ? "true" : "false"; - case Types.TINYINT: - return Byte.toString(((byte[]) this.data[column])[this.blockLine]); - case Types.SMALLINT: - return Short.toString(((short[]) this.data[column])[this.blockLine]); - case Types.INTEGER: - return Integer.toString(((int[]) this.data[column])[this.blockLine]); - case Types.BIGINT: - return Long.toString(((long[]) this.data[column])[this.blockLine]); - case Types.REAL: - return Float.toString(((float[]) this.data[column])[this.blockLine]); - case Types.DOUBLE: - return Double.toString(((double[]) this.data[column])[this.blockLine]); - case Types.DATE: - Date aux1 = new Date(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); - return protocol.getMonetDate().format(aux1); - case Types.TIME: - Time aux2 = new Time(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); - return protocol.getMonetTimePrinter().format(aux2); - case Types.TIME_WITH_TIMEZONE: - Time aux3 = new Time(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); - return protocol.getMonetTimeTzPrinter().format(aux3); - case Types.TIMESTAMP: - Timestamp aux4; - if(this.isEmbedded) { - aux4 = new Timestamp(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); - } else { - aux4 = ((TimestampHelper[]) this.data[column])[this.blockLine].getTimestamp(); - } - return protocol.getMonetTimestampPrinter().format(aux4); - case Types.TIMESTAMP_WITH_TIMEZONE: - Timestamp aux5; - if(this.isEmbedded) { - aux5 = new Timestamp(((Calendar[]) this.data[column])[this.blockLine].getTimeInMillis()); - } else { - aux5 = ((TimestampHelper[]) this.data[column])[this.blockLine].getTimestamp(); - } - return protocol.getMonetTimestampTzPrinter().format(aux5); - default: //BLOB, CLOB, BigDecimal - return ((Object[]) this.data[column])[this.blockLine].toString(); - } - } - - /** - * Gets the current row value as a Java Object. - * - * @param column The column index starting from 0 - * @return The Object representation of the data type - */ - public Object getValueAsObject(int column) { - switch (this.jdbcSQLTypes[column]) { - case Types.BOOLEAN: - return ((byte[]) this.data[column])[this.blockLine] == 1; - case Types.TINYINT: - return ((byte[]) this.data[column])[this.blockLine]; - case Types.SMALLINT: - return ((short[]) this.data[column])[this.blockLine]; - case Types.INTEGER: - return ((int[]) this.data[column])[this.blockLine]; - case Types.BIGINT: - return ((long[]) this.data[column])[this.blockLine]; - case Types.REAL: - return ((float[]) this.data[column])[this.blockLine]; - case Types.DOUBLE: - return ((double[]) this.data[column])[this.blockLine]; - case Types.TIMESTAMP: - case Types.TIMESTAMP_WITH_TIMEZONE: - if(isEmbedded) { - return ((Calendar[]) this.data[column])[this.blockLine]; - } else { - return ((TimestampHelper[]) this.data[column])[this.blockLine].getCalendar(); - } - default: - return ((Object[]) this.data[column])[this.blockLine]; - } - } -}
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java @@ -73,7 +73,7 @@ public class ResultSetResponse implement /** the offset to be used on Xexport queries */ private int blockOffset = 0; /** A List of result blocks (chunks of size fetchSize/cacheSize) */ - private final DataBlockResponse[] resultBlocks; + private final AbstractDataBlockResponse[] resultBlocks; /** * Sole constructor, which requires a MonetConnection parent to be given. @@ -121,8 +121,9 @@ public class ResultSetResponse implement this.columnLengths = new int[columncount]; this.JdbcSQLTypes = new int[columncount]; - this.resultBlocks = new DataBlockResponse[(tuplecount / cacheSize) + 1]; - this.resultBlocks[0] = new DataBlockResponse(rowcount, columncount, con, con.getProtocol(), this.JdbcSQLTypes); + this.resultBlocks = new AbstractDataBlockResponse[(tuplecount / cacheSize) + 1]; + this.resultBlocks[0] = con.getProtocol().getAnEmptyDataBlockResponse(rowcount, columncount, + con.getProtocol(), this.JdbcSQLTypes); } /** @@ -158,9 +159,10 @@ public class ResultSetResponse implement * @param offset the offset number of rows for this block * @param rowcount the number of rows for this block */ - public DataBlockResponse addDataBlockResponse(int offset, int rowcount) { + public AbstractDataBlockResponse addDataBlockResponse(int offset, int rowcount) { int block = (offset - blockOffset) / cacheSize; - DataBlockResponse res = new DataBlockResponse(rowcount, this.name.length, this.con, this.con.getProtocol(), JdbcSQLTypes); + AbstractDataBlockResponse res = con.getProtocol().getAnEmptyDataBlockResponse(rowcount, + this.getColumncount(), this.con.getProtocol(), JdbcSQLTypes); resultBlocks[block] = res; return res; } @@ -316,7 +318,7 @@ public class ResultSetResponse implement * @return the exact row read as requested or null if the requested row is out of the scope of the result set * @throws SQLException if an database error occurs */ - public DataBlockResponse getDataBlockCorrespondingToLine(int row) throws SQLException { + public AbstractDataBlockResponse getDataBlockCorrespondingToLine(int row) throws SQLException { if (row >= tuplecount || row < 0) return null; @@ -324,7 +326,7 @@ public class ResultSetResponse implement int blockLine = (row - blockOffset) % cacheSize; // do we have the right block loaded? (optimistic try) - DataBlockResponse rawr; + AbstractDataBlockResponse rawr; // load block if appropriate if ((rawr = resultBlocks[block]) == null) { // TODO: ponder about a maximum number of blocks to keep in memory when dealing with random access to @@ -387,7 +389,7 @@ public class ResultSetResponse implement // close the data block associated with us for (int i = 1; i < resultBlocks.length; i++) { - DataBlockResponse r = resultBlocks[i]; + AbstractDataBlockResponse r = resultBlocks[i]; if (r != null) r.close(); }