Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
jTDS JDBC Driver Source Code Files
jTDS JDBC Driver Source Code Files are provided in the source package file, jtds-1.3.1-fyi.zip.
You can browse jTDS JDBC Driver Source Code files below:
✍: FYIcenter.com
⏎ net/sourceforge/jtds/jdbc/CachedResultSet.java
//jTDS JDBC Driver for Microsoft SQL Server and Sybase //Copyright (C) 2004 The jTDS Project // //This library is free software; you can redistribute it and/or //modify it under the terms of the GNU Lesser General Public //License as published by the Free Software Foundation; either //version 2.1 of the License, or (at your option) any later version. // //This library is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //Lesser General Public License for more details. // //You should have received a copy of the GNU Lesser General Public //License along with this library; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // package net.sourceforge.jtds.jdbc; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Types; import java.util.ArrayList; import java.util.HashSet; /** * A memory cached scrollable/updateable result set. * <p/> * Notes: * <ol> * <li>For maximum performance use the scroll insensitive result set type. * <li>As the result set is cached in memory this implementation is limited * to small result sets. * <li>Updateable or scroll sensitive result sets are limited to selects * which reference one table only. * <li>Scroll sensitive result sets must have primary keys. * <li>Updates are optimistic. To guard against lost updates it is * recommended that the table includes a timestamp column. * <li>This class is a plug-in replacement for the MSCursorResultSet class * which may be advantageous in certain applications as the scroll * insensitive result set implemented here is much faster than the server * side cursor. * <li>Updateable result sets cannot be built from the output of stored * procedures. * <li>This implementation uses 'select ... for browse' to obtain the column * meta data needed to generate update statements etc. * <li>Named forward updateable cursors are also supported in which case * positioned updates and deletes are used referencing a server side * declared cursor. * <li>Named forward read only declared cursors can have a larger fetch size * specified allowing a cursor alternative to the default direct select * method. * </ol> * * @author Mike Hutchinson * @version $Id: CachedResultSet.java,v 1.26 2007-07-08 17:28:23 bheineman Exp $ * @todo Should add a "close statement" flag to the constructors */ public class CachedResultSet extends JtdsResultSet { /** Indicates currently inserting. */ protected boolean onInsertRow; /** Buffer row used for inserts. */ protected ParamInfo[] insertRow; /** The "update" row. */ protected ParamInfo[] updateRow; // FIXME Remember if the row was updated/deleted for each row in the ResultSet /** Indicates that row has been updated. */ protected boolean rowUpdated; /** Indicates that row has been deleted. */ protected boolean rowDeleted; /** True if this is a local temporary result set. */ protected final boolean tempResultSet; /** Cursor TdsCore object. */ protected final TdsCore cursorTds; /** Updates TdsCore object used for positioned updates. */ protected final TdsCore updateTds; /** Flag to indicate Sybase. */ protected boolean isSybase; /** Fetch size has been changed. */ protected boolean sizeChanged; /** Original SQL statement. */ protected String sql; /** Original procedure name. */ protected final String procName; /** Original parameters. */ protected final ParamInfo[] procedureParams; /** Table is keyed. */ protected boolean isKeyed; /** First table name in select. */ protected String tableName; /** The parent connection object */ protected JtdsConnection connection; /** * Constructs a new cached result set. * <p/> * This result set will either be cached in memory or, if the cursor name * is set, can be a forward only server side cursor. This latter form of * cursor can also support positioned updates. * * @param statement the parent statement object * @param sql the SQL statement used to build the result set * @param procName an optional stored procedure name * @param procedureParams parameters for prepared statements * @param resultSetType the result set type eg scrollable * @param concurrency the result set concurrency eg updateable * @exception SQLException if an error occurs */ CachedResultSet(JtdsStatement statement, String sql, String procName, ParamInfo[] procedureParams, int resultSetType, int concurrency) throws SQLException { super(statement, resultSetType, concurrency, null); connection = (JtdsConnection) statement.getConnection(); cursorTds = statement.getTds(); this.sql = sql; this.procName = procName; this.procedureParams = procedureParams; if (resultSetType == ResultSet.TYPE_FORWARD_ONLY && concurrency != ResultSet.CONCUR_READ_ONLY && cursorName != null) { // Need an addtional TDS for positioned updates updateTds = new TdsCore(connection, statement.getMessages()); } else { updateTds = cursorTds; } isSybase = Driver.SYBASE == connection.getServerType(); tempResultSet = false; // // Now create the specified type of cursor // cursorCreate(); } /** * Constructs a cached result set based on locally generated data. * * @param statement the parent statement object * @param colName array of column names * @param colType array of corresponding data types * @exception SQLException if an error occurs */ CachedResultSet(JtdsStatement statement, String[] colName, int[] colType) throws SQLException { super(statement, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, null); // // Construct the column descriptor array // columns = new ColInfo[colName.length]; for (int i = 0; i < colName.length; i++) { ColInfo ci = new ColInfo(); ci.name = colName[i]; ci.realName = colName[i]; ci.jdbcType = colType[i]; ci.isCaseSensitive = false; ci.isIdentity = false; ci.isWriteable = false; ci.nullable = 2; ci.scale = 0; TdsData.fillInType(ci); columns[i] = ci; } columnCount = getColumnCount(columns); rowData = new ArrayList(INITIAL_ROW_COUNT); rowsInResult = 0; pos = POS_BEFORE_FIRST; tempResultSet = true; cursorName = null; cursorTds = null; updateTds = null; procName = null; procedureParams = null; } /** * Creates a cached result set with the same columns (and optionally data) * as an existing result set. * * @param rs the result set to copy * @param load load data from the supplied result set * @throws SQLException if an error occurs */ CachedResultSet(JtdsResultSet rs, boolean load) throws SQLException { super((JtdsStatement)rs.getStatement(), rs.getStatement().getResultSetType(), rs.getStatement().getResultSetConcurrency(), null); // JtdsStatement stmt = ((JtdsStatement) rs.getStatement()); // // OK If the user requested an updateable result set tell them // they can't have one! // if (concurrency != ResultSet.CONCUR_READ_ONLY) { concurrency = ResultSet.CONCUR_READ_ONLY; stmt.addWarning(new SQLWarning( Messages.get("warning.cursordowngraded", "CONCUR_READ_ONLY"), "01000")); } // // If the user requested a scroll sensitive cursor tell them // they can't have that either! // if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE) { resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; stmt.addWarning(new SQLWarning( Messages.get("warning.cursordowngraded", "TYPE_SCROLL_INSENSITIVE"), "01000")); } columns = rs.getColumns(); columnCount = getColumnCount(columns); rowData = new ArrayList(INITIAL_ROW_COUNT); rowsInResult = 0; pos = POS_BEFORE_FIRST; tempResultSet = true; cursorName = null; cursorTds = null; updateTds = null; procName = null; procedureParams = null; // // Load result set into buffer // if (load) { while (rs.next()) { rowData.add(copyRow(rs.getCurrentRow())); } rowsInResult = rowData.size(); } } /** * Creates a cached result set containing one row. * * @param statement the parent statement object * @param columns the column descriptor array * @param data the row data * @throws SQLException if an error occurs */ CachedResultSet(JtdsStatement statement, ColInfo columns[], Object data[]) throws SQLException { super(statement, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, null); this.columns = columns; columnCount = getColumnCount(columns); rowData = new ArrayList(1); rowsInResult = 1; pos = POS_BEFORE_FIRST; tempResultSet = true; cursorName = null; rowData.add(copyRow(data)); cursorTds = null; updateTds = null; procName = null; procedureParams = null; } /** * <p> <b>Warning! Ensure the provided data matches the column layout of this * {@link ResultSet}. All kind of weird behavior and errors could be expected * otherwise. </b></p> * * @param data * data of the row to add */ void addRow( Object data[] ) { rowsInResult ++; rowData.add( copyRow( data ) ); } /** * Modify the concurrency of the result set. * <p/> * Use to make result set read only once loaded. * * @param concurrency the concurrency value eg * <code>ResultSet.CONCUR_READ_ONLY</code> */ void setConcurrency(int concurrency) { this.concurrency = concurrency; } /** * Creates a new scrollable result set in memory or a named server cursor. * * @exception SQLException if an error occurs */ private void cursorCreate() throws SQLException { // boolean isSelect = false; int requestedConcurrency = concurrency; int requestedType = resultSetType; // // If the useCursor property is set we will try and use a server // side cursor for forward read only cursors. With the default // fetch size of 100 this is a reasonable emulation of the // MS fast forward cursor. // if (cursorName == null && connection.getUseCursors() && resultSetType == ResultSet.TYPE_FORWARD_ONLY && concurrency == ResultSet.CONCUR_READ_ONLY) { // The useCursors connection property was set true // so we need to create a private cursor name cursorName = connection.getCursorName(); } // // Validate the SQL statement to ensure we have a select. // if (resultSetType != ResultSet.TYPE_FORWARD_ONLY || concurrency != ResultSet.CONCUR_READ_ONLY || cursorName != null) { // // We are going to need access to a SELECT statement for // this to work. Reparse the SQL now and check. // String tmp[] = SQLParser.parse(sql, new ArrayList(), (JtdsConnection) statement.getConnection(), true); if ("select".equals(tmp[2])) { isSelect = true; if (tmp[3] != null && tmp[3].length() > 0) { // OK We have a select with at least one table. tableName = tmp[3]; } else { // Can't find a table name so can't update concurrency = ResultSet.CONCUR_READ_ONLY; } } else { // No good we can't update and we can't declare a cursor cursorName = null; concurrency = ResultSet.CONCUR_READ_ONLY; if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) { resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; } } } // // If a cursor name is specified we try and declare a conventional cursor. // A server error will occur if we try to create a named cursor on a non // select statement. // if (cursorName != null) { // // Create and execute DECLARE CURSOR // StringBuilder cursorSQL = new StringBuilder(sql.length() + cursorName.length()+ 128); cursorSQL.append("DECLARE ").append(cursorName) .append(" CURSOR FOR "); // // We need to adjust any parameter offsets now as the prepended // DECLARE CURSOR will throw the parameter positions off. // ParamInfo[] parameters = procedureParams; if (procedureParams != null && procedureParams.length > 0) { parameters = new ParamInfo[procedureParams.length]; int offset = cursorSQL.length(); for (int i = 0; i < parameters.length; i++) { // Clone parameters to avoid corrupting offsets in original parameters[i] = (ParamInfo) procedureParams[i].clone(); parameters[i].markerPos += offset; } } cursorSQL.append(sql); cursorTds.executeSQL(cursorSQL.toString(), null, parameters, false, statement.getQueryTimeout(), statement.getMaxRows(), statement.getMaxFieldSize(), true); cursorTds.clearResponseQueue(); cursorTds.getMessages().checkErrors(); // // OK now open cursor and fetch the first set (fetchSize) rows // cursorSQL.setLength(0); cursorSQL.append("\r\nOPEN ").append(cursorName); if (fetchSize > 1 && isSybase) { cursorSQL.append("\r\nSET CURSOR ROWS ").append(fetchSize); cursorSQL.append(" FOR ").append(cursorName); } cursorSQL.append("\r\nFETCH ").append(cursorName); cursorTds.executeSQL(cursorSQL.toString(), null, null, false, statement.getQueryTimeout(), statement.getMaxRows(), statement.getMaxFieldSize(), true); // // Check we have a result set // while (!cursorTds.getMoreResults() && !cursorTds.isEndOfResponse()); if (!cursorTds.isResultSet()) { // Throw exception but queue up any others SQLException ex = new SQLException( Messages.get("error.statement.noresult"), "24000"); ex.setNextException(statement.getMessages().exceptions); throw ex; } columns = cursorTds.getColumns(); if (connection.getServerType() == Driver.SQLSERVER) { // Last column will be rowstat but will not be marked as hidden // as we do not have the Column meta data returned by the API // cursor. // Hide it now to avoid confusion (also should not be updated). if (columns.length > 0) { columns[columns.length - 1].isHidden = true; } } columnCount = getColumnCount(columns); rowsInResult = cursorTds.isDataInResultSet() ? 1 : 0; } else { // // Open a memory cached scrollable or forward only possibly updateable cursor // if (isSelect && (concurrency != ResultSet.CONCUR_READ_ONLY || resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE)) { // Need to execute SELECT .. FOR BROWSE to get // the MetaData we require for updates etc // OK Should have an SQL select statement // append " FOR BROWSE" to obtain table names // NB. We can't use any jTDS temporary stored proc cursorTds.executeSQL(sql + " FOR BROWSE", null, procedureParams, false, statement.getQueryTimeout(), statement.getMaxRows(), statement.getMaxFieldSize(), true); while (!cursorTds.getMoreResults() && !cursorTds.isEndOfResponse()); if (!cursorTds.isResultSet()) { // Throw exception but queue up any others SQLException ex = new SQLException( Messages.get("error.statement.noresult"), "24000"); ex.setNextException(statement.getMessages().exceptions); throw ex; } columns = cursorTds.getColumns(); columnCount = getColumnCount(columns); rowData = new ArrayList(INITIAL_ROW_COUNT); // // Load result set into buffer // cacheResultSetRows(); rowsInResult = rowData.size(); pos = POS_BEFORE_FIRST; // // If cursor is built over one table and the table has // key columns then the result set is updateable and / or // can be used as a scroll sensitive result set. // if (!isCursorUpdateable()) { // No so downgrade concurrency = ResultSet.CONCUR_READ_ONLY; if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) { resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE; } } } else { // // Create a read only cursor using direct SQL // cursorTds.executeSQL(sql, procName, procedureParams, false, statement.getQueryTimeout(), statement.getMaxRows(), statement.getMaxFieldSize(), true); while (!cursorTds.getMoreResults() && !cursorTds.isEndOfResponse()); if (!cursorTds.isResultSet()) { // Throw exception but queue up any others SQLException ex = new SQLException( Messages.get("error.statement.noresult"), "24000"); ex.setNextException(statement.getMessages().exceptions); throw ex; } columns = cursorTds.getColumns(); columnCount = getColumnCount(columns); rowData = new ArrayList(INITIAL_ROW_COUNT); // // Load result set into buffer // cacheResultSetRows(); rowsInResult = rowData.size(); pos = POS_BEFORE_FIRST; } } // // Report any cursor downgrade warnings // if (concurrency < requestedConcurrency) { statement.addWarning(new SQLWarning( Messages.get("warning.cursordowngraded", "CONCUR_READ_ONLY"), "01000")); } if (resultSetType < requestedType) { statement.addWarning(new SQLWarning( Messages.get("warning.cursordowngraded", "TYPE_SCROLL_INSENSITIVE"), "01000")); } // // Report any SQLExceptions // statement.getMessages().checkErrors(); } /** * Analyses the tables in the result set and determines if the primary key * columns needed to make it updateable exist. * <p/> * Sybase (and SQL 6.5) will automatically include any additional key and * timestamp columns as hidden fields even if the user does not reference * them in the select statement. * <p/> * If the table is unkeyed but there is an identity column then this is * promoted to a key. * <p/> * Alternatively we can update, provided all the columns in the table row * have been selected, by regarding all of them as keys. * <p/> * SQL Server 7+ does not return the correct primary key meta data for * temporary tables so the driver has to query the catalog to locate any * keys. * * @return <code>true<code> if there is one table and it is keyed */ boolean isCursorUpdateable() throws SQLException { // // Get fully qualified table names and check keys // isKeyed = false; HashSet tableSet = new HashSet(); for (int i = 0; i < columns.length; i++) { ColInfo ci = columns[i]; if (ci.isKey) { // If a table lacks a key Sybase flags all columns except timestamps as keys. // This does not make much sense in the case of text or image fields! if ("text".equals(ci.sqlType) || "image".equals(ci.sqlType)) { ci.isKey = false; } else { isKeyed = true; } } else if (ci.isIdentity) { // This is a good choice for a row identifier! ci.isKey = true; isKeyed = true; } StringBuilder key = new StringBuilder(); if (ci.tableName != null && ci.tableName.length() > 0) { key.setLength(0); if (ci.catalog != null) { key.append(ci.catalog).append('.'); if (ci.schema == null) { key.append('.'); } } if (ci.schema != null) { key.append(ci.schema).append('.'); } key.append(ci.tableName); tableName = key.toString(); tableSet.add(tableName); } } // // MJH - SQL Server 7/2000 does not return key information for temporary tables. // I regard this as a bug! // See if we can find up to the first 8 index columns for ourselves. // if (tableName.startsWith("#") && cursorTds.getTdsVersion() >= Driver.TDS70) { StringBuilder sql = new StringBuilder(1024); sql.append("SELECT "); for (int i = 1; i <= 8; i++) { if (i > 1) { sql.append(','); } sql.append("index_col('tempdb..").append(tableName); sql.append("', indid, ").append(i).append(')'); } sql.append(" FROM tempdb..sysindexes WHERE id = object_id('tempdb.."); sql.append(tableName).append("') AND indid > 0 AND "); sql.append("(status & 2048) = 2048"); cursorTds.executeSQL(sql.toString(), null, null, false, 0, statement.getMaxRows(), statement.getMaxFieldSize(), true); while (!cursorTds.getMoreResults() && !cursorTds.isEndOfResponse()); if (cursorTds.isResultSet() && cursorTds.getNextRow()) { Object row[] = cursorTds.getRowData(); for (int i =0 ; i < row.length; i++) { String name = (String)row[i]; if (name != null) { for (int c = 0; c < columns.length; c++) { if (columns[c].realName != null && columns[c].realName.equalsIgnoreCase(name)) { columns[c].isKey = true; isKeyed = true; break; } } } } } // Report any errors found statement.getMessages().checkErrors(); } // // Final fall back make all columns pseudo keys! // Sybase seems to do this automatically. // if (!isKeyed) { for (int i = 0; i < columns.length; i++) { String type = columns[i].sqlType; if (!"ntext".equals(type) && !"text".equals(type) && !"image".equals(type) && !"timestamp".equals(type) && columns[i].tableName != null) { columns[i].isKey = true; isKeyed = true; } } } return (tableSet.size() == 1 && isKeyed); } /** * Fetches the next result row from the internal row array. * * @param rowNum the row number to fetch * @return <code>true</code> if a result set row is returned * @throws SQLException if an error occurs */ private boolean cursorFetch(int rowNum) throws SQLException { rowUpdated = false; // if (cursorName != null) { // // Using a conventional forward only server cursor // if (!cursorTds.getNextRow()) { // Need to fetch more rows from server StringBuilder sql = new StringBuilder(128); if (isSybase && sizeChanged) { // Sybase allows us to set a fetch size sql.append("SET CURSOR ROWS ").append(fetchSize); sql.append(" FOR ").append(cursorName); sql.append("\r\n"); } sql.append("FETCH ").append(cursorName); // Get the next row or block of rows. cursorTds.executeSQL(sql.toString(), null, null, false, statement.getQueryTimeout(), statement.getMaxRows(), statement.getMaxFieldSize(), true); while (!cursorTds.getMoreResults() && !cursorTds.isEndOfResponse()); sizeChanged = false; // Indicate fetch size updated if (!cursorTds.isResultSet() || !cursorTds.getNextRow()) { pos = POS_AFTER_LAST; currentRow = null; statement.getMessages().checkErrors(); return false; } } currentRow = statement.getTds().getRowData(); pos++; rowsInResult = pos; statement.getMessages().checkErrors(); return currentRow != null; } // // JDBC2 style Scrollable and/or Updateable cursor // if (rowsInResult == 0) { pos = POS_BEFORE_FIRST; currentRow = null; return false; } if (rowNum == pos) { // On current row // return true; } if (rowNum < 1) { currentRow = null; pos = POS_BEFORE_FIRST; return false; } if (rowNum > rowsInResult) { currentRow = null; pos = POS_AFTER_LAST; return false; } pos = rowNum; currentRow = (Object[])rowData.get(rowNum-1); rowDeleted = currentRow == null; if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE && currentRow != null) { refreshRow(); } return true; } /** * Closes the result set. */ private void cursorClose() throws SQLException { if (cursorName != null) { statement.clearWarnings(); String sql; if (isSybase) { sql = "CLOSE " + cursorName + "\r\nDEALLOCATE CURSOR " + cursorName; } else { sql = "CLOSE " + cursorName + "\r\nDEALLOCATE " + cursorName; } cursorTds.submitSQL(sql); } rowData = null; } /** * Creates a parameter object for an UPDATE, DELETE or INSERT statement. * * @param pos the substitution position of the parameter marker in the SQL * @param info the <code>ColInfo</code> column descriptor * @param value the column data item * @return the new parameter as a <code>ParamInfo</code> object */ protected static ParamInfo buildParameter(int pos, ColInfo info, Object value, boolean isUnicode) throws SQLException { int length = 0; if (value instanceof String) { length = ((String)value).length(); } else if (value instanceof byte[]) { length = ((byte[])value).length; } else if (value instanceof BlobImpl) { BlobImpl blob = (BlobImpl)value; value = blob.getBinaryStream(); length = (int)blob.length(); } else if (value instanceof ClobImpl) { ClobImpl clob = (ClobImpl)value; value = clob.getCharacterStream(); length = (int)clob.length(); } ParamInfo param = new ParamInfo(info, null, value, length); param.isUnicode = "nvarchar".equals(info.sqlType) || "nchar".equals(info.sqlType) || "ntext".equals(info.sqlType) || isUnicode; param.markerPos = pos; return param; } /** * Sets the specified column's data value. * * @param colIndex index of the column * @param value new column value * @return the value, possibly converted to an internal type */ protected Object setColValue(int colIndex, int jdbcType, Object value, int length) throws SQLException { value = super.setColValue(colIndex, jdbcType, value, length); if (!onInsertRow && currentRow == null) { throw new SQLException(Messages.get("error.resultset.norow"), "24000"); } colIndex--; ParamInfo pi; ColInfo ci = columns[colIndex]; boolean isUnicode = TdsData.isUnicode(ci); if (onInsertRow) { pi = insertRow[colIndex]; if (pi == null) { pi = new ParamInfo(-1, isUnicode); pi.collation = ci.collation; pi.charsetInfo = ci.charsetInfo; insertRow[colIndex] = pi; } } else { if (updateRow == null) { updateRow = new ParamInfo[columnCount]; } pi = updateRow[colIndex]; if (pi == null) { pi = new ParamInfo(-1, isUnicode); pi.collation = ci.collation; pi.charsetInfo = ci.charsetInfo; updateRow[colIndex] = pi; } } if (value == null) { pi.value = null; pi.length = 0; pi.jdbcType = ci.jdbcType; pi.isSet = true; if (pi.jdbcType == Types.NUMERIC || pi.jdbcType == Types.DECIMAL) { pi.scale = TdsData.DEFAULT_SCALE; } else { pi.scale = 0; } } else { pi.value = value; pi.length = length; pi.isSet = true; pi.jdbcType = jdbcType; if (pi.value instanceof BigDecimal) { pi.scale = ((BigDecimal)pi.value).scale(); } else { pi.scale = 0; } } return value; } /** * Builds a WHERE clause for UPDATE or DELETE statements. * * @param sql the SQL Statement to append the WHERE clause to * @param params the parameter descriptor array for this statement * @param select true if this WHERE clause will be used in a select * statement * @return the parameter list as a <code>ParamInfo[]</code> * @throws SQLException if an error occurs */ ParamInfo[] buildWhereClause(StringBuilder sql, ArrayList params, boolean select) throws SQLException { // // Now construct where clause // sql.append(" WHERE "); if (cursorName != null) { // // Use a positioned update // sql.append(" CURRENT OF ").append(cursorName); } else { int count = 0; for (int i = 0; i < columns.length; i++) { if (currentRow[i] == null) { if (!"text".equals(columns[i].sqlType) && !"ntext".equals(columns[i].sqlType) && !"image".equals(columns[i].sqlType) && columns[i].tableName != null) { if (count > 0) { sql.append(" AND "); } sql.append(columns[i].realName); sql.append(" IS NULL"); } } else { if (isKeyed && select) { // For refresh select only include key columns if (columns[i].isKey) { if (count > 0) { sql.append(" AND "); } sql.append(columns[i].realName); sql.append("=?"); count++; params.add(buildParameter(sql.length() - 1, columns[i], currentRow[i], connection.getUseUnicode())); } } else { // Include all available 'searchable' columns in updates/deletes to protect // against lost updates. if (!"text".equals(columns[i].sqlType) && !"ntext".equals(columns[i].sqlType) && !"image".equals(columns[i].sqlType) && columns[i].tableName != null) { if (count > 0) { sql.append(" AND "); } sql.append(columns[i].realName); sql.append("=?"); count++; params.add(buildParameter(sql.length() - 1, columns[i], currentRow[i], connection.getUseUnicode())); } } } } } return (ParamInfo[]) params.toArray(new ParamInfo[params.size()]); } /** * Refreshes a result set row from keyed tables. * <p/> * If all the tables in the result set have primary keys then the result * set row can be refreshed by refetching the individual table rows. * * @throws SQLException if an error occurs */ protected void refreshKeyedRows() throws SQLException { // // Construct a SELECT statement // StringBuilder sql = new StringBuilder(100 + columns.length * 10); sql.append("SELECT "); int count = 0; for (int i = 0; i < columns.length; i++) { if (!columns[i].isKey && columns[i].tableName != null) { if (count > 0) { sql.append(','); } sql.append(columns[i].realName); count++; } } if (count == 0) { // No non key columns in this table? return; } sql.append(" FROM "); sql.append(tableName); // // Construct a where clause using keyed columns only // ArrayList params = new ArrayList(); buildWhereClause(sql, params, true); ParamInfo parameters[] = (ParamInfo[]) params.toArray(new ParamInfo[params.size()]); // // Execute the select // TdsCore tds = statement.getTds(); tds.executeSQL(sql.toString(), null, parameters, false, 0, statement.getMaxRows(), statement.getMaxFieldSize(), true); if (!tds.isEndOfResponse()) { if (tds.getMoreResults() && tds.getNextRow()) { // refresh the row data Object col[] = tds.getRowData(); count = 0; for (int i = 0; i < columns.length; i++) { if (!columns[i].isKey) { currentRow[i] = col[count++]; } } } else { currentRow = null; } } else { currentRow = null; } tds.clearResponseQueue(); statement.getMessages().checkErrors(); if (currentRow == null) { rowData.set(pos-1, null); rowDeleted = true; } } /** * Refreshes the row by rereading the result set. * <p/> * Obviously very slow on large result sets but may be the only option if * tables do not have keys. */ protected void refreshReRead() throws SQLException { int savePos = pos; cursorCreate(); absolute(savePos); } // // -------------------- java.sql.ResultSet methods ------------------- // public void setFetchSize(int size) throws SQLException { sizeChanged = size != fetchSize; super.setFetchSize(size); } public void afterLast() throws SQLException { checkOpen(); checkScrollable(); if (pos != POS_AFTER_LAST) { cursorFetch(rowsInResult+1); } } public void beforeFirst() throws SQLException { checkOpen(); checkScrollable(); if (pos != POS_BEFORE_FIRST) { cursorFetch(0); } } public void cancelRowUpdates() throws SQLException { checkOpen(); checkUpdateable(); if (onInsertRow) { throw new SQLException(Messages.get("error.resultset.insrow"), "24000"); } if (updateRow != null) { rowUpdated = false; for (int i = 0; i < updateRow.length; i++) { if (updateRow[i] != null) { updateRow[i].clearInValue(); } } } } public void close() throws SQLException { if (!closed) { try { cursorClose(); } finally { closed = true; statement = null; } } } public void deleteRow() throws SQLException { checkOpen(); checkUpdateable(); if (currentRow == null) { throw new SQLException(Messages.get("error.resultset.norow"), "24000"); } if (onInsertRow) { throw new SQLException(Messages.get("error.resultset.insrow"), "24000"); } // // Construct an SQL DELETE statement // StringBuilder sql = new StringBuilder(128); ArrayList params = new ArrayList(); sql.append("DELETE FROM "); sql.append(tableName); // // Create the WHERE clause // ParamInfo parameters[] = buildWhereClause(sql, params, false); // // Execute the delete statement // updateTds.executeSQL(sql.toString(), null, parameters, false, 0, statement.getMaxRows(), statement.getMaxFieldSize(), true); int updateCount = 0; while (!updateTds.isEndOfResponse()) { if (!updateTds.getMoreResults()) { if (updateTds.isUpdateCount()) { updateCount = updateTds.getUpdateCount(); } } } updateTds.clearResponseQueue(); statement.getMessages().checkErrors(); if (updateCount == 0) { // No delete. Possibly row was changed on database by another user? throw new SQLException(Messages.get("error.resultset.deletefail"), "24000"); } rowDeleted = true; currentRow = null; if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) { // Leave a 'hole' in the result set array. rowData.set(pos-1, null); } } public void insertRow() throws SQLException { checkOpen(); checkUpdateable(); if (!onInsertRow) { throw new SQLException(Messages.get("error.resultset.notinsrow"), "24000"); } if (!tempResultSet) { // // Construct an SQL INSERT statement // StringBuilder sql = new StringBuilder(128); ArrayList params = new ArrayList(); sql.append("INSERT INTO "); sql.append(tableName); int sqlLen = sql.length(); // // Create column list // sql.append(" ("); int count = 0; for (int i = 0; i < columnCount; i++) { if (insertRow[i] != null) { if (count > 0) { sql.append(", "); } sql.append(columns[i].realName); count++; } } // // Create new values list // sql.append(") VALUES("); count = 0; for (int i = 0; i < columnCount; i++) { if (insertRow[i] != null) { if (count > 0) { sql.append(", "); } sql.append('?'); insertRow[i].markerPos = sql.length()-1; params.add(insertRow[i]); count++; } } sql.append(')'); if (count == 0) { // Empty insert sql.setLength(sqlLen); if (isSybase) { sql.append(" VALUES()"); } else { sql.append(" DEFAULT VALUES"); } } ParamInfo parameters[] = (ParamInfo[]) params.toArray(new ParamInfo[params.size()]); // // execute the insert statement // updateTds.executeSQL(sql.toString(), null, parameters, false, 0, statement.getMaxRows(), statement.getMaxFieldSize(), true); int updateCount = 0; while (!updateTds.isEndOfResponse()) { if (!updateTds.getMoreResults()) { if (updateTds.isUpdateCount()) { updateCount = updateTds.getUpdateCount(); } } } updateTds.clearResponseQueue(); statement.getMessages().checkErrors(); if (updateCount < 1) { // No Insert. Probably will not get here as duplicate key etc // will have already been reported as an exception. throw new SQLException(Messages.get("error.resultset.insertfail"), "24000"); } } // if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE || (resultSetType == ResultSet.TYPE_FORWARD_ONLY && cursorName == null)) { // // Now insert copy of row into result set buffer // JtdsConnection con = (JtdsConnection)statement.getConnection(); Object row[] = newRow(); for (int i = 0; i < insertRow.length; i++) { if (insertRow[i] != null) { row[i] = Support.convert(con, insertRow[i].value, columns[i].jdbcType, con.getCharset()); } } rowData.add(row); } rowsInResult++; // // Clear row data // for (int i = 0; insertRow != null && i < insertRow.length; i++) { if (insertRow[i] != null) { insertRow[i].clearInValue(); } } } public void moveToCurrentRow() throws SQLException { checkOpen(); checkUpdateable(); insertRow = null; onInsertRow = false; } public void moveToInsertRow() throws SQLException { checkOpen(); checkUpdateable(); insertRow = new ParamInfo[columnCount]; onInsertRow = true; } public void refreshRow() throws SQLException { checkOpen(); if (onInsertRow) { throw new SQLException(Messages.get("error.resultset.insrow"), "24000"); } // // If row is being updated discard updates now // if (concurrency != ResultSet.CONCUR_READ_ONLY) { cancelRowUpdates(); rowUpdated = false; } if (resultSetType == ResultSet.TYPE_FORWARD_ONLY || currentRow == null) { // Do not try and refresh the row in these cases. return; } // // If result set is keyed we can refresh the row data from the // database using the key. // NB. MS SQL Server #Temporary tables with keys are not identified correctly // in the column meta data sent after 'for browse'. This means that // temporary tables can not be used with this logic. // if (isKeyed) { // OK all tables are keyed refreshKeyedRows(); } else { // No good have to use brute force approach refreshReRead(); } } public void updateRow() throws SQLException { checkOpen(); checkUpdateable(); rowUpdated = false; rowDeleted = false; if (currentRow == null) { throw new SQLException(Messages.get("error.resultset.norow"), "24000"); } if (onInsertRow) { throw new SQLException(Messages.get("error.resultset.insrow"), "24000"); } if (updateRow == null) { // Nothing to update return; } boolean keysChanged = false; // // Construct an SQL UPDATE statement // StringBuilder sql = new StringBuilder(128); ArrayList params = new ArrayList(); sql.append("UPDATE "); sql.append(tableName); // // OK now create assign new values // sql.append(" SET "); int count = 0; for (int i = 0; i < columnCount; i++) { if (updateRow[i] != null) { if (count > 0) { sql.append(", "); } sql.append(columns[i].realName); sql.append("=?"); updateRow[i].markerPos = sql.length()-1; params.add(updateRow[i]); count++; if (columns[i].isKey) { // Key is changing so in memory row will need to be deleted // and reinserted at end of row buffer. keysChanged = true; } } } if (count == 0) { // There are no columns to update in this table // so bail out now. return; } // // Now construct where clause // ParamInfo parameters[] = buildWhereClause(sql, params, false); // // Now execute update // updateTds.executeSQL(sql.toString(), null, parameters, false, 0, statement.getMaxRows(), statement.getMaxFieldSize(), true); int updateCount = 0; while (!updateTds.isEndOfResponse()) { if (!updateTds.getMoreResults()) { if (updateTds.isUpdateCount()) { updateCount = updateTds.getUpdateCount(); } } } updateTds.clearResponseQueue(); statement.getMessages().checkErrors(); if (updateCount == 0) { // No update. Possibly row was changed on database by another user? throw new SQLException(Messages.get("error.resultset.updatefail"), "24000"); } // // Update local copy of data // if (resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE) { // Make in memory copy reflect database update // Could use refreshRow but this is much faster. JtdsConnection con = (JtdsConnection)statement.getConnection(); for (int i = 0; i < updateRow.length; i++) { if (updateRow[i] != null) { if (updateRow[i].value instanceof byte[] && (columns[i].jdbcType == Types.CHAR || columns[i].jdbcType == Types.VARCHAR || columns[i].jdbcType == Types.LONGVARCHAR)) { // Need to handle byte[] to varchar otherwise field // will be set to hex string rather than characters. try { currentRow[i] = new String((byte[])updateRow[i].value, con.getCharset()); } catch (UnsupportedEncodingException e) { currentRow[i] = new String((byte[])updateRow[i].value); } } else { currentRow[i] = Support.convert(con, updateRow[i].value, columns[i].jdbcType, con.getCharset()); } } } } // // Update state of cached row data // if (keysChanged && resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE) { // Leave hole at current position and add updated row to end of set rowData.add(currentRow); rowsInResult = rowData.size(); rowData.set(pos-1, null); currentRow = null; rowDeleted = true; } else { rowUpdated = true; } // // Clear update values // cancelRowUpdates(); } public boolean first() throws SQLException { checkOpen(); checkScrollable(); return cursorFetch(1); } public boolean isLast() throws SQLException { checkOpen(); return(pos == rowsInResult) && (rowsInResult != 0); } public boolean last() throws SQLException { checkOpen(); checkScrollable(); return cursorFetch(rowsInResult); } public boolean next() throws SQLException { checkOpen(); if (pos != POS_AFTER_LAST) { return cursorFetch(pos+1); } else { return false; } } public boolean previous() throws SQLException { checkOpen(); checkScrollable(); if (pos == POS_AFTER_LAST) { pos = rowsInResult+1; } return cursorFetch(pos-1); } public boolean rowDeleted() throws SQLException { checkOpen(); return rowDeleted; } public boolean rowInserted() throws SQLException { checkOpen(); // return pos > initialRowCnt; return false; // Same as MSCursorResultSet } public boolean rowUpdated() throws SQLException { checkOpen(); // return rowUpdated; return false; // Same as MSCursorResultSet } public boolean absolute(int row) throws SQLException { checkOpen(); checkScrollable(); if (row < 1) { row = (rowsInResult + 1) + row; } return cursorFetch(row); } public boolean relative(int row) throws SQLException { checkScrollable(); if (pos == POS_AFTER_LAST) { return absolute((rowsInResult+1)+row); } else { return absolute(pos+row); } } public String getCursorName() throws SQLException { checkOpen(); // Hide internal cursor names if (cursorName != null && !cursorName.startsWith("_jtds")) { return cursorName; } throw new SQLException(Messages.get("error.resultset.noposupdate"), "24000"); } }
⏎ net/sourceforge/jtds/jdbc/CachedResultSet.java
Or download all of them as a single archive file:
File name: jtds-1.3.1-fyi.zip File size: 323160 bytes Release date: 2013-06-08 Download
⇐ What Is jtds-1.3.1-dist.zip?
2016-11-26, 7856👍, 0💬
Popular Posts:
JRE 5 sunjce_provider.jar is the JAR file for JRE 5 Sun JCE Provider, which provides implementations...
The Bouncy Castle Crypto package is a Java implementation of cryptographic algorithms, it was develo...
ANTLR is a powerful parser generator for multiple programming languages including Java. ANTLR contai...
What Is poi-scratchpad-5.2.3.jar ?poi-scratchpad-5.2.3.jar is one of the JAR files for Apache POI 5....
Joda-Time provides a quality replacement for the Java date and time classes. The design allows for m...