📄 monetresultset.java
字号:
/* * The contents of this file are subject to the MonetDB Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://monetdb.cwi.nl/Legal/MonetDBLicense-1.1.html * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is the MonetDB Database System. * * The Initial Developer of the Original Code is CWI. * Portions created by CWI are Copyright (C) 1997-2007 CWI. * All Rights Reserved. */package nl.cwi.monetdb.jdbc;import java.sql.*;import java.io.*;import java.util.*;import java.math.*;import java.net.*;/** * A ResultSet suitable for the MonetDB database. * <br /><br /> * A table of data representing a database result set, which is usually * generated by executing a statement that queries the database. * <br /><br /> * A ResultSet object maintains a cursor pointing to its current row of data. * Initially the cursor is positioned before the first row. The next method * moves the cursor to the next row, and because it returns false when there * are no more rows in the ResultSet object, it can be used in a while loop to * iterate through the result set. * <br /><br /> * The current state of this ResultSet is that it supports positioning in the * result set, absolute and relative. A slight performance difference between * FORWARD_ONLY or result sets scrollable in both directions can be noticed as * for FORWARD_ONLY result sets the memory usage will be likely lower for large * result sets. * * @author Fabian Groffen <Fabian.Groffen@cwi.nl> * @version 0.7 */public class MonetResultSet implements ResultSet { /** The last column read using some getXXX function */ private int lastColumnRead = -1; // the following have default access modifier for the MonetVirtualResultSet /** The current line of the buffer split in columns */ String[] result; /** The current position of the cursor for this ResultSet object */ int curRow = 0; // a blank final is immutable once assigned in the constructor /** A Header to retrieve lines from */ private final MonetConnection.ResultSetResponse header; /** The names of the columns in this ResultSet */ private final String[] columns; /** The MonetDB types of the columns in this ResultSet */ private final String[] types; /** The id of this ResultSet (needed for closing) */ private final String tableID; /** The number of rows in this ResultSet */ final int tupleCount; // default for the MonetVirtualResultSet /** The parental Statement object */ private final Statement statement; /** The type of this ResultSet (forward or scrollable) */ private int type = TYPE_FORWARD_ONLY; /** The concurrency of this ResultSet (currently only read-only) */ private int concurrency = CONCUR_READ_ONLY; /** The warnings for this ResultSet object */ private SQLWarning warnings; /** * Main constructor backed by the given Header. * * @param statement the statement which created this ResultSet * @param header a header containing the query, resultset type, etc. * @throws SQLException is a protocol error occurs */ MonetResultSet( Statement statement, MonetConnection.ResultSetResponse header) throws SQLException { this.statement = statement; this.header = header; this.type = header.getRSType(); this.concurrency = header.getRSConcur(); // well there is only one supported concurrency, so we don't have to // bother about that // throws SQLException on getters of Header, so we find out immediately // if an error occurred for this query columns = header.getNames(); types = header.getTypes(); tableID = "" + header.id; tupleCount = header.tuplecount; // create result array result = new String[columns.length]; } /** * Constructor used by MonetFillableResultSet. * DO NOT USE THIS CONSTRUCTOR IF YOU ARE NOT EXTENDING THIS OBJECT! * * @param columns the column names * @param types the column types * @param results the number of rows in the ResultSet * @throws IOException if communicating with monet failed * @throws SQLException is a protocol error occurs */ MonetResultSet( String[] columns, String[] types, int results ) throws IllegalArgumentException { if (columns == null || types == null) { throw new IllegalArgumentException("One of the given arguments is null!"); } if (columns.length != types.length) { throw new IllegalArgumentException("Given arguments are not the same size!"); } if (results < 0) { throw new IllegalArgumentException("Negative rowcount not allowed!"); } this.header = null; this.tableID = null; this.statement = null; // no parent, required for specs this.columns = columns; this.types = types; this.tupleCount = results; } //== methods of interface ResultSet // Chapter 14.2.2 Sun JDBC 3.0 Specification /** * Moves the cursor to the given row number in this ResultSet object. * <br /><br /> * If the row number is positive, the cursor moves to the given row number * with respect to the beginning of the result set. The first row is row 1, * the second is row 2, and so on. * <br /><br /> * If the given row number is negative, the cursor moves to an absolute row * position with respect to the end of the result set. For example, calling * the method absolute(-1) positions the cursor on the last row; calling the * method absolute(-2) moves the cursor to the next-to-last row, and so on. * <br /><br /> * An attempt to position the cursor beyond the first/last row in the result * set leaves the cursor before the first row or after the last row. * Note: calling absolute(1) is the same as calling first(). Calling * absolute(-1) is the same as calling last(). * * @param row the number of the row to which the cursor should move. A * positive number indicates the row number counting from the * beginning of the result set; a negative number indicates the row * number counting from the end of the result set * @return true if the cursor is on the result set; false otherwise * @throws SQLException if a database access error occurs, or the result set * type is TYPE_FORWARD_ONLY */ public boolean absolute(int row) throws SQLException { if (row != curRow + 1 && type == TYPE_FORWARD_ONLY) throw new SQLException("(Absolute) positioning not allowed on forward " + " only result sets!"); if (header.isClosed()) throw new SQLException("ResultSet is closed!"); // first calculate what the JDBC row is if (row < 0) { // calculate the negatives... row = tupleCount + row + 1; } // now place the row not farther than just before or after the result if (row < 0) row = 0; // before first else if (row > tupleCount + 1) row = tupleCount + 1; // after last String tmpLine = header.getLine(row - 1); // store it curRow = row; if (tmpLine == null) return(false); int len = tmpLine.length(); char[] chrLine = new char[len]; tmpLine.getChars(0, len, chrLine, 0); // extract separate fields by examining string, char for char boolean inString = false, escaped = false; int cursor = 2, column = 0, i = 2; StringBuffer uesc = new StringBuffer(); for (; i < len; i++) { switch(chrLine[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 && chrLine[i - 1] == ',') || (i + 1 == len - 1 && chrLine[++i] == ']')) // dirty { // split! if (chrLine[cursor] == '"' && chrLine[i - 2] == '"') { // reuse the StringBuffer by cleaning it uesc.delete(0, uesc.length()); // prevent capacity increasements uesc.ensureCapacity((i - 2) - (cursor + 1)); for (int pos = cursor + 1; pos < i - 2; pos++) { if (chrLine[pos] == '\\' && pos + 1 < i - 2) { pos++; // strToStr and strFromStr in gdk_atoms.mx only // support \t \n \\ \" and \377 switch (chrLine[pos]) { case '\\': uesc.append('\\'); break; case 'n': uesc.append('\n'); break; case 't': uesc.append('\t'); break; case '"': uesc.append('"'); 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 && chrLine[pos + 1] >= '0' && chrLine[pos + 1] <= '7' && chrLine[pos + 2] >= '0' && chrLine[pos + 2] <= '7' ) { // we got the number! try { uesc.append((char)(Integer.parseInt("" + chrLine[pos] + chrLine[pos + 1] + chrLine[pos + 2], 8))); pos += 2; break; } catch (NumberFormatException e) { // hmmm, this point should never be reached actually... throw new AssertionError("Flow error, should never try to parse non-number"); } } // do default action if number seems not to be correct default: // this is wrong, just ignore the escape, and print the char uesc.append(chrLine[pos]); break; } } else { uesc.append(chrLine[pos]); } } // put the unescaped string in the right place result[column++] = uesc.toString(); } else if ((i - 1) - cursor == 4 && tmpLine.indexOf("NULL", cursor) == cursor) { result[column++] = null; } else { result[column++] = tmpLine.substring(cursor, i - 1); } cursor = i + 1; } // reset escaped flag escaped = false; break; } } // check if this result is of the size we expected it to be if (column != columns.length) throw new AssertionError("Illegal result length: " + column + "\nlast read: " + (column > 0 ? result[column - 1] : "<none>")); // reset lastColumnRead lastColumnRead = -1; return(true); } /** * Moves the cursor to the end of this ResultSet object, just after the last * row. This method has no effect if the result set contains no rows. * * @throws SQLException if a database access error occurs or the result set * type is TYPE_FORWARD_ONLY */ public void afterLast() throws SQLException { absolute(tupleCount + 1); } /** * Moves the cursor to the front of this ResultSet object, just before the * first row. This method has no effect if the result set contains no rows. * * @throws SQLException if a database access error occurs or the result set * type is TYPE_FORWARD_ONLY */ public void beforeFirst() throws SQLException { absolute(0); } /** * Clears all warnings reported for this ResultSet object. After a call to * this method, the method getWarnings returns null until a new warning is * reported for this ResultSet object. */ public void clearWarnings() { warnings = null; } /** * Releases this ResultSet object's database (and JDBC) resources * immediately instead of waiting for this to happen when it is * automatically closed. */ public void close() { if (!header.isClosed()) { header.close(); } } // Chapter 14.2.3 from Sun JDBC 3.0 specification /** * Maps the given ResultSet column name to its ResultSet column index. * Column names supplied to getter methods are case insensitive. If a select * list contains the same column more than once, the first instance of the * column will be returned. * * @param columnName the name of the column * @return the column index of the given column name * @throws SQLException if the ResultSet object does not contain columnName */ public int findColumn(String columnName) throws SQLException { for (int i = 0; i < columns.length; i++) { if (columns[i].equalsIgnoreCase(columnName)) return(i + 1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -