Mercurial > hg > monetdb-java
view src/main/java/nl/cwi/monetdb/mcl/responses/DataBlockResponse.java @ 74:17365ed26611 embedded
In Java, you cannot get a pointer to a String, in order to make a faster memory copy. So I had to change a StringBuilder to a CharBuffer which allows to retrieve the pointer and make a faster processing.
author | Pedro Ferreira <pedro.ferreira@monetdbsolutions.com> |
---|---|
date | Thu, 15 Dec 2016 11:25:04 +0100 (2016-12-15) |
parents | 953422c41194 |
children | 0ae34196c54e |
line wrap: on
line source
package nl.cwi.monetdb.mcl.responses; import nl.cwi.monetdb.mcl.protocol.AbstractProtocol; import nl.cwi.monetdb.mcl.protocol.ProtocolException; import nl.cwi.monetdb.mcl.protocol.ServerResponses; import java.sql.SQLException; import java.sql.Types; /** * The DataBlockResponse is tabular data belonging to a * ResultSetResponse. 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. * * 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. */ 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; /** 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. * * @param rowcount the number of rows * @param columncount the number of columns * @param forward whether this is a forward only result */ DataBlockResponse(int rowcount, int columncount, boolean forward, AbstractProtocol protocol, int[] JdbcSQLTypes) { this.pos = -1; this.data = new Object[columncount]; this.nullMappings = new boolean[rowcount][columncount]; this.protocol = protocol; this.jdbcSQLTypes = JdbcSQLTypes; } /** * addLine adds a String of data to this object's data array. Note that an IndexOutOfBoundsException can be thrown * when an attempt is made to add more than the original construction size specified. * * @param line the header line as String * @param response the line type according to the MAPI protocol * @throws ProtocolException If the result line is not expected */ @Override 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 int nextPos = ++this.pos; this.protocol.parseTupleLine(nextPos, line, this.jdbcSQLTypes, this.data, this.nullMappings[nextPos]); } /** * 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.nullMappings.length; } /** * Indicates that no more header lines will be added to this Response implementation. In most cases this is a * redundant operation because the data array is full. However... it can happen that this is NOT the case! * * @throws SQLException if not all rows are filled */ @Override public void complete() throws SQLException { 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"); } } /** * 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; 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 this.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(); } } 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]; } } }