Mercurial > hg > monetdb-java
changeset 73:953422c41194 embedded
The data retrieval in ResultSets is now Column wise. Ready to start the embedded integrate, but it has to perform extra tests for the more rare types.
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 @@ -3,7 +3,7 @@ package nl.cwi.monetdb.jdbc; import nl.cwi.monetdb.jdbc.types.INET; import nl.cwi.monetdb.jdbc.types.URL; import nl.cwi.monetdb.mcl.connection.*; -import nl.cwi.monetdb.mcl.connection.socket.MapiLanguage; +import nl.cwi.monetdb.mcl.connection.mapi.MapiLanguage; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; import nl.cwi.monetdb.mcl.protocol.ServerResponses; @@ -1344,9 +1344,8 @@ public abstract class MonetConnection ex } /** - * A list of Response objects. Responses are added to this list. - * Methods of this class are not synchronized. This is left as - * responsibility to the caller to prevent concurrent access. + * A list of Response objects. Responses are added to this list. Methods of this class are not synchronized. This is + * left as responsibility to the caller to prevent concurrent access. */ public class ResponseList { @@ -1368,9 +1367,8 @@ public abstract class MonetConnection ex private int curResponse = -1; /** - * Main constructor. The query argument can either be a String - * or List. An SQLException is thrown if another object - * instance is supplied. + * Main constructor. The query argument can either be a String or List. An SQLException is thrown if another + * object instance is supplied. * * @param cachesize overall cachesize to use * @param maxrows maximum number of rows to allow in the set @@ -1419,8 +1417,7 @@ public abstract class MonetConnection ex } curResponse++; if (curResponse >= responses.size()) { - // ResponseList is obviously completed so, there are no - // more responses + // ResponseList is obviously completed so, there are no more responses return null; } else { // return this response
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java @@ -293,12 +293,12 @@ final public class MonetDriver implement typeMap.put("date", Types.DATE); typeMap.put("decimal", Types.DECIMAL); typeMap.put("double", Types.DOUBLE); - typeMap.put("geometry", Types.VARCHAR); - typeMap.put("geometrya", Types.VARCHAR); + typeMap.put("geometry", Types.OTHER); + typeMap.put("geometrya", Types.OTHER); typeMap.put("hugeint", Types.NUMERIC); //but we will convert to java.math.BigInteger - typeMap.put("inet", Types.VARCHAR); + typeMap.put("inet", Types.OTHER); typeMap.put("int", Types.INTEGER); - typeMap.put("json", Types.VARCHAR); + typeMap.put("json", Types.OTHER); // typeMap.put("mbr", Types.???); typeMap.put("month_interval", Types.INTEGER); // typeMap.put("oid", Types.BIGINT); @@ -314,8 +314,8 @@ final public class MonetDriver implement typeMap.put("timetz", Types.TIME); // new in Java 8: Types.TIME_WITH_TIMEZONE (value 2013). Can't use it yet as we compile for java 7 typeMap.put("tinyint", Types.TINYINT); - typeMap.put("url", Types.VARCHAR); - typeMap.put("uuid", Types.VARCHAR); + typeMap.put("url", Types.OTHER); + typeMap.put("uuid", Types.OTHER); typeMap.put("varchar", Types.VARCHAR); typeMap.put("wrd", Types.BIGINT); }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetPreparedStatement.java @@ -67,6 +67,20 @@ import java.util.Map; * @version 0.4 */ public class MonetPreparedStatement extends MonetStatement implements PreparedStatement { + + /* only parse the date patterns once, use multiple times */ + /** Format of a timestamp with RFC822 time zone */ + private static final SimpleDateFormat MTimestampZ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); + /** Format of a timestamp */ + private static final SimpleDateFormat MTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + /** Format of a time with RFC822 time zone */ + private static final SimpleDateFormat MTimeZ = new SimpleDateFormat("HH:mm:ss.SSSZ"); + /** Format of a time */ + private static final SimpleDateFormat MTime = new SimpleDateFormat("HH:mm:ss.SSS"); + /** Format of a date used by mserver */ + private static final SimpleDateFormat MDate = new SimpleDateFormat("yyyy-MM-dd"); + + private final MonetConnection connection; private final String[] monetdbType; private final int[] javaType; private final int[] digits; @@ -77,28 +91,11 @@ public class MonetPreparedStatement exte private final int id; private final int size; private final int rscolcnt; - private final String[] values; - - private final MonetConnection connection; - - /* only parse the date patterns once, use multiple times */ - /** Format of a timestamp with RFC822 time zone */ - private final SimpleDateFormat mTimestampZ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); - /** Format of a timestamp */ - private final SimpleDateFormat mTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - /** Format of a time with RFC822 time zone */ - private final SimpleDateFormat mTimeZ = new SimpleDateFormat("HH:mm:ss.SSSZ"); - /** Format of a time */ - private final SimpleDateFormat mTime = new SimpleDateFormat("HH:mm:ss.SSS"); - /** Format of a date used by mserver */ - private final SimpleDateFormat mDate = new SimpleDateFormat("yyyy-MM-dd"); /** - * MonetPreparedStatement constructor which checks the arguments for - * validity. A MonetPreparedStatement is backed by a - * {@link MonetStatement}, which deals with most of the required stuff of - * this class. + * MonetPreparedStatement constructor which checks the arguments for validity. A MonetPreparedStatement is backed + * by a {@link MonetStatement}, which deals with most of the required stuff of this class. * * @param connection the connection that created this Statement * @param resultSetType type of {@link ResultSet} to produce @@ -153,8 +150,7 @@ public class MonetPreparedStatement exte //== methods interface PreparedStatement /** - * Adds a set of parameters to this PreparedStatement object's batch - * of commands. + * Adds a set of parameters to this PreparedStatement object's batch of commands. * * @throws SQLException if a database access error occurs */ @@ -197,10 +193,9 @@ public class MonetPreparedStatement exte * getUpdateCount to retrieve the result; you must call * getMoreResults to move to any subsequent result(s). * - * @return true if the first result is a ResultSet object; false if the - * first result is an update count or there is no result - * @throws SQLException if a database access error occurs or an argument - * is supplied to this method + * @return true if the first result is a ResultSet object; false if the first result is an update count or there is + * no result + * @throws SQLException if a database access error occurs or an argument is supplied to this method */ @Override public boolean execute() throws SQLException { @@ -214,13 +209,10 @@ public class MonetPreparedStatement exte } /** - * Executes the SQL query in this PreparedStatement object and returns the - * ResultSet object generated by the query. + * Executes the SQL query in this PreparedStatement object and returns the ResultSet object generated by the query. * - * @return a ResultSet object that contains the data produced by the query; - * never null - * @throws SQLException if a database access error occurs or the SQL - * statement does not return a ResultSet object + * @return a ResultSet object that contains the data produced by the query never null + * @throws SQLException if a database access error occurs or the SQL statement does not return a ResultSet object */ @Override public ResultSet executeQuery() throws SQLException { @@ -243,8 +235,7 @@ public class MonetPreparedStatement exte * * @return either (1) the row count for INSERT, UPDATE, or DELETE * statements or (2) 0 for SQL statements that return nothing - * @throws SQLException if a database access error occurs or the SQL - * statement returns a ResultSet object + * @throws SQLException if a database access error occurs or the SQL statement returns a ResultSet object */ @Override public int executeUpdate() throws SQLException { @@ -275,6 +266,7 @@ public class MonetPreparedStatement exte } throw new SQLException("No such column with index: " + colnr, "M1M05"); } + /** * Returns the index (0..size-1) in the backing arrays for the given * parameter number or an SQLException when not found @@ -291,7 +283,6 @@ public class MonetPreparedStatement exte throw new SQLException("No such parameter with index: " + paramnr, "M1M05"); } - /* helper for the anonymous class inside getMetaData */ private abstract class rsmdw extends MonetWrapper implements ResultSetMetaData {} /** @@ -377,8 +368,7 @@ public class MonetPreparedStatement exte } /** - * Indicates whether the designated column can be used in a - * where clause. + * Indicates whether the designated column can be used in a where clause. * * Returning true for all here, even for CLOB, BLOB. * @@ -391,11 +381,9 @@ public class MonetPreparedStatement exte } /** - * Indicates whether the designated column is a cash value. - * From the MonetDB database perspective it is by definition - * unknown whether the value is a currency, because there are - * no currency datatypes such as MONEY. With this knowledge - * we can always return false here. + * Indicates whether the designated column is a cash value. From the MonetDB database perspective it is by + * definition unknown whether the value is a currency, because there are no currency datatypes such as + * MONEY. With this knowledge we can always return false here. * * @param column the first column is 1, the second is 2, ... * @return false @@ -406,8 +394,7 @@ public class MonetPreparedStatement exte } /** - * Indicates whether values in the designated column are signed - * numbers. + * Indicates whether values in the designated column are signed numbers. * Within MonetDB all numeric types (except oid and ptr) are signed. * * @param column the first column is 1, the second is 2, ... @@ -438,12 +425,10 @@ public class MonetPreparedStatement exte } /** - * Indicates the designated column's normal maximum width in - * characters. + * Indicates the designated column's normal maximum width in characters. * * @param column the first column is 1, the second is 2, ... - * @return the normal maximum number of characters allowed as the - * width of the designated column + * @return the normal maximum number of characters allowed as the width of the designated column * @throws SQLException if there is no such column */ @Override @@ -487,10 +472,8 @@ public class MonetPreparedStatement exte } /** - * Get the designated column's number of decimal digits. - * This method is currently very expensive as it needs to - * retrieve the information from the database using an SQL - * query. + * Get the designated column's number of decimal digits. This method is currently very expensive as it + * needs to retrieve the information from the database using an SQL query. * * @param column the first column is 1, the second is 2, ... * @return precision @@ -506,10 +489,8 @@ public class MonetPreparedStatement exte } /** - * Gets the designated column's number of digits to right of - * the decimal point. This method is currently very - * expensive as it needs to retrieve the information from - * the database using an SQL query. + * Gets the designated column's number of digits to right of the decimal point. This method is currently + * very expensive as it needs to retrieve the information from the database using an SQL query. * * @param column the first column is 1, the second is 2, ... * @return scale @@ -525,10 +506,8 @@ public class MonetPreparedStatement exte } /** - * Indicates the nullability of values in the designated - * column. This method is currently very expensive as it - * needs to retrieve the information from the database using - * an SQL query. + * Indicates the nullability of values in the designated column. This method is currently very expensive as + * it needs to retrieve the information from the database using an SQL query. * * @param column the first column is 1, the second is 2, ... * @return nullability @@ -544,8 +523,7 @@ public class MonetPreparedStatement exte * MonetDB does not support the catalog naming concept as in: catalog.schema.table naming scheme * * @param column the first column is 1, the second is 2, ... - * @return the name of the catalog for the table in which the given - * column appears or "" if not applicable + * @return the name of the catalog for the table in which the given column appears or "" if not applicable */ @Override public String getCatalogName(int column) throws SQLException { @@ -553,9 +531,8 @@ public class MonetPreparedStatement exte } /** - * Indicates whether the designated column is definitely not - * writable. MonetDB does not support cursor updates, so - * nothing is writable. + * Indicates whether the designated column is definitely not writable. MonetDB does not support cursor + * updates, so nothing is writable. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -566,8 +543,7 @@ public class MonetPreparedStatement exte } /** - * Indicates whether it is possible for a write on the - * designated column to succeed. + * Indicates whether it is possible for a write on the designated column to succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -578,8 +554,7 @@ public class MonetPreparedStatement exte } /** - * Indicates whether a write on the designated column will - * definitely succeed. + * Indicates whether a write on the designated column will definitely succeed. * * @param column the first column is 1, the second is 2, ... * @return true if so; false otherwise @@ -590,18 +565,14 @@ public class MonetPreparedStatement exte } /** - * Returns the fully-qualified name of the Java class whose - * instances are manufactured if the method - * ResultSet.getObject is called to retrieve a value from - * the column. ResultSet.getObject may return a subclass of - * the class returned by this method. + * Returns the fully-qualified name of the Java class whose instances are manufactured if the method + * ResultSet.getObject is called to retrieve a value from the column. ResultSet.getObject may return a + * subclass of the class returned by this method. * * @param column the first column is 1, the second is 2, ... - * @return the fully-qualified name of the class in the Java - * programming language that would be used by the method - * ResultSet.getObject to retrieve the value in the - * specified column. This is the class name used for custom - * mapping. + * @return the fully-qualified name of the class in the Java programming language that would be used by the + * method ResultSet.getObject to retrieve the value in the specified column. This is the class name used + * for custom mapping. * @throws SQLException if there is no such column */ @Override @@ -610,9 +581,8 @@ public class MonetPreparedStatement exte } /** - * Gets the designated column's suggested title for use in - * printouts and displays. This is currently equal to - * getColumnName(). + * Gets the designated column's suggested title for use in printouts and displays. This is currently equal + * to getColumnName(). * * @param column the first column is 1, the second is 2, ... * @return the suggested column title @@ -659,9 +629,8 @@ public class MonetPreparedStatement exte * Retrieves the designated column's database-specific type name. * * @param column the first column is 1, the second is 2, ... - * @return type name used by the database. If the column type is a - * user-defined type, then a fully-qualified type name is - * returned. + * @return type name used by the database. If the column type is a user-defined type, then a + * fully-qualified type name is returned. * @throws SQLException if there is no such column */ @Override @@ -678,20 +647,17 @@ public class MonetPreparedStatement exte /* helper class for the anonymous class in getParameterMetaData */ private abstract class pmdw extends MonetWrapper implements ParameterMetaData {} /** - * Retrieves the number, types and properties of this - * PreparedStatement object's parameters. + * Retrieves the number, types and properties of this PreparedStatement object's parameters. * - * @return a ParameterMetaData object that contains information - * about the number, types and properties of this - * PreparedStatement object's parameters + * @return a ParameterMetaData object that contains information about the number, types and properties of this + * PreparedStatement object's parameters * @throws SQLException if a database access error occurs */ @Override public ParameterMetaData getParameterMetaData() throws SQLException { return new pmdw() { /** - * Retrieves the number of parameters in the - * PreparedStatement object for which this ParameterMetaData + * Retrieves the number of parameters in the PreparedStatement object for which this ParameterMetaData * object contains information. * * @return the number of parameters @@ -710,16 +676,13 @@ public class MonetPreparedStatement exte } /** - * Retrieves whether null values are allowed in the - * designated parameter. + * Retrieves whether null values are allowed in the designated parameter. * * This is currently always unknown for MonetDB/SQL. * * @param param the first parameter is 1, the second is 2, ... - * @return the nullability status of the given parameter; - * one of ParameterMetaData.parameterNoNulls, - * ParameterMetaData.parameterNullable, or - * ParameterMetaData.parameterNullableUnknown + * @return the nullability status of the given parameter; one of ParameterMetaData.parameterNoNulls, + * ParameterMetaData.parameterNullable, or ParameterMetaData.parameterNullableUnknown * @throws SQLException if a database access error occurs */ @Override @@ -728,8 +691,7 @@ public class MonetPreparedStatement exte } /** - * Retrieves whether values for the designated parameter can - * be signed numbers. + * Retrieves whether values for the designated parameter can be signed numbers. * * @param param the first parameter is 1, the second is 2, ... * @return true if so; false otherwise @@ -737,7 +699,7 @@ public class MonetPreparedStatement exte */ @Override public boolean isSigned(int param) throws SQLException { - // we can hardcode this, based on the colum type + // we can hardcode this, based on the column type switch (getParameterType(param)) { case Types.NUMERIC: case Types.DECIMAL: @@ -1364,8 +1326,8 @@ public class MonetPreparedStatement exte if (cal == null) { setValue(parameterIndex, "date '" + x.toString() + "'"); } else { - mDate.setTimeZone(cal.getTimeZone()); - setValue(parameterIndex, "date '" + mDate.format(x) + "'"); + MDate.setTimeZone(cal.getTimeZone()); + setValue(parameterIndex, "date '" + MDate.format(x) + "'"); } } @@ -2238,7 +2200,7 @@ public class MonetPreparedStatement exte if (hasTimeZone) { // timezone shouldn't matter, since the server is timezone // aware in this case - String RFC822 = mTimeZ.format(x); + String RFC822 = MTimeZ.format(x); setValue(index, "timetz '" + RFC822.substring(0, 15) + ":" + RFC822.substring(15) + "'"); } else { // server is not timezone aware for this field, and no @@ -2248,8 +2210,8 @@ public class MonetPreparedStatement exte if (cal == null) { setValue(index, "time '" + x.toString() + "'"); } else { - mTime.setTimeZone(cal.getTimeZone()); - setValue(index, "time '" + mTime.format(x) + "'"); + MTime.setTimeZone(cal.getTimeZone()); + setValue(index, "time '" + MTime.format(x) + "'"); } } } @@ -2295,7 +2257,7 @@ public class MonetPreparedStatement exte if (hasTimeZone) { // timezone shouldn't matter, since the server is timezone // aware in this case - String RFC822 = mTimestampZ.format(x); + String RFC822 = MTimestampZ.format(x); setValue(index, "timestamptz '" + RFC822.substring(0, 26) + ":" + RFC822.substring(26) + "'"); } else { // server is not timezone aware for this field, and no @@ -2305,8 +2267,8 @@ public class MonetPreparedStatement exte if (cal == null) { setValue(index, "timestamp '" + x.toString() + "'"); } else { - mTimestamp.setTimeZone(cal.getTimeZone()); - setValue(index, "timestamp '" + mTimestamp.format(x) + "'"); + MTimestamp.setTimeZone(cal.getTimeZone()); + setValue(index, "timestamp '" + MTimestamp.format(x) + "'"); } } } @@ -2377,8 +2339,7 @@ public class MonetPreparedStatement exte } /** - * Call close to release the server-sided handle for this - * PreparedStatement. + * Call close to release the server-sided handle for this PreparedStatement. */ @Override protected void finalize() { @@ -2388,9 +2349,8 @@ public class MonetPreparedStatement exte //== end methods interface PreparedStatement /** - * Sets the given index with the supplied value. If the given index is - * out of bounds, and SQLException is thrown. The given value should - * never be null. + * Sets the given index with the supplied value. If the given index is out of bounds, and SQLException is thrown. + * The given value should never be null. * * @param index the parameter index * @param val the exact String representation to set
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetResultSet.java @@ -10,12 +10,10 @@ package nl.cwi.monetdb.jdbc; import nl.cwi.monetdb.jdbc.types.INET; import nl.cwi.monetdb.jdbc.types.URL; +import nl.cwi.monetdb.mcl.responses.DataBlockResponse; import nl.cwi.monetdb.mcl.responses.ResultSetResponse; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; +import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; @@ -92,7 +90,7 @@ public class MonetResultSet extends Mone /** Just a dummy variable to keep store the fetchsize set. */ private int fetchSize; /** The current row's values */ - Object[] values; + DataBlockResponse currentBlock; /** * Main constructor backed by the given Header. @@ -120,7 +118,6 @@ public class MonetResultSet extends Mone this.columns = header.getNames(); this.types = header.getTypes(); this.JdbcSQLTypes = header.getJdbcSQLTypes(); - this.values = header.getLine(this.curRow); } /** @@ -156,6 +153,11 @@ 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 @@ -201,19 +203,17 @@ public class MonetResultSet extends Mone } else if (row > tupleCount + 1) { row = tupleCount + 1; // after last } - - this.values = header.getLine(row - 1); // store it this.curRow = row; - return this.values != null; + this.currentBlock = header.getDataBlockCorrespondingToLine(row - 1); + return this.curRow <= this.tupleCount; } /** * Moves the cursor to the end of this ResultSet object, just after the last row. This method has no effect if the * result set contains no rows. * - * @throws SQLException if a database access error occurs or the result set - * type is TYPE_FORWARD_ONLY + * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY */ @Override public void afterLast() throws SQLException { @@ -224,8 +224,7 @@ public class MonetResultSet extends Mone * Moves the cursor to the front of this ResultSet object, just before the first row. This method has no effect * if the result set contains no rows. * - * @throws SQLException if a database access error occurs or the result set - * type is TYPE_FORWARD_ONLY + * @throws SQLException if a database access error occurs or the result set type is TYPE_FORWARD_ONLY */ @Override public void beforeFirst() throws SQLException { @@ -407,13 +406,10 @@ public class MonetResultSet extends Mone @Override public Reader getCharacterStream(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return new StringReader((String) val); + return new StringReader(currentBlock.getValueAsString(columnIndex - 1)); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -485,13 +481,10 @@ public class MonetResultSet extends Mone @Override public Blob getBlob(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return (MonetBlob) val; + return (MonetBlob) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -528,13 +521,10 @@ public class MonetResultSet extends Mone @Override public Clob getClob(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return (MonetClob) val; + return (MonetClob) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -605,13 +595,10 @@ public class MonetResultSet extends Mone @Override public BigDecimal getBigDecimal(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return (BigDecimal) val; + return (BigDecimal) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -633,16 +620,12 @@ public class MonetResultSet extends Mone @Deprecated public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - - BigDecimal bd = (BigDecimal) val; - bd.setScale(scale); - return bd; + BigDecimal val = (BigDecimal) currentBlock.getObjectValue(columnIndex - 1); + val.setScale(scale); + return val; } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -693,24 +676,21 @@ public class MonetResultSet extends Mone @Override public boolean getBoolean(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; - return false; // if the value is SQL NULL, the value returned is false + if(setLastNullValue(columnIndex - 1)) { + return false; // if the value is SQL NULL, the value returned is false } - lastReadWasNull = false; // match type specific values switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BOOLEAN: - return (Boolean) val; + return currentBlock.getBooleanValue(columnIndex - 1); case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness case Types.CLOB: - String other = (String) val; - if ("false".equalsIgnoreCase(other) || "0".equals(val)) + String val = currentBlock.getValueAsString(columnIndex - 1); + if ("false".equalsIgnoreCase(val) || "0".equals(val)) return false; - if ("true".equalsIgnoreCase(other) || "1".equals(val)) + if ("true".equalsIgnoreCase(val) || "1".equals(val)) return true; throw newSQLInvalidColumnIndexException(columnIndex); case Types.BIT: // MonetDB doesn't use type BinaryDigit, it's here for completeness @@ -764,13 +744,10 @@ public class MonetResultSet extends Mone @Override public byte getByte(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; - return (byte) 0; + if(setLastNullValue(columnIndex - 1)) { + return 0; } - lastReadWasNull = false; - return (Byte) val; + return currentBlock.getByteValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -804,22 +781,18 @@ public class MonetResultSet extends Mone @Override public byte[] getBytes(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - // According to Table B-6, getBytes() only operates on BINARY types switch (JdbcSQLTypes[columnIndex - 1]) { case Types.BLOB: - return ((MonetBlob) val).getBuffer(); + return ((MonetBlob) currentBlock.getObjectValue(columnIndex - 1)).getBuffer(); case Types.BINARY: case Types.VARBINARY: case Types.LONGVARBINARY: // unpack the HEX (BLOB) notation to real bytes - return (byte[]) val; + return (byte[]) currentBlock.getObjectValue(columnIndex - 1); default: throw new SQLException("Cannot operate on " + types[columnIndex - 1] + " type", "M1M05"); } @@ -899,13 +872,10 @@ public class MonetResultSet extends Mone @Override public double getDouble(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; - return 0; + if(setLastNullValue(columnIndex - 1)) { + return 0.0d; } - lastReadWasNull = false; - return (Double) val; + return currentBlock.getDoubleValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -1019,13 +989,10 @@ public class MonetResultSet extends Mone @Override public float getFloat(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; - return 0; + if(setLastNullValue(columnIndex - 1)) { + return 0.0f; } - lastReadWasNull = false; - return (Float) val; + return currentBlock.getFloatValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -1056,15 +1023,11 @@ public class MonetResultSet extends Mone */ @Override public int getInt(int columnIndex) throws SQLException { - Object val; try { - val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return 0; } - lastReadWasNull = false; - return (Integer) val; + return currentBlock.getIntValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -1095,15 +1058,13 @@ public class MonetResultSet extends Mone */ @Override public long getLong(int columnIndex) throws SQLException { - Object val; try { - val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return 0; } - lastReadWasNull = false; - return (Long) val; + return currentBlock.getLongValue(columnIndex - 1); + } catch (ClassCastException ex) { + throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); } @@ -1486,7 +1447,8 @@ public class MonetResultSet extends Mone * an SQL query. * * @param column the first column is 1, the second is 2, ... - * @return the nullability status of the given column; one of columnNoNulls, columnNullable or columnNullableUnknown + * @return the nullability status of the given column; one of columnNoNulls, columnNullable or + * columnNullableUnknown * @throws SQLException if a database access error occurs */ @Override @@ -1680,17 +1642,13 @@ public class MonetResultSet extends Mone */ @Override public Object getObject(int columnIndex) throws SQLException { - // Many generic JDBC programs use getObject(colnr) to retrieve value objects from a resultset - // For speed the implementation should be as fast as possible, so avoid method calls (by inlining code) where possible + // Many generic JDBC programs use getObject(colnr) to retrieve value objects from a resultset. For speed the + // implementation should be as fast as possible, so avoid method calls (by inlining code) where possible final int JdbcType; - final Object val; try { - val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; JdbcType = JdbcSQLTypes[columnIndex - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); @@ -1698,6 +1656,8 @@ public class MonetResultSet extends Mone switch(JdbcType) { case Types.BIT: // MonetDB doesn't use type BInary digiT, it's here for completeness + case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness + case Types.BOOLEAN: case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: @@ -1707,22 +1667,32 @@ public class MonetResultSet extends Mone case Types.REAL: case Types.DECIMAL: case Types.NUMERIC: - case Types.BOOLEAN: case Types.CHAR: - case Types.LONGVARCHAR: // MonetDB doesn't use type LONGVARCHAR, it's here for completeness + case Types.VARCHAR: + case Types.BLOB: case Types.CLOB: - case Types.BLOB: - return val; - case Types.VARCHAR: { + return currentBlock.getValueAsObject(columnIndex - 1); + case Types.DATE: + return getDate(columnIndex); + case Types.TIME: + return getTime(columnIndex); + case Types.TIMESTAMP: + return getTimestamp(columnIndex); + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY, it's here for completeness + return getBytes(columnIndex); + case Types.OTHER: { // The MonetDB types: inet, json, url and uuid are all mapped to Types.VARCHAR in MonetDriver.typeMap // 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); switch (MonetDBType.length()) { case 3: if ("url".equals(MonetDBType)) { try { URL url_obj = new URL(); - url_obj.fromString((String) val); + url_obj.fromString(val); return url_obj; } catch (Exception exc) { // ignore exception and just return the val String object @@ -1734,7 +1704,7 @@ public class MonetResultSet extends Mone if ("inet".equals(MonetDBType)) { try { INET inet_obj = new INET(); - inet_obj.fromString((String) val); + inet_obj.fromString(val); return inet_obj; } catch (Exception exc) { // ignore exception and just return the val String object @@ -1743,13 +1713,12 @@ public class MonetResultSet extends Mone } else if ("uuid".equals(MonetDBType)) { try { - return UUID.fromString((String) val); + return UUID.fromString(val); } catch (IllegalArgumentException exc) { // ignore exception and just return the val String object return val; } -// } else -// if ("json".equals(MonetDBType)) { +// } else if ("json".equals(MonetDBType)) { // There is no support for JSON in standard java class libraries. // Possibly we could use org.json.simple.JSONObject or other/faster libs // javax.json.Json is not released yet (see https://json-processing-spec.java.net/) @@ -1762,17 +1731,6 @@ public class MonetResultSet extends Mone } return val; } - case Types.DATE: - return getDate(columnIndex); - case Types.TIME: - return getTime(columnIndex); - case Types.TIMESTAMP: - return getTimestamp(columnIndex); - case Types.BINARY: - case Types.VARBINARY: - case Types.LONGVARBINARY: // MonetDB doesn't use type LONGVARBINARY, it's here for completeness - return getBytes(columnIndex); - case Types.OTHER: default: // When we get here the column type is a non-standard JDBC SQL type, possibly a User Defined Type. // Just call getObject(int, Map) for those rare cases. @@ -1821,15 +1779,11 @@ public class MonetResultSet extends Mone @Override @SuppressWarnings("unchecked") public Object getObject(int columnIndex, Map<String,Class<?>> map) throws SQLException { - final Object val; final String MonetDBtype; try { - val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; MonetDBtype = types[columnIndex - 1]; } catch (IndexOutOfBoundsException e) { throw newSQLInvalidColumnIndexException(columnIndex); @@ -1845,7 +1799,7 @@ public class MonetResultSet extends Mone } if (type == null || type == String.class) { - return val; + return currentBlock.getValueAsString(columnIndex - 1); } else if (type == BigDecimal.class) { return getBigDecimal(columnIndex); } else if (type == Boolean.class) { @@ -2022,7 +1976,7 @@ public class MonetResultSet extends Mone x.readSQL(input, MonetDBtype); return x; } else { - return val; + return currentBlock.getObjectValue(columnIndex - 1); } } @@ -2234,13 +2188,10 @@ public class MonetResultSet extends Mone @Override public short getShort(int columnIndex) throws SQLException { try { - Object val = this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return 0; } - lastReadWasNull = false; - return (Short) val; + return currentBlock.getShortValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -2285,13 +2236,10 @@ public class MonetResultSet extends Mone @Override public String getString(int columnIndex) throws SQLException { try { - String val = (String) this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return val; + return currentBlock.getValueAsString(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -2403,13 +2351,10 @@ public class MonetResultSet extends Mone @Override public Date getDate(int columnIndex, Calendar cal) throws SQLException { try { - Date val = (Date) this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return val; + return (Date) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -2479,13 +2424,10 @@ public class MonetResultSet extends Mone if (cal == null) throw new IllegalArgumentException("No Calendar object given!"); try { - Time val = (Time) this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return val; + return (Time) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -2555,13 +2497,10 @@ public class MonetResultSet extends Mone if (cal == null) throw new IllegalArgumentException("No Calendar object given!"); try { - Timestamp val = (Timestamp) this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; - return val; + return (Timestamp) currentBlock.getObjectValue(columnIndex - 1); } catch (ClassCastException ex) { throw new SQLException(ex.getMessage()); } catch (IndexOutOfBoundsException e) { @@ -2624,14 +2563,11 @@ public class MonetResultSet extends Mone @Override public java.net.URL getURL(int columnIndex) throws SQLException { try { - String val = (String) this.values[columnIndex - 1]; - if (val == null) { - lastReadWasNull = true; + if(setLastNullValue(columnIndex - 1)) { return null; } - lastReadWasNull = false; try { - return new java.net.URL(val); + return new java.net.URL(currentBlock.getValueAsString(columnIndex - 1)); } catch (MalformedURLException e) { throw new SQLException(e.getMessage(), "M1M05"); }
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetStatement.java @@ -678,13 +678,13 @@ public class MonetStatement extends Mone if (header instanceof UpdateResponse) { String lastid = ((UpdateResponse)header).getLastid(); if (lastid.equals("-1")) { - results = new String[0][1]; + results = new String[1][1]; } else { results = new String[1][1]; results[0][0] = lastid; } } else { - results = new String[0][1]; + results = new String[1][1]; } try { @@ -1211,17 +1211,17 @@ final class MonetVirtualResultSet extend throws IllegalArgumentException { super(statement, columns, types, jdbcTypes, results.length); this.results = results; - closed = false; + this.closed = false; + this.currentBlock.setData(results); } /** - * This method is overridden in order to let it use the results array - * instead of the cache in the Statement object that created it. + * This method is overridden in order to let it use the results array instead of the cache in the Statement object + * that created it. * - * @param row the number of the row to which the cursor should move. A - * positive number indicates the row number counting from the - * beginning of the result set; a negative number indicates the row - * number counting from the end of the result set + * @param row the number of the row to which the cursor should move. A positive number indicates the row number + * counting from the beginning of the result set; a negative number indicates the row number counting from the end + * of the result set * @return true if the cursor is on the result set; false otherwise * @throws SQLException if a database error occurs */ @@ -1245,7 +1245,9 @@ final class MonetVirtualResultSet extend // see if we have the row if (row < 1 || row > tupleCount) return false; - System.arraycopy(this.results[row - 1], 0, this.values, 0, this.results[row - 1].length); + String[] values = (String[]) this.currentBlock.getData()[0]; + + System.arraycopy(this.results[row - 1], 0, values, 0, this.results[row - 1].length); return true; }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/MonetDBConnectionFactory.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/MonetDBConnectionFactory.java @@ -3,8 +3,8 @@ package nl.cwi.monetdb.mcl.connection; import nl.cwi.monetdb.jdbc.MonetConnection; import nl.cwi.monetdb.jdbc.MonetDriver; import nl.cwi.monetdb.mcl.connection.embedded.EmbeddedConnection; -import nl.cwi.monetdb.mcl.connection.socket.MapiConnection; -import nl.cwi.monetdb.mcl.connection.socket.MapiLanguage; +import nl.cwi.monetdb.mcl.connection.mapi.MapiConnection; +import nl.cwi.monetdb.mcl.connection.mapi.MapiLanguage; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import java.io.File;
rename from src/main/java/nl/cwi/monetdb/mcl/connection/socket/AbstractSocket.java rename to src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java --- a/src/main/java/nl/cwi/monetdb/mcl/connection/socket/AbstractSocket.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java @@ -1,4 +1,4 @@ -package nl.cwi.monetdb.mcl.connection.socket; +package nl.cwi.monetdb.mcl.connection.mapi; import java.io.Closeable; import java.io.IOException; @@ -85,18 +85,23 @@ public abstract class AbstractSocket imp public int readLine(StringBuilder builder) throws IOException { builder.setLength(0); boolean found = false; + char[] array = this.stringsDecoded.array(); + int position = this.stringsDecoded.position(); while(!found) { if(!this.stringsDecoded.hasRemaining()) { this.readToBuffer(); + array = this.stringsDecoded.array(); + position = 0; } - char c = this.stringsDecoded.get(); + char c = array[position++]; if(c == '\n') { found = true; } else { builder.append(c); } } + this.stringsDecoded.position(position); return builder.length(); }
rename from src/main/java/nl/cwi/monetdb/mcl/connection/socket/MapiConnection.java rename to src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java --- a/src/main/java/nl/cwi/monetdb/mcl/connection/socket/MapiConnection.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java @@ -1,4 +1,4 @@ -package nl.cwi.monetdb.mcl.connection.socket; +package nl.cwi.monetdb.mcl.connection.mapi; import nl.cwi.monetdb.jdbc.MonetConnection; import nl.cwi.monetdb.mcl.connection.ChannelSecurity;
rename from src/main/java/nl/cwi/monetdb/mcl/connection/socket/MapiLanguage.java rename to src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiLanguage.java --- a/src/main/java/nl/cwi/monetdb/mcl/connection/socket/MapiLanguage.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiLanguage.java @@ -1,4 +1,4 @@ -package nl.cwi.monetdb.mcl.connection.socket; +package nl.cwi.monetdb.mcl.connection.mapi; import nl.cwi.monetdb.mcl.connection.IMonetDBLanguage;
rename from src/main/java/nl/cwi/monetdb/mcl/connection/socket/OldMapiSocket.java rename to src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java --- a/src/main/java/nl/cwi/monetdb/mcl/connection/socket/OldMapiSocket.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java @@ -1,4 +1,4 @@ -package nl.cwi.monetdb.mcl.connection.socket; +package nl.cwi.monetdb.mcl.connection.mapi; import java.io.IOException; import java.io.InputStream;
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java @@ -46,7 +46,8 @@ public abstract class AbstractProtocol<T public abstract TableResultHeaders getNextTableHeader(Object line, String[] stringValues, int[] intValues) throws ProtocolException; - public abstract int parseTupleLine(Object line, Object[] values, int[] typesMap) throws ProtocolException; + public abstract int parseTupleLine(int lineNumber, Object line, int[] typesMap, Object[] values, boolean[] nulls) + throws ProtocolException; public abstract String getRemainingStringLine(int startIndex);
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/embedded/EmbeddedProtocol.java @@ -72,7 +72,7 @@ public class EmbeddedProtocol extends Ab } @Override - public int parseTupleLine(Object line, Object[] values, int[] typesMap) throws ProtocolException { + public int parseTupleLine(int lineNumber, Object line, int[] typesMap, Object[] values, boolean[] nulls) throws ProtocolException { return 0; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/newmapi/NewMapiProtocol.java @@ -61,7 +61,7 @@ public class NewMapiProtocol extends Abs } @Override - public int parseTupleLine(Object line, Object[] values, int[] typesMap) throws ProtocolException { + public int parseTupleLine(int lineNumber, Object line, int[] typesMap, Object[] values, boolean[] nulls) throws ProtocolException { return 0; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiProtocol.java @@ -1,7 +1,7 @@ package nl.cwi.monetdb.mcl.protocol.oldmapi; import nl.cwi.monetdb.jdbc.MonetConnection; -import nl.cwi.monetdb.mcl.connection.socket.OldMapiSocket; +import nl.cwi.monetdb.mcl.connection.mapi.OldMapiSocket; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; import nl.cwi.monetdb.mcl.protocol.ServerResponses; @@ -128,9 +128,10 @@ public class OldMapiProtocol extends Abs } @Override - public int parseTupleLine(Object line, Object[] values, int[] typesMap) throws ProtocolException { - return OldMapiTupleLineParser.OldMapiParseTupleLine((StringBuilder) line, values, this.tupleLineBuilder, - typesMap); + public int parseTupleLine(int lineNumber, Object line, int[] typesMap, Object[] data, boolean[] nulls) + throws ProtocolException { + return OldMapiTupleLineParser.OldMapiParseTupleLine(lineNumber, (StringBuilder) line, + this.tupleLineBuilder, typesMap, data, nulls); } @Override
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTableHeaderParser.java @@ -90,10 +90,10 @@ final class OldMapiTableHeaderParser { if (builder.charAt(i) == ',' && builder.charAt(i + 1) == '\t') { intValues[elem++] = tmp; tmp = 0; + i++; } else { tmp *= 10; - // note: don't use Character.isDigit() here, because - // we only want ISO-LATIN-1 digits + // note: don't use Character.isDigit() here, because we only want ISO-LATIN-1 digits if (builder.charAt(i) >= '0' && builder.charAt(i) <= '9') { tmp += (int) builder.charAt(i) - (int)'0'; } else {
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParser.java @@ -17,16 +17,17 @@ import java.text.SimpleDateFormat; */ final class OldMapiTupleLineParser { - static int OldMapiParseTupleLine(StringBuilder line, Object[] values, StringBuilder helper, int[] jDBCTypesMap) throws ProtocolException { + static int OldMapiParseTupleLine(int lineNumber, StringBuilder line, StringBuilder helper, int[] typesMap, + Object[] values, boolean[] nulls) throws ProtocolException { int len = line.length(); // first detect whether this is a single value line (=) or a real tuple ([) if (line.charAt(0) == '=') { - if (values.length != 1) { - throw new ProtocolException(values.length + " columns expected, but only single value found"); + if (typesMap.length != 1) { + throw new ProtocolException(typesMap.length + " columns expected, but only single value found"); } // return the whole string but the leading = - values[0] = line.substring(1); + OldMapiStringToJavaObjectConverter(line.substring(1), lineNumber, values[0], typesMap[0]); return 1; } @@ -119,11 +120,14 @@ final class OldMapiTupleLineParser { } // put the unescaped string in the right place - values[column] = OldMapiStringToJavaObjectConverter(helper.toString(), jDBCTypesMap[column]); + OldMapiStringToJavaObjectConverter(helper.toString(), lineNumber, values[column], typesMap[column]); + nulls[column] = false; } else if ((i - 1) - cursor == 4 && line.indexOf("NULL", cursor) == cursor) { - values[column] = null; + SetNullValue(lineNumber, values[column], typesMap[column]); + nulls[column] = true; } else { - values[column] = OldMapiStringToJavaObjectConverter(line.substring(cursor, i - 1), jDBCTypesMap[column]); + OldMapiStringToJavaObjectConverter(line.substring(cursor, i - 1), lineNumber, values[column], typesMap[column]); + nulls[column] = false; } column++; cursor = i + 1; @@ -134,7 +138,7 @@ final class OldMapiTupleLineParser { } } // check if this result is of the size we expected it to be - if (column != values.length) + if (column != typesMap.length) throw new ProtocolException("illegal result length: " + column + "\nlast read: " + (column > 0 ? values[column - 1] : "<none>")); return column; } @@ -154,56 +158,101 @@ final class OldMapiTupleLineParser { return res; } - private static Object OldMapiStringToJavaObjectConverter(String toParse, int jDBCMapping) throws ProtocolException { + private static void OldMapiStringToJavaObjectConverter(String toParse, int lineNumber, Object columnArray, + int jDBCMapping) throws ProtocolException { switch (jDBCMapping) { + case Types.BOOLEAN: + ((boolean[]) columnArray)[lineNumber] = Boolean.parseBoolean(toParse); + break; + case Types.TINYINT: + ((byte[]) columnArray)[lineNumber] = Byte.parseByte(toParse); + break; + case Types.SMALLINT: + ((short[]) columnArray)[lineNumber] = Short.parseShort(toParse); + break; + case Types.INTEGER: + ((int[]) columnArray)[lineNumber] = Integer.parseInt(toParse); + break; case Types.BIGINT: - return Long.parseLong(toParse); - case Types.BLOB: - return new MonetBlob(BinaryBlobConverter(toParse)); - case Types.BINARY: - return BinaryBlobConverter(toParse); - case Types.BOOLEAN: - return Boolean.parseBoolean(toParse); + ((long[]) columnArray)[lineNumber] = Long.parseLong(toParse); + break; + case Types.REAL: + ((float[]) columnArray)[lineNumber] = Float.parseFloat(toParse); + break; + case Types.DOUBLE: + ((double[]) columnArray)[lineNumber] = Double.parseDouble(toParse); + break; + case Types.DECIMAL: + ((Object[]) columnArray)[lineNumber] = new BigDecimal(toParse); + break; + case Types.NUMERIC: + ((Object[]) columnArray)[lineNumber] = new BigInteger(toParse); + break; case Types.CHAR: - return toParse; - case Types.CLOB: - return new MonetClob(toParse); + case Types.VARCHAR: + case Types.OTHER: + ((Object[]) columnArray)[lineNumber] = toParse; + break; case Types.DATE: try { - return DateParser.parse(toParse); + ((Object[]) columnArray)[lineNumber] = DateParser.parse(toParse); + } catch (ParseException e) { + throw new ProtocolException(e.getMessage()); + } + break; + case Types.TIME: + try { + ((Object[]) columnArray)[lineNumber] = TimeParser.parse(toParse); } catch (ParseException e) { throw new ProtocolException(e.getMessage()); } - case Types.DECIMAL: - return new BigDecimal(toParse); - case Types.DOUBLE: - return Double.parseDouble(toParse); - case Types.NUMERIC: - return new BigInteger(toParse); - case Types.INTEGER: - return Integer.parseInt(toParse); - case Types.REAL: - return Float.parseFloat(toParse); - case Types.SMALLINT: - return Short.parseShort(toParse); - case Types.TIME: + break; + case Types.TIMESTAMP: try { - return TimeParser.parse(toParse); + ((Object[]) columnArray)[lineNumber] = TimestampParser.parse(toParse); } catch (ParseException e) { throw new ProtocolException(e.getMessage()); } - case Types.TIMESTAMP: - try { - return TimestampParser.parse(toParse); - } catch (ParseException e) { - throw new ProtocolException(e.getMessage()); - } + break; + case Types.CLOB: + ((Object[]) columnArray)[lineNumber] = new MonetClob(toParse); + break; + case Types.BLOB: + ((Object[]) columnArray)[lineNumber] = new MonetBlob(BinaryBlobConverter(toParse)); + break; + case Types.BINARY: + ((Object[]) columnArray)[lineNumber] = BinaryBlobConverter(toParse); + break; + default: + throw new ProtocolException("Unknown type!"); + } + } + + private static void SetNullValue(int lineNumber, Object columnArray, int jDBCMapping) throws ProtocolException { + switch (jDBCMapping) { + case Types.BOOLEAN: + ((boolean[]) columnArray)[lineNumber] = false; + break; case Types.TINYINT: - return Byte.parseByte(toParse); - case Types.VARCHAR: - return toParse; + ((byte[]) columnArray)[lineNumber] = Byte.MIN_VALUE; + break; + case Types.SMALLINT: + ((short[]) columnArray)[lineNumber] = Short.MIN_VALUE; + break; + case Types.INTEGER: + ((int[]) columnArray)[lineNumber] = Integer.MIN_VALUE; + break; + case Types.BIGINT: + ((long[]) columnArray)[lineNumber] = Long.MIN_VALUE; + break; + case Types.REAL: + ((float[]) columnArray)[lineNumber] = Float.MIN_VALUE; + break; + case Types.DOUBLE: + ((double[]) columnArray)[lineNumber] = Double.MIN_VALUE; + break; default: - return null; + ((Object[]) columnArray)[lineNumber] = null; } } }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java @@ -5,6 +5,7 @@ import nl.cwi.monetdb.mcl.protocol.Proto import nl.cwi.monetdb.mcl.protocol.ServerResponses; import java.sql.SQLException; +import java.sql.Types; /** * The DataBlockResponse is tabular data belonging to a @@ -26,15 +27,17 @@ import java.sql.SQLException; public class DataBlockResponse implements IIncompleteResponse { /** The array to keep the data in */ - private final Object[][] data; + private Object[] data; /** The counter which keeps the current position in the data array */ private int pos; - /** Whether we can discard lines as soon as we have read them */ - private boolean forwardOnly; /** The connection protocol to parse the tuple lines */ private final AbstractProtocol<?> protocol; /** The JdbcSQLTypes mapping */ private final int[] jdbcSQLTypes; + /** A mapping of null values of the current Row */ + private boolean[][] nullMappings; + /** A 'pointer' to the current line */ + private int blockLine; /** * Constructs a DataBlockResponse object. @@ -45,8 +48,8 @@ public class DataBlockResponse implement */ DataBlockResponse(int rowcount, int columncount, boolean forward, AbstractProtocol<?> protocol, int[] JdbcSQLTypes) { this.pos = -1; - this.forwardOnly = forward; - this.data = new Object[rowcount][columncount]; + this.data = new Object[columncount]; + this.nullMappings = new boolean[rowcount][columncount]; this.protocol = protocol; this.jdbcSQLTypes = JdbcSQLTypes; } @@ -63,9 +66,41 @@ public class DataBlockResponse implement public void addLine(ServerResponses response, Object line) throws ProtocolException { if (response != ServerResponses.RESULT) throw new ProtocolException("protocol violation: unexpected line in data block: " + line.toString()); + + if(this.pos == -1) { //if it's the first line, initialize the matrix + int numberOfColumns = this.data.length, numberOfRows = this.nullMappings.length; + for (int i = 0 ; i < numberOfColumns ; i++) { + switch (this.jdbcSQLTypes[i]) { + case Types.BOOLEAN: + this.data[i] = new boolean[numberOfRows]; + break; + case Types.TINYINT: + this.data[i] = new byte[numberOfRows]; + break; + case Types.SMALLINT: + this.data[i] = new short[numberOfRows]; + break; + case Types.INTEGER: + this.data[i] = new int[numberOfRows]; + break; + case Types.BIGINT: + this.data[i] = new long[numberOfRows]; + break; + case Types.REAL: + this.data[i] = new float[numberOfRows]; + break; + case Types.DOUBLE: + this.data[i] = new double[numberOfRows]; + break; + default: + this.data[i] = new Object[numberOfRows]; + } + } + } + // add to the backing array - Object[] next = this.data[++this.pos]; - this.protocol.parseTupleLine(line, next, this.jdbcSQLTypes); + int nextPos = ++this.pos; + this.protocol.parseTupleLine(nextPos, line, this.jdbcSQLTypes, this.data, this.nullMappings[nextPos]); } /** @@ -76,7 +111,7 @@ public class DataBlockResponse implement @Override public boolean wantsMore() { // remember: pos is the value already stored - return (this.pos + 1) < this.data.length; + return (this.pos + 1) < this.nullMappings.length; } /** @@ -87,8 +122,8 @@ public class DataBlockResponse implement */ @Override public void complete() throws SQLException { - if ((this.pos + 1) != this.data.length) { - throw new SQLException("Inconsistent state detected! Current block capacity: " + this.data.length + + if ((this.pos + 1) != this.nullMappings.length) { + throw new SQLException("Inconsistent state detected! Current block capacity: " + this.nullMappings.length + ", block usage: " + (this.pos + 1) + ". Did MonetDB send what it promised to?", "M0M10"); } } @@ -99,28 +134,109 @@ public class DataBlockResponse implement @Override public void close() { // feed all rows to the garbage collector - for (int i = 0; i < data.length; i++) { - for (int j = 0; j < data[0].length; j++) { - data[i][j] = null; - } + int numberOfColumns = this.data.length; + for (int i = 0; i < numberOfColumns; i++) { data[i] = null; + nullMappings[i] = null; + } + data = null; + nullMappings = null; + } + + /* Methods to be called after the block construction has been completed */ + + void setBlockLine(int blockLine) { + this.blockLine = blockLine; + } + + public void setData(Object[] data) { /* For VirtualResultSet :( */ + this.data = data; + } + + public Object[] getData() { /* For VirtualResultSet :( */ + return data; + } + + public boolean checkValueIsNull(int column) { + return this.nullMappings[this.blockLine][column]; + } + + public boolean getBooleanValue(int column) { + return ((boolean[]) this.data[column])[this.blockLine]; + } + + public byte getByteValue(int column) { + return ((byte[]) this.data[column])[this.blockLine]; + } + + public short getShortValue(int column) { + return ((short[]) this.data[column])[this.blockLine]; + } + + public int getIntValue(int column) { + return ((int[]) this.data[column])[this.blockLine]; + } + + public long getLongValue(int column) { + return ((long[]) this.data[column])[this.blockLine]; + } + + public float getFloatValue(int column) { + return ((float[]) this.data[column])[this.blockLine]; + } + + public double getDoubleValue(int column) { + return ((double[]) this.data[column])[this.blockLine]; + } + + public Object getObjectValue(int column) { + return ((Object[]) this.data[column])[this.blockLine]; + } + + public String getValueAsString(int column) { + switch (this.jdbcSQLTypes[column]) { + case Types.BOOLEAN: + return Boolean.toString(((boolean[]) this.data[column])[this.blockLine]); + 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.CHAR: + case Types.VARCHAR: + case Types.CLOB: + case Types.OTHER: + return (String) ((Object[]) this.data[column])[this.blockLine]; + default: + return ((Object[]) this.data[column])[this.blockLine].toString(); } } - /** - * Retrieves the required row. Warning: if the requested rows is out of bounds, an IndexOutOfBoundsException will - * be thrown. - * - * @param line the row to retrieve - * @return the requested row as String - */ - Object[] getRow(int line) { - if (forwardOnly) { - Object[] ret = data[line]; - data[line] = null; - return ret; - } else { - return data[line]; + public Object getValueAsObject(int column) { + switch (this.jdbcSQLTypes[column]) { + case Types.BOOLEAN: + return ((boolean[]) this.data[column])[this.blockLine]; + 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]); + 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 @@ -25,7 +25,7 @@ import java.sql.Types; */ public class ResultSetResponse implements IIncompleteResponse { - private static final byte IsSetFinalValue = 15; + private static final byte IS_SET_FINAL_VALUE = 15; /** The number of columns in this result */ private final int columncount; @@ -73,8 +73,7 @@ public class ResultSetResponse implement * @param tuplecount the total number of tuples in the result set * @param columncount the number of columns in the result set * @param rowcount the number of rows in the current block - * @param parent the parent that created this Response and will - * supply new result blocks when necessary + * @param parent the parent that created this Response and will supply new result blocks when necessary * @param seq the query sequence number */ public ResultSetResponse(MonetConnection con, MonetConnection.ResponseList parent, int id, int seq, int rowcount, @@ -139,7 +138,7 @@ public class ResultSetResponse implement */ @Override public boolean wantsMore() { - return this.isSet < IsSetFinalValue || resultBlocks[0].wantsMore(); + return this.isSet < IS_SET_FINAL_VALUE || resultBlocks[0].wantsMore(); } /** @@ -205,6 +204,11 @@ public class ResultSetResponse implement return type; } + /** + * Returns the JDBC types of the columns + * + * @return the JDBC types of the columns + */ public int[] getJdbcSQLTypes() { return JdbcSQLTypes; } @@ -272,7 +276,7 @@ public class ResultSetResponse implement */ @Override public void addLine(ServerResponses response, Object line) throws ProtocolException { - if (this.isSet >= IsSetFinalValue) { + if (this.isSet >= IS_SET_FINAL_VALUE) { this.resultBlocks[0].addLine(response, line); } else if (response != ServerResponses.HEADER) { throw new ProtocolException("header expected, got: " + response.toString()); @@ -307,7 +311,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 Object[] getLine(int row) throws SQLException { + public DataBlockResponse getDataBlockCorrespondingToLine(int row) throws SQLException { if (row >= tuplecount || row < 0) return null; @@ -355,7 +359,8 @@ public class ResultSetResponse implement throw new AssertionError("block " + block + " should have been fetched by now :("); } } - return rawr.getRow(blockLine); + rawr.setBlockLine(blockLine); + return rawr; } /**