Source Code Cross Referenced for CachedResultSet.java in  » Database-JDBC-Connection-Pool » jTDS » net » sourceforge » jtds » jdbc » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database JDBC Connection Pool » jTDS » net.sourceforge.jtds.jdbc 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        //jTDS JDBC Driver for Microsoft SQL Server and Sybase
0002:        //Copyright (C) 2004 The jTDS Project
0003:        //
0004:        //This library is free software; you can redistribute it and/or
0005:        //modify it under the terms of the GNU Lesser General Public
0006:        //License as published by the Free Software Foundation; either
0007:        //version 2.1 of the License, or (at your option) any later version.
0008:        //
0009:        //This library is distributed in the hope that it will be useful,
0010:        //but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:        //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:        //Lesser General Public License for more details.
0013:        //
0014:        //You should have received a copy of the GNU Lesser General Public
0015:        //License along with this library; if not, write to the Free Software
0016:        //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017:        //
0018:        package net.sourceforge.jtds.jdbc;
0019:
0020:        import java.io.UnsupportedEncodingException;
0021:        import java.math.BigDecimal;
0022:        import java.sql.ResultSet;
0023:        import java.sql.SQLException;
0024:        import java.sql.SQLWarning;
0025:        import java.sql.Types;
0026:        import java.util.ArrayList;
0027:        import java.util.HashSet;
0028:
0029:        /**
0030:         * A memory cached scrollable/updateable result set.
0031:         * <p/>
0032:         * Notes:
0033:         * <ol>
0034:         *   <li>For maximum performance use the scroll insensitive result set type.
0035:         *   <li>As the result set is cached in memory this implementation is limited
0036:         *     to small result sets.
0037:         *   <li>Updateable or scroll sensitive result sets are limited to selects
0038:         *     which reference one table only.
0039:         *   <li>Scroll sensitive result sets must have primary keys.
0040:         *   <li>Updates are optimistic. To guard against lost updates it is
0041:         *     recommended that the table includes a timestamp column.
0042:         *   <li>This class is a plug-in replacement for the MSCursorResultSet class
0043:         *     which may be advantageous in certain applications as the scroll
0044:         *     insensitive result set implemented here is much faster than the server
0045:         *     side cursor.
0046:         *   <li>Updateable result sets cannot be built from the output of stored
0047:         *     procedures.
0048:         *   <li>This implementation uses 'select ... for browse' to obtain the column
0049:         *     meta data needed to generate update statements etc.
0050:         *   <li>Named forward updateable cursors are also supported in which case
0051:         *     positioned updates and deletes are used referencing a server side
0052:         *     declared cursor.
0053:         *   <li>Named forward read only declared cursors can have a larger fetch size
0054:         *     specified allowing a cursor alternative to the default direct select
0055:         *     method.
0056:         * </ol>
0057:         *
0058:         * @author Mike Hutchinson
0059:         * @version $Id: CachedResultSet.java,v 1.26 2007/07/08 17:28:23 bheineman Exp $
0060:         * @todo Should add a "close statement" flag to the constructors
0061:         */
0062:        public class CachedResultSet extends JtdsResultSet {
0063:
0064:            /** Indicates currently inserting. */
0065:            protected boolean onInsertRow;
0066:            /** Buffer row used for inserts. */
0067:            protected ParamInfo[] insertRow;
0068:            /** The "update" row. */
0069:            protected ParamInfo[] updateRow;
0070:            // FIXME Remember if the row was updated/deleted for each row in the ResultSet
0071:            /** Indicates that row has been updated. */
0072:            protected boolean rowUpdated;
0073:            /** Indicates that row has been deleted. */
0074:            protected boolean rowDeleted;
0075:            /** The row count of the initial result set. */
0076:            protected int initialRowCnt;
0077:            /** True if this is a local temporary result set. */
0078:            protected final boolean tempResultSet;
0079:            /** Cursor TdsCore object. */
0080:            protected final TdsCore cursorTds;
0081:            /** Updates TdsCore object used for positioned updates. */
0082:            protected final TdsCore updateTds;
0083:            /** Flag to indicate Sybase. */
0084:            protected boolean isSybase;
0085:            /** Fetch size has been changed. */
0086:            protected boolean sizeChanged;
0087:            /** Original SQL statement. */
0088:            protected String sql;
0089:            /** Original procedure name. */
0090:            protected final String procName;
0091:            /** Original parameters. */
0092:            protected final ParamInfo[] procedureParams;
0093:            /** Table is keyed. */
0094:            protected boolean isKeyed;
0095:            /** First table name in select. */
0096:            protected String tableName;
0097:            /** The parent connection object */
0098:            protected ConnectionJDBC2 connection;
0099:
0100:            /**
0101:             * Constructs a new cached result set.
0102:             * <p/>
0103:             * This result set will either be cached in memory or, if the cursor name
0104:             * is set, can be a forward only server side cursor. This latter form of
0105:             * cursor can also support positioned updates.
0106:             *
0107:             * @param statement       the parent statement object
0108:             * @param sql             the SQL statement used to build the result set
0109:             * @param procName        an optional stored procedure name
0110:             * @param procedureParams parameters for prepared statements
0111:             * @param resultSetType   the result set type eg scrollable
0112:             * @param concurrency     the result set concurrency eg updateable
0113:             * @exception SQLException if an error occurs
0114:             */
0115:            CachedResultSet(JtdsStatement statement, String sql,
0116:                    String procName, ParamInfo[] procedureParams,
0117:                    int resultSetType, int concurrency) throws SQLException {
0118:                super (statement, resultSetType, concurrency, null);
0119:                this .connection = (ConnectionJDBC2) statement.getConnection();
0120:                this .cursorTds = statement.getTds();
0121:                this .sql = sql;
0122:                this .procName = procName;
0123:                this .procedureParams = procedureParams;
0124:                if (resultSetType == ResultSet.TYPE_FORWARD_ONLY
0125:                        && concurrency != ResultSet.CONCUR_READ_ONLY
0126:                        && cursorName != null) {
0127:                    // Need an addtional TDS for positioned updates
0128:                    this .updateTds = new TdsCore(connection, statement
0129:                            .getMessages());
0130:                } else {
0131:                    this .updateTds = this .cursorTds;
0132:                }
0133:                this .isSybase = Driver.SYBASE == connection.getServerType();
0134:                this .tempResultSet = false;
0135:                //
0136:                // Now create the specified type of cursor
0137:                //
0138:                cursorCreate();
0139:            }
0140:
0141:            /**
0142:             * Constructs a cached result set based on locally generated data.
0143:             *
0144:             * @param statement the parent statement object
0145:             * @param colName   array of column names
0146:             * @param colType   array of corresponding data types
0147:             * @exception SQLException if an error occurs
0148:             */
0149:            CachedResultSet(JtdsStatement statement, String[] colName,
0150:                    int[] colType) throws SQLException {
0151:                super (statement, ResultSet.TYPE_FORWARD_ONLY,
0152:                        ResultSet.CONCUR_UPDATABLE, null);
0153:                //
0154:                // Construct the column descriptor array
0155:                //
0156:                this .columns = new ColInfo[colName.length];
0157:                for (int i = 0; i < colName.length; i++) {
0158:                    ColInfo ci = new ColInfo();
0159:                    ci.name = colName[i];
0160:                    ci.realName = colName[i];
0161:                    ci.jdbcType = colType[i];
0162:                    ci.isCaseSensitive = false;
0163:                    ci.isIdentity = false;
0164:                    ci.isWriteable = false;
0165:                    ci.nullable = 2;
0166:                    ci.scale = 0;
0167:                    TdsData.fillInType(ci);
0168:                    columns[i] = ci;
0169:                }
0170:                this .columnCount = getColumnCount(columns);
0171:                this .rowData = new ArrayList(INITIAL_ROW_COUNT);
0172:                this .rowsInResult = 0;
0173:                this .initialRowCnt = 0;
0174:                this .pos = POS_BEFORE_FIRST;
0175:                this .tempResultSet = true;
0176:                this .cursorName = null;
0177:                this .cursorTds = null;
0178:                this .updateTds = null;
0179:                this .procName = null;
0180:                this .procedureParams = null;
0181:            }
0182:
0183:            /**
0184:             * Creates a cached result set with the same columns (and optionally data)
0185:             * as an existing result set.
0186:             *
0187:             * @param rs   the result set to copy
0188:             * @param load load data from the supplied result set
0189:             * @throws SQLException if an error occurs
0190:             */
0191:            CachedResultSet(JtdsResultSet rs, boolean load) throws SQLException {
0192:                super ((JtdsStatement) rs.getStatement(), rs.getStatement()
0193:                        .getResultSetType(), rs.getStatement()
0194:                        .getResultSetConcurrency(), null);
0195:                //
0196:                JtdsStatement stmt = ((JtdsStatement) rs.getStatement());
0197:                //
0198:                // OK If the user requested an updateable result set tell them
0199:                // they can't have one!
0200:                //
0201:                if (concurrency != ResultSet.CONCUR_READ_ONLY) {
0202:                    concurrency = ResultSet.CONCUR_READ_ONLY;
0203:                    stmt.addWarning(new SQLWarning(Messages.get(
0204:                            "warning.cursordowngraded", "CONCUR_READ_ONLY"),
0205:                            "01000"));
0206:                }
0207:                //
0208:                // If the user requested a scroll sensitive cursor tell them
0209:                // they can't have that either!
0210:                //
0211:                if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE) {
0212:                    resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
0213:                    stmt.addWarning(new SQLWarning(Messages.get(
0214:                            "warning.cursordowngraded",
0215:                            "TYPE_SCROLL_INSENSITIVE"), "01000"));
0216:                }
0217:
0218:                this .columns = rs.getColumns();
0219:                this .columnCount = getColumnCount(columns);
0220:                this .rowData = new ArrayList(INITIAL_ROW_COUNT);
0221:                this .rowsInResult = 0;
0222:                this .initialRowCnt = 0;
0223:                this .pos = POS_BEFORE_FIRST;
0224:                this .tempResultSet = true;
0225:                this .cursorName = null;
0226:                this .cursorTds = null;
0227:                this .updateTds = null;
0228:                this .procName = null;
0229:                this .procedureParams = null;
0230:                //
0231:                // Load result set into buffer
0232:                //
0233:                if (load) {
0234:                    while (rs.next()) {
0235:                        rowData.add(copyRow(rs.getCurrentRow()));
0236:                    }
0237:                    this .rowsInResult = rowData.size();
0238:                    this .initialRowCnt = rowsInResult;
0239:                }
0240:            }
0241:
0242:            /**
0243:             * Creates a cached result set containing one row.
0244:             *
0245:             * @param statement the parent statement object
0246:             * @param columns   the column descriptor array
0247:             * @param data      the row data
0248:             * @throws SQLException if an error occurs
0249:             */
0250:            CachedResultSet(JtdsStatement statement, ColInfo columns[],
0251:                    Object data[]) throws SQLException {
0252:                super (statement, ResultSet.TYPE_FORWARD_ONLY,
0253:                        ResultSet.CONCUR_READ_ONLY, null);
0254:                this .columns = columns;
0255:                this .columnCount = getColumnCount(columns);
0256:                this .rowData = new ArrayList(1);
0257:                this .rowsInResult = 1;
0258:                this .initialRowCnt = 1;
0259:                this .pos = POS_BEFORE_FIRST;
0260:                this .tempResultSet = true;
0261:                this .cursorName = null;
0262:                this .rowData.add(copyRow(data));
0263:                this .cursorTds = null;
0264:                this .updateTds = null;
0265:                this .procName = null;
0266:                this .procedureParams = null;
0267:            }
0268:
0269:            /**
0270:             * Modify the concurrency of the result set.
0271:             * <p/>
0272:             * Use to make result set read only once loaded.
0273:             *
0274:             * @param concurrency the concurrency value eg
0275:             *                    <code>ResultSet.CONCUR_READ_ONLY</code>
0276:             */
0277:            void setConcurrency(int concurrency) {
0278:                this .concurrency = concurrency;
0279:            }
0280:
0281:            /**
0282:             * Creates a new scrollable result set in memory or a named server cursor.
0283:             *
0284:             * @exception SQLException if an error occurs
0285:             */
0286:            private void cursorCreate() throws SQLException {
0287:                //
0288:                boolean isSelect = false;
0289:                int requestedConcurrency = concurrency;
0290:                int requestedType = resultSetType;
0291:
0292:                //
0293:                // If the useCursor property is set we will try and use a server
0294:                // side cursor for forward read only cursors. With the default
0295:                // fetch size of 100 this is a reasonable emulation of the
0296:                // MS fast forward cursor.
0297:                //
0298:                if (cursorName == null && connection.getUseCursors()
0299:                        && resultSetType == ResultSet.TYPE_FORWARD_ONLY
0300:                        && concurrency == ResultSet.CONCUR_READ_ONLY) {
0301:                    // The useCursors connection property was set true
0302:                    // so we need to create a private cursor name
0303:                    cursorName = connection.getCursorName();
0304:                }
0305:                //
0306:                // Validate the SQL statement to ensure we have a select.
0307:                //
0308:                if (resultSetType != ResultSet.TYPE_FORWARD_ONLY
0309:                        || concurrency != ResultSet.CONCUR_READ_ONLY
0310:                        || cursorName != null) {
0311:                    //
0312:                    // We are going to need access to a SELECT statement for
0313:                    // this to work. Reparse the SQL now and check.
0314:                    //
0315:                    String tmp[] = SQLParser.parse(sql, new ArrayList(),
0316:                            (ConnectionJDBC2) statement.getConnection(), true);
0317:
0318:                    if ("select".equals(tmp[2])) {
0319:                        isSelect = true;
0320:                        if (tmp[3] != null && tmp[3].length() > 0) {
0321:                            // OK We have a select with at least one table.
0322:                            tableName = tmp[3];
0323:                        } else {
0324:                            // Can't find a table name so can't update
0325:                            concurrency = ResultSet.CONCUR_READ_ONLY;
0326:                        }
0327:                    } else {
0328:                        // No good we can't update and we can't declare a cursor
0329:                        cursorName = null;
0330:                        concurrency = ResultSet.CONCUR_READ_ONLY;
0331:                        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
0332:                            resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
0333:                        }
0334:                    }
0335:                }
0336:                //
0337:                // If a cursor name is specified we try and declare a conventional cursor.
0338:                // A server error will occur if we try to create a named cursor on a non
0339:                // select statement.
0340:                //
0341:                if (cursorName != null) {
0342:                    //
0343:                    // Create and execute DECLARE CURSOR
0344:                    //
0345:                    StringBuffer cursorSQL = new StringBuffer(sql.length()
0346:                            + cursorName.length() + 128);
0347:                    cursorSQL.append("DECLARE ").append(cursorName).append(
0348:                            " CURSOR FOR ");
0349:                    //
0350:                    // We need to adjust any parameter offsets now as the prepended
0351:                    // DECLARE CURSOR will throw the parameter positions off.
0352:                    //
0353:                    ParamInfo[] parameters = procedureParams;
0354:                    if (procedureParams != null && procedureParams.length > 0) {
0355:                        parameters = new ParamInfo[procedureParams.length];
0356:                        int offset = cursorSQL.length();
0357:                        for (int i = 0; i < parameters.length; i++) {
0358:                            // Clone parameters to avoid corrupting offsets in original
0359:                            parameters[i] = (ParamInfo) procedureParams[i]
0360:                                    .clone();
0361:                            parameters[i].markerPos += offset;
0362:                        }
0363:                    }
0364:                    cursorSQL.append(sql);
0365:                    cursorTds.executeSQL(cursorSQL.toString(), null,
0366:                            parameters, false, statement.getQueryTimeout(),
0367:                            statement.getMaxRows(),
0368:                            statement.getMaxFieldSize(), true);
0369:                    cursorTds.clearResponseQueue();
0370:                    cursorTds.getMessages().checkErrors();
0371:                    //
0372:                    // OK now open cursor and fetch the first set (fetchSize) rows
0373:                    //
0374:                    cursorSQL.setLength(0);
0375:                    cursorSQL.append("\r\nOPEN ").append(cursorName);
0376:                    if (fetchSize > 1 && isSybase) {
0377:                        cursorSQL.append("\r\nSET CURSOR ROWS ").append(
0378:                                fetchSize);
0379:                        cursorSQL.append(" FOR ").append(cursorName);
0380:                    }
0381:                    cursorSQL.append("\r\nFETCH ").append(cursorName);
0382:                    cursorTds.executeSQL(cursorSQL.toString(), null, null,
0383:                            false, statement.getQueryTimeout(), statement
0384:                                    .getMaxRows(), statement.getMaxFieldSize(),
0385:                            true);
0386:                    //
0387:                    // Check we have a result set
0388:                    //
0389:                    while (!cursorTds.getMoreResults()
0390:                            && !cursorTds.isEndOfResponse())
0391:                        ;
0392:
0393:                    if (!cursorTds.isResultSet()) {
0394:                        // Throw exception but queue up any others
0395:                        SQLException ex = new SQLException(Messages
0396:                                .get("error.statement.noresult"), "24000");
0397:                        ex.setNextException(statement.getMessages().exceptions);
0398:                        throw ex;
0399:                    }
0400:                    columns = cursorTds.getColumns();
0401:                    if (connection.getServerType() == Driver.SQLSERVER) {
0402:                        // Last column will be rowstat but will not be marked as hidden
0403:                        // as we do not have the Column meta data returned by the API
0404:                        // cursor.
0405:                        // Hide it now to avoid confusion (also should not be updated).
0406:                        if (columns.length > 0) {
0407:                            columns[columns.length - 1].isHidden = true;
0408:                        }
0409:                    }
0410:                    columnCount = getColumnCount(columns);
0411:                    rowsInResult = cursorTds.isDataInResultSet() ? 1 : 0;
0412:                } else {
0413:                    //
0414:                    // Open a memory cached scrollable or forward only possibly updateable cursor
0415:                    //
0416:                    if (isSelect
0417:                            && (concurrency != ResultSet.CONCUR_READ_ONLY || resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE)) {
0418:                        // Need to execute SELECT .. FOR BROWSE to get
0419:                        // the MetaData we require for updates etc
0420:                        // OK Should have an SQL select statement
0421:                        // append " FOR BROWSE" to obtain table names
0422:                        // NB. We can't use any jTDS temporary stored proc
0423:                        cursorTds.executeSQL(sql + " FOR BROWSE", null,
0424:                                procedureParams, false, statement
0425:                                        .getQueryTimeout(), statement
0426:                                        .getMaxRows(), statement
0427:                                        .getMaxFieldSize(), true);
0428:                        while (!cursorTds.getMoreResults()
0429:                                && !cursorTds.isEndOfResponse())
0430:                            ;
0431:                        if (!cursorTds.isResultSet()) {
0432:                            // Throw exception but queue up any others
0433:                            SQLException ex = new SQLException(Messages
0434:                                    .get("error.statement.noresult"), "24000");
0435:                            ex
0436:                                    .setNextException(statement.getMessages().exceptions);
0437:                            throw ex;
0438:                        }
0439:                        columns = cursorTds.getColumns();
0440:                        columnCount = getColumnCount(columns);
0441:                        rowData = new ArrayList(INITIAL_ROW_COUNT);
0442:                        //
0443:                        // Load result set into buffer
0444:                        //
0445:                        cacheResultSetRows();
0446:                        rowsInResult = rowData.size();
0447:                        initialRowCnt = rowsInResult;
0448:                        pos = POS_BEFORE_FIRST;
0449:                        //
0450:                        // If cursor is built over one table and the table has
0451:                        // key columns then the result set is updateable and / or
0452:                        // can be used as a scroll sensitive result set.
0453:                        //
0454:                        if (!isCursorUpdateable()) {
0455:                            // No so downgrade
0456:                            concurrency = ResultSet.CONCUR_READ_ONLY;
0457:                            if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
0458:                                resultSetType = ResultSet.TYPE_SCROLL_INSENSITIVE;
0459:                            }
0460:                        }
0461:                    } else {
0462:                        //
0463:                        // Create a read only cursor using direct SQL
0464:                        //
0465:                        cursorTds.executeSQL(sql, procName, procedureParams,
0466:                                false, statement.getQueryTimeout(), statement
0467:                                        .getMaxRows(), statement
0468:                                        .getMaxFieldSize(), true);
0469:                        while (!cursorTds.getMoreResults()
0470:                                && !cursorTds.isEndOfResponse())
0471:                            ;
0472:
0473:                        if (!cursorTds.isResultSet()) {
0474:                            // Throw exception but queue up any others
0475:                            SQLException ex = new SQLException(Messages
0476:                                    .get("error.statement.noresult"), "24000");
0477:                            ex
0478:                                    .setNextException(statement.getMessages().exceptions);
0479:                            throw ex;
0480:                        }
0481:                        columns = cursorTds.getColumns();
0482:                        columnCount = getColumnCount(columns);
0483:                        rowData = new ArrayList(INITIAL_ROW_COUNT);
0484:                        //
0485:                        // Load result set into buffer
0486:                        //
0487:                        cacheResultSetRows();
0488:                        rowsInResult = rowData.size();
0489:                        initialRowCnt = rowsInResult;
0490:                        pos = POS_BEFORE_FIRST;
0491:                    }
0492:                }
0493:                //
0494:                // Report any cursor downgrade warnings
0495:                //
0496:                if (concurrency < requestedConcurrency) {
0497:                    statement.addWarning(new SQLWarning(Messages.get(
0498:                            "warning.cursordowngraded", "CONCUR_READ_ONLY"),
0499:                            "01000"));
0500:                }
0501:                if (resultSetType < requestedType) {
0502:                    statement.addWarning(new SQLWarning(Messages.get(
0503:                            "warning.cursordowngraded",
0504:                            "TYPE_SCROLL_INSENSITIVE"), "01000"));
0505:                }
0506:                //
0507:                // Report any SQLExceptions
0508:                //
0509:                statement.getMessages().checkErrors();
0510:            }
0511:
0512:            /**
0513:             * Analyses the tables in the result set and determines if the primary key
0514:             * columns needed to make it updateable exist.
0515:             * <p/>
0516:             * Sybase (and SQL 6.5) will automatically include any additional key and
0517:             * timestamp columns as hidden fields even if the user does not reference
0518:             * them in the select statement.
0519:             * <p/>
0520:             * If the table is unkeyed but there is an identity column then this is
0521:             * promoted to a key.
0522:             * <p/>
0523:             * Alternatively we can update, provided all the columns in the table row
0524:             * have been selected, by regarding all of them as keys.
0525:             * <p/>
0526:             * SQL Server 7+ does not return the correct primary key meta data for
0527:             * temporary tables so the driver has to query the catalog to locate any
0528:             * keys.
0529:             *
0530:             * @return <code>true<code> if there is one table and it is keyed
0531:             */
0532:            boolean isCursorUpdateable() throws SQLException {
0533:                //
0534:                // Get fully qualified table names and check keys
0535:                //
0536:                isKeyed = false;
0537:                HashSet tableSet = new HashSet();
0538:                for (int i = 0; i < columns.length; i++) {
0539:                    ColInfo ci = columns[i];
0540:                    if (ci.isKey) {
0541:                        // If a table lacks a key Sybase flags all columns except timestamps as keys.
0542:                        // This does not make much sense in the case of text or image fields!
0543:                        if ("text".equals(ci.sqlType)
0544:                                || "image".equals(ci.sqlType)) {
0545:                            ci.isKey = false;
0546:                        } else {
0547:                            isKeyed = true;
0548:                        }
0549:                    } else if (ci.isIdentity) {
0550:                        // This is a good choice for a row identifier!
0551:                        ci.isKey = true;
0552:                        isKeyed = true;
0553:                    }
0554:                    StringBuffer key = new StringBuffer();
0555:                    if (ci.tableName != null && ci.tableName.length() > 0) {
0556:                        key.setLength(0);
0557:                        if (ci.catalog != null) {
0558:                            key.append(ci.catalog).append('.');
0559:                            if (ci.schema == null) {
0560:                                key.append('.');
0561:                            }
0562:                        }
0563:                        if (ci.schema != null) {
0564:                            key.append(ci.schema).append('.');
0565:                        }
0566:                        key.append(ci.tableName);
0567:                        tableName = key.toString();
0568:                        tableSet.add(tableName);
0569:                    }
0570:                }
0571:                //
0572:                // MJH - SQL Server 7/2000 does not return key information for temporary tables.
0573:                // I regard this as a bug!
0574:                // See if we can find up to the first 8 index columns for ourselves.
0575:                //
0576:                if (tableName.startsWith("#")
0577:                        && cursorTds.getTdsVersion() >= Driver.TDS70) {
0578:                    StringBuffer sql = new StringBuffer(1024);
0579:                    sql.append("SELECT ");
0580:                    for (int i = 1; i <= 8; i++) {
0581:                        if (i > 1) {
0582:                            sql.append(',');
0583:                        }
0584:                        sql.append("index_col('tempdb..").append(tableName);
0585:                        sql.append("', indid, ").append(i).append(')');
0586:                    }
0587:                    sql
0588:                            .append(" FROM tempdb..sysindexes WHERE id = object_id('tempdb..");
0589:                    sql.append(tableName).append("') AND indid > 0 AND ");
0590:                    sql.append("(status & 2048) = 2048");
0591:                    cursorTds.executeSQL(sql.toString(), null, null, false, 0,
0592:                            statement.getMaxRows(),
0593:                            statement.getMaxFieldSize(), true);
0594:                    while (!cursorTds.getMoreResults()
0595:                            && !cursorTds.isEndOfResponse())
0596:                        ;
0597:
0598:                    if (cursorTds.isResultSet() && cursorTds.getNextRow()) {
0599:                        Object row[] = cursorTds.getRowData();
0600:                        for (int i = 0; i < row.length; i++) {
0601:                            String name = (String) row[i];
0602:                            if (name != null) {
0603:                                for (int c = 0; c < columns.length; c++) {
0604:                                    if (columns[c].realName != null
0605:                                            && columns[c].realName
0606:                                                    .equalsIgnoreCase(name)) {
0607:                                        columns[c].isKey = true;
0608:                                        isKeyed = true;
0609:                                        break;
0610:                                    }
0611:                                }
0612:                            }
0613:                        }
0614:                    }
0615:                    // Report any errors found
0616:                    statement.getMessages().checkErrors();
0617:                }
0618:                //
0619:                // Final fall back make all columns pseudo keys!
0620:                // Sybase seems to do this automatically.
0621:                //
0622:                if (!isKeyed) {
0623:                    for (int i = 0; i < columns.length; i++) {
0624:                        String type = columns[i].sqlType;
0625:                        if (!"ntext".equals(type) && !"text".equals(type)
0626:                                && !"image".equals(type)
0627:                                && !"timestamp".equals(type)
0628:                                && columns[i].tableName != null) {
0629:                            columns[i].isKey = true;
0630:                            isKeyed = true;
0631:                        }
0632:                    }
0633:                }
0634:
0635:                return (tableSet.size() == 1 && isKeyed);
0636:            }
0637:
0638:            /**
0639:             * Fetches the next result row from the internal row array.
0640:             *
0641:             * @param rowNum the row number to fetch
0642:             * @return <code>true</code> if a result set row is returned
0643:             * @throws SQLException if an error occurs
0644:             */
0645:            private boolean cursorFetch(int rowNum) throws SQLException {
0646:                rowUpdated = false;
0647:                //
0648:                if (cursorName != null) {
0649:                    //
0650:                    // Using a conventional forward only server cursor
0651:                    //
0652:                    if (!cursorTds.getNextRow()) {
0653:                        // Need to fetch more rows from server
0654:                        StringBuffer sql = new StringBuffer(128);
0655:                        if (isSybase && sizeChanged) {
0656:                            // Sybase allows us to set a fetch size
0657:                            sql.append("SET CURSOR ROWS ").append(fetchSize);
0658:                            sql.append(" FOR ").append(cursorName);
0659:                            sql.append("\r\n");
0660:                        }
0661:                        sql.append("FETCH ").append(cursorName);
0662:                        // Get the next row or block of rows.
0663:                        cursorTds.executeSQL(sql.toString(), null, null, false,
0664:                                statement.getQueryTimeout(), statement
0665:                                        .getMaxRows(), statement
0666:                                        .getMaxFieldSize(), true);
0667:                        while (!cursorTds.getMoreResults()
0668:                                && !cursorTds.isEndOfResponse())
0669:                            ;
0670:
0671:                        sizeChanged = false; // Indicate fetch size updated
0672:
0673:                        if (!cursorTds.isResultSet() || !cursorTds.getNextRow()) {
0674:                            pos = POS_AFTER_LAST;
0675:                            currentRow = null;
0676:                            statement.getMessages().checkErrors();
0677:                            return false;
0678:                        }
0679:                    }
0680:                    currentRow = statement.getTds().getRowData();
0681:                    pos++;
0682:                    rowsInResult = pos;
0683:                    statement.getMessages().checkErrors();
0684:
0685:                    return currentRow != null;
0686:
0687:                }
0688:                //
0689:                // JDBC2 style Scrollable and/or Updateable cursor
0690:                //
0691:                if (rowsInResult == 0) {
0692:                    pos = POS_BEFORE_FIRST;
0693:                    currentRow = null;
0694:                    return false;
0695:                }
0696:                if (rowNum == pos) {
0697:                    // On current row
0698:                    //
0699:                    return true;
0700:                }
0701:                if (rowNum < 1) {
0702:                    currentRow = null;
0703:                    pos = POS_BEFORE_FIRST;
0704:                    return false;
0705:                }
0706:                if (rowNum > rowsInResult) {
0707:                    currentRow = null;
0708:                    pos = POS_AFTER_LAST;
0709:                    return false;
0710:                }
0711:                pos = rowNum;
0712:                currentRow = (Object[]) rowData.get(rowNum - 1);
0713:                rowDeleted = currentRow == null;
0714:
0715:                if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE
0716:                        && currentRow != null) {
0717:                    refreshRow();
0718:                }
0719:
0720:                return true;
0721:            }
0722:
0723:            /**
0724:             * Closes the result set.
0725:             */
0726:            private void cursorClose() throws SQLException {
0727:                if (cursorName != null) {
0728:                    statement.clearWarnings();
0729:                    String sql;
0730:                    if (isSybase) {
0731:                        sql = "CLOSE " + cursorName + "\r\nDEALLOCATE CURSOR "
0732:                                + cursorName;
0733:                    } else {
0734:                        sql = "CLOSE " + cursorName + "\r\nDEALLOCATE "
0735:                                + cursorName;
0736:                    }
0737:                    cursorTds.submitSQL(sql);
0738:                }
0739:                rowData = null;
0740:            }
0741:
0742:            /**
0743:             * Creates a parameter object for an UPDATE, DELETE or INSERT statement.
0744:             *
0745:             * @param pos   the substitution position of the parameter marker in the SQL
0746:             * @param info  the <code>ColInfo</code> column descriptor
0747:             * @param value the column data item
0748:             * @return the new parameter as a <code>ParamInfo</code> object
0749:             */
0750:            protected static ParamInfo buildParameter(int pos, ColInfo info,
0751:                    Object value, boolean isUnicode) throws SQLException {
0752:
0753:                int length = 0;
0754:                if (value instanceof  String) {
0755:                    length = ((String) value).length();
0756:                } else if (value instanceof  byte[]) {
0757:                    length = ((byte[]) value).length;
0758:                } else if (value instanceof  BlobImpl) {
0759:                    BlobImpl blob = (BlobImpl) value;
0760:                    value = blob.getBinaryStream();
0761:                    length = (int) blob.length();
0762:                } else if (value instanceof  ClobImpl) {
0763:                    ClobImpl clob = (ClobImpl) value;
0764:                    value = clob.getCharacterStream();
0765:                    length = (int) clob.length();
0766:                }
0767:                ParamInfo param = new ParamInfo(info, null, value, length);
0768:                param.isUnicode = "nvarchar".equals(info.sqlType)
0769:                        || "nchar".equals(info.sqlType)
0770:                        || "ntext".equals(info.sqlType) || isUnicode;
0771:                param.markerPos = pos;
0772:
0773:                return param;
0774:            }
0775:
0776:            /**
0777:             * Sets the specified column's data value.
0778:             *
0779:             * @param colIndex index of the column
0780:             * @param value    new column value
0781:             * @return the value, possibly converted to an internal type
0782:             */
0783:            protected Object setColValue(int colIndex, int jdbcType,
0784:                    Object value, int length) throws SQLException {
0785:
0786:                value = super .setColValue(colIndex, jdbcType, value, length);
0787:
0788:                if (!onInsertRow && currentRow == null) {
0789:                    throw new SQLException(Messages
0790:                            .get("error.resultset.norow"), "24000");
0791:                }
0792:                colIndex--;
0793:                ParamInfo pi;
0794:                ColInfo ci = columns[colIndex];
0795:                boolean isUnicode = TdsData.isUnicode(ci);
0796:
0797:                if (onInsertRow) {
0798:                    pi = insertRow[colIndex];
0799:                    if (pi == null) {
0800:                        pi = new ParamInfo(-1, isUnicode);
0801:                        pi.collation = ci.collation;
0802:                        pi.charsetInfo = ci.charsetInfo;
0803:                        insertRow[colIndex] = pi;
0804:                    }
0805:                } else {
0806:                    if (updateRow == null) {
0807:                        updateRow = new ParamInfo[columnCount];
0808:                    }
0809:                    pi = updateRow[colIndex];
0810:                    if (pi == null) {
0811:                        pi = new ParamInfo(-1, isUnicode);
0812:                        pi.collation = ci.collation;
0813:                        pi.charsetInfo = ci.charsetInfo;
0814:                        updateRow[colIndex] = pi;
0815:                    }
0816:                }
0817:
0818:                if (value == null) {
0819:                    pi.value = null;
0820:                    pi.length = 0;
0821:                    pi.jdbcType = ci.jdbcType;
0822:                    pi.isSet = true;
0823:                    if (pi.jdbcType == Types.NUMERIC
0824:                            || pi.jdbcType == Types.DECIMAL) {
0825:                        pi.scale = TdsData.DEFAULT_SCALE;
0826:                    } else {
0827:                        pi.scale = 0;
0828:                    }
0829:                } else {
0830:                    pi.value = value;
0831:                    pi.length = length;
0832:                    pi.isSet = true;
0833:                    pi.jdbcType = jdbcType;
0834:                    if (pi.value instanceof  BigDecimal) {
0835:                        pi.scale = ((BigDecimal) pi.value).scale();
0836:                    } else {
0837:                        pi.scale = 0;
0838:                    }
0839:                }
0840:
0841:                return value;
0842:            }
0843:
0844:            /**
0845:             * Builds a WHERE clause for UPDATE or DELETE statements.
0846:             *
0847:             * @param sql    the SQL Statement to append the WHERE clause to
0848:             * @param params the parameter descriptor array for this statement
0849:             * @param select true if this WHERE clause will be used in a select
0850:             *               statement
0851:             * @return the parameter list as a <code>ParamInfo[]</code>
0852:             * @throws SQLException if an error occurs
0853:             */
0854:            ParamInfo[] buildWhereClause(StringBuffer sql, ArrayList params,
0855:                    boolean select) throws SQLException {
0856:                //
0857:                // Now construct where clause
0858:                //
0859:                sql.append(" WHERE ");
0860:                if (cursorName != null) {
0861:                    //
0862:                    // Use a positioned update
0863:                    //
0864:                    sql.append(" CURRENT OF ").append(cursorName);
0865:                } else {
0866:                    int count = 0;
0867:                    for (int i = 0; i < columns.length; i++) {
0868:                        if (currentRow[i] == null) {
0869:                            if (!"text".equals(columns[i].sqlType)
0870:                                    && !"ntext".equals(columns[i].sqlType)
0871:                                    && !"image".equals(columns[i].sqlType)
0872:                                    && columns[i].tableName != null) {
0873:                                if (count > 0) {
0874:                                    sql.append(" AND ");
0875:                                }
0876:                                sql.append(columns[i].realName);
0877:                                sql.append(" IS NULL");
0878:                            }
0879:                        } else {
0880:                            if (isKeyed && select) {
0881:                                // For refresh select only include key columns
0882:                                if (columns[i].isKey) {
0883:                                    if (count > 0) {
0884:                                        sql.append(" AND ");
0885:                                    }
0886:                                    sql.append(columns[i].realName);
0887:                                    sql.append("=?");
0888:                                    count++;
0889:                                    params.add(buildParameter(sql.length() - 1,
0890:                                            columns[i], currentRow[i],
0891:                                            connection.getUseUnicode()));
0892:                                }
0893:                            } else {
0894:                                // Include all available 'searchable' columns in updates/deletes to protect
0895:                                // against lost updates.
0896:                                if (!"text".equals(columns[i].sqlType)
0897:                                        && !"ntext".equals(columns[i].sqlType)
0898:                                        && !"image".equals(columns[i].sqlType)
0899:                                        && columns[i].tableName != null) {
0900:                                    if (count > 0) {
0901:                                        sql.append(" AND ");
0902:                                    }
0903:                                    sql.append(columns[i].realName);
0904:                                    sql.append("=?");
0905:                                    count++;
0906:                                    params.add(buildParameter(sql.length() - 1,
0907:                                            columns[i], currentRow[i],
0908:                                            connection.getUseUnicode()));
0909:                                }
0910:                            }
0911:                        }
0912:                    }
0913:                }
0914:                return (ParamInfo[]) params
0915:                        .toArray(new ParamInfo[params.size()]);
0916:            }
0917:
0918:            /**
0919:             * Refreshes a result set row from keyed tables.
0920:             * <p/>
0921:             * If all the tables in the result set have primary keys then the result
0922:             * set row can be refreshed by refetching the individual table rows.
0923:             *
0924:             * @throws SQLException if an error occurs
0925:             */
0926:            protected void refreshKeyedRows() throws SQLException {
0927:                //
0928:                // Construct a SELECT statement
0929:                //
0930:                StringBuffer sql = new StringBuffer(100 + columns.length * 10);
0931:                sql.append("SELECT ");
0932:                int count = 0;
0933:                for (int i = 0; i < columns.length; i++) {
0934:                    if (!columns[i].isKey && columns[i].tableName != null) {
0935:                        if (count > 0) {
0936:                            sql.append(',');
0937:                        }
0938:                        sql.append(columns[i].realName);
0939:                        count++;
0940:                    }
0941:                }
0942:                if (count == 0) {
0943:                    // No non key columns in this table?
0944:                    return;
0945:                }
0946:                sql.append(" FROM ");
0947:                sql.append(tableName);
0948:                //
0949:                // Construct a where clause using keyed columns only
0950:                //
0951:                ArrayList params = new ArrayList();
0952:                buildWhereClause(sql, params, true);
0953:                ParamInfo parameters[] = (ParamInfo[]) params
0954:                        .toArray(new ParamInfo[params.size()]);
0955:                //
0956:                // Execute the select
0957:                //
0958:                TdsCore tds = statement.getTds();
0959:                tds.executeSQL(sql.toString(), null, parameters, false, 0,
0960:                        statement.getMaxRows(), statement.getMaxFieldSize(),
0961:                        true);
0962:                if (!tds.isEndOfResponse()) {
0963:                    if (tds.getMoreResults() && tds.getNextRow()) {
0964:                        // refresh the row data
0965:                        Object col[] = tds.getRowData();
0966:                        count = 0;
0967:                        for (int i = 0; i < columns.length; i++) {
0968:                            if (!columns[i].isKey) {
0969:                                currentRow[i] = col[count++];
0970:                            }
0971:                        }
0972:                    } else {
0973:                        currentRow = null;
0974:                    }
0975:                } else {
0976:                    currentRow = null;
0977:                }
0978:                tds.clearResponseQueue();
0979:                statement.getMessages().checkErrors();
0980:                if (currentRow == null) {
0981:                    rowData.set(pos - 1, null);
0982:                    rowDeleted = true;
0983:                }
0984:            }
0985:
0986:            /**
0987:             * Refreshes the row by rereading the result set.
0988:             * <p/>
0989:             * Obviously very slow on large result sets but may be the only option if
0990:             * tables do not have keys.
0991:             */
0992:            protected void refreshReRead() throws SQLException {
0993:                int savePos = pos;
0994:                cursorCreate();
0995:                absolute(savePos);
0996:            }
0997:
0998:            //
0999:            //  -------------------- java.sql.ResultSet methods -------------------
1000:            //
1001:
1002:            public void setFetchSize(int size) throws SQLException {
1003:                sizeChanged = size != fetchSize;
1004:                super .setFetchSize(size);
1005:            }
1006:
1007:            public void afterLast() throws SQLException {
1008:                checkOpen();
1009:                checkScrollable();
1010:                if (pos != POS_AFTER_LAST) {
1011:                    cursorFetch(rowsInResult + 1);
1012:                }
1013:            }
1014:
1015:            public void beforeFirst() throws SQLException {
1016:                checkOpen();
1017:                checkScrollable();
1018:
1019:                if (pos != POS_BEFORE_FIRST) {
1020:                    cursorFetch(0);
1021:                }
1022:            }
1023:
1024:            public void cancelRowUpdates() throws SQLException {
1025:                checkOpen();
1026:                checkUpdateable();
1027:                if (onInsertRow) {
1028:                    throw new SQLException(Messages
1029:                            .get("error.resultset.insrow"), "24000");
1030:                }
1031:                if (updateRow != null) {
1032:                    rowUpdated = false;
1033:                    for (int i = 0; i < updateRow.length; i++) {
1034:                        if (updateRow[i] != null) {
1035:                            updateRow[i].clearInValue();
1036:                        }
1037:                    }
1038:                }
1039:            }
1040:
1041:            public void close() throws SQLException {
1042:                if (!closed) {
1043:                    try {
1044:                        cursorClose();
1045:                    } finally {
1046:                        closed = true;
1047:                        statement = null;
1048:                    }
1049:                }
1050:            }
1051:
1052:            public void deleteRow() throws SQLException {
1053:                checkOpen();
1054:                checkUpdateable();
1055:
1056:                if (currentRow == null) {
1057:                    throw new SQLException(Messages
1058:                            .get("error.resultset.norow"), "24000");
1059:                }
1060:
1061:                if (onInsertRow) {
1062:                    throw new SQLException(Messages
1063:                            .get("error.resultset.insrow"), "24000");
1064:                }
1065:
1066:                //
1067:                // Construct an SQL DELETE statement
1068:                //
1069:                StringBuffer sql = new StringBuffer(128);
1070:                ArrayList params = new ArrayList();
1071:                sql.append("DELETE FROM ");
1072:                sql.append(tableName);
1073:                //
1074:                // Create the WHERE clause
1075:                //
1076:                ParamInfo parameters[] = buildWhereClause(sql, params, false);
1077:                //
1078:                // Execute the delete statement
1079:                //
1080:                updateTds.executeSQL(sql.toString(), null, parameters, false,
1081:                        0, statement.getMaxRows(), statement.getMaxFieldSize(),
1082:                        true);
1083:                int updateCount = 0;
1084:                while (!updateTds.isEndOfResponse()) {
1085:                    if (!updateTds.getMoreResults()) {
1086:                        if (updateTds.isUpdateCount()) {
1087:                            updateCount = updateTds.getUpdateCount();
1088:                        }
1089:                    }
1090:                }
1091:                updateTds.clearResponseQueue();
1092:                statement.getMessages().checkErrors();
1093:                if (updateCount == 0) {
1094:                    // No delete. Possibly row was changed on database by another user?
1095:                    throw new SQLException(Messages
1096:                            .get("error.resultset.deletefail"), "24000");
1097:                }
1098:                rowDeleted = true;
1099:                currentRow = null;
1100:                if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
1101:                    // Leave a 'hole' in the result set array.
1102:                    rowData.set(pos - 1, null);
1103:                }
1104:            }
1105:
1106:            public void insertRow() throws SQLException {
1107:                checkOpen();
1108:
1109:                checkUpdateable();
1110:
1111:                if (!onInsertRow) {
1112:                    throw new SQLException(Messages
1113:                            .get("error.resultset.notinsrow"), "24000");
1114:                }
1115:
1116:                if (!tempResultSet) {
1117:                    //
1118:                    // Construct an SQL INSERT statement
1119:                    //
1120:                    StringBuffer sql = new StringBuffer(128);
1121:                    ArrayList params = new ArrayList();
1122:                    sql.append("INSERT INTO ");
1123:                    sql.append(tableName);
1124:                    int sqlLen = sql.length();
1125:                    //
1126:                    // Create column list
1127:                    //
1128:                    sql.append(" (");
1129:                    int count = 0;
1130:                    for (int i = 0; i < columnCount; i++) {
1131:                        if (insertRow[i] != null) {
1132:                            if (count > 0) {
1133:                                sql.append(", ");
1134:                            }
1135:                            sql.append(columns[i].realName);
1136:                            count++;
1137:                        }
1138:                    }
1139:                    //
1140:                    // Create new values list
1141:                    //
1142:                    sql.append(") VALUES(");
1143:                    count = 0;
1144:                    for (int i = 0; i < columnCount; i++) {
1145:                        if (insertRow[i] != null) {
1146:                            if (count > 0) {
1147:                                sql.append(", ");
1148:                            }
1149:                            sql.append('?');
1150:                            insertRow[i].markerPos = sql.length() - 1;
1151:                            params.add(insertRow[i]);
1152:                            count++;
1153:                        }
1154:                    }
1155:                    sql.append(')');
1156:                    if (count == 0) {
1157:                        // Empty insert
1158:                        sql.setLength(sqlLen);
1159:                        if (isSybase) {
1160:                            sql.append(" VALUES()");
1161:                        } else {
1162:                            sql.append(" DEFAULT VALUES");
1163:                        }
1164:                    }
1165:                    ParamInfo parameters[] = (ParamInfo[]) params
1166:                            .toArray(new ParamInfo[params.size()]);
1167:                    //
1168:                    // execute the insert statement
1169:                    //
1170:                    updateTds.executeSQL(sql.toString(), null, parameters,
1171:                            false, 0, statement.getMaxRows(), statement
1172:                                    .getMaxFieldSize(), true);
1173:                    int updateCount = 0;
1174:                    while (!updateTds.isEndOfResponse()) {
1175:                        if (!updateTds.getMoreResults()) {
1176:                            if (updateTds.isUpdateCount()) {
1177:                                updateCount = updateTds.getUpdateCount();
1178:                            }
1179:                        }
1180:                    }
1181:                    updateTds.clearResponseQueue();
1182:                    statement.getMessages().checkErrors();
1183:                    if (updateCount < 1) {
1184:                        // No Insert. Probably will not get here as duplicate key etc
1185:                        // will have already been reported as an exception.
1186:                        throw new SQLException(Messages
1187:                                .get("error.resultset.insertfail"), "24000");
1188:                    }
1189:                }
1190:                //
1191:                if (resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE
1192:                        || (resultSetType == ResultSet.TYPE_FORWARD_ONLY && cursorName == null)) {
1193:                    //
1194:                    // Now insert copy of row into result set buffer
1195:                    //
1196:                    ConnectionJDBC2 con = (ConnectionJDBC2) statement
1197:                            .getConnection();
1198:                    Object row[] = newRow();
1199:                    for (int i = 0; i < insertRow.length; i++) {
1200:                        if (insertRow[i] != null) {
1201:                            row[i] = Support.convert(con, insertRow[i].value,
1202:                                    columns[i].jdbcType, con.getCharset());
1203:                        }
1204:                    }
1205:                    rowData.add(row);
1206:                }
1207:                rowsInResult++;
1208:                initialRowCnt++;
1209:                //
1210:                // Clear row data
1211:                //
1212:                for (int i = 0; insertRow != null && i < insertRow.length; i++) {
1213:                    if (insertRow[i] != null) {
1214:                        insertRow[i].clearInValue();
1215:                    }
1216:                }
1217:            }
1218:
1219:            public void moveToCurrentRow() throws SQLException {
1220:                checkOpen();
1221:                checkUpdateable();
1222:                insertRow = null;
1223:                onInsertRow = false;
1224:            }
1225:
1226:            public void moveToInsertRow() throws SQLException {
1227:                checkOpen();
1228:                checkUpdateable();
1229:                insertRow = new ParamInfo[columnCount];
1230:                onInsertRow = true;
1231:            }
1232:
1233:            public void refreshRow() throws SQLException {
1234:                checkOpen();
1235:
1236:                if (onInsertRow) {
1237:                    throw new SQLException(Messages
1238:                            .get("error.resultset.insrow"), "24000");
1239:                }
1240:
1241:                //
1242:                // If row is being updated discard updates now
1243:                //
1244:                if (concurrency != ResultSet.CONCUR_READ_ONLY) {
1245:                    cancelRowUpdates();
1246:                    rowUpdated = false;
1247:                }
1248:                if (resultSetType == ResultSet.TYPE_FORWARD_ONLY
1249:                        || currentRow == null) {
1250:                    // Do not try and refresh the row in these cases.
1251:                    return;
1252:                }
1253:                //
1254:                // If result set is keyed we can refresh the row data from the
1255:                // database using the key.
1256:                // NB. MS SQL Server #Temporary tables with keys are not identified correctly
1257:                // in the column meta data sent after 'for browse'. This means that
1258:                // temporary tables can not be used with this logic.
1259:                //
1260:                if (isKeyed) {
1261:                    // OK all tables are keyed
1262:                    refreshKeyedRows();
1263:                } else {
1264:                    // No good have to use brute force approach
1265:                    refreshReRead();
1266:                }
1267:            }
1268:
1269:            public void updateRow() throws SQLException {
1270:                checkOpen();
1271:                checkUpdateable();
1272:
1273:                rowUpdated = false;
1274:                rowDeleted = false;
1275:                if (currentRow == null) {
1276:                    throw new SQLException(Messages
1277:                            .get("error.resultset.norow"), "24000");
1278:                }
1279:
1280:                if (onInsertRow) {
1281:                    throw new SQLException(Messages
1282:                            .get("error.resultset.insrow"), "24000");
1283:                }
1284:
1285:                if (updateRow == null) {
1286:                    // Nothing to update
1287:                    return;
1288:                }
1289:                boolean keysChanged = false;
1290:                //
1291:                // Construct an SQL UPDATE statement
1292:                //
1293:                StringBuffer sql = new StringBuffer(128);
1294:                ArrayList params = new ArrayList();
1295:                sql.append("UPDATE ");
1296:                sql.append(tableName);
1297:                //
1298:                // OK now create assign new values
1299:                //
1300:                sql.append(" SET ");
1301:                int count = 0;
1302:                for (int i = 0; i < columnCount; i++) {
1303:                    if (updateRow[i] != null) {
1304:                        if (count > 0) {
1305:                            sql.append(", ");
1306:                        }
1307:                        sql.append(columns[i].realName);
1308:                        sql.append("=?");
1309:                        updateRow[i].markerPos = sql.length() - 1;
1310:                        params.add(updateRow[i]);
1311:                        count++;
1312:                        if (columns[i].isKey) {
1313:                            // Key is changing so in memory row will need to be deleted
1314:                            // and reinserted at end of row buffer.
1315:                            keysChanged = true;
1316:                        }
1317:                    }
1318:                }
1319:                if (count == 0) {
1320:                    // There are no columns to update in this table
1321:                    // so bail out now.
1322:                    return;
1323:                }
1324:                //
1325:                // Now construct where clause
1326:                //
1327:                ParamInfo parameters[] = buildWhereClause(sql, params, false);
1328:                //
1329:                // Now execute update
1330:                //
1331:                updateTds.executeSQL(sql.toString(), null, parameters, false,
1332:                        0, statement.getMaxRows(), statement.getMaxFieldSize(),
1333:                        true);
1334:                int updateCount = 0;
1335:                while (!updateTds.isEndOfResponse()) {
1336:                    if (!updateTds.getMoreResults()) {
1337:                        if (updateTds.isUpdateCount()) {
1338:                            updateCount = updateTds.getUpdateCount();
1339:                        }
1340:                    }
1341:                }
1342:                updateTds.clearResponseQueue();
1343:                statement.getMessages().checkErrors();
1344:
1345:                if (updateCount == 0) {
1346:                    // No update. Possibly row was changed on database by another user?
1347:                    throw new SQLException(Messages
1348:                            .get("error.resultset.updatefail"), "24000");
1349:                }
1350:                //
1351:                // Update local copy of data
1352:                //
1353:                if (resultSetType != ResultSet.TYPE_SCROLL_INSENSITIVE) {
1354:                    // Make in memory copy reflect database update
1355:                    // Could use refreshRow but this is much faster.
1356:                    ConnectionJDBC2 con = (ConnectionJDBC2) statement
1357:                            .getConnection();
1358:                    for (int i = 0; i < updateRow.length; i++) {
1359:                        if (updateRow[i] != null) {
1360:                            if (updateRow[i].value instanceof  byte[]
1361:                                    && (columns[i].jdbcType == Types.CHAR
1362:                                            || columns[i].jdbcType == Types.VARCHAR || columns[i].jdbcType == Types.LONGVARCHAR)) {
1363:                                // Need to handle byte[] to varchar otherwise field
1364:                                // will be set to hex string rather than characters.
1365:                                try {
1366:                                    currentRow[i] = new String(
1367:                                            (byte[]) updateRow[i].value, con
1368:                                                    .getCharset());
1369:                                } catch (UnsupportedEncodingException e) {
1370:                                    currentRow[i] = new String(
1371:                                            (byte[]) updateRow[i].value);
1372:                                }
1373:                            } else {
1374:                                currentRow[i] = Support.convert(con,
1375:                                        updateRow[i].value,
1376:                                        columns[i].jdbcType, con.getCharset());
1377:                            }
1378:                        }
1379:                    }
1380:                }
1381:                //
1382:                // Update state of cached row data
1383:                //
1384:                if (keysChanged
1385:                        && resultSetType >= ResultSet.TYPE_SCROLL_SENSITIVE) {
1386:                    // Leave hole at current position and add updated row to end of set
1387:                    rowData.add(currentRow);
1388:                    rowsInResult = rowData.size();
1389:                    rowData.set(pos - 1, null);
1390:                    currentRow = null;
1391:                    rowDeleted = true;
1392:                } else {
1393:                    rowUpdated = true;
1394:                }
1395:                //
1396:                // Clear update values
1397:                //
1398:                cancelRowUpdates();
1399:            }
1400:
1401:            public boolean first() throws SQLException {
1402:                checkOpen();
1403:                checkScrollable();
1404:                return cursorFetch(1);
1405:            }
1406:
1407:            public boolean isLast() throws SQLException {
1408:                checkOpen();
1409:
1410:                return (pos == rowsInResult) && (rowsInResult != 0);
1411:            }
1412:
1413:            public boolean last() throws SQLException {
1414:                checkOpen();
1415:                checkScrollable();
1416:                return cursorFetch(rowsInResult);
1417:            }
1418:
1419:            public boolean next() throws SQLException {
1420:                checkOpen();
1421:                if (pos != POS_AFTER_LAST) {
1422:                    return cursorFetch(pos + 1);
1423:                } else {
1424:                    return false;
1425:                }
1426:            }
1427:
1428:            public boolean previous() throws SQLException {
1429:                checkOpen();
1430:                checkScrollable();
1431:                if (pos == POS_AFTER_LAST) {
1432:                    pos = rowsInResult + 1;
1433:                }
1434:                return cursorFetch(pos - 1);
1435:            }
1436:
1437:            public boolean rowDeleted() throws SQLException {
1438:                checkOpen();
1439:
1440:                return rowDeleted;
1441:            }
1442:
1443:            public boolean rowInserted() throws SQLException {
1444:                checkOpen();
1445:
1446:                //         return pos > initialRowCnt;
1447:                return false; // Same as MSCursorResultSet
1448:            }
1449:
1450:            public boolean rowUpdated() throws SQLException {
1451:                checkOpen();
1452:
1453:                //         return rowUpdated;
1454:                return false; // Same as MSCursorResultSet
1455:            }
1456:
1457:            public boolean absolute(int row) throws SQLException {
1458:                checkOpen();
1459:                checkScrollable();
1460:                if (row < 1) {
1461:                    row = (rowsInResult + 1) + row;
1462:                }
1463:
1464:                return cursorFetch(row);
1465:            }
1466:
1467:            public boolean relative(int row) throws SQLException {
1468:                checkScrollable();
1469:                if (pos == POS_AFTER_LAST) {
1470:                    return absolute((rowsInResult + 1) + row);
1471:                } else {
1472:                    return absolute(pos + row);
1473:                }
1474:            }
1475:
1476:            public String getCursorName() throws SQLException {
1477:                checkOpen();
1478:                // Hide internal cursor names
1479:                if (cursorName != null && !cursorName.startsWith("_jtds")) {
1480:                    return this .cursorName;
1481:                }
1482:                throw new SQLException(Messages
1483:                        .get("error.resultset.noposupdate"), "24000");
1484:            }
1485:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.