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 <= 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: }
|