Source Code Cross Referenced for ConnectionJDBC2.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.lang.ref.WeakReference;
0021:        import java.sql.CallableStatement;
0022:        import java.sql.DatabaseMetaData;
0023:        import java.sql.PreparedStatement;
0024:        import java.sql.SQLException;
0025:        import java.sql.SQLWarning;
0026:        import java.sql.Savepoint;
0027:        import java.sql.Statement;
0028:        import java.sql.Types;
0029:        import java.sql.ResultSet;
0030:        import java.net.UnknownHostException;
0031:        import java.io.*;
0032:        import java.util.ArrayList;
0033:        import java.util.Collection;
0034:        import java.util.HashMap;
0035:        import java.util.Iterator;
0036:        import java.util.Map;
0037:        import java.util.Properties;
0038:        import java.util.HashSet;
0039:        import java.util.Random;
0040:
0041:        import net.sourceforge.jtds.jdbc.cache.*;
0042:        import net.sourceforge.jtds.util.*;
0043:
0044:        /**
0045:         * jTDS implementation of the java.sql.Connection interface.
0046:         * <p>
0047:         * Implementation notes:
0048:         * <ol>
0049:         * <li>Environment setting code carried over from old jTDS otherwise
0050:         *     generally a new implementation of Connection.
0051:         * <li>Connection properties and SQLException text messages are loaded from
0052:         *     a properties file.
0053:         * <li>Character set choices are also loaded from a resource file and the original
0054:         *     Encoder class has gone.
0055:         * <li>Prepared SQL statements are converted to procedures in the prepareSQL method.
0056:         * <li>Use of Stored procedures is optional and controlled via connection property.
0057:         * <li>This Connection object maintains a table of weak references to associated
0058:         *     statements. This allows the connection object to control the statements (for
0059:         *     example to close them) but without preventing them being garbage collected in
0060:         *     a pooled environment.
0061:         * </ol>
0062:         *
0063:         * @author Mike Hutchinson
0064:         * @author Alin Sinpalean
0065:         * @version $Id: ConnectionJDBC2.java,v 1.119 2007/07/08 21:43:02 bheineman Exp $
0066:         */
0067:        public class ConnectionJDBC2 implements  java.sql.Connection {
0068:            /**
0069:             * SQL query to determine the server charset on Sybase.
0070:             */
0071:            private static final String SYBASE_SERVER_CHARSET_QUERY = "select name from master.dbo.syscharsets where id ="
0072:                    + " (select value from master.dbo.sysconfigures where config=131)";
0073:
0074:            /**
0075:             * SQL query to determine the server charset on MS SQL Server 6.5.
0076:             */
0077:            private static final String SQL_SERVER_65_CHARSET_QUERY = "select name from master.dbo.syscharsets where id ="
0078:                    + " (select csid from master.dbo.syscharsets, master.dbo.sysconfigures"
0079:                    + " where config=1123 and id = value)";
0080:
0081:            /** Sybase initial connection string. */
0082:            private static final String SYBASE_INITIAL_SQL = "SET TRANSACTION ISOLATION LEVEL 1\r\n"
0083:                    + "SET CHAINED OFF\r\n"
0084:                    + "SET QUOTED_IDENTIFIER ON\r\n"
0085:                    + "SET TEXTSIZE 2147483647";
0086:            /**
0087:             * SQL Server initial connection string. Also contains a
0088:             * <code>SELECT @@MAX_PRECISION</code> query to retrieve
0089:             * the maximum precision for DECIMAL/NUMERIC data. */
0090:            private static final String SQL_SERVER_INITIAL_SQL = "SELECT @@MAX_PRECISION\r\n"
0091:                    + "SET TRANSACTION ISOLATION LEVEL READ COMMITTED\r\n"
0092:                    + "SET IMPLICIT_TRANSACTIONS OFF\r\n"
0093:                    + "SET QUOTED_IDENTIFIER ON\r\n"
0094:                    + "SET TEXTSIZE 2147483647";
0095:            /**
0096:             * SQL Server custom transaction isolation level.
0097:             */
0098:            public static final int TRANSACTION_SNAPSHOT = 4096;
0099:
0100:            /*
0101:             * Conection attributes
0102:             */
0103:
0104:            /** The orginal connection URL. */
0105:            private final String url;
0106:            /** The server host name. */
0107:            private String serverName;
0108:            /** The server port number. */
0109:            private int portNumber;
0110:            /** The make of SQL Server (sybase/microsoft). */
0111:            private int serverType;
0112:            /** The SQL Server instance. */
0113:            private String instanceName;
0114:            /** The requested database name. */
0115:            private String databaseName;
0116:            /** The current database name. */
0117:            private String currentDatabase;
0118:            /** The Windows Domain name. */
0119:            private String domainName;
0120:            /** The database user ID. */
0121:            private String user;
0122:            /** The user password. */
0123:            private String password;
0124:            /** The server character set. */
0125:            private String serverCharset;
0126:            /** The application name. */
0127:            private String appName;
0128:            /** The program name. */
0129:            private String progName;
0130:            /** Workstation ID. */
0131:            private String wsid;
0132:            /** The server message language. */
0133:            private String language;
0134:            /** The client MAC Address. */
0135:            private String macAddress;
0136:            /** The server protocol version. */
0137:            private int tdsVersion;
0138:            /** The network TCP/IP socket. */
0139:            private final SharedSocket socket;
0140:            /** The cored TDS protocol object. */
0141:            private final TdsCore baseTds;
0142:            /** The initial network packet size. */
0143:            private int netPacketSize = TdsCore.MIN_PKT_SIZE;
0144:            /** User requested packet size. */
0145:            private int packetSize;
0146:            /** SQL Server 2000 collation. */
0147:            private byte collation[];
0148:            /** True if user specifies an explicit charset. */
0149:            private boolean charsetSpecified;
0150:            /** The database product name eg SQL SERVER. */
0151:            private String databaseProductName;
0152:            /** The product version eg 11.92. */
0153:            private String databaseProductVersion;
0154:            /** The major version number eg 11. */
0155:            private int databaseMajorVersion;
0156:            /** The minor version number eg 92. */
0157:            private int databaseMinorVersion;
0158:            /** True if this connection is closed. */
0159:            private boolean closed;
0160:            /** True if this connection is read only. */
0161:            private boolean readOnly;
0162:            /** List of statements associated with this connection. */
0163:            private final ArrayList statements = new ArrayList();
0164:            /** Default transaction isolation level. */
0165:            private int transactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED;
0166:            /** Default auto commit state. */
0167:            private boolean autoCommit = true;
0168:            /** Diagnostc messages for this connection. */
0169:            private final SQLDiagnostic messages;
0170:            /** Connection's current rowcount limit. */
0171:            private int rowCount;
0172:            /** Connection's current maximum field size limit. */
0173:            private int textSize;
0174:            /** Maximum decimal precision. */
0175:            private int maxPrecision = TdsData.DEFAULT_PRECISION_38; // Sybase default
0176:            /** Stored procedure unique ID number. */
0177:            private int spSequenceNo = 1;
0178:            /** Cursor unique ID number. */
0179:            private int cursorSequenceNo = 1;
0180:            /** Procedures in this transaction. */
0181:            private final ArrayList procInTran = new ArrayList();
0182:            /** Java charset for encoding. */
0183:            private CharsetInfo charsetInfo;
0184:            /** Method for preparing SQL used in Prepared Statements. */
0185:            private int prepareSql;
0186:            /** The amount of LOB data to buffer in memory. */
0187:            private long lobBuffer;
0188:            /** The maximum number of statements to keep open. */
0189:            private int maxStatements;
0190:            /** Statement cache.*/
0191:            private StatementCache statementCache;
0192:            /** Send parameters as unicode. */
0193:            private boolean useUnicode = true;
0194:            /** Use named pipe IPC instead of TCP/IP sockets. */
0195:            private boolean namedPipe;
0196:            /** Only return the last update count. */
0197:            private boolean lastUpdateCount;
0198:            /** TCP_NODELAY */
0199:            private boolean tcpNoDelay = true;
0200:            /** Login timeout value in seconds or 0. */
0201:            private int loginTimeout;
0202:            /** Sybase capability mask.*/
0203:            private int sybaseInfo;
0204:            /** True if running distributed transaction. */
0205:            private boolean xaTransaction;
0206:            /** Current emulated XA State eg start/end/prepare etc. */
0207:            private int xaState;
0208:            /** Current XA Transaction ID. */
0209:            private Object xid;
0210:            /** True if driver should emulate distributed transactions. */
0211:            private boolean xaEmulation = true;
0212:            /** Mutual exclusion lock to control access to connection. */
0213:            private final Semaphore mutex = new Semaphore(1);
0214:            /** Socket timeout value in seconds or 0. */
0215:            private int socketTimeout;
0216:            /** SSL setting. */
0217:            private String ssl;
0218:            /** The maximum size of a batch. */
0219:            private int batchSize;
0220:            /** Use metadata cache for prepared statements. */
0221:            private boolean useMetadataCache;
0222:            /** Use fast forward cursors for forward only result sets. */
0223:            private boolean useCursors;
0224:            /** The directory to buffer data to */
0225:            private File bufferDir;
0226:            /** The global buffer memory limit for all connections (in kilobytes). */
0227:            private int bufferMaxMemory;
0228:            /** The minimum number of packets per statement to buffer to memory. */
0229:            private int bufferMinPackets;
0230:            /** Map large types (IMAGE and TEXT/NTEXT) to LOBs by default. */
0231:            private boolean useLOBs;
0232:            /** A cached <code>TdsCore</code> instance to reuse on new statements. */
0233:            private TdsCore cachedTds;
0234:            /** The local address to bind to when connecting to a database via TCP/IP. */
0235:            private String bindAddress;
0236:            /** Force use of jCIFS library on Windows when connecting via named pipes. */
0237:            private boolean useJCIFS;
0238:            /** When doing NTLM authentication, send NTLMv2 response rather than regular response */
0239:            private boolean useNTLMv2 = false;
0240:
0241:            /**
0242:             * Default constructor.
0243:             * <p/>
0244:             * Used for testing.
0245:             */
0246:            private ConnectionJDBC2() {
0247:                url = null;
0248:                socket = null;
0249:                baseTds = null;
0250:                messages = null;
0251:            }
0252:
0253:            /**
0254:             * Create a new database connection.
0255:             *
0256:             * @param url The connection URL starting jdbc:jtds:.
0257:             * @param info The additional connection properties.
0258:             * @throws SQLException
0259:             */
0260:            ConnectionJDBC2(String url, Properties info) throws SQLException {
0261:                this .url = url;
0262:                //
0263:                // Extract properties into instance variables
0264:                //
0265:                unpackProperties(info);
0266:                this .messages = new SQLDiagnostic(serverType);
0267:                //
0268:                // Get the instance port, if it is specified.
0269:                // Named pipes use instance names differently.
0270:                //
0271:                if (instanceName.length() > 0 && !namedPipe) {
0272:                    final MSSqlServerInfo msInfo = new MSSqlServerInfo(
0273:                            serverName);
0274:
0275:                    portNumber = msInfo.getPortForInstance(instanceName);
0276:
0277:                    if (portNumber == -1) {
0278:                        throw new SQLException(Messages.get(
0279:                                "error.msinfo.badinst", serverName,
0280:                                instanceName), "08003");
0281:                    }
0282:                }
0283:
0284:                SharedSocket.setMemoryBudget(bufferMaxMemory * 1024);
0285:                SharedSocket.setMinMemPkts(bufferMinPackets);
0286:                SQLWarning warn;
0287:
0288:                try {
0289:                    Object timer = null;
0290:                    if (loginTimeout > 0) {
0291:                        // Start a login timer
0292:                        timer = TimerThread.getInstance().setTimer(
0293:                                loginTimeout * 1000,
0294:                                new TimerThread.TimerListener() {
0295:                                    public void timerExpired() {
0296:                                        if (socket != null) {
0297:                                            socket.forceClose();
0298:                                        }
0299:                                    }
0300:                                });
0301:                    }
0302:
0303:                    if (namedPipe) {
0304:                        // Use named pipe
0305:                        socket = createNamedPipe(this );
0306:                    } else {
0307:                        // Use plain TCP/IP socket
0308:                        socket = new SharedSocket(this );
0309:                    }
0310:
0311:                    if (timer != null
0312:                            && TimerThread.getInstance().hasExpired(timer)) {
0313:                        // If the timer has expired during the connection phase, close
0314:                        // the socket and throw an exception
0315:                        socket.forceClose();
0316:                        throw new IOException("Login timed out");
0317:                    }
0318:
0319:                    if (charsetSpecified) {
0320:                        loadCharset(serverCharset);
0321:                    } else {
0322:                        // Need a default charset to process login packets for TDS 4.2/5.0
0323:                        // Will discover the actual serverCharset later
0324:                        loadCharset("iso_1");
0325:                        serverCharset = ""; // But don't send charset name to server!
0326:                    }
0327:
0328:                    //
0329:                    // Create TDS protocol object
0330:                    //
0331:                    baseTds = new TdsCore(this , messages);
0332:
0333:                    //
0334:                    // Negotiate SSL connection if required
0335:                    //
0336:                    if (tdsVersion >= Driver.TDS80 && !namedPipe) {
0337:                        baseTds.negotiateSSL(instanceName, ssl);
0338:                    }
0339:
0340:                    //
0341:                    // Now try to login
0342:                    //
0343:                    baseTds.login(serverName, databaseName, user, password,
0344:                            domainName, serverCharset, appName, progName, wsid,
0345:                            language, macAddress, packetSize);
0346:
0347:                    if (timer != null) {
0348:                        // Cancel loginTimer
0349:                        TimerThread.getInstance().cancelTimer(timer);
0350:                    }
0351:
0352:                    //
0353:                    // Save any login warnings so that they will not be overwritten by
0354:                    // the internal configuration SQL statements e.g. setCatalog() etc.
0355:                    //
0356:                    warn = messages.warnings;
0357:
0358:                    // Update the tdsVersion with the value in baseTds. baseTds sets
0359:                    // the TDS version for the socket and there are no other objects
0360:                    // with cached TDS versions at this point.
0361:                    tdsVersion = baseTds.getTdsVersion();
0362:                    if (tdsVersion < Driver.TDS70 && databaseName.length() > 0) {
0363:                        // Need to select the default database
0364:                        setCatalog(databaseName);
0365:                    }
0366:                } catch (UnknownHostException e) {
0367:                    throw Support.linkException(new SQLException(Messages.get(
0368:                            "error.connection.badhost", e.getMessage()),
0369:                            "08S03"), e);
0370:                } catch (IOException e) {
0371:                    if (loginTimeout > 0
0372:                            && e.getMessage().indexOf("timed out") >= 0) {
0373:                        throw Support.linkException(new SQLException(Messages
0374:                                .get("error.connection.timeout"), "HYT01"), e);
0375:                    }
0376:                    throw Support.linkException(new SQLException(Messages.get(
0377:                            "error.connection.ioerror", e.getMessage()),
0378:                            "08S01"), e);
0379:                } catch (SQLException e) {
0380:                    if (loginTimeout > 0
0381:                            && e.getMessage().indexOf("socket closed") >= 0) {
0382:                        throw Support.linkException(new SQLException(Messages
0383:                                .get("error.connection.timeout"), "HYT01"), e);
0384:                    }
0385:
0386:                    throw e;
0387:                }
0388:
0389:                // If charset is still unknown and the collation is not set either,
0390:                // determine the charset by querying (we're using Sybase or SQL Server
0391:                // 6.5)
0392:                if ((serverCharset == null || serverCharset.length() == 0)
0393:                        && collation == null) {
0394:                    loadCharset(determineServerCharset());
0395:                }
0396:
0397:                // Initial database settings.
0398:                // Sets: auto commit mode  = true
0399:                //       transaction isolation = read committed.
0400:                if (serverType == Driver.SYBASE) {
0401:                    baseTds.submitSQL(SYBASE_INITIAL_SQL);
0402:                } else {
0403:                    // Also discover the maximum decimal precision:  28 (default)
0404:                    // or 38 for MS SQL Server 6.5/7, or 38 for 2000 and later.
0405:                    Statement stmt = this .createStatement();
0406:                    ResultSet rs = stmt.executeQuery(SQL_SERVER_INITIAL_SQL);
0407:
0408:                    if (rs.next()) {
0409:                        maxPrecision = rs.getByte(1);
0410:                    }
0411:
0412:                    rs.close();
0413:                    stmt.close();
0414:                }
0415:
0416:                //
0417:                // Restore any login warnings so that the user can retrieve them
0418:                // by calling Connection.getWarnings()
0419:                //
0420:                messages.warnings = warn;
0421:            }
0422:
0423:            /**
0424:             * Creates a {@link SharedSocket} object representing a connection to a named
0425:             * pipe.  If the <code>os.name</code> system property starts with "Windows"
0426:             * (case-insensitive) and the <code>useJCIFS</code> parameter is
0427:             * <code>false</code>, a {@link SharedLocalNamedPipe} object is created.
0428:             * Else a {@link SharedNamedPipe} is created which uses
0429:             * <a href="http://jcifs.samba.org/">jCIFS</a> to provide a pure-Java
0430:             * implementation of Windows named pipes.
0431:             * <p>
0432:             * This method will retry for <code>loginTimeout</code> seconds to create a
0433:             * named pipe if an <code>IOException</code> continues to be thrown stating,
0434:             * "All pipe instances are busy".  If <code>loginTimeout</code> is set to
0435:             * zero (e.g., not set), a default of 20 seconds will be used.
0436:             *
0437:             * @param connection the connection object
0438:             * @return an object representing the named pipe connection
0439:             * @throws IOException on error; if an <code>IOException</code> is thrown with
0440:             * a message stating "All pipe instances are busy", then the method timed out
0441:             * after <code>loginTimeout</code> milliseconds attempting to create a named pipe.
0442:             */
0443:            private SharedSocket createNamedPipe(ConnectionJDBC2 connection)
0444:                    throws IOException {
0445:
0446:                final long loginTimeout = connection.getLoginTimeout();
0447:                final long retryTimeout = (loginTimeout > 0 ? loginTimeout : 20) * 1000;
0448:                final long startLoginTimeout = System.currentTimeMillis();
0449:                final Random random = new Random(startLoginTimeout);
0450:                final boolean isWindowsOS = Support.isWindowsOS();
0451:
0452:                SharedSocket socket = null;
0453:                IOException lastIOException = null;
0454:                int exceptionCount = 0;
0455:
0456:                do {
0457:                    try {
0458:                        if (isWindowsOS && !connection.getUseJCIFS()) {
0459:                            socket = new SharedLocalNamedPipe(connection);
0460:                        } else {
0461:                            socket = new SharedNamedPipe(connection);
0462:                        }
0463:                    } catch (IOException ioe) {
0464:                        exceptionCount++;
0465:                        lastIOException = ioe;
0466:                        if (ioe.getMessage().toLowerCase().indexOf(
0467:                                "all pipe instances are busy") >= 0) {
0468:                            // Per a Microsoft knowledgebase article, wait 200 ms to 1 second each time
0469:                            // we get an "All pipe instances are busy" error.
0470:                            // http://support.microsoft.com/default.aspx?scid=KB;EN-US;165189
0471:                            final int randomWait = random.nextInt(800) + 200;
0472:                            if (Logger.isActive()) {
0473:                                Logger.println("Retry #" + exceptionCount
0474:                                        + " Wait " + randomWait + " ms: "
0475:                                        + ioe.getMessage());
0476:                            }
0477:                            try {
0478:                                Thread.sleep(randomWait);
0479:                            } catch (InterruptedException ie) {
0480:                                // Do nothing; retry again
0481:                            }
0482:                        } else {
0483:                            throw ioe;
0484:                        }
0485:                    }
0486:                } while (socket == null
0487:                        && (System.currentTimeMillis() - startLoginTimeout) < retryTimeout);
0488:
0489:                if (socket == null) {
0490:                    final IOException ioException = new IOException(
0491:                            "Connection timed out to named pipe");
0492:                    Support.linkException(ioException, lastIOException);
0493:                    throw ioException;
0494:                }
0495:
0496:                return socket;
0497:            }
0498:
0499:            /**
0500:             * Retrive the shared socket.
0501:             *
0502:             * @return The <code>SharedSocket</code> object.
0503:             */
0504:            SharedSocket getSocket() {
0505:                return this .socket;
0506:            }
0507:
0508:            /**
0509:             * Retrieve the TDS protocol version.
0510:             *
0511:             * @return The TDS version as an <code>int</code>.
0512:             */
0513:            int getTdsVersion() {
0514:                return this .tdsVersion;
0515:            }
0516:
0517:            /**
0518:             * Retrieves the next unique stored procedure name.
0519:             * <p>Notes:
0520:             * <ol>
0521:             * <li>Some versions of Sybase require an id with
0522:             * a length of &lt;= 10.
0523:             * <li>The format of this name works for sybase and Microsoft
0524:             * and allows for 16M names per session.
0525:             * <li>The leading '#jtds' indicates this is a temporary procedure and
0526:             * the '#' is removed by the lower level TDS5 routines.
0527:             * </ol>
0528:             * Not synchronized because it's only called from the synchronized
0529:             * {@link #prepareSQL} method.
0530:             *
0531:             * @return the next temporary SP name as a <code>String</code>
0532:             */
0533:            String getProcName() {
0534:                String seq = "000000"
0535:                        + Integer.toHexString(spSequenceNo++).toUpperCase();
0536:
0537:                return "#jtds" + seq.substring(seq.length() - 6, seq.length());
0538:            }
0539:
0540:            /**
0541:             * Retrieves the next unique cursor name.
0542:             *
0543:             * @return the next cursor name as a <code>String</code>
0544:             */
0545:            synchronized String getCursorName() {
0546:                String seq = "000000"
0547:                        + Integer.toHexString(cursorSequenceNo++).toUpperCase();
0548:
0549:                return "_jtds" + seq.substring(seq.length() - 6, seq.length());
0550:            }
0551:
0552:            /**
0553:             * Try to convert the SQL statement into a statement prepare.
0554:             * <p>
0555:             * Synchronized because it accesses the procedure cache and the
0556:             * <code>baseTds</code>, but the method call also needs to made in a
0557:             * <code>synchronized (connection)</code> block together with the execution
0558:             * (if the prepared statement is actually executed) to ensure the
0559:             * transaction isn't rolled back between this method call and the actual
0560:             * execution.
0561:             *
0562:             * @param pstmt        the target prepared statement
0563:             * @param sql          the SQL statement to prepare
0564:             * @param params       the parameters
0565:             * @param returnKeys   indicates whether the statement will return
0566:             *                     generated keys
0567:             * @param cursorNeeded indicates whether a cursor prepare is needed
0568:             * @return the SQL procedure name as a <code>String</code> or null if the
0569:             *         SQL cannot be prepared
0570:             */
0571:            synchronized String prepareSQL(JtdsPreparedStatement pstmt,
0572:                    String sql, ParamInfo[] params, boolean returnKeys,
0573:                    boolean cursorNeeded) throws SQLException {
0574:                if (prepareSql == TdsCore.UNPREPARED
0575:                        || prepareSql == TdsCore.EXECUTE_SQL) {
0576:                    return null; // User selected not to use procs
0577:                }
0578:
0579:                if (serverType == Driver.SYBASE) {
0580:                    if (tdsVersion != Driver.TDS50) {
0581:                        return null; // No longer support stored procs with 4.2
0582:                    }
0583:
0584:                    if (returnKeys) {
0585:                        return null; // Sybase cannot use @@IDENTITY in proc
0586:                    }
0587:
0588:                    if (cursorNeeded) {
0589:                        //
0590:                        // We are going to use the CachedResultSet so there is
0591:                        // no point in preparing the SQL as it will be discarded
0592:                        // in favour of a version with "FOR BROWSE" appended.
0593:                        //
0594:                        return null;
0595:                    }
0596:                }
0597:
0598:                //
0599:                // Check parameters set and obtain native types
0600:                //
0601:                for (int i = 0; i < params.length; i++) {
0602:                    if (!params[i].isSet) {
0603:                        throw new SQLException(Messages.get(
0604:                                "error.prepare.paramnotset", Integer
0605:                                        .toString(i + 1)), "07000");
0606:                    }
0607:
0608:                    TdsData.getNativeType(this , params[i]);
0609:
0610:                    if (serverType == Driver.SYBASE) {
0611:                        if ("text".equals(params[i].sqlType)
0612:                                || "image".equals(params[i].sqlType)) {
0613:                            return null; // Sybase does not support text/image params
0614:                        }
0615:                    }
0616:                }
0617:
0618:                String key = Support.getStatementKey(sql, params, serverType,
0619:                        getCatalog(), autoCommit, cursorNeeded);
0620:
0621:                //
0622:                // See if we have already built this one
0623:                //
0624:                ProcEntry proc = (ProcEntry) statementCache.get(key);
0625:
0626:                if (proc != null) {
0627:                    //
0628:                    // Yes found in cache OK
0629:                    //
0630:
0631:                    // If already used by the statement, decrement use count
0632:                    if (pstmt.handles != null && pstmt.handles.contains(proc)) {
0633:                        proc.release();
0634:                    }
0635:
0636:                    pstmt.setColMetaData(proc.getColMetaData());
0637:                    if (serverType == Driver.SYBASE) {
0638:                        pstmt.setParamMetaData(proc.getParamMetaData());
0639:                    }
0640:                } else {
0641:                    //
0642:                    // No, so create the stored procedure now
0643:                    //
0644:                    proc = new ProcEntry();
0645:
0646:                    if (serverType == Driver.SQLSERVER) {
0647:                        proc.setName(baseTds.microsoftPrepare(sql, params,
0648:                                cursorNeeded, pstmt.getResultSetType(), pstmt
0649:                                        .getResultSetConcurrency()));
0650:
0651:                        if (proc.toString() == null) {
0652:                            proc.setType(ProcEntry.PREP_FAILED);
0653:                        } else if (prepareSql == TdsCore.TEMPORARY_STORED_PROCEDURES) {
0654:                            proc.setType(ProcEntry.PROCEDURE);
0655:                        } else {
0656:                            proc.setType((cursorNeeded) ? ProcEntry.CURSOR
0657:                                    : ProcEntry.PREPARE);
0658:                            // Meta data may be returned by sp_prepare
0659:                            proc.setColMetaData(baseTds.getColumns());
0660:                            pstmt.setColMetaData(proc.getColMetaData());
0661:                        }
0662:                        // TODO Find some way of getting parameter meta data for MS
0663:                    } else {
0664:                        proc.setName(baseTds.sybasePrepare(sql, params));
0665:
0666:                        if (proc.toString() == null) {
0667:                            proc.setType(ProcEntry.PREP_FAILED);
0668:                        } else {
0669:                            proc.setType(ProcEntry.PROCEDURE);
0670:                        }
0671:                        // Sybase gives us lots of useful information about the result set
0672:                        proc.setColMetaData(baseTds.getColumns());
0673:                        proc.setParamMetaData(baseTds.getParameters());
0674:                        pstmt.setColMetaData(proc.getColMetaData());
0675:                        pstmt.setParamMetaData(proc.getParamMetaData());
0676:                    }
0677:                    // OK we have built a proc so add it to the cache.
0678:                    addCachedProcedure(key, proc);
0679:                }
0680:                // Add the handle to the prepared statement so that the handles
0681:                // can be used to clean up the statement cache properly when the
0682:                // prepared statement is closed.
0683:                if (pstmt.handles == null) {
0684:                    pstmt.handles = new HashSet(10);
0685:                }
0686:
0687:                pstmt.handles.add(proc);
0688:
0689:                // Give the user the name will be null if prepare failed
0690:                return proc.toString();
0691:            }
0692:
0693:            /**
0694:             * Add a stored procedure to the cache.
0695:             * <p>
0696:             * Not explicitly synchronized because it's only called by synchronized
0697:             * methods.
0698:             *
0699:             * @param key The signature of the procedure to cache.
0700:             * @param proc The stored procedure descriptor.
0701:             */
0702:            void addCachedProcedure(String key, ProcEntry proc) {
0703:                statementCache.put(key, proc);
0704:
0705:                if (!autoCommit && proc.getType() == ProcEntry.PROCEDURE
0706:                        && serverType == Driver.SQLSERVER) {
0707:                    procInTran.add(key);
0708:                }
0709:            }
0710:
0711:            /**
0712:             * Remove a stored procedure from the cache.
0713:             * <p>
0714:             * Not explicitly synchronized because it's only called by synchronized
0715:             * methods.
0716:             *
0717:             * @param key The signature of the procedure to remove from the cache.
0718:             */
0719:            void removeCachedProcedure(String key) {
0720:                statementCache.remove(key);
0721:
0722:                if (!autoCommit) {
0723:                    procInTran.remove(key);
0724:                }
0725:            }
0726:
0727:            /**
0728:             * Retrieves the maximum statement cache size.
0729:             *
0730:             * @return the maximum statement cache size
0731:             */
0732:            int getMaxStatements() {
0733:                return maxStatements;
0734:            }
0735:
0736:            /**
0737:             * Retrieves the server type.
0738:             *
0739:             * @return the server type as an <code>int</code> where 1 == SQLSERVER and
0740:             *         2 == SYBASE.
0741:             */
0742:            public int getServerType() {
0743:                return this .serverType;
0744:            }
0745:
0746:            /**
0747:             * Sets the network packet size.
0748:             *
0749:             * @param size the new packet size
0750:             */
0751:            void setNetPacketSize(int size) {
0752:                this .netPacketSize = size;
0753:            }
0754:
0755:            /**
0756:             * Retrieves the network packet size.
0757:             *
0758:             * @return the packet size as an <code>int</code>
0759:             */
0760:            int getNetPacketSize() {
0761:                return this .netPacketSize;
0762:            }
0763:
0764:            /**
0765:             * Retrieves the current row count on this connection.
0766:             *
0767:             * @return the row count as an <code>int</code>
0768:             */
0769:            int getRowCount() {
0770:                return this .rowCount;
0771:            }
0772:
0773:            /**
0774:             * Sets the current row count on this connection.
0775:             *
0776:             * @param count the new row count
0777:             */
0778:            void setRowCount(int count) {
0779:                rowCount = count;
0780:            }
0781:
0782:            /**
0783:             * Retrieves the current maximum textsize on this connection.
0784:             *
0785:             * @return the maximum textsize as an <code>int</code>
0786:             */
0787:            public int getTextSize() {
0788:                return textSize;
0789:            }
0790:
0791:            /**
0792:             * Sets the current maximum textsize on this connection.
0793:             *
0794:             * @param textSize the new maximum textsize
0795:             */
0796:            public void setTextSize(int textSize) {
0797:                this .textSize = textSize;
0798:            }
0799:
0800:            /**
0801:             * Retrieves the status of the lastUpdateCount flag.
0802:             *
0803:             * @return the lastUpdateCount flag as a <code>boolean</code>
0804:             */
0805:            boolean getLastUpdateCount() {
0806:                return this .lastUpdateCount;
0807:            }
0808:
0809:            /**
0810:             * Retrieves the maximum decimal precision.
0811:             *
0812:             * @return the precision as an <code>int</code>
0813:             */
0814:            int getMaxPrecision() {
0815:                return this .maxPrecision;
0816:            }
0817:
0818:            /**
0819:             * Retrieves the LOB buffer size.
0820:             *
0821:             * @return the LOB buffer size as a <code>long</code>
0822:             */
0823:            long getLobBuffer() {
0824:                return this .lobBuffer;
0825:            }
0826:
0827:            /**
0828:             * Retrieves the Prepared SQL method.
0829:             *
0830:             * @return the Prepared SQL method
0831:             */
0832:            int getPrepareSql() {
0833:                return this .prepareSql;
0834:            }
0835:
0836:            /**
0837:             * Retrieves the batch size to be used internally.
0838:             *
0839:             * @return the batch size as an <code>int</code>
0840:             */
0841:            int getBatchSize() {
0842:                return this .batchSize;
0843:            }
0844:
0845:            /**
0846:             * Retrieves the boolean indicating whether metadata caching
0847:             * is enabled.
0848:             *
0849:             * @return <code>true</code> if metadata caching is enabled,
0850:             *         <code>false</code> if caching is disabled
0851:             */
0852:            boolean getUseMetadataCache() {
0853:                return this .useMetadataCache;
0854:            }
0855:
0856:            /**
0857:             * Indicates whether fast forward only cursors should be used for forward
0858:             * only result sets.
0859:             *
0860:             * @return <code>true</code> if fast forward cursors are requested
0861:             */
0862:            boolean getUseCursors() {
0863:                return this .useCursors;
0864:            }
0865:
0866:            /**
0867:             * Indicates whether large types (IMAGE and TEXT/NTEXT) should be mapped by
0868:             * default to LOB types or <code>String</code> and <code>byte[]</code>
0869:             * respectively.
0870:             *
0871:             * @return <code>true</code> if the default mapping should be to LOBs,
0872:             *         <code>false</code> otherwise
0873:             */
0874:            boolean getUseLOBs() {
0875:                return this .useLOBs;
0876:            }
0877:
0878:            /**
0879:             * Indicates whether, when doing Windows authentication to an MS SQL server,
0880:             * NTLMv2 should be used. When this is set to "false", LM and NTLM responses
0881:             * are sent to the server, which should work fine in most cases. However,
0882:             * some servers are configured to require LMv2 and NTLMv2. In these rare
0883:             * cases, this property should be set to "true".
0884:             */
0885:            boolean getUseNTLMv2() {
0886:                return this .useNTLMv2;
0887:            }
0888:
0889:            /**
0890:             * Retrieves the application name for this connection.
0891:             *
0892:             * @return the application name
0893:             */
0894:            String getAppName() {
0895:                return this .appName;
0896:            }
0897:
0898:            /**
0899:             * Retrieves the bind address for this connection.
0900:             *
0901:             * @return the bind address
0902:             */
0903:            String getBindAddress() {
0904:                return this .bindAddress;
0905:            }
0906:
0907:            /**
0908:             * Returns the directory where data should be buffered to.
0909:             *
0910:             * @return the directory where data should be buffered to.
0911:             */
0912:            File getBufferDir() {
0913:                return this .bufferDir;
0914:            }
0915:
0916:            /**
0917:             * Retrieves the maximum amount of memory in Kb to buffer for <em>all</em> connections.
0918:             *
0919:             * @return the maximum amount of memory in Kb to buffer for <em>all</em> connections
0920:             */
0921:            int getBufferMaxMemory() {
0922:                return this .bufferMaxMemory;
0923:            }
0924:
0925:            /**
0926:             * Retrieves the minimum number of packets to buffer per {@link Statement} for this connection.
0927:             *
0928:             * @return the minimum number of packets to buffer per {@link Statement}
0929:             */
0930:            int getBufferMinPackets() {
0931:                return this .bufferMinPackets;
0932:            }
0933:
0934:            /**
0935:             * Retrieves the database name for this connection.
0936:             *
0937:             * @return the database name
0938:             */
0939:            String getDatabaseName() {
0940:                return this .databaseName;
0941:            }
0942:
0943:            /**
0944:             * Retrieves the domain name for this connection.
0945:             *
0946:             * @return the domain name
0947:             */
0948:            String getDomainName() {
0949:                return this .domainName;
0950:            }
0951:
0952:            /**
0953:             * Retrieves the instance name for this connection.
0954:             *
0955:             * @return the instance name
0956:             */
0957:            String getInstanceName() {
0958:                return this .instanceName;
0959:            }
0960:
0961:            /**
0962:             * Retrieves the login timeout for this connection.
0963:             *
0964:             * @return the login timeout
0965:             */
0966:            int getLoginTimeout() {
0967:                return this .loginTimeout;
0968:            }
0969:
0970:            /**
0971:             * Retrieves the socket timeout for this connection.
0972:             *
0973:             * @return the socket timeout
0974:             */
0975:            int getSocketTimeout() {
0976:                return this .socketTimeout;
0977:            }
0978:
0979:            /**
0980:             * Retrieves the MAC (ethernet) address for this connection.
0981:             *
0982:             * @return the MAC (ethernet) address
0983:             */
0984:            String getMacAddress() {
0985:                return this .macAddress;
0986:            }
0987:
0988:            /**
0989:             * Retrieves the named pipe setting for this connection.
0990:             *
0991:             * @return the named pipe setting
0992:             */
0993:            boolean getNamedPipe() {
0994:                return this .namedPipe;
0995:            }
0996:
0997:            /**
0998:             * Retrieves the packet size for this connection.
0999:             *
1000:             * @return the packet size
1001:             */
1002:            int getPacketSize() {
1003:                return this .packetSize;
1004:            }
1005:
1006:            /**
1007:             * Retrieves the password for this connection.
1008:             *
1009:             * @return the password
1010:             */
1011:            String getPassword() {
1012:                return this .password;
1013:            }
1014:
1015:            /**
1016:             * Retrieves the port number for this connection.
1017:             *
1018:             * @return the port number
1019:             */
1020:            int getPortNumber() {
1021:                return this .portNumber;
1022:            }
1023:
1024:            /**
1025:             * Retrieves the program name for this connection.
1026:             *
1027:             * @return the program name
1028:             */
1029:            String getProgName() {
1030:                return this .progName;
1031:            }
1032:
1033:            /**
1034:             * Retrieves the server name for this connection.
1035:             *
1036:             * @return the server name
1037:             */
1038:            String getServerName() {
1039:                return this .serverName;
1040:            }
1041:
1042:            /**
1043:             * Retrieves the tcpNoDelay setting for this connection.
1044:             *
1045:             * @return the tcpNoDelay setting
1046:             */
1047:            boolean getTcpNoDelay() {
1048:                return this .tcpNoDelay;
1049:            }
1050:
1051:            /**
1052:             * Retrieves the useJCIFS setting for this connection.
1053:             *
1054:             * @return the useJCIFS setting
1055:             */
1056:            boolean getUseJCIFS() {
1057:                return this .useJCIFS;
1058:            }
1059:
1060:            /**
1061:             * Retrieves the user for this connection.
1062:             *
1063:             * @return the user
1064:             */
1065:            String getUser() {
1066:                return this .user;
1067:            }
1068:
1069:            /**
1070:             * Retrieves the workstation ID (WSID) for this connection.
1071:             *
1072:             * @return the workstation ID (WSID)
1073:             */
1074:            String getWsid() {
1075:                return this .wsid;
1076:            }
1077:
1078:            /**
1079:             * Transfers the properties to the local instance variables.
1080:             *
1081:             * @param info The connection properties Object.
1082:             * @throws SQLException If an invalid property value is found.
1083:             */
1084:            protected void unpackProperties(Properties info)
1085:                    throws SQLException {
1086:
1087:                serverName = info.getProperty(Messages.get(Driver.SERVERNAME));
1088:                portNumber = parseIntegerProperty(info, Driver.PORTNUMBER);
1089:                serverType = parseIntegerProperty(info, Driver.SERVERTYPE);
1090:                databaseName = info.getProperty(Messages
1091:                        .get(Driver.DATABASENAME));
1092:                instanceName = info.getProperty(Messages.get(Driver.INSTANCE));
1093:                domainName = info.getProperty(Messages.get(Driver.DOMAIN));
1094:                user = info.getProperty(Messages.get(Driver.USER));
1095:                password = info.getProperty(Messages.get(Driver.PASSWORD));
1096:                macAddress = info.getProperty(Messages.get(Driver.MACADDRESS));
1097:                appName = info.getProperty(Messages.get(Driver.APPNAME));
1098:                progName = info.getProperty(Messages.get(Driver.PROGNAME));
1099:                wsid = info.getProperty(Messages.get(Driver.WSID));
1100:                serverCharset = info.getProperty(Messages.get(Driver.CHARSET));
1101:                language = info.getProperty(Messages.get(Driver.LANGUAGE));
1102:                bindAddress = info
1103:                        .getProperty(Messages.get(Driver.BINDADDRESS));
1104:                lastUpdateCount = "true".equalsIgnoreCase(info
1105:                        .getProperty(Messages.get(Driver.LASTUPDATECOUNT)));
1106:                useUnicode = "true".equalsIgnoreCase(info.getProperty(Messages
1107:                        .get(Driver.SENDSTRINGPARAMETERSASUNICODE)));
1108:                namedPipe = "true".equalsIgnoreCase(info.getProperty(Messages
1109:                        .get(Driver.NAMEDPIPE)));
1110:                tcpNoDelay = "true".equalsIgnoreCase(info.getProperty(Messages
1111:                        .get(Driver.TCPNODELAY)));
1112:                useCursors = (serverType == Driver.SQLSERVER)
1113:                        && "true".equalsIgnoreCase(info.getProperty(Messages
1114:                                .get(Driver.USECURSORS)));
1115:                useLOBs = "true".equalsIgnoreCase(info.getProperty(Messages
1116:                        .get(Driver.USELOBS)));
1117:                useMetadataCache = "true".equalsIgnoreCase(info
1118:                        .getProperty(Messages.get(Driver.CACHEMETA)));
1119:                xaEmulation = "true".equalsIgnoreCase(info.getProperty(Messages
1120:                        .get(Driver.XAEMULATION)));
1121:                useJCIFS = "true".equalsIgnoreCase(info.getProperty(Messages
1122:                        .get(Driver.USEJCIFS)));
1123:                charsetSpecified = serverCharset.length() > 0;
1124:                useNTLMv2 = "true".equalsIgnoreCase(info.getProperty(Messages
1125:                        .get(Driver.USENTLMV2)));
1126:
1127:                //note:mdb in certain cases (e.g. NTLMv2) the domain name must be
1128:                //  all upper case for things to work.
1129:                if (domainName != null)
1130:                    domainName = domainName.toUpperCase();
1131:
1132:                Integer parsedTdsVersion = DefaultProperties.getTdsVersion(info
1133:                        .getProperty(Messages.get(Driver.TDS)));
1134:                if (parsedTdsVersion == null) {
1135:                    throw new SQLException(Messages.get(
1136:                            "error.connection.badprop", Messages
1137:                                    .get(Driver.TDS)), "08001");
1138:                }
1139:                tdsVersion = parsedTdsVersion.intValue();
1140:
1141:                packetSize = parseIntegerProperty(info, Driver.PACKETSIZE);
1142:                if (packetSize < TdsCore.MIN_PKT_SIZE) {
1143:                    if (tdsVersion >= Driver.TDS70) {
1144:                        // Default of 0 means let the server specify packet size
1145:                        packetSize = (packetSize == 0) ? 0
1146:                                : TdsCore.DEFAULT_MIN_PKT_SIZE_TDS70;
1147:                    } else if (tdsVersion == Driver.TDS42) {
1148:                        // Sensible minimum for older versions of TDS
1149:                        packetSize = TdsCore.MIN_PKT_SIZE;
1150:                    } // else for TDS 5 can auto negotiate
1151:                }
1152:                if (packetSize > TdsCore.MAX_PKT_SIZE) {
1153:                    packetSize = TdsCore.MAX_PKT_SIZE;
1154:                }
1155:                packetSize = (packetSize / 512) * 512;
1156:
1157:                loginTimeout = parseIntegerProperty(info, Driver.LOGINTIMEOUT);
1158:                socketTimeout = parseIntegerProperty(info, Driver.SOTIMEOUT);
1159:                lobBuffer = parseLongProperty(info, Driver.LOBBUFFER);
1160:
1161:                maxStatements = parseIntegerProperty(info, Driver.MAXSTATEMENTS);
1162:
1163:                statementCache = new ProcedureCache(maxStatements);
1164:                prepareSql = parseIntegerProperty(info, Driver.PREPARESQL);
1165:                if (prepareSql < 0) {
1166:                    prepareSql = 0;
1167:                } else if (prepareSql > 3) {
1168:                    prepareSql = 3;
1169:                }
1170:                // For Sybase use equivalent of sp_executesql.
1171:                if (tdsVersion < Driver.TDS70 && prepareSql == TdsCore.PREPARE) {
1172:                    prepareSql = TdsCore.EXECUTE_SQL;
1173:                }
1174:                // For SQL 6.5 sp_executesql not available so use stored procedures.
1175:                if (tdsVersion < Driver.TDS50
1176:                        && prepareSql == TdsCore.EXECUTE_SQL) {
1177:                    prepareSql = TdsCore.TEMPORARY_STORED_PROCEDURES;
1178:                }
1179:
1180:                ssl = info.getProperty(Messages.get(Driver.SSL));
1181:
1182:                batchSize = parseIntegerProperty(info, Driver.BATCHSIZE);
1183:                if (batchSize < 0) {
1184:                    throw new SQLException(Messages.get(
1185:                            "error.connection.badprop", Messages
1186:                                    .get(Driver.BATCHSIZE)), "08001");
1187:                }
1188:
1189:                bufferDir = new File(info.getProperty(Messages
1190:                        .get(Driver.BUFFERDIR)));
1191:                if (!bufferDir.isDirectory()) {
1192:                    if (!bufferDir.mkdirs()) {
1193:                        throw new SQLException(Messages.get(
1194:                                "error.connection.badprop", Messages
1195:                                        .get(Driver.BUFFERDIR)), "08001");
1196:                    }
1197:                }
1198:
1199:                bufferMaxMemory = parseIntegerProperty(info,
1200:                        Driver.BUFFERMAXMEMORY);
1201:                if (bufferMaxMemory < 0) {
1202:                    throw new SQLException(Messages.get(
1203:                            "error.connection.badprop", Messages
1204:                                    .get(Driver.BUFFERMAXMEMORY)), "08001");
1205:                }
1206:
1207:                bufferMinPackets = parseIntegerProperty(info,
1208:                        Driver.BUFFERMINPACKETS);
1209:                if (bufferMinPackets < 1) {
1210:                    throw new SQLException(Messages.get(
1211:                            "error.connection.badprop", Messages
1212:                                    .get(Driver.BUFFERMINPACKETS)), "08001");
1213:                }
1214:            }
1215:
1216:            /**
1217:             * Parse a string property value into an integer value.
1218:             *
1219:             * @param info The connection properties object.
1220:             * @param key The message key used to retrieve the property name.
1221:             * @return The integer value of the string property value.
1222:             * @throws SQLException If the property value can't be parsed.
1223:             */
1224:            private static int parseIntegerProperty(final Properties info,
1225:                    final String key) throws SQLException {
1226:
1227:                final String propertyName = Messages.get(key);
1228:                try {
1229:                    return Integer.parseInt(info.getProperty(propertyName));
1230:                } catch (NumberFormatException e) {
1231:                    throw new SQLException(Messages.get(
1232:                            "error.connection.badprop", propertyName), "08001");
1233:                }
1234:            }
1235:
1236:            /**
1237:             * Parse a string property value into a long value.
1238:             *
1239:             * @param info The connection properties object.
1240:             * @param key The message key used to retrieve the property name.
1241:             * @return The long value of the string property value.
1242:             * @throws SQLException If the property value can't be parsed.
1243:             */
1244:            private static long parseLongProperty(final Properties info,
1245:                    final String key) throws SQLException {
1246:
1247:                final String propertyName = Messages.get(key);
1248:                try {
1249:                    return Long.parseLong(info.getProperty(propertyName));
1250:                } catch (NumberFormatException e) {
1251:                    throw new SQLException(Messages.get(
1252:                            "error.connection.badprop", propertyName), "08001");
1253:                }
1254:            }
1255:
1256:            /**
1257:             * Retrieve the Java charset to use for encoding.
1258:             *
1259:             * @return the Charset name as a <code>String</code>
1260:             */
1261:            protected String getCharset() {
1262:                return charsetInfo.getCharset();
1263:            }
1264:
1265:            /**
1266:             * Retrieve the multibyte status of the current character set.
1267:             *
1268:             * @return <code>boolean</code> true if a multi byte character set
1269:             */
1270:            protected boolean isWideChar() {
1271:                return charsetInfo.isWideChars();
1272:            }
1273:
1274:            /**
1275:             * Retrieve the <code>CharsetInfo</code> instance used by this connection.
1276:             *
1277:             * @return the default <code>CharsetInfo</code> for this connection
1278:             */
1279:            protected CharsetInfo getCharsetInfo() {
1280:                return charsetInfo;
1281:            }
1282:
1283:            /**
1284:             * Retrieve the sendParametersAsUnicode flag.
1285:             *
1286:             * @return <code>boolean</code> true if parameters should be sent as unicode.
1287:             */
1288:            protected boolean getUseUnicode() {
1289:                return this .useUnicode;
1290:            }
1291:
1292:            /**
1293:             * Retrieve the Sybase capability data.
1294:             *
1295:             * @return Capability bit mask as an <code>int</code>.
1296:             */
1297:            protected boolean getSybaseInfo(int flag) {
1298:                return (this .sybaseInfo & flag) != 0;
1299:            }
1300:
1301:            /**
1302:             * Set the Sybase capability data.
1303:             *
1304:             * @param mask The capability bit mask.
1305:             */
1306:            protected void setSybaseInfo(int mask) {
1307:                this .sybaseInfo = mask;
1308:            }
1309:
1310:            /**
1311:             * Called by the protocol to change the current character set.
1312:             *
1313:             * @param charset the server character set name
1314:             */
1315:            protected void setServerCharset(final String charset)
1316:                    throws SQLException {
1317:                // If the user specified a charset, ignore environment changes
1318:                if (charsetSpecified) {
1319:                    Logger.println("Server charset " + charset
1320:                            + ". Ignoring as user requested " + serverCharset
1321:                            + '.');
1322:                    return;
1323:                }
1324:
1325:                if (!charset.equals(serverCharset)) {
1326:                    loadCharset(charset);
1327:
1328:                    if (Logger.isActive()) {
1329:                        Logger.println("Set charset to " + serverCharset + '/'
1330:                                + charsetInfo);
1331:                    }
1332:                }
1333:            }
1334:
1335:            /**
1336:             * Load the Java charset to match the server character set.
1337:             *
1338:             * @param charset the server character set
1339:             */
1340:            private void loadCharset(String charset) throws SQLException {
1341:                // MS SQL Server's iso_1 is Cp1252 not ISO-8859-1!
1342:                if (getServerType() == Driver.SQLSERVER
1343:                        && charset.equalsIgnoreCase("iso_1")) {
1344:                    charset = "Cp1252";
1345:                }
1346:
1347:                // Do not default to any charset; if the charset is not found we want
1348:                // to know about it
1349:                CharsetInfo tmp = CharsetInfo.getCharset(charset);
1350:
1351:                if (tmp == null) {
1352:                    throw new SQLException(Messages.get(
1353:                            "error.charset.nomapping", charset), "2C000");
1354:                }
1355:
1356:                loadCharset(tmp, charset);
1357:                serverCharset = charset;
1358:            }
1359:
1360:            /**
1361:             * Load the Java charset to match the server character set.
1362:             *
1363:             * @param ci the <code>CharsetInfo</code> to load
1364:             */
1365:            private void loadCharset(CharsetInfo ci, String ref)
1366:                    throws SQLException {
1367:                try {
1368:                    "This is a test".getBytes(ci.getCharset());
1369:
1370:                    charsetInfo = ci;
1371:                } catch (UnsupportedEncodingException ex) {
1372:                    throw new SQLException(Messages.get(
1373:                            "error.charset.invalid", ref, ci.getCharset()),
1374:                            "2C000");
1375:                }
1376:
1377:                socket.setCharsetInfo(charsetInfo);
1378:            }
1379:
1380:            /**
1381:             * Discovers the server charset for server versions that do not send
1382:             * <code>ENVCHANGE</code> packets on login ack, by executing a DB
1383:             * vendor/version specific query.
1384:             * <p>
1385:             * Will throw an <code>SQLException</code> if used on SQL Server 7.0 or
1386:             * 2000; the idea is that the charset should already be determined from
1387:             * <code>ENVCHANGE</code> packets for these DB servers.
1388:             * <p>
1389:             * Should only be called from the constructor.
1390:             *
1391:             * @return the default server charset
1392:             * @throws SQLException if an error condition occurs
1393:             */
1394:            private String determineServerCharset() throws SQLException {
1395:                String queryStr = null;
1396:
1397:                switch (serverType) {
1398:                case Driver.SQLSERVER:
1399:                    if (databaseProductVersion.indexOf("6.5") >= 0) {
1400:                        queryStr = SQL_SERVER_65_CHARSET_QUERY;
1401:                    } else {
1402:                        // This will never happen. Versions 7.0 and 2000 of SQL
1403:                        // Server always send ENVCHANGE packets, even over TDS 4.2.
1404:                        throw new SQLException(
1405:                                "Please use TDS protocol version 7.0 or higher");
1406:                    }
1407:                    break;
1408:                case Driver.SYBASE:
1409:                    // There's no need to check for versions here
1410:                    queryStr = SYBASE_SERVER_CHARSET_QUERY;
1411:                    break;
1412:                }
1413:
1414:                Statement stmt = this .createStatement();
1415:                ResultSet rs = stmt.executeQuery(queryStr);
1416:                rs.next();
1417:                String charset = rs.getString(1);
1418:                rs.close();
1419:                stmt.close();
1420:
1421:                return charset;
1422:            }
1423:
1424:            /**
1425:             * Set the default collation for this connection.
1426:             * <p>
1427:             * Set by a SQL Server 2000 environment change packet. The collation
1428:             * consists of the following fields:
1429:             * <ul>
1430:             * <li>bits 0-19  - The locale eg 0x0409 for US English which maps to code
1431:             *                  page 1252 (Latin1_General).
1432:             * <li>bits 20-31 - Reserved.
1433:             * <li>bits 32-39 - Sort order (csid from syscharsets)
1434:             * </ul>
1435:             * If the sort order is non-zero it determines the character set, otherwise
1436:             * the character set is determined by the locale id.
1437:             *
1438:             * @param collation The new collation.
1439:             */
1440:            void setCollation(byte[] collation) throws SQLException {
1441:                String strCollation = "0x" + Support.toHex(collation);
1442:                // If the user specified a charset, ignore environment changes
1443:                if (charsetSpecified) {
1444:                    Logger.println("Server collation " + strCollation
1445:                            + ". Ignoring as user requested " + serverCharset
1446:                            + '.');
1447:                    return;
1448:                }
1449:
1450:                CharsetInfo tmp = CharsetInfo.getCharset(collation);
1451:
1452:                loadCharset(tmp, strCollation);
1453:                this .collation = collation;
1454:
1455:                if (Logger.isActive()) {
1456:                    Logger.println("Set collation to " + strCollation + '/'
1457:                            + charsetInfo);
1458:                }
1459:            }
1460:
1461:            /**
1462:             * Retrieve the SQL Server 2000 default collation.
1463:             *
1464:             * @return The collation as a <code>byte[5]</code>.
1465:             */
1466:            byte[] getCollation() {
1467:                return this .collation;
1468:            }
1469:
1470:            /**
1471:             * Retrieves whether a specific charset was requested on creation. If this
1472:             * is the case, all character data should be encoded/decoded using that
1473:             * charset.
1474:             */
1475:            boolean isCharsetSpecified() {
1476:                return charsetSpecified;
1477:            }
1478:
1479:            /**
1480:             * Called by the protcol to change the current database context.
1481:             *
1482:             * @param newDb The new database selected on the server.
1483:             * @param oldDb The old database as known by the server.
1484:             * @throws SQLException
1485:             */
1486:            protected void setDatabase(final String newDb, final String oldDb)
1487:                    throws SQLException {
1488:                if (currentDatabase != null
1489:                        && !oldDb.equalsIgnoreCase(currentDatabase)) {
1490:                    throw new SQLException(
1491:                            Messages.get("error.connection.dbmismatch", oldDb,
1492:                                    databaseName), "HY096");
1493:                }
1494:
1495:                currentDatabase = newDb;
1496:
1497:                if (Logger.isActive()) {
1498:                    Logger.println("Changed database from " + oldDb + " to "
1499:                            + newDb);
1500:                }
1501:            }
1502:
1503:            /**
1504:             * Update the connection instance with information about the server.
1505:             *
1506:             * @param databaseProductName The server name eg SQL Server.
1507:             * @param databaseMajorVersion The major version eg 11
1508:             * @param databaseMinorVersion The minor version eg 92
1509:             * @param buildNumber The server build number.
1510:             */
1511:            protected void setDBServerInfo(String databaseProductName,
1512:                    int databaseMajorVersion, int databaseMinorVersion,
1513:                    int buildNumber) {
1514:                this .databaseProductName = databaseProductName;
1515:                this .databaseMajorVersion = databaseMajorVersion;
1516:                this .databaseMinorVersion = databaseMinorVersion;
1517:
1518:                if (tdsVersion >= Driver.TDS70) {
1519:                    StringBuffer buf = new StringBuffer(10);
1520:
1521:                    if (databaseMajorVersion < 10) {
1522:                        buf.append('0');
1523:                    }
1524:
1525:                    buf.append(databaseMajorVersion).append('.');
1526:
1527:                    if (databaseMinorVersion < 10) {
1528:                        buf.append('0');
1529:                    }
1530:
1531:                    buf.append(databaseMinorVersion).append('.');
1532:                    buf.append(buildNumber);
1533:
1534:                    while (buf.length() < 10) {
1535:                        buf.insert(6, '0');
1536:                    }
1537:
1538:                    this .databaseProductVersion = buf.toString();
1539:                } else {
1540:                    databaseProductVersion = databaseMajorVersion + "."
1541:                            + databaseMinorVersion;
1542:                }
1543:            }
1544:
1545:            /**
1546:             * Removes a statement object from the list maintained by the connection
1547:             * and cleans up the statement cache if necessary.
1548:             * <p>
1549:             * Synchronized because it accesses the statement list, the statement cache
1550:             * and the <code>baseTds</code>.
1551:             *
1552:             * @param statement the statement to remove
1553:             */
1554:            synchronized void removeStatement(JtdsStatement statement)
1555:                    throws SQLException {
1556:                // Remove the JtdsStatement from the statement list
1557:                synchronized (statements) {
1558:                    for (int i = 0; i < statements.size(); i++) {
1559:                        WeakReference wr = (WeakReference) statements.get(i);
1560:
1561:                        if (wr != null) {
1562:                            Statement stmt = (Statement) wr.get();
1563:
1564:                            // Remove the statement if found but also remove all
1565:                            // statements that have already been garbage collected
1566:                            if (stmt == null || stmt == statement) {
1567:                                statements.set(i, null);
1568:                            }
1569:                        }
1570:                    }
1571:                }
1572:
1573:                if (statement instanceof  JtdsPreparedStatement) {
1574:                    // Clean up the prepared statement cache; getObsoleteHandles will
1575:                    // decrement the usage count for the set of used handles
1576:                    Collection handles = statementCache
1577:                            .getObsoleteHandles(((JtdsPreparedStatement) statement).handles);
1578:
1579:                    if (handles != null) {
1580:                        if (serverType == Driver.SQLSERVER) {
1581:                            // SQL Server unprepare
1582:                            StringBuffer cleanupSql = new StringBuffer(handles
1583:                                    .size() * 32);
1584:                            for (Iterator iterator = handles.iterator(); iterator
1585:                                    .hasNext();) {
1586:                                ProcEntry pe = (ProcEntry) iterator.next();
1587:                                // Could get put back if in a transaction that is
1588:                                // rolled back
1589:                                pe.appendDropSQL(cleanupSql);
1590:                            }
1591:                            if (cleanupSql.length() > 0) {
1592:                                baseTds.executeSQL(cleanupSql.toString(), null,
1593:                                        null, true, 0, -1, -1, true);
1594:                                baseTds.clearResponseQueue();
1595:                            }
1596:                        } else {
1597:                            // Sybase unprepare
1598:                            for (Iterator iterator = handles.iterator(); iterator
1599:                                    .hasNext();) {
1600:                                ProcEntry pe = (ProcEntry) iterator.next();
1601:                                if (pe.toString() != null) {
1602:                                    // Remove the Sybase light weight proc
1603:                                    baseTds.sybaseUnPrepare(pe.toString());
1604:                                }
1605:                            }
1606:                        }
1607:                    }
1608:                }
1609:            }
1610:
1611:            /**
1612:             * Adds a statement object to the list maintained by the connection.
1613:             * <p/>
1614:             * WeakReferences are used so that statements can still be closed and
1615:             * garbage collected even if not explicitly closed by the connection.
1616:             *
1617:             * @param statement statement to add
1618:             */
1619:            void addStatement(JtdsStatement statement) {
1620:                synchronized (statements) {
1621:                    for (int i = 0; i < statements.size(); i++) {
1622:                        WeakReference wr = (WeakReference) statements.get(i);
1623:
1624:                        if (wr == null) {
1625:                            statements.set(i, new WeakReference(statement));
1626:                            return;
1627:                        }
1628:                    }
1629:
1630:                    statements.add(new WeakReference(statement));
1631:                }
1632:            }
1633:
1634:            /**
1635:             * Checks that the connection is still open.
1636:             *
1637:             * @throws SQLException if the connection is closed
1638:             */
1639:            void checkOpen() throws SQLException {
1640:                if (closed) {
1641:                    throw new SQLException(Messages.get("error.generic.closed",
1642:                            "Connection"), "HY010");
1643:                }
1644:            }
1645:
1646:            /**
1647:             * Checks that this connection is in local transaction mode.
1648:             *
1649:             * @param method the method name being tested
1650:             * @throws SQLException if in XA distributed transaction mode
1651:             */
1652:            void checkLocal(String method) throws SQLException {
1653:                if (xaTransaction) {
1654:                    throw new SQLException(Messages.get(
1655:                            "error.connection.badxaop", method), "HY010");
1656:                }
1657:            }
1658:
1659:            /**
1660:             * Reports that user tried to call a method which has not been implemented.
1661:             *
1662:             * @param method the method name to report in the error message
1663:             * @throws SQLException always, with the not implemented message
1664:             */
1665:            static void notImplemented(String method) throws SQLException {
1666:                throw new SQLException(Messages.get("error.generic.notimp",
1667:                        method), "HYC00");
1668:            }
1669:
1670:            /**
1671:             * Retrieves the DBMS major version.
1672:             *
1673:             * @return the version as an <code>int</code>
1674:             */
1675:            public int getDatabaseMajorVersion() {
1676:                return this .databaseMajorVersion;
1677:            }
1678:
1679:            /**
1680:             * Retrieves the DBMS minor version.
1681:             *
1682:             * @return the version as an <code>int</code>
1683:             */
1684:            public int getDatabaseMinorVersion() {
1685:                return this .databaseMinorVersion;
1686:            }
1687:
1688:            /**
1689:             * Retrieves the DBMS product name.
1690:             *
1691:             * @return the name as a <code>String</code>
1692:             */
1693:            String getDatabaseProductName() {
1694:                return this .databaseProductName;
1695:            }
1696:
1697:            /**
1698:             * Retrieves the DBMS product version.
1699:             *
1700:             * @return the version as a <code>String</code>
1701:             */
1702:            String getDatabaseProductVersion() {
1703:                return this .databaseProductVersion;
1704:            }
1705:
1706:            /**
1707:             * Retrieves the original connection URL.
1708:             *
1709:             * @return the connection url as a <code>String</code>
1710:             */
1711:            String getURL() {
1712:                return this .url;
1713:            }
1714:
1715:            /**
1716:             * Retrieves the host and port for this connection.
1717:             * <p>
1718:             * Used to identify same resource manager in XA transactions.
1719:             *
1720:             * @return the hostname and port as a <code>String</code>
1721:             */
1722:            public String getRmHost() {
1723:                return serverName + ':' + portNumber;
1724:            }
1725:
1726:            /**
1727:             * Forces the closed status on the statement if an I/O error has occurred.
1728:             */
1729:            void setClosed() {
1730:                closed = true;
1731:
1732:                // Make sure we release the socket and all data buffered at the socket
1733:                // level
1734:                try {
1735:                    socket.close();
1736:                } catch (IOException e) {
1737:                    // Ignore; shouldn't happen anyway
1738:                }
1739:            }
1740:
1741:            /**
1742:             * Invokes the <code>xp_jtdsxa</code> extended stored procedure on the
1743:             * server.
1744:             * <p/>
1745:             * Synchronized because it accesses the <code>baseTds</code>.
1746:             *
1747:             * @param args the arguments eg cmd, rmid, flags etc.
1748:             * @param data option byte data eg open string xid etc.
1749:             * @return optional byte data eg OLE cookie
1750:             * @throws SQLException if an error condition occurs
1751:             */
1752:            synchronized byte[][] sendXaPacket(int args[], byte[] data)
1753:                    throws SQLException {
1754:                ParamInfo params[] = new ParamInfo[6];
1755:                params[0] = new ParamInfo(Types.INTEGER, null, ParamInfo.RETVAL);
1756:                params[1] = new ParamInfo(Types.INTEGER, new Integer(args[1]),
1757:                        ParamInfo.INPUT);
1758:                params[2] = new ParamInfo(Types.INTEGER, new Integer(args[2]),
1759:                        ParamInfo.INPUT);
1760:                params[3] = new ParamInfo(Types.INTEGER, new Integer(args[3]),
1761:                        ParamInfo.INPUT);
1762:                params[4] = new ParamInfo(Types.INTEGER, new Integer(args[4]),
1763:                        ParamInfo.INPUT);
1764:                params[5] = new ParamInfo(Types.VARBINARY, data,
1765:                        ParamInfo.OUTPUT);
1766:                //
1767:                // Execute our extended stored procedure (let's hope it is installed!).
1768:                //
1769:                baseTds.executeSQL(null, "master..xp_jtdsxa", params, false, 0,
1770:                        -1, -1, true);
1771:                //
1772:                // Now process results
1773:                //
1774:                ArrayList xids = new ArrayList();
1775:                while (!baseTds.isEndOfResponse()) {
1776:                    if (baseTds.getMoreResults()) {
1777:                        // This had better be the results from a xa_recover command
1778:                        while (baseTds.getNextRow()) {
1779:                            Object row[] = baseTds.getRowData();
1780:                            if (row.length == 1 && row[0] instanceof  byte[]) {
1781:                                xids.add(row[0]);
1782:                            }
1783:                        }
1784:                    }
1785:                }
1786:                messages.checkErrors();
1787:                if (params[0].getOutValue() instanceof  Integer) {
1788:                    // Should be return code from XA command
1789:                    args[0] = ((Integer) params[0].getOutValue()).intValue();
1790:                } else {
1791:                    args[0] = -7; // XAException.XAER_RMFAIL
1792:                }
1793:                if (xids.size() > 0) {
1794:                    // List of XIDs from xa_recover
1795:                    byte list[][] = new byte[xids.size()][];
1796:                    for (int i = 0; i < xids.size(); i++) {
1797:                        list[i] = (byte[]) xids.get(i);
1798:                    }
1799:                    return list;
1800:                } else if (params[5].getOutValue() instanceof  byte[]) {
1801:                    // xa_open  the xa connection ID
1802:                    // xa_start OLE Transaction cookie
1803:                    byte cookie[][] = new byte[1][];
1804:                    cookie[0] = (byte[]) params[5].getOutValue();
1805:                    return cookie;
1806:                } else {
1807:                    // All other cases
1808:                    return null;
1809:                }
1810:            }
1811:
1812:            /**
1813:             * Enlists the current connection in a distributed transaction.
1814:             *
1815:             * @param oleTranID the OLE transaction cookie or null to delist
1816:             * @throws SQLException if an error condition occurs
1817:             */
1818:            synchronized void enlistConnection(byte[] oleTranID)
1819:                    throws SQLException {
1820:                if (oleTranID != null) {
1821:                    // TODO: Stored procs are no good but maybe prepare will be OK.
1822:                    this .prepareSql = TdsCore.EXECUTE_SQL;
1823:                    baseTds.enlistConnection(1, oleTranID);
1824:                    xaTransaction = true;
1825:                } else {
1826:                    baseTds.enlistConnection(1, null);
1827:                    xaTransaction = false;
1828:                }
1829:            }
1830:
1831:            /**
1832:             * Sets the XA transaction ID when running in emulation mode.
1833:             *
1834:             * @param xid the XA Transaction ID
1835:             */
1836:            void setXid(Object xid) {
1837:                this .xid = xid;
1838:                xaTransaction = xid != null;
1839:            }
1840:
1841:            /**
1842:             * Gets the XA transaction ID when running in emulation mode.
1843:             *
1844:             * @return the transaction ID as an <code>Object</code>
1845:             */
1846:            Object getXid() {
1847:                return xid;
1848:            }
1849:
1850:            /**
1851:             * Sets the XA state variable.
1852:             *
1853:             * @param value the XA state value
1854:             */
1855:            void setXaState(int value) {
1856:                this .xaState = value;
1857:            }
1858:
1859:            /**
1860:             * Retrieves the XA state variable.
1861:             *
1862:             * @return the xa state variable as an <code>int</code>
1863:             */
1864:            int getXaState() {
1865:                return this .xaState;
1866:            }
1867:
1868:            /**
1869:             * Retrieves the XA Emulation flag.
1870:             * @return True if in XA emulation mode.
1871:             */
1872:            boolean isXaEmulation() {
1873:                return xaEmulation;
1874:            }
1875:
1876:            /**
1877:             * Retrieves the connection mutex and acquires an exclusive lock on the
1878:             * network connection.
1879:             *
1880:             * @return the mutex object as a <code>Semaphore</code>
1881:             */
1882:            Semaphore getMutex() {
1883:                // Thread.interrupted() will clear the interrupt status
1884:                boolean interrupted = Thread.interrupted();
1885:
1886:                try {
1887:                    this .mutex.acquire();
1888:                } catch (InterruptedException e) {
1889:                    throw new IllegalStateException(
1890:                            "Thread execution interrupted");
1891:                }
1892:
1893:                if (interrupted) {
1894:                    // Bug [1596743] do not absorb interrupt status
1895:                    Thread.currentThread().interrupt();
1896:                }
1897:
1898:                return this .mutex;
1899:            }
1900:
1901:            /**
1902:             * Releases (either closes or caches) a <code>TdsCore</code>.
1903:             *
1904:             * @param tds the <code>TdsCore</code> instance to release
1905:             * @throws SQLException if an error occurs while closing or cleaning up
1906:             * @todo Should probably synchronize on another object
1907:             */
1908:            synchronized void releaseTds(TdsCore tds) throws SQLException {
1909:                if (cachedTds != null) {
1910:                    // There's already a cached TdsCore; close this one
1911:                    tds.close();
1912:                } else {
1913:                    // No cached TdsCore; clean up this one and cache it
1914:                    tds.clearResponseQueue();
1915:                    tds.cleanUp();
1916:                    cachedTds = tds;
1917:                }
1918:            }
1919:
1920:            /**
1921:             * Retrieves the cached <code>TdsCore</code> or <code>null</code> if
1922:             * nothing is cached and resets the cache (sets it to <code>null</code>).
1923:             *
1924:             * @return the value of {@link #cachedTds}
1925:             * @todo Should probably synchronize on another object
1926:             */
1927:            synchronized TdsCore getCachedTds() {
1928:                TdsCore result = cachedTds;
1929:                cachedTds = null;
1930:                return result;
1931:            }
1932:
1933:            //
1934:            // ------------------- java.sql.Connection interface methods -------------------
1935:            //
1936:
1937:            public int getHoldability() throws SQLException {
1938:                checkOpen();
1939:
1940:                return JtdsResultSet.HOLD_CURSORS_OVER_COMMIT;
1941:            }
1942:
1943:            public int getTransactionIsolation() throws SQLException {
1944:                checkOpen();
1945:
1946:                return this .transactionIsolation;
1947:            }
1948:
1949:            synchronized public void clearWarnings() throws SQLException {
1950:                checkOpen();
1951:                messages.clearWarnings();
1952:            }
1953:
1954:            /**
1955:             * Releases this <code>Connection</code> object's database and JDBC
1956:             * resources immediately instead of waiting for them to be automatically
1957:             * released.
1958:             * <p>
1959:             * Calling the method close on a <code>Connection</code> object that is
1960:             * already closed is a no-op.
1961:             * <p>
1962:             * <b>Note:</b> A <code>Connection</code> object is automatically closed
1963:             * when it is garbage collected. Certain fatal errors also close a
1964:             * <code>Connection</code> object.
1965:             * <p>
1966:             * Synchronized because it accesses the statement list and the
1967:             * <code>baseTds</code>.
1968:             *
1969:             * @throws SQLException if a database access error occurs
1970:             */
1971:            synchronized public void close() throws SQLException {
1972:                if (!closed) {
1973:                    try {
1974:                        //
1975:                        // Close any open statements
1976:                        //
1977:                        ArrayList tmpList;
1978:
1979:                        synchronized (statements) {
1980:                            tmpList = new ArrayList(statements);
1981:                            statements.clear();
1982:                        }
1983:
1984:                        for (int i = 0; i < tmpList.size(); i++) {
1985:                            WeakReference wr = (WeakReference) tmpList.get(i);
1986:
1987:                            if (wr != null) {
1988:                                Statement stmt = (Statement) wr.get();
1989:                                if (stmt != null) {
1990:                                    try {
1991:                                        stmt.close();
1992:                                    } catch (SQLException ex) {
1993:                                        // Ignore
1994:                                    }
1995:                                }
1996:                            }
1997:                        }
1998:
1999:                        try {
2000:                            // Tell the server the session is ending
2001:                            baseTds.closeConnection();
2002:                            // Close network connection
2003:                            baseTds.close();
2004:                            // Close cached TdsCore
2005:                            if (cachedTds != null) {
2006:                                cachedTds.close();
2007:                                cachedTds = null;
2008:                            }
2009:                        } catch (SQLException ex) {
2010:                            // Ignore
2011:                        }
2012:
2013:                        socket.close();
2014:                    } catch (IOException e) {
2015:                        // Ignore
2016:                    } finally {
2017:                        closed = true;
2018:                    }
2019:                }
2020:            }
2021:
2022:            synchronized public void commit() throws SQLException {
2023:                checkOpen();
2024:                checkLocal("commit");
2025:
2026:                if (getAutoCommit()) {
2027:                    throw new SQLException(Messages.get(
2028:                            "error.connection.autocommit", "commit"), "25000");
2029:                }
2030:
2031:                baseTds.submitSQL("IF @@TRANCOUNT > 0 COMMIT TRAN");
2032:                procInTran.clear();
2033:                clearSavepoints();
2034:            }
2035:
2036:            synchronized public void rollback() throws SQLException {
2037:                checkOpen();
2038:                checkLocal("rollback");
2039:
2040:                if (getAutoCommit()) {
2041:                    throw new SQLException(Messages.get(
2042:                            "error.connection.autocommit", "rollback"), "25000");
2043:                }
2044:
2045:                baseTds.submitSQL("IF @@TRANCOUNT > 0 ROLLBACK TRAN");
2046:
2047:                for (int i = 0; i < procInTran.size(); i++) {
2048:                    String key = (String) procInTran.get(i);
2049:                    if (key != null) {
2050:                        statementCache.remove(key);
2051:                    }
2052:                }
2053:                procInTran.clear();
2054:
2055:                clearSavepoints();
2056:            }
2057:
2058:            public boolean getAutoCommit() throws SQLException {
2059:                checkOpen();
2060:
2061:                return this .autoCommit;
2062:            }
2063:
2064:            public boolean isClosed() throws SQLException {
2065:                return closed;
2066:            }
2067:
2068:            public boolean isReadOnly() throws SQLException {
2069:                checkOpen();
2070:
2071:                return this .readOnly;
2072:            }
2073:
2074:            public void setHoldability(int holdability) throws SQLException {
2075:                checkOpen();
2076:                switch (holdability) {
2077:                case JtdsResultSet.HOLD_CURSORS_OVER_COMMIT:
2078:                    break;
2079:                case JtdsResultSet.CLOSE_CURSORS_AT_COMMIT:
2080:                    throw new SQLException(Messages.get(
2081:                            "error.generic.optvalue",
2082:                            "CLOSE_CURSORS_AT_COMMIT", "setHoldability"),
2083:                            "HY092");
2084:                default:
2085:                    throw new SQLException(Messages.get(
2086:                            "error.generic.badoption", Integer
2087:                                    .toString(holdability), "holdability"),
2088:                            "HY092");
2089:                }
2090:            }
2091:
2092:            synchronized public void setTransactionIsolation(int level)
2093:                    throws SQLException {
2094:                checkOpen();
2095:
2096:                if (transactionIsolation == level) {
2097:                    // No need to submit a request
2098:                    return;
2099:                }
2100:
2101:                String sql = "SET TRANSACTION ISOLATION LEVEL ";
2102:                boolean sybase = serverType == Driver.SYBASE;
2103:
2104:                switch (level) {
2105:                case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
2106:                    sql += (sybase) ? "0" : "READ UNCOMMITTED";
2107:                    break;
2108:                case java.sql.Connection.TRANSACTION_READ_COMMITTED:
2109:                    sql += (sybase) ? "1" : "READ COMMITTED";
2110:                    break;
2111:                case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
2112:                    sql += (sybase) ? "2" : "REPEATABLE READ";
2113:                    break;
2114:                case java.sql.Connection.TRANSACTION_SERIALIZABLE:
2115:                    sql += (sybase) ? "3" : "SERIALIZABLE";
2116:                    break;
2117:                case TRANSACTION_SNAPSHOT:
2118:                    if (sybase) {
2119:                        throw new SQLException(Messages.get(
2120:                                "error.generic.optvalue",
2121:                                "TRANSACTION_SNAPSHOT",
2122:                                "setTransactionIsolation"), "HY024");
2123:                    } else {
2124:                        sql += "SNAPSHOT";
2125:                    }
2126:                    break;
2127:                case java.sql.Connection.TRANSACTION_NONE:
2128:                    throw new SQLException(Messages.get(
2129:                            "error.generic.optvalue", "TRANSACTION_NONE",
2130:                            "setTransactionIsolation"), "HY024");
2131:                default:
2132:                    throw new SQLException(Messages.get(
2133:                            "error.generic.badoption", Integer.toString(level),
2134:                            "level"), "HY092");
2135:                }
2136:
2137:                transactionIsolation = level;
2138:                baseTds.submitSQL(sql);
2139:            }
2140:
2141:            synchronized public void setAutoCommit(boolean autoCommit)
2142:                    throws SQLException {
2143:                checkOpen();
2144:                checkLocal("setAutoCommit");
2145:
2146:                if (this .autoCommit == autoCommit) {
2147:                    // If we don't need to change the current auto commit mode, don't
2148:                    // submit a request and don't commit either. Section 10.1.1 of the
2149:                    // JDBC 3.0 spec states that the transaction should be committed
2150:                    // only "if the value of auto-commit is _changed_ in the middle of
2151:                    // a transaction". This takes precedence over the API docs, which
2152:                    // states that "if this method is called during a transaction, the
2153:                    // transaction is committed".
2154:                    return;
2155:                }
2156:
2157:                StringBuffer sql = new StringBuffer(70);
2158:                //
2159:                if (!this .autoCommit) {
2160:                    // If we're in manual commit mode the spec requires that we commit
2161:                    // the transaction when setAutoCommit() is called
2162:                    sql.append("IF @@TRANCOUNT > 0 COMMIT TRAN\r\n");
2163:                }
2164:
2165:                if (serverType == Driver.SYBASE) {
2166:                    if (autoCommit) {
2167:                        sql.append("SET CHAINED OFF");
2168:                    } else {
2169:                        sql.append("SET CHAINED ON");
2170:                    }
2171:                } else {
2172:                    if (autoCommit) {
2173:                        sql.append("SET IMPLICIT_TRANSACTIONS OFF");
2174:                    } else {
2175:                        sql.append("SET IMPLICIT_TRANSACTIONS ON");
2176:                    }
2177:                }
2178:
2179:                baseTds.submitSQL(sql.toString());
2180:                this .autoCommit = autoCommit;
2181:            }
2182:
2183:            public void setReadOnly(boolean readOnly) throws SQLException {
2184:                checkOpen();
2185:                this .readOnly = readOnly;
2186:            }
2187:
2188:            public String getCatalog() throws SQLException {
2189:                checkOpen();
2190:
2191:                return this .currentDatabase;
2192:            }
2193:
2194:            synchronized public void setCatalog(String catalog)
2195:                    throws SQLException {
2196:                checkOpen();
2197:
2198:                if (currentDatabase != null && currentDatabase.equals(catalog)) {
2199:                    return;
2200:                }
2201:
2202:                int maxlength = tdsVersion >= Driver.TDS70 ? 128 : 30;
2203:
2204:                if (catalog.length() > maxlength || catalog.length() < 1) {
2205:                    throw new SQLException(Messages.get(
2206:                            "error.generic.badparam", catalog, "catalog"),
2207:                            "3D000");
2208:                }
2209:
2210:                String sql = tdsVersion >= Driver.TDS70 ? ("use [" + catalog + ']')
2211:                        : "use " + catalog;
2212:                baseTds.submitSQL(sql);
2213:            }
2214:
2215:            public DatabaseMetaData getMetaData() throws SQLException {
2216:                checkOpen();
2217:
2218:                return new JtdsDatabaseMetaData(this );
2219:            }
2220:
2221:            public SQLWarning getWarnings() throws SQLException {
2222:                checkOpen();
2223:
2224:                return messages.getWarnings();
2225:            }
2226:
2227:            public Savepoint setSavepoint() throws SQLException {
2228:                checkOpen();
2229:                notImplemented("Connection.setSavepoint()");
2230:
2231:                return null;
2232:            }
2233:
2234:            public void releaseSavepoint(Savepoint savepoint)
2235:                    throws SQLException {
2236:                checkOpen();
2237:                notImplemented("Connection.releaseSavepoint(Savepoint)");
2238:            }
2239:
2240:            public void rollback(Savepoint savepoint) throws SQLException {
2241:                checkOpen();
2242:                notImplemented("Connection.rollback(Savepoint)");
2243:            }
2244:
2245:            public Statement createStatement() throws SQLException {
2246:                checkOpen();
2247:
2248:                return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
2249:                        java.sql.ResultSet.CONCUR_READ_ONLY);
2250:            }
2251:
2252:            synchronized public Statement createStatement(int type,
2253:                    int concurrency) throws SQLException {
2254:                checkOpen();
2255:
2256:                JtdsStatement stmt = new JtdsStatement(this , type, concurrency);
2257:                addStatement(stmt);
2258:
2259:                return stmt;
2260:            }
2261:
2262:            public Statement createStatement(int type, int concurrency,
2263:                    int holdability) throws SQLException {
2264:                checkOpen();
2265:                setHoldability(holdability);
2266:
2267:                return createStatement(type, concurrency);
2268:            }
2269:
2270:            public Map getTypeMap() throws SQLException {
2271:                checkOpen();
2272:
2273:                return new HashMap();
2274:            }
2275:
2276:            public void setTypeMap(Map map) throws SQLException {
2277:                checkOpen();
2278:                notImplemented("Connection.setTypeMap(Map)");
2279:            }
2280:
2281:            public String nativeSQL(String sql) throws SQLException {
2282:                checkOpen();
2283:
2284:                if (sql == null || sql.length() == 0) {
2285:                    throw new SQLException(Messages.get("error.generic.nosql"),
2286:                            "HY000");
2287:                }
2288:
2289:                String[] result = SQLParser.parse(sql, new ArrayList(), this ,
2290:                        false);
2291:
2292:                return result[0];
2293:            }
2294:
2295:            public CallableStatement prepareCall(String sql)
2296:                    throws SQLException {
2297:                checkOpen();
2298:
2299:                return prepareCall(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
2300:                        java.sql.ResultSet.CONCUR_READ_ONLY);
2301:            }
2302:
2303:            synchronized public CallableStatement prepareCall(String sql,
2304:                    int type, int concurrency) throws SQLException {
2305:                checkOpen();
2306:
2307:                if (sql == null || sql.length() == 0) {
2308:                    throw new SQLException(Messages.get("error.generic.nosql"),
2309:                            "HY000");
2310:                }
2311:
2312:                JtdsCallableStatement stmt = new JtdsCallableStatement(this ,
2313:                        sql, type, concurrency);
2314:                addStatement(stmt);
2315:
2316:                return stmt;
2317:            }
2318:
2319:            public CallableStatement prepareCall(String sql, int type,
2320:                    int concurrency, int holdability) throws SQLException {
2321:                checkOpen();
2322:                setHoldability(holdability);
2323:                return prepareCall(sql, type, concurrency);
2324:            }
2325:
2326:            public PreparedStatement prepareStatement(String sql)
2327:                    throws SQLException {
2328:                checkOpen();
2329:
2330:                return prepareStatement(sql,
2331:                        java.sql.ResultSet.TYPE_FORWARD_ONLY,
2332:                        java.sql.ResultSet.CONCUR_READ_ONLY);
2333:            }
2334:
2335:            public PreparedStatement prepareStatement(String sql,
2336:                    int autoGeneratedKeys) throws SQLException {
2337:                checkOpen();
2338:
2339:                if (sql == null || sql.length() == 0) {
2340:                    throw new SQLException(Messages.get("error.generic.nosql"),
2341:                            "HY000");
2342:                }
2343:
2344:                if (autoGeneratedKeys != JtdsStatement.RETURN_GENERATED_KEYS
2345:                        && autoGeneratedKeys != JtdsStatement.NO_GENERATED_KEYS) {
2346:                    throw new SQLException(Messages.get(
2347:                            "error.generic.badoption", Integer
2348:                                    .toString(autoGeneratedKeys),
2349:                            "autoGeneratedKeys"), "HY092");
2350:                }
2351:
2352:                JtdsPreparedStatement stmt = new JtdsPreparedStatement(
2353:                        this ,
2354:                        sql,
2355:                        java.sql.ResultSet.TYPE_FORWARD_ONLY,
2356:                        java.sql.ResultSet.CONCUR_READ_ONLY,
2357:                        autoGeneratedKeys == JtdsStatement.RETURN_GENERATED_KEYS);
2358:                addStatement(stmt);
2359:
2360:                return stmt;
2361:            }
2362:
2363:            synchronized public PreparedStatement prepareStatement(String sql,
2364:                    int type, int concurrency) throws SQLException {
2365:                checkOpen();
2366:
2367:                if (sql == null || sql.length() == 0) {
2368:                    throw new SQLException(Messages.get("error.generic.nosql"),
2369:                            "HY000");
2370:                }
2371:
2372:                JtdsPreparedStatement stmt = new JtdsPreparedStatement(this ,
2373:                        sql, type, concurrency, false);
2374:                addStatement(stmt);
2375:
2376:                return stmt;
2377:            }
2378:
2379:            public PreparedStatement prepareStatement(String sql, int type,
2380:                    int concurrency, int holdability) throws SQLException {
2381:                checkOpen();
2382:                setHoldability(holdability);
2383:
2384:                return prepareStatement(sql, type, concurrency);
2385:            }
2386:
2387:            public PreparedStatement prepareStatement(String sql,
2388:                    int[] columnIndexes) throws SQLException {
2389:                if (columnIndexes == null) {
2390:                    throw new SQLException(Messages.get(
2391:                            "error.generic.nullparam", "prepareStatement"),
2392:                            "HY092");
2393:                } else if (columnIndexes.length != 1) {
2394:                    throw new SQLException(Messages.get(
2395:                            "error.generic.needcolindex", "prepareStatement"),
2396:                            "HY092");
2397:                }
2398:
2399:                return prepareStatement(sql,
2400:                        JtdsStatement.RETURN_GENERATED_KEYS);
2401:            }
2402:
2403:            public Savepoint setSavepoint(String name) throws SQLException {
2404:                checkOpen();
2405:                notImplemented("Connection.setSavepoint(String)");
2406:
2407:                return null;
2408:            }
2409:
2410:            public PreparedStatement prepareStatement(String sql,
2411:                    String[] columnNames) throws SQLException {
2412:                if (columnNames == null) {
2413:                    throw new SQLException(Messages.get(
2414:                            "error.generic.nullparam", "prepareStatement"),
2415:                            "HY092");
2416:                } else if (columnNames.length != 1) {
2417:                    throw new SQLException(Messages.get(
2418:                            "error.generic.needcolname", "prepareStatement"),
2419:                            "HY092");
2420:                }
2421:
2422:                return prepareStatement(sql,
2423:                        JtdsStatement.RETURN_GENERATED_KEYS);
2424:            }
2425:
2426:            /**
2427:             * Releases all savepoints. Used internally when committing or rolling back
2428:             * a transaction.
2429:             */
2430:            void clearSavepoints() {
2431:            }
2432:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.