Categories:
Audio (13)
Biotech (29)
Bytecode (36)
Database (77)
Framework (7)
Game (7)
General (507)
Graphics (53)
I/O (35)
IDE (2)
JAR Tools (101)
JavaBeans (21)
JDBC (121)
JDK (426)
JSP (20)
Logging (108)
Mail (58)
Messaging (8)
Network (84)
PDF (97)
Report (7)
Scripting (84)
Security (32)
Server (121)
Servlet (26)
SOAP (24)
Testing (54)
Web (15)
XML (309)
Collections:
Other Resources:
jTDS JDBC Driver Source Code Files
jTDS JDBC Driver Source Code Files are provided in the source package file, jtds-1.3.1-fyi.zip.
You can browse jTDS JDBC Driver Source Code files below:
✍: FYIcenter.com
⏎ net/sourceforge/jtds/jdbc/JtdsConnection.java
// jTDS JDBC Driver for Microsoft SQL Server and Sybase // Copyright (C) 2004 The jTDS Project // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // package net.sourceforge.jtds.jdbc; import java.lang.ref.WeakReference; import java.sql.*; import java.net.UnknownHostException; import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.HashSet; import java.util.Random; import net.sourceforge.jtds.jdbc.cache.*; import net.sourceforge.jtds.util.*; /** * jTDS implementation of the java.sql.Connection interface. * <p> * Implementation notes: * <ol> * <li>Environment setting code carried over from old jTDS otherwise * generally a new implementation of Connection. * <li>Connection properties and SQLException text messages are loaded from * a properties file. * <li>Character set choices are also loaded from a resource file and the original * Encoder class has gone. * <li>Prepared SQL statements are converted to procedures in the prepareSQL method. * <li>Use of Stored procedures is optional and controlled via connection property. * <li>This Connection object maintains a table of weak references to associated * statements. This allows the connection object to control the statements (for * example to close them) but without preventing them being garbage collected in * a pooled environment. * </ol> * * @author Mike Hutchinson * @author Alin Sinpalean * @version $Id: JtdsConnection.java,v 1.119.2.14 2010-05-17 10:27:00 ickzon Exp $ */ public class JtdsConnection implements java.sql.Connection { /** * SQL query to determine the server charset on Sybase. */ private static final String SYBASE_SERVER_CHARSET_QUERY = "select name from master.dbo.syscharsets where id =" + " (select value from master.dbo.sysconfigures where config=131)"; /** * SQL query to determine the server charset on MS SQL Server 6.5. */ private static final String SQL_SERVER_65_CHARSET_QUERY = "select name from master.dbo.syscharsets where id =" + " (select csid from master.dbo.syscharsets, master.dbo.sysconfigures" + " where config=1123 and id = value)"; /** Sybase initial connection string. */ private static final String SYBASE_INITIAL_SQL = "SET TRANSACTION ISOLATION LEVEL 1\r\n" + "SET CHAINED OFF\r\n" + "SET QUOTED_IDENTIFIER ON\r\n"+ "SET TEXTSIZE 2147483647"; /** * SQL Server initial connection string. Also contains a * <code>SELECT @@MAX_PRECISION</code> query to retrieve * the maximum precision for DECIMAL/NUMERIC data. */ private static final String SQL_SERVER_INITIAL_SQL = "SELECT @@MAX_PRECISION\r\n" + "SET TRANSACTION ISOLATION LEVEL READ COMMITTED\r\n" + "SET IMPLICIT_TRANSACTIONS OFF\r\n" + "SET QUOTED_IDENTIFIER ON\r\n"+ "SET TEXTSIZE 2147483647"; /** * SQL Server custom transaction isolation level. */ public static final int TRANSACTION_SNAPSHOT = 4096; /* * Conection attributes */ /** The orginal connection URL. */ private final String url; /** The server host name. */ private String serverName; /** The server port number. */ private int portNumber; /** The make of SQL Server (sybase/microsoft). */ private int serverType; /** The SQL Server instance. */ private String instanceName; /** The requested database name. */ private String databaseName; /** The current database name. */ private String currentDatabase; /** The Windows Domain name. */ private String domainName; /** The database user ID. */ private String user; /** The user password. */ private String password; /** The server character set. */ private String serverCharset; /** The application name. */ private String appName; /** The program name. */ private String progName; /** Workstation ID. */ private String wsid; /** The server message language. */ private String language; /** The client MAC Address. */ private String macAddress; /** The server protocol version. */ private int tdsVersion; /** The network TCP/IP socket. */ private final SharedSocket socket; /** The cored TDS protocol object. */ private final TdsCore baseTds; /** The initial network packet size. */ private int netPacketSize = TdsCore.MIN_PKT_SIZE; /** User requested packet size. */ private int packetSize; /** SQL Server 2000 collation. */ private byte collation[]; /** True if user specifies an explicit charset. */ private boolean charsetSpecified; /** The database product name eg SQL SERVER. */ private String databaseProductName; /** The product version eg 11.92. */ private String databaseProductVersion; /** The major version number eg 11. */ private int databaseMajorVersion; /** The minor version number eg 92. */ private int databaseMinorVersion; /** True if this connection is closed. */ private boolean closed; /** True if this connection is read only. */ private boolean readOnly; /** List of statements associated with this connection. */ private final ArrayList statements = new ArrayList(); /** Default transaction isolation level. */ private int transactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED; /** Default auto commit state. */ private boolean autoCommit = true; /** Diagnostc messages for this connection. */ private final SQLDiagnostic messages; /** Connection's current rowcount limit. */ private int rowCount; /** Connection's current maximum field size limit. */ private int textSize; /** Maximum decimal precision. */ private int maxPrecision = TdsData.DEFAULT_PRECISION_38; // Sybase default /** Stored procedure unique ID number. */ private int spSequenceNo = 1; /** Cursor unique ID number. */ private int cursorSequenceNo = 1; /** Procedures in this transaction. */ private final ArrayList procInTran = new ArrayList(); /** Java charset for encoding. */ private CharsetInfo charsetInfo; /** Method for preparing SQL used in Prepared Statements. */ private int prepareSql; /** The amount of LOB data to buffer in memory. */ private long lobBuffer; /** The maximum number of statements to keep open. */ private int maxStatements; /** Statement cache.*/ private StatementCache statementCache; /** Send parameters as unicode. */ private boolean useUnicode = true; /** Use named pipe IPC instead of TCP/IP sockets. */ private boolean namedPipe; /** Only return the last update count. */ private boolean lastUpdateCount; /** TCP_NODELAY */ private boolean tcpNoDelay = true; /** Login timeout value in seconds or 0. */ private int loginTimeout; /** Sybase capability mask.*/ private int sybaseInfo; /** True if running distributed transaction. */ private boolean xaTransaction; /** Current emulated XA State eg start/end/prepare etc. */ private int xaState; /** Current XA Transaction ID. */ private Object xid; /** True if driver should emulate distributed transactions. */ private boolean xaEmulation = true; /** Mutual exclusion lock to control access to connection. */ private final Semaphore mutex = new Semaphore(1); /** Socket timeout value in seconds or 0. */ private int socketTimeout; /** True to enable socket keep alive. */ private boolean socketKeepAlive; /** The process ID to report to a server when connecting. */ private static Integer processId; /** SSL setting. */ private String ssl; /** The maximum size of a batch. */ private int batchSize; /** Use metadata cache for prepared statements. */ private boolean useMetadataCache; /** Use fast forward cursors for forward only result sets. */ private boolean useCursors; /** The directory to buffer data to */ private File bufferDir; /** The global buffer memory limit for all connections (in kilobytes). */ private int bufferMaxMemory; /** The minimum number of packets per statement to buffer to memory. */ private int bufferMinPackets; /** Map large types (IMAGE and TEXT/NTEXT) to LOBs by default. */ private boolean useLOBs; /** A cached <code>TdsCore</code> instance to reuse on new statements. */ private TdsCore cachedTds; /** The local address to bind to when connecting to a database via TCP/IP. */ private String bindAddress; /** Force use of jCIFS library on Windows when connecting via named pipes. */ private boolean useJCIFS; /** When doing NTLM authentication, send NTLMv2 response rather than regular response */ private boolean useNTLMv2 = false; /** Force Kerberos authentication */ private boolean useKerberos = false; /** the number of currently open connections */ private static int[] connections = new int[1]; /** The list of savepoints. */ private ArrayList savepoints; /** Maps each savepoint to a list of temp procedures created since the savepoint */ private Map savepointProcInTran; /** Counter for generating unique savepoint identifiers */ private int savepointId; /** * Default constructor. * <p/> * Used for testing. */ private JtdsConnection() { synchronized( connections ) { connections[0] ++; } url = null; socket = null; baseTds = null; messages = null; } /** * Create a new database connection. * * @param url The connection URL starting jdbc:jtds:. * @param info The additional connection properties. * @throws SQLException */ JtdsConnection(String url, Properties info) throws SQLException { synchronized( connections ) { connections[0] ++; } this.url = url; // // Extract properties into instance variables // unpackProperties(info); messages = new SQLDiagnostic(serverType); // // Get the instance port, if it is specified. // Named pipes use instance names differently. // if (instanceName.length() > 0 && !namedPipe) { try { final MSSqlServerInfo msInfo = new MSSqlServerInfo(serverName); portNumber = msInfo.getPortForInstance(instanceName); } catch (SQLException e) { // may already have a valid portNumber but Microsoft SQL Server Browser disabled if (portNumber <= 0) { throw e; } } if (portNumber == -1) { throw new SQLException( Messages.get("error.msinfo.badinst", serverName, instanceName), "08003"); } } SharedSocket.setMemoryBudget(bufferMaxMemory * 1024); SharedSocket.setMinMemPkts(bufferMinPackets); SQLWarning warn; Object timer = null; boolean loginError = false; try { if (loginTimeout > 0) { // Start a login timer timer = TimerThread.getInstance().setTimer(loginTimeout * 1000, new TimerThread.TimerListener() { public void timerExpired() { if (socket != null) { socket.forceClose(); } } }); } if (namedPipe) { // Use named pipe socket = createNamedPipe(this); } else { // Use plain TCP/IP socket socket = new SharedSocket(this); } if( macAddress.equals( DefaultProperties.MAC_ADDRESS ) ) { String mac = socket.getMAC(); macAddress = mac != null ? mac : macAddress; } if (timer != null && TimerThread.getInstance().hasExpired(timer)) { // If the timer has expired during the connection phase, close // the socket and throw an exception socket.forceClose(); throw new IOException("Login timed out"); } if ( charsetSpecified ) { loadCharset(serverCharset); } else { // Need a default charset to process login packets for TDS 4.2/5.0 // Will discover the actual serverCharset later loadCharset("iso_1"); serverCharset = ""; // But don't send charset name to server! } // // Create TDS protocol object // baseTds = new TdsCore(this, messages); // // Negotiate SSL connection if required // if (tdsVersion >= Driver.TDS80 && !namedPipe) { baseTds.negotiateSSL(instanceName, ssl); } // // Now try to login // baseTds.login(serverName, databaseName, user, password, domainName, serverCharset, appName, progName, wsid, language, macAddress, packetSize); // // Save any login warnings so that they will not be overwritten by // the internal configuration SQL statements e.g. setCatalog() etc. // warn = messages.warnings; // Update the tdsVersion with the value in baseTds. baseTds sets // the TDS version for the socket and there are no other objects // with cached TDS versions at this point. tdsVersion = baseTds.getTdsVersion(); if (tdsVersion < Driver.TDS70 && databaseName.length() > 0) { // Need to select the default database setCatalog(databaseName); } // If charset is still unknown and the collation is not set either, // determine the charset by querying (we're using Sybase or SQL Server // 6.5) if ((serverCharset == null || serverCharset.length() == 0) && collation == null) { loadCharset(determineServerCharset()); } // Initial database settings. // Sets: auto commit mode = true // transaction isolation = read committed. if (serverType == Driver.SYBASE) { baseTds.submitSQL(SYBASE_INITIAL_SQL); } else { // Also discover the maximum decimal precision: 28 (default) // or 38 for MS SQL Server 6.5/7, or 38 for 2000 and later. Statement stmt = this.createStatement(); ResultSet rs = stmt.executeQuery(SQL_SERVER_INITIAL_SQL); if (rs.next()) { maxPrecision = rs.getByte(1); } rs.close(); stmt.close(); } } catch (UnknownHostException e) { loginError = true; throw Support.linkException( new SQLException(Messages.get("error.connection.badhost", e.getMessage()), "08S03"), e); } catch (IOException e) { loginError = true; if (loginTimeout > 0 && e.getMessage().indexOf("timed out") >= 0) { throw Support.linkException( new SQLException(Messages.get("error.connection.timeout"), "HYT01"), e); } throw Support.linkException( new SQLException(Messages.get("error.connection.ioerror", e.getMessage()), "08S01"), e); } catch (SQLException e) { loginError = true; if (loginTimeout > 0 && e.getMessage().indexOf("socket closed") >= 0) { throw Support.linkException( new SQLException(Messages.get("error.connection.timeout"), "HYT01"), e); } throw e; } catch (RuntimeException e) { loginError = true; throw e; } finally { // fix for bug [1755448], socket not closed after login error if (loginError) { close(); } else if (timer != null) { // Cancel loginTimer TimerThread.getInstance().cancelTimer(timer); } } // // Restore any login warnings so that the user can retrieve them // by calling Connection.getWarnings() // messages.warnings = warn; } /** * Ensure all resources are released. */ protected void finalize() throws Throwable { try { close(); } finally { super.finalize(); } } /** * Creates a {@link SharedSocket} object representing a connection to a named * pipe. If the <code>os.name</code> system property starts with "Windows" * (case-insensitive) and the <code>useJCIFS</code> parameter is * <code>false</code>, a {@link SharedLocalNamedPipe} object is created. * Else a {@link SharedNamedPipe} is created which uses * <a href="http://jcifs.samba.org/">jCIFS</a> to provide a pure-Java * implementation of Windows named pipes. * <p> * This method will retry for <code>loginTimeout</code> seconds to create a * named pipe if an <code>IOException</code> continues to be thrown stating, * "All pipe instances are busy". If <code>loginTimeout</code> is set to * zero (e.g., not set), a default of 20 seconds will be used. * * @param connection the connection object * @return an object representing the named pipe connection * @throws IOException on error; if an <code>IOException</code> is thrown with * a message stating "All pipe instances are busy", then the method timed out * after <code>loginTimeout</code> milliseconds attempting to create a named pipe. */ private SharedSocket createNamedPipe(JtdsConnection connection) throws IOException { final long loginTimeout = connection.getLoginTimeout(); final long retryTimeout = (loginTimeout > 0 ? loginTimeout : 20) * 1000; final long startLoginTimeout = System.currentTimeMillis(); final Random random = new Random(startLoginTimeout); final boolean isWindowsOS = Support.isWindowsOS(); SharedSocket socket = null; IOException lastIOException = null; int exceptionCount = 0; do { try { if (isWindowsOS && !connection.getUseJCIFS()) { socket = new SharedLocalNamedPipe(connection); } else { socket = new SharedNamedPipe(connection); } } catch (IOException ioe) { exceptionCount++; lastIOException = ioe; if (ioe.getMessage().toLowerCase().indexOf("all pipe instances are busy") >= 0) { // Per a Microsoft knowledgebase article, wait 200 ms to 1 second each time // we get an "All pipe instances are busy" error. // http://support.microsoft.com/default.aspx?scid=KB;EN-US;165189 final int randomWait = random.nextInt(800) + 200; if (Logger.isActive()) { Logger.println("Retry #" + exceptionCount + " Wait " + randomWait + " ms: " + ioe.getMessage()); } try { Thread.sleep(randomWait); } catch (InterruptedException ie) { // Do nothing; retry again } } else { throw ioe; } } } while (socket == null && (System.currentTimeMillis() - startLoginTimeout) < retryTimeout); if (socket == null) { final IOException ioException = new IOException("Connection timed out to named pipe"); Support.linkException(ioException, lastIOException); throw ioException; } return socket; } /** * Retrive the shared socket. * * @return The <code>SharedSocket</code> object. */ SharedSocket getSocket() { return socket; } /** * Retrieve the TDS protocol version. * * @return The TDS version as an <code>int</code>. */ int getTdsVersion() { return tdsVersion; } /** * Retrieves the next unique stored procedure name. * <p>Notes: * <ol> * <li>Some versions of Sybase require an id with * a length of <= 10. * <li>The format of this name works for sybase and Microsoft * and allows for 16M names per session. * <li>The leading '#jtds' indicates this is a temporary procedure and * the '#' is removed by the lower level TDS5 routines. * </ol> * Not synchronized because it's only called from the synchronized * {@link #prepareSQL} method. * * @return the next temporary SP name as a <code>String</code> */ String getProcName() { String seq = "000000" + Integer.toHexString(spSequenceNo++).toUpperCase(); return "#jtds" + seq.substring(seq.length() - 6, seq.length()); } /** * Retrieves the next unique cursor name. * * @return the next cursor name as a <code>String</code> */ synchronized String getCursorName() { String seq = "000000" + Integer.toHexString(cursorSequenceNo++).toUpperCase(); return "_jtds" + seq.substring(seq.length() - 6, seq.length()); } /** * Try to convert the SQL statement into a statement prepare. * <p> * Synchronized because it accesses the procedure cache and the * <code>baseTds</code>, but the method call also needs to made in a * <code>synchronized (connection)</code> block together with the execution * (if the prepared statement is actually executed) to ensure the * transaction isn't rolled back between this method call and the actual * execution. * * @param pstmt the target prepared statement * @param sql the SQL statement to prepare * @param params the parameters * @param returnKeys indicates whether the statement will return * generated keys * @param cursorNeeded indicates whether a cursor prepare is needed * @return the SQL procedure name as a <code>String</code> or null if the * SQL cannot be prepared */ synchronized String prepareSQL(JtdsPreparedStatement pstmt, String sql, ParamInfo[] params, boolean returnKeys, boolean cursorNeeded) throws SQLException { if (prepareSql == TdsCore.UNPREPARED || prepareSql == TdsCore.EXECUTE_SQL) { return null; // User selected not to use procs } if (serverType == Driver.SYBASE) { if (tdsVersion != Driver.TDS50) { return null; // No longer support stored procs with 4.2 } if (returnKeys) { return null; // Sybase cannot use @@IDENTITY in proc } if (cursorNeeded) { // // We are going to use the CachedResultSet so there is // no point in preparing the SQL as it will be discarded // in favour of a version with "FOR BROWSE" appended. // return null; } } // // Check parameters set and obtain native types // for (int i = 0; i < params.length; i++) { if (!params[i].isSet) { throw new SQLException(Messages.get("error.prepare.paramnotset", Integer.toString(i+1)), "07000"); } TdsData.getNativeType(this, params[i]); if (serverType == Driver.SYBASE) { if ("text".equals(params[i].sqlType) || "image".equals(params[i].sqlType)) { return null; // Sybase does not support text/image params } } } String key = Support.getStatementKey(sql, params, serverType, getCatalog(), autoCommit, cursorNeeded); // // See if we have already built this one // ProcEntry proc = (ProcEntry) statementCache.get(key); if (proc != null) { // // Yes found in cache OK // // If already used by the statement, decrement use count if (pstmt.handles != null && pstmt.handles.contains(proc)) { proc.release(); } pstmt.setColMetaData(proc.getColMetaData()); if (serverType == Driver.SYBASE) { pstmt.setParamMetaData(proc.getParamMetaData()); } } else { // // No, so create the stored procedure now // proc = new ProcEntry(); if (serverType == Driver.SQLSERVER) { proc.setName( baseTds.microsoftPrepare( sql, params, cursorNeeded, pstmt.getResultSetType(), pstmt.getResultSetConcurrency())); if (proc.toString() == null) { proc.setType(ProcEntry.PREP_FAILED); } else if (prepareSql == TdsCore.TEMPORARY_STORED_PROCEDURES) { proc.setType(ProcEntry.PROCEDURE); } else { proc.setType((cursorNeeded) ? ProcEntry.CURSOR : ProcEntry.PREPARE); // Meta data may be returned by sp_prepare proc.setColMetaData(baseTds.getColumns()); pstmt.setColMetaData(proc.getColMetaData()); } // TODO Find some way of getting parameter meta data for MS } else { proc.setName(baseTds.sybasePrepare(sql, params)); if (proc.toString() == null) { proc.setType(ProcEntry.PREP_FAILED); } else { proc.setType(ProcEntry.PROCEDURE); } // Sybase gives us lots of useful information about the result set proc.setColMetaData(baseTds.getColumns()); proc.setParamMetaData(baseTds.getParameters()); pstmt.setColMetaData(proc.getColMetaData()); pstmt.setParamMetaData(proc.getParamMetaData()); } // OK we have built a proc so add it to the cache. addCachedProcedure(key, proc); } // Add the handle to the prepared statement so that the handles // can be used to clean up the statement cache properly when the // prepared statement is closed. if (pstmt.handles == null) { pstmt.handles = new HashSet(10); } pstmt.handles.add(proc); // Give the user the name will be null if prepare failed return proc.toString(); } /** * Add a stored procedure to the cache. * <p> * Not explicitly synchronized because it's only called by synchronized * methods. * * @param key The signature of the procedure to cache. * @param proc The stored procedure descriptor. */ void addCachedProcedure(String key, ProcEntry proc) { statementCache.put(key, proc); if (!autoCommit && proc.getType() == ProcEntry.PROCEDURE && serverType == Driver.SQLSERVER) { procInTran.add(key); } if (getServerType() == Driver.SQLSERVER && proc.getType() == ProcEntry.PROCEDURE) { // Only need to track SQL Server temp stored procs addCachedProcedure(key); } } /** * Remove a stored procedure from the cache. * <p> * Not explicitly synchronized because it's only called by synchronized * methods. * * @param key The signature of the procedure to remove from the cache. */ void removeCachedProcedure(String key) { statementCache.remove(key); if (!autoCommit) { procInTran.remove(key); } } /** * Retrieves the maximum statement cache size. * * @return the maximum statement cache size */ int getMaxStatements() { return maxStatements; } /** * Retrieves the server type. * * @return the server type as an <code>int</code> where 1 == SQLSERVER and * 2 == SYBASE. */ public int getServerType() { return serverType; } /** * Sets the network packet size. * * @param size the new packet size */ void setNetPacketSize(int size) { netPacketSize = size; } /** * Retrieves the network packet size. * * @return the packet size as an <code>int</code> */ int getNetPacketSize() { return netPacketSize; } /** * Retrieves the current row count on this connection. * * @return the row count as an <code>int</code> */ int getRowCount() { return rowCount; } /** * Sets the current row count on this connection. * * @param count the new row count */ void setRowCount(int count) { rowCount = count; } /** * Retrieves the current maximum textsize on this connection. * * @return the maximum textsize as an <code>int</code> */ public int getTextSize() { return textSize; } /** * Sets the current maximum textsize on this connection. * * @param textSize the new maximum textsize */ public void setTextSize(int textSize) { this.textSize = textSize; } /** * Retrieves the status of the lastUpdateCount flag. * * @return the lastUpdateCount flag as a <code>boolean</code> */ boolean getLastUpdateCount() { return lastUpdateCount; } /** * Retrieves the maximum decimal precision. * * @return the precision as an <code>int</code> */ int getMaxPrecision() { return maxPrecision; } /** * Retrieves the LOB buffer size. * * @return the LOB buffer size as a <code>long</code> */ long getLobBuffer() { return lobBuffer; } /** * Retrieves the Prepared SQL method. * * @return the Prepared SQL method */ int getPrepareSql() { return prepareSql; } /** * Retrieves the batch size to be used internally. * * @return the batch size as an <code>int</code> */ int getBatchSize() { return batchSize; } /** * Retrieves the boolean indicating whether metadata caching * is enabled. * * @return <code>true</code> if metadata caching is enabled, * <code>false</code> if caching is disabled */ boolean getUseMetadataCache() { return useMetadataCache; } /** * Indicates whether fast forward only cursors should be used for forward * only result sets. * * @return <code>true</code> if fast forward cursors are requested */ boolean getUseCursors() { return useCursors; } /** * Indicates whether large types (IMAGE and TEXT/NTEXT) should be mapped by * default to LOB types or <code>String</code> and <code>byte[]</code> * respectively. * * @return <code>true</code> if the default mapping should be to LOBs, * <code>false</code> otherwise */ boolean getUseLOBs() { return useLOBs; } /** * Indicates whether, when doing Windows authentication to an MS SQL server, * NTLMv2 should be used. When this is set to "false", LM and NTLM responses * are sent to the server, which should work fine in most cases. However, * some servers are configured to require LMv2 and NTLMv2. In these rare * cases, this property should be set to "true". */ boolean getUseNTLMv2() { return useNTLMv2; } /** * Return whether to use Kerberos authentication for MS SQL Server. */ boolean getUseKerberos() { return useKerberos; } /** * Retrieves the application name for this connection. * * @return the application name */ String getAppName() { return appName; } /** * Retrieves the bind address for this connection. * * @return the bind address */ String getBindAddress() { return bindAddress; } /** * Returns the directory where data should be buffered to. * * @return the directory where data should be buffered to. */ File getBufferDir() { return bufferDir; } /** * Retrieves the maximum amount of memory in Kb to buffer for <em>all</em> connections. * * @return the maximum amount of memory in Kb to buffer for <em>all</em> connections */ int getBufferMaxMemory() { return bufferMaxMemory; } /** * Retrieves the minimum number of packets to buffer per {@link Statement} for this connection. * * @return the minimum number of packets to buffer per {@link Statement} */ int getBufferMinPackets() { return bufferMinPackets; } /** * Retrieves the database name for this connection. * * @return the database name */ String getDatabaseName() { return databaseName; } /** * Retrieves the domain name for this connection. * * @return the domain name */ String getDomainName() { return domainName; } /** * Retrieves the instance name for this connection. * * @return the instance name */ String getInstanceName() { return instanceName; } /** * Retrieves the login timeout for this connection. * * @return the login timeout */ int getLoginTimeout() { return loginTimeout; } /** * Retrieves the socket timeout for this connection. * * @return the socket timeout */ int getSocketTimeout() { return socketTimeout; } /** * Retrieves whether to enable socket keep alive. * * @return <code>true</code> if the socket keep alive is enabled */ boolean getSocketKeepAlive() { return socketKeepAlive; } /** * Retrieves the process ID to send to a server when a connection is * established. * * @return the process ID */ int getProcessId() { return processId.intValue(); } /** * Retrieves the MAC (ethernet) address for this connection. * * @return the MAC (ethernet) address */ String getMacAddress() { return macAddress; } /** * Retrieves the named pipe setting for this connection. * * @return the named pipe setting */ boolean getNamedPipe() { return namedPipe; } /** * Retrieves the packet size for this connection. * * @return the packet size */ int getPacketSize() { return packetSize; } /** * Retrieves the password for this connection. * * @return the password */ String getPassword() { return password; } /** * Retrieves the port number for this connection. * * @return the port number */ int getPortNumber() { return portNumber; } /** * Retrieves the program name for this connection. * * @return the program name */ String getProgName() { return progName; } /** * Retrieves the server name for this connection. * * @return the server name */ String getServerName() { return serverName; } /** * Retrieves the tcpNoDelay setting for this connection. * * @return the tcpNoDelay setting */ boolean getTcpNoDelay() { return tcpNoDelay; } /** * Retrieves the useJCIFS setting for this connection. * * @return the useJCIFS setting */ boolean getUseJCIFS() { return useJCIFS; } /** * Retrieves the user for this connection. * * @return the user */ String getUser() { return user; } /** * Retrieves the workstation ID (WSID) for this connection. * * @return the workstation ID (WSID) */ String getWsid() { return wsid; } /** * Transfers the properties to the local instance variables. * * @param info The connection properties Object. * @throws SQLException If an invalid property value is found. */ protected void unpackProperties(Properties info) throws SQLException { serverName = info.getProperty(Messages.get(Driver.SERVERNAME)); portNumber = parseIntegerProperty(info, Driver.PORTNUMBER); serverType = parseIntegerProperty(info, Driver.SERVERTYPE); databaseName = info.getProperty(Messages.get(Driver.DATABASENAME)); instanceName = info.getProperty(Messages.get(Driver.INSTANCE)); domainName = info.getProperty(Messages.get(Driver.DOMAIN)); user = info.getProperty(Messages.get(Driver.USER)); password = info.getProperty(Messages.get(Driver.PASSWORD)); macAddress = info.getProperty(Messages.get(Driver.MACADDRESS)); appName = info.getProperty(Messages.get(Driver.APPNAME)); progName = info.getProperty(Messages.get(Driver.PROGNAME)); wsid = info.getProperty(Messages.get(Driver.WSID)); serverCharset = info.getProperty(Messages.get(Driver.CHARSET)); language = info.getProperty(Messages.get(Driver.LANGUAGE)); bindAddress = info.getProperty(Messages.get(Driver.BINDADDRESS)); lastUpdateCount = parseBooleanProperty(info,Driver.LASTUPDATECOUNT); useUnicode = parseBooleanProperty(info,Driver.SENDSTRINGPARAMETERSASUNICODE); namedPipe = parseBooleanProperty(info,Driver.NAMEDPIPE); tcpNoDelay = parseBooleanProperty(info,Driver.TCPNODELAY); useCursors = (serverType == Driver.SQLSERVER) && parseBooleanProperty(info,Driver.USECURSORS); useLOBs = parseBooleanProperty(info,Driver.USELOBS); useMetadataCache = parseBooleanProperty(info,Driver.CACHEMETA); xaEmulation = parseBooleanProperty(info,Driver.XAEMULATION); useJCIFS = parseBooleanProperty(info,Driver.USEJCIFS); charsetSpecified = serverCharset.length() > 0; useNTLMv2 = parseBooleanProperty(info,Driver.USENTLMV2); useKerberos = parseBooleanProperty(info,Driver.USEKERBEROS); //note:mdb in certain cases (e.g. NTLMv2) the domain name must be // all upper case for things to work. if( domainName != null ) domainName = domainName.toUpperCase(); Integer parsedTdsVersion = DefaultProperties.getTdsVersion(info.getProperty(Messages.get(Driver.TDS))); if (parsedTdsVersion == null) { throw new SQLException(Messages.get("error.connection.badprop", Messages.get(Driver.TDS)), "08001"); } tdsVersion = parsedTdsVersion.intValue(); packetSize = parseIntegerProperty(info, Driver.PACKETSIZE); if (packetSize < TdsCore.MIN_PKT_SIZE) { if (tdsVersion >= Driver.TDS70) { // Default of 0 means let the server specify packet size packetSize = (packetSize == 0) ? 0 : TdsCore.DEFAULT_MIN_PKT_SIZE_TDS70; } else if (tdsVersion == Driver.TDS42) { // Sensible minimum for older versions of TDS packetSize = TdsCore.MIN_PKT_SIZE; } // else for TDS 5 can auto negotiate } if (packetSize > TdsCore.MAX_PKT_SIZE) { packetSize = TdsCore.MAX_PKT_SIZE; } packetSize = (packetSize / 512) * 512; loginTimeout = parseIntegerProperty(info, Driver.LOGINTIMEOUT); socketTimeout = parseIntegerProperty(info, Driver.SOTIMEOUT); socketKeepAlive = parseBooleanProperty(info,Driver.SOKEEPALIVE); autoCommit = parseBooleanProperty(info,Driver.AUTOCOMMIT); String pid = info.getProperty(Messages.get(Driver.PROCESSID)); if ("compute".equals(pid)) { // only determine a single PID for the VM's (or classloader's) life time if (processId == null) { // random number until the real process ID can be determined processId = new Integer(new Random(System.currentTimeMillis()).nextInt(32768)); } } else if (pid.length() > 0) { processId = new Integer(parseIntegerProperty(info, Driver.PROCESSID)); } lobBuffer = parseLongProperty(info, Driver.LOBBUFFER); maxStatements = parseIntegerProperty(info, Driver.MAXSTATEMENTS); statementCache = new ProcedureCache(maxStatements); prepareSql = parseIntegerProperty(info, Driver.PREPARESQL); if (prepareSql < 0) { prepareSql = 0; } else if (prepareSql > 3) { prepareSql = 3; } // For Sybase use equivalent of sp_executesql. if (tdsVersion < Driver.TDS70 && prepareSql == TdsCore.PREPARE) { prepareSql = TdsCore.EXECUTE_SQL; } // For SQL 6.5 sp_executesql not available so use stored procedures. if (tdsVersion < Driver.TDS50 && prepareSql == TdsCore.EXECUTE_SQL) { prepareSql = TdsCore.TEMPORARY_STORED_PROCEDURES; } ssl = info.getProperty(Messages.get(Driver.SSL)); batchSize = parseIntegerProperty(info, Driver.BATCHSIZE); if (batchSize < 0) { throw new SQLException(Messages.get("error.connection.badprop", Messages.get(Driver.BATCHSIZE)), "08001"); } bufferDir = new File(info.getProperty(Messages.get(Driver.BUFFERDIR))); if (!bufferDir.isDirectory()) { if (!bufferDir.mkdirs()) { throw new SQLException(Messages.get("error.connection.badprop", Messages.get(Driver.BUFFERDIR)), "08001"); } } bufferMaxMemory = parseIntegerProperty(info, Driver.BUFFERMAXMEMORY); if (bufferMaxMemory < 0) { throw new SQLException(Messages.get("error.connection.badprop", Messages.get(Driver.BUFFERMAXMEMORY)), "08001"); } bufferMinPackets = parseIntegerProperty(info, Driver.BUFFERMINPACKETS); if (bufferMinPackets < 1) { throw new SQLException(Messages.get("error.connection.badprop", Messages.get(Driver.BUFFERMINPACKETS)), "08001"); } } /** * Parse a string property value into an boolean value. * * @param info The connection properties object. * @param key The message key used to retrieve the property name. * @return The boolean value of the string property value. * @throws SQLException If the property value can't be parsed. */ private static boolean parseBooleanProperty(final Properties info, final String key) throws SQLException { final String propertyName = Messages.get(key); String prop = info.getProperty(propertyName); if (! (prop == null || "true".equalsIgnoreCase(prop) || "false".equalsIgnoreCase(prop))) throw new SQLException( Messages.get("error.connection.badprop", propertyName), "08001"); return "true".equalsIgnoreCase(prop); } /** * Parse a string property value into an integer value. * * @param info The connection properties object. * @param key The message key used to retrieve the property name. * @return The integer value of the string property value. * @throws SQLException If the property value can't be parsed. */ private static int parseIntegerProperty(final Properties info, final String key) throws SQLException { final String propertyName = Messages.get(key); try { return Integer.parseInt(info.getProperty(propertyName)); } catch (NumberFormatException e) { throw new SQLException( Messages.get("error.connection.badprop", propertyName), "08001"); } } /** * Parse a string property value into a long value. * * @param info The connection properties object. * @param key The message key used to retrieve the property name. * @return The long value of the string property value. * @throws SQLException If the property value can't be parsed. */ private static long parseLongProperty(final Properties info, final String key) throws SQLException { final String propertyName = Messages.get(key); try { return Long.parseLong(info.getProperty(propertyName)); } catch (NumberFormatException e) { throw new SQLException( Messages.get("error.connection.badprop", propertyName), "08001"); } } /** * Retrieve the Java charset to use for encoding. * * @return the Charset name as a <code>String</code> */ protected String getCharset() { return charsetInfo.getCharset(); } /** * Retrieve the multibyte status of the current character set. * * @return <code>boolean</code> true if a multi byte character set */ protected boolean isWideChar() { return charsetInfo.isWideChars(); } /** * Retrieve the <code>CharsetInfo</code> instance used by this connection. * * @return the default <code>CharsetInfo</code> for this connection */ protected CharsetInfo getCharsetInfo() { return charsetInfo; } /** * Retrieve the sendParametersAsUnicode flag. * * @return <code>boolean</code> true if parameters should be sent as unicode. */ protected boolean getUseUnicode() { return useUnicode; } /** * Retrieve the Sybase capability data. * * @return Capability bit mask as an <code>int</code>. */ protected boolean getSybaseInfo(int flag) { return (sybaseInfo & flag) != 0; } /** * Set the Sybase capability data. * * @param mask The capability bit mask. */ protected void setSybaseInfo(int mask) { sybaseInfo = mask; } /** * Called by the protocol to change the current character set. * * @param charset the server character set name */ protected void setServerCharset(final String charset) throws SQLException { // If the user specified a charset, ignore environment changes if (charsetSpecified) { Logger.println("Server charset " + charset + ". Ignoring as user requested " + serverCharset + '.'); return; } if (!charset.equals(serverCharset)) { loadCharset(charset); if (Logger.isActive()) { Logger.println("Set charset to " + serverCharset + '/' + charsetInfo); } } } /** * Load the Java charset to match the server character set. * * @param charset the server character set */ private void loadCharset(String charset) throws SQLException { // MS SQL Server's iso_1 is Cp1252 not ISO-8859-1! if (getServerType() == Driver.SQLSERVER && charset.equalsIgnoreCase("iso_1")) { charset = "Cp1252"; } // Do not default to any charset; if the charset is not found we want // to know about it CharsetInfo tmp = CharsetInfo.getCharset(charset); if (tmp == null) { throw new SQLException( Messages.get("error.charset.nomapping", charset), "2C000"); } loadCharset(tmp, charset); serverCharset = charset; } /** * Load the Java charset to match the server character set. * * @param ci the <code>CharsetInfo</code> to load */ private void loadCharset(CharsetInfo ci, String ref) throws SQLException { try { "This is a test".getBytes(ci.getCharset()); charsetInfo = ci; } catch (UnsupportedEncodingException ex) { throw new SQLException( Messages.get("error.charset.invalid", ref, ci.getCharset()), "2C000"); } socket.setCharsetInfo(charsetInfo); } /** * Discovers the server charset for server versions that do not send * <code>ENVCHANGE</code> packets on login ack, by executing a DB * vendor/version specific query. * <p> * Will throw an <code>SQLException</code> if used on SQL Server 7.0 or * 2000; the idea is that the charset should already be determined from * <code>ENVCHANGE</code> packets for these DB servers. * <p> * Should only be called from the constructor. * * @return the default server charset * @throws SQLException if an error condition occurs */ private String determineServerCharset() throws SQLException { String queryStr = null; switch (serverType) { case Driver.SQLSERVER: if (databaseProductVersion.indexOf("6.5") >= 0) { queryStr = SQL_SERVER_65_CHARSET_QUERY; } else { // This will never happen. Versions 7.0 and 2000 of SQL // Server always send ENVCHANGE packets, even over TDS 4.2. throw new SQLException( "Please use TDS protocol version 7.0 or higher"); } break; case Driver.SYBASE: // There's no need to check for versions here queryStr = SYBASE_SERVER_CHARSET_QUERY; break; } Statement stmt = this.createStatement(); ResultSet rs = stmt.executeQuery(queryStr); rs.next(); String charset = rs.getString(1); rs.close(); stmt.close(); return charset; } /** * Set the default collation for this connection. * <p> * Set by a SQL Server 2000 environment change packet. The collation * consists of the following fields: * <ul> * <li>bits 0-19 - The locale eg 0x0409 for US English which maps to code * page 1252 (Latin1_General). * <li>bits 20-31 - Reserved. * <li>bits 32-39 - Sort order (csid from syscharsets) * </ul> * If the sort order is non-zero it determines the character set, otherwise * the character set is determined by the locale id. * * @param collation The new collation. */ void setCollation(byte[] collation) throws SQLException { String strCollation = "0x" + Support.toHex(collation); // If the user specified a charset, ignore environment changes if (charsetSpecified) { Logger.println("Server collation " + strCollation + ". Ignoring as user requested " + serverCharset + '.'); return; } CharsetInfo tmp = CharsetInfo.getCharset(collation); loadCharset(tmp, strCollation); this.collation = collation; if (Logger.isActive()) { Logger.println("Set collation to " + strCollation + '/' + charsetInfo); } } /** * Retrieve the SQL Server 2000 default collation. * * @return The collation as a <code>byte[5]</code>. */ byte[] getCollation() { return collation; } /** * Retrieves whether a specific charset was requested on creation. If this * is the case, all character data should be encoded/decoded using that * charset. */ boolean isCharsetSpecified() { return charsetSpecified; } /** * Called by the protcol to change the current database context. * * @param newDb The new database selected on the server. * @param oldDb The old database as known by the server. * @throws SQLException */ protected void setDatabase(final String newDb, final String oldDb) throws SQLException { if (currentDatabase != null && !oldDb.equalsIgnoreCase(currentDatabase)) { throw new SQLException(Messages.get("error.connection.dbmismatch", oldDb, databaseName), "HY096"); } currentDatabase = newDb; if (Logger.isActive()) { Logger.println("Changed database from " + oldDb + " to " + newDb); } } /** * Update the connection instance with information about the server. * * @param databaseProductName The server name eg SQL Server. * @param databaseMajorVersion The major version eg 11 * @param databaseMinorVersion The minor version eg 92 * @param buildNumber The server build number. */ protected void setDBServerInfo(String databaseProductName, int databaseMajorVersion, int databaseMinorVersion, int buildNumber) { this.databaseProductName = databaseProductName; this.databaseMajorVersion = databaseMajorVersion; this.databaseMinorVersion = databaseMinorVersion; if (tdsVersion >= Driver.TDS70) { StringBuilder buf = new StringBuilder(10); if (databaseMajorVersion < 10) { buf.append('0'); } buf.append(databaseMajorVersion).append('.'); if (databaseMinorVersion < 10) { buf.append('0'); } buf.append(databaseMinorVersion).append('.'); buf.append(buildNumber); while (buf.length() < 10) { buf.insert(6, '0'); } databaseProductVersion = buf.toString(); } else { databaseProductVersion = databaseMajorVersion + "." + databaseMinorVersion; } } /** * Removes a statement object from the list maintained by the connection * and cleans up the statement cache if necessary. * <p> * Synchronized because it accesses the statement list, the statement cache * and the <code>baseTds</code>. * * @param statement the statement to remove */ synchronized void removeStatement(JtdsStatement statement) throws SQLException { // Remove the JtdsStatement from the statement list synchronized (statements) { for (int i = 0; i < statements.size(); i++) { WeakReference wr = (WeakReference) statements.get(i); if (wr != null) { Statement stmt = (Statement) wr.get(); // Remove the statement if found but also remove all // statements that have already been garbage collected if (stmt == null || stmt == statement) { statements.set(i, null); } } } } if (statement instanceof JtdsPreparedStatement) { // Clean up the prepared statement cache; getObsoleteHandles will // decrement the usage count for the set of used handles Collection handles = statementCache.getObsoleteHandles( ((JtdsPreparedStatement) statement).handles); if (handles != null) { if (serverType == Driver.SQLSERVER) { // SQL Server unprepare StringBuilder cleanupSql = new StringBuilder(handles.size() * 32); for (Iterator iterator = handles.iterator(); iterator.hasNext(); ) { ProcEntry pe = (ProcEntry) iterator.next(); // Could get put back if in a transaction that is // rolled back pe.appendDropSQL(cleanupSql); } if (cleanupSql.length() > 0) { baseTds.executeSQL(cleanupSql.toString(), null, null, true, 0, -1, -1, true); baseTds.clearResponseQueue(); } } else { // Sybase unprepare for (Iterator iterator = handles.iterator(); iterator.hasNext(); ) { ProcEntry pe = (ProcEntry)iterator.next(); if (pe.toString() != null) { // Remove the Sybase light weight proc baseTds.sybaseUnPrepare(pe.toString()); } } } } } } /** * Adds a statement object to the list maintained by the connection. * <p/> * WeakReferences are used so that statements can still be closed and * garbage collected even if not explicitly closed by the connection. * * @param statement statement to add */ void addStatement(JtdsStatement statement) { synchronized (statements) { for (int i = 0; i < statements.size(); i++) { WeakReference wr = (WeakReference) statements.get(i); // FIXME: entries from statements should be dropped immediately // on GC, instead of being kept until overwritten or connection // being closed if (wr == null || wr.get() == null) { statements.set(i, new WeakReference(statement)); return; } } statements.add(new WeakReference(statement)); } } /** * Checks that the connection is still open. * * @throws SQLException if the connection is closed */ void checkOpen() throws SQLException { if (closed) { throw new SQLException( Messages.get("error.generic.closed", "Connection"), "HY010"); } } /** * Checks that this connection is in local transaction mode. * * @param method the method name being tested * @throws SQLException if in XA distributed transaction mode */ void checkLocal(String method) throws SQLException { if (xaTransaction) { throw new SQLException( Messages.get("error.connection.badxaop", method), "HY010"); } } /** * Reports that user tried to call a method which has not been implemented. * * @param method the method name to report in the error message * @throws SQLException always, with the not implemented message */ static void notImplemented(String method) throws SQLException { throw new SQLException( Messages.get("error.generic.notimp", method), "HYC00"); } /** * Retrieves the DBMS major version. * * @return the version as an <code>int</code> */ public int getDatabaseMajorVersion() { return databaseMajorVersion; } /** * Retrieves the DBMS minor version. * * @return the version as an <code>int</code> */ public int getDatabaseMinorVersion() { return databaseMinorVersion; } /** * Retrieves the DBMS product name. * * @return the name as a <code>String</code> */ String getDatabaseProductName() { return databaseProductName; } /** * Retrieves the DBMS product version. * * @return the version as a <code>String</code> */ String getDatabaseProductVersion() { return databaseProductVersion; } /** * Retrieves the original connection URL. * * @return the connection url as a <code>String</code> */ String getURL() { return url; } /** * Retrieves the host and port for this connection. * <p> * Used to identify same resource manager in XA transactions. * * @return the hostname and port as a <code>String</code> */ public String getRmHost() { return serverName + ':' + portNumber; } /** * Forces the closed status on the statement if an I/O error has occurred. */ void setClosed() { if (!closed) { closed = true; // Make sure we release the socket and all data buffered at the socket // level try { socket.close(); } catch (IOException e) { // Ignore; shouldn't happen anyway } } } /** * Invokes the <code>xp_jtdsxa</code> extended stored procedure on the * server. * <p/> * Synchronized because it accesses the <code>baseTds</code>. * * @param args the arguments eg cmd, rmid, flags etc. * @param data option byte data eg open string xid etc. * @return optional byte data eg OLE cookie * @throws SQLException if an error condition occurs */ synchronized byte[][] sendXaPacket(int args[], byte[] data) throws SQLException { ParamInfo params[] = new ParamInfo[6]; params[0] = new ParamInfo(Types.INTEGER, null, ParamInfo.RETVAL); params[1] = new ParamInfo(Types.INTEGER, new Integer(args[1]), ParamInfo.INPUT); params[2] = new ParamInfo(Types.INTEGER, new Integer(args[2]), ParamInfo.INPUT); params[3] = new ParamInfo(Types.INTEGER, new Integer(args[3]), ParamInfo.INPUT); params[4] = new ParamInfo(Types.INTEGER, new Integer(args[4]), ParamInfo.INPUT); params[5] = new ParamInfo(Types.VARBINARY, data, ParamInfo.OUTPUT); // // Execute our extended stored procedure (let's hope it is installed!). // baseTds.executeSQL(null, "master..xp_jtdsxa", params, false, 0, -1, -1, true); // // Now process results // ArrayList xids = new ArrayList(); while (!baseTds.isEndOfResponse()) { if (baseTds.getMoreResults()) { // This had better be the results from a xa_recover command while (baseTds.getNextRow()) { Object row[] = baseTds.getRowData(); if (row.length == 1 && row[0] instanceof byte[]) { xids.add(row[0]); } } } } messages.checkErrors(); if (params[0].getOutValue() instanceof Integer) { // Should be return code from XA command args[0] = ((Integer)params[0].getOutValue()).intValue(); } else { args[0] = -7; // XAException.XAER_RMFAIL } if (xids.size() > 0) { // List of XIDs from xa_recover byte list[][] = new byte[xids.size()][]; for (int i = 0; i < xids.size(); i++) { list[i] = (byte[])xids.get(i); } return list; } else if (params[5].getOutValue() instanceof byte[]) { // xa_open the xa connection ID // xa_start OLE Transaction cookie byte cookie[][] = new byte[1][]; cookie[0] = (byte[])params[5].getOutValue(); return cookie; } else { // All other cases return null; } } /** * Enlists the current connection in a distributed transaction. * * @param oleTranID the OLE transaction cookie or null to delist * @throws SQLException if an error condition occurs */ synchronized void enlistConnection(byte[] oleTranID) throws SQLException { if (oleTranID != null) { // TODO: Stored procs are no good but maybe prepare will be OK. prepareSql = TdsCore.EXECUTE_SQL; baseTds.enlistConnection(1, oleTranID); xaTransaction = true; } else { baseTds.enlistConnection(1, null); xaTransaction = false; } } /** * Sets the XA transaction ID when running in emulation mode. * * @param xid the XA Transaction ID */ void setXid(Object xid) { this.xid = xid; xaTransaction = xid != null; } /** * Gets the XA transaction ID when running in emulation mode. * * @return the transaction ID as an <code>Object</code> */ Object getXid() { return xid; } /** * Sets the XA state variable. * * @param value the XA state value */ void setXaState(int value) { xaState = value; } /** * Retrieves the XA state variable. * * @return the xa state variable as an <code>int</code> */ int getXaState() { return xaState; } /** * Retrieves the XA Emulation flag. * @return True if in XA emulation mode. */ boolean isXaEmulation() { return xaEmulation; } /** * Retrieves the connection mutex and acquires an exclusive lock on the * network connection. * * @return * the mutex object as a <code>Semaphore</code> */ Semaphore getMutex() { boolean interrupted = false; while( true ) { // JDBC can not be interrupted, retry on InterruptedException try { mutex.acquire(); break; } catch( InterruptedException e ) { // interrupt status is cleared now interrupted = true; } } // Bug [1596743] do not absorb interrupt status if( interrupted ) { Thread.currentThread().interrupt(); } return mutex; } /** * Releases (either closes or caches) a <code>TdsCore</code>. * * @param tds * the <code>TdsCore</code> instance to release * * @throws SQLException * if an error occurs while closing or cleaning up */ synchronized void releaseTds( TdsCore tds ) throws SQLException { if( cachedTds != null ) { // There's already a cached TdsCore; close this one tds.close(); } else { // No cached TdsCore; clean up this one and cache it tds.clearResponseQueue(); tds.cleanUp(); cachedTds = tds; } } /** * Retrieves the cached <code>TdsCore</code> or <code>null</code> if * nothing is cached and resets the cache (sets it to <code>null</code>). * * @return the value of {@link #cachedTds} * @todo Should probably synchronize on another object */ synchronized TdsCore getCachedTds() { TdsCore result = cachedTds; cachedTds = null; return result; } // // ------------------- java.sql.Connection interface methods ------------------- // public int getHoldability() throws SQLException { checkOpen(); return JtdsResultSet.HOLD_CURSORS_OVER_COMMIT; } synchronized public int getTransactionIsolation() throws SQLException { checkOpen(); return transactionIsolation; } synchronized public void clearWarnings() throws SQLException { checkOpen(); messages.clearWarnings(); } /** * Releases this <code>Connection</code> object's database and JDBC * resources immediately instead of waiting for them to be automatically * released. * <p> * Calling the method close on a <code>Connection</code> object that is * already closed is a no-op. * <p> * <b>Note:</b> A <code>Connection</code> object is automatically closed * when it is garbage collected. Certain fatal errors also close a * <code>Connection</code> object. * <p> * Synchronized because it accesses the statement list and the * <code>baseTds</code>. * * @throws SQLException if a database access error occurs */ synchronized public void close() throws SQLException { if (!closed) { try { // // Close any open statements // ArrayList tmpList; synchronized (statements) { tmpList = new ArrayList(statements); statements.clear(); } for (int i = 0; i < tmpList.size(); i++) { WeakReference wr = (WeakReference)tmpList.get(i); if (wr != null) { Statement stmt = (Statement) wr.get(); if (stmt != null) { try { stmt.close(); } catch (SQLException ex) { // Ignore } } } } try { // Tell the server the session is ending, close network connection if (baseTds != null) { baseTds.closeConnection(); baseTds.close(); } // Close cached TdsCore if (cachedTds != null) { cachedTds.close(); cachedTds = null; } } catch (SQLException ex) { // Ignore } if (socket != null) { socket.close(); } } catch (IOException e) { // Ignore } finally { closed = true; synchronized( connections ) { if (--connections[0] == 0) { TimerThread.stopTimer(); } } } } } synchronized public void commit() throws SQLException { checkOpen(); checkLocal("commit"); if (getAutoCommit()) { throw new SQLException( Messages.get("error.connection.autocommit", "commit"), "25000"); } baseTds.submitSQL("IF @@TRANCOUNT > 0 COMMIT TRAN"); procInTran.clear(); clearSavepoints(); } synchronized public void rollback() throws SQLException { checkOpen(); checkLocal("rollback"); if (getAutoCommit()) { throw new SQLException( Messages.get("error.connection.autocommit", "rollback"), "25000"); } baseTds.submitSQL("IF @@TRANCOUNT > 0 ROLLBACK TRAN"); for (int i = 0; i < procInTran.size(); i++) { String key = (String) procInTran.get(i); if (key != null) { statementCache.remove(key); } } procInTran.clear(); clearSavepoints(); } synchronized public boolean getAutoCommit() throws SQLException { checkOpen(); return autoCommit; } public boolean isClosed() throws SQLException { return closed; } public boolean isReadOnly() throws SQLException { checkOpen(); return readOnly; } public void setHoldability(int holdability) throws SQLException { checkOpen(); switch (holdability) { case JtdsResultSet.HOLD_CURSORS_OVER_COMMIT: break; case JtdsResultSet.CLOSE_CURSORS_AT_COMMIT: throw new SQLException( Messages.get("error.generic.optvalue", "CLOSE_CURSORS_AT_COMMIT", "setHoldability"), "HY092"); default: throw new SQLException( Messages.get("error.generic.badoption", Integer.toString(holdability), "holdability"), "HY092"); } } synchronized public void setTransactionIsolation(int level) throws SQLException { checkOpen(); if (transactionIsolation == level) { // No need to submit a request return; } String sql = "SET TRANSACTION ISOLATION LEVEL "; boolean sybase = serverType == Driver.SYBASE; switch (level) { case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED: sql += (sybase) ? "0" : "READ UNCOMMITTED"; break; case java.sql.Connection.TRANSACTION_READ_COMMITTED: sql += (sybase) ? "1" : "READ COMMITTED"; break; case java.sql.Connection.TRANSACTION_REPEATABLE_READ: sql += (sybase) ? "2" : "REPEATABLE READ"; break; case java.sql.Connection.TRANSACTION_SERIALIZABLE: sql += (sybase) ? "3" : "SERIALIZABLE"; break; case TRANSACTION_SNAPSHOT: if (sybase) { throw new SQLException( Messages.get("error.generic.optvalue", "TRANSACTION_SNAPSHOT", "setTransactionIsolation"), "HY024"); } else { sql += "SNAPSHOT"; } break; case java.sql.Connection.TRANSACTION_NONE: throw new SQLException( Messages.get("error.generic.optvalue", "TRANSACTION_NONE", "setTransactionIsolation"), "HY024"); default: throw new SQLException( Messages.get("error.generic.badoption", Integer.toString(level), "level"), "HY092"); } transactionIsolation = level; baseTds.submitSQL(sql); } synchronized public void setAutoCommit(boolean autoCommit) throws SQLException { checkOpen(); checkLocal("setAutoCommit"); if (this.autoCommit == autoCommit) { // If we don't need to change the current auto commit mode, don't // submit a request and don't commit either. Section 10.1.1 of the // JDBC 3.0 spec states that the transaction should be committed // only "if the value of auto-commit is _changed_ in the middle of // a transaction". This takes precedence over the API docs, which // states that "if this method is called during a transaction, the // transaction is committed". return; } StringBuilder sql = new StringBuilder(70); // if (!this.autoCommit) { // If we're in manual commit mode the spec requires that we commit // the transaction when setAutoCommit() is called sql.append("IF @@TRANCOUNT > 0 COMMIT TRAN\r\n"); } if (serverType == Driver.SYBASE) { if (autoCommit) { sql.append("SET CHAINED OFF"); } else { sql.append("SET CHAINED ON"); } } else { if (autoCommit) { sql.append("SET IMPLICIT_TRANSACTIONS OFF"); } else { sql.append("SET IMPLICIT_TRANSACTIONS ON"); } } baseTds.submitSQL(sql.toString()); this.autoCommit = autoCommit; } public void setReadOnly(boolean readOnly) throws SQLException { checkOpen(); this.readOnly = readOnly; } synchronized public String getCatalog() throws SQLException { checkOpen(); return currentDatabase; } synchronized public void setCatalog(String catalog) throws SQLException { checkOpen(); if (currentDatabase != null && currentDatabase.equals(catalog)) { return; } int maxlength = tdsVersion >= Driver.TDS70 ? 128 : 30; if (catalog.length() > maxlength || catalog.length() < 1) { throw new SQLException( Messages.get("error.generic.badparam", catalog, "catalog"), "3D000"); } String sql = tdsVersion >= Driver.TDS70 ? ("use [" + catalog + ']') : "use " + catalog; baseTds.submitSQL(sql); } public DatabaseMetaData getMetaData() throws SQLException { checkOpen(); return new JtdsDatabaseMetaData(this); } public SQLWarning getWarnings() throws SQLException { checkOpen(); return messages.getWarnings(); } public Statement createStatement() throws SQLException { checkOpen(); return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); } synchronized public Statement createStatement(int type, int concurrency) throws SQLException { checkOpen(); JtdsStatement stmt = new JtdsStatement(this, type, concurrency); addStatement(stmt); return stmt; } public Statement createStatement(int type, int concurrency, int holdability) throws SQLException { checkOpen(); setHoldability(holdability); return createStatement(type, concurrency); } public Map getTypeMap() throws SQLException { checkOpen(); return new HashMap(); } public void setTypeMap(Map map) throws SQLException { checkOpen(); notImplemented("Connection.setTypeMap(Map)"); } public String nativeSQL(String sql) throws SQLException { checkOpen(); if (sql == null || sql.length() == 0) { throw new SQLException(Messages.get("error.generic.nosql"), "HY000"); } String[] result = SQLParser.parse(sql, new ArrayList(), this, false); return result[0]; } public CallableStatement prepareCall(String sql) throws SQLException { checkOpen(); return prepareCall(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); } synchronized public CallableStatement prepareCall(String sql, int type, int concurrency) throws SQLException { checkOpen(); if (sql == null || sql.length() == 0) { throw new SQLException(Messages.get("error.generic.nosql"), "HY000"); } JtdsCallableStatement stmt = new JtdsCallableStatement(this, sql, type, concurrency); addStatement(stmt); return stmt; } public CallableStatement prepareCall( String sql, int type, int concurrency, int holdability) throws SQLException { checkOpen(); setHoldability(holdability); return prepareCall(sql, type, concurrency); } public PreparedStatement prepareStatement(String sql) throws SQLException { checkOpen(); return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); } public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { checkOpen(); if (sql == null || sql.length() == 0) { throw new SQLException(Messages.get("error.generic.nosql"), "HY000"); } if (autoGeneratedKeys != JtdsStatement.RETURN_GENERATED_KEYS && autoGeneratedKeys != JtdsStatement.NO_GENERATED_KEYS) { throw new SQLException( Messages.get("error.generic.badoption", Integer.toString(autoGeneratedKeys), "autoGeneratedKeys"), "HY092"); } JtdsPreparedStatement stmt = new JtdsPreparedStatement(this, sql, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, autoGeneratedKeys == JtdsStatement.RETURN_GENERATED_KEYS); addStatement(stmt); return stmt; } synchronized public PreparedStatement prepareStatement(String sql, int type, int concurrency) throws SQLException { checkOpen(); if (sql == null || sql.length() == 0) { throw new SQLException(Messages.get("error.generic.nosql"), "HY000"); } JtdsPreparedStatement stmt = new JtdsPreparedStatement(this, sql, type, concurrency, false); addStatement(stmt); return stmt; } public PreparedStatement prepareStatement( String sql, int type, int concurrency, int holdability) throws SQLException { checkOpen(); setHoldability(holdability); return prepareStatement(sql, type, concurrency); } public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { if (columnIndexes == null) { throw new SQLException( Messages.get("error.generic.nullparam", "prepareStatement"),"HY092"); } else if (columnIndexes.length != 1) { throw new SQLException( Messages.get("error.generic.needcolindex", "prepareStatement"),"HY092"); } return prepareStatement(sql, JtdsStatement.RETURN_GENERATED_KEYS); } public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { if (columnNames == null) { throw new SQLException( Messages.get("error.generic.nullparam", "prepareStatement"),"HY092"); } else if (columnNames.length != 1) { throw new SQLException( Messages.get("error.generic.needcolname", "prepareStatement"),"HY092"); } return prepareStatement(sql, JtdsStatement.RETURN_GENERATED_KEYS); } /** * Add a savepoint to the list maintained by this connection. * * @param savepoint The savepoint object to add. * @throws SQLException */ private void setSavepoint(SavepointImpl savepoint) throws SQLException { Statement statement = null; try { statement = createStatement(); statement.execute("IF @@TRANCOUNT=0 BEGIN " + "SET IMPLICIT_TRANSACTIONS OFF; " + "BEGIN TRAN; " // Fix for bug []Patch: in SET IMPLICIT_TRANSACTIONS ON + "SET IMPLICIT_TRANSACTIONS ON; " + "END " // mode BEGIN TRAN actually starts two transactions! + "SAVE TRAN jtds" + savepoint.getId()); } finally { if (statement != null) { statement.close(); } } synchronized (this) { if (savepoints == null) { savepoints = new ArrayList(); } savepoints.add(savepoint); } } /** * Releases all savepoints. Used internally when committing or rolling back * a transaction. */ private synchronized void clearSavepoints() { if (savepoints != null) { savepoints.clear(); } if (savepointProcInTran != null) { savepointProcInTran.clear(); } savepointId = 0; } // JDBC 3 public synchronized void releaseSavepoint(Savepoint savepoint) throws SQLException { checkOpen(); if (savepoints == null) { throw new SQLException( Messages.get("error.connection.badsavep"), "25000"); } int index = savepoints.indexOf(savepoint); if (index == -1) { throw new SQLException( Messages.get("error.connection.badsavep"), "25000"); } Object tmpSavepoint = savepoints.remove(index); if (savepointProcInTran != null) { if (index != 0) { // If this wasn't the outermost savepoint, move all procedures // to the "wrapping" savepoint's list; when and if that // savepoint will be rolled back it will clear these procedures // too List keys = (List) savepointProcInTran.get(savepoint); if (keys != null) { Savepoint wrapping = (Savepoint) savepoints.get(index - 1); List wrappingKeys = (List) savepointProcInTran.get(wrapping); if (wrappingKeys == null) { wrappingKeys = new ArrayList(); } wrappingKeys.addAll(keys); savepointProcInTran.put(wrapping, wrappingKeys); } } // If this was the outermost savepoint, just drop references to // all procedures; they will be managed by the connection savepointProcInTran.remove(tmpSavepoint); } } public synchronized void rollback(Savepoint savepoint) throws SQLException { checkOpen(); checkLocal("rollback"); if (savepoints == null) { throw new SQLException( Messages.get("error.connection.badsavep"), "25000"); } int index = savepoints.indexOf(savepoint); if (index == -1) { throw new SQLException( Messages.get("error.connection.badsavep"), "25000"); } else if (getAutoCommit()) { throw new SQLException( Messages.get("error.connection.savenorollback"), "25000"); } Statement statement = null; try { statement = createStatement(); statement.execute("ROLLBACK TRAN jtds" + ((SavepointImpl) savepoint).getId()); } finally { if (statement != null) { statement.close(); } } int size = savepoints.size(); for (int i = size - 1; i >= index; i--) { Object tmpSavepoint = savepoints.remove(i); if (savepointProcInTran == null) { continue; } List keys = (List) savepointProcInTran.get(tmpSavepoint); if (keys == null) { continue; } for (Iterator iterator = keys.iterator(); iterator.hasNext();) { String key = (String) iterator.next(); removeCachedProcedure(key); } } // recreate savepoint setSavepoint((SavepointImpl) savepoint); } synchronized public Savepoint setSavepoint() throws SQLException { checkOpen(); checkLocal("setSavepoint"); if (getAutoCommit()) { throw new SQLException( Messages.get("error.connection.savenoset"), "25000"); } SavepointImpl savepoint = new SavepointImpl(getNextSavepointId()); setSavepoint(savepoint); return savepoint; } synchronized public Savepoint setSavepoint(String name) throws SQLException { checkOpen(); checkLocal("setSavepoint"); if (getAutoCommit()) { throw new SQLException( Messages.get("error.connection.savenoset"), "25000"); } else if (name == null) { throw new SQLException( Messages.get("error.connection.savenullname", "savepoint"), "25000"); } SavepointImpl savepoint = new SavepointImpl(getNextSavepointId(), name); setSavepoint(savepoint); return savepoint; } /** * Returns the next savepoint identifier. * * @return the next savepoint identifier */ private int getNextSavepointId() { return ++savepointId; } /** * Add a stored procedure to the savepoint cache. * * @param key The signature of the procedure to cache. */ synchronized void addCachedProcedure(String key) { if (savepoints == null || savepoints.size() == 0) { return; } if (savepointProcInTran == null) { savepointProcInTran = new HashMap(); } // Retrieve the current savepoint Object savepoint = savepoints.get(savepoints.size() - 1); List keys = (List) savepointProcInTran.get(savepoint); if (keys == null) { keys = new ArrayList(); } keys.add(key); savepointProcInTran.put(savepoint, keys); } /////// JDBC4 demarcation, do NOT put any JDBC3 code below this line /////// /* (non-Javadoc) * @see java.sql.Connection#createArrayOf(java.lang.String, java.lang.Object[]) */ public Array createArrayOf(String typeName, Object[] elements) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#createBlob() */ public Blob createBlob() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#createClob() */ public Clob createClob() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#createNClob() */ public NClob createNClob() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#createSQLXML() */ public SQLXML createSQLXML() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#createStruct(java.lang.String, java.lang.Object[]) */ public Struct createStruct(String typeName, Object[] attributes) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#getClientInfo() */ public Properties getClientInfo() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#getClientInfo(java.lang.String) */ public String getClientInfo(String name) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#isValid(int) */ public boolean isValid(int timeout) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#setClientInfo(java.util.Properties) */ public void setClientInfo(Properties properties) throws SQLClientInfoException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Connection#setClientInfo(java.lang.String, java.lang.String) */ public void setClientInfo(String name, String value) throws SQLClientInfoException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Wrapper#isWrapperFor(java.lang.Class) */ public boolean isWrapperFor(Class arg0) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } /* (non-Javadoc) * @see java.sql.Wrapper#unwrap(java.lang.Class) */ public Object unwrap(Class arg0) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } //// JDBC4.1 demarcation, do NOT put any JDBC3/4.0 code below this line //// @Override public void setSchema(String schema) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } @Override public String getSchema() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } @Override public void abort(java.util.concurrent.Executor executor) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } @Override public void setNetworkTimeout(java.util.concurrent.Executor executor, int milliseconds) throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } @Override public int getNetworkTimeout() throws SQLException { // TODO Auto-generated method stub throw new AbstractMethodError(); } }
⏎ net/sourceforge/jtds/jdbc/JtdsConnection.java
Or download all of them as a single archive file:
File name: jtds-1.3.1-fyi.zip File size: 323160 bytes Release date: 2013-06-08 Download
⇐ What Is jtds-1.3.1-dist.zip?
2016-11-26, 7813👍, 0💬
Popular Posts:
This package is the backport of java.util.concurrent API, introduced in Java 5.0 and further refined...
This package is the backport of java.util.concurrent API, introduced in Java 5.0 and further refined...
JDK 7 tools.jar is the JAR file for JDK 7 tools. It contains Java classes to support different JDK t...
Swingx is the SwingLabs Swing Component Extensions. JAR File Size and Download Location: File name: ...
JDK 8 tools.jar is the JAR file for JDK 8 tools. It contains Java classes to support different JDK t...