Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (102)
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 (322)
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, ≈22🔥, 0💬
Popular Posts:
What Is jtds-1.2.2.jar? jtds-1.2.2.jar is the JAR files of jTDS Java library 1.2.2, which is a JDBC ...
Apache Avalon began in 1999 as the Java Apache Server Framework and in late 2002 separated from the ...
iText is an ideal library for developers looking to enhance web- and other applications with dynamic...
JDK 11 jdk.compiler.jmod is the JMOD file for JDK 11 Compiler tool, which can be invoked by the "jav...
JDK 17 jdk.internal.le.jmod is the JMOD file for JDK 17 Internal Line Editing module. JDK 17 Interna...