Mercurial > hg > monetdb-java
changeset 170:477c4de0eda2 embedded
Fixed identation and in-memory option for the embedded connection
line wrap: on
line diff
--- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>monetdb</groupId> <artifactId>monetdb-jdbc-new</artifactId> - <version>2.31</version> + <version>2.32</version> <name>MonetDB JDBC new</name> <description>MonetDB Adapted JDBC driver for embedded connection</description> <url>https://www.monetdb.org</url>
--- a/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in +++ b/src/main/java/nl/cwi/monetdb/jdbc/MonetDriver.java.in @@ -69,7 +69,7 @@ public final class MonetDriver implement /** MonetDB default port to connect to */ private static final String PORT = "@JDBC_DEF_PORT@"; - private static Class embeddedConnectionClass = null; + private static Class embeddedConnectionClass = null; // initialize this class: register it at the DriverManager // Chapter 9.2 from Sun JDBC 3.0 specification @@ -348,59 +348,59 @@ public final class MonetDriver implement throw new SQLException("Invalid URL: it does not start with: " + MONETURL, "08M26"); if(!url.startsWith("jdbc:monetdb:embedded:")) { - // url should be of style jdbc:monetdb://<host>/<database> - isEmbedded = false; - URI uri; - try { - uri = new URI(url.substring(5)); - } catch (URISyntaxException e) { - throw new SQLException(e.toString(), "08M26"); - } + // url should be of style jdbc:monetdb://<host>/<database> + isEmbedded = false; + URI uri; + try { + uri = new URI(url.substring(5)); + } catch (URISyntaxException e) { + throw new SQLException(e.toString(), "08M26"); + } - String uri_host = uri.getHost(); - if (uri_host == null) - throw new SQLException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26"); - info.put("host", uri_host); + String uri_host = uri.getHost(); + if (uri_host == null) + throw new SQLException("Invalid URL: no hostname given or unparsable in '" + url + "'", "08M26"); + info.put("host", uri_host); - int uri_port = uri.getPort(); - if (uri_port > 0) - info.put("port", "" + uri_port); + int uri_port = uri.getPort(); + if (uri_port > 0) + info.put("port", "" + uri_port); - // check the database - String uri_path = uri.getPath(); - if (uri_path != null && uri_path.length() != 0) { - uri_path = uri_path.substring(1); - if (!uri_path.trim().isEmpty()) - info.put("database", uri_path); - } + // check the database + String uri_path = uri.getPath(); + if (uri_path != null && uri_path.length() != 0) { + uri_path = uri_path.substring(1); + if (!uri_path.trim().isEmpty()) + info.put("database", uri_path); + } - String uri_query = uri.getQuery(); - if (uri_query != null) { - // handle additional arguments - String args[] = uri_query.split("&"); - for (String arg : args) { - tmp = arg.indexOf('='); - if (tmp > 0) - info.put(arg.substring(0, tmp), arg.substring(tmp + 1)); - } - } + String uri_query = uri.getQuery(); + if (uri_query != null) { + // handle additional arguments + String args[] = uri_query.split("&"); + for (String arg : args) { + tmp = arg.indexOf('='); + if (tmp > 0) + info.put(arg.substring(0, tmp), arg.substring(tmp + 1)); + } + } } else { - // url should be of style jdbc:monetdb:embedded:<directory> - isEmbedded = true; - info.put("directory", url.substring(22)); + // url should be of style jdbc:monetdb:embedded:<directory> + isEmbedded = true; + info.put("directory", url.substring(22)); } info.put("embedded", Boolean.toString(isEmbedded)); - // finally return the Connection as requested + // finally return the Connection as requested return CreateMonetDBJDBCConnection(info); } - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") private static MonetConnection CreateMonetDBJDBCConnection(Properties props) throws SQLException, IllegalArgumentException { MonetConnection res; - boolean isEmbedded = Boolean.parseBoolean(props.getProperty("embedded", "false")); + boolean isEmbedded = Boolean.parseBoolean(props.getProperty("embedded", "false")); String language = props.getProperty("language", "sql"); String username = props.getProperty("user"); String password = props.getProperty("password"); @@ -409,21 +409,20 @@ public final class MonetDriver implement if(isEmbedded) { //instantiate the connection try { - String directory = props.getProperty("directory"); - if (directory == null || directory.trim().isEmpty()) - throw new IllegalArgumentException("directory should not be null or empty"); - - if(embeddedConnectionClass == null) { - embeddedConnectionClass = Class.forName("nl.cwi.monetdb.embedded.jdbc.EmbeddedConnection"); - if(embeddedConnectionClass == null) { //if it is still null then there is a problem! - throw new SQLException("EmbeddedConnection Class not found! Please add the MonetDBJavaEmbedded JAR to the Classpath!"); - } - } - res = (MonetConnection) embeddedConnectionClass - .getDeclaredConstructor(Properties.class, String.class, String.class, String.class) - .newInstance(props, hash, language, directory); + String directory = props.getProperty("directory"); + if (directory != null && (directory.trim().isEmpty() || directory.equals(":memory:"))) + directory = null; + if(embeddedConnectionClass == null) { + embeddedConnectionClass = Class.forName("nl.cwi.monetdb.embedded.jdbc.EmbeddedConnection"); + if(embeddedConnectionClass == null) { //if it is still null then there is a problem! + throw new SQLException("EmbeddedConnection Class not found! Please add monetdb-java-lite jar to the CLASSPATH"); + } + } + res = (MonetConnection) embeddedConnectionClass + .getDeclaredConstructor(Properties.class, String.class, String.class, String.class) + .newInstance(props, hash, language, directory); } catch (InvocationTargetException | InstantiationException | IllegalAccessException | - NoSuchMethodException | ClassNotFoundException e) { + NoSuchMethodException | ClassNotFoundException e) { throw new SQLException(e); } } else { @@ -434,12 +433,12 @@ public final class MonetDriver implement throw new IllegalArgumentException("user should not be null or empty"); if (password == null || password.trim().isEmpty()) throw new IllegalArgumentException("password should not be null or empty"); - String database = props.getProperty("database"); - if (database == null || database.trim().isEmpty()) - throw new IllegalArgumentException("database should not be null or empty"); + String database = props.getProperty("database"); + if (database == null || database.trim().isEmpty()) + throw new IllegalArgumentException("database should not be null or empty"); - boolean blobIsBinary = Boolean.valueOf(props.getProperty("treat_blob_as_binary", "false")); - boolean clobIsLongChar = Boolean.valueOf(props.getProperty("treat_clob_as_longvarchar", "false")); + boolean blobIsBinary = Boolean.valueOf(props.getProperty("treat_blob_as_binary", "false")); + boolean clobIsLongChar = Boolean.valueOf(props.getProperty("treat_clob_as_longvarchar", "false")); boolean negative1 = false, failedparse1 = false; int port = 0; @@ -506,7 +505,7 @@ public final class MonetDriver implement throw new SQLException("Unable to connect (" + con.getHostname() + ":" + con.getPort() + "): " + e.getMessage(), "08006"); } else { - throw new SQLException("Unable to connect: " + e.getMessage(), "08006"); + throw new SQLException("Unable to connect: " + e.getMessage(), "08006"); } } catch (ProtocolException e) { throw new SQLException(e.getMessage(), "08001");
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/ControlCommands.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/ControlCommands.java @@ -15,14 +15,14 @@ package nl.cwi.monetdb.mcl.connection; */ public final class ControlCommands { - /* Please don't change the order or the values */ + /* Please don't change the order or the values */ - /** Send autocommit statement */ - public static final int AUTO_COMMIT = 1; - /** Set reply size for the server (for the maxrows specification) */ - public static final int REPLY_SIZE = 2; - /** Release a prepared statement data */ - public static final int RELEASE = 3; - /** Close a query */ - public static final int CLOSE = 4; + /** Send autocommit statement */ + public static final int AUTO_COMMIT = 1; + /** Set reply size for the server (for the maxrows specification) */ + public static final int REPLY_SIZE = 2; + /** Release a prepared statement data */ + public static final int RELEASE = 3; + /** Close a query */ + public static final int CLOSE = 4; }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/IMonetDBLanguage.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/IMonetDBLanguage.java @@ -16,40 +16,40 @@ package nl.cwi.monetdb.mcl.connection; */ public interface IMonetDBLanguage { - /** - * Gets the String representation of a query delimiter represented through the index parameter. - * - * @param index The delimiter index starting from 0 - * @return The String representation of the delimiter - */ - String getQueryTemplateIndex(int index); + /** + * Gets the String representation of a query delimiter represented through the index parameter. + * + * @param index The delimiter index starting from 0 + * @return The String representation of the delimiter + */ + String getQueryTemplateIndex(int index); - /** - * Gets the String representation of a command delimiter represented through the index parameter. - * - * @param index The delimiter index starting from 0 - * @return The String representation of the delimiter - */ - String getCommandTemplateIndex(int index); + /** + * Gets the String representation of a command delimiter represented through the index parameter. + * + * @param index The delimiter index starting from 0 + * @return The String representation of the delimiter + */ + String getCommandTemplateIndex(int index); - /** - * Gets all query delimiters. - * - * @return All query delimiters - */ - String[] getQueryTemplates(); + /** + * Gets all query delimiters. + * + * @return All query delimiters + */ + String[] getQueryTemplates(); - /** - * Gets all command delimiters. - * - * @return All command delimiters - */ - String[] getCommandTemplates(); + /** + * Gets all command delimiters. + * + * @return All command delimiters + */ + String[] getCommandTemplates(); - /** - * Gets the String representation of the language currently used. - * - * @return The String representation of the language currently used. - */ - String getRepresentation(); + /** + * Gets the String representation of the language currently used. + * + * @return The String representation of the language currently used. + */ + String getRepresentation(); }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/SenderThread.java @@ -27,121 +27,121 @@ import java.util.concurrent.locks.Reentr */ public class SenderThread extends Thread { - /** The state WAIT represents this thread to be waiting for something to do */ - private static final int WAIT = 1; - /** The state QUERY represents this thread to be executing a query */ - private static final int QUERY = 2; - /** The state SHUTDOWN is the final state that ends this thread */ - private static final int SHUTDOWN = 3; + /** The state WAIT represents this thread to be waiting for something to do */ + private static final int WAIT = 1; + /** The state QUERY represents this thread to be executing a query */ + private static final int QUERY = 2; + /** The state SHUTDOWN is the final state that ends this thread */ + private static final int SHUTDOWN = 3; - private String[] templ; - private String query; - private AbstractProtocol protocol; - private String error; - private int state = SenderThread.WAIT; - private final Lock sendLock = new ReentrantLock(); - private final Condition queryAvailable = sendLock.newCondition(); - private final Condition waiting = sendLock.newCondition(); + private String[] templ; + private String query; + private AbstractProtocol protocol; + private String error; + private int state = SenderThread.WAIT; + private final Lock sendLock = new ReentrantLock(); + private final Condition queryAvailable = sendLock.newCondition(); + private final Condition waiting = sendLock.newCondition(); - /** - * Constructor which immediately starts this thread and sets it into daemon mode. - * - * @param out the socket to write to - */ - public SenderThread(AbstractProtocol out) { - super("SendThread"); - this.setDaemon(true); - this.protocol = out; - this.start(); - } + /** + * Constructor which immediately starts this thread and sets it into daemon mode. + * + * @param out the socket to write to + */ + public SenderThread(AbstractProtocol out) { + super("SendThread"); + this.setDaemon(true); + this.protocol = out; + this.start(); + } - @Override - public void run() { - this.sendLock.lock(); - try { - while (true) { - while (this.state == SenderThread.WAIT) { - try { - this.queryAvailable.await(); - } catch (InterruptedException e) { - // woken up, eh? - } - } - if (this.state == SenderThread.SHUTDOWN) - break; + @Override + public void run() { + this.sendLock.lock(); + try { + while (true) { + while (this.state == SenderThread.WAIT) { + try { + this.queryAvailable.await(); + } catch (InterruptedException e) { + // woken up, eh? + } + } + if (this.state == SenderThread.SHUTDOWN) + break; - // state is QUERY here - try { - this.protocol.writeNextQuery((templ[0] == null ? "" : templ[0]), query, - (templ[1] == null ? "" : templ[1])); - } catch (IOException e) { - this.error = e.getMessage(); - } + // state is QUERY here + try { + this.protocol.writeNextQuery((templ[0] == null ? "" : templ[0]), query, + (templ[1] == null ? "" : templ[1])); + } catch (IOException e) { + this.error = e.getMessage(); + } - // update our state, and notify, maybe someone is waiting for us in throwErrors - this.state = SenderThread.WAIT; - this.waiting.signal(); - } - } finally { - this.sendLock.unlock(); - } - } + // update our state, and notify, maybe someone is waiting for us in throwErrors + this.state = SenderThread.WAIT; + this.waiting.signal(); + } + } finally { + this.sendLock.unlock(); + } + } - /** - * Starts sending the given query over the given socket. Beware that the thread should be finished (can be assured - * by calling throwErrors()) before this method is called! - * - * @param templ the query template - * @param query the query itself - * @throws SQLException if this SendThread is already in use - */ - public void runQuery(String[] templ, String query) throws SQLException { - this.sendLock.lock(); - try { - if (this.state != SenderThread.WAIT) { - throw new SQLException("Sender Thread already in use or shutting down!", "M0M03"); - } - this.templ = templ; - this.query = query; - // let the thread know there is some work to do - this.state = SenderThread.QUERY; - this.queryAvailable.signal(); - } finally { - this.sendLock.unlock(); - } - } + /** + * Starts sending the given query over the given socket. Beware that the thread should be finished (can be assured + * by calling throwErrors()) before this method is called! + * + * @param templ the query template + * @param query the query itself + * @throws SQLException if this SendThread is already in use + */ + public void runQuery(String[] templ, String query) throws SQLException { + this.sendLock.lock(); + try { + if (this.state != SenderThread.WAIT) { + throw new SQLException("Sender Thread already in use or shutting down!", "M0M03"); + } + this.templ = templ; + this.query = query; + // let the thread know there is some work to do + this.state = SenderThread.QUERY; + this.queryAvailable.signal(); + } finally { + this.sendLock.unlock(); + } + } - /** - * Returns errors encountered during the sending process. - * - * @return the errors or null if none - */ - public String getErrors() { - this.sendLock.lock(); - try { - // make sure the thread is in WAIT state, not QUERY - while (this.state == SenderThread.QUERY) { - try { - this.waiting.await(); - } catch (InterruptedException e) { - // just try again - } - } - if (this.state == SenderThread.SHUTDOWN) - this.error = "SendThread is shutting down"; - } finally { - this.sendLock.unlock(); - } - return error; - } + /** + * Returns errors encountered during the sending process. + * + * @return the errors or null if none + */ + public String getErrors() { + this.sendLock.lock(); + try { + // make sure the thread is in WAIT state, not QUERY + while (this.state == SenderThread.QUERY) { + try { + this.waiting.await(); + } catch (InterruptedException e) { + // just try again + } + } + if (this.state == SenderThread.SHUTDOWN) + this.error = "SendThread is shutting down"; + } finally { + this.sendLock.unlock(); + } + return error; + } - /** - * Requests this SendThread to stop. - */ - public void shutdown() { - sendLock.lock(); - state = SenderThread.SHUTDOWN; - sendLock.unlock(); - this.interrupt(); // break any wait conditions - } + /** + * Requests this SendThread to stop. + */ + public void shutdown() { + sendLock.lock(); + state = SenderThread.SHUTDOWN; + sendLock.unlock(); + this.interrupt(); // break any wait conditions + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/BufferReallocator.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/BufferReallocator.java @@ -18,61 +18,61 @@ import java.nio.CharBuffer; */ public final class BufferReallocator { - /** - * The possible MAX_ARRAY_SIZE, according to {@link AbstractStringBuilder}. - */ - private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + /** + * The possible MAX_ARRAY_SIZE, according to {@link AbstractStringBuilder}. + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; - /** - * Calculates the CharBuffer's new capacity, throwing a {@link OutOfMemoryError}, if the capacity causes overflow. - * The capacity will always try to duplicate. - * - * @param buffer The buffer whose capacity will be expanded - * @return The buffer's new capacity - */ - private static int getNewCapacity(CharBuffer buffer) { - int minCapacity = buffer.capacity() << 1; - int newCapacity = (buffer.capacity() << 1) + 2; - if (newCapacity - minCapacity < 0) { - newCapacity = minCapacity; - } + /** + * Calculates the CharBuffer's new capacity, throwing a {@link OutOfMemoryError}, if the capacity causes overflow. + * The capacity will always try to duplicate. + * + * @param buffer The buffer whose capacity will be expanded + * @return The buffer's new capacity + */ + private static int getNewCapacity(CharBuffer buffer) { + int minCapacity = buffer.capacity() << 1; + int newCapacity = (buffer.capacity() << 1) + 2; + if (newCapacity - minCapacity < 0) { + newCapacity = minCapacity; + } - if(newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) { - if (Integer.MAX_VALUE - minCapacity < 0) { // overflow - throw new OutOfMemoryError(); - } - return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE; - } else { - return newCapacity; - } - } + if(newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) { + if (Integer.MAX_VALUE - minCapacity < 0) { // overflow + throw new OutOfMemoryError(); + } + return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE; + } else { + return newCapacity; + } + } - /** - * Reallocates the buffer by creating a new one with the new capacity and the contents of the previous one. - * - * @param buffer The buffer whose capacity will be expanded - * @return The new buffer allocated - */ - public static CharBuffer reallocateBuffer(CharBuffer buffer) { - int newCapacity = getNewCapacity(buffer); - CharBuffer newBuffer = CharBuffer.wrap(new char[newCapacity]); - buffer.flip(); - newBuffer.put(buffer.array()); - return newBuffer; - } + /** + * Reallocates the buffer by creating a new one with the new capacity and the contents of the previous one. + * + * @param buffer The buffer whose capacity will be expanded + * @return The new buffer allocated + */ + public static CharBuffer reallocateBuffer(CharBuffer buffer) { + int newCapacity = getNewCapacity(buffer); + CharBuffer newBuffer = CharBuffer.wrap(new char[newCapacity]); + buffer.flip(); + newBuffer.put(buffer.array()); + return newBuffer; + } - /** - * Ensures that a buffer has a certain amount of capacity, creating a new one if the new capacity is larger than the - * current one in the buffer - * - * @param buffer The buffer whose capacity will be checked - * @param capacityThreshold The capacity threshold to test - * @return The original buffer or the new one allocated - */ - public static CharBuffer ensureCapacity(CharBuffer buffer, int capacityThreshold) { - if(capacityThreshold > buffer.capacity()) { - buffer = CharBuffer.wrap(new char[capacityThreshold]); - } - return buffer; - } + /** + * Ensures that a buffer has a certain amount of capacity, creating a new one if the new capacity is larger than the + * current one in the buffer + * + * @param buffer The buffer whose capacity will be checked + * @param capacityThreshold The capacity threshold to test + * @return The original buffer or the new one allocated + */ + public static CharBuffer ensureCapacity(CharBuffer buffer, int capacityThreshold) { + if(capacityThreshold > buffer.capacity()) { + buffer = CharBuffer.wrap(new char[capacityThreshold]); + } + return buffer; + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/ChannelSecurity.java @@ -18,40 +18,40 @@ import java.security.NoSuchAlgorithmExce */ public final class ChannelSecurity { - private static char hexChar(int n) { return (n > 9) ? (char) ('a' + (n - 10)) : (char) ('0' + n); } + private static char hexChar(int n) { return (n > 9) ? (char) ('a' + (n - 10)) : (char) ('0' + n); } - /** - * Helper method to convert a byte string to a hexadecimal String representation. - * - * @param digest The byte array to convert - * @return The byte array as a hexadecimal string - */ - private static String toHex(byte[] digest) { - char[] result = new char[digest.length * 2]; - int pos = 0; - for (byte aDigest : digest) { - result[pos++] = hexChar((aDigest & 0xf0) >> 4); - result[pos++] = hexChar(aDigest & 0x0f); - } - return new String(result); - } + /** + * Helper method to convert a byte string to a hexadecimal String representation. + * + * @param digest The byte array to convert + * @return The byte array as a hexadecimal string + */ + private static String toHex(byte[] digest) { + char[] result = new char[digest.length * 2]; + int pos = 0; + for (byte aDigest : digest) { + result[pos++] = hexChar((aDigest & 0xf0) >> 4); + result[pos++] = hexChar(aDigest & 0x0f); + } + return new String(result); + } - /** - * Digests several byte[] into a String digest, using a specified hash algorithm. - * - * @param algorithm The hash algorithm to use - * @param toDigests The Strings to digest - * @return The Strings digest as a hexadecimal string - */ - public static String digestStrings(String algorithm, byte[]... toDigests) { - try { - MessageDigest md = MessageDigest.getInstance(algorithm); - for (byte[] str : toDigests) { - md.update(str); - } - return toHex(md.digest()); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError("internal error: " + e.toString()); - } - } + /** + * Digests several byte[] into a String digest, using a specified hash algorithm. + * + * @param algorithm The hash algorithm to use + * @param toDigests The Strings to digest + * @return The Strings digest as a hexadecimal string + */ + public static String digestStrings(String algorithm, byte[]... toDigests) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + for (byte[] str : toDigests) { + md.update(str); + } + return toHex(md.digest()); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError("internal error: " + e.toString()); + } + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/GregorianCalendarParser.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/GregorianCalendarParser.java @@ -26,192 +26,192 @@ import java.util.TimeZone; */ public final class GregorianCalendarParser { - /** - * Small helper method that returns the intrinsic value of a char if it represents a digit. If a non-digit character - * is encountered an MCLParseException is thrown. - * - * @param c the char - * @param pos the position - * @return the intrinsic value of the char - * @throws ProtocolException if c is not a digit - */ - private static int getIntrinsicValue(char c, int pos) throws ProtocolException { - // note: don't use Character.isDigit() here, because we only want ISO-LATIN-1 digits - if (c >= '0' && c <= '9') { - return (int)c - (int)'0'; - } else { - throw new ProtocolException("Expected a digit", pos); - } - } + /** + * Small helper method that returns the intrinsic value of a char if it represents a digit. If a non-digit character + * is encountered an MCLParseException is thrown. + * + * @param c the char + * @param pos the position + * @return the intrinsic value of the char + * @throws ProtocolException if c is not a digit + */ + private static int getIntrinsicValue(char c, int pos) throws ProtocolException { + // note: don't use Character.isDigit() here, because we only want ISO-LATIN-1 digits + if (c >= '0' && c <= '9') { + return (int)c - (int)'0'; + } else { + throw new ProtocolException("Expected a digit", pos); + } + } - /** The time zone information, to be re-used to avoid memory allocations */ - private static final TimeZone defaultTimeZone = TimeZone.getDefault(); + /** The time zone information, to be re-used to avoid memory allocations */ + private static final TimeZone defaultTimeZone = TimeZone.getDefault(); - /** - * Parses a date or time or timestamp MAPI String into a Java {@link Calendar} instance. - * - * @param mrs A MonetResultSet instance where warning can be added - * @param toParse The date or time or timestamp String to parse - * @param pos The position of the String to start the parsing - * @param parser The parser to use - * @param jdbcType The JDBC type of the column - * @return A {@link Calendar} instance of the parsed date - */ - public static Calendar parseDateString(MonetResultSet mrs, String toParse, ParsePosition pos, - SimpleDateFormat parser, int jdbcType) { - pos.setIndex(0); - Calendar res = new GregorianCalendar(); - if(jdbcType == Types.TIME || jdbcType == Types.TIMESTAMP || jdbcType == Types.DATE) { - parser.setTimeZone(TimeZone.getTimeZone("GMT" + toParse.substring(toParse.length() - 6))); - } else { - parser.setTimeZone(defaultTimeZone); - } + /** + * Parses a date or time or timestamp MAPI String into a Java {@link Calendar} instance. + * + * @param mrs A MonetResultSet instance where warning can be added + * @param toParse The date or time or timestamp String to parse + * @param pos The position of the String to start the parsing + * @param parser The parser to use + * @param jdbcType The JDBC type of the column + * @return A {@link Calendar} instance of the parsed date + */ + public static Calendar parseDateString(MonetResultSet mrs, String toParse, ParsePosition pos, + SimpleDateFormat parser, int jdbcType) { + pos.setIndex(0); + Calendar res = new GregorianCalendar(); + if(jdbcType == Types.TIME || jdbcType == Types.TIMESTAMP || jdbcType == Types.DATE) { + parser.setTimeZone(TimeZone.getTimeZone("GMT" + toParse.substring(toParse.length() - 6))); + } else { + parser.setTimeZone(defaultTimeZone); + } - Date aux = parser.parse(toParse, pos); - if (aux == null) { - // parsing failed - int epos = pos.getErrorIndex(); - if (epos == -1) { - mrs.addWarning("parsing '" + toParse + "' failed", "01M10"); - } else if (epos < toParse.length()) { - mrs.addWarning("parsing failed, found: '" + toParse.charAt(epos) + "' in: \"" + toParse + - "\" at pos: " + pos.getErrorIndex(), "01M10"); - } else { - mrs.addWarning("parsing failed, expected more data after '" + toParse + "'", "01M10"); - } - res.clear(); - } else { - res.setTime(aux); - } + Date aux = parser.parse(toParse, pos); + if (aux == null) { + // parsing failed + int epos = pos.getErrorIndex(); + if (epos == -1) { + mrs.addWarning("parsing '" + toParse + "' failed", "01M10"); + } else if (epos < toParse.length()) { + mrs.addWarning("parsing failed, found: '" + toParse.charAt(epos) + "' in: \"" + toParse + + "\" at pos: " + pos.getErrorIndex(), "01M10"); + } else { + mrs.addWarning("parsing failed, expected more data after '" + toParse + "'", "01M10"); + } + res.clear(); + } else { + res.setTime(aux); + } - if (jdbcType != Types.DATE) { - // parse additional nanos (if any) - int pos1 = pos.getIndex(), nanos; - char[] monDate = toParse.toCharArray(); - if (pos1 < monDate.length && monDate[pos1] == '.') { - pos1++; - int ctr; - try { - nanos = getIntrinsicValue(monDate[pos1], pos1++); - for (ctr = 1; pos1 < monDate.length && monDate[pos1] >= '0' && monDate[pos1] <= '9'; ctr++) { - if (ctr < 9) { - nanos *= 10; - nanos += (getIntrinsicValue(monDate[pos1], pos1)); - } - if (ctr == 2) // we have three at this point - res.set(Calendar.MILLISECOND, nanos); - pos1++; - } - while (ctr++ < 9) - nanos *= 10; - } catch(ProtocolException e) { - mrs.addWarning(e.getMessage() + " found: '" + monDate[e.getErrorOffset()] + "' in: \"" + - toParse + "\" at pos: " + e.getErrorOffset(), "01M10"); - res.clear(); - } - } - } - return res; - } + if (jdbcType != Types.DATE) { + // parse additional nanos (if any) + int pos1 = pos.getIndex(), nanos; + char[] monDate = toParse.toCharArray(); + if (pos1 < monDate.length && monDate[pos1] == '.') { + pos1++; + int ctr; + try { + nanos = getIntrinsicValue(monDate[pos1], pos1++); + for (ctr = 1; pos1 < monDate.length && monDate[pos1] >= '0' && monDate[pos1] <= '9'; ctr++) { + if (ctr < 9) { + nanos *= 10; + nanos += (getIntrinsicValue(monDate[pos1], pos1)); + } + if (ctr == 2) // we have three at this point + res.set(Calendar.MILLISECOND, nanos); + pos1++; + } + while (ctr++ < 9) + nanos *= 10; + } catch(ProtocolException e) { + mrs.addWarning(e.getMessage() + " found: '" + monDate[e.getErrorOffset()] + "' in: \"" + + toParse + "\" at pos: " + e.getErrorOffset(), "01M10"); + res.clear(); + } + } + } + return res; + } - /** - * Parses a date MAPI String into a Java {@link Calendar} instance. - * - * @param toParse The date String to parse - * @param pos The position of the String to start the parsing - * @param parser The parser to use (date) - * @return A {@link Calendar} instance of the parsed date - * @throws ProtocolException If the String could not be parsed - */ - public static Calendar parseDate(String toParse, ParsePosition pos, SimpleDateFormat parser) - throws ProtocolException { - pos.setIndex(0); - Calendar res = new GregorianCalendar(); - Date util = parser.parse(toParse, pos); - if(util == null) { - res.clear(); - } else { - res.setTime(util); - } - return res; - } + /** + * Parses a date MAPI String into a Java {@link Calendar} instance. + * + * @param toParse The date String to parse + * @param pos The position of the String to start the parsing + * @param parser The parser to use (date) + * @return A {@link Calendar} instance of the parsed date + * @throws ProtocolException If the String could not be parsed + */ + public static Calendar parseDate(String toParse, ParsePosition pos, SimpleDateFormat parser) + throws ProtocolException { + pos.setIndex(0); + Calendar res = new GregorianCalendar(); + Date util = parser.parse(toParse, pos); + if(util == null) { + res.clear(); + } else { + res.setTime(util); + } + return res; + } - /** - * Parses a time or a timestamp MAPI String into a Java {@link Calendar} instance. - * - * @param toParse The time String to parse - * @param pos The position of the String to start the parsing - * @param hasTimeZone If the time String has timezone information - * @param parser The parser to use (time) - * @return A {@link Calendar} instance of the parsed time - * @throws ProtocolException If the String could not be parsed - */ - public static Calendar parseTime(String toParse, ParsePosition pos, SimpleDateFormat parser, boolean hasTimeZone) - throws ProtocolException { - pos.setIndex(0); - if(hasTimeZone) { // MonetDB/SQL99: Sign TwoDigitHours : Minutes - parser.setTimeZone(TimeZone.getTimeZone("GMT" + toParse.substring(toParse.length() - 6))); - } else { - parser.setTimeZone(defaultTimeZone); - } + /** + * Parses a time or a timestamp MAPI String into a Java {@link Calendar} instance. + * + * @param toParse The time String to parse + * @param pos The position of the String to start the parsing + * @param hasTimeZone If the time String has timezone information + * @param parser The parser to use (time) + * @return A {@link Calendar} instance of the parsed time + * @throws ProtocolException If the String could not be parsed + */ + public static Calendar parseTime(String toParse, ParsePosition pos, SimpleDateFormat parser, boolean hasTimeZone) + throws ProtocolException { + pos.setIndex(0); + if(hasTimeZone) { // MonetDB/SQL99: Sign TwoDigitHours : Minutes + parser.setTimeZone(TimeZone.getTimeZone("GMT" + toParse.substring(toParse.length() - 6))); + } else { + parser.setTimeZone(defaultTimeZone); + } - Calendar res = new GregorianCalendar(); - Date util = parser.parse(toParse, pos); - if(util == null) { - res.clear(); - } else { - res.setTime(util); - } - return res; - } + Calendar res = new GregorianCalendar(); + Date util = parser.parse(toParse, pos); + if(util == null) { + res.clear(); + } else { + res.setTime(util); + } + return res; + } - /** - * Parses a timestamp MAPI String into a {@link TimestampHelper} instance. - * - * @param toParse The timestamp String to parse - * @param pos The position of the String to start the parsing - * @param hasTimeZone If the timestamp String has timezone information - * @param parser The parser to use (timestamp) - * @return A {@link TimestampHelper} instance of the parsed timestamp with nanos information - * @throws ProtocolException If the String could not be parsed - */ - public static TimestampHelper parseTimestamp(String toParse, ParsePosition pos, SimpleDateFormat parser, - boolean hasTimeZone) throws ProtocolException { - pos.setIndex(0); - if(hasTimeZone) { // MonetDB/SQL99: Sign TwoDigitHours : Minutes - parser.setTimeZone(TimeZone.getTimeZone("GMT" + toParse.substring(toParse.length() - 6))); - } else { - parser.setTimeZone(defaultTimeZone); - } + /** + * Parses a timestamp MAPI String into a {@link TimestampHelper} instance. + * + * @param toParse The timestamp String to parse + * @param pos The position of the String to start the parsing + * @param hasTimeZone If the timestamp String has timezone information + * @param parser The parser to use (timestamp) + * @return A {@link TimestampHelper} instance of the parsed timestamp with nanos information + * @throws ProtocolException If the String could not be parsed + */ + public static TimestampHelper parseTimestamp(String toParse, ParsePosition pos, SimpleDateFormat parser, + boolean hasTimeZone) throws ProtocolException { + pos.setIndex(0); + if(hasTimeZone) { // MonetDB/SQL99: Sign TwoDigitHours : Minutes + parser.setTimeZone(TimeZone.getTimeZone("GMT" + toParse.substring(toParse.length() - 6))); + } else { + parser.setTimeZone(defaultTimeZone); + } - GregorianCalendar res = new GregorianCalendar(); - Date util = parser.parse(toParse, pos); - if(util != null) { - res.setTime(util); - } else { - res.clear(); - } + GregorianCalendar res = new GregorianCalendar(); + Date util = parser.parse(toParse, pos); + if(util != null) { + res.setTime(util); + } else { + res.clear(); + } - // parse additional nanos (if any) - int pos1 = pos.getIndex(), nanos = 0; - if (pos1 < toParse.length() && toParse.charAt(pos1) == '.') { - pos1++; - int ctr; + // parse additional nanos (if any) + int pos1 = pos.getIndex(), nanos = 0; + if (pos1 < toParse.length() && toParse.charAt(pos1) == '.') { + pos1++; + int ctr; - nanos = getIntrinsicValue(toParse.charAt(pos1), pos1++); - for (ctr = 1; pos1 < toParse.length() && toParse.charAt(pos1) >= '0' && toParse.charAt(pos1) <= '9'; ctr++) { - if (ctr < 9) { - nanos *= 10; - nanos += (getIntrinsicValue(toParse.charAt(pos1), pos1)); - } - if (ctr == 2) { // we have three at this point - res.set(Calendar.MILLISECOND, nanos); - } - pos1++; - } - while (ctr++ < 9) - nanos *= 10; - } - return new TimestampHelper(res, nanos); - } + nanos = getIntrinsicValue(toParse.charAt(pos1), pos1++); + for (ctr = 1; pos1 < toParse.length() && toParse.charAt(pos1) >= '0' && toParse.charAt(pos1) <= '9'; ctr++) { + if (ctr < 9) { + nanos *= 10; + nanos += (getIntrinsicValue(toParse.charAt(pos1), pos1)); + } + if (ctr == 2) { // we have three at this point + res.set(Calendar.MILLISECOND, nanos); + } + pos1++; + } + while (ctr++ < 9) + nanos *= 10; + } + return new TimestampHelper(res, nanos); + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/TimestampHelper.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/helpers/TimestampHelper.java @@ -12,61 +12,61 @@ import java.util.Calendar; */ public class TimestampHelper { - /** The calendar instance */ - private Calendar calendar; + /** The calendar instance */ + private Calendar calendar; - /** The nanoseconds information */ - private int nanoseconds; + /** The nanoseconds information */ + private int nanoseconds; - TimestampHelper(Calendar calendar, int nanoseconds) { - this.calendar = calendar; - this.nanoseconds = nanoseconds; - } + TimestampHelper(Calendar calendar, int nanoseconds) { + this.calendar = calendar; + this.nanoseconds = nanoseconds; + } - /** - * Gets the Calendar instance. - * - * @return The Calendar instance - */ - public Calendar getCalendar() { - return calendar; - } + /** + * Gets the Calendar instance. + * + * @return The Calendar instance + */ + public Calendar getCalendar() { + return calendar; + } - /** - * Sets the Calendar instance. - * - * @param calendar The Calendar instance - */ - public void setCalendar(Calendar calendar) { - this.calendar = calendar; - } + /** + * Sets the Calendar instance. + * + * @param calendar The Calendar instance + */ + public void setCalendar(Calendar calendar) { + this.calendar = calendar; + } - /** - * Gets the nanoseconds information. - * - * @return The nanoseconds information - */ - public int getNanoseconds() { - return nanoseconds; - } + /** + * Gets the nanoseconds information. + * + * @return The nanoseconds information + */ + public int getNanoseconds() { + return nanoseconds; + } - /** - * Sets the nanoseconds information. - * - * @param nanoseconds The nanoseconds information - */ - public void setNanoseconds(int nanoseconds) { - this.nanoseconds = nanoseconds; - } + /** + * Sets the nanoseconds information. + * + * @param nanoseconds The nanoseconds information + */ + public void setNanoseconds(int nanoseconds) { + this.nanoseconds = nanoseconds; + } - /** - * Generates a {@link Timestamp} instance from the provided information. - * - * @return The generated {@link Timestamp} instance - */ - public Timestamp getTimestamp() { - Timestamp res = new Timestamp(calendar.getTimeInMillis()); - res.setNanos(nanoseconds); - return res; - } + /** + * Generates a {@link Timestamp} instance from the provided information. + * + * @return The generated {@link Timestamp} instance + */ + public Timestamp getTimestamp() { + Timestamp res = new Timestamp(calendar.getTimeInMillis()); + res.setNanos(nanoseconds); + return res; + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/AbstractSocket.java @@ -33,241 +33,241 @@ import java.nio.charset.StandardCharsets * @author Pedro Ferreira */ public abstract class AbstractSocket implements Closeable { - /** The TCP Socket to mserver */ - protected final Socket socket; - /** The MAPI connection this socket belong to */ - protected final MapiConnection connection; - /** ByteBuffer to read from the underlying socket InputStream */ - private final ByteBuffer bufferIn; - /** ByteBuffer to write into the underlying socket OutputStream */ - private final ByteBuffer bufferOut; - /** The bytes read from the bufferIn decoded into UTF-16 */ - private final CharBuffer stringsDecoded; - /** The bytes to write into the bufferOut encoded into UTF-8 */ - private final CharBuffer stringsEncoded; - /** UTF-8 encoder */ - private final CharsetEncoder utf8Encoder = StandardCharsets.UTF_8.newEncoder(); - /** UTF-8 decoder */ - private final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder(); + /** The TCP Socket to mserver */ + protected final Socket socket; + /** The MAPI connection this socket belong to */ + protected final MapiConnection connection; + /** ByteBuffer to read from the underlying socket InputStream */ + private final ByteBuffer bufferIn; + /** ByteBuffer to write into the underlying socket OutputStream */ + private final ByteBuffer bufferOut; + /** The bytes read from the bufferIn decoded into UTF-16 */ + private final CharBuffer stringsDecoded; + /** The bytes to write into the bufferOut encoded into UTF-8 */ + private final CharBuffer stringsEncoded; + /** UTF-8 encoder */ + private final CharsetEncoder utf8Encoder = StandardCharsets.UTF_8.newEncoder(); + /** UTF-8 decoder */ + private final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder(); - AbstractSocket(String hostname, int port, MapiConnection connection) throws IOException { - this.socket = new Socket(hostname, port); - this.connection = connection; - this.bufferIn = ByteBuffer.wrap(new byte[getFullBlockSize()]); - this.bufferOut = ByteBuffer.wrap(new byte[getFullBlockSize()]); - this.stringsDecoded = CharBuffer.allocate(getFullBlockSize()); - this.stringsDecoded.flip(); - this.stringsEncoded = CharBuffer.allocate(getFullBlockSize()); - } + AbstractSocket(String hostname, int port, MapiConnection connection) throws IOException { + this.socket = new Socket(hostname, port); + this.connection = connection; + this.bufferIn = ByteBuffer.wrap(new byte[getFullBlockSize()]); + this.bufferOut = ByteBuffer.wrap(new byte[getFullBlockSize()]); + this.stringsDecoded = CharBuffer.allocate(getFullBlockSize()); + this.stringsDecoded.flip(); + this.stringsEncoded = CharBuffer.allocate(getFullBlockSize()); + } - /** - * Get the socket timeout in milliseconds. - * - * @return The currently in use socket timeout in milliseconds - * @throws SocketException If an error in the underlying connection happened - */ - int getSoTimeout() throws SocketException { - return socket.getSoTimeout(); - } + /** + * Get the socket timeout in milliseconds. + * + * @return The currently in use socket timeout in milliseconds + * @throws SocketException If an error in the underlying connection happened + */ + int getSoTimeout() throws SocketException { + return socket.getSoTimeout(); + } - /** - * Sets the socket timeout in milliseconds. - * - * @param s The socket timeout in milliseconds - * @throws SocketException If an error in the underlying connection happened - */ - void setSoTimeout(int s) throws SocketException { - socket.setSoTimeout(s); - } + /** + * Sets the socket timeout in milliseconds. + * + * @param s The socket timeout in milliseconds + * @throws SocketException If an error in the underlying connection happened + */ + void setSoTimeout(int s) throws SocketException { + socket.setSoTimeout(s); + } - /** - * Sets the TCP no delay feature in the underlying socket. - * - * @param on A true or false value - * @throws SocketException If an error in the underlying connection happened - */ - void setTcpNoDelay(boolean on) throws SocketException { - socket.setTcpNoDelay(on); - } + /** + * Sets the TCP no delay feature in the underlying socket. + * + * @param on A true or false value + * @throws SocketException If an error in the underlying connection happened + */ + void setTcpNoDelay(boolean on) throws SocketException { + socket.setTcpNoDelay(on); + } - /** - * Sets the underlying socket Endianness. - * - * @param bo A ByteOrder order value either Little-endian or Big-endian - */ - void setSocketChannelEndianness(ByteOrder bo) { - this.bufferIn.order(bo); - this.bufferOut.order(bo); - } + /** + * Sets the underlying socket Endianness. + * + * @param bo A ByteOrder order value either Little-endian or Big-endian + */ + void setSocketChannelEndianness(ByteOrder bo) { + this.bufferIn.order(bo); + this.bufferOut.order(bo); + } - /** - * Gets the underlying socket full block size. - * - * @return The underlying socket full block size - */ - public abstract int getFullBlockSize(); + /** + * Gets the underlying socket full block size. + * + * @return The underlying socket full block size + */ + public abstract int getFullBlockSize(); - /** - * Gets the underlying socket block size. - * - * @return The underlying socket block size - */ - public abstract int getBlockSize(); + /** + * Gets the underlying socket block size. + * + * @return The underlying socket block size + */ + public abstract int getBlockSize(); - /** - * Reads from the underlying socket into the bufferIn. - * - * @return The number off bytes read - * @throws IOException If an error in the underlying connection happened - */ - abstract int readToBufferIn(ByteBuffer bufferIn) throws IOException; + /** + * Reads from the underlying socket into the bufferIn. + * + * @return The number off bytes read + * @throws IOException If an error in the underlying connection happened + */ + abstract int readToBufferIn(ByteBuffer bufferIn) throws IOException; - /** - * Writes from bufferOut into the underlying socket. - * - * @return The number off bytes written - * @throws IOException If an error in the underlying connection happened - */ - abstract int writeFromBufferOut(ByteBuffer bufferOut) throws IOException; + /** + * Writes from bufferOut into the underlying socket. + * + * @return The number off bytes written + * @throws IOException If an error in the underlying connection happened + */ + abstract int writeFromBufferOut(ByteBuffer bufferOut) throws IOException; - /** - * Flushes the output. - * - * @throws IOException If an error in the underlying connection happened - */ - abstract void flush() throws IOException; + /** + * Flushes the output. + * + * @throws IOException If an error in the underlying connection happened + */ + abstract void flush() throws IOException; - /** - * Helper method to read and decode UTF-8 data. - * - * @throws IOException If an error in the underlying connection happened - */ - private void readToInputBuffer() throws IOException { - int read = this.readToBufferIn(this.bufferIn); - if(read == 0) { - throw new IOException("The server has reached EOF!"); - } - this.stringsDecoded.clear(); - this.utf8Decoder.reset(); - this.utf8Decoder.decode(this.bufferIn, this.stringsDecoded,true); - this.utf8Decoder.flush(this.stringsDecoded); - this.stringsDecoded.flip(); - } + /** + * Helper method to read and decode UTF-8 data. + * + * @throws IOException If an error in the underlying connection happened + */ + private void readToInputBuffer() throws IOException { + int read = this.readToBufferIn(this.bufferIn); + if(read == 0) { + throw new IOException("The server has reached EOF!"); + } + this.stringsDecoded.clear(); + this.utf8Decoder.reset(); + this.utf8Decoder.decode(this.bufferIn, this.stringsDecoded,true); + this.utf8Decoder.flush(this.stringsDecoded); + this.stringsDecoded.flip(); + } - /** - * Reads a line into the input lineBuffer, reallocating it if necessary. - * - * @param lineBuffer The buffer the data will be read into - * @return The input lineBuffer - * @throws IOException If an error in the underlying connection happened - */ - public CharBuffer readLine(CharBuffer lineBuffer) throws IOException { - lineBuffer.clear(); - boolean found = false; - char[] sourceArray = this.stringsDecoded.array(); - int sourcePosition = this.stringsDecoded.position(); - int sourceLimit = this.stringsDecoded.limit(); - char[] destinationArray = lineBuffer.array(); - int destinationPosition = 0; - int destinationLimit = lineBuffer.limit(); + /** + * Reads a line into the input lineBuffer, reallocating it if necessary. + * + * @param lineBuffer The buffer the data will be read into + * @return The input lineBuffer + * @throws IOException If an error in the underlying connection happened + */ + public CharBuffer readLine(CharBuffer lineBuffer) throws IOException { + lineBuffer.clear(); + boolean found = false; + char[] sourceArray = this.stringsDecoded.array(); + int sourcePosition = this.stringsDecoded.position(); + int sourceLimit = this.stringsDecoded.limit(); + char[] destinationArray = lineBuffer.array(); + int destinationPosition = 0; + int destinationLimit = lineBuffer.limit(); - while(!found) { - if(sourcePosition >= sourceLimit) { - this.stringsDecoded.position(sourcePosition); - this.readToInputBuffer(); - sourceArray = this.stringsDecoded.array(); - sourcePosition = 0; - sourceLimit = this.stringsDecoded.limit(); - } - char c = sourceArray[sourcePosition++]; - if(c == '\n') { - found = true; - } else { - if(destinationPosition + 1 >= destinationLimit) { - lineBuffer = BufferReallocator.reallocateBuffer(lineBuffer); - destinationArray = lineBuffer.array(); - destinationLimit = lineBuffer.limit(); - } - destinationArray[destinationPosition++] = c; - } - } - this.stringsDecoded.position(sourcePosition); - lineBuffer.position(destinationPosition); - lineBuffer.flip(); - return lineBuffer; - } + while(!found) { + if(sourcePosition >= sourceLimit) { + this.stringsDecoded.position(sourcePosition); + this.readToInputBuffer(); + sourceArray = this.stringsDecoded.array(); + sourcePosition = 0; + sourceLimit = this.stringsDecoded.limit(); + } + char c = sourceArray[sourcePosition++]; + if(c == '\n') { + found = true; + } else { + if(destinationPosition + 1 >= destinationLimit) { + lineBuffer = BufferReallocator.reallocateBuffer(lineBuffer); + destinationArray = lineBuffer.array(); + destinationLimit = lineBuffer.limit(); + } + destinationArray[destinationPosition++] = c; + } + } + this.stringsDecoded.position(sourcePosition); + lineBuffer.position(destinationPosition); + lineBuffer.flip(); + return lineBuffer; + } - /** - * Helper method to write, encode into UTF-8 and flush. - * - * @param toFlush A boolean indicating to flush the underlying stream or not - * @throws IOException If an error in the underlying connection happened - */ - private void writeToOutputBuffer(boolean toFlush) throws IOException { - this.stringsEncoded.flip(); - this.utf8Encoder.reset(); - CoderResult res; - int written = 0; - do { //to avoid overflow in the UTF-16 to UTF-8 conversion, has to do this cycle - res = this.utf8Encoder.encode(this.stringsEncoded, this.bufferOut, false); - written += this.writeFromBufferOut(this.bufferOut); - } while (res == CoderResult.OVERFLOW); + /** + * Helper method to write, encode into UTF-8 and flush. + * + * @param toFlush A boolean indicating to flush the underlying stream or not + * @throws IOException If an error in the underlying connection happened + */ + private void writeToOutputBuffer(boolean toFlush) throws IOException { + this.stringsEncoded.flip(); + this.utf8Encoder.reset(); + CoderResult res; + int written = 0; + do { //to avoid overflow in the UTF-16 to UTF-8 conversion, has to do this cycle + res = this.utf8Encoder.encode(this.stringsEncoded, this.bufferOut, false); + written += this.writeFromBufferOut(this.bufferOut); + } while (res == CoderResult.OVERFLOW); - this.utf8Encoder.encode(this.stringsEncoded, this.bufferOut, true); - this.utf8Encoder.flush(this.bufferOut); - written += this.writeFromBufferOut(this.bufferOut); + this.utf8Encoder.encode(this.stringsEncoded, this.bufferOut, true); + this.utf8Encoder.flush(this.bufferOut); + written += this.writeFromBufferOut(this.bufferOut); - this.stringsEncoded.clear(); - this.bufferOut.clear(); - if(written == 0) { - throw new IOException("The query could not be sent to the server!"); - } else { - if(toFlush) { - this.flush(); - } - } - } + this.stringsEncoded.clear(); + this.bufferOut.clear(); + if(written == 0) { + throw new IOException("The query could not be sent to the server!"); + } else { + if(toFlush) { + this.flush(); + } + } + } - /** - * Writes a String line into the underlying socket. - * - * @param line The line to write in the socket - * @throws IOException If an error in the underlying connection happened - */ - private void writeNextBlock(String line) throws IOException { - int limit = line.length(); - int destinationPosition = this.stringsEncoded.position(); - int destinationCapacity = this.stringsEncoded.capacity(); - char[] destinationArray = this.stringsEncoded.array(); + /** + * Writes a String line into the underlying socket. + * + * @param line The line to write in the socket + * @throws IOException If an error in the underlying connection happened + */ + private void writeNextBlock(String line) throws IOException { + int limit = line.length(); + int destinationPosition = this.stringsEncoded.position(); + int destinationCapacity = this.stringsEncoded.capacity(); + char[] destinationArray = this.stringsEncoded.array(); - for (int i = 0; i < limit; i++) { - if (destinationPosition >= destinationCapacity) { - this.stringsEncoded.position(destinationPosition); - this.writeToOutputBuffer(false); - destinationArray = this.stringsEncoded.array(); - destinationPosition = 0; - } - destinationArray[destinationPosition++] = line.charAt(i); - } - this.stringsEncoded.position(destinationPosition); - } + for (int i = 0; i < limit; i++) { + if (destinationPosition >= destinationCapacity) { + this.stringsEncoded.position(destinationPosition); + this.writeToOutputBuffer(false); + destinationArray = this.stringsEncoded.array(); + destinationPosition = 0; + } + destinationArray[destinationPosition++] = line.charAt(i); + } + this.stringsEncoded.position(destinationPosition); + } - /** - * Writes a String line as well a String prefix and suffix if supplied. - * - * @param prefix The prefix to write before the line if provided - * @param line The line to write into the socket - * @param suffix The suffix to write after the line if provided - * @throws IOException If an error in the underlying connection happened - */ - public void writeNextLine(String prefix, String line, String suffix) throws IOException { - if(prefix != null) { - this.writeNextBlock(prefix); - } - this.writeNextBlock(line); - if(suffix != null) { - this.writeNextBlock(suffix); - } - this.writeToOutputBuffer(true); - } + /** + * Writes a String line as well a String prefix and suffix if supplied. + * + * @param prefix The prefix to write before the line if provided + * @param line The line to write into the socket + * @param suffix The suffix to write after the line if provided + * @throws IOException If an error in the underlying connection happened + */ + public void writeNextLine(String prefix, String line, String suffix) throws IOException { + if(prefix != null) { + this.writeNextBlock(prefix); + } + this.writeNextBlock(line); + if(suffix != null) { + this.writeNextBlock(suffix); + } + this.writeToOutputBuffer(true); + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiConnection.java @@ -33,505 +33,505 @@ import java.util.*; */ public class MapiConnection extends MonetConnection { - /** the PROMPT ASCII char sent by the server */ - static final char PROMPT_CHAR = '.'; - /** the default number of rows that are (attempted to) read at once */ - private static final int DEF_FETCHSIZE = 250; + /** the PROMPT ASCII char sent by the server */ + static final char PROMPT_CHAR = '.'; + /** the default number of rows that are (attempted to) read at once */ + private static final int DEF_FETCHSIZE = 250; - /** The hostname to connect to */ - private final String hostname; - /** The port to connect on the host to */ - private final int port; - /** The database to connect to */ - private String database; - /** The TCP Socket timeout in milliseconds. Default is 0 meaning the timeout is disabled (i.e., timeout of infinity) */ - private int soTimeout = 0; - /** Whether we should follow redirects */ - private boolean followRedirects = true; - /** How many redirections do we follow until we're fed up with it? */ - private int ttl = 10; - /** Protocol version of the connection */ - private int version; - /** Endianness of the server */ - private ByteOrder serverEndianness; + /** The hostname to connect to */ + private final String hostname; + /** The port to connect on the host to */ + private final int port; + /** The database to connect to */ + private String database; + /** The TCP Socket timeout in milliseconds. Default is 0 meaning the timeout is disabled (i.e., timeout of infinity) */ + private int soTimeout = 0; + /** Whether we should follow redirects */ + private boolean followRedirects = true; + /** How many redirections do we follow until we're fed up with it? */ + private int ttl = 10; + /** Protocol version of the connection */ + private int version; + /** Endianness of the server */ + private ByteOrder serverEndianness; - public MapiConnection(Properties props, String hash, String language, boolean blobIsBinary, boolean clobIsLongChar, - String hostname, int port, String database) throws IOException { - super(props, hash, MapiLanguage.getLanguageFromString(language), blobIsBinary, clobIsLongChar); - this.hostname = hostname; - this.port = port; - this.database = database; - } + public MapiConnection(Properties props, String hash, String language, boolean blobIsBinary, boolean clobIsLongChar, + String hostname, int port, String database) throws IOException { + super(props, hash, MapiLanguage.getLanguageFromString(language), blobIsBinary, clobIsLongChar); + this.hostname = hostname; + this.port = port; + this.database = database; + } - /** - * Gets the hostname of the server used on this connection. - * - * @return The hostname of the server used on this connection - */ - public String getHostname() { - return hostname; - } + /** + * Gets the hostname of the server used on this connection. + * + * @return The hostname of the server used on this connection + */ + public String getHostname() { + return hostname; + } - /** - * Gets the port of the server used on this connection. - * - * @return The port of the server used on this connection - */ - public int getPort() { - return port; - } + /** + * Gets the port of the server used on this connection. + * + * @return The port of the server used on this connection + */ + public int getPort() { + return port; + } - /** - * Gets the database to connect to. If database is null, a connection is made to the default database of the server. - * This is also the default. - * - * @return The database name - */ - public String getDatabase() { - return database; - } + /** + * Gets the database to connect to. If database is null, a connection is made to the default database of the server. + * This is also the default. + * + * @return The database name + */ + public String getDatabase() { + return database; + } - /** - * Gets the SO_TIMEOUT from the underlying Socket. - * - * @return The currently in use timeout in milliseconds - */ - @Override - public int getSoTimeout() { - try { - if(protocol != null) { - this.soTimeout = ((OldMapiProtocol)protocol).getSocket().getSoTimeout(); - } - return this.soTimeout; - } catch (SocketException e) { - this.addWarning("The socket timeout could not be get", "M1M05"); - } - return -1; - } + /** + * Gets the SO_TIMEOUT from the underlying Socket. + * + * @return The currently in use timeout in milliseconds + */ + @Override + public int getSoTimeout() { + try { + if(protocol != null) { + this.soTimeout = ((OldMapiProtocol)protocol).getSocket().getSoTimeout(); + } + return this.soTimeout; + } catch (SocketException e) { + this.addWarning("The socket timeout could not be get", "M1M05"); + } + return -1; + } - /** - * 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. - * - * @param timeout The specified timeout, in milliseconds. A timeout of zero is interpreted as an infinite timeout - */ - @Override - public void setSoTimeout(int timeout) { - if (timeout < 0) { - throw new IllegalArgumentException("Timeout can't be negative"); - } - try { - if(protocol != null) { - ((OldMapiProtocol)protocol).getSocket().setSoTimeout(timeout); - } - this.soTimeout = timeout; - } catch (SocketException e) { - this.addWarning("The socket timeout could not be set", "M1M05"); - } - } + /** + * 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. + * + * @param timeout The specified timeout, in milliseconds. A timeout of zero is interpreted as an infinite timeout + */ + @Override + public void setSoTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("Timeout can't be negative"); + } + try { + if(protocol != null) { + ((OldMapiProtocol)protocol).getSocket().setSoTimeout(timeout); + } + this.soTimeout = timeout; + } catch (SocketException e) { + this.addWarning("The socket timeout could not be set", "M1M05"); + } + } - /** - * Gets whether MCL redirections should be followed or not. If set to false, an MCLException will be thrown when a - * redirect is encountered during connect. The default behaviour is to automatically follow redirects. - * - * @return Whether to follow redirects (true) or not (false) - */ - public boolean isFollowRedirects() { - return followRedirects; - } + /** + * Gets whether MCL redirections should be followed or not. If set to false, an MCLException will be thrown when a + * redirect is encountered during connect. The default behaviour is to automatically follow redirects. + * + * @return Whether to follow redirects (true) or not (false) + */ + public boolean isFollowRedirects() { + return followRedirects; + } - /** - * Gets the number of redirects that are followed when followRedirects is true. In order to avoid going into an - * endless loop due to some evil server, or another error, a maximum number of redirects that may be followed can be - * set here. Note that to disable the following of redirects you should use setFollowRedirects. - * - * @see #isFollowRedirects() - * @return The number of redirects before an exception is thrown - */ - public int getTtl() { - return ttl; - } + /** + * Gets the number of redirects that are followed when followRedirects is true. In order to avoid going into an + * endless loop due to some evil server, or another error, a maximum number of redirects that may be followed can be + * set here. Note that to disable the following of redirects you should use setFollowRedirects. + * + * @see #isFollowRedirects() + * @return The number of redirects before an exception is thrown + */ + public int getTtl() { + return ttl; + } - /** - * Gets the mapi protocol version used by this socket. The protocol version depends on the server being used. - * - * @return The mapi protocol version used by this socket - */ - public int getVersion() { - return version; - } + /** + * Gets the mapi protocol version used by this socket. The protocol version depends on the server being used. + * + * @return The mapi protocol version used by this socket + */ + public int getVersion() { + return version; + } - /** - * Gets the connection server endianness. - * - * @return The connection server endianness - */ - public ByteOrder getServerEndianness() { - return serverEndianness; - } + /** + * Gets the connection server endianness. + * + * @return The connection server endianness + */ + public ByteOrder getServerEndianness() { + return serverEndianness; + } - /** - * On a MAPI connection, the block size will be the block size of the connection. - * - * @return The block size length - */ - @Override - public int getBlockSize() { - return ((OldMapiProtocol)protocol).getSocket().getBlockSize(); - } + /** + * On a MAPI connection, the block size will be the block size of the connection. + * + * @return The block size length + */ + @Override + public int getBlockSize() { + return ((OldMapiProtocol)protocol).getSocket().getBlockSize(); + } - /** - * On a MAPI connection the default fetch size per DataBlock is 250 rows. - * - * @return The default fetch size - */ - @Override - public int getDefFetchsize() { - return DEF_FETCHSIZE; - } + /** + * On a MAPI connection the default fetch size per DataBlock is 250 rows. + * + * @return The default fetch size + */ + @Override + public int getDefFetchsize() { + return DEF_FETCHSIZE; + } - @Override - public int initialStringBuilderSize() { - return this.getBlockSize(); - } + @Override + public int initialStringBuilderSize() { + return this.getBlockSize(); + } - /** - * Closes the underlying connection implementation. On a MAPI connection, the underlying socket is closed. - * - * @throws IOException if an I/O error occurs while closing the connection - */ - @Override - public synchronized void closeUnderlyingConnection() throws IOException { - ((OldMapiProtocol)protocol).getSocket().close(); - } + /** + * Closes the underlying connection implementation. On a MAPI connection, the underlying socket is closed. + * + * @throws IOException if an I/O error occurs while closing the connection + */ + @Override + public synchronized void closeUnderlyingConnection() throws IOException { + ((OldMapiProtocol)protocol).getSocket().close(); + } - /** - * Gets the underlying connection JDBC String URL. - * - * @return The underlying connection JDBC String URL - */ - @Override - public String getJDBCURL() { - String res = "jdbc:monetdb://" + this.hostname + ":" + this.port + "/" + this.database; - if (this.getLanguage() == MapiLanguage.LANG_MAL) - res += "?language=mal"; - return res; - } + /** + * Gets the underlying connection JDBC String URL. + * + * @return The underlying connection JDBC String URL + */ + @Override + public String getJDBCURL() { + String res = "jdbc:monetdb://" + this.hostname + ":" + this.port + "/" + this.database; + if (this.getLanguage() == MapiLanguage.LANG_MAL) + res += "?language=mal"; + return res; + } - /** - * Sends a control command to the server. On a MAPI connection, regular MonetDB commands are sent to the server. - * - * @param commandID the command identifier according to {@link ControlCommands} listing - * @param data The integer to send according to the control command - * @throws SQLException if an IO exception or a database error occurs - */ - @Override - public void sendControlCommand(int commandID, int data) throws SQLException { - String command = null; - switch (commandID) { - case ControlCommands.AUTO_COMMIT: - command = "auto_commit " + ((data == 1) ? "1" : "0"); - break; - case ControlCommands.REPLY_SIZE: - command = "reply_size " + data; - break; - case ControlCommands.RELEASE: - command = "release " + data; - break; - case ControlCommands.CLOSE: - command = "close " + data; - } - try { - protocol.writeNextQuery(language.getCommandTemplateIndex(0), command, - language.getCommandTemplateIndex(1)); - protocol.waitUntilPrompt(); - int csrh = protocol.getCurrentServerResponse(); - if (csrh == ServerResponses.ERROR) { - String error = protocol.getRemainingStringLine(0); - throw new SQLException(error.substring(6), error.substring(0, 5)); - } - } catch (SocketTimeoutException e) { - close(); // JDBC 4.1 semantics, abort() - throw new SQLException("connection timed out", "08M33"); - } catch (IOException e) { - throw new SQLException(e.getMessage(), "08000"); - } - } + /** + * Sends a control command to the server. On a MAPI connection, regular MonetDB commands are sent to the server. + * + * @param commandID the command identifier according to {@link ControlCommands} listing + * @param data The integer to send according to the control command + * @throws SQLException if an IO exception or a database error occurs + */ + @Override + public void sendControlCommand(int commandID, int data) throws SQLException { + String command = null; + switch (commandID) { + case ControlCommands.AUTO_COMMIT: + command = "auto_commit " + ((data == 1) ? "1" : "0"); + break; + case ControlCommands.REPLY_SIZE: + command = "reply_size " + data; + break; + case ControlCommands.RELEASE: + command = "release " + data; + break; + case ControlCommands.CLOSE: + command = "close " + data; + } + try { + protocol.writeNextQuery(language.getCommandTemplateIndex(0), command, + language.getCommandTemplateIndex(1)); + protocol.waitUntilPrompt(); + int csrh = protocol.getCurrentServerResponse(); + if (csrh == ServerResponses.ERROR) { + String error = protocol.getRemainingStringLine(0); + throw new SQLException(error.substring(6), error.substring(0, 5)); + } + } catch (SocketTimeoutException e) { + close(); // JDBC 4.1 semantics, abort() + throw new SQLException("connection timed out", "08M33"); + } catch (IOException e) { + throw new SQLException(e.getMessage(), "08000"); + } + } - /** - * Connects to the given host and port, logging in as the given user. If followRedirect is false, a - * RedirectionException is thrown when a redirect is encountered. - * - * @param user The user name to authenticate - * @param pass The user's password - * @return A List with informational (warning) messages. If this list is empty; then there are no warnings. - * @throws IOException if an I/O error occurs when creating the socket - * @throws ProtocolException if bogus data is received - * @throws MCLException if an MCL related error occurs - */ - @Override - public List<String> connect(String user, String pass) throws IOException, ProtocolException, MCLException { - // Wrap around the internal connect that needs to know if it should really make a TCP connection or not. - List<String> res = connect(this.hostname, this.port, user, pass, true); - // apply NetworkTimeout value from legacy (pre 4.1) driver so_timeout calls - this.setSoTimeout(this.getSoTimeout()); - return res; - } + /** + * Connects to the given host and port, logging in as the given user. If followRedirect is false, a + * RedirectionException is thrown when a redirect is encountered. + * + * @param user The user name to authenticate + * @param pass The user's password + * @return A List with informational (warning) messages. If this list is empty; then there are no warnings. + * @throws IOException if an I/O error occurs when creating the socket + * @throws ProtocolException if bogus data is received + * @throws MCLException if an MCL related error occurs + */ + @Override + public List<String> connect(String user, String pass) throws IOException, ProtocolException, MCLException { + // Wrap around the internal connect that needs to know if it should really make a TCP connection or not. + List<String> res = connect(this.hostname, this.port, user, pass, true); + // apply NetworkTimeout value from legacy (pre 4.1) driver so_timeout calls + this.setSoTimeout(this.getSoTimeout()); + return res; + } - private List<String> connect(String host, int port, String user, String pass, boolean makeConnection) - throws IOException, ProtocolException, MCLException { - if (ttl-- <= 0) - throw new MCLException("Maximum number of redirects reached, aborting connection attempt. Sorry."); + private List<String> connect(String host, int port, String user, String pass, boolean makeConnection) + throws IOException, ProtocolException, MCLException { + if (ttl-- <= 0) + throw new MCLException("Maximum number of redirects reached, aborting connection attempt. Sorry."); - if (makeConnection) { - this.protocol = new OldMapiProtocol(new OldMapiSocket(this.hostname, this.port, this)); - //set nodelay, as it greatly speeds up small messages (like we often do) - ((OldMapiProtocol)this.protocol).getSocket().setTcpNoDelay(true); - ((OldMapiProtocol)this.protocol).getSocket().setSoTimeout(this.soTimeout); - } + if (makeConnection) { + this.protocol = new OldMapiProtocol(new OldMapiSocket(this.hostname, this.port, this)); + //set nodelay, as it greatly speeds up small messages (like we often do) + ((OldMapiProtocol)this.protocol).getSocket().setTcpNoDelay(true); + ((OldMapiProtocol)this.protocol).getSocket().setSoTimeout(this.soTimeout); + } - this.protocol.fetchNextResponseData(); - String nextLine = this.protocol.getRemainingStringLine(0); - this.protocol.waitUntilPrompt(); - String test = this.getChallengeResponse(nextLine, user, pass, this.language.getRepresentation(), - this.database, this.hash); - this.protocol.writeNextQuery("", test, ""); + this.protocol.fetchNextResponseData(); + String nextLine = this.protocol.getRemainingStringLine(0); + this.protocol.waitUntilPrompt(); + String test = this.getChallengeResponse(nextLine, user, pass, this.language.getRepresentation(), + this.database, this.hash); + this.protocol.writeNextQuery("", test, ""); - List<String> redirects = new ArrayList<>(); - List<String> warns = new ArrayList<>(); - String err = ""; - int next; + List<String> redirects = new ArrayList<>(); + List<String> warns = new ArrayList<>(); + String err = ""; + int next; - do { - this.protocol.fetchNextResponseData(); - next = this.protocol.getCurrentServerResponse(); - switch (next) { - case ServerResponses.ERROR: - err += "\n" + this.protocol.getRemainingStringLine(7); - break; - case ServerResponses.INFO: - warns.add(this.protocol.getRemainingStringLine(1)); - break; - case ServerResponses.REDIRECT: - redirects.add(this.protocol.getRemainingStringLine(1)); - } - } while (next != ServerResponses.PROMPT); + do { + this.protocol.fetchNextResponseData(); + next = this.protocol.getCurrentServerResponse(); + switch (next) { + case ServerResponses.ERROR: + err += "\n" + this.protocol.getRemainingStringLine(7); + break; + case ServerResponses.INFO: + warns.add(this.protocol.getRemainingStringLine(1)); + break; + case ServerResponses.REDIRECT: + redirects.add(this.protocol.getRemainingStringLine(1)); + } + } while (next != ServerResponses.PROMPT); - if (!err.equals("")) { - this.close(); - throw new MCLException(err.trim()); - } - if (!redirects.isEmpty()) { - if (followRedirects) { - // Ok, server wants us to go somewhere else. The list might have multiple clues on where to go. For now - // we don't support anything intelligent but trying the first one. URI should be in form of: - // "mapi:monetdb://host:port/database?arg=value&..." or "mapi:merovingian://proxy?arg=value&..." note - // that the extra arguments must be obeyed in both cases - String suri = redirects.get(0); - if (!suri.startsWith("mapi:")) - throw new MCLException("unsupported redirect: " + suri); + if (!err.equals("")) { + this.close(); + throw new MCLException(err.trim()); + } + if (!redirects.isEmpty()) { + if (followRedirects) { + // Ok, server wants us to go somewhere else. The list might have multiple clues on where to go. For now + // we don't support anything intelligent but trying the first one. URI should be in form of: + // "mapi:monetdb://host:port/database?arg=value&..." or "mapi:merovingian://proxy?arg=value&..." note + // that the extra arguments must be obeyed in both cases + String suri = redirects.get(0); + if (!suri.startsWith("mapi:")) + throw new MCLException("unsupported redirect: " + suri); - URI u; - try { - u = new URI(suri.substring(5)); - } catch (URISyntaxException e) { - throw new ProtocolException(e.toString()); - } - String tmp = u.getQuery(); - if (tmp != null) { - String args[] = tmp.split("&"); - for (String arg : args) { - int pos = arg.indexOf("="); - if (pos > 0) { - tmp = arg.substring(0, pos); - switch (tmp) { - case "database": - tmp = arg.substring(pos + 1); - if (!tmp.equals(database)) { - warns.add("redirect points to different " + "database: " + tmp); - this.database = tmp; - } - break; - case "language": - tmp = arg.substring(pos + 1); - warns.add("redirect specifies use of different language: " + tmp); - this.language = MapiLanguage.getLanguageFromString(tmp); - break; - case "user": - tmp = arg.substring(pos + 1); - if (!tmp.equals(user)) - warns.add("ignoring different username '" + tmp + "' set by " + - "redirect, what are the security implications?"); - break; - case "password": - warns.add("ignoring different password set by redirect, " + - "what are the security implications?"); - break; - default: - warns.add("ignoring unknown argument '" + tmp + "' from redirect"); - break; - } - } else { - warns.add("ignoring illegal argument from redirect: " + arg); - } - } - } + URI u; + try { + u = new URI(suri.substring(5)); + } catch (URISyntaxException e) { + throw new ProtocolException(e.toString()); + } + String tmp = u.getQuery(); + if (tmp != null) { + String args[] = tmp.split("&"); + for (String arg : args) { + int pos = arg.indexOf("="); + if (pos > 0) { + tmp = arg.substring(0, pos); + switch (tmp) { + case "database": + tmp = arg.substring(pos + 1); + if (!tmp.equals(database)) { + warns.add("redirect points to different " + "database: " + tmp); + this.database = tmp; + } + break; + case "language": + tmp = arg.substring(pos + 1); + warns.add("redirect specifies use of different language: " + tmp); + this.language = MapiLanguage.getLanguageFromString(tmp); + break; + case "user": + tmp = arg.substring(pos + 1); + if (!tmp.equals(user)) + warns.add("ignoring different username '" + tmp + "' set by " + + "redirect, what are the security implications?"); + break; + case "password": + warns.add("ignoring different password set by redirect, " + + "what are the security implications?"); + break; + default: + warns.add("ignoring unknown argument '" + tmp + "' from redirect"); + break; + } + } else { + warns.add("ignoring illegal argument from redirect: " + arg); + } + } + } - switch (u.getScheme()) { - case "monetdb": - tmp = u.getPath(); - if (tmp != null && tmp.length() != 0) { - tmp = tmp.substring(1).trim(); - if (!tmp.isEmpty() && !tmp.equals(database)) { - warns.add("redirect points to different " + "database: " + tmp); - this.database = tmp; - } - } - int p = u.getPort(); - warns.addAll(connect(u.getHost(), p == -1 ? port : p, user, pass, true)); - warns.add("Redirect by " + host + ":" + port + " to " + suri); - break; - case "merovingian": - // reuse this connection to inline connect to the right database that Merovingian proxies for us - warns.addAll(connect(host, port, user, pass, false)); - break; - default: - throw new MCLException("unsupported scheme in redirect: " + suri); - } - } else { - StringBuilder msg = new StringBuilder("The server sent a redirect for this connection:"); - for (String it : redirects) { - msg.append(" [").append(it).append("]"); - } - throw new MCLException(msg.toString()); - } - } - return warns; - } + switch (u.getScheme()) { + case "monetdb": + tmp = u.getPath(); + if (tmp != null && tmp.length() != 0) { + tmp = tmp.substring(1).trim(); + if (!tmp.isEmpty() && !tmp.equals(database)) { + warns.add("redirect points to different " + "database: " + tmp); + this.database = tmp; + } + } + int p = u.getPort(); + warns.addAll(connect(u.getHost(), p == -1 ? port : p, user, pass, true)); + warns.add("Redirect by " + host + ":" + port + " to " + suri); + break; + case "merovingian": + // reuse this connection to inline connect to the right database that Merovingian proxies for us + warns.addAll(connect(host, port, user, pass, false)); + break; + default: + throw new MCLException("unsupported scheme in redirect: " + suri); + } + } else { + StringBuilder msg = new StringBuilder("The server sent a redirect for this connection:"); + for (String it : redirects) { + msg.append(" [").append(it).append("]"); + } + throw new MCLException(msg.toString()); + } + } + return warns; + } - /** - * A little helper function that processes a challenge string, and returns a response string for the server. - * If the challenge string is null, a challengeless response is returned. - * - * @param chalstr the challenge string - * @param username the username to use - * @param password the password to use - * @param language the language to use - * @param database the database to connect to - * @param hash the hash method(s) to use, or NULL for all supported hashes - */ - private String getChallengeResponse(String chalstr, String username, String password, String language, - String database, String hash) throws ProtocolException, MCLException, - IOException { - String response; - String algo; + /** + * A little helper function that processes a challenge string, and returns a response string for the server. + * If the challenge string is null, a challengeless response is returned. + * + * @param chalstr the challenge string + * @param username the username to use + * @param password the password to use + * @param language the language to use + * @param database the database to connect to + * @param hash the hash method(s) to use, or NULL for all supported hashes + */ + private String getChallengeResponse(String chalstr, String username, String password, String language, + String database, String hash) throws ProtocolException, MCLException, + IOException { + String response; + String algo; - // parse the challenge string, split it on ':' - String[] chaltok = chalstr.split(":"); - if (chaltok.length <= 4) - throw new ProtocolException("Server challenge string unusable! Challenge contains too few tokens: " - + chalstr); + // parse the challenge string, split it on ':' + String[] chaltok = chalstr.split(":"); + if (chaltok.length <= 4) + throw new ProtocolException("Server challenge string unusable! Challenge contains too few tokens: " + + chalstr); - // challenge string to use as salt/key - String challenge = chaltok[0]; - String servert = chaltok[1]; - try { - this.version = Integer.parseInt(chaltok[2].trim()); // protocol version - } catch (NumberFormatException e) { - throw new ProtocolException("Protocol version unparseable: " + chaltok[2]); - } + // challenge string to use as salt/key + String challenge = chaltok[0]; + String servert = chaltok[1]; + try { + this.version = Integer.parseInt(chaltok[2].trim()); // protocol version + } catch (NumberFormatException e) { + throw new ProtocolException("Protocol version unparseable: " + chaltok[2]); + } - switch (chaltok[4]) { - case "BIG": - this.serverEndianness = ByteOrder.BIG_ENDIAN; - break; - case "LIT": - this.serverEndianness = ByteOrder.LITTLE_ENDIAN; - break; - default: - throw new ProtocolException("Invalid byte-order: " + chaltok[4]); - } - ((OldMapiProtocol)protocol).getSocket().setSocketChannelEndianness(this.serverEndianness); + switch (chaltok[4]) { + case "BIG": + this.serverEndianness = ByteOrder.BIG_ENDIAN; + break; + case "LIT": + this.serverEndianness = ByteOrder.LITTLE_ENDIAN; + break; + default: + throw new ProtocolException("Invalid byte-order: " + chaltok[4]); + } + ((OldMapiProtocol)protocol).getSocket().setSocketChannelEndianness(this.serverEndianness); - // handle the challenge according to the version it is - switch (this.version) { - case 9: - // proto 9 is like 8, but uses a hash instead of the plain password, the server tells us which hash in - // the challenge after the byte-order + // handle the challenge according to the version it is + switch (this.version) { + case 9: + // proto 9 is like 8, but uses a hash instead of the plain password, the server tells us which hash in + // the challenge after the byte-order /* NOTE: Java doesn't support RIPEMD160 :( */ - switch (chaltok[5]) { - case "SHA512": - algo = "SHA-512"; - break; - case "SHA384": - algo = "SHA-384"; - break; - case "SHA256": - algo = "SHA-256"; + switch (chaltok[5]) { + case "SHA512": + algo = "SHA-512"; + break; + case "SHA384": + algo = "SHA-384"; + break; + case "SHA256": + algo = "SHA-256"; /* NOTE: Java supports SHA-224 only on 8 */ - break; - case "SHA1": - algo = "SHA-1"; - break; - case "MD5": - algo = "MD5"; - break; - default: - throw new MCLException("Unsupported password hash: " + chaltok[5]); - } + break; + case "SHA1": + algo = "SHA-1"; + break; + case "MD5": + algo = "MD5"; + break; + default: + throw new MCLException("Unsupported password hash: " + chaltok[5]); + } - password = ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8")); + password = ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8")); - // proto 7 (finally) used the challenge and works with a password hash. The supported implementations - // come from the server challenge. We chose the best hash we can find, in the order SHA1, MD5, plain. - // Also, the byte-order is reported in the challenge string. proto 8 made this obsolete, but retained - // the byte-order report for future "binary" transports. In proto 8, the byte-order of the blocks is - // always little endian because most machines today are. - String hashes = (hash == null ? chaltok[3] : hash); - Set<String> hashesSet = new HashSet<>(Arrays.asList(hashes.toUpperCase().split("[, ]"))); + // proto 7 (finally) used the challenge and works with a password hash. The supported implementations + // come from the server challenge. We chose the best hash we can find, in the order SHA1, MD5, plain. + // Also, the byte-order is reported in the challenge string. proto 8 made this obsolete, but retained + // the byte-order report for future "binary" transports. In proto 8, the byte-order of the blocks is + // always little endian because most machines today are. + String hashes = (hash == null ? chaltok[3] : hash); + Set<String> hashesSet = new HashSet<>(Arrays.asList(hashes.toUpperCase().split("[, ]"))); - // if we deal with merovingian, mask our credentials - if (servert.equals("merovingian") && !language.equals("control")) { - username = "merovingian"; - password = "merovingian"; - } - String pwhash; + // if we deal with merovingian, mask our credentials + if (servert.equals("merovingian") && !language.equals("control")) { + username = "merovingian"; + password = "merovingian"; + } + String pwhash; - if (hashesSet.contains("SHA512")) { - algo = "SHA-512"; - pwhash = "{SHA512}"; - } else if (hashesSet.contains("SHA384")) { - algo = "SHA-384"; - pwhash = "{SHA384}"; - } else if (hashesSet.contains("SHA256")) { - algo = "SHA-256"; - pwhash = "{SHA256}"; - } else if (hashesSet.contains("SHA1")) { - algo = "SHA-1"; - pwhash = "{SHA1}"; - } else if (hashesSet.contains("MD5")) { - algo = "MD5"; - pwhash = "{MD5}"; - } else { - throw new MCLException("No supported password hashes in " + hashes); - } + if (hashesSet.contains("SHA512")) { + algo = "SHA-512"; + pwhash = "{SHA512}"; + } else if (hashesSet.contains("SHA384")) { + algo = "SHA-384"; + pwhash = "{SHA384}"; + } else if (hashesSet.contains("SHA256")) { + algo = "SHA-256"; + pwhash = "{SHA256}"; + } else if (hashesSet.contains("SHA1")) { + algo = "SHA-1"; + pwhash = "{SHA1}"; + } else if (hashesSet.contains("MD5")) { + algo = "MD5"; + pwhash = "{MD5}"; + } else { + throw new MCLException("No supported password hashes in " + hashes); + } - pwhash += ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8"), - challenge.getBytes("UTF-8")); + pwhash += ChannelSecurity.digestStrings(algo, password.getBytes("UTF-8"), + challenge.getBytes("UTF-8")); - // generate response - response = "BIG:"; // JVM byte-order is big-endian - response += username + ":" + pwhash + ":" + language; - response += ":" + (database == null ? "" : database) + ":"; + // generate response + response = "BIG:"; // JVM byte-order is big-endian + response += username + ":" + pwhash + ":" + language; + response += ":" + (database == null ? "" : database) + ":"; - this.conn_props.setProperty("hash", hashes); - this.conn_props.setProperty("language", language); - this.conn_props.setProperty("database", database); + this.conn_props.setProperty("hash", hashes); + this.conn_props.setProperty("language", language); + this.conn_props.setProperty("database", database); - return response; - default: - throw new MCLException("Unsupported protocol version: " + version); - } - } + return response; + default: + throw new MCLException("Unsupported protocol version: " + version); + } + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiLanguage.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/MapiLanguage.java @@ -17,58 +17,58 @@ import nl.cwi.monetdb.mcl.connection.IMo */ public enum MapiLanguage implements IMonetDBLanguage { - /** the SQL language */ - LANG_SQL(new String[]{"s", "\n;", "\n;\n"}, new String[]{"X", null, "\nX"}, "sql"), - /** the MAL language (officially *NOT* supported) */ - LANG_MAL(new String[]{null, ";\n", ";\n"}, new String[]{null, null, null}, "mal"), - /** an unknown language */ - LANG_UNKNOWN(null, null, "unknown"); + /** the SQL language */ + LANG_SQL(new String[]{"s", "\n;", "\n;\n"}, new String[]{"X", null, "\nX"}, "sql"), + /** the MAL language (officially *NOT* supported) */ + LANG_MAL(new String[]{null, ";\n", ";\n"}, new String[]{null, null, null}, "mal"), + /** an unknown language */ + LANG_UNKNOWN(null, null, "unknown"); - MapiLanguage(String[] queryTemplates, String[] commandTemplates, String representation) { - this.queryTemplates = queryTemplates; - this.commandTemplates = commandTemplates; - this.representation = representation; - } + MapiLanguage(String[] queryTemplates, String[] commandTemplates, String representation) { + this.queryTemplates = queryTemplates; + this.commandTemplates = commandTemplates; + this.representation = representation; + } - private final String[] queryTemplates; + private final String[] queryTemplates; - private final String[] commandTemplates; + private final String[] commandTemplates; - private final String representation; + private final String representation; - @Override - public String getQueryTemplateIndex(int index) { - return queryTemplates[index]; - } + @Override + public String getQueryTemplateIndex(int index) { + return queryTemplates[index]; + } - @Override - public String getCommandTemplateIndex(int index) { - return commandTemplates[index]; - } + @Override + public String getCommandTemplateIndex(int index) { + return commandTemplates[index]; + } - @Override - public String[] getQueryTemplates() { - return queryTemplates; - } + @Override + public String[] getQueryTemplates() { + return queryTemplates; + } - @Override - public String[] getCommandTemplates() { - return commandTemplates; - } + @Override + public String[] getCommandTemplates() { + return commandTemplates; + } - @Override - public String getRepresentation() { - return representation; - } + @Override + public String getRepresentation() { + return representation; + } - public static MapiLanguage getLanguageFromString(String language) { - switch (language) { - case "sql": - return LANG_SQL; - case "mal": - return LANG_MAL; - default: - return LANG_UNKNOWN; - } - } + public static MapiLanguage getLanguageFromString(String language) { + switch (language) { + case "sql": + return LANG_SQL; + case "mal": + return LANG_MAL; + default: + return LANG_UNKNOWN; + } + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java +++ b/src/main/java/nl/cwi/monetdb/mcl/connection/mapi/OldMapiSocket.java @@ -36,327 +36,327 @@ import java.nio.ByteBuffer; */ public class OldMapiSocket extends AbstractSocket { - /** The full blocksize to use in the upper layer buffers */ - public final static int FULL_BLOCK = 8 * 1024; + /** The full blocksize to use in the upper layer buffers */ + public final static int FULL_BLOCK = 8 * 1024; - /** The blocksize (hardcoded in compliance with stream.mx) */ - private static final int BLOCK = FULL_BLOCK - 2; + /** The blocksize (hardcoded in compliance with stream.mx) */ + private static final int BLOCK = FULL_BLOCK - 2; - /** - * A short in two bytes for holding the block size in bytes. - */ - private final byte[] blklen = new byte[2]; + /** + * A short in two bytes for holding the block size in bytes. + */ + private final byte[] blklen = new byte[2]; - /** - * The socket input stream read by blocks. - */ - private final OldMapiBlockInputStream inStream; + /** + * The socket input stream read by blocks. + */ + private final OldMapiBlockInputStream inStream; - /** - * The socket output stream written by blocks. - */ - private final OldMapiBlockOutputStream outStream; + /** + * The socket output stream written by blocks. + */ + private final OldMapiBlockOutputStream outStream; - OldMapiSocket(String hostname, int port, MapiConnection connection) throws IOException { - super(hostname, port, connection); - this.inStream = new OldMapiBlockInputStream(socket.getInputStream()); - this.outStream = new OldMapiBlockOutputStream(socket.getOutputStream()); - } + OldMapiSocket(String hostname, int port, MapiConnection connection) throws IOException { + super(hostname, port, connection); + this.inStream = new OldMapiBlockInputStream(socket.getInputStream()); + this.outStream = new OldMapiBlockOutputStream(socket.getOutputStream()); + } - /** - * The block size to be used in the upper layer buffers - */ - @Override - public int getFullBlockSize() { - return FULL_BLOCK; - } + /** + * The block size to be used in the upper layer buffers + */ + @Override + public int getFullBlockSize() { + return FULL_BLOCK; + } - /** - * The block size will be the one hardcoded on the connection. - */ - @Override - public int getBlockSize() { - return BLOCK; - } + /** + * The block size will be the one hardcoded on the connection. + */ + @Override + public int getBlockSize() { + return BLOCK; + } - @Override - int readToBufferIn(ByteBuffer bufferIn) throws IOException { - return this.inStream.read(bufferIn); - } + @Override + int readToBufferIn(ByteBuffer bufferIn) throws IOException { + return this.inStream.read(bufferIn); + } - @Override - int writeFromBufferOut(ByteBuffer bufferOut) throws IOException { - return this.outStream.write(bufferOut); - } + @Override + int writeFromBufferOut(ByteBuffer bufferOut) throws IOException { + return this.outStream.write(bufferOut); + } - @Override - void flush() throws IOException { - this.outStream.flush(); - } + @Override + void flush() throws IOException { + this.outStream.flush(); + } - @Override - public void close() throws IOException { - this.socket.close(); - } + @Override + public void close() throws IOException { + this.socket.close(); + } - /** - * Inner class that is used to make the data on the blocked stream available as a normal stream. - */ - private class OldMapiBlockInputStream { + /** + * Inner class that is used to make the data on the blocked stream available as a normal stream. + */ + private class OldMapiBlockInputStream { - private final InputStream inStream; + private final InputStream inStream; - private int readPos = 0; + private int readPos = 0; - private int blockLen = 0; + private int blockLen = 0; - private final byte[] block = new byte[BLOCK + 3]; //\n.\n + private final byte[] block = new byte[BLOCK + 3]; //\n.\n - /** - * Constructs this BlockInputStream, backed by the given InputStream. A BufferedInputStream is internally used. - */ - OldMapiBlockInputStream(InputStream in) { - this.inStream = in; - } + /** + * Constructs this BlockInputStream, backed by the given InputStream. A BufferedInputStream is internally used. + */ + OldMapiBlockInputStream(InputStream in) { + this.inStream = in; + } - public int available() { - return blockLen - readPos; - } + public int available() { + return blockLen - readPos; + } - /** - * Small wrapper to get a blocking variant of the read() method on the BufferedInputStream. We want to benefit - * from the Buffered pre-fetching, but not dealing with half blocks. Changing this class to be able to use the - * partially received data will greatly complicate matters, while a performance improvement is debatable given - * the relatively small size of our blocks. Maybe it does speed up on slower links, then consider this method a - * quick bug fix/workaround. - * - * @return false if reading the block failed due to EOF - */ - private boolean _read(byte[] b, int len) throws IOException { - int s; - int off = 0; + /** + * Small wrapper to get a blocking variant of the read() method on the BufferedInputStream. We want to benefit + * from the Buffered pre-fetching, but not dealing with half blocks. Changing this class to be able to use the + * partially received data will greatly complicate matters, while a performance improvement is debatable given + * the relatively small size of our blocks. Maybe it does speed up on slower links, then consider this method a + * quick bug fix/workaround. + * + * @return false if reading the block failed due to EOF + */ + private boolean _read(byte[] b, int len) throws IOException { + int s; + int off = 0; - while (len > 0) { - s = inStream.read(b, off, len); - if (s == -1) { - // if we have read something before, we should have been able to read the whole, so make this fatal - if (off > 0) { - throw new IOException("Read from " + connection.getHostname() + ":" + - connection.getPort() + ": Incomplete block read from stream"); - } - return false; - } - len -= s; - off += s; - } - return true; - } + while (len > 0) { + s = inStream.read(b, off, len); + if (s == -1) { + // if we have read something before, we should have been able to read the whole, so make this fatal + if (off > 0) { + throw new IOException("Read from " + connection.getHostname() + ":" + + connection.getPort() + ": Incomplete block read from stream"); + } + return false; + } + len -= s; + off += s; + } + return true; + } - /** - * Reads the next block on the stream into the internal buffer, or writes the prompt in the buffer. - * <p> - * The blocked stream protocol consists of first a two byte integer indicating the length of the block, then the - * block, followed by another length + block. The end of such sequence is put in the last bit of the length, and - * hence this length should be shifted to the right to obtain the real length value first. We simply fetch - * blocks here as soon as they are needed for the stream's read methods. - * <p> - * The user-flush, which is an implicit effect of the end of a block sequence, is communicated beyond the stream - * by inserting a prompt sequence on the stream after the last block. This method makes sure that a final block - * ends with a newline, if it doesn't already, in order to facilitate a Reader that is possibly chained to this - * InputStream. - * <p> - * If the stream is not positioned correctly, hell will break loose. - */ - private int readBlock() throws IOException { - // read next two bytes (short) - if (!_read(blklen, 2)) - return -1; + /** + * Reads the next block on the stream into the internal buffer, or writes the prompt in the buffer. + * <p> + * The blocked stream protocol consists of first a two byte integer indicating the length of the block, then the + * block, followed by another length + block. The end of such sequence is put in the last bit of the length, and + * hence this length should be shifted to the right to obtain the real length value first. We simply fetch + * blocks here as soon as they are needed for the stream's read methods. + * <p> + * The user-flush, which is an implicit effect of the end of a block sequence, is communicated beyond the stream + * by inserting a prompt sequence on the stream after the last block. This method makes sure that a final block + * ends with a newline, if it doesn't already, in order to facilitate a Reader that is possibly chained to this + * InputStream. + * <p> + * If the stream is not positioned correctly, hell will break loose. + */ + private int readBlock() throws IOException { + // read next two bytes (short) + if (!_read(blklen, 2)) + return -1; - // Get the short-value and store its value in blockLen. - blockLen = (short) ((blklen[0] & 0xFF) >> 1 | (blklen[1] & 0xFF) << 7); - readPos = 0; + // Get the short-value and store its value in blockLen. + blockLen = (short) ((blklen[0] & 0xFF) >> 1 | (blklen[1] & 0xFF) << 7); + readPos = 0; - // sanity check to avoid bad servers make us do an ugly stack trace - if (blockLen > block.length) - throw new AssertionError("Server sent a block larger than BLOCKsize: " + - blockLen + " > " + block.length); - if (!_read(block, blockLen)) - return -1; + // sanity check to avoid bad servers make us do an ugly stack trace + if (blockLen > block.length) + throw new AssertionError("Server sent a block larger than BLOCKsize: " + + blockLen + " > " + block.length); + if (!_read(block, blockLen)) + return -1; - // if this is the last block, make it end with a newline and prompt - if ((blklen[0] & 0x1) == 1) { - if (blockLen > 0 && block[blockLen - 1] != '\n') { - // to terminate the block in a Reader - block[blockLen++] = '\n'; - } - // insert 'fake' flush - block[blockLen++] = MapiConnection.PROMPT_CHAR; - block[blockLen++] = '\n'; - } - return blockLen; - } + // if this is the last block, make it end with a newline and prompt + if ((blklen[0] & 0x1) == 1) { + if (blockLen > 0 && block[blockLen - 1] != '\n') { + // to terminate the block in a Reader + block[blockLen++] = '\n'; + } + // insert 'fake' flush + block[blockLen++] = MapiConnection.PROMPT_CHAR; + block[blockLen++] = '\n'; + } + return blockLen; + } - public int read() throws IOException { - if (available() == 0) { - if (readBlock() == -1) - return -1; - } - return (int) block[readPos++]; - } + public int read() throws IOException { + if (available() == 0) { + if (readBlock() == -1) + return -1; + } + return (int) block[readPos++]; + } - public int read(ByteBuffer b) throws IOException { - return read(b, 0, b.capacity()); - } + public int read(ByteBuffer b) throws IOException { + return read(b, 0, b.capacity()); + } - public int read(ByteBuffer b, int off, int len) throws IOException { - b.clear(); - int t; - int size = 0; - while (size < len) { - t = available(); - if (t == 0) { - if (size != 0) - break; - if (readBlock() == -1) { - size = -1; - break; - } - t = available(); - } - if (len > t) { - System.arraycopy(block, readPos, b.array(), off, t); - off += t; - len -= t; - readPos += t; - size += t; - } else { - System.arraycopy(block, readPos, b.array(), off, len); - readPos += len; - size += len; - 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; - } + public int read(ByteBuffer b, int off, int len) throws IOException { + b.clear(); + int t; + int size = 0; + while (size < len) { + t = available(); + if (t == 0) { + if (size != 0) + break; + if (readBlock() == -1) { + size = -1; + break; + } + t = available(); + } + if (len > t) { + System.arraycopy(block, readPos, b.array(), off, t); + off += t; + len -= t; + readPos += t; + size += t; + } else { + System.arraycopy(block, readPos, b.array(), off, len); + readPos += len; + size += len; + 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; + } - public long skip(long n) throws IOException { - long skip = n; - int t; - while (skip > 0) { - t = available(); - if (skip > t) { - skip -= t; - readPos += t; - readBlock(); - } else { - readPos += skip; - break; - } - } - return n; - } - } + public long skip(long n) throws IOException { + long skip = n; + int t; + while (skip > 0) { + t = available(); + if (skip > t) { + skip -= t; + readPos += t; + readBlock(); + } else { + readPos += skip; + break; + } + } + return n; + } + } - /** - * Inner class that is used to write data on a normal stream as a blocked stream. A call to the flush() method will - * write a "final" block to the underlying stream. Non-final blocks are written as soon as one or more bytes would - * not fit in the current block any more. This allows to write to a block to it's full size, and then flush it - * explicitly to have a final block being written to the stream. - */ - class OldMapiBlockOutputStream { + /** + * Inner class that is used to write data on a normal stream as a blocked stream. A call to the flush() method will + * write a "final" block to the underlying stream. Non-final blocks are written as soon as one or more bytes would + * not fit in the current block any more. This allows to write to a block to it's full size, and then flush it + * explicitly to have a final block being written to the stream. + */ + class OldMapiBlockOutputStream { - private final OutputStream outStream; + private final OutputStream outStream; - private int writePos = 0; + private int writePos = 0; - private byte[] block = new byte[BLOCK]; + private byte[] block = new byte[BLOCK]; - private int blocksize = 0; + private int blocksize = 0; - /** - * Constructs this BlockOutputStream, backed by the given OutputStream. A BufferedOutputStream is internally - * used. - */ - OldMapiBlockOutputStream(OutputStream out) { - this.outStream = out; - } + /** + * Constructs this BlockOutputStream, backed by the given OutputStream. A BufferedOutputStream is internally + * used. + */ + OldMapiBlockOutputStream(OutputStream out) { + this.outStream = out; + } - void flush() throws IOException { - // write the block (as final) then flush. - writeBlock(true); - outStream.flush(); - } + void flush() throws IOException { + // write the block (as final) then flush. + writeBlock(true); + outStream.flush(); + } - /** - * writeBlock puts the data in the block on the stream. The boolean last controls whether the block is sent with - * an indicator to note it is the last block of a sequence or not. - * - * @param last whether this is the last block - * @throws IOException if writing to the stream failed - */ - void writeBlock(boolean last) throws IOException { - if (last) { - // always fits, because of BLOCK's size - blocksize = (short) writePos; - // this is the last block, so encode least significant bit in the first byte (little-endian) - blklen[0] = (byte) (blocksize << 1 & 0xFF | 1); - blklen[1] = (byte) (blocksize >> 7); - } else { - // always fits, because of BLOCK's size - blocksize = (short) BLOCK; - // another block will follow, encode least significant bit in the first byte (little-endian) - blklen[0] = (byte) (blocksize << 1 & 0xFF); - blklen[1] = (byte) (blocksize >> 7); - } - outStream.write(blklen); - // write the actual block - outStream.write(block, 0, writePos); - writePos = 0; - } + /** + * writeBlock puts the data in the block on the stream. The boolean last controls whether the block is sent with + * an indicator to note it is the last block of a sequence or not. + * + * @param last whether this is the last block + * @throws IOException if writing to the stream failed + */ + void writeBlock(boolean last) throws IOException { + if (last) { + // always fits, because of BLOCK's size + blocksize = (short) writePos; + // this is the last block, so encode least significant bit in the first byte (little-endian) + blklen[0] = (byte) (blocksize << 1 & 0xFF | 1); + blklen[1] = (byte) (blocksize >> 7); + } else { + // always fits, because of BLOCK's size + blocksize = (short) BLOCK; + // another block will follow, encode least significant bit in the first byte (little-endian) + blklen[0] = (byte) (blocksize << 1 & 0xFF); + blklen[1] = (byte) (blocksize >> 7); + } + outStream.write(blklen); + // write the actual block + outStream.write(block, 0, writePos); + writePos = 0; + } - void write(int b) throws IOException { - if (writePos == BLOCK) { - writeBlock(false); - } - block[writePos++] = (byte) b; - } + void write(int b) throws IOException { + if (writePos == BLOCK) { + writeBlock(false); + } + block[writePos++] = (byte) b; + } - int write(ByteBuffer b) throws IOException { - return write(b, 0, b.position()); - } + int write(ByteBuffer b) throws IOException { + return write(b, 0, b.position()); + } - int write(ByteBuffer b, int off, int len) throws IOException { - int t, written = 0; - while (len > 0) { - t = BLOCK - writePos; - if (len > t) { - System.arraycopy(b.array(), off, block, writePos, t); - off += t; - len -= t; - writePos += t; - written += t; - writeBlock(false); - } else { - System.arraycopy(b.array(), off, block, writePos, len); - writePos += len; - written += len; - break; - } - } - b.clear(); - return written; - } + int write(ByteBuffer b, int off, int len) throws IOException { + int t, written = 0; + while (len > 0) { + t = BLOCK - writePos; + if (len > t) { + System.arraycopy(b.array(), off, block, writePos, t); + off += t; + len -= t; + writePos += t; + written += t; + writeBlock(false); + } else { + System.arraycopy(b.array(), off, block, writePos, len); + writePos += len; + written += len; + break; + } + } + b.clear(); + return written; + } - public void close() throws IOException { - // we don't want the flush() method to be called (default of the FilterOutputStream), so we close manually - // here - outStream.close(); - } - } + public void close() throws IOException { + // we don't want the flush() method to be called (default of the FilterOutputStream), so we close manually + // here + outStream.close(); + } + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/AbstractProtocol.java @@ -24,241 +24,241 @@ import java.util.Map; */ public abstract class AbstractProtocol { - /* only parse the date patterns once, use multiple times */ - /** Format of a date used by mserver */ - private final SimpleDateFormat monetDate = new SimpleDateFormat("yyyy-MM-dd"); + /* only parse the date patterns once, use multiple times */ + /** Format of a date used by mserver */ + private final SimpleDateFormat monetDate = new SimpleDateFormat("yyyy-MM-dd"); - /** Format of a time */ - private final SimpleDateFormat monetTime = new SimpleDateFormat("HH:mm:ss.SSS"); - /** Format of a time with RFC822 time zone */ - private final SimpleDateFormat monetTimeTz = new SimpleDateFormat("HH:mm:ss.SSSZ"); - /** Format to print a Time String */ - private final SimpleDateFormat monetTimePrinter = new SimpleDateFormat("HH:mm:ss"); - /** Format to print a TimeTz String */ - private final SimpleDateFormat monetTimeTzPrinter = new SimpleDateFormat("HH:mm:ssXXX"); + /** Format of a time */ + private final SimpleDateFormat monetTime = new SimpleDateFormat("HH:mm:ss.SSS"); + /** Format of a time with RFC822 time zone */ + private final SimpleDateFormat monetTimeTz = new SimpleDateFormat("HH:mm:ss.SSSZ"); + /** Format to print a Time String */ + private final SimpleDateFormat monetTimePrinter = new SimpleDateFormat("HH:mm:ss"); + /** Format to print a TimeTz String */ + private final SimpleDateFormat monetTimeTzPrinter = new SimpleDateFormat("HH:mm:ssXXX"); - /** Format of a timestamp */ - private final SimpleDateFormat monetTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - /** Format of a timestamp with RFC822 time zone */ - private final SimpleDateFormat monetTimestampTz = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); - /** Format to print a TimeStamp String */ - private final SimpleDateFormat monetTimestampPrinter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"); - /** Format to print a TimeStampTz String */ - private final SimpleDateFormat monetTimestampTzPrinter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSXXX"); + /** Format of a timestamp */ + private final SimpleDateFormat monetTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + /** Format of a timestamp with RFC822 time zone */ + private final SimpleDateFormat monetTimestampTz = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); + /** Format to print a TimeStamp String */ + private final SimpleDateFormat monetTimestampPrinter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS"); + /** Format to print a TimeStampTz String */ + private final SimpleDateFormat monetTimestampTzPrinter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSXXX"); - /** A helper to parse Dates, Times and Timestamps, to be reused during the connection to save memory allocations. */ - private final ParsePosition monetParserPosition = new ParsePosition(0); + /** A helper to parse Dates, Times and Timestamps, to be reused during the connection to save memory allocations. */ + private final ParsePosition monetParserPosition = new ParsePosition(0); - /** - * Gets the MonetDB Date formatter. - * - * @return The MonetDB Date formatter - */ - public SimpleDateFormat getMonetDate() { - return monetDate; - } + /** + * Gets the MonetDB Date formatter. + * + * @return The MonetDB Date formatter + */ + public SimpleDateFormat getMonetDate() { + return monetDate; + } - /** - * Gets the MonetDB Time formatter. - * - * @return The MonetDB Time formatter - */ - public SimpleDateFormat getMonetTime() { - return monetTime; - } + /** + * Gets the MonetDB Time formatter. + * + * @return The MonetDB Time formatter + */ + public SimpleDateFormat getMonetTime() { + return monetTime; + } - /** - * Gets the MonetDB Time with RFC822 time zone formatter. - * - * @return The MonetDB Time with RFC822 time zone formatter - */ - public SimpleDateFormat getMonetTimeTz() { - return monetTimeTz; - } + /** + * Gets the MonetDB Time with RFC822 time zone formatter. + * + * @return The MonetDB Time with RFC822 time zone formatter + */ + public SimpleDateFormat getMonetTimeTz() { + return monetTimeTz; + } - /** - * Gets the MonetDB Time printer. - * - * @return The MonetDB Time printer - */ - public SimpleDateFormat getMonetTimePrinter() { - return monetTimePrinter; - } + /** + * Gets the MonetDB Time printer. + * + * @return The MonetDB Time printer + */ + public SimpleDateFormat getMonetTimePrinter() { + return monetTimePrinter; + } - /** - * Gets the MonetDB Time with timezone printer. - * - * @return The MonetDB Time with timezone printer. - */ - public SimpleDateFormat getMonetTimeTzPrinter() { - return monetTimeTzPrinter; - } + /** + * Gets the MonetDB Time with timezone printer. + * + * @return The MonetDB Time with timezone printer. + */ + public SimpleDateFormat getMonetTimeTzPrinter() { + return monetTimeTzPrinter; + } - /** - * Gets the MonetDB Timestamp formatter. - * - * @return The MonetDB Timestamp formatter - */ - public SimpleDateFormat getMonetTimestamp() { - return monetTimestamp; - } + /** + * Gets the MonetDB Timestamp formatter. + * + * @return The MonetDB Timestamp formatter + */ + public SimpleDateFormat getMonetTimestamp() { + return monetTimestamp; + } - /** - * Gets the MonetDB Timestamp with RFC822 time zone formatter. - * - * @return The MonetDB Timestamp with RFC822 time zone formatter - */ - public SimpleDateFormat getMonetTimestampTz() { - return monetTimestampTz; - } + /** + * Gets the MonetDB Timestamp with RFC822 time zone formatter. + * + * @return The MonetDB Timestamp with RFC822 time zone formatter + */ + public SimpleDateFormat getMonetTimestampTz() { + return monetTimestampTz; + } - /** - * Gets the MonetDB Timestamp printer. - * - * @return The MonetDB Timestamp printer - */ - public SimpleDateFormat getMonetTimestampPrinter() { - return monetTimestampPrinter; - } + /** + * Gets the MonetDB Timestamp printer. + * + * @return The MonetDB Timestamp printer + */ + public SimpleDateFormat getMonetTimestampPrinter() { + return monetTimestampPrinter; + } - /** - * Gets the MonetDB Timestamp with timezone printer. - * - * @return The MonetDB Timestamp with timezone printer. - */ - public SimpleDateFormat getMonetTimestampTzPrinter() { - return monetTimestampTzPrinter; - } + /** + * Gets the MonetDB Timestamp with timezone printer. + * + * @return The MonetDB Timestamp with timezone printer. + */ + public SimpleDateFormat getMonetTimestampTzPrinter() { + return monetTimestampTzPrinter; + } - /** - * Gets the Protocol parser position. - * - * @return The Protocol parser position - */ - public ParsePosition getMonetParserPosition() { - return monetParserPosition; - } + /** + * Gets the Protocol parser position. + * + * @return The Protocol parser position + */ + public ParsePosition getMonetParserPosition() { + return monetParserPosition; + } - /** - * Waits until the server sends the PROMPT message, meaning that the next response is ready for retrieval. - * - * @throws IOException If an error in the underlying connection happened. - */ - public abstract void waitUntilPrompt() throws IOException; + /** + * Waits until the server sends the PROMPT message, meaning that the next response is ready for retrieval. + * + * @throws IOException If an error in the underlying connection happened. + */ + public abstract void waitUntilPrompt() throws IOException; - /** - * Fetches the server's next response data. - * - * @throws IOException If an error in the underlying connection happened. - */ - public abstract void fetchNextResponseData() throws IOException; + /** + * Fetches the server's next response data. + * + * @throws IOException If an error in the underlying connection happened. + */ + public abstract void fetchNextResponseData() throws IOException; - /** - * Gets the current server response. - * - * @return The integer representation of the server response - */ - public abstract int getCurrentServerResponse(); + /** + * Gets the current server response. + * + * @return The integer representation of the server response + */ + public abstract int getCurrentServerResponse(); - /** - * Gets the next starter header of a server response. - * - * @return The integer representation of {@link StarterHeaders} - */ - public abstract int getNextStarterHeader(); + /** + * Gets the next starter header of a server response. + * + * @return The integer representation of {@link StarterHeaders} + */ + public abstract int getNextStarterHeader(); - /** - * Gets the next ResultSet response from the server, belonging to a ResponseList. - * - * @param con The current MonetDB's JDBC connection - * @param list The Response List this result set will belong to - * @param seqnr The sequence number of this result set on the Response List - * @param maxrows A maxrows to set if so - * @return The ResultSet instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - public abstract ResultSetResponse getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, - int seqnr, int maxrows) throws ProtocolException; + /** + * Gets the next ResultSet response from the server, belonging to a ResponseList. + * + * @param con The current MonetDB's JDBC connection + * @param list The Response List this result set will belong to + * @param seqnr The sequence number of this result set on the Response List + * @param maxrows A maxrows to set if so + * @return The ResultSet instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + public abstract ResultSetResponse getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, + int seqnr, int maxrows) throws ProtocolException; - /** - * Gets the next UpdateResponse response from the server. - * - * @return An UpdateResponse instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - public abstract UpdateResponse getNextUpdateResponse() throws ProtocolException; + /** + * Gets the next UpdateResponse response from the server. + * + * @return An UpdateResponse instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + public abstract UpdateResponse getNextUpdateResponse() throws ProtocolException; - /** - * Gets the next SchemaResponse response from the server. - * - * @return A SchemaResponse instance - */ - public SchemaResponse getNextSchemaResponse() { - return new SchemaResponse(); - } + /** + * Gets the next SchemaResponse response from the server. + * + * @return A SchemaResponse instance + */ + public SchemaResponse getNextSchemaResponse() { + return new SchemaResponse(); + } - /** - * Gets the next AutoCommitResponse response from the server. - * - * @return An AutoCommitResponse instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - public abstract AutoCommitResponse getNextAutoCommitResponse() throws ProtocolException; + /** + * Gets the next AutoCommitResponse response from the server. + * + * @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); + /** + * 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 - * against the keys of the Map. - * @return The DataBlockResponse instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - public abstract AbstractDataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) - throws ProtocolException; + /** + * 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 + * against the keys of the Map. + * @return The DataBlockResponse instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + public abstract AbstractDataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) + throws ProtocolException; - /** - * Gets the next Table Header for a ResultSetResponse. More than one of the parameter arrays can be filled at once. - * - * @param columnNames The column names array - * @param columnLengths The column lengths array - * @param types The columns SQL names array - * @param tableNames The columns schemas and names in format schema.table - * @return A TableResultHeaders integer representation, representing which of the fields was filled - * @throws ProtocolException If an error in the underlying connection happened. - */ - public abstract int getNextTableHeader(String[] columnNames, int[] columnLengths, String[] types, - String[] tableNames) throws ProtocolException; + /** + * Gets the next Table Header for a ResultSetResponse. More than one of the parameter arrays can be filled at once. + * + * @param columnNames The column names array + * @param columnLengths The column lengths array + * @param types The columns SQL names array + * @param tableNames The columns schemas and names in format schema.table + * @return A TableResultHeaders integer representation, representing which of the fields was filled + * @throws ProtocolException If an error in the underlying connection happened. + */ + public abstract int getNextTableHeader(String[] columnNames, int[] columnLengths, String[] types, + String[] tableNames) 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. - * - * @param startIndex The first index in the response line to retrieve the String - * @return The String representation of the line starting at the provided index - */ - public abstract String getRemainingStringLine(int startIndex); + /** + * 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. + * + * @param startIndex The first index in the response line to retrieve the String + * @return The String representation of the line starting at the provided index + */ + public abstract String getRemainingStringLine(int startIndex); - /** - * Writes a user query to the server, while providing the respective prefixes and suffixes depending on the current - * language and connection used. - * - * @param prefix The prefix to append at the beginning of the query string - * @param query The user query to submit to the server - * @param suffix The suffix to append at the end of the query string - * @throws IOException If an error in the underlying connection happened. - */ - public abstract void writeNextQuery(String prefix, String query, String suffix) throws IOException; + /** + * Writes a user query to the server, while providing the respective prefixes and suffixes depending on the current + * language and connection used. + * + * @param prefix The prefix to append at the beginning of the query string + * @param query The user query to submit to the server + * @param suffix The suffix to append at the end of the query string + * @throws IOException If an error in the underlying connection happened. + */ + public abstract void writeNextQuery(String prefix, String query, String suffix) throws IOException; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/ServerResponses.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/ServerResponses.java @@ -15,26 +15,26 @@ package nl.cwi.monetdb.mcl.protocol; */ public final class ServerResponses { - private ServerResponses() {} + private ServerResponses() {} - /* Please don't change the order or the values */ + /* Please don't change the order or the values */ - /** "there is currently no line", or the the type is unknown is represented by UNKNOWN */ - public static final int UNKNOWN = 0; - /** a line starting with ! indicates ERROR */ - public static final int ERROR = 1; - /** a line starting with % indicates HEADER */ - public static final int HEADER = 2; - /** a line starting with [ indicates RESULT */ - public static final int RESULT = 3; - /** a line which matches the pattern of prompt1 is a PROMPT */ - public static final int PROMPT = 4; - /** a line which matches the pattern of prompt2 is a MORE */ - public static final int MORE = 5; - /** a line starting with & indicates the start of a header block */ - public static final int SOHEADER = 6; - /** a line starting with ^ indicates REDIRECT */ - public static final int REDIRECT = 7; - /** a line starting with # indicates INFO */ - public static final int INFO = 8; + /** "there is currently no line", or the the type is unknown is represented by UNKNOWN */ + public static final int UNKNOWN = 0; + /** a line starting with ! indicates ERROR */ + public static final int ERROR = 1; + /** a line starting with % indicates HEADER */ + public static final int HEADER = 2; + /** a line starting with [ indicates RESULT */ + public static final int RESULT = 3; + /** a line which matches the pattern of prompt1 is a PROMPT */ + public static final int PROMPT = 4; + /** a line which matches the pattern of prompt2 is a MORE */ + public static final int MORE = 5; + /** a line starting with & indicates the start of a header block */ + public static final int SOHEADER = 6; + /** a line starting with ^ indicates REDIRECT */ + public static final int REDIRECT = 7; + /** a line starting with # indicates INFO */ + public static final int INFO = 8; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/StarterHeaders.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/StarterHeaders.java @@ -16,25 +16,25 @@ package nl.cwi.monetdb.mcl.protocol; */ public final class StarterHeaders { - private StarterHeaders() {} + private StarterHeaders() {} - /* Please don't change the order or the values */ + /* Please don't change the order or the values */ - /** A parse response (not handled) */ - public static final int Q_PARSE = 0; - /** A tabular response (typical ResultSet) */ - public static final int Q_TABLE = 1; - /** A response to an update statement, contains number of affected rows and generated key-id */ - public static final int Q_UPDATE = 2; - /** A response to a schema update */ - public static final int Q_SCHEMA = 3; - /** A response to a transaction statement (start, rollback, abort, commit) */ - public static final int Q_TRANS = 4; - /** A tabular response in response to a PREPARE statement containing information about the wildcard values that - * need to be supplied */ - public static final int Q_PREPARE = 5; - /** A tabular continuation response (for a ResultSet) */ - public static final int Q_BLOCK = 6; - /** An unknown and unsupported response */ - public static final int Q_UNKNOWN = 7; + /** A parse response (not handled) */ + public static final int Q_PARSE = 0; + /** A tabular response (typical ResultSet) */ + public static final int Q_TABLE = 1; + /** A response to an update statement, contains number of affected rows and generated key-id */ + public static final int Q_UPDATE = 2; + /** A response to a schema update */ + public static final int Q_SCHEMA = 3; + /** A response to a transaction statement (start, rollback, abort, commit) */ + public static final int Q_TRANS = 4; + /** A tabular response in response to a PREPARE statement containing information about the wildcard values that + * need to be supplied */ + public static final int Q_PREPARE = 5; + /** A tabular continuation response (for a ResultSet) */ + public static final int Q_BLOCK = 6; + /** An unknown and unsupported response */ + public static final int Q_UNKNOWN = 7; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/TableResultHeaders.java @@ -16,20 +16,20 @@ package nl.cwi.monetdb.mcl.protocol; */ public final class TableResultHeaders { - private TableResultHeaders() {} + private TableResultHeaders() {} - /* Please don't change the order or the values */ + /* Please don't change the order or the values */ - /** When an unknown table header is returned on a MAPI connection */ - public static final int UNKNOWN = 0; - /** The column names */ - public static final int NAME = 1; - /** The column lengths */ - public static final int LENGTH = 2; - /** The column table and schemas names in format of schema.table */ - public static final int TABLE = 4; - /** The SQL name of the MonetDB data type of the column */ - public static final int TYPE = 8; - /** This header is returned by the JDBC embedded telling that it fetches all the previous headers at once */ - public static final int ALL = 15; + /** When an unknown table header is returned on a MAPI connection */ + public static final int UNKNOWN = 0; + /** The column names */ + public static final int NAME = 1; + /** The column lengths */ + public static final int LENGTH = 2; + /** The column table and schemas names in format of schema.table */ + public static final int TABLE = 4; + /** The SQL name of the MonetDB data type of the column */ + public static final int TYPE = 8; + /** This header is returned by the JDBC embedded telling that it fetches all the previous headers at once */ + public static final int ALL = 15; }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiDataBlockResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiDataBlockResponse.java @@ -23,276 +23,276 @@ import java.util.Calendar; */ 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; + /** 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]; - } + 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 2013: //Types.TIME_WITH_TIMEZONE: - this.data[i] = new Calendar[this.rowcount]; - break; - case Types.TIMESTAMP: - case 2014: //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); - } + @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 2013: //Types.TIME_WITH_TIMEZONE: + this.data[i] = new Calendar[this.rowcount]; + break; + case Types.TIMESTAMP: + case 2014: //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; - } + /** + * 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; - } + /** + * 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; - } + /** + * 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 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 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 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 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 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 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 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 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 2013: //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 2014: //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 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 2013: //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 2014: //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 2014: //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 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 2014: //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; - } + @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 @@ -33,256 +33,256 @@ import java.util.Map; */ public class OldMapiProtocol extends AbstractProtocol { - /** - * The default size for the tuple lines' CharBuffer (it should be less than the OldMapiSocket BLOCK size). - */ - private static final int TUPLE_LINE_BUFFER_DEFAULT_SIZE = 1024; + /** + * The default size for the tuple lines' CharBuffer (it should be less than the OldMapiSocket BLOCK size). + */ + private static final int TUPLE_LINE_BUFFER_DEFAULT_SIZE = 1024; - /** Format of a time string from the old MAPI connection */ - final SimpleDateFormat timeParser = new SimpleDateFormat("HH:mm:ss"); + /** Format of a time string from the old MAPI connection */ + final SimpleDateFormat timeParser = new SimpleDateFormat("HH:mm:ss"); - /** Format of a timestamp string from the old MAPI connection */ - final SimpleDateFormat timestampParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + /** Format of a timestamp string from the old MAPI connection */ + final SimpleDateFormat timestampParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - /**The current server response */ - private int currentServerResponseHeader = ServerResponses.UNKNOWN; + /**The current server response */ + private int currentServerResponseHeader = ServerResponses.UNKNOWN; - /** The underlying MAPI socket connection */ - private final OldMapiSocket socket; + /** The underlying MAPI socket connection */ + private final OldMapiSocket socket; - /** The buffer used to parse server's responses */ - CharBuffer lineBuffer; + /** The buffer used to parse server's responses */ + CharBuffer lineBuffer; - /** A helper buffer used to parse tuple line responses */ - CharBuffer tupleLineBuffer; + /** A helper buffer used to parse tuple line responses */ + CharBuffer tupleLineBuffer; - public OldMapiProtocol(OldMapiSocket socket) { - this.socket = socket; - this.lineBuffer = CharBuffer.wrap(new char[OldMapiSocket.FULL_BLOCK]); - this.tupleLineBuffer = CharBuffer.wrap(new char[TUPLE_LINE_BUFFER_DEFAULT_SIZE]); - } + public OldMapiProtocol(OldMapiSocket socket) { + this.socket = socket; + this.lineBuffer = CharBuffer.wrap(new char[OldMapiSocket.FULL_BLOCK]); + this.tupleLineBuffer = CharBuffer.wrap(new char[TUPLE_LINE_BUFFER_DEFAULT_SIZE]); + } - /** - * Gets the underlying socket. - * - * @return The underlying socket - */ - public OldMapiSocket getSocket() { - return socket; - } + /** + * Gets the underlying socket. + * + * @return The underlying socket + */ + public OldMapiSocket getSocket() { + return socket; + } - /** - * Gets the current server response, obtained through the fetchNextResponseData method. - * - * @return The integer representation of {@link ServerResponses} - */ - @Override - public int getCurrentServerResponse() { - return currentServerResponseHeader; - } + /** + * Gets the current server response, obtained through the fetchNextResponseData method. + * + * @return The integer representation of {@link ServerResponses} + */ + @Override + public int getCurrentServerResponse() { + return currentServerResponseHeader; + } - /** - * Reads up till the MonetDB prompt, indicating the server is ready for a new command. - * - * If there are errors present in the lines that are read, then they are put in one string and returned <b>after</b> - * the prompt has been found. - * - * @throws IOException if an IO exception occurs while talking to the server - */ - @Override - public void waitUntilPrompt() throws IOException { - while(this.currentServerResponseHeader != ServerResponses.PROMPT) { - this.lineBuffer = this.socket.readLine(this.lineBuffer); - if(this.lineBuffer.limit() == 0) { - throw new IOException("Connection to server lost!"); - } - this.currentServerResponseHeader = OldMapiServerResponseParser.parseOldMapiServerResponse(this); - this.lineBuffer.position(0); - if (this.currentServerResponseHeader == ServerResponses.ERROR) { - this.lineBuffer.position(1); - } - } - } + /** + * Reads up till the MonetDB prompt, indicating the server is ready for a new command. + * + * If there are errors present in the lines that are read, then they are put in one string and returned <b>after</b> + * the prompt has been found. + * + * @throws IOException if an IO exception occurs while talking to the server + */ + @Override + public void waitUntilPrompt() throws IOException { + while(this.currentServerResponseHeader != ServerResponses.PROMPT) { + this.lineBuffer = this.socket.readLine(this.lineBuffer); + if(this.lineBuffer.limit() == 0) { + throw new IOException("Connection to server lost!"); + } + this.currentServerResponseHeader = OldMapiServerResponseParser.parseOldMapiServerResponse(this); + this.lineBuffer.position(0); + if (this.currentServerResponseHeader == ServerResponses.ERROR) { + this.lineBuffer.position(1); + } + } + } - /** - * Reads a line of text from the socket. A line is considered to be terminated by any one of a line feed ('\n'). - * - * Warning: until the server properly prefixes all of its error messages with SQLSTATE codes, this method prefixes - * all errors it sees without sqlstate with the generic data exception code (22000). - * - * @throws IOException If an I/O error occurs - */ - @Override - public void fetchNextResponseData() throws IOException { - this.lineBuffer = this.socket.readLine(this.lineBuffer); - if(this.lineBuffer.limit() == 0) { - throw new IOException("Connection to server lost!"); - } - this.currentServerResponseHeader = OldMapiServerResponseParser.parseOldMapiServerResponse(this); - if (this.currentServerResponseHeader == ServerResponses.ERROR && !this.lineBuffer.toString() - .matches("^[0-9A-Z]{5}!.+")) { - int limit = this.lineBuffer.limit(); - CharBuffer newbuffer = CharBuffer.wrap(new char[this.lineBuffer.capacity() + 7]); - newbuffer.put("!22000"); - newbuffer.put(this.lineBuffer.array(), 0, limit); - newbuffer.limit(limit + 6); - newbuffer.flip(); - this.lineBuffer = newbuffer; - } - this.lineBuffer.position(1); - } + /** + * Reads a line of text from the socket. A line is considered to be terminated by any one of a line feed ('\n'). + * + * Warning: until the server properly prefixes all of its error messages with SQLSTATE codes, this method prefixes + * all errors it sees without sqlstate with the generic data exception code (22000). + * + * @throws IOException If an I/O error occurs + */ + @Override + public void fetchNextResponseData() throws IOException { + this.lineBuffer = this.socket.readLine(this.lineBuffer); + if(this.lineBuffer.limit() == 0) { + throw new IOException("Connection to server lost!"); + } + this.currentServerResponseHeader = OldMapiServerResponseParser.parseOldMapiServerResponse(this); + if (this.currentServerResponseHeader == ServerResponses.ERROR && !this.lineBuffer.toString() + .matches("^[0-9A-Z]{5}!.+")) { + int limit = this.lineBuffer.limit(); + CharBuffer newbuffer = CharBuffer.wrap(new char[this.lineBuffer.capacity() + 7]); + newbuffer.put("!22000"); + newbuffer.put(this.lineBuffer.array(), 0, limit); + newbuffer.limit(limit + 6); + newbuffer.flip(); + this.lineBuffer = newbuffer; + } + this.lineBuffer.position(1); + } - /** - * Gets the next starter header of a server response. - * - * @return The integer representation of {@link StarterHeaders} - */ - @Override - public int getNextStarterHeader() { - return OldMapiStartOfHeaderParser.getNextStartHeaderOnOldMapi(this); - } + /** + * Gets the next starter header of a server response. + * + * @return The integer representation of {@link StarterHeaders} + */ + @Override + public int getNextStarterHeader() { + return OldMapiStartOfHeaderParser.getNextStartHeaderOnOldMapi(this); + } - /** - * Gets the next ResultSet response from the server, belonging to a ResponseList. - * - * @param con The current MonetDB's JDBC connection - * @param list The Response List this result set will belong to - * @param seqnr The sequence number of this result set on the Response List - * @param maxrows A maxrows to set if so - * @return The ResultSet instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - @Override - public ResultSetResponse getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, int seqnr, - int maxrows) throws ProtocolException { - int id = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //The order cannot be switched!! - int tuplecount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); - int columncount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); - int rowcount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); - if (maxrows != 0 && tuplecount > maxrows) { - tuplecount = maxrows; - } - return new ResultSetResponse(con, list, id, seqnr, rowcount, tuplecount, columncount); - } + /** + * Gets the next ResultSet response from the server, belonging to a ResponseList. + * + * @param con The current MonetDB's JDBC connection + * @param list The Response List this result set will belong to + * @param seqnr The sequence number of this result set on the Response List + * @param maxrows A maxrows to set if so + * @return The ResultSet instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + @Override + public ResultSetResponse getNextResultSetResponse(MonetConnection con, MonetConnection.ResponseList list, int seqnr, + int maxrows) throws ProtocolException { + int id = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //The order cannot be switched!! + int tuplecount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); + int columncount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); + int rowcount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); + if (maxrows != 0 && tuplecount > maxrows) { + tuplecount = maxrows; + } + return new ResultSetResponse(con, list, id, seqnr, rowcount, tuplecount, columncount); + } - /** - * Gets the next UpdateResponse response from the server. - * - * @return An UpdateResponse instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - @Override - public UpdateResponse getNextUpdateResponse() throws ProtocolException { - int count = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //The order cannot be switched!! - int lastId = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); - return new UpdateResponse(lastId, count); - } + /** + * Gets the next UpdateResponse response from the server. + * + * @return An UpdateResponse instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + @Override + public UpdateResponse getNextUpdateResponse() throws ProtocolException { + int count = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //The order cannot be switched!! + int lastId = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); + return new UpdateResponse(lastId, count); + } - /** - * Gets the next AutoCommitResponse response from the server. - * - * @return An AutoCommitResponse instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - @Override - public AutoCommitResponse getNextAutoCommitResponse() throws ProtocolException { - boolean ac = this.lineBuffer.get() == 't'; - return new AutoCommitResponse(ac); - } + /** + * Gets the next AutoCommitResponse response from the server. + * + * @return An AutoCommitResponse instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + @Override + public AutoCommitResponse getNextAutoCommitResponse() throws ProtocolException { + boolean ac = this.lineBuffer.get() == 't'; + return new AutoCommitResponse(ac); + } - @Override - public AbstractDataBlockResponse getAnEmptyDataBlockResponse(int rowcount, int columncount, - AbstractProtocol protocol, int[] JdbcSQLTypes) { - return new OldMapiDataBlockResponse(rowcount, columncount, protocol, JdbcSQLTypes); - } + @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 - * - * @param rsresponses A map of ResultSetResponse, in which this Block will belong to one of them, by checking its id - * against the keys of the Map. - * @return The DataBlockResponse instance - * @throws ProtocolException If an error in the underlying connection happened. - */ - @Override - public AbstractDataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) - throws ProtocolException { - int id = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //The order cannot be switched!! - OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //column count - int rowcount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); - int offset = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); + /** + * 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 + * against the keys of the Map. + * @return The DataBlockResponse instance + * @throws ProtocolException If an error in the underlying connection happened. + */ + @Override + public AbstractDataBlockResponse getNextDatablockResponse(Map<Integer, ResultSetResponse> rsresponses) + throws ProtocolException { + int id = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //The order cannot be switched!! + OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); //column count + int rowcount = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); + int offset = OldMapiStartOfHeaderParser.getNextResponseDataAsInt(this); - ResultSetResponse rs = rsresponses.get(id); - if (rs == null) { - return null; - } - return rs.addDataBlockResponse(offset, rowcount); - } + ResultSetResponse rs = rsresponses.get(id); + if (rs == null) { + return null; + } + return rs.addDataBlockResponse(offset, rowcount); + } - /** - * Gets the next Table Header for a ResultSetResponse. More than one of the parameter arrays can be filled at once. - * - * @param columnNames The column names array - * @param columnLengths The column lengths array - * @param types The columns SQL names array - * @param tableNames The columns schemas and names in format schema.table - * @return A TableResultHeaders integer representation, representing which of the fields was filled - * @throws ProtocolException If an error in the underlying connection happened. - */ - @Override - public int getNextTableHeader(String[] columnNames, int[] columnLengths, String[] types, String[] tableNames) - throws ProtocolException { - return OldMapiTableHeaderParser.getNextTableHeader(this.lineBuffer, columnNames, columnLengths, types, - tableNames); - } + /** + * Gets the next Table Header for a ResultSetResponse. More than one of the parameter arrays can be filled at once. + * + * @param columnNames The column names array + * @param columnLengths The column lengths array + * @param types The columns SQL names array + * @param tableNames The columns schemas and names in format schema.table + * @return A TableResultHeaders integer representation, representing which of the fields was filled + * @throws ProtocolException If an error in the underlying connection happened. + */ + @Override + public int getNextTableHeader(String[] columnNames, int[] columnLengths, String[] types, String[] tableNames) + throws ProtocolException { + return OldMapiTableHeaderParser.getNextTableHeader(this.lineBuffer, columnNames, columnLengths, types, + tableNames); + } - /** - * 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. - */ - int parseTupleLines(int firstLineNumber, int[] typesMap, Object[] values) throws ProtocolException { - OldMapiTupleLineParser.oldMapiParseTupleLine(this, firstLineNumber, typesMap, values); - return firstLineNumber; - } + /** + * 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. + */ + int parseTupleLines(int firstLineNumber, int[] typesMap, Object[] values) throws ProtocolException { + OldMapiTupleLineParser.oldMapiParseTupleLine(this, firstLineNumber, typesMap, values); + return firstLineNumber; + } - /** - * 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. - * - * @param startIndex The first index in the response line to retrieve the String - * @return The String representation of the line starting at the provided index - */ - @Override - public String getRemainingStringLine(int startIndex) { - if(this.lineBuffer.limit() > startIndex) { - if(this.lineBuffer.array()[startIndex] == '!') { - startIndex++; - } - return new String(this.lineBuffer.array(), startIndex, this.lineBuffer.limit() - startIndex); - } else { - return null; - } - } + /** + * 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. + * + * @param startIndex The first index in the response line to retrieve the String + * @return The String representation of the line starting at the provided index + */ + @Override + public String getRemainingStringLine(int startIndex) { + if(this.lineBuffer.limit() > startIndex) { + if(this.lineBuffer.array()[startIndex] == '!') { + startIndex++; + } + return new String(this.lineBuffer.array(), startIndex, this.lineBuffer.limit() - startIndex); + } else { + return null; + } + } - /** - * Writes a user query to the server, while providing the respective prefixes and suffixes depending on the current - * language and connection used. - * - * @param prefix The prefix to append at the beginning of the query string - * @param query The user query to submit to the server - * @param suffix The suffix to append at the end of the query string - * @throws IOException If an error in the underlying connection happened. - */ - @Override - public void writeNextQuery(String prefix, String query, String suffix) throws IOException { - this.socket.writeNextLine(prefix, query, suffix); - // reset reader state, last line isn't valid any more now - this.currentServerResponseHeader = ServerResponses.UNKNOWN; - } + /** + * Writes a user query to the server, while providing the respective prefixes and suffixes depending on the current + * language and connection used. + * + * @param prefix The prefix to append at the beginning of the query string + * @param query The user query to submit to the server + * @param suffix The suffix to append at the end of the query string + * @throws IOException If an error in the underlying connection happened. + */ + @Override + public void writeNextQuery(String prefix, String query, String suffix) throws IOException { + this.socket.writeNextLine(prefix, query, suffix); + // reset reader state, last line isn't valid any more now + this.currentServerResponseHeader = ServerResponses.UNKNOWN; + } }
--- 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,47 +17,47 @@ import nl.cwi.monetdb.mcl.protocol.Serve */ final class OldMapiServerResponseParser { - private OldMapiServerResponseParser() {} + private OldMapiServerResponseParser() {} - /** - * Retrieves the next server response from an old MAPI protocol instance. - * - * @param protocol An Old MAPI protocol instance from which the next server response will be retrieved - * @return The integer representation of the next server response - */ - static int parseOldMapiServerResponse(OldMapiProtocol protocol) { - int res; - switch (protocol.lineBuffer.get()) { - case '!': - res = ServerResponses.ERROR; - break; - case '&': - res = ServerResponses.SOHEADER; - break; - case '%': - res = ServerResponses.HEADER; - break; - case '[': - res = ServerResponses.RESULT; - break; - case '=': - res = ServerResponses.RESULT; - break; - case '^': - res = ServerResponses.REDIRECT; - break; - case '#': - res = ServerResponses.INFO; - break; - case '.': - res = ServerResponses.PROMPT; - break; - case ',': - res = ServerResponses.MORE; - break; - default: - res = ServerResponses.UNKNOWN; - } - return res; - } + /** + * Retrieves the next server response from an old MAPI protocol instance. + * + * @param protocol An Old MAPI protocol instance from which the next server response will be retrieved + * @return The integer representation of the next server response + */ + static int parseOldMapiServerResponse(OldMapiProtocol protocol) { + int res; + switch (protocol.lineBuffer.get()) { + case '!': + res = ServerResponses.ERROR; + break; + case '&': + res = ServerResponses.SOHEADER; + break; + case '%': + res = ServerResponses.HEADER; + break; + case '[': + res = ServerResponses.RESULT; + break; + case '=': + res = ServerResponses.RESULT; + break; + case '^': + res = ServerResponses.REDIRECT; + break; + case '#': + res = ServerResponses.INFO; + break; + case '.': + res = ServerResponses.PROMPT; + break; + case ',': + res = ServerResponses.MORE; + break; + default: + res = ServerResponses.UNKNOWN; + } + return res; + } }
--- 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,115 +19,115 @@ import nl.cwi.monetdb.mcl.protocol.Start */ final class OldMapiStartOfHeaderParser { - private OldMapiStartOfHeaderParser() {} + private OldMapiStartOfHeaderParser() {} - static int getNextStartHeaderOnOldMapi(OldMapiProtocol protocol) { - int res; - switch (protocol.lineBuffer.get()) { - case '0': - res = StarterHeaders.Q_PARSE; - break; - case '1': - res = StarterHeaders.Q_TABLE; - protocol.lineBuffer.get(); - break; - case '2': - res = StarterHeaders.Q_UPDATE; - protocol.lineBuffer.get(); - break; - case '3': - res = StarterHeaders.Q_SCHEMA; - break; - case '4': - res = StarterHeaders.Q_TRANS; - protocol.lineBuffer.get(); - break; - case '5': - res = StarterHeaders.Q_PREPARE; - protocol.lineBuffer.get(); - break; - case '6': - res = StarterHeaders.Q_BLOCK; - protocol.lineBuffer.get(); - break; - default: - res = StarterHeaders.Q_UNKNOWN; - } - return res; - } + static int getNextStartHeaderOnOldMapi(OldMapiProtocol protocol) { + int res; + switch (protocol.lineBuffer.get()) { + case '0': + res = StarterHeaders.Q_PARSE; + break; + case '1': + res = StarterHeaders.Q_TABLE; + protocol.lineBuffer.get(); + break; + case '2': + res = StarterHeaders.Q_UPDATE; + protocol.lineBuffer.get(); + break; + case '3': + res = StarterHeaders.Q_SCHEMA; + break; + case '4': + res = StarterHeaders.Q_TRANS; + protocol.lineBuffer.get(); + break; + case '5': + res = StarterHeaders.Q_PREPARE; + protocol.lineBuffer.get(); + break; + case '6': + res = StarterHeaders.Q_BLOCK; + protocol.lineBuffer.get(); + break; + default: + res = StarterHeaders.Q_UNKNOWN; + } + return res; + } - /** - * Returns the next token in the Protocol's lineBuffer as an integer. The value is considered to end at the end of - * the lineBuffer or at a space. If a non-numeric character is encountered a ProtocolException is thrown. - * - * @param protocol An Old Mapi Protocol instance where the next token will be retrieved - * @return The next token in the Protocol as an integer - * @throws ProtocolException if no numeric value could be read - */ - static int getNextResponseDataAsInt(OldMapiProtocol protocol) throws ProtocolException { - int currentPointer = protocol.lineBuffer.position(); - int limit = protocol.lineBuffer.limit(); - char[] array = protocol.lineBuffer.array(); + /** + * Returns the next token in the Protocol's lineBuffer as an integer. The value is considered to end at the end of + * the lineBuffer or at a space. If a non-numeric character is encountered a ProtocolException is thrown. + * + * @param protocol An Old Mapi Protocol instance where the next token will be retrieved + * @return The next token in the Protocol as an integer + * @throws ProtocolException if no numeric value could be read + */ + static int getNextResponseDataAsInt(OldMapiProtocol protocol) throws ProtocolException { + int currentPointer = protocol.lineBuffer.position(); + int limit = protocol.lineBuffer.limit(); + char[] array = protocol.lineBuffer.array(); - if (currentPointer >= limit) { - throw new ProtocolException("unexpected end of string", currentPointer - 1); - } - int tmp = 0; - boolean positive = true; - char chr = array[currentPointer++]; - // note: don't use Character.isDigit() here, because we only want ISO-LATIN-1 digits - if (chr >= '0' && chr <= '9') { - tmp = (int)chr - (int)'0'; - } else if(chr == '-') { - positive = false; - } else { - throw new ProtocolException("expected a digit", currentPointer - 1); - } + if (currentPointer >= limit) { + throw new ProtocolException("unexpected end of string", currentPointer - 1); + } + int tmp = 0; + boolean positive = true; + char chr = array[currentPointer++]; + // note: don't use Character.isDigit() here, because we only want ISO-LATIN-1 digits + if (chr >= '0' && chr <= '9') { + tmp = (int)chr - (int)'0'; + } else if(chr == '-') { + positive = false; + } else { + throw new ProtocolException("expected a digit", currentPointer - 1); + } - while (currentPointer < limit) { - chr = array[currentPointer++]; - if(chr == ' ') { - break; - } - tmp *= 10; - if (chr >= '0' && chr <= '9') { - tmp += (int)chr - (int)'0'; - } else { - throw new ProtocolException("expected a digit", currentPointer - 1); - } - } - protocol.lineBuffer.position(currentPointer); - return positive ? tmp : -tmp; - } + while (currentPointer < limit) { + chr = array[currentPointer++]; + if(chr == ' ') { + break; + } + tmp *= 10; + if (chr >= '0' && chr <= '9') { + tmp += (int)chr - (int)'0'; + } else { + throw new ProtocolException("expected a digit", currentPointer - 1); + } + } + protocol.lineBuffer.position(currentPointer); + return positive ? tmp : -tmp; + } - /** - * Returns the next token in the Protocol's lineBuffer as a String. The value is considered to end at the end of the - * lineBuffer or at a space. If no character is found, a ProtocolException is thrown. - * - * @param protocol An Old Mapi Protocol instance where the next token will be retrieved - * @return The next token in the Protocol as a String - * @throws ProtocolException if no character could be read - */ - static String getNextResponseDataAsString(OldMapiProtocol protocol) throws ProtocolException { - int currentPointer = protocol.lineBuffer.position(); - int limit = protocol.lineBuffer.limit(); - char[] array = protocol.lineBuffer.array(); + /** + * Returns the next token in the Protocol's lineBuffer as a String. The value is considered to end at the end of the + * lineBuffer or at a space. If no character is found, a ProtocolException is thrown. + * + * @param protocol An Old Mapi Protocol instance where the next token will be retrieved + * @return The next token in the Protocol as a String + * @throws ProtocolException if no character could be read + */ + static String getNextResponseDataAsString(OldMapiProtocol protocol) throws ProtocolException { + int currentPointer = protocol.lineBuffer.position(); + int limit = protocol.lineBuffer.limit(); + char[] array = protocol.lineBuffer.array(); - if (currentPointer >= limit) { - throw new ProtocolException("unexpected end of string", currentPointer - 1); - } - int cnt = 0, mark = currentPointer; - char chr; + if (currentPointer >= limit) { + throw new ProtocolException("unexpected end of string", currentPointer - 1); + } + int cnt = 0, mark = currentPointer; + char chr; - while (currentPointer < limit) { - chr = array[currentPointer++]; - if(chr == ' ') { - break; - } - cnt++; - } + while (currentPointer < limit) { + chr = array[currentPointer++]; + if(chr == ' ') { + break; + } + cnt++; + } - protocol.lineBuffer.position(mark); - return new String(array, 0, cnt); - } + protocol.lineBuffer.position(mark); + return new String(array, 0, cnt); + } }
--- 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,143 +21,143 @@ import java.nio.CharBuffer; */ final class OldMapiTableHeaderParser { - private OldMapiTableHeaderParser() {} + private OldMapiTableHeaderParser() {} - /** - * Retrieves the next table result set header and fills the respective array of values. - * - * @param lineBuffer An Old Mapi Protocol's lineBuffer to retrieve data - * @param columnNames The result set column names - * @param columnLengths The result set column lengths - * @param types The result set column SQL types - * @param tableNames The result set columns schema and table names in format schema.table - * @return The integer representation of the Table Result Header retrieved - * @throws ProtocolException If an error while parsing occurred - */ - static int getNextTableHeader(CharBuffer lineBuffer, String[] columnNames, int[] columnLengths, String[] types, - String[] tableNames) throws ProtocolException { - int res = TableResultHeaders.UNKNOWN; - int currentLength = lineBuffer.limit(); - char[] array = lineBuffer.array(); + /** + * Retrieves the next table result set header and fills the respective array of values. + * + * @param lineBuffer An Old Mapi Protocol's lineBuffer to retrieve data + * @param columnNames The result set column names + * @param columnLengths The result set column lengths + * @param types The result set column SQL types + * @param tableNames The result set columns schema and table names in format schema.table + * @return The integer representation of the Table Result Header retrieved + * @throws ProtocolException If an error while parsing occurred + */ + static int getNextTableHeader(CharBuffer lineBuffer, String[] columnNames, int[] columnLengths, String[] types, + String[] tableNames) throws ProtocolException { + int res = TableResultHeaders.UNKNOWN; + int currentLength = lineBuffer.limit(); + char[] array = lineBuffer.array(); - int pos = 0; - boolean foundChar = false, nameFound = false; + int pos = 0; + boolean foundChar = false, nameFound = false; - // find header name - for (int i = currentLength - 1; i >= 0; i--) { - switch (array[i]) { - case ' ': - case '\n': - case '\t': - case '\r': - if (!foundChar) { - currentLength = i - 1; - } else { - pos = i + 1; - } - break; - case '#': - // found! - nameFound = true; - if (pos == 0) pos = i + 1; - i = 0; // force the loop to terminate - break; - default: - foundChar = true; - pos = 0; - break; - } - } - if (!nameFound) - throw new ProtocolException("Invalid header, no header name found", pos); + // find header name + for (int i = currentLength - 1; i >= 0; i--) { + switch (array[i]) { + case ' ': + case '\n': + case '\t': + case '\r': + if (!foundChar) { + currentLength = i - 1; + } else { + pos = i + 1; + } + break; + case '#': + // found! + nameFound = true; + if (pos == 0) pos = i + 1; + i = 0; // force the loop to terminate + break; + default: + foundChar = true; + pos = 0; + break; + } + } + if (!nameFound) + throw new ProtocolException("Invalid header, no header name found", pos); - // depending on the name of the header, we continue - switch (array[pos]) { - case 'n': //name - if (currentLength - pos == 4) { - getStringValues(array, pos - 3, columnNames); - res = TableResultHeaders.NAME; - } - break; - case 'l': //length - if (currentLength - pos == 6) { - getIntValues(array, pos - 3, columnLengths); - res = TableResultHeaders.LENGTH; - } - break; - case 't': - if (currentLength - pos == 4) { //type - getStringValues(array, pos - 3, types); - res = TableResultHeaders.TYPE; - } else if (currentLength - pos == 10) { //table_name - getStringValues(array, pos - 3, tableNames); - res = TableResultHeaders.TABLE; - } - break; - default: - throw new ProtocolException("Unknown header: " + new String(array, pos, currentLength - pos)); - } - return res; - } + // depending on the name of the header, we continue + switch (array[pos]) { + case 'n': //name + if (currentLength - pos == 4) { + getStringValues(array, pos - 3, columnNames); + res = TableResultHeaders.NAME; + } + break; + case 'l': //length + if (currentLength - pos == 6) { + getIntValues(array, pos - 3, columnLengths); + res = TableResultHeaders.LENGTH; + } + break; + case 't': + if (currentLength - pos == 4) { //type + getStringValues(array, pos - 3, types); + res = TableResultHeaders.TYPE; + } else if (currentLength - pos == 10) { //table_name + getStringValues(array, pos - 3, tableNames); + res = TableResultHeaders.TABLE; + } + break; + default: + throw new ProtocolException("Unknown header: " + new String(array, pos, currentLength - pos)); + } + return res; + } - /** - * Fills a String array header with values. - * - * As of Oct2014-SP1 release MAPI adds double quotes around names when the name contains a comma or a tab or a space - * or a # or " character. - * See issue: https://www.monetdb.org/bugzilla/show_bug.cgi?id=3616 If the parsed name string part has a " as first - * and last character, we remove those added double quotes here. - * - * @param array The lineBuffer's backing array - * @param stop The position to stop parsing - * @param stringValues The String array to fill - */ - private static void getStringValues(char[] array, int stop, String[] stringValues) { - int elem = 0, start = 2; + /** + * Fills a String array header with values. + * + * As of Oct2014-SP1 release MAPI adds double quotes around names when the name contains a comma or a tab or a space + * or a # or " character. + * See issue: https://www.monetdb.org/bugzilla/show_bug.cgi?id=3616 If the parsed name string part has a " as first + * and last character, we remove those added double quotes here. + * + * @param array The lineBuffer's backing array + * @param stop The position to stop parsing + * @param stringValues The String array to fill + */ + private static void getStringValues(char[] array, int stop, String[] stringValues) { + int elem = 0, start = 2; - for (int i = start + 1; i < stop; i++) { - if (array[i] == '\t' && array[i - 1] == ',') { - if (array[start] == '"') { - start++; // skip leading double quote - } - stringValues[elem++] = new String(array, start, i - (array[i - 2] == '"' ? 2 : 1) - start); - start = i + 1; - } - } - // add the left over part (last column) - if (array[start] == '"') { - start++; // skip leading double quote - } - stringValues[elem] = new String(array, start, stop - (array[stop - 1] == '"' ? 1 : 0) - start); - } + for (int i = start + 1; i < stop; i++) { + if (array[i] == '\t' && array[i - 1] == ',') { + if (array[start] == '"') { + start++; // skip leading double quote + } + stringValues[elem++] = new String(array, start, i - (array[i - 2] == '"' ? 2 : 1) - start); + start = i + 1; + } + } + // add the left over part (last column) + if (array[start] == '"') { + start++; // skip leading double quote + } + stringValues[elem] = new String(array, start, stop - (array[stop - 1] == '"' ? 1 : 0) - start); + } - /** - * Fills an integer array header with values. - * - * @param array The lineBuffer's backing array - * @param stop The position to stop parsing - * @param intValues The integer array to fill - * @throws ProtocolException If an error while parsing occurred - */ - private static void getIntValues(char[] array, int stop, int[] intValues) throws ProtocolException { - int elem = 0, tmp = 0, start = 2; + /** + * Fills an integer array header with values. + * + * @param array The lineBuffer's backing array + * @param stop The position to stop parsing + * @param intValues The integer array to fill + * @throws ProtocolException If an error while parsing occurred + */ + private static void getIntValues(char[] array, int stop, int[] intValues) throws ProtocolException { + int elem = 0, tmp = 0, start = 2; - for (int i = start; i < stop; i++) { - if (array[i] == ',' && array[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 - if (array[i] >= '0' && array[i] <= '9') { - tmp += (int) array[i] - (int)'0'; - } else { - throw new ProtocolException("Expected a digit in " + new String(array) + " at " + i); - } - } - } - // add the left over part - intValues[elem] = tmp; - } + for (int i = start; i < stop; i++) { + if (array[i] == ',' && array[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 + if (array[i] >= '0' && array[i] <= '9') { + tmp += (int) array[i] - (int)'0'; + } else { + throw new ProtocolException("Expected a digit in " + new String(array) + " at " + i); + } + } + } + // add the left over part + intValues[elem] = tmp; + } }
--- 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,294 +29,294 @@ import java.util.Calendar; */ final class OldMapiTupleLineParser { - private OldMapiTupleLineParser() {} + private OldMapiTupleLineParser() {} - /** - * The character array representation of a NULL value found in a tuple. - */ - private static final char[] NULL_STRING = new char[]{'N','U','L','L'}; + /** + * The character array representation of a NULL value found in a tuple. + */ + private static final char[] NULL_STRING = new char[]{'N','U','L','L'}; - /** - * Parses the given OldMapiProtocol's lineBuffer source as tuple line for a DataBlock. If source cannot be parsed, a - * ProtocolException is thrown. The OldMapiProtocol's tupleLineBuffer is used to help parsing a column value. - * - * @param protocol The OldMapiProtocol instance to parse its lineBuffer - * @param lineNumber The row line number on the DataBlock to insert the tuple - * @param typesMap The JDBC types mapping array - * @param values An array of columns to fill the parsed values - * @return The number of columns parsed - * @throws ProtocolException If an error occurs during parsing - */ - static int oldMapiParseTupleLine(OldMapiProtocol protocol, int lineNumber, int[] typesMap, Object[] values) - throws ProtocolException { - CharBuffer lineBuffer = protocol.lineBuffer; - CharBuffer tupleLineBuffer = protocol.tupleLineBuffer; + /** + * Parses the given OldMapiProtocol's lineBuffer source as tuple line for a DataBlock. If source cannot be parsed, a + * ProtocolException is thrown. The OldMapiProtocol's tupleLineBuffer is used to help parsing a column value. + * + * @param protocol The OldMapiProtocol instance to parse its lineBuffer + * @param lineNumber The row line number on the DataBlock to insert the tuple + * @param typesMap The JDBC types mapping array + * @param values An array of columns to fill the parsed values + * @return The number of columns parsed + * @throws ProtocolException If an error occurs during parsing + */ + static int oldMapiParseTupleLine(OldMapiProtocol protocol, int lineNumber, int[] typesMap, Object[] values) + throws ProtocolException { + CharBuffer lineBuffer = protocol.lineBuffer; + CharBuffer tupleLineBuffer = protocol.tupleLineBuffer; - int len = lineBuffer.limit(); - char[] array = lineBuffer.array(); + int len = lineBuffer.limit(); + char[] array = lineBuffer.array(); - // first detect whether this is a single value line (=) or a real tuple ([) - if (array[0] == '=') { - if (typesMap.length != 1) { - throw new ProtocolException(typesMap.length + " columns expected, but only single value found"); - } - // return the whole string but the leading = - oldMapiStringToJavaDataConversion(protocol, array, 1, len - 1, lineNumber, values[0], - typesMap[0]); - return 1; - } + // first detect whether this is a single value line (=) or a real tuple ([) + if (array[0] == '=') { + if (typesMap.length != 1) { + throw new ProtocolException(typesMap.length + " columns expected, but only single value found"); + } + // return the whole string but the leading = + oldMapiStringToJavaDataConversion(protocol, array, 1, len - 1, lineNumber, values[0], + typesMap[0]); + return 1; + } - // extract separate fields by examining string, char for char - boolean inString = false, escaped = false; - int cursor = 2, column = 0, i = 2; - for (; i < len; i++) { - switch(array[i]) { - default: - escaped = false; - break; - case '\\': - escaped = !escaped; - break; - case '"': - /** - * If all strings are wrapped between two quotes, a \" can never exist outside a string. Thus if we - * believe that we are not within a string, we can safely assume we're about to enter a string if we - * find a quote. If we are in a string we should stop being in a string if we find a quote which is - * not prefixed by a \, for that would be an escaped quote. However, a nasty situation can occur - * where the string is like "test \\" as obvious, a test for a \ in front of a " doesn't hold here - * for all cases. Because "test \\\"" can exist as well, we need to know if a quote is prefixed by - * an escaping slash or not. - */ - if (!inString) { - inString = true; - } else if (!escaped) { - inString = false; - } - // reset escaped flag - escaped = false; - break; - case '\t': - if (!inString && (i > 0 && array[i - 1] == ',') || (i + 1 == len - 1 && array[++i] == ']')) { // dirty - // split! - if (array[cursor] == '"' && array[i - 2] == '"') { - // reuse the tupleLineBuffer by cleaning it and ensure the capacity - tupleLineBuffer.clear(); - tupleLineBuffer = BufferReallocator.ensureCapacity(tupleLineBuffer, (i - 2) - (cursor + 1)); + // extract separate fields by examining string, char for char + boolean inString = false, escaped = false; + int cursor = 2, column = 0, i = 2; + for (; i < len; i++) { + switch(array[i]) { + default: + escaped = false; + break; + case '\\': + escaped = !escaped; + break; + case '"': + /** + * If all strings are wrapped between two quotes, a \" can never exist outside a string. Thus if we + * believe that we are not within a string, we can safely assume we're about to enter a string if we + * find a quote. If we are in a string we should stop being in a string if we find a quote which is + * not prefixed by a \, for that would be an escaped quote. However, a nasty situation can occur + * where the string is like "test \\" as obvious, a test for a \ in front of a " doesn't hold here + * for all cases. Because "test \\\"" can exist as well, we need to know if a quote is prefixed by + * an escaping slash or not. + */ + if (!inString) { + inString = true; + } else if (!escaped) { + inString = false; + } + // reset escaped flag + escaped = false; + break; + case '\t': + if (!inString && (i > 0 && array[i - 1] == ',') || (i + 1 == len - 1 && array[++i] == ']')) { // dirty + // split! + if (array[cursor] == '"' && array[i - 2] == '"') { + // reuse the tupleLineBuffer by cleaning it and ensure the capacity + tupleLineBuffer.clear(); + tupleLineBuffer = BufferReallocator.ensureCapacity(tupleLineBuffer, (i - 2) - (cursor + 1)); - for (int pos = cursor + 1; pos < i - 2; pos++) { - if (array[cursor] == '\\' && pos + 1 < i - 2) { - pos++; - // strToStr and strFromStr in gdk_atoms.mx only support \t \n \\ \" and \377 - switch (array[pos]) { - case '\\': - tupleLineBuffer.put('\\'); - break; - case 'n': - tupleLineBuffer.put('\n'); - break; - case 't': - tupleLineBuffer.put('\t'); - break; - case '"': - tupleLineBuffer.put('"'); - break; - case '0': case '1': case '2': case '3': - // this could be an octal number, let's check it out - if (pos + 2 < i - 2 && array[pos + 1] >= '0' && array[pos + 1] <= '7' && - array[pos + 2] >= '0' && array[pos + 2] <= '7') { - // we got the number! - try { - tupleLineBuffer.put((char)(Integer.parseInt("" + array[pos] + array[pos + 1] + array[pos + 2], 8))); - pos += 2; - } catch (NumberFormatException e) { - // hmmm, this point should never be reached actually... - throw new AssertionError("Flow error, should never try to parse non-number"); - } - } else { - // do default action if number seems not to be correct - tupleLineBuffer.put(array[pos]); - } - break; - default: - // this is wrong, just ignore the escape, and print the char - tupleLineBuffer.put(array[pos]); - break; - } - } else if(array[pos] == '\\') { - pos++; - switch (array[pos]) { - case '\\': - tupleLineBuffer.put('\\'); - break; - case 'n': - tupleLineBuffer.put('\n'); - break; - case 't': - tupleLineBuffer.put('\t'); - break; - case '"': - tupleLineBuffer.put('"'); - break; - } - } else { - tupleLineBuffer.put(array[pos]); - } - } - // put the unescaped string in the right place - tupleLineBuffer.flip(); - oldMapiStringToJavaDataConversion(protocol, tupleLineBuffer.array(), 0, - tupleLineBuffer.limit(), lineNumber, values[column], typesMap[column]); - } else if ((i - 1) - cursor == 4 && OldMapiTupleLineParserHelper.charIndexOf(array, - 0,array.length, NULL_STRING, 0,4, cursor) == cursor) { - setNullValue(lineNumber, values[column], typesMap[column]); - } else { - oldMapiStringToJavaDataConversion(protocol, array, cursor, i - 1 - cursor, lineNumber, - values[column], typesMap[column]); - } - column++; - cursor = i + 1; - } - // reset escaped flag - escaped = false; - break; - } - } - protocol.tupleLineBuffer = tupleLineBuffer; - // check if this result is of the size we expected it to be - if (column != typesMap.length) - throw new ProtocolException("illegal result length: " + column + "\nlast read: " - + (column > 0 ? values[column - 1] : "<none>")); - return column; - } + for (int pos = cursor + 1; pos < i - 2; pos++) { + if (array[cursor] == '\\' && pos + 1 < i - 2) { + pos++; + // strToStr and strFromStr in gdk_atoms.mx only support \t \n \\ \" and \377 + switch (array[pos]) { + case '\\': + tupleLineBuffer.put('\\'); + break; + case 'n': + tupleLineBuffer.put('\n'); + break; + case 't': + tupleLineBuffer.put('\t'); + break; + case '"': + tupleLineBuffer.put('"'); + break; + case '0': case '1': case '2': case '3': + // this could be an octal number, let's check it out + if (pos + 2 < i - 2 && array[pos + 1] >= '0' && array[pos + 1] <= '7' && + array[pos + 2] >= '0' && array[pos + 2] <= '7') { + // we got the number! + try { + tupleLineBuffer.put((char)(Integer.parseInt("" + array[pos] + array[pos + 1] + array[pos + 2], 8))); + pos += 2; + } catch (NumberFormatException e) { + // hmmm, this point should never be reached actually... + throw new AssertionError("Flow error, should never try to parse non-number"); + } + } else { + // do default action if number seems not to be correct + tupleLineBuffer.put(array[pos]); + } + break; + default: + // this is wrong, just ignore the escape, and print the char + tupleLineBuffer.put(array[pos]); + break; + } + } else if(array[pos] == '\\') { + pos++; + switch (array[pos]) { + case '\\': + tupleLineBuffer.put('\\'); + break; + case 'n': + tupleLineBuffer.put('\n'); + break; + case 't': + tupleLineBuffer.put('\t'); + break; + case '"': + tupleLineBuffer.put('"'); + break; + } + } else { + tupleLineBuffer.put(array[pos]); + } + } + // put the unescaped string in the right place + tupleLineBuffer.flip(); + oldMapiStringToJavaDataConversion(protocol, tupleLineBuffer.array(), 0, + tupleLineBuffer.limit(), lineNumber, values[column], typesMap[column]); + } else if ((i - 1) - cursor == 4 && OldMapiTupleLineParserHelper.charIndexOf(array, + 0,array.length, NULL_STRING, 0,4, cursor) == cursor) { + setNullValue(lineNumber, values[column], typesMap[column]); + } else { + oldMapiStringToJavaDataConversion(protocol, array, cursor, i - 1 - cursor, lineNumber, + values[column], typesMap[column]); + } + column++; + cursor = i + 1; + } + // reset escaped flag + escaped = false; + break; + } + } + protocol.tupleLineBuffer = tupleLineBuffer; + // check if this result is of the size we expected it to be + if (column != typesMap.length) + throw new ProtocolException("illegal result length: " + column + "\nlast read: " + + (column > 0 ? values[column - 1] : "<none>")); + return column; + } - /** - * Parses a BLOB String from a tuple column, converting it into a Java byte[] representation. - * - * @param toParse The CharBuffer's backing array - * @param startPosition The first position in the array to parse - * @param count The number of characters to read from the starter position - * @return A Java byte[] instance with the parsed BLOB - */ - private static byte[] binaryBlobConverter(char[] toParse, int startPosition, int count) { - int len = (startPosition + count) / 2; - byte[] res = new byte[len]; - for (int i = 0; i < len; i++) { - res[i] = (byte) Integer.parseInt(new String(toParse, 2 * i, (2 * i) + 2), 16); - } - return res; - } + /** + * Parses a BLOB String from a tuple column, converting it into a Java byte[] representation. + * + * @param toParse The CharBuffer's backing array + * @param startPosition The first position in the array to parse + * @param count The number of characters to read from the starter position + * @return A Java byte[] instance with the parsed BLOB + */ + private static byte[] binaryBlobConverter(char[] toParse, int startPosition, int count) { + int len = (startPosition + count) / 2; + byte[] res = new byte[len]; + for (int i = 0; i < len; i++) { + res[i] = (byte) Integer.parseInt(new String(toParse, 2 * i, (2 * i) + 2), 16); + } + return res; + } - /** - * Converts a segment of a CharBuffer's backing array into a Java primitive or object depending on the JDBC Mapping. - * - * @param toParse The CharBuffer's backing array - * @param startPosition The first position in the array to parse - * @param count The number of characters to read from the starter position - * @param lineNumber The row line number on the DataBlock to insert the tuple - * @param columnArray The column array where the value will be appended - * @param jDBCMapping The JDBC mapping of the value - * @throws ProtocolException If the JDBC Mapping is unknown - */ - private static void oldMapiStringToJavaDataConversion(OldMapiProtocol protocol, char[] toParse, int startPosition, - int count, int lineNumber, Object columnArray, - int jDBCMapping) throws ProtocolException { - switch (jDBCMapping) { - case Types.BOOLEAN: - ((byte[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToBoolean(toParse, startPosition); - break; - case Types.TINYINT: - ((byte[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToByte(toParse, startPosition, count); - break; - case Types.SMALLINT: - ((short[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToShort(toParse, startPosition, count); - break; - case Types.INTEGER: - ((int[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToInt(toParse, startPosition, count); - break; - case Types.BIGINT: - ((long[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToLong(toParse, startPosition, count); - break; - case Types.REAL: - ((float[]) columnArray)[lineNumber] = Float.parseFloat(new String(toParse, startPosition, count)); - break; - case Types.DOUBLE: - ((double[]) columnArray)[lineNumber] = Double.parseDouble(new String(toParse, startPosition, count)); - break; - case Types.NUMERIC: - case Types.DECIMAL: - ((BigDecimal[]) columnArray)[lineNumber] = new BigDecimal(toParse, startPosition, count); - break; - case Types.CHAR: - case Types.VARCHAR: - case Types.LONGVARCHAR: - case Types.OTHER: - ((String[]) columnArray)[lineNumber] = new String(toParse, startPosition, count); - break; - case Types.DATE: - ((Calendar[]) columnArray)[lineNumber] = GregorianCalendarParser.parseDate(new String(toParse, startPosition, count), - protocol.getMonetParserPosition(), protocol.getMonetDate()); - break; - case Types.TIME: - ((Calendar[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTime(new String(toParse, startPosition, count), - protocol.getMonetParserPosition(), protocol.timeParser, false); - break; - case 2013: //Types.TIME_WITH_TIMEZONE: - ((Calendar[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTime(new String(toParse, startPosition, count), - protocol.getMonetParserPosition(), protocol.timeParser, true); - break; - case Types.TIMESTAMP: - ((TimestampHelper[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTimestamp(new String(toParse, startPosition, count), - protocol.getMonetParserPosition(), protocol.timestampParser, false); - break; - case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: - ((TimestampHelper[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTimestamp(new String(toParse, startPosition, count), - protocol.getMonetParserPosition(), protocol.timestampParser, true); - break; - case Types.CLOB: - ((MonetClob[]) columnArray)[lineNumber] = new MonetClob(toParse, startPosition, count); - break; - case Types.BLOB: - ((MonetBlob[]) columnArray)[lineNumber] = new MonetBlob(binaryBlobConverter(toParse, startPosition, count)); - break; - case Types.LONGVARBINARY: - ((byte[][]) columnArray)[lineNumber] = binaryBlobConverter(toParse, startPosition, count); - break; - default: - throw new ProtocolException("Unknown JDBC mapping!"); - } - } + /** + * Converts a segment of a CharBuffer's backing array into a Java primitive or object depending on the JDBC Mapping. + * + * @param toParse The CharBuffer's backing array + * @param startPosition The first position in the array to parse + * @param count The number of characters to read from the starter position + * @param lineNumber The row line number on the DataBlock to insert the tuple + * @param columnArray The column array where the value will be appended + * @param jDBCMapping The JDBC mapping of the value + * @throws ProtocolException If the JDBC Mapping is unknown + */ + private static void oldMapiStringToJavaDataConversion(OldMapiProtocol protocol, char[] toParse, int startPosition, + int count, int lineNumber, Object columnArray, + int jDBCMapping) throws ProtocolException { + switch (jDBCMapping) { + case Types.BOOLEAN: + ((byte[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToBoolean(toParse, startPosition); + break; + case Types.TINYINT: + ((byte[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToByte(toParse, startPosition, count); + break; + case Types.SMALLINT: + ((short[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToShort(toParse, startPosition, count); + break; + case Types.INTEGER: + ((int[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToInt(toParse, startPosition, count); + break; + case Types.BIGINT: + ((long[]) columnArray)[lineNumber] = OldMapiTupleLineParserHelper.charArrayToLong(toParse, startPosition, count); + break; + case Types.REAL: + ((float[]) columnArray)[lineNumber] = Float.parseFloat(new String(toParse, startPosition, count)); + break; + case Types.DOUBLE: + ((double[]) columnArray)[lineNumber] = Double.parseDouble(new String(toParse, startPosition, count)); + break; + case Types.NUMERIC: + case Types.DECIMAL: + ((BigDecimal[]) columnArray)[lineNumber] = new BigDecimal(toParse, startPosition, count); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + case Types.OTHER: + ((String[]) columnArray)[lineNumber] = new String(toParse, startPosition, count); + break; + case Types.DATE: + ((Calendar[]) columnArray)[lineNumber] = GregorianCalendarParser.parseDate(new String(toParse, startPosition, count), + protocol.getMonetParserPosition(), protocol.getMonetDate()); + break; + case Types.TIME: + ((Calendar[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTime(new String(toParse, startPosition, count), + protocol.getMonetParserPosition(), protocol.timeParser, false); + break; + case 2013: //Types.TIME_WITH_TIMEZONE: + ((Calendar[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTime(new String(toParse, startPosition, count), + protocol.getMonetParserPosition(), protocol.timeParser, true); + break; + case Types.TIMESTAMP: + ((TimestampHelper[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTimestamp(new String(toParse, startPosition, count), + protocol.getMonetParserPosition(), protocol.timestampParser, false); + break; + case 2014: //Types.TIMESTAMP_WITH_TIMEZONE: + ((TimestampHelper[]) columnArray)[lineNumber] = GregorianCalendarParser.parseTimestamp(new String(toParse, startPosition, count), + protocol.getMonetParserPosition(), protocol.timestampParser, true); + break; + case Types.CLOB: + ((MonetClob[]) columnArray)[lineNumber] = new MonetClob(toParse, startPosition, count); + break; + case Types.BLOB: + ((MonetBlob[]) columnArray)[lineNumber] = new MonetBlob(binaryBlobConverter(toParse, startPosition, count)); + break; + case Types.LONGVARBINARY: + ((byte[][]) columnArray)[lineNumber] = binaryBlobConverter(toParse, startPosition, count); + break; + default: + throw new ProtocolException("Unknown JDBC mapping!"); + } + } - /** - * Maps MonetDB's null values with their respective Java representation. For the primitive types, we will map them - * to their minimum values, while for objects we just map into null pointers. - * - * @param lineNumber The row line number on the DataBlock to insert the tuple - * @param columnArray The column array where the value will be appended - * @param jDBCMapping The JDBC mapping of the value - */ - private static void setNullValue(int lineNumber, Object columnArray, int jDBCMapping) { - switch (jDBCMapping) { - case Types.BOOLEAN: - case Types.TINYINT: - ((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: - ((Object[]) columnArray)[lineNumber] = null; - } - } + /** + * Maps MonetDB's null values with their respective Java representation. For the primitive types, we will map them + * to their minimum values, while for objects we just map into null pointers. + * + * @param lineNumber The row line number on the DataBlock to insert the tuple + * @param columnArray The column array where the value will be appended + * @param jDBCMapping The JDBC mapping of the value + */ + private static void setNullValue(int lineNumber, Object columnArray, int jDBCMapping) { + switch (jDBCMapping) { + case Types.BOOLEAN: + case Types.TINYINT: + ((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: + ((Object[]) columnArray)[lineNumber] = null; + } + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParserHelper.java +++ b/src/main/java/nl/cwi/monetdb/mcl/protocol/oldmapi/OldMapiTupleLineParserHelper.java @@ -19,214 +19,214 @@ import nl.cwi.monetdb.mcl.protocol.Proto */ final class OldMapiTupleLineParserHelper { - private OldMapiTupleLineParserHelper() {} + private 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; - } + /** + * 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); + 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++); + 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; - } + if (j == end) { + /* Found whole string. */ + return i - sourceOffset; + } + } + } + return -1; + } - /** - * The character array representation of a TRUE value. - */ - private static final char[] TRUE_CONSTANT = new char[]{'t','r','u','e'}; + /** + * The character array representation of a TRUE value. + */ + private static final char[] TRUE_CONSTANT = 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, TRUE_CONSTANT, 0, 4, start) - == start ? (byte)1 : (byte)0; - } + /** + * 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, TRUE_CONSTANT, 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++]; + /** + * 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; - } + 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++]; + /** + * 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; - } + 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++]; + /** + * 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; - } + 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++]; + /** + * 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; - } + 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; + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/AbstractDataBlockResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/AbstractDataBlockResponse.java @@ -28,185 +28,185 @@ import java.util.Calendar; */ 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; + /** 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; - } + /** + * 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(); + /** + * 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; + /** + * 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 */ + /* 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; - } + /** + * 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; - } + /** + * 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 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 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 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 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 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 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 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 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 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; + /** + * 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 2013: //Types.TIME_WITH_TIMEZONE: - aux = protocol.getMonetTimePrinter(); - break; - case Types.TIMESTAMP: - case 2014: //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); - } - } + /** + * 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 2013: //Types.TIME_WITH_TIMEZONE: + aux = protocol.getMonetTimePrinter(); + break; + case Types.TIMESTAMP: + case 2014: //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(); + /** + * Get the last parsed nanoseconds value. + * + * @return The last parsed nanoseconds value + */ + public abstract int getLastNanos(); }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/AutoCommitResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/AutoCommitResponse.java @@ -17,22 +17,22 @@ package nl.cwi.monetdb.mcl.responses; */ public class AutoCommitResponse extends SchemaResponse { - /** - * If the auto commit mode is set. - */ - private final boolean autocommit; + /** + * If the auto commit mode is set. + */ + private final boolean autocommit; - public AutoCommitResponse(boolean ac) { - // fill the blank final - this.autocommit = ac; - } + public AutoCommitResponse(boolean ac) { + // fill the blank final + this.autocommit = ac; + } - /** - * The auto commit value - * - * @return The auto commit value - */ - public boolean isAutocommit() { - return autocommit; - } + /** + * The auto commit value + * + * @return The auto commit value + */ + public boolean isAutocommit() { + return autocommit; + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/IIncompleteResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/IIncompleteResponse.java @@ -19,18 +19,18 @@ import nl.cwi.monetdb.mcl.protocol.Proto */ public interface IIncompleteResponse extends IResponse { - /** - * Returns whether this Response expects more lines to be added to it. - * - * @return true if a next line should be added, false otherwise - */ - boolean wantsMore(); + /** + * Returns whether this Response expects more lines to be added to it. + * + * @return true if a next line should be added, false otherwise + */ + boolean wantsMore(); - /** - * Adds a batch of data to the Response instance. - * - * @param protocol The connection's protocol to fetch data from - * @throws ProtocolException If the result line is not expected - */ - void addLines(AbstractProtocol protocol) throws ProtocolException; + /** + * Adds a batch of data to the Response instance. + * + * @param protocol The connection's protocol to fetch data from + * @throws ProtocolException If the result line is not expected + */ + void addLines(AbstractProtocol protocol) throws ProtocolException; }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/IResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/IResponse.java @@ -16,8 +16,8 @@ package nl.cwi.monetdb.mcl.responses; */ public interface IResponse { - /** - * Instructs the Response implementation to close and do the necessary clean up procedures. - */ - void close(); + /** + * Instructs the Response implementation to close and do the necessary clean up procedures. + */ + void close(); }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/ResultSetResponse.java @@ -35,372 +35,372 @@ import java.sql.Types; */ public class ResultSetResponse implements IIncompleteResponse { - /** The expected final value after the table headers are set. */ - private static final byte IS_SET_FINAL_VALUE = 15; + /** The expected final value after the table headers are set. */ + private static final byte IS_SET_FINAL_VALUE = 15; - /** The number of rows in the current block */ - private final int rowcount; - /** The total number of rows this result set has */ - private final int tuplecount; - /** The numbers of rows to retrieve per DataBlockResponse */ - private int cacheSize; - /** The table ID of this result */ - private final int id; - /** The names of the columns in this result */ - private final String[] name; - /** The types of the columns in this result */ - private final String[] type; - /** The JDBC SQL types of the columns in this ResultSet. The content will be derived from the MonetDB types[] */ - private final int[] JdbcSQLTypes; - /** The max string length for each column in this result */ - private final int[] columnLengths; - /** The table for each column in this result */ - private final String[] tableNames; - /** The query sequence number */ - private final int seqnr; - /** A bitmap telling whether the headers are set or not */ - private byte isSet; - /** Whether this Response is closed */ - private boolean closed; - /** The connection belonging for this ResultSetResponse */ - private MonetConnection con; - /** The Connection that we should use when requesting a new block */ - private MonetConnection.ResponseList parent; - /** Whether the fetchSize was explicitly set by the user */ - private boolean cacheSizeSetExplicitly = false; - /** Whether we should send an Xclose command to the server if we close this Response */ - private boolean destroyOnClose; - /** the offset to be used on Xexport queries */ - private int blockOffset = 0; - /** A List of result blocks (chunks of size fetchSize/cacheSize) */ - private final AbstractDataBlockResponse[] resultBlocks; + /** The number of rows in the current block */ + private final int rowcount; + /** The total number of rows this result set has */ + private final int tuplecount; + /** The numbers of rows to retrieve per DataBlockResponse */ + private int cacheSize; + /** The table ID of this result */ + private final int id; + /** The names of the columns in this result */ + private final String[] name; + /** The types of the columns in this result */ + private final String[] type; + /** The JDBC SQL types of the columns in this ResultSet. The content will be derived from the MonetDB types[] */ + private final int[] JdbcSQLTypes; + /** The max string length for each column in this result */ + private final int[] columnLengths; + /** The table for each column in this result */ + private final String[] tableNames; + /** The query sequence number */ + private final int seqnr; + /** A bitmap telling whether the headers are set or not */ + private byte isSet; + /** Whether this Response is closed */ + private boolean closed; + /** The connection belonging for this ResultSetResponse */ + private MonetConnection con; + /** The Connection that we should use when requesting a new block */ + private MonetConnection.ResponseList parent; + /** Whether the fetchSize was explicitly set by the user */ + private boolean cacheSizeSetExplicitly = false; + /** Whether we should send an Xclose command to the server if we close this Response */ + private boolean destroyOnClose; + /** the offset to be used on Xexport queries */ + private int blockOffset = 0; + /** A List of result blocks (chunks of size fetchSize/cacheSize) */ + private final AbstractDataBlockResponse[] resultBlocks; - /** - * Sole constructor, which requires a MonetConnection parent to be given. - * - * @param con The connection of this ResultSet - * @param parent the parent that created this Response and will supply new result blocks when necessary - * @param id the ID of the result set - * @param seq the query sequence number - * @param rowcount the number of rows in the current block - * @param tuplecount the total number of tuples in the result set - * @param columncount the number of columns in the result set - */ - public ResultSetResponse(MonetConnection con, MonetConnection.ResponseList parent, int id, int seq, int rowcount, - int tuplecount, int columncount) { - this.con = con; - this.parent = parent; - if (parent.getCachesize() == 0) { - /* Below we have to calculate how many "chunks" we need to allocate to store the entire result. However, if - the user didn't set a cache size, as in this case, we need to stick to our defaults. */ - cacheSize = con.getDefFetchsize(); - cacheSizeSetExplicitly = false; - } else { - cacheSize = parent.getCachesize(); - cacheSizeSetExplicitly = true; - } + /** + * Sole constructor, which requires a MonetConnection parent to be given. + * + * @param con The connection of this ResultSet + * @param parent the parent that created this Response and will supply new result blocks when necessary + * @param id the ID of the result set + * @param seq the query sequence number + * @param rowcount the number of rows in the current block + * @param tuplecount the total number of tuples in the result set + * @param columncount the number of columns in the result set + */ + public ResultSetResponse(MonetConnection con, MonetConnection.ResponseList parent, int id, int seq, int rowcount, + int tuplecount, int columncount) { + this.con = con; + this.parent = parent; + if (parent.getCachesize() == 0) { + /* Below we have to calculate how many "chunks" we need to allocate to store the entire result. However, if + the user didn't set a cache size, as in this case, we need to stick to our defaults. */ + cacheSize = con.getDefFetchsize(); + cacheSizeSetExplicitly = false; + } else { + cacheSize = parent.getCachesize(); + cacheSizeSetExplicitly = true; + } - /* So far, so good. Now the problem with EXPLAIN, DOT, etc queries is, that they don't support any block - fetching, so we need to always fetch everything at once. For that reason, the cache size is here set to the - rowcount if it's larger, such that we do a full fetch at once. (Because we always set a reply_size, we can - only get a larger rowcount from the server if it doesn't paginate, because it's a pseudo SQL result.) */ - if (rowcount > cacheSize) { - cacheSize = rowcount; - } - this.seqnr = seq; - this.destroyOnClose = id > 0 && tuplecount > rowcount; - this.id = id; - this.rowcount = rowcount; + /* So far, so good. Now the problem with EXPLAIN, DOT, etc queries is, that they don't support any block + fetching, so we need to always fetch everything at once. For that reason, the cache size is here set to the + rowcount if it's larger, such that we do a full fetch at once. (Because we always set a reply_size, we can + only get a larger rowcount from the server if it doesn't paginate, because it's a pseudo SQL result.) */ + if (rowcount > cacheSize) { + cacheSize = rowcount; + } + this.seqnr = seq; + this.destroyOnClose = id > 0 && tuplecount > rowcount; + this.id = id; + this.rowcount = rowcount; - int maxrows = parent.getMaxrows(); - this.tuplecount = (maxrows != 0 && tuplecount > maxrows) ? maxrows : tuplecount; + int maxrows = parent.getMaxrows(); + this.tuplecount = (maxrows != 0 && tuplecount > maxrows) ? maxrows : tuplecount; - this.name = new String[columncount]; - this.type = new String[columncount]; - this.tableNames = new String[columncount]; - this.columnLengths = new int[columncount]; - this.JdbcSQLTypes = new int[columncount]; + this.name = new String[columncount]; + this.type = new String[columncount]; + this.tableNames = new String[columncount]; + this.columnLengths = new int[columncount]; + this.JdbcSQLTypes = new int[columncount]; - this.resultBlocks = new AbstractDataBlockResponse[(tuplecount / cacheSize) + 1]; - this.resultBlocks[0] = con.getProtocol().getAnEmptyDataBlockResponse(rowcount, columncount, - con.getProtocol(), this.JdbcSQLTypes); - } + this.resultBlocks = new AbstractDataBlockResponse[(tuplecount / cacheSize) + 1]; + this.resultBlocks[0] = con.getProtocol().getAnEmptyDataBlockResponse(rowcount, columncount, + con.getProtocol(), this.JdbcSQLTypes); + } - /** - * Internal utility method to fill the JdbcSQLTypes array with derivable values. By doing it once (in the - * constructor) we can avoid doing this in many getXyz() methods again and again thereby improving getXyz() method - * performance. - */ - private void populateJdbcSQLTypesArray() { - for (int i = 0; i < this.type.length; i++) { - int javaSQLtype = MonetDriver.getJavaType(this.type[i]); - if (javaSQLtype == Types.BLOB && con.getBlobAsBinary()) { - javaSQLtype = Types.LONGVARBINARY; - } - if (javaSQLtype == Types.CLOB && con.getClobAsLongChar()) { - javaSQLtype = Types.LONGVARCHAR; - } - this.JdbcSQLTypes[i] = javaSQLtype; - } - } + /** + * Internal utility method to fill the JdbcSQLTypes array with derivable values. By doing it once (in the + * constructor) we can avoid doing this in many getXyz() methods again and again thereby improving getXyz() method + * performance. + */ + private void populateJdbcSQLTypesArray() { + for (int i = 0; i < this.type.length; i++) { + int javaSQLtype = MonetDriver.getJavaType(this.type[i]); + if (javaSQLtype == Types.BLOB && con.getBlobAsBinary()) { + javaSQLtype = Types.LONGVARBINARY; + } + if (javaSQLtype == Types.CLOB && con.getClobAsLongChar()) { + javaSQLtype = Types.LONGVARCHAR; + } + this.JdbcSQLTypes[i] = javaSQLtype; + } + } - /** - * Returns whether this ResultSetResponse needs more lines. This method returns true if not all headers are set, - * or the first DataBlockResponse reports to want more. - */ - @Override - public boolean wantsMore() { - return this.isSet < IS_SET_FINAL_VALUE || resultBlocks[0].wantsMore(); - } + /** + * Returns whether this ResultSetResponse needs more lines. This method returns true if not all headers are set, + * or the first DataBlockResponse reports to want more. + */ + @Override + public boolean wantsMore() { + return this.isSet < IS_SET_FINAL_VALUE || resultBlocks[0].wantsMore(); + } - /** - * Adds the given DataBlockResponse to this ResultSetResponse at the given block position. - * - * @param offset the offset number of rows for this block - * @param rowcount the number of rows for this block - */ - public AbstractDataBlockResponse addDataBlockResponse(int offset, int rowcount) { - int block = (offset - blockOffset) / cacheSize; - AbstractDataBlockResponse res = con.getProtocol().getAnEmptyDataBlockResponse(rowcount, - this.getColumncount(), this.con.getProtocol(), JdbcSQLTypes); - resultBlocks[block] = res; - return res; - } + /** + * Adds the given DataBlockResponse to this ResultSetResponse at the given block position. + * + * @param offset the offset number of rows for this block + * @param rowcount the number of rows for this block + */ + public AbstractDataBlockResponse addDataBlockResponse(int offset, int rowcount) { + int block = (offset - blockOffset) / cacheSize; + AbstractDataBlockResponse res = con.getProtocol().getAnEmptyDataBlockResponse(rowcount, + this.getColumncount(), this.con.getProtocol(), JdbcSQLTypes); + resultBlocks[block] = res; + return res; + } - /** - * Returns this ResultSet id - * - * @return The resultSet id - */ - public int getId() { - return id; - } + /** + * Returns this ResultSet id + * + * @return The resultSet id + */ + public int getId() { + return id; + } - /** - * Returns the number of columns on this ResultSet - * - * @return The number of columns on this ResultSet - */ - public int getColumncount() { - return name.length; - } + /** + * Returns the number of columns on this ResultSet + * + * @return The number of columns on this ResultSet + */ + public int getColumncount() { + return name.length; + } - /** - * Returns the number of rows on this ResultSet - * - * @return The number of rows on this ResultSet - */ - public int getTuplecount() { - return tuplecount; - } + /** + * Returns the number of rows on this ResultSet + * + * @return The number of rows on this ResultSet + */ + public int getTuplecount() { + return tuplecount; + } - /** - * Returns the number of rows on the current block - * - * @return The number of rows on the current block - */ - public int getRowcount() { - return rowcount; - } + /** + * Returns the number of rows on the current block + * + * @return The number of rows on the current block + */ + public int getRowcount() { + return rowcount; + } - /** - * Returns the names of the columns - * - * @return The names of the columns - */ - public String[] getNames() { - return name; - } + /** + * Returns the names of the columns + * + * @return The names of the columns + */ + public String[] getNames() { + return name; + } - /** - * Returns the types of the columns - * - * @return The types of the columns - */ - public String[] getTypes() { - return type; - } + /** + * Returns the types of the columns + * + * @return The types of the columns + */ + public String[] getTypes() { + return type; + } - /** - * Returns the JDBC types of the columns - * - * @return The JDBC types of the columns - */ - public int[] getJdbcSQLTypes() { - return JdbcSQLTypes; - } + /** + * Returns the JDBC types of the columns + * + * @return The JDBC types of the columns + */ + public int[] getJdbcSQLTypes() { + return JdbcSQLTypes; + } - /** - * Returns the tables of the columns - * - * @return The tables of the columns - */ - public String[] getTableNames() { - return tableNames; - } + /** + * Returns the tables of the columns + * + * @return The tables of the columns + */ + public String[] getTableNames() { + return tableNames; + } - /** - * Returns the lengths of the columns - * - * @return The lengths of the columns - */ - public int[] getColumnLengths() { - return columnLengths; - } + /** + * Returns the lengths of the columns + * + * @return The lengths of the columns + */ + public int[] getColumnLengths() { + return columnLengths; + } - /** - * Returns the cache size used within this Response - * - * @return The cache size - */ - public int getCacheSize() { - return cacheSize; - } + /** + * Returns the cache size used within this Response + * + * @return The cache size + */ + public int getCacheSize() { + return cacheSize; + } - /** - * Returns the current block offset - * - * @return The current block offset - */ - public int getBlockOffset() { - return blockOffset; - } + /** + * Returns the current block offset + * + * @return The current block offset + */ + public int getBlockOffset() { + return blockOffset; + } - /** - * Returns the ResultSet type, FORWARD_ONLY or not. - * - * @return The ResultSet type - */ - public int getRSType() { - return parent.getRstype(); - } + /** + * Returns the ResultSet type, FORWARD_ONLY or not. + * + * @return The ResultSet type + */ + public int getRSType() { + return parent.getRstype(); + } - /** - * Returns the concurrency of the ResultSet. - * - * @return The ResultSet concurrency - */ - public int getRSConcur() { - return parent.getRsconcur(); - } + /** + * Returns the concurrency of the ResultSet. + * + * @return The ResultSet concurrency + */ + public int getRSConcur() { + return parent.getRsconcur(); + } - /** - * Gets the next table headers from the underlying protocol, or gets the next rows on to the underlying - * DataResponse if the headers are already retrieved. - * - * @param protocol the connection's protocol - * @throws ProtocolException if has a wrong header - */ - @Override - public void addLines(AbstractProtocol protocol) throws ProtocolException { - if (this.isSet >= IS_SET_FINAL_VALUE) { - this.resultBlocks[0].addLines(protocol); - } else { - int csrh = protocol.getCurrentServerResponse(); - if (csrh != ServerResponses.HEADER) { - throw new ProtocolException("header expected, got: " + protocol.getRemainingStringLine(0)); - } else { - int next = con.getProtocol().getNextTableHeader(this.name, this.columnLengths, this.type, this.tableNames); - this.isSet |= next; - if (this.isSet >= IS_SET_FINAL_VALUE) { - this.populateJdbcSQLTypesArray(); //VERY IMPORTANT to populate the JDBC types array - } - } - } - } + /** + * Gets the next table headers from the underlying protocol, or gets the next rows on to the underlying + * DataResponse if the headers are already retrieved. + * + * @param protocol the connection's protocol + * @throws ProtocolException if has a wrong header + */ + @Override + public void addLines(AbstractProtocol protocol) throws ProtocolException { + if (this.isSet >= IS_SET_FINAL_VALUE) { + this.resultBlocks[0].addLines(protocol); + } else { + int csrh = protocol.getCurrentServerResponse(); + if (csrh != ServerResponses.HEADER) { + throw new ProtocolException("header expected, got: " + protocol.getRemainingStringLine(0)); + } else { + int next = con.getProtocol().getNextTableHeader(this.name, this.columnLengths, this.type, this.tableNames); + this.isSet |= next; + if (this.isSet >= IS_SET_FINAL_VALUE) { + this.populateJdbcSQLTypesArray(); //VERY IMPORTANT to populate the JDBC types array + } + } + } + } - /** - * Returns a line from the cache. If the line is already present in the cache, it is returned, if not appropriate - * actions are taken to make sure the right block is being fetched and as soon as the requested line is fetched it - * is returned. - * - * @param row the row in the result set to return - * @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 AbstractDataBlockResponse getDataBlockCorrespondingToLine(int row) throws SQLException { - if (row >= tuplecount || row < 0) - return null; + /** + * Returns a line from the cache. If the line is already present in the cache, it is returned, if not appropriate + * actions are taken to make sure the right block is being fetched and as soon as the requested line is fetched it + * is returned. + * + * @param row the row in the result set to return + * @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 AbstractDataBlockResponse getDataBlockCorrespondingToLine(int row) throws SQLException { + if (row >= tuplecount || row < 0) + return null; - int block = (row - blockOffset) / cacheSize; - int blockLine = (row - blockOffset) % cacheSize; + int block = (row - blockOffset) / cacheSize; + int blockLine = (row - blockOffset) % cacheSize; - // do we have the right block loaded? (optimistic try) - 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 - // reduce memory blow-up + // do we have the right block loaded? (optimistic try) + 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 + // reduce memory blow-up - // if we're running forward only, we can discard the resultset block loaded - if (parent.getRstype() == ResultSet.TYPE_FORWARD_ONLY) { - for (int i = 0; i < block; i++) - resultBlocks[i] = null; + // if we're running forward only, we can discard the resultset block loaded + if (parent.getRstype() == ResultSet.TYPE_FORWARD_ONLY) { + for (int i = 0; i < block; i++) + resultBlocks[i] = null; - if (MonetConnection.getSeqCounter() - 1 == seqnr && !cacheSizeSetExplicitly && - tuplecount - row > cacheSize && cacheSize < con.getDefFetchsize() * 10) { - // there has no query been issued after this one, so we can consider this an uninterrupted - // continuation request. Let's once increase the cacheSize as it was not explicitly set, - // since the chances are high that we won't bother anyone else by doing so, and just gaining - // some performance. + if (MonetConnection.getSeqCounter() - 1 == seqnr && !cacheSizeSetExplicitly && + tuplecount - row > cacheSize && cacheSize < con.getDefFetchsize() * 10) { + // there has no query been issued after this one, so we can consider this an uninterrupted + // continuation request. Let's once increase the cacheSize as it was not explicitly set, + // since the chances are high that we won't bother anyone else by doing so, and just gaining + // some performance. - // store the previous position in the blockOffset variable - blockOffset += cacheSize; + // store the previous position in the blockOffset variable + blockOffset += cacheSize; - // increase the cache size (a lot) - cacheSize *= 10; + // increase the cache size (a lot) + cacheSize *= 10; - // by changing the cacheSize, we also change the block measures. Luckily we don't care about - // previous blocks because we have a forward running pointer only. However, we do have to - // recalculate the block number, to ensure the next call to find this new block. - block = (row - blockOffset) / cacheSize; - blockLine = (row - blockOffset) % cacheSize; - } - } + // by changing the cacheSize, we also change the block measures. Luckily we don't care about + // previous blocks because we have a forward running pointer only. However, we do have to + // recalculate the block number, to ensure the next call to find this new block. + block = (row - blockOffset) / cacheSize; + blockLine = (row - blockOffset) % cacheSize; + } + } - // ok, need to fetch cache block first - parent.executeQuery(con.getLanguage().getCommandTemplates(), "export " + id + " " - + ((block * cacheSize) + blockOffset) + " " + cacheSize); - rawr = resultBlocks[block]; - if (rawr == null) { - throw new AssertionError("block " + block + " should have been fetched by now :("); - } - } - rawr.setBlockLine(blockLine); - return rawr; - } + // ok, need to fetch cache block first + parent.executeQuery(con.getLanguage().getCommandTemplates(), "export " + id + " " + + ((block * cacheSize) + blockOffset) + " " + cacheSize); + rawr = resultBlocks[block]; + if (rawr == null) { + throw new AssertionError("block " + block + " should have been fetched by now :("); + } + } + rawr.setBlockLine(blockLine); + return rawr; + } - /** - * Closes this Response by sending an Xclose to the server indicating that the result can be closed at the server - * side as well. - */ - @Override - public void close() { - if (closed) return; - // send command to server indicating we're done with this result only if we had an ID in the header and this - // result was larger than the reply size - try { - if (destroyOnClose) { - con.sendControlCommand(ControlCommands.CLOSE, id); - } - } catch (SQLException e) { - // probably a connection error... - } + /** + * Closes this Response by sending an Xclose to the server indicating that the result can be closed at the server + * side as well. + */ + @Override + public void close() { + if (closed) return; + // send command to server indicating we're done with this result only if we had an ID in the header and this + // result was larger than the reply size + try { + if (destroyOnClose) { + con.sendControlCommand(ControlCommands.CLOSE, id); + } + } catch (SQLException e) { + // probably a connection error... + } - // close the data block associated with us - for (AbstractDataBlockResponse r : resultBlocks) { - if (r != null) r.close(); - } + // close the data block associated with us + for (AbstractDataBlockResponse r : resultBlocks) { + if (r != null) r.close(); + } - closed = true; - } + closed = true; + } - /** - * Returns whether this Response is closed - * - * @return whether this Response is closed - */ - public boolean isClosed() { - return closed; - } + /** + * Returns whether this Response is closed + * + * @return whether this Response is closed + */ + public boolean isClosed() { + return closed; + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/SchemaResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/SchemaResponse.java @@ -21,12 +21,12 @@ import java.sql.Statement; */ public class SchemaResponse implements IResponse { - public int getState() { - return Statement.SUCCESS_NO_INFO; - } + public int getState() { + return Statement.SUCCESS_NO_INFO; + } - @Override - public void close() { - // nothing to do here... - } + @Override + public void close() { + // nothing to do here... + } }
--- a/src/main/java/nl/cwi/monetdb/mcl/responses/UpdateResponse.java +++ b/src/main/java/nl/cwi/monetdb/mcl/responses/UpdateResponse.java @@ -19,42 +19,42 @@ package nl.cwi.monetdb.mcl.responses; */ public class UpdateResponse implements IResponse { - /** - * The id of the response before this one. - */ - private final int lastid; + /** + * The id of the response before this one. + */ + private final int lastid; - /** - * The number of rows affected by the update statement. - */ - private final int count; + /** + * The number of rows affected by the update statement. + */ + private final int count; - public UpdateResponse(int lastid, int count) { - // fill the blank finals - this.lastid = lastid; - this.count = count; - } + public UpdateResponse(int lastid, int count) { + // fill the blank finals + this.lastid = lastid; + this.count = count; + } - /** - * The id of the response before this one. - * - * @return The id of the response before this one - */ - public int getLastid() { - return lastid; - } + /** + * The id of the response before this one. + * + * @return The id of the response before this one + */ + public int getLastid() { + return lastid; + } - /** - * The number of rows affected by the update statement. - * - * @return The number of rows affected by the update statement - */ - public int getCount() { - return count; - } + /** + * The number of rows affected by the update statement. + * + * @return The number of rows affected by the update statement + */ + public int getCount() { + return count; + } - @Override - public void close() { - // nothing to do here... - } + @Override + public void close() { + // nothing to do here... + } }