0001: /**
0002: * Sequoia: Database clustering technology.
0003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
0004: * Science And Control (INRIA).
0005: * Copyright (C) 2005-2006 Continuent, Inc.
0006: * Contact: sequoia@continuent.org
0007: *
0008: * Licensed under the Apache License, Version 2.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.apache.org/licenses/LICENSE-2.0
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: * Initial developer(s): Emmanuel Cecchet.
0021: * Contributor(s): Julie Marguerite, Guillaume Bort, Duncan Smith, Vadim Kassin,
0022: * Nicolas Modrzyk, Jaco Swart, Jean-Bernard van Zuylen
0023: * Completely refactored by Marc Herbert to remove the use of Java serialization.
0024: */package org.continuent.sequoia.driver;
0025:
0026: import java.io.IOException;
0027: import java.net.Socket;
0028: import java.security.GeneralSecurityException;
0029: import java.sql.ResultSet;
0030: import java.sql.ResultSetMetaData;
0031: import java.sql.SQLException;
0032: import java.sql.SQLWarning;
0033: import java.sql.Savepoint;
0034: import java.util.ArrayList;
0035: import java.util.HashMap;
0036: import java.util.Iterator;
0037: import java.util.LinkedList;
0038: import java.util.List;
0039: import java.util.Map;
0040:
0041: import org.continuent.sequoia.common.exceptions.AuthenticationException;
0042: import org.continuent.sequoia.common.exceptions.NoMoreBackendException;
0043: import org.continuent.sequoia.common.exceptions.NoMoreControllerException;
0044: import org.continuent.sequoia.common.exceptions.NotImplementedException;
0045: import org.continuent.sequoia.common.exceptions.ProtocolException;
0046: import org.continuent.sequoia.common.exceptions.driver.DriverIOException;
0047: import org.continuent.sequoia.common.exceptions.driver.DriverSQLException;
0048: import org.continuent.sequoia.common.exceptions.driver.VirtualDatabaseUnavailableException;
0049: import org.continuent.sequoia.common.exceptions.driver.protocol.BackendDriverException;
0050: import org.continuent.sequoia.common.exceptions.driver.protocol.ControllerCoreException;
0051: import org.continuent.sequoia.common.exceptions.driver.protocol.SerializableException;
0052: import org.continuent.sequoia.common.protocol.Commands;
0053: import org.continuent.sequoia.common.protocol.SQLDataSerialization;
0054: import org.continuent.sequoia.common.protocol.TypeTag;
0055: import org.continuent.sequoia.common.protocol.SQLDataSerialization.Serializer;
0056: import org.continuent.sequoia.common.sql.Request;
0057: import org.continuent.sequoia.common.sql.RequestWithResultSetParameters;
0058: import org.continuent.sequoia.common.stream.DriverBufferedInputStream;
0059: import org.continuent.sequoia.common.stream.DriverBufferedOutputStream;
0060:
0061: /**
0062: * This class implements the communication protocol to the Controller.
0063: * <p>
0064: * Connection.java was inspired from the PostgreSQL JDBC driver by Peter T.
0065: * Mount.
0066: *
0067: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
0068: * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
0069: * @author <a href="mailto:vadim@kase.kz">Vadim Kassin </a>
0070: * @author <a href="mailto:duncan@mightybot.com">Duncan Smith </a>
0071: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
0072: * @author <a href="mailto:jaco.swart@iblocks.co.uk">Jaco Swart </a>
0073: * @author <a href="mailto:Marc.Herbert@emicnetworks.com">Marc Herbert </a>
0074: * @author <a href="mailto:jbvanzuylen@transwide.com">Jean-Bernard van Zuylen
0075: * </a>
0076: * @version 2.0
0077: */
0078: public class Connection implements java.sql.Connection {
0079: /** Status of the connection. */
0080: protected boolean isClosed = false;
0081:
0082: protected String escapeChar;
0083:
0084: /** Sequoia controller we are connected to */
0085: protected ControllerInfo controllerInfo = null;
0086:
0087: // ConnectionClosingThread
0088: /** Driver that created us. */
0089: protected Driver driver = null;
0090:
0091: /** Connection with the controller. */
0092: protected Socket socket;
0093: /** Socket input stream. */
0094: protected DriverBufferedInputStream socketInput;
0095: /** Socket output stream. */
0096: protected DriverBufferedOutputStream socketOutput;
0097:
0098: /** @see org.continuent.sequoia.controller.core.ControllerConstants#SQL_SHORT_FORM_LENGTH */
0099: public static final int ABBREV_REQUEST_LENGTH = 40;
0100:
0101: // used by Statement (and maybe also by some others _below_)
0102: static final String LINE_SEPARATOR = System
0103: .getProperty("line.separator");
0104:
0105: // Member variables describing the state of the connection
0106:
0107: /** Commit mode of the connection (<code>true</code>= automatic). */
0108: protected boolean autoCommit = true;
0109:
0110: /** Is the connection in read-only mode ? */
0111: protected boolean readOnly = false;
0112:
0113: /** Has a write request been executed in the current transaction? */
0114: boolean writeExecutedInTransaction = false;
0115:
0116: /** Default transaction isolation level if the user has not enforced one */
0117: public static final int DEFAULT_TRANSACTION_ISOLATION_LEVEL = -1;
0118:
0119: /** Current transaction isolation level. */
0120: protected int isolationLevel = DEFAULT_TRANSACTION_ISOLATION_LEVEL;
0121:
0122: /** transaction identifier. */
0123: protected long transactionId = 0;
0124:
0125: /** List of <code>Warnings</code> for this connection. */
0126: protected SQLWarning firstWarning = null;
0127:
0128: /** ResultSet holdability (JDBC 3) */
0129: protected int holdability = HOLDABILITY_NOT_SET;
0130: private static final int HOLDABILITY_NOT_SET = -1;
0131:
0132: /** Meta-data of Sequoia connections. */
0133: protected DatabaseMetaData metaData = null;
0134:
0135: /** Parsed URL to the database. */
0136: private final SequoiaUrl sequoiaUrl;
0137:
0138: /** Virtual database user used for this connection. */
0139: protected String vdbUser = null;
0140: protected String vdbPassword = null;
0141:
0142: private boolean connectionPooling;
0143:
0144: // Escape processing tuning
0145: protected boolean escapeBackslash;
0146: protected boolean escapeSingleQuote;
0147:
0148: // flag to check if a new transaction must be
0149: // started before executing any statement
0150: private boolean mustBeginTransaction = false;
0151:
0152: // True if the connection must be persisted on all cluster backends even when
0153: // autoCommit=true
0154: private boolean persistentConnection;
0155:
0156: // Persistent connection identifier if persistentConnection is true
0157: private long persistentConnectionId;
0158:
0159: // Do we want SQL Warnings ?
0160: private boolean retrieveSQLWarnings = false;
0161:
0162: // Do we force generated keys to be retrieved?
0163: private boolean alwaysGetGeneratedKeys = false;
0164:
0165: /*****************************************************************************
0166: * *************** * Constructor and get/set methods * ***********************
0167: * ****************************************************************************
0168: */
0169:
0170: /**
0171: * Creates a new <code>Connection</code> instance.
0172: *
0173: * @param driver calling driver
0174: * @param socket connection with the controller
0175: * @param in socket input stream
0176: * @param out socket output stream
0177: * @param sequoiaUrl Sequoia URL of the database
0178: * @param controller controller we are connected to
0179: * @param userName user login
0180: * @param password user password
0181: * @throws AuthenticationException login error
0182: * @throws IOException stream error
0183: * @throws SQLException if the virtual database is not available on the
0184: * controller
0185: */
0186: Connection(Driver driver, Socket socket,
0187: DriverBufferedInputStream in,
0188: DriverBufferedOutputStream out, SequoiaUrl sequoiaUrl,
0189: ControllerInfo controller, String userName, String password)
0190: throws AuthenticationException, IOException,
0191: VirtualDatabaseUnavailableException {
0192: this .driver = driver;
0193: this .socket = socket;
0194: this .socketInput = in;
0195: this .socketOutput = out;
0196: this .sequoiaUrl = sequoiaUrl;
0197: this .controllerInfo = controller;
0198: this .vdbUser = userName;
0199: this .vdbPassword = password;
0200:
0201: escapeBackslash = driver.getEscapeBackslash();
0202: escapeChar = driver.getEscapeChar();
0203: escapeSingleQuote = driver.getEscapeSingleQuote();
0204: connectionPooling = driver.getConnectionPooling();
0205: persistentConnection = driver.getPersistentConnection();
0206: retrieveSQLWarnings = driver.getRetrieveSQLWarnings();
0207: alwaysGetGeneratedKeys = driver.getRetrieveGeneratedKeys();
0208:
0209: // Is virtual database available?
0210: if (!in.readBoolean()) // failed
0211: throw new VirtualDatabaseUnavailableException(in
0212: .readLongUTF());
0213:
0214: // Is authentication successful?
0215: if (!in.readBoolean()) // failed
0216: throw new AuthenticationException(in.readLongUTF());
0217:
0218: setUrlParametersOptionsOnConnection(sequoiaUrl);
0219:
0220: out.writeLongUTF(LINE_SEPARATOR);
0221: out.writeBoolean(persistentConnection);
0222: out.flush();
0223:
0224: if (persistentConnection) {
0225: if (in.readBoolean())
0226: persistentConnectionId = in.readLong();
0227: else
0228: throw new AuthenticationException(
0229: "No more persistent connections available for virtual database "
0230: + sequoiaUrl.getDatabaseName()
0231: + "[url=" + sequoiaUrl + "]");
0232: }
0233:
0234: out.writeBoolean(retrieveSQLWarnings);
0235: out.flush();
0236:
0237: if (sequoiaUrl.isDebugEnabled())
0238: System.out.println("New connection:" + this .toString());
0239: }
0240:
0241: /**
0242: * Set SequoiaUrl parameters options on connection.
0243: *
0244: * @param sequoiaUrl the Sequoia URL to use
0245: */
0246: private void setUrlParametersOptionsOnConnection(
0247: SequoiaUrl sequoiaUrl) {
0248: HashMap sequoiaUrlParameters = sequoiaUrl.getParameters();
0249:
0250: String escapeBaskslash = (String) sequoiaUrlParameters
0251: .get(Driver.ESCAPE_BACKSLASH_PROPERTY);
0252: if (escapeBaskslash != null)
0253: setEscapeBackslash(new Boolean(escapeBaskslash)
0254: .booleanValue());
0255:
0256: String escapeQuote = (String) sequoiaUrlParameters
0257: .get(Driver.ESCAPE_SINGLE_QUOTE_PROPERTY);
0258: if (escapeQuote != null)
0259: setEscapeSingleQuote(new Boolean(escapeQuote)
0260: .booleanValue());
0261:
0262: String escapeCharacter = (String) sequoiaUrlParameters
0263: .get(Driver.ESCAPE_CHARACTER_PROPERTY);
0264: if (escapeCharacter != null)
0265: setEscapeChar(escapeCharacter);
0266:
0267: // true if transparent connection pooling must be used
0268: String connPool = (String) sequoiaUrlParameters
0269: .get(Driver.CONNECTION_POOLING_PROPERTY);
0270: if (connPool != null)
0271: this .connectionPooling = "true".equals(connPool);
0272:
0273: String persistentConn = (String) sequoiaUrlParameters
0274: .get(Driver.PERSISTENT_CONNECTION_PROPERTY);
0275: if (persistentConn != null)
0276: this .persistentConnection = "true".equals(persistentConn);
0277:
0278: String retSQLWarns = (String) sequoiaUrlParameters
0279: .get(Driver.RETRIEVE_SQL_WARNINGS_PROPERTY);
0280: if (retSQLWarns != null)
0281: this .retrieveSQLWarnings = "true".equals(retSQLWarns);
0282:
0283: String retGeneratedKeys = (String) sequoiaUrlParameters
0284: .get(Driver.ALWAYS_RETRIEVE_GENERATED_KEYS_PROPERTY);
0285: if (retGeneratedKeys != null)
0286: this .alwaysGetGeneratedKeys = "true"
0287: .equals(retGeneratedKeys);
0288:
0289: if (sequoiaUrl.isDebugEnabled()) {
0290: // Give a warning for unrecognized driver options in the URL
0291: // (only in the URL: unknown properties have been filtered out)
0292: for (Iterator iter = sequoiaUrlParameters.entrySet()
0293: .iterator(); iter.hasNext();) {
0294: Map.Entry e = (Map.Entry) iter.next();
0295: String param = (String) e.getKey();
0296: if (!Driver.driverPropertiesNames.contains(param))
0297: System.out
0298: .println("Unrecognized driver parameter: "
0299: + param + " = "
0300: + (String) e.getValue());
0301: }
0302: }
0303: }
0304:
0305: /**
0306: * Get the information about the controller we are connected to
0307: *
0308: * @return <code>ControllerInfo</code> object of the controller
0309: */
0310: public ControllerInfo getControllerInfo() {
0311: return controllerInfo;
0312: }
0313:
0314: /**
0315: * Gets the password used to login to the database.
0316: *
0317: * @return password
0318: */
0319: public String getPassword() {
0320: return vdbPassword;
0321: }
0322:
0323: /**
0324: * Gets the untouched String URL that was passed by the client application
0325: *
0326: * @return value of url.
0327: */
0328: public String getUrl() {
0329: return sequoiaUrl.getUrl();
0330: }
0331:
0332: /**
0333: * Gets the parsed Sequoia URL, including merged properties
0334: *
0335: * @return the parsed Sequoia URL
0336: */
0337: SequoiaUrl getSequoiaUrl() {
0338: return sequoiaUrl;
0339: }
0340:
0341: /**
0342: * Gets the user name used to login to the database.
0343: *
0344: * @return login name
0345: */
0346: public String getUserName() {
0347: return vdbUser;
0348: }
0349:
0350: /**
0351: * Returns the escapeBackslash value.
0352: *
0353: * @return Returns the escapeBackslash.
0354: */
0355: public boolean isEscapeBackslash() {
0356: return escapeBackslash;
0357: }
0358:
0359: /**
0360: * Sets the escapeBackslash value.
0361: *
0362: * @param escapeBackslash The escapeBackslash to set.
0363: */
0364: public void setEscapeBackslash(boolean escapeBackslash) {
0365: this .escapeBackslash = escapeBackslash;
0366: }
0367:
0368: /**
0369: * Returns the escapeSingleQuote value.
0370: *
0371: * @return Returns the escapeSingleQuote.
0372: */
0373: public boolean isEscapeSingleQuote() {
0374: return escapeSingleQuote;
0375: }
0376:
0377: /**
0378: * Sets the escapeSingleQuote value.
0379: *
0380: * @param escapeSingleQuote The escapeSingleQuote to set.
0381: */
0382: public void setEscapeSingleQuote(boolean escapeSingleQuote) {
0383: this .escapeSingleQuote = escapeSingleQuote;
0384: }
0385:
0386: /**
0387: * Sets the escapeCharacter value
0388: *
0389: * @param escapeChar the escapeChar value to set
0390: */
0391: public void setEscapeChar(String escapeChar) {
0392: this .escapeChar = escapeChar;
0393: }
0394:
0395: /**
0396: * @return Returns the escapeChar.
0397: */
0398: public String getEscapeChar() {
0399: return escapeChar;
0400: }
0401:
0402: /**
0403: * Returns the connectionPooling value.
0404: *
0405: * @return Returns the connectionPooling.
0406: */
0407: public boolean isConnectionPooling() {
0408: return connectionPooling;
0409: }
0410:
0411: /**
0412: * Sets the connectionPooling value.
0413: *
0414: * @param connectionPooling The connectionPooling to set.
0415: */
0416: public void setConnectionPooling(boolean connectionPooling) {
0417: this .connectionPooling = connectionPooling;
0418: }
0419:
0420: /**
0421: * Returns the alwaysGetGeneratedKeys value.
0422: *
0423: * @return Returns the alwaysGetGeneratedKeys.
0424: */
0425: boolean isAlwaysGettingGeneratedKeys() {
0426: return alwaysGetGeneratedKeys;
0427: }
0428:
0429: //
0430: // java.sql.Connection implementation
0431: //
0432:
0433: /**
0434: * After this call, <code>getWarnings()</code> returns <code>null</code>
0435: * until a new call to getWarnings() on this connection.
0436: *
0437: * @exception SQLException if a database access error occurs
0438: */
0439: public void clearWarnings() throws SQLException {
0440: if (!persistentConnection || !retrieveSQLWarnings)
0441: // nop
0442: return;
0443: if (isClosed) {
0444: // on a closed connection, just reset the warnings
0445: // jdbc spec doesn't ask to throw SQLException
0446: firstWarning = null;
0447: return;
0448: }
0449:
0450: try {
0451: sendCommand(Commands.ConnectionClearWarnings);
0452: socketOutput.writeLong(persistentConnectionId);
0453: socketOutput.flush();
0454: if (sequoiaUrl.isDebugEnabled())
0455: System.out.println("Executing "
0456: + getCurrentMethodName());
0457: // forget the ack, we just need to know if an exception occured
0458: receiveBooleanOrException();
0459: } catch (SerializableException e) {
0460: throw new DriverSQLException(e);
0461: } catch (IOException e) {
0462: try { // Connection failed, try to reconnect and re-send command
0463: reconnect();
0464: clearWarnings();
0465: } catch (DriverSQLException e1) {
0466: throw new DriverSQLException(
0467: "Connection lost while clearWarnings() and automatic reconnect failed("
0468: + e1 + ")", e1);
0469: }
0470: }
0471:
0472: }
0473:
0474: /**
0475: * Releases the connection. In fact, the connection is marked to be released
0476: * but will be effectively closed by the <code>ConnectionClosingThread</code>
0477: * if the connection has not been reused before.
0478: *
0479: * @exception DriverSQLException if an error occurs
0480: */
0481: public void close() throws DriverSQLException {
0482: synchronized (this ) // Wait until other methods/Commands are done
0483: {
0484: // Spec says:
0485: // Calling the method close on a Connection object that is already closed
0486: // is a no-op.
0487: if (isClosed)
0488: return;
0489: isClosed = true;
0490: /*
0491: * All JDBC entry points (methods) of this Connection have to
0492: * throwSQLExceptionIfClosed(). Relaxed: at least every JDBC method _with
0493: * some side-effect_ has to throwSQLExceptionIfClosed(). So now we are
0494: * safe and can leave the lock, since they will fail anyway.
0495: */
0496: }
0497:
0498: if (connectionPooling && !persistentConnection) { // Try to pool the connection for later reuse
0499: // Persistent connections are not pooled to free resources right away
0500: if (sequoiaUrl.isDebugEnabled())
0501: System.out
0502: .println("Resetting connection and adding it to the pool");
0503: autoCommit = true;
0504: mustBeginTransaction = false;
0505: readOnly = false;
0506: writeExecutedInTransaction = false;
0507: isolationLevel = DEFAULT_TRANSACTION_ISOLATION_LEVEL;
0508: try {
0509: sendCommand(Commands.Reset);
0510: socketOutput.flush();
0511: if (socketInput != null) {
0512: // Wait for the controller to receive the reset, in order to have
0513: // an exception thrown if there are no more controllers
0514: receiveBooleanOrException();
0515: }
0516: } catch (Exception ignored) {
0517: // Try to reconnect to inform other controllers that we are reseting
0518: // this connection. Do it only once to avoid endless loops
0519: reconnect();
0520: try {
0521: sendCommand(Commands.Reset);
0522: socketOutput.flush();
0523: if (socketInput != null) {
0524: // Wait for the controller to receive the reset, in order to have
0525: // an exception thrown if there are no more controllers
0526: receiveBooleanOrException();
0527: }
0528: } catch (Exception e) {
0529: // Ok, no way to reset connection on the controller side, let's throw
0530: // the exception
0531: throw new DriverSQLException(
0532: "Error while closing the connection\n"
0533: + e.getLocalizedMessage(), e);
0534: }
0535: }
0536:
0537: // only one (Connection) accessing the pool at a time
0538: synchronized (driver.pendingConnectionClosing) {
0539: if (!driver.connectionClosingThreadisAlive) { // First connection to close, start a new closing thread
0540: if (sequoiaUrl.isDebugEnabled())
0541: System.out
0542: .println("Starting a new connection closing thread");
0543: ConnectionClosingThread t = new ConnectionClosingThread(
0544: driver);
0545: t.start();
0546: }
0547: // Add to the list
0548: driver.pendingConnectionClosing.add(this );
0549: }
0550: } else { // Close connection
0551: try {
0552: // driver = null; // probably useless since we use now
0553: // throwSQLExceptionIfClosed(), but
0554: // harmless anyway
0555: if (socketOutput != null) {
0556: if (sequoiaUrl.isDebugEnabled())
0557: System.out.println("Closing connection");
0558: sendCommand(Commands.Close);
0559: socketOutput.flush();
0560: if (socketInput != null) { // Wait for the controller to receive the connection and close the
0561: // stream. If we do not wait for the controller ack, the connection
0562: // is closed on the controller before the closing is handled which
0563: // results in an ugly warning message on the controller side. We are
0564: // not in a hurry when closing the connection so let do the things
0565: // nicely!
0566: receiveBooleanOrException();
0567: socketInput.close();
0568: }
0569: socketOutput.close();
0570: }
0571:
0572: } catch (Exception ignore) {
0573: // we attempt to reconnect to another controller:
0574: // since the connection was persistent, controllers
0575: // must be informed of the close operation so that
0576: // they can clean up their resources properly
0577: reconnect();
0578: close();
0579: }
0580: }
0581: }
0582:
0583: /**
0584: * Makes all changes made since the previous commit/rollback permanent and
0585: * releases any database locks currently held by the <code>Connection</code>.
0586: * This method should only be used when auto-commit has been disabled. (If
0587: * <code>autoCommit</code>== <code>true</code>, then we throw a
0588: * DriverSQLException).
0589: *
0590: * @exception DriverSQLException if a database access error occurs or the
0591: * connection is in autocommit mode
0592: * @see Connection#setAutoCommit(boolean)
0593: */
0594: public synchronized void commit() throws DriverSQLException {
0595: throwSQLExceptionIfClosed();
0596: if (autoCommit) {
0597: if (isCommitInAutoCommitAllowed()) {
0598: return; // nothing to do
0599: } else {
0600: throw new DriverSQLException(
0601: "Trying to commit a connection in autocommit mode");
0602: }
0603: }
0604: // Check if we are not committing an empty transaction (not started yet)
0605: if (mustBeginTransaction)
0606: return;
0607:
0608: doCommit();
0609: }
0610:
0611: private void doCommit() throws DriverSQLException {
0612: try {
0613: sendCommand(Commands.Commit);
0614: socketOutput.flush();
0615:
0616: // Commit acknowledgement
0617: long acknowledgedTransactionId = receiveLongOrException();
0618: // sanity check
0619: if (acknowledgedTransactionId != transactionId) {
0620: throw new DriverSQLException(
0621: "Protocol error during commit (acknowledge transaction ID = "
0622: + acknowledgedTransactionId
0623: + ", expected transaction ID = "
0624: + transactionId + ")");
0625: }
0626: mustBeginTransaction = true; // lazy begin
0627: writeExecutedInTransaction = false;
0628: } catch (SerializableException e) {
0629: throw new DriverSQLException(e);
0630: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the commit
0631: try {
0632: // this should resend transactionId (among others)
0633: reconnect();
0634:
0635: // get an ack
0636: long acknowledgedTransactionId = retrieveCommitResult();
0637: // sanity check
0638: if (acknowledgedTransactionId != transactionId) {
0639: throw new DriverSQLException(
0640: "Protocol error during commit (acknowledge transaction ID = "
0641: + acknowledgedTransactionId
0642: + ", expected transaction ID = "
0643: + transactionId + ")");
0644: }
0645: mustBeginTransaction = true;
0646: writeExecutedInTransaction = false;
0647:
0648: // The controller will automatically redo the commit if it was not done
0649: // earlier so we can safely return here, this is a success.
0650: return;
0651: } catch (DriverSQLException e1) {
0652: throw new DriverSQLException(
0653: "Connection lost during commit of transaction '"
0654: + transactionId
0655:
0656: + "' and automatic reconnect failed("
0657: + e1 + ")", e1);
0658: }
0659: }
0660: }
0661:
0662: /**
0663: * Determines whether or not commit should be allowed when autocommit is
0664: * enabled.
0665: *
0666: * @return true when commit is allowed with autocommit
0667: */
0668: private boolean isCommitInAutoCommitAllowed() {
0669: String allowed = (String) sequoiaUrl.getParameters().get(
0670: Driver.ALLOW_COMMIT_WITH_AUTOCOMMIT_PROPERTY);
0671:
0672: return allowed != null && allowed.toLowerCase().equals("true");
0673: }
0674:
0675: /**
0676: * SQL statements without parameters are normally executed using
0677: * <code>Statement</code> objects. If the same SQL statement is executed
0678: * many times, it is more efficient to use a <code>PreparedStatement</code>.
0679: * The <code>ResultSet</code> will be
0680: * <code>TYPE_FORWARD_ONLY</cde>/<code>CONCUR_READ_ONLY</code>.
0681: * *
0682: * @return a new <code>Statement</code> object
0683: * @exception DriverSQLException passed through from the constructor
0684: */
0685: public java.sql.Statement createStatement()
0686: throws DriverSQLException {
0687: throwSQLExceptionIfClosed();
0688: return new Statement(this , driver);
0689: }
0690:
0691: /**
0692: * SQL statements without parameters are normally executed using
0693: * <code>Statement</code> objects. If the same SQL statement is executed
0694: * many times, it is more efficient to use a <code>PreparedStatement</code>.
0695: *
0696: * @param resultSetType resultSetType to use
0697: * @param resultSetConcurrency resultSetConcurrency to use
0698: * @return a new <code>Statement</code> object
0699: * @exception SQLException passed through from the constructor
0700: */
0701: public java.sql.Statement createStatement(int resultSetType,
0702: int resultSetConcurrency) throws SQLException {
0703: throwSQLExceptionIfClosed();
0704: Statement s = new Statement(this , driver);
0705: s.setResultSetType(resultSetType);
0706: s.setResultSetConcurrency(resultSetConcurrency);
0707: return s;
0708: }
0709:
0710: /**
0711: * Creates a <code>Statement</code> object that will generate
0712: * <code>ResultSet</code> objects with the given type, concurrency, and
0713: * holdability.
0714: * <p>
0715: * This method is the same as the <code>createStatement</code> method above,
0716: * but it allows the default result set type, concurrency, and holdability to
0717: * be overridden.
0718: *
0719: * @param resultSetType one of the following <code>ResultSet</code>
0720: * constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
0721: * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
0722: * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
0723: * @param resultSetConcurrency one of the following <code>ResultSet</code>
0724: * constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
0725: * <code>ResultSet.CONCUR_UPDATABLE</code>
0726: * @param resultSetHoldability one of the following <code>ResultSet</code>
0727: * constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
0728: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
0729: * @return a new <code>Statement</code> object that will generate
0730: * <code>ResultSet</code> objects with the given type, concurrency,
0731: * and holdability
0732: * @exception SQLException if a database access error occurs or the given
0733: * parameters are not <code>ResultSet</code> constants
0734: * indicating type, concurrency, and holdability
0735: * @see ResultSet
0736: * @since JDK 1.4
0737: */
0738: public java.sql.Statement createStatement(int resultSetType,
0739: int resultSetConcurrency, int resultSetHoldability)
0740: throws SQLException {
0741: throw new NotImplementedException(getCurrentMethodName());
0742: }
0743:
0744: /**
0745: * Gets the current auto-commit state.
0746: *
0747: * @return current state of the auto-commit mode
0748: * @exception DriverSQLException is connection is closed
0749: * @see Connection#setAutoCommit
0750: */
0751: public boolean getAutoCommit() throws DriverSQLException {
0752: throwSQLExceptionIfClosed();
0753: return this .autoCommit;
0754: }
0755:
0756: /**
0757: * A connection's database is able to provide information describing its
0758: * tables, its supported SQL grammar, its stored procedures, the capabilities
0759: * of this connection, etc. This information is made available through a
0760: * DatabaseMetaData object.
0761: *
0762: * @return a <code>DatabaseMetaData</code> object for this connection
0763: * @exception DriverSQLException if connection is closed
0764: */
0765: public java.sql.DatabaseMetaData getMetaData()
0766: throws DriverSQLException {
0767: throwSQLExceptionIfClosed();
0768: if (metaData == null) {
0769: metaData = new DatabaseMetaData(this );
0770: }
0771: return metaData;
0772: }
0773:
0774: /**
0775: * Return current catalog name.
0776: *
0777: * @return name of the current <code>VirtualDatabase</code>
0778: * @throws DriverSQLException if any error occurs
0779: * @see Connection#getCatalog()
0780: */
0781: public synchronized String getCatalog() throws DriverSQLException {
0782: throwSQLExceptionIfClosed();
0783: try {
0784: sendCommand(Commands.ConnectionGetCatalog);
0785: socketOutput.flush();
0786:
0787: if (sequoiaUrl.isDebugEnabled())
0788: System.out.println("Executing "
0789: + getCurrentMethodName());
0790:
0791: return receiveStringOrException();
0792: } catch (SerializableException e) {
0793: throw new DriverSQLException(e);
0794: } catch (IOException e) {
0795: try {
0796: reconnect();
0797: return getCatalog();
0798: } catch (DriverSQLException e1) {
0799: throw new DriverSQLException(
0800: "Connection lost while executing "
0801: + getCurrentMethodName()
0802: + " and automatic reconnect failed ",
0803: e1);
0804: }
0805: }
0806: }
0807:
0808: /**
0809: * getCatalogs definition.
0810: *
0811: * @return instace of <code>ResultSet<code>
0812: * @throws DriverSQLException if fails (include ANY exception that can be thrown in the code)
0813: */
0814: public synchronized ResultSet getCatalogs()
0815: throws DriverSQLException {
0816: throwSQLExceptionIfClosed();
0817: try {
0818: sendCommand(Commands.ConnectionGetCatalogs);
0819: socketOutput.flush();
0820:
0821: if (sequoiaUrl.isDebugEnabled())
0822: System.out.println(getCurrentMethodName());
0823:
0824: return receiveResultSet(getCurrentMethodName());
0825: } catch (SerializableException e) {
0826: throw new DriverSQLException(e);
0827: } catch (IOException e) {
0828: try {
0829: reconnect();
0830: return getCatalogs();
0831: } catch (DriverSQLException e1) {
0832: throw new DriverSQLException(
0833: "Connection lost and automatic reconnect failed while executing "
0834: + getCurrentMethodName(), e1);
0835: }
0836: }
0837: }
0838:
0839: /**
0840: * Retrieves the current holdability of <code>ResultSet</code> objects
0841: * created using this <code>Connection</code> object. If the value was not
0842: * set using setHoldability(), the default value of the database is returned.
0843: *
0844: * @return the holdability, one of
0845: * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
0846: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
0847: * @throws SQLException if a database access occurs
0848: * @see #setHoldability
0849: * @see ResultSet
0850: * @since JDK 1.4
0851: */
0852: public int getHoldability() throws SQLException {
0853: if (holdability == HOLDABILITY_NOT_SET)
0854: holdability = getMetaData().getResultSetHoldability();
0855: return holdability;
0856: }
0857:
0858: /**
0859: * Gets this Connection's current transaction isolation mode. If the
0860: * transaction isolation has not been set using setTransactionIsolation, this
0861: * method will return by default
0862: * java.sql.Connection.TRANSACTION_READ_UNCOMMITTED whatever transaction
0863: * isolation is really used by the cluster nodes. If you want to enfore
0864: * TRANSACTION_READ_UNCOMMITTED, you have to explicitely call
0865: * setTransactionIsolation(java.sql.Connection.TRANSACTION_READ_UNCOMMITTED)
0866: *
0867: * @return the current <code>TRANSACTION_*</code> mode value
0868: * @exception DriverSQLException if a database access error occurs
0869: * @see #setTransactionIsolation(int)
0870: */
0871: public int getTransactionIsolation() throws DriverSQLException {
0872: throwSQLExceptionIfClosed();
0873: // Warning, here we assume that if no transaction isolation is set the
0874: // database will provide READ_UNCOMMITED.
0875: if (isolationLevel == DEFAULT_TRANSACTION_ISOLATION_LEVEL)
0876: return driver.getDefaultTransactionIsolationLevel();
0877: return isolationLevel;
0878: }
0879:
0880: /**
0881: * Sequoia does NOT support type map.
0882: *
0883: * @return an exception
0884: * @exception SQLException not supported
0885: */
0886: public java.util.Map getTypeMap() throws SQLException {
0887: throw new NotImplementedException(getCurrentMethodName());
0888: }
0889:
0890: /**
0891: * Returns the first warning reported by calls on this connection. Subsequent
0892: * warnings will be chained to this SQLWarning<br>
0893: * <B>Note: </B> If the 'persistent connections' option is set to false, this
0894: * function will always return null.
0895: *
0896: * @return the first SQLWarning or null
0897: * @exception DriverSQLException if a database access error occurs or this
0898: * method is called on a closed connection
0899: */
0900: public SQLWarning getWarnings() throws DriverSQLException {
0901: throwSQLExceptionIfClosed();
0902: if (!persistentConnection || !retrieveSQLWarnings)
0903: return firstWarning;
0904:
0905: try {
0906: sendCommand(Commands.ConnectionGetWarnings);
0907: socketOutput.writeLong(persistentConnectionId);
0908: socketOutput.flush();
0909: if (sequoiaUrl.isDebugEnabled())
0910: System.out.println("Executing "
0911: + getCurrentMethodName());
0912: return receiveSQLWarnings();
0913: } catch (SerializableException e) {
0914: throw new DriverSQLException(e);
0915: } catch (IOException e) {
0916: try { // Connection failed, try to reconnect and re-send command
0917: reconnect();
0918: return getWarnings();
0919: } catch (DriverSQLException e1) {
0920: throw new DriverSQLException(
0921: "Connection lost while getting SQL Warnings and automatic reconnect failed("
0922: + e1 + ")", e1);
0923: }
0924: }
0925: }
0926:
0927: /**
0928: * Returns <code>true</code> if the connection has been closed by the user
0929: * (but Sequoia may leave it open underneath, unknown to the user).
0930: *
0931: * @return <code>true</code> if connection has never been opened or
0932: * <code>close()</code> has been called
0933: */
0934: public boolean isClosed() {
0935: return isClosed;
0936: }
0937:
0938: /**
0939: * Tests to see if the connection is in read only Mode. Note that we cannot
0940: * really put the database in read only mode, but we pretend we can by
0941: * returning the value of the <code>readOnly</code> flag.
0942: *
0943: * @return <code>true</code> if the connection is read only
0944: */
0945: public boolean isReadOnly() {
0946: return readOnly;
0947: }
0948:
0949: /**
0950: * As we can't know for sure which database will execute this request (now or
0951: * later), we can't translate it in the native query language of the
0952: * underlying DBMS. Therefore the query is returned unchanged.
0953: *
0954: * @param query the query to change
0955: * @return the original query
0956: */
0957: public String nativeSQL(String query) {
0958: return query;
0959: }
0960:
0961: /**
0962: * A SQL statement with or without <code>IN</code> parameters can be
0963: * pre-compiled and stored in a PreparedStatement object. This object can then
0964: * be used to efficiently execute this statement multiple times.
0965: *
0966: * @param sql a SQL statement that may contain one or more '?' IN * parameter
0967: * placeholders
0968: * @return a new <code>PreparedStatement</code> object containing the
0969: * pre-compiled statement.
0970: * @exception SQLException if a database access error occurs.
0971: */
0972: public java.sql.PreparedStatement prepareStatement(String sql)
0973: throws SQLException {
0974: throwSQLExceptionIfClosed();
0975: return new PreparedStatement(this , sql, driver);
0976: }
0977:
0978: /**
0979: * Creates a default <code>PreparedStatement</code> object that has the
0980: * capability to retrieve auto-generated keys. The given constant tells the
0981: * driver whether it should make auto-generated keys available for retrieval.
0982: * This parameter is ignored if the SQL statement is not an
0983: * <code>INSERT</code> statement.
0984: * <p>
0985: * <b>Note: </b> This method is optimized for handling parametric SQL
0986: * statements that benefit from precompilation. If the driver supports
0987: * precompilation, the method <code>prepareStatement</code> will send the
0988: * statement to the database for precompilation. Some drivers may not support
0989: * precompilation. In this case, the statement may not be sent to the database
0990: * until the <code>PreparedStatement</code> object is executed. This has no
0991: * direct effect on users; however, it does affect which methods throw certain
0992: * SQLExceptions.
0993: * <p>
0994: * Result sets created using the returned <code>PreparedStatement</code>
0995: * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
0996: * concurrency level of <code>CONCUR_READ_ONLY</code>.
0997: *
0998: * @param sql an SQL statement that may contain one or more '?' IN parameter
0999: * placeholders
1000: * @param autoGeneratedKeys a flag indicating whether auto-generated keys
1001: * should be returned; one of
1002: * <code>Statement.RETURN_GENERATED_KEYS</code> or
1003: * <code>Statement.NO_GENERATED_KEYS</code>
1004: * @return a new <code>PreparedStatement</code> object, containing the
1005: * pre-compiled SQL statement, that will have the capability of
1006: * returning auto-generated keys
1007: * @exception SQLException if a database access error occurs or the given
1008: * parameter is not a <code>Statement</code> constant
1009: * indicating whether auto-generated keys should be returned
1010: * @since JDK 1.4
1011: */
1012: public java.sql.PreparedStatement prepareStatement(String sql,
1013: int autoGeneratedKeys) throws SQLException {
1014: throwSQLExceptionIfClosed();
1015: PreparedStatement ps = new PreparedStatement(this , sql, driver,
1016: autoGeneratedKeys);
1017: return ps;
1018: }
1019:
1020: /**
1021: * Creates a default <code>PreparedStatement</code> object capable of
1022: * returning the auto-generated keys designated by the given array. This array
1023: * contains the indexes of the columns in the target table that contain the
1024: * auto-generated keys that should be made available. This array is ignored if
1025: * the SQL statement is not an <code>INSERT</code> statement.
1026: * <p>
1027: * An SQL statement with or without IN parameters can be pre-compiled and
1028: * stored in a <code>PreparedStatement</code> object. This object can then
1029: * be used to efficiently execute this statement multiple times.
1030: * <p>
1031: * <b>Note: </b> This method is optimized for handling parametric SQL
1032: * statements that benefit from precompilation. If the driver supports
1033: * precompilation, the method <code>prepareStatement</code> will send the
1034: * statement to the database for precompilation. Some drivers may not support
1035: * precompilation. In this case, the statement may not be sent to the database
1036: * until the <code>PreparedStatement</code> object is executed. This has no
1037: * direct effect on users; however, it does affect which methods throw certain
1038: * SQLExceptions.
1039: * <p>
1040: * Result sets created using the returned <code>PreparedStatement</code>
1041: * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
1042: * concurrency level of <code>CONCUR_READ_ONLY</code>.
1043: *
1044: * @param sql an SQL statement that may contain one or more '?' IN parameter
1045: * placeholders
1046: * @param columnIndexes an array of column indexes indicating the columns that
1047: * should be returned from the inserted row or rows
1048: * @return a new <code>PreparedStatement</code> object, containing the
1049: * pre-compiled statement, that is capable of returning the
1050: * auto-generated keys designated by the given array of column indexes
1051: * @exception SQLException if a database access error occurs
1052: * @since JDK 1.4
1053: */
1054: public java.sql.PreparedStatement prepareStatement(String sql,
1055: int[] columnIndexes) throws SQLException {
1056: throwSQLExceptionIfClosed();
1057: PreparedStatement ps = new PreparedStatement(this , sql, driver,
1058: PreparedStatement.RETURN_GENERATED_KEYS);
1059: return ps;
1060: }
1061:
1062: /**
1063: * Creates a default <code>PreparedStatement</code> object capable of
1064: * returning the auto-generated keys designated by the given array. This array
1065: * contains the names of the columns in the target table that contain the
1066: * auto-generated keys that should be returned. This array is ignored if the
1067: * SQL statement is not an <code>INSERT</code> statement.
1068: * <p>
1069: * An SQL statement with or without IN parameters can be pre-compiled and
1070: * stored in a <code>PreparedStatement</code> object. This object can then
1071: * be used to efficiently execute this statement multiple times.
1072: * <p>
1073: * <b>Note: </b> This method is optimized for handling parametric SQL
1074: * statements that benefit from precompilation. If the driver supports
1075: * precompilation, the method <code>prepareStatement</code> will send the
1076: * statement to the database for precompilation. Some drivers may not support
1077: * precompilation. In this case, the statement may not be sent to the database
1078: * until the <code>PreparedStatement</code> object is executed. This has no
1079: * direct effect on users; however, it does affect which methods throw certain
1080: * <code>SQLExceptions</code>.
1081: * <p>
1082: * Result sets created using the returned <code>PreparedStatement</code>
1083: * object will by default be type <code>TYPE_FORWARD_ONLY</code> and have a
1084: * concurrency level of <code>CONCUR_READ_ONLY</code>.
1085: *
1086: * @param sql an SQL statement that may contain one or more '?' IN parameter
1087: * placeholders
1088: * @param columnNames an array of column names indicating the columns that
1089: * should be returned from the inserted row or rows
1090: * @return a new <code>PreparedStatement</code> object, containing the
1091: * pre-compiled statement, that is capable of returning the
1092: * auto-generated keys designated by the given array of column names
1093: * @exception SQLException if a database access error occurs
1094: * @since JDK 1.4
1095: */
1096: public java.sql.PreparedStatement prepareStatement(String sql,
1097: String[] columnNames) throws SQLException {
1098: throwSQLExceptionIfClosed();
1099: PreparedStatement ps = new PreparedStatement(this , sql, driver,
1100: PreparedStatement.RETURN_GENERATED_KEYS);
1101: return ps;
1102: }
1103:
1104: /**
1105: * A SQL statement with or without IN parameters can be pre-compiled and
1106: * stored in a <code>PreparedStatement</code> object. This object can then
1107: * be used to efficiently execute this statement multiple times.
1108: *
1109: * @param sql a SQL statement that may contain one or more '?' IN
1110: * @param resultSetType <code>ResultSetType</code> to use
1111: * @param resultSetConcurrency <code>ResultSetConcurrency</code> to use
1112: * @return a new <code>PreparedStatement</code> object
1113: * @exception SQLException passed through from the constructor
1114: */
1115: public java.sql.PreparedStatement prepareStatement(String sql,
1116: int resultSetType, int resultSetConcurrency)
1117: throws SQLException {
1118: throwSQLExceptionIfClosed();
1119: PreparedStatement s = new PreparedStatement(this , sql, driver);
1120: s.setResultSetType(resultSetType);
1121: s.setResultSetConcurrency(resultSetConcurrency);
1122: return s;
1123: }
1124:
1125: /**
1126: * Creates a <code>PreparedStatement</code> object that will generate
1127: * <code>ResultSet</code> objects with the given type, concurrency, and
1128: * holdability.
1129: * <p>
1130: * This method is the same as the <code>prepareStatement</code> method
1131: * above, but it allows the default result set type, concurrency, and
1132: * holdability to be overridden.
1133: *
1134: * @param sql a <code>String</code> object that is the SQL statement to be
1135: * sent to the database; may contain one or more ? IN parameters
1136: * @param resultSetType one of the following <code>ResultSet</code>
1137: * constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
1138: * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
1139: * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
1140: * @param resultSetConcurrency one of the following <code>ResultSet</code>
1141: * constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
1142: * <code>ResultSet.CONCUR_UPDATABLE</code>
1143: * @param resultSetHoldability one of the following <code>ResultSet</code>
1144: * constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
1145: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
1146: * @return a new <code>PreparedStatement</code> object, containing the
1147: * pre-compiled SQL statement, that will generate
1148: * <code>ResultSet</code> objects with the given type, concurrency,
1149: * and holdability
1150: * @exception SQLException if a database access error occurs or the given
1151: * parameters are not <code>ResultSet</code> constants
1152: * indicating type, concurrency, and holdability
1153: * @see ResultSet
1154: * @since JDK 1.4
1155: */
1156: public java.sql.PreparedStatement prepareStatement(String sql,
1157: int resultSetType, int resultSetConcurrency,
1158: int resultSetHoldability) throws SQLException {
1159: throwSQLExceptionIfClosed();
1160: PreparedStatement ps = new PreparedStatement(this , sql, driver);
1161: ps.setResultSetType(resultSetType);
1162: ps.setResultSetConcurrency(resultSetConcurrency);
1163: setHoldability(resultSetHoldability);
1164: return ps;
1165: }
1166:
1167: /**
1168: * Creates a CallableStatement that contains sql and produces a ResultSet that
1169: * is TYPE_SCROLL_INSENSITIVE and CONCUR_READ_ONLY.
1170: *
1171: * @param sql SQL request
1172: * @return a CallableStatement
1173: * @exception SQLException not supported
1174: */
1175: public java.sql.CallableStatement prepareCall(String sql)
1176: throws SQLException {
1177: throwSQLExceptionIfClosed();
1178: return prepareCall(sql,
1179: java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,
1180: java.sql.ResultSet.CONCUR_READ_ONLY);
1181: }
1182:
1183: /**
1184: * @see java.sql.Connection#prepareCall(java.lang.String, int, int)
1185: */
1186: public java.sql.CallableStatement prepareCall(String sql,
1187: int resultSetType, int resultSetConcurrency)
1188: throws SQLException {
1189: throwSQLExceptionIfClosed();
1190: CallableStatement c = new CallableStatement(this , sql, driver);
1191: c.setResultSetType(resultSetType);
1192: c.setResultSetConcurrency(resultSetConcurrency);
1193: return c;
1194: }
1195:
1196: /**
1197: * Creates a <code>CallableStatement</code> object that will generate
1198: * <code>ResultSet</code> objects with the given type and concurrency. This
1199: * method is the same as the <code>prepareCall</code> method above, but it
1200: * allows the default result set type, result set concurrency type and
1201: * holdability to be overridden.
1202: *
1203: * @param sql a <code>String</code> object that is the SQL statement to be
1204: * sent to the database; may contain on or more ? parameters
1205: * @param resultSetType one of the following <code>ResultSet</code>
1206: * constants: <code>ResultSet.TYPE_FORWARD_ONLY</code>,
1207: * <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
1208: * <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
1209: * @param resultSetConcurrency one of the following <code>ResultSet</code>
1210: * constants: <code>ResultSet.CONCUR_READ_ONLY</code> or
1211: * <code>ResultSet.CONCUR_UPDATABLE</code>
1212: * @param resultSetHoldability one of the following <code>ResultSet</code>
1213: * constants: <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
1214: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
1215: * @return a new <code>CallableStatement</code> object, containing the
1216: * pre-compiled SQL statement, that will generate
1217: * <code>ResultSet</code> objects with the given type, concurrency,
1218: * and holdability
1219: * @exception SQLException if a database access error occurs or the given
1220: * parameters are not <code>ResultSet</code> constants
1221: * indicating type, concurrency, and holdability
1222: * @see ResultSet
1223: * @since JDK 1.4
1224: */
1225: public java.sql.CallableStatement prepareCall(String sql,
1226: int resultSetType, int resultSetConcurrency,
1227: int resultSetHoldability) throws SQLException {
1228: throwSQLExceptionIfClosed();
1229: CallableStatement cs = new CallableStatement(this , sql, driver);
1230: cs.setResultSetType(resultSetType);
1231: cs.setResultSetConcurrency(resultSetConcurrency);
1232: setHoldability(resultSetHoldability);
1233: return cs;
1234: }
1235:
1236: /**
1237: * Removes the given <code>Savepoint</code> object from the current
1238: * transaction. Any reference to the savepoint after it have been removed will
1239: * cause an <code>SQLException</code> to be thrown.
1240: *
1241: * @param savepoint the <code>Savepoint</code> object to be removed
1242: * @exception DriverSQLException if a database access error occurs or the
1243: * given <code>Savepoint</code> object is not a valid savepoint
1244: * in the current transaction
1245: * @since JDK 1.4
1246: */
1247: public void releaseSavepoint(Savepoint savepoint)
1248: throws DriverSQLException {
1249: throwSQLExceptionIfClosed();
1250: if (savepoint == null)
1251: throw new DriverSQLException("Savepoint cannot be null");
1252:
1253: if (autoCommit)
1254: throw new DriverSQLException(
1255: "Trying to release a savepoint in autocommit mode");
1256:
1257: if (driver == null)
1258: throw new DriverSQLException(
1259: "No driver to release a savepoint");
1260:
1261: try {
1262: sendCommand(Commands.ReleaseSavepoint);
1263: savepointOnStream(savepoint);
1264: socketOutput.flush();
1265:
1266: this .receiveBooleanOrException();
1267: } catch (SerializableException e) {
1268: throw new DriverSQLException(e);
1269: } catch (IOException e) {
1270: try {
1271: // Connection failed, try to reconnect and release the savepoint again
1272: reconnect();
1273: boolean done = retrieveReleaseSavepoint(savepoint);
1274: if (!done) {
1275: releaseSavepoint(savepoint);
1276: }
1277: } catch (DriverSQLException e1) {
1278: throw new DriverSQLException(
1279: "Connection lost while releasing savepoint '"
1280: + savepoint
1281: + "' and automatic reconnect failed("
1282: + e1 + ")", e1);
1283: }
1284: }
1285: }
1286:
1287: /**
1288: * Drops all changes made since the previous commit/rollback and releases any
1289: * database locks currently held by this connection. If the connection was in
1290: * autocommit mode, we throw a DriverSQLException.
1291: *
1292: * @exception DriverSQLException if a database access error occurs or the
1293: * connection is in autocommit mode
1294: * @see Connection#commit()
1295: */
1296: public synchronized void rollback() throws DriverSQLException {
1297: throwSQLExceptionIfClosed();
1298: if (autoCommit)
1299: throw new DriverSQLException(
1300: "Trying to rollback a connection in autocommit mode");
1301:
1302: // Check if we are not rollbacking an empty transaction (not started yet)
1303: if (mustBeginTransaction)
1304: return;
1305:
1306: try {
1307: sendCommand(Commands.Rollback);
1308: socketOutput.flush();
1309:
1310: // rollback acknowledgement
1311: long acknowledgedTransactionId = receiveLongOrException();
1312: if (acknowledgedTransactionId != transactionId) {
1313: throw new DriverSQLException(
1314: "Protocol error during rollback (acknowledge transaction ID = "
1315: + acknowledgedTransactionId
1316: + ", expected transaction ID = "
1317: + transactionId + ")");
1318: }
1319: mustBeginTransaction = true;
1320: writeExecutedInTransaction = false;
1321: } catch (SerializableException e) {
1322: throw new DriverSQLException(e);
1323: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the rollback
1324: try {
1325: reconnect();
1326:
1327: long acknowledgedTransactionId = retrieveRollbackResult();
1328: if (acknowledgedTransactionId != transactionId) {
1329: throw new DriverSQLException(
1330: "Protocol error during rollback failover (acknowledge transaction ID = "
1331: + acknowledgedTransactionId
1332: + ", expected transaction ID = "
1333: + transactionId + ")");
1334: }
1335: mustBeginTransaction = true;
1336:
1337: // The controller will automatically redo the rollback if it was not
1338: // done earlier so we can safely return here, this is a success.
1339: return;
1340: } catch (DriverSQLException e1) {
1341: throw new DriverSQLException(
1342: "Connection lost during rollback of transaction '"
1343: + transactionId
1344:
1345: + "' and automatic reconnect failed("
1346: + e1 + ")", e1);
1347: }
1348: }
1349: }
1350:
1351: /**
1352: * Undoes all changes made after the given <code>Savepoint</code> object was
1353: * set.
1354: * <p>
1355: * This method should be used only when auto-commit has been disabled.
1356: *
1357: * @param savepoint the <code>Savepoint</code> object to roll back to
1358: * @exception DriverSQLException if a database access error occurs, the
1359: * <code>Savepoint</code> object is no longer valid, or this
1360: * <code>Connection</code> object is currently in auto-commit
1361: * mode
1362: * @see Savepoint
1363: * @see #rollback()
1364: * @since JDK 1.4
1365: */
1366: public void rollback(Savepoint savepoint) throws DriverSQLException {
1367: throwSQLExceptionIfClosed();
1368: if (savepoint == null)
1369: throw new DriverSQLException("Savepoint cannot be null");
1370:
1371: if (autoCommit)
1372: throw new DriverSQLException(
1373: "Trying to rollback to a savepoint in autocommit mode");
1374:
1375: if (driver == null)
1376: throw new DriverSQLException(
1377: "No driver to rollback to savepoint");
1378:
1379: try {
1380: sendCommand(Commands.RollbackToSavepoint);
1381: savepointOnStream(savepoint);
1382: socketOutput.flush();
1383:
1384: this .receiveBooleanOrException();
1385: } catch (SerializableException e) {
1386: throw new DriverSQLException(e);
1387: } catch (IOException e) {
1388: try {
1389: // Connection failed, try to reconnect and rollback again if checkpoint
1390: // still exists
1391: reconnect();
1392: boolean isCheckpointRemoved = retrieveReleaseSavepoint(savepoint);
1393: if (!isCheckpointRemoved)
1394: rollback(savepoint);
1395: } catch (DriverSQLException e1) {
1396: throw new DriverSQLException(
1397: "Connection lost while rollbacking to savepoint '"
1398: + savepoint
1399: + "' and automatic reconnect failed("
1400: + e1 + ")", e1);
1401: }
1402: }
1403: }
1404:
1405: private void begin() throws DriverSQLException, ProtocolException {
1406: try {
1407: sendCommand(Commands.Begin);
1408: socketOutput.flush();
1409:
1410: transactionId = receiveLongOrException();
1411:
1412: if (sequoiaUrl.isDebugEnabled())
1413: System.out.println("Transaction " + transactionId
1414: + " has been started");
1415: } catch (SerializableException e) {
1416: throw new DriverSQLException(e);
1417: } catch (IOException e) {
1418: // Connection failed, try to reconnect and re-exec the query
1419: if (sequoiaUrl.isInfoEnabled())
1420: System.out
1421: .println("I/O Error while trying to disable autocommit\n"
1422: + e.getLocalizedMessage());
1423: reconnect();
1424: begin();
1425: }
1426: }
1427:
1428: /**
1429: * To keep track of active transactions, sequoia uses the old BEGIN modeless
1430: * model. Backend's connections on the controller are always maintained/reset
1431: * in autocommit mode = true, except of course when in the middle of an active
1432: * transaction. So part of our job here is to translate the new ODBC/JDBC
1433: * autoCommit mode concept to the old BEGIN model.
1434: *
1435: * @see Commands#SetAutoCommit
1436: * @see java.sql.Connection#setAutoCommit(boolean)
1437: * @param autoCommitArg <code>true</code> enables auto-commit;
1438: * <code>false</code> disables it
1439: * @exception DriverSQLException if a database access error occurs
1440: * @throws DriverIOException if an IO error occured with the controller
1441: */
1442: public synchronized void setAutoCommit(boolean autoCommitArg)
1443: throws DriverSQLException, DriverIOException {
1444: throwSQLExceptionIfClosed();
1445:
1446: // Do nothing if already in the right state
1447: if (this .autoCommit == autoCommitArg)
1448: return;
1449:
1450: // true -> false (send nothing, lazy begin)
1451: if (this .autoCommit) {
1452: this .autoCommit = false;
1453: this .mustBeginTransaction = true;
1454: return;
1455: }
1456:
1457: // false -> true
1458: if (mustBeginTransaction) { // Transaction has NOT yet begun
1459: // Just cancel the (lazy, not yet done) begin
1460: mustBeginTransaction = false;
1461: this .autoCommit = true;
1462: return;
1463: } else {
1464: if (sequoiaUrl.isDebugEnabled())
1465: System.out
1466: .println("Setting connection in autocommit mode");
1467: doCommit();
1468: this .autoCommit = true;
1469: }
1470: }
1471:
1472: /**
1473: * Change the current catalog
1474: *
1475: * @param catalog a <code>String</code> value
1476: * @exception SQLException if fails or if catalog name is invalid
1477: */
1478: public synchronized void setCatalog(String catalog)
1479: throws SQLException {
1480: throwSQLExceptionIfClosed();
1481: if (catalog == null)
1482: throw new DriverSQLException("Invalid Catalog");
1483: sequoiaUrl.setUrl(driver.changeDatabaseName(
1484: sequoiaUrl.getUrl(), catalog));
1485:
1486: try {
1487: sendCommand(Commands.ConnectionSetCatalog);
1488: socketOutput.writeLongUTF(catalog);
1489: socketOutput.flush();
1490:
1491: if (sequoiaUrl.isDebugEnabled())
1492: System.out.println("Executing "
1493: + getCurrentMethodName() + " with catalog '"
1494: + catalog + "'");
1495:
1496: if (!receiveBooleanOrException())
1497: throw new DriverSQLException("Invalid Catalog");
1498:
1499: } catch (SerializableException e) {
1500: throw new DriverSQLException(e);
1501: } catch (IOException e) {
1502: try {
1503: // Connection failed, try to reconnect and re-set the catalog
1504: reconnect();
1505: setCatalog(catalog);
1506: } catch (DriverSQLException e1) {
1507: throw new DriverSQLException(
1508: "Connection lost while setting the catalog '"
1509: + catalog
1510: + "' and automatic reconnect failed("
1511: + e1 + ")", e1);
1512: }
1513: }
1514: }
1515:
1516: /**
1517: * Changes the holdability of <code>ResultSet</code> objects created using
1518: * this <code>Connection</code> object to the given holdability.
1519: *
1520: * @param holdability a <code>ResultSet</code> holdability constant; one of
1521: * <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
1522: * <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
1523: * @throws SQLException if a database access occurs, the given parameter is
1524: * not a <code>ResultSet</code> constant indicating holdability,
1525: * or the given holdability is not supported
1526: * @see #getHoldability
1527: * @see ResultSet
1528: * @since JDK 1.4
1529: */
1530: public void setHoldability(int holdability) throws SQLException {
1531: if ((holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT)
1532: && (holdability != ResultSet.CLOSE_CURSORS_AT_COMMIT))
1533: throw new SQLException("Invalid holdaibility value "
1534: + holdability);
1535: this .holdability = holdability;
1536: }
1537:
1538: /**
1539: * You can put a connection in read-only mode as a hint to enable database
1540: * optimizations
1541: *
1542: * @param readOnly <code>true</code> enables read-only mode;
1543: * <code>false</code> disables it
1544: * @exception DriverSQLException if a database access error occurs
1545: */
1546: public void setReadOnly(boolean readOnly) throws DriverSQLException {
1547: throwSQLExceptionIfClosed();
1548:
1549: try {
1550: sendCommand(Commands.SetReadOnly);
1551: socketOutput.writeBoolean(readOnly);
1552: socketOutput.flush();
1553:
1554: if (sequoiaUrl.isDebugEnabled())
1555: System.out.println("Setting connection to read-only="
1556: + readOnly);
1557:
1558: receiveBooleanOrException();
1559: // Success
1560: this .readOnly = readOnly;
1561: return;
1562:
1563: } catch (SerializableException e) {
1564: throw new DriverSQLException(e);
1565: } catch (IOException ioe) {
1566: try {
1567: // Connection failed, try to reconnect and re-set the transaction
1568: // isolation level
1569: reconnect();
1570: setReadOnly(readOnly);
1571: } catch (DriverSQLException e1) {
1572: throw new DriverSQLException(
1573: "Connection lost while setting the connection to read-only="
1574: + readOnly
1575: + " and automatic reconnect failed("
1576: + e1 + ")", e1);
1577: }
1578: }
1579: }
1580:
1581: private static final int SAVEPOINT_NOT_SET = -1;
1582:
1583: /**
1584: * Creates an unnamed savepoint in the current transaction and returns the new
1585: * <code>Savepoint</code> object that represents it.
1586: *
1587: * @return the new <code>Savepoint</code> object
1588: * @exception DriverSQLException if a database access error occurs or this
1589: * <code>Connection</code> object is currently in auto-commit
1590: * mode
1591: * @see Savepoint
1592: * @since JDK 1.4
1593: */
1594: public Savepoint setSavepoint() throws DriverSQLException {
1595: throwSQLExceptionIfClosed();
1596: beginTransactionIfNeeded();
1597: if (autoCommit)
1598: throw new DriverSQLException(
1599: "Trying to set a savepoint in autocommit mode");
1600:
1601: if (driver == null)
1602: throw new DriverSQLException("No driver to set a savepoint");
1603:
1604: int savepointId = SAVEPOINT_NOT_SET;
1605: try {
1606: sendCommand(Commands.SetUnnamedSavepoint);
1607: socketOutput.flush();
1608:
1609: savepointId = receiveIntOrException();
1610: return new org.continuent.sequoia.driver.Savepoint(
1611: savepointId);
1612: } catch (SerializableException e) {
1613: throw new DriverSQLException(e);
1614: } catch (IOException ioe) {
1615: try {
1616: // Connection failed, try to reconnect and re-set the savepoint if it
1617: // was not set before the failure
1618: reconnect();
1619: org.continuent.sequoia.driver.Savepoint savepoint = new org.continuent.sequoia.driver.Savepoint(
1620: savepointId);
1621: boolean checkpointDoesNotExit = (savepointId == SAVEPOINT_NOT_SET)
1622: || retrieveReleaseSavepoint(savepoint);
1623: if (checkpointDoesNotExit)
1624: return this .setSavepoint(); // retry, did not work the first time
1625: else
1626: return savepoint; // ok, already set
1627: } catch (DriverSQLException e1) {
1628: throw new DriverSQLException(
1629: "Connection lost while setting an unnamed savepoint and automatic reconnect failed("
1630: + e1 + ")", e1);
1631: }
1632: }
1633: }
1634:
1635: /**
1636: * Creates a savepoint with the given name in the current transaction and
1637: * returns the new <code>Savepoint</code> object that represents it.
1638: *
1639: * @param name a <code>String</code> containing the name of the savepoint
1640: * @return the new <code>Savepoint</code> object
1641: * @exception DriverSQLException if a database access error occurs or this
1642: * <code>Connection</code> object is currently in auto-commit
1643: * mode
1644: * @see Savepoint
1645: * @since JDK 1.4
1646: */
1647: public Savepoint setSavepoint(String name)
1648: throws DriverSQLException {
1649: throwSQLExceptionIfClosed();
1650: beginTransactionIfNeeded();
1651: if (name == null)
1652: throw new IllegalArgumentException(
1653: "Savepoint name cannot be null");
1654:
1655: if (autoCommit)
1656: throw new DriverSQLException(
1657: "Trying to set a savepoint in autocommit mode");
1658:
1659: if (driver == null)
1660: throw new DriverSQLException("No driver to set a savepoint");
1661:
1662: try {
1663: sendCommand(Commands.SetNamedSavepoint);
1664: socketOutput.writeLongUTF(name);
1665: socketOutput.flush();
1666:
1667: this .receiveBooleanOrException();
1668: return new org.continuent.sequoia.driver.Savepoint(name);
1669: } catch (SerializableException se) {
1670: throw new DriverSQLException(se);
1671: } catch (IOException e) {
1672: try {
1673: // Connection failed, try to reconnect and re-set the savepoint if it
1674: // was not set before the failure
1675: reconnect();
1676: org.continuent.sequoia.driver.Savepoint savepoint = new org.continuent.sequoia.driver.Savepoint(
1677: name);
1678: boolean checkpointDoesNotExit = retrieveReleaseSavepoint(savepoint);
1679: if (checkpointDoesNotExit)
1680: return setSavepoint(name);
1681: else
1682: return savepoint;
1683: } catch (DriverSQLException e1) {
1684: throw new DriverSQLException(
1685: "Connection lost while setting the savepoint '"
1686: + name
1687: + "' and automatic reconnect failed("
1688: + e1 + ")", e1);
1689: }
1690: }
1691: }
1692:
1693: /**
1694: * You can call this method to try to change the transaction isolation level
1695: * using one of the TRANSACTION_* values.
1696: * <p>
1697: * <B>Note: </B> this method cannot be called while in the middle of a
1698: * transaction. The JDBC spec says it should trigger a commit. We should
1699: * probably let the backend handle this, not trying to add our own complexity.
1700: *
1701: * @param level one of the TRANSACTION_* isolation values with * the exception
1702: * of TRANSACTION_NONE; some databases may * not support other values
1703: * @exception DriverSQLException if a database access error occurs
1704: * @see java.sql.DatabaseMetaData#supportsTransactionIsolationLevel
1705: */
1706: public synchronized void setTransactionIsolation(int level)
1707: throws DriverSQLException {
1708: throwSQLExceptionIfClosed();
1709: // Check if we are in a transaction or not. We have no trace on the driver
1710: // side if a read query has already been executed or not in the current
1711: // transaction (if any). We let the controller check for this (we only check
1712: // for writes here) as well as if the underlying databases support the
1713: // transaction isolation level. If this is not supported, the driver will
1714: // send back an exception.
1715: if ((autoCommit == false) && writeExecutedInTransaction)
1716: throw new DriverSQLException(
1717: getCurrentMethodName()
1718: + " cannot be called in a transaction that has executed write requests.");
1719:
1720: if (level != isolationLevel) { // Only try to change if there is a new value
1721: if ((level == TRANSACTION_READ_COMMITTED)
1722: || (level == TRANSACTION_READ_UNCOMMITTED)
1723: || (level == TRANSACTION_REPEATABLE_READ)
1724: || (level == TRANSACTION_SERIALIZABLE)) {
1725: try {
1726: sendCommand(Commands.SetTransactionIsolation);
1727: socketOutput.writeInt(level);
1728: socketOutput.flush();
1729:
1730: if (sequoiaUrl.isDebugEnabled())
1731: System.out
1732: .println("Setting transaction isolation level to "
1733: + level);
1734:
1735: receiveBooleanOrException();
1736: // Success
1737: isolationLevel = level;
1738: return;
1739:
1740: } catch (SerializableException e) {
1741: throw new DriverSQLException(e);
1742: } catch (IOException ioe) {
1743: try {
1744: // Connection failed, try to reconnect and re-set the transaction
1745: // isolation level
1746: reconnect();
1747: setTransactionIsolation(level);
1748: } catch (DriverSQLException e1) {
1749: throw new DriverSQLException(
1750: "Connection lost while setting the transaction isolation level '"
1751: + level
1752: + "' and automatic reconnect failed("
1753: + e1 + ")", e1);
1754: }
1755: }
1756: } else
1757: throw new DriverSQLException(
1758: "Invalid transaction isolation level " + level);
1759: } // we were already in that level; do nothing.
1760: }
1761:
1762: /**
1763: * Sequoia does NOT support type map.
1764: *
1765: * @param map ignored
1766: * @exception SQLException not supported
1767: */
1768: public void setTypeMap(java.util.Map map) throws SQLException {
1769: throw new NotImplementedException(getCurrentMethodName());
1770: }
1771:
1772: /*
1773: * Connection Sequoia internals
1774: */
1775:
1776: /**
1777: * Begins a new transaction if needed (<code>mustBeginTransaction</code> is
1778: * set to <code>true</code>).
1779: *
1780: * @throws DriverSQLException if begin fails
1781: */
1782: private void beginTransactionIfNeeded() throws DriverSQLException {
1783: if (!mustBeginTransaction)
1784: return;
1785:
1786: begin();
1787: this .mustBeginTransaction = false;
1788: }
1789:
1790: /**
1791: * Fetch multiple results from a query executed with Statement.execute()
1792: *
1793: * @return the list of results
1794: */
1795: private List fetchMultipleResultsFromStream(String callerName)
1796: throws IOException, SerializableException,
1797: ProtocolException {
1798: boolean hasResult;
1799: int updateCount = 0;
1800: LinkedList results = new LinkedList();
1801: do {
1802: hasResult = receiveBooleanOrException();
1803: if (hasResult) {
1804: DriverResultSet rs = receiveResultSet(callerName);
1805: if (rs == null) {
1806: // This happens during transparent failover when the controller's
1807: // request cache hasn't found a result for a given request.
1808: return null;
1809: } else {
1810: results.addLast(rs);
1811: }
1812: } else {
1813: updateCount = receiveIntOrException();
1814: results.addLast(new Integer(updateCount));
1815: }
1816: } while (hasResult || updateCount != -1);
1817: return results;
1818: }
1819:
1820: /**
1821: * Fetch named parameters from the stream.
1822: *
1823: * @return HashMap of <ParameterName,ParameterValue> or null
1824: * @throws ProtocolException if a protocol error occured
1825: * @throws IOException if an error with the socket occured
1826: * @throws SerializableException if a problem occured deserializing an object
1827: */
1828: private HashMap fetchNamedParameters() throws ProtocolException,
1829: IOException, SerializableException {
1830: if (sequoiaUrl.isDebugEnabled())
1831: System.out.println("Retrieving named parameters");
1832: String paramName = receiveStringOrException();
1833: if ("0".equals(paramName))
1834: return null;
1835: HashMap params = new HashMap();
1836: while (!"0".equals(paramName)) {
1837: Object value = receiveObject();
1838: params.put(paramName, value);
1839: paramName = receiveStringOrException();
1840: }
1841: return params;
1842: }
1843:
1844: /**
1845: * Fetch OUT parameters from the stream.
1846: *
1847: * @return HashMap of <ParameterName,ParameterValue> or null
1848: * @throws ProtocolException if a protocol error occured
1849: * @throws IOException if an error with the socket occured
1850: * @throws SerializableException if a problem occured deserializing an object
1851: */
1852: private HashMap fetchOutParameters() throws ProtocolException,
1853: IOException, SerializableException {
1854: if (sequoiaUrl.isDebugEnabled())
1855: System.out.println("Retrieving out parameters");
1856:
1857: int index = receiveIntOrException();
1858: if (index == 0)
1859: return null;
1860: HashMap params = new HashMap();
1861: while (index != 0) {
1862: Object value = receiveObject();
1863: params.put(new Integer(index), value);
1864: index = receiveIntOrException();
1865: }
1866: return params;
1867: }
1868:
1869: /**
1870: * Set the autocommit mode and read-only status on this request.
1871: *
1872: * @param request The request to set
1873: */
1874: private void setConnectionParametersOnRequest(Request request) {
1875: request.setIsAutoCommit(autoCommit);
1876: }
1877:
1878: /**
1879: * Receive an object from the stream by fetching a tag first and then the
1880: * value from the proper serializer.
1881: *
1882: * @return the deserialized object (can be null)
1883: * @throws IOException if a socket error occurs
1884: * @throws ProtocolException if a protocol corruption is detected
1885: */
1886: private Object receiveObject() throws IOException,
1887: ProtocolException {
1888: TypeTag tag = new TypeTag(socketInput);
1889:
1890: // Handle the null specific case
1891: if (TypeTag.JAVA_NULL.equals(tag))
1892: return null;
1893:
1894: // We have a real object to de-serialize
1895: try {
1896: Serializer serializer = SQLDataSerialization
1897: .getSerializer(tag);
1898: return serializer.receiveFromStream(socketInput);
1899: } catch (IllegalArgumentException iae) {
1900: ProtocolException pe = new ProtocolException(
1901: "Protocol corruption: received unknown TypeTag "
1902: + tag + " when receiving object.");
1903: pe.initCause(iae);
1904: throw pe;
1905: }
1906: }
1907:
1908: /**
1909: * Returns a DriverResultSet read from the stream or throws the
1910: * SerializableException that came instead
1911: *
1912: * @param callerName used for error messages. Is this really useful?
1913: * @return received ResultSet
1914: * @throws IOException stream or protocol error
1915: * @throws SerializableException received from the controller
1916: */
1917: private DriverResultSet receiveResultSet(String callerName)
1918: throws IOException, ProtocolException,
1919: SerializableException {
1920: TypeTag tag = new TypeTag(socketInput);
1921:
1922: if (TypeTag.NULL_RESULTSET.equals(tag))
1923: return null;
1924:
1925: if (TypeTag.RESULTSET.equals(tag)) {
1926: DriverResultSet drs = new DriverResultSet(this );
1927: return drs;
1928: }
1929:
1930: if (TypeTag.EXCEPTION.equals(tag))
1931: throw receiveException();
1932:
1933: throw new ProtocolException(callerName
1934: + ": expected a resultset, received unexpected tag: "
1935: + tag);
1936: }
1937:
1938: /**
1939: * Deserialize SQL warnings from the stream: converts BackendDriverException
1940: * to an SQLWarning chain
1941: *
1942: * @return the deserialized warning chain
1943: * @throws IOException stream error
1944: * @throws ProtocolException protocol error
1945: * @throws SerializableException
1946: */
1947: private SQLWarning receiveSQLWarnings() throws IOException,
1948: ProtocolException, SerializableException {
1949: if (!receiveBooleanOrException())
1950: // no warning
1951: return null;
1952: // Receive the warning as a BackendDriverException
1953: SerializableException e = receiveException();
1954: if (!(e instanceof BackendDriverException))
1955: throw new ProtocolException(
1956: "Unknown exception received instead of SQLWarning");
1957: return convertToSQLWarnings(e);
1958: }
1959:
1960: /**
1961: * Deserialize an exception from the stream: converts explicit protocol typing
1962: * into java types.
1963: *
1964: * @return the deserialized exception read from the stream
1965: * @throws IOException stream error
1966: * @throws ProtocolException protocol error
1967: */
1968: private SerializableException receiveException()
1969: throws IOException, ProtocolException {
1970: TypeTag exceptionType = new TypeTag(socketInput);
1971:
1972: if (TypeTag.BACKEND_EXCEPTION.equals(exceptionType))
1973: return new BackendDriverException(socketInput);
1974: if (TypeTag.CORE_EXCEPTION.equals(exceptionType))
1975: return new ControllerCoreException(socketInput);
1976:
1977: throw new ProtocolException("received unknown exception type");
1978: }
1979:
1980: /**
1981: * Returns a String read from the stream or throws the SerializableException
1982: * that came instead.
1983: *
1984: * @throws IOException stream or protocol error
1985: * @throws SerializableException coming from the controller
1986: * @throws ProtocolException protocol error
1987: */
1988: private String receiveStringOrException() throws IOException,
1989: SerializableException, ProtocolException {
1990: TypeTag tag = new TypeTag(socketInput);
1991: if (TypeTag.NOT_EXCEPTION.equals(tag)) {
1992: String answer = socketInput.readLongUTF();
1993: return answer;
1994: }
1995:
1996: throw receiveException();
1997: }
1998:
1999: /**
2000: * Returns a boolean read from the stream or throws the SerializableException
2001: * that came instead.
2002: *
2003: * @throws IOException stream or protocol error
2004: * @throws SerializableException coming from the controller
2005: * @throws ProtocolException protocol error
2006: */
2007: private boolean receiveBooleanOrException() throws IOException,
2008: SerializableException, ProtocolException {
2009: TypeTag tag = new TypeTag(socketInput);
2010: if (TypeTag.NOT_EXCEPTION.equals(tag)) {
2011: boolean answer = socketInput.readBoolean();
2012: return answer;
2013: }
2014:
2015: throw receiveException();
2016: }
2017:
2018: /**
2019: * Returns a int read from the stream or throws the SerializableException that
2020: * came instead.
2021: *
2022: * @throws IOException stream or protocol error
2023: * @throws SerializableException coming from the controller
2024: * @throws ProtocolException protocol error
2025: */
2026: private int receiveIntOrException() throws IOException,
2027: SerializableException, ProtocolException {
2028: TypeTag tag = new TypeTag(socketInput);
2029: if (TypeTag.NOT_EXCEPTION.equals(tag)) {
2030: int answer = socketInput.readInt();
2031: return answer;
2032: }
2033:
2034: throw receiveException();
2035: }
2036:
2037: /**
2038: * Returns a long read from the stream or throws the SerializableException
2039: * that came instead.
2040: *
2041: * @throws IOException stream or protocol error
2042: * @throws SerializableException coming from the controller
2043: * @throws ProtocolException protocol error
2044: */
2045: private long receiveLongOrException() throws IOException,
2046: SerializableException, ProtocolException {
2047: TypeTag tag = new TypeTag(socketInput);
2048: if (TypeTag.NOT_EXCEPTION.equals(tag)) {
2049: long answer = socketInput.readLong();
2050: return answer;
2051: }
2052:
2053: throw receiveException();
2054: }
2055:
2056: /**
2057: * Serialize a savepoint on the output stream by sending only the needed
2058: * parameters to reconstruct it on the controller
2059: *
2060: * @param savepoint the savepoint to send
2061: * @throws IOException if fails
2062: */
2063: private void savepointOnStream(Savepoint savepoint)
2064: throws IOException {
2065: writeExecutedInTransaction = true;
2066:
2067: try {
2068: socketOutput.writeLongUTF(savepoint.getSavepointName());
2069: return;
2070: } catch (SQLException ignore) {
2071: // Ignoring because we are dealing with an un-named savepoint
2072: }
2073:
2074: try {
2075: socketOutput.writeLongUTF(String.valueOf(savepoint
2076: .getSavepointId()));
2077: return;
2078: } catch (SQLException ignore) {
2079: // We should never get here
2080: }
2081: }
2082:
2083: /**
2084: * Check if the given release savepoint has been successfully performed.
2085: *
2086: * @return true if the release savepoint has been successfully performed
2087: * @throws DriverSQLException if an error occured
2088: */
2089: private boolean retrieveReleaseSavepoint(Savepoint savepoint)
2090: throws DriverSQLException {
2091: try {
2092: sendCommand(Commands.RetrieveReleaseSavepoint);
2093: socketOutput.writeLongUTF(savepoint.getSavepointName());
2094: socketOutput.flush();
2095: return receiveBooleanOrException();
2096: } catch (Throwable e) {
2097: throw new DriverSQLException(getCurrentMethodName()
2098: + " failed on new controller (" + e + ")");
2099: }
2100: }
2101:
2102: /**
2103: * Check if the given commit has been successfully performed.
2104: *
2105: * @return the transaction id if the commit has been successfully performed
2106: * @throws DriverSQLException if an error occured on the commit
2107: */
2108: private long retrieveCommitResult() throws DriverSQLException {
2109: try {
2110: sendCommand(Commands.RetrieveCommitResult);
2111: socketOutput.flush();
2112: return receiveLongOrException();
2113: } catch (Throwable e) {
2114: throw new DriverSQLException(getCurrentMethodName()
2115: + " failed on new controller (" + e + ")");
2116: }
2117: }
2118:
2119: /**
2120: * Check if the given rollback has been successfully performed.
2121: *
2122: * @return the transaction id if the rollback has been successfully performed
2123: * @throws DriverSQLException if an error occured on the rollback
2124: */
2125: private long retrieveRollbackResult() throws DriverSQLException {
2126: try {
2127: sendCommand(Commands.RetrieveRollbackResult);
2128: socketOutput.flush();
2129: return receiveLongOrException();
2130: } catch (Throwable e) {
2131: throw new DriverSQLException(getCurrentMethodName()
2132: + " failed on new controller (" + e + ")");
2133: }
2134: }
2135:
2136: /**
2137: * Check if the given query already executed or not on the controller we are
2138: * currently connected to.
2139: *
2140: * @param request the stored procedure to check
2141: * @return null if not found or a List composed of a
2142: * <code>java.sql.ResultSet</code> value, an <code>ArrayList</code>
2143: * of OUT parameters, and a <code>HashMap</code> of named parameters
2144: * result objects.
2145: * @throws DriverSQLException if an error occurs
2146: */
2147: private ResultAndWarnings retrieveExecuteQueryResultWithParameters(
2148: Request request) throws DriverSQLException {
2149: try {
2150: sendCommand(Commands.RetrieveExecuteQueryResultWithParameters);
2151: socketOutput.writeLong(request.getId());
2152: socketOutput.flush();
2153: SQLWarning sqlw = receiveSQLWarnings();
2154: DriverResultSet drs = receiveResultSet(getCurrentMethodName());
2155: if (drs == null)
2156: return null;
2157: List result = new ArrayList(3);
2158: result.add(drs);
2159: result.add(fetchOutParameters());
2160: result.add(fetchNamedParameters());
2161: return new ResultAndWarnings(result, sqlw);
2162: } catch (Throwable e) {
2163: throw new DriverSQLException(getCurrentMethodName()
2164: + " failed on new controller (" + e + ")");
2165: }
2166: }
2167:
2168: /**
2169: * Check if the given query already executed or not on the controller we are
2170: * currently connected to.
2171: *
2172: * @param request the request to check
2173: * @return -1 if not found or a List composed of an <code>Integer</code>
2174: * (number of updated rows), an <code>ArrayList</code> of OUT
2175: * parameters, and a <code>HashMap</code> of named parameters result
2176: * objects.
2177: * @throws DriverSQLException if an error occurs
2178: */
2179: private ResultAndWarnings retrieveExecuteUpdateResultWithParameters(
2180: Request request) throws DriverSQLException {
2181: try {
2182: sendCommand(Commands.RetrieveExecuteUpdateResultWithParameters);
2183: socketOutput.writeLong(request.getId());
2184: socketOutput.flush();
2185: SQLWarning sqlw = receiveSQLWarnings();
2186: int updateCount = receiveIntOrException();
2187: if (updateCount == -1)
2188: return null; // No result found in failover
2189: List result = new ArrayList(3);
2190: result.add(new Integer(updateCount));
2191: result.add(fetchOutParameters());
2192: result.add(fetchNamedParameters());
2193: return new ResultAndWarnings(result, sqlw);
2194: } catch (Throwable e) {
2195: throw new DriverSQLException(getCurrentMethodName()
2196: + " failed on new controller (" + e + ")");
2197: }
2198: }
2199:
2200: /**
2201: * Check if the given query already executed or not on the controller we are
2202: * currently connected to.
2203: *
2204: * @param request the request to check
2205: * @return null if not found or a List composed of 1. a <code>List</code> of
2206: * results <code>java.sql.ResultSet</code> value, 2. an
2207: * <code>ArrayList</code> of OUT parameters, and 3. a
2208: * <code>HashMap</code> of named parameters result objects.
2209: * @throws DriverSQLException if an error occurs
2210: */
2211: private ResultAndWarnings retrieveExecuteResultWithParameters(
2212: Request request) throws DriverSQLException {
2213: try {
2214: sendCommand(Commands.RetrieveExecuteResultWithParameters);
2215: socketOutput.writeLong(request.getId());
2216: socketOutput.flush();
2217: SQLWarning statementWarnings = receiveSQLWarnings();
2218: List results = fetchMultipleResultsFromStream(getCurrentMethodName());
2219: if (results == null)
2220: return null; // No result found in failover
2221: List result = new ArrayList(3);
2222: result.add(results);
2223: result.add(fetchOutParameters());
2224: result.add(fetchNamedParameters());
2225: return new ResultAndWarnings(result, statementWarnings);
2226: } catch (Throwable e) {
2227: throw new DriverSQLException(getCurrentMethodName()
2228: + " failed on new controller (" + e + ")");
2229: }
2230: }
2231:
2232: /**
2233: * Check if the given query already executed or not on the controller we are
2234: * currently connected to.
2235: *
2236: * @param request the request to check
2237: * @return int the number of updated rows or -1 if not found
2238: * @throws DriverSQLException if an error occurs
2239: */
2240: private ResultAndWarnings retrieveExecuteUpdateResult(
2241: Request request) throws DriverSQLException {
2242: try {
2243: sendCommand(Commands.RetrieveExecuteUpdateResult);
2244: socketOutput.writeLong(request.getId());
2245: socketOutput.flush();
2246: SQLWarning sqlw = receiveSQLWarnings();
2247: int uc = receiveIntOrException();
2248: return new ResultAndWarnings(uc, sqlw);
2249: } catch (Throwable e) {
2250: throw new DriverSQLException(getCurrentMethodName()
2251: + " failed on new controller (" + e + ")");
2252: }
2253: }
2254:
2255: /**
2256: * Check if the given query already executed or not on the controller we are
2257: * currently connected to.
2258: *
2259: * @param request the request to check
2260: * @return int the number of updated rows or -1 if not found
2261: * @throws DriverSQLException if an error occurs
2262: */
2263: private DriverGeneratedKeysResult retrieveExecuteUpdateWithKeysResult(
2264: Request request) throws DriverSQLException {
2265: try {
2266: sendCommand(Commands.RetrieveExecuteUpdateWithKeysResult);
2267: socketOutput.writeLong(request.getId());
2268: socketOutput.flush();
2269:
2270: SQLWarning sqlw = receiveSQLWarnings();
2271: int updateCount = receiveIntOrException();
2272: if (updateCount == -1)
2273: return null;
2274:
2275: // Fetch the ResultSet containing the autogenerated keys.
2276: TypeTag tag = new TypeTag(socketInput);
2277: if (TypeTag.RESULTSET.equals(tag)) {
2278: DriverResultSet drs = new DriverResultSet(this );
2279: return new DriverGeneratedKeysResult(drs, updateCount,
2280: sqlw);
2281: }
2282:
2283: if (TypeTag.NULL_RESULTSET.equals(tag))
2284: return new DriverGeneratedKeysResult(null, updateCount,
2285: sqlw);
2286:
2287: // Error, unexpected answer
2288: throw new ProtocolException(getCurrentMethodName()
2289: + ": protocol corruption for request "
2290: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH));
2291: } catch (Throwable e) {
2292: throw new DriverSQLException(getCurrentMethodName()
2293: + " failed on new controller (" + e + ")");
2294: }
2295: }
2296:
2297: /**
2298: * Check if the given query already executed or not on the controller we are
2299: * currently connected to.
2300: *
2301: * @param request the request to check
2302: * @return a <code>List</code> of results or null if not found
2303: * @throws DriverSQLException if an error occurs
2304: */
2305: private ResultAndWarnings retrieveExecuteResult(Request request)
2306: throws DriverSQLException {
2307: try {
2308: sendCommand(Commands.RetrieveExecuteResult);
2309: socketOutput.writeLong(request.getId());
2310: socketOutput.flush();
2311: SQLWarning statementWarnings = receiveSQLWarnings();
2312: List resList = fetchMultipleResultsFromStream(getCurrentMethodName());
2313: return new ResultAndWarnings(resList, statementWarnings);
2314: } catch (Throwable e) {
2315: throw new DriverSQLException(getCurrentMethodName()
2316: + " failed on new controller (" + e + ")");
2317: }
2318: }
2319:
2320: void reallyClose() throws IOException, DriverSQLException {
2321: sendCommand(Commands.Close);
2322: }
2323:
2324: /**
2325: * Try to reconnect to the next controller chosen according to the
2326: * policy specified in the JDBC URL of this connection.
2327: *
2328: * @throws DriverSQLException if an error occured during reconnect
2329: */
2330: private synchronized void reconnect() throws DriverSQLException,
2331: VirtualDatabaseUnavailableException {
2332: // Get rid of current connection
2333: try {
2334: this .socket.close();
2335: } catch (IOException ignore) {
2336: }
2337: try {
2338: this .socketInput.close();
2339: } catch (IOException ignore) {
2340: }
2341: try {
2342: this .socketOutput.close();
2343: } catch (IOException ignore) {
2344: }
2345: // only one (Connection) accessing the pool at a time
2346: synchronized (driver.pendingConnectionClosing) {
2347: if (driver.pendingConnectionClosing.remove(this ))
2348: System.out
2349: .println("Warning! Closed call before reconnect");
2350: }
2351:
2352: SequoiaUrl tempUrl = sequoiaUrl;
2353: if (persistentConnection) {
2354: /**
2355: * We do not want to create a persistent connection on the new connection,
2356: * because we will just close it during the restore operation. We need to
2357: * make a copy of sequoiaUrl, because it is shared with other connections.
2358: */
2359: try {
2360: tempUrl = sequoiaUrl.getTemporaryCloneForReconnection(
2361: vdbUser, vdbPassword);
2362: } catch (SQLException e) {
2363: // if we could not get a new sequoiaUrl we will just use the original
2364: }
2365: }
2366:
2367: Connection newconn = null;
2368: // At this point, the current controller is down and we have to try a
2369: // new one that will be allocated by the policy specified in the URL.
2370: try {
2371: newconn = driver.getConnectionToNewController(tempUrl);
2372: if (newconn != null)
2373: controllerInfo = newconn.getControllerInfo();
2374: } catch (AuthenticationException e) {
2375: // Should not happen, this probably mean an inconsistency in controller
2376: // configuration but safely ignore (see below)
2377: String msg = "Warning! Authentication exception received on connection retry, controller configuration might be inconsistent";
2378: if (sequoiaUrl.isInfoEnabled())
2379: System.out.println(msg);
2380: throw new DriverSQLException(msg, e);
2381: } catch (NoMoreControllerException nmc) {
2382: throw new DriverSQLException(nmc);
2383: } catch (GeneralSecurityException gse) {
2384: String msg = "Fatal General Security Exception received while trying to reconnect";
2385: if (sequoiaUrl.isInfoEnabled())
2386: System.out.println(msg);
2387: throw new DriverSQLException(msg, gse);
2388: }
2389:
2390: // newconn cannot be null here else an excepection would have been thrown
2391: // earlier.
2392:
2393: // Success: let's use the new connection for ourselves
2394: this .socket = newconn.socket;
2395: this .socketInput = newconn.socketInput;
2396: this .socketOutput = newconn.socketOutput;
2397: this .controllerInfo = newconn.controllerInfo;
2398: this .isClosed = false;
2399: try {
2400: if (sequoiaUrl.isDebugEnabled())
2401: System.out
2402: .println("Restoring connection state on controller "
2403: + controllerInfo);
2404: sendCommand(Commands.RestoreConnectionState);
2405: socketOutput.writeBoolean(writeExecutedInTransaction);
2406: if (mustBeginTransaction) { // Say that we are in autoCommit, begin will be done later
2407: // Fixes SEQUOIA-522
2408: socketOutput.writeBoolean(true);
2409: } else {
2410: socketOutput.writeBoolean(autoCommit);
2411: if (!autoCommit)
2412: socketOutput.writeLong(transactionId);
2413: }
2414: socketOutput.writeBoolean(persistentConnection);
2415: if (persistentConnection)
2416: socketOutput.writeLong(persistentConnectionId);
2417: socketOutput.writeBoolean(retrieveSQLWarnings);
2418: socketOutput.flush();
2419: // Read ack. We won't do anything with it but reading this it is the only
2420: // way to dectect that the controller is down (see SEQUOIA-632)
2421: socketInput.readBoolean();
2422:
2423: // Restore read-only state (not part of RestoreConnectionState command for
2424: // backward compatibility)
2425: setReadOnly(readOnly);
2426: } catch (IOException e) {
2427: reconnect();
2428: }
2429: }
2430:
2431: /**
2432: * Before sending a command code, checks that the controller is
2433: * synchronized/ready to accept it. Then sends it.
2434: *
2435: * @param command to send
2436: * @throws IOException on socket error
2437: * @throws DriverSQLException on protocol corruption
2438: */
2439: private void sendCommand(int command) throws IOException,
2440: DriverSQLException {
2441: if (socketInput.readInt() != Commands.ControllerPrompt)
2442: throw new DriverSQLException(
2443: "Protocol corruption while trying to send command: "
2444: + command + ". Check the previous command");
2445: socketOutput.writeInt(Commands.CommandPrefix);
2446: socketOutput.writeInt(command);
2447: }
2448:
2449: /**
2450: * @see #close()
2451: */
2452: private void throwSQLExceptionIfClosed(String message)
2453: throws DriverSQLException {
2454: if (isClosed)
2455: throw new DriverSQLException(message);
2456: }
2457:
2458: /**
2459: * @see #close()
2460: */
2461: private void throwSQLExceptionIfClosed() throws DriverSQLException {
2462: // default message
2463: throwSQLExceptionIfClosed("Tried to operate on a closed Connection");
2464: }
2465:
2466: private DriverSQLException wrapIOExceptionInDriverSQLException(
2467: String callerName, IOException ioe) {
2468: return new DriverSQLException("I/O Error on method "
2469: + callerName + "():\n" + ioe.getLocalizedMessage(), ioe);
2470: }
2471:
2472: /**
2473: * Utility function to convert the given chain of backendException to a chain
2474: * of SQLWarnings
2475: *
2476: * @param toConvert exception chain to convert
2477: */
2478: protected SQLWarning convertToSQLWarnings(
2479: SerializableException toConvert) {
2480: if (toConvert == null)
2481: return null;
2482: SQLWarning sqlw = new SQLWarning(toConvert.getMessage(),
2483: toConvert.getSQLState(), toConvert.getErrorCode());
2484: Throwable t = toConvert.getCause();
2485: if (t != null) {
2486: if (t instanceof SerializableException)
2487: sqlw
2488: .setNextWarning(convertToSQLWarnings((SerializableException) t));
2489: else if (sequoiaUrl.isDebugEnabled())
2490: System.out.println("Unexpected exception type "
2491: + t.getClass()
2492: + "while converting warning chain");
2493: }
2494: return sqlw;
2495: }
2496:
2497: /**
2498: * Performs a read request and return the reply.
2499: *
2500: * @param request the read request to execute
2501: * @return a <code>java.sql.ResultSet</code> value
2502: * @exception DriverSQLException if an error occurs
2503: */
2504: protected synchronized DriverResultSet statementExecuteQuery(
2505: RequestWithResultSetParameters request)
2506: throws DriverSQLException, NotImplementedException {
2507: throwSQLExceptionIfClosed("Closed connection cannot process request '"
2508: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
2509: beginTransactionIfNeeded();
2510:
2511: try {
2512: setConnectionParametersOnRequest(request);
2513: sendCommand(Commands.StatementExecuteQuery);
2514: request.sendToStream(socketOutput);
2515: socketOutput.flush();
2516: if (sequoiaUrl.isDebugEnabled())
2517: System.out.println("Executing "
2518: + getCurrentMethodName() + " with request "
2519: + request);
2520:
2521: SQLWarning statementWarnings = receiveSQLWarnings();
2522: TypeTag tag = new TypeTag(socketInput);
2523:
2524: // First case, we received our ResultSet, let's fetch it.
2525: if (TypeTag.RESULTSET.equals(tag)) {
2526: try {
2527: DriverResultSet drs = new DriverResultSet(this );
2528: drs.setStatementWarnings(statementWarnings);
2529: return drs;
2530: } catch (ProtocolException e) {
2531: throw new DriverSQLException(
2532: "Protocol corruption in "
2533: + getCurrentMethodName()
2534: + " with request "
2535: + request
2536: .getSqlShortForm(ABBREV_REQUEST_LENGTH),
2537: e);
2538: } catch (IOException e) { // Error while reading, retry
2539: if (sequoiaUrl.isInfoEnabled())
2540: System.out
2541: .println("IOException occured trying to reconnect ("
2542: + e.getLocalizedMessage() + ")");
2543: reconnect();
2544: return statementExecuteQuery(request);
2545: }
2546: }
2547:
2548: if (TypeTag.NULL_RESULTSET.equals(tag))
2549: return null;
2550:
2551: // From this point on, we had an exception
2552: if (TypeTag.EXCEPTION.equals(tag)) {
2553: Exception recvEx = null;
2554: recvEx = receiveException();
2555: // dirty hack until cleanup
2556: if (recvEx instanceof ControllerCoreException)
2557: recvEx = ((ControllerCoreException) recvEx)
2558: .compatibilityWrapperHack();
2559:
2560: if (recvEx instanceof NoMoreBackendException) {
2561: if (sequoiaUrl.isInfoEnabled())
2562: System.out
2563: .println("No more backend available on controller");
2564: throw new DriverSQLException(recvEx);
2565: } else if (recvEx instanceof IOException) {
2566: // We shouldn't have been able to receive this IOE because it means
2567: // that the socket died (at the controller side)
2568: throw new ProtocolException(
2569: "Received exception of unexpected type ("
2570: + ((IOException) recvEx)
2571: .getLocalizedMessage()
2572: + ")");
2573: } else if (recvEx instanceof BackendDriverException) {
2574: // TODO: temporary fix until DriverSQLException is fixed
2575: throw new DriverSQLException(
2576: (SerializableException) recvEx);
2577: } else if (recvEx instanceof NotImplementedException) {
2578: // incredibly ugly.
2579: throw (NotImplementedException) recvEx;
2580: } else if (recvEx instanceof SQLException) {
2581: throw new DriverSQLException((SQLException) recvEx);
2582: }
2583: }
2584:
2585: // Error, unexpected answer
2586: throw new ProtocolException("Protocol corruption in "
2587: + getCurrentMethodName() + " for request "
2588: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH));
2589: } catch (SerializableException se) {
2590: throw new DriverSQLException(se);
2591: } catch (RuntimeException e) {
2592: e.printStackTrace();
2593: throw new DriverSQLException(getCurrentMethodName()
2594: + ": error occured while request '"
2595: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH)
2596: + "' was processed by Sequoia Controller", e);
2597: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
2598: try {
2599: if (sequoiaUrl.isInfoEnabled())
2600: System.out
2601: .println("IOException occured trying to reconnect ("
2602: + e.getMessage() + ")");
2603: reconnect();
2604: return statementExecuteQuery(request);
2605: } catch (DriverSQLException e1) {
2606: throw new DriverSQLException(
2607: "Connection lost while executing "
2608: + getCurrentMethodName()
2609: + " with request '"
2610: + request
2611: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
2612: + "' and automatic reconnect failed ("
2613: + e1 + ")", e1);
2614: }
2615: }
2616: }
2617:
2618: /**
2619: * Performs a write request and return the number of rows affected.
2620: *
2621: * @param request the write request to execute
2622: * @return number of rows affected
2623: * @exception DriverSQLException if an error occurs
2624: */
2625: protected synchronized ResultAndWarnings statementExecuteUpdate(
2626: Request request) throws DriverSQLException {
2627: throwSQLExceptionIfClosed("Closed connection cannot process request '"
2628: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
2629: beginTransactionIfNeeded();
2630:
2631: boolean requestIdIsSet = false;
2632: try {
2633: setConnectionParametersOnRequest(request);
2634: sendCommand(Commands.StatementExecuteUpdate);
2635: request.sendToStream(socketOutput);
2636: socketOutput.flush();
2637: if (sequoiaUrl.isDebugEnabled())
2638: System.out.println("Executing "
2639: + getCurrentMethodName() + " with request "
2640: + request);
2641:
2642: request.setId(receiveLongOrException());
2643: requestIdIsSet = true;
2644: if (!autoCommit)
2645: writeExecutedInTransaction = true;
2646:
2647: SQLWarning statementWarnings = receiveSQLWarnings();
2648: int uc = receiveIntOrException();
2649: return new ResultAndWarnings(uc, statementWarnings);
2650:
2651: } catch (SerializableException se) {
2652: throw new DriverSQLException(se);
2653: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
2654: try {
2655: reconnect();
2656: if (requestIdIsSet) { // Controller handled the query, check if it was executed
2657: ResultAndWarnings result = retrieveExecuteUpdateResult(request);
2658: if (result != null && result.getUpdateCount() != -1) {
2659: return result;
2660: }
2661: }
2662: // At this point the query failed before any controller succeeded in
2663: // executing the query
2664:
2665: return statementExecuteUpdate(request);
2666: } catch (DriverSQLException e1) {
2667: throw new DriverSQLException(
2668: "Connection lost while executing "
2669: + getCurrentMethodName()
2670: + " with request '"
2671: + request
2672: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
2673: + "' and automatic reconnect failed ("
2674: + e1 + ")", e1);
2675: }
2676: }
2677: }
2678:
2679: /**
2680: * Call a request that returns a list of results.
2681: *
2682: * @param request the request to execute
2683: * @return a <code>List</code> of results
2684: * @throws DriverSQLException if an error occurs
2685: */
2686: protected synchronized ResultAndWarnings statementExecute(
2687: RequestWithResultSetParameters request)
2688: throws DriverSQLException {
2689: throwSQLExceptionIfClosed("Closed Connection cannot process request '"
2690: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
2691: beginTransactionIfNeeded();
2692:
2693: boolean requestIdIsSet = false;
2694: try {
2695: setConnectionParametersOnRequest(request);
2696: sendCommand(Commands.StatementExecute);
2697: request.sendToStream(socketOutput);
2698: socketOutput.flush();
2699: if (sequoiaUrl.isDebugEnabled())
2700: System.out.println("Executing "
2701: + getCurrentMethodName() + " with request"
2702: + request);
2703:
2704: request.setId(receiveLongOrException());
2705: requestIdIsSet = true;
2706: if (!autoCommit)
2707: writeExecutedInTransaction = true;
2708:
2709: SQLWarning sqlw = receiveSQLWarnings();
2710: List res = fetchMultipleResultsFromStream(getCurrentMethodName());
2711: return new ResultAndWarnings(res, sqlw);
2712: } catch (RuntimeException e) {
2713: throw new DriverSQLException(getCurrentMethodName()
2714: + ": Error occured while request '"
2715: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH)
2716: + "' was processed by Sequoia Controller", e);
2717: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
2718: try {
2719: reconnect();
2720: if (requestIdIsSet) { // Controller handled the query, check if it was executed
2721: ResultAndWarnings rww = retrieveExecuteResult(request);
2722: if (rww != null && rww.getResultList() != null)
2723: return rww;
2724: }
2725: // At this point the query failed before any controller succeeded in
2726: // executing the query
2727:
2728: return statementExecute(request);
2729: } catch (DriverSQLException e1) {
2730: throw new DriverSQLException(
2731: "Connection lost while executing "
2732: + getCurrentMethodName()
2733: + " with request '"
2734: + request
2735: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
2736: + "' and automatic reconnect failed ("
2737: + e1 + ")", e1);
2738: }
2739: } catch (SerializableException e) {
2740: throw new DriverSQLException(e);
2741: }
2742: }
2743:
2744: /**
2745: * Performs a write request and returns the auto-generated keys
2746: *
2747: * @param request the write request to execute
2748: * @return auto generated keys
2749: * @exception DriverSQLException if an error occurs
2750: */
2751: protected synchronized DriverGeneratedKeysResult statementExecuteUpdateWithKeys(
2752: Request request) throws DriverSQLException {
2753: throwSQLExceptionIfClosed("Closed Connection cannot process request '"
2754: + request.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
2755: beginTransactionIfNeeded();
2756:
2757: boolean requestIdIsSet = false;
2758: try {
2759: setConnectionParametersOnRequest(request);
2760: sendCommand(Commands.StatementExecuteUpdateWithKeys);
2761: request.sendToStream(socketOutput);
2762: socketOutput.flush();
2763: if (sequoiaUrl.isDebugEnabled())
2764: System.out.println("Executing "
2765: + getCurrentMethodName() + " with request: "
2766: + request);
2767:
2768: try {
2769: request.setId(receiveLongOrException());
2770: requestIdIsSet = true;
2771: if (!autoCommit)
2772: writeExecutedInTransaction = true;
2773:
2774: // Receive the warnings
2775: SQLWarning sqlw = receiveSQLWarnings();
2776:
2777: // Receive the update count or an exception
2778: int updateCount = receiveIntOrException();
2779:
2780: // Fetch the ResultSet containing the autogenerated keys.
2781: TypeTag tag = new TypeTag(socketInput);
2782: if (TypeTag.RESULTSET.equals(tag)) {
2783: try {
2784: DriverResultSet drs = new DriverResultSet(this );
2785: return new DriverGeneratedKeysResult(drs,
2786: updateCount, sqlw);
2787: } catch (ProtocolException e) {
2788: throw new DriverSQLException(
2789: "Protocol corruption in "
2790: + getCurrentMethodName()
2791: + " with request "
2792: + request
2793: .getSqlShortForm(ABBREV_REQUEST_LENGTH),
2794: e);
2795: } catch (IOException e) { // Error while reading, retry
2796: if (sequoiaUrl.isInfoEnabled())
2797: System.out
2798: .println("IOException occured trying to reconnect ("
2799: + e.getLocalizedMessage()
2800: + ")");
2801: reconnect();
2802: return statementExecuteUpdateWithKeys(request);
2803: }
2804: }
2805:
2806: if (TypeTag.NULL_RESULTSET.equals(tag))
2807: return new DriverGeneratedKeysResult(null,
2808: updateCount, sqlw);
2809:
2810: // Error, unexpected answer
2811: throw new ProtocolException("Protocol corruption in "
2812: + getCurrentMethodName()
2813: + " for request "
2814: + request
2815: .getSqlShortForm(ABBREV_REQUEST_LENGTH));
2816: } catch (SerializableException e) {
2817: throw new DriverSQLException(e);
2818: }
2819: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
2820: try {
2821: reconnect();
2822: if (requestIdIsSet) { // Controller handled the query, check if it was executed
2823: DriverGeneratedKeysResult result = retrieveExecuteUpdateWithKeysResult(request);
2824: if (result != null)
2825: return result;
2826: }
2827: // At this point the query failed before any controller succeeded in
2828: // executing the query
2829: return statementExecuteUpdateWithKeys(request);
2830: } catch (DriverSQLException e1) {
2831: throw new DriverSQLException(
2832: "Connection lost while executing "
2833: + getCurrentMethodName()
2834: + " with request '"
2835: + request
2836: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
2837: + "' and automatic reconnect failed ("
2838: + e1 + ")", e1);
2839: }
2840: }
2841: }
2842:
2843: /**
2844: * Call a stored procedure (with IN/OUT and/or named parameters) that returns
2845: * a ResultSet.
2846: *
2847: * @param proc the stored procedure call
2848: * @return a List composed of a <code>java.sql.ResultSet</code> value, an
2849: * <code>ArrayList</code> of OUT parameters, and a
2850: * <code>HashMap</code> of named parameters result objects.
2851: * @exception DriverSQLException if an error occurs
2852: */
2853: protected synchronized ResultAndWarnings callableStatementExecuteQuery(
2854: RequestWithResultSetParameters proc)
2855: throws DriverSQLException {
2856: throwSQLExceptionIfClosed("Closed Connection cannot process request '"
2857: + proc.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
2858: beginTransactionIfNeeded();
2859:
2860: boolean procIdIsSet = false;
2861: try {
2862: setConnectionParametersOnRequest(proc);
2863: sendCommand(Commands.CallableStatementExecuteQueryWithParameters);
2864: proc.sendToStream(socketOutput);
2865: socketOutput.flush();
2866: if (sequoiaUrl.isDebugEnabled())
2867: System.out.println("Executing "
2868: + getCurrentMethodName() + " with procedure '"
2869: + proc + "'");
2870:
2871: proc.setId(receiveLongOrException());
2872: procIdIsSet = true;
2873: if (!autoCommit)
2874: writeExecutedInTransaction = true;
2875:
2876: if (sequoiaUrl.isDebugEnabled())
2877: System.out.println("Received unique id " + proc.getId()
2878: + " for procedure '" + proc + "'");
2879:
2880: SQLWarning sqlw = receiveSQLWarnings();
2881:
2882: Exception recvEx = null;
2883: TypeTag tag = new TypeTag(socketInput);
2884:
2885: DriverResultSet drs = null;
2886:
2887: /*
2888: * TODO: the code below is a complete mess. One reason is it's still half
2889: * legacy design from old protocol, half from the new procotol. It could
2890: * easily be made much simpler. We should use #receiveResultSet() TODO:
2891: * test NoMoreBackendException
2892: */
2893: if (!TypeTag.NULL_RESULTSET.equals(tag)) {
2894: if (TypeTag.EXCEPTION.equals(tag)) {
2895: recvEx = receiveException();
2896: // dirty hack until cleanup
2897: if (recvEx instanceof ControllerCoreException)
2898: recvEx = ((ControllerCoreException) recvEx)
2899: .compatibilityWrapperHack();
2900:
2901: if (recvEx instanceof DriverSQLException)
2902: throw (DriverSQLException) recvEx;
2903:
2904: throw new DriverSQLException(recvEx);
2905: } else if (!TypeTag.RESULTSET.equals(tag))
2906: throw new DriverSQLException(
2907: getCurrentMethodName()
2908: + ": Unexpected response for request "
2909: + proc
2910: .getSqlShortForm(ABBREV_REQUEST_LENGTH),
2911: recvEx);
2912:
2913: // Ok, we have a real non-null ResultSet, fetch it
2914: drs = new DriverResultSet(this );
2915: if (sequoiaUrl.isDebugEnabled())
2916: System.out.println("Retrieved ResultSet: " + drs);
2917: }
2918:
2919: // Now fetch the OUT parameters
2920: HashMap outParameters = fetchOutParameters();
2921:
2922: // Now fetch the named parameters
2923: HashMap namedParameters = fetchNamedParameters();
2924:
2925: List result = new ArrayList(3);
2926: result.add(drs);
2927: result.add(outParameters);
2928: result.add(namedParameters);
2929: return new ResultAndWarnings(result, sqlw);
2930: } catch (RuntimeException e) {
2931: throw new DriverSQLException(getCurrentMethodName()
2932: + ": Error occured while request '"
2933: + proc.getSqlShortForm(ABBREV_REQUEST_LENGTH)
2934: + "' was processed by Sequoia Controller", e);
2935: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
2936: try {
2937: reconnect();
2938: if (procIdIsSet) { // Controller handled the query, check if it was executed
2939: ResultAndWarnings result = retrieveExecuteQueryResultWithParameters(proc);
2940: if (result != null
2941: && result.getResultList() != null)
2942: return result;
2943: }
2944: // At this point the query failed before any controller succeeded in
2945: // executing the query
2946:
2947: return callableStatementExecuteQuery(proc);
2948: } catch (DriverSQLException e1) {
2949: throw new DriverSQLException(
2950: "Connection lost while executing "
2951: + getCurrentMethodName()
2952: + " on procedure request '"
2953: + proc
2954: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
2955: + "' and automatic reconnect failed ("
2956: + e1 + ")", e1);
2957: }
2958: } catch (SerializableException se) {
2959: throw new DriverSQLException(se);
2960: }
2961: }
2962:
2963: /**
2964: * Call a stored procedure that performs an update.
2965: *
2966: * @param proc the stored procedure call
2967: * @return a List composed of an Integer (number of updated rows), an
2968: * <code>ArrayList</code> of OUT parameters, and a
2969: * <code>HashMap</code> of named parameters result objects.
2970: * @exception DriverSQLException if an error occurs
2971: */
2972: protected synchronized ResultAndWarnings callableStatementExecuteUpdate(
2973: Request proc) throws DriverSQLException {
2974: throwSQLExceptionIfClosed("Closed Connection cannot process request '"
2975: + proc.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
2976: beginTransactionIfNeeded();
2977:
2978: boolean procIdIsSet = false;
2979: try {
2980: setConnectionParametersOnRequest(proc);
2981: sendCommand(Commands.CallableStatementExecuteUpdateWithParameters);
2982: proc.sendToStream(socketOutput);
2983: socketOutput.flush();
2984: if (sequoiaUrl.isDebugEnabled())
2985: System.out.println("Executing "
2986: + getCurrentMethodName() + " with procedure '"
2987: + proc + "'");
2988:
2989: proc.setId(receiveLongOrException());
2990: procIdIsSet = true;
2991: if (!autoCommit)
2992: writeExecutedInTransaction = true;
2993:
2994: if (sequoiaUrl.isDebugEnabled())
2995: System.out.println("Received unique id " + proc.getId()
2996: + " for procedure '" + proc + "'");
2997:
2998: // Receive SQLWarnings
2999: SQLWarning sqlw = receiveSQLWarnings();
3000:
3001: // Receive update count
3002: Integer updateCount = new Integer(receiveIntOrException());
3003:
3004: // Now fetch the OUT parameters
3005: HashMap outParameters = fetchOutParameters();
3006:
3007: // Now fetch the named parameters
3008: HashMap namedParameters = fetchNamedParameters();
3009:
3010: List result = new ArrayList(3);
3011: result.add(updateCount);
3012: result.add(outParameters);
3013: result.add(namedParameters);
3014: return new ResultAndWarnings(result, sqlw);
3015:
3016: } catch (SerializableException se) {
3017: throw new DriverSQLException(se);
3018: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
3019: try {
3020: reconnect();
3021: if (procIdIsSet) { // Controller handled the query, check if it was executed
3022: ResultAndWarnings result = retrieveExecuteUpdateResultWithParameters(proc);
3023: if (result != null
3024: && result.getResultList() != null)
3025: return result;
3026: }
3027: // At this point the query failed before any controller succeeded in
3028: // executing the query
3029:
3030: return callableStatementExecuteUpdate(proc);
3031: } catch (DriverSQLException e1) {
3032: throw new DriverSQLException(
3033: "Connection lost while executing "
3034: + getCurrentMethodName()
3035: + " on procedure request '"
3036: + proc
3037: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
3038: + "' and automatic reconnect failed ("
3039: + e1 + ")", e1);
3040: }
3041: }
3042: }
3043:
3044: /**
3045: * Call a stored procedure that returns a list of results.
3046: *
3047: * @param proc the stored procedure call
3048: * @return a List composed of 1. <code>List</code> of results, 2.
3049: * <code>ArrayList</code> of OUT parameters, and 3.
3050: * <code>HashMap</code> of named parameters result objects.
3051: * @exception DriverSQLException if an error occurs
3052: */
3053: protected synchronized ResultAndWarnings callableStatementExecute(
3054: RequestWithResultSetParameters proc)
3055: throws DriverSQLException {
3056: throwSQLExceptionIfClosed("Closed Connection cannot process request '"
3057: + proc.getSqlShortForm(ABBREV_REQUEST_LENGTH) + "'");
3058: beginTransactionIfNeeded();
3059:
3060: boolean procIdIsSet = false;
3061: try {
3062: setConnectionParametersOnRequest(proc);
3063: sendCommand(Commands.CallableStatementExecuteWithParameters);
3064: proc.sendToStream(socketOutput);
3065: socketOutput.flush();
3066: if (sequoiaUrl.isDebugEnabled())
3067: System.out.println("Executing "
3068: + getCurrentMethodName() + " with procedure '"
3069: + proc + "'");
3070:
3071: proc.setId(receiveLongOrException());
3072: procIdIsSet = true;
3073: if (!autoCommit)
3074: writeExecutedInTransaction = true;
3075:
3076: if (sequoiaUrl.isDebugEnabled())
3077: System.out.println("Received unique id " + proc.getId()
3078: + " for procedure '" + proc + "'");
3079:
3080: // Fetch warnings
3081: SQLWarning sqlw = receiveSQLWarnings();
3082:
3083: // Fetch results
3084: List resultList = fetchMultipleResultsFromStream(getCurrentMethodName());
3085:
3086: // Now fetch the OUT parameters
3087: HashMap outParameters = fetchOutParameters();
3088:
3089: // Now fetch the named parameters
3090: HashMap namedParameters = fetchNamedParameters();
3091:
3092: List result = new ArrayList(3);
3093: result.add(resultList);
3094: result.add(outParameters);
3095: result.add(namedParameters);
3096: return new ResultAndWarnings(result, sqlw);
3097: } catch (RuntimeException e) {
3098: throw new DriverSQLException(getCurrentMethodName()
3099: + ": Error occured while request '"
3100: + proc.getSqlShortForm(ABBREV_REQUEST_LENGTH)
3101: + "' was processed by Sequoia Controller", e);
3102: } catch (IOException e) { // Connection failed, try to reconnect and re-exec the query
3103: try {
3104: reconnect();
3105: if (procIdIsSet) { // Controller handled the query, check if it was executed
3106: ResultAndWarnings rww = retrieveExecuteResultWithParameters(proc);
3107: if (rww != null && rww.getResultList() != null)
3108: return rww;
3109: }
3110: // At this point the query failed before any controller succeeded in
3111: // executing the query
3112:
3113: return callableStatementExecute(proc);
3114: } catch (DriverSQLException e1) {
3115: throw new DriverSQLException(
3116: "Connection lost while executing "
3117: + getCurrentMethodName()
3118: + " with procedure request'"
3119: + proc
3120: .getSqlShortForm(ABBREV_REQUEST_LENGTH)
3121: + "' and automatic reconnect failed ("
3122: + e1 + ")", e1);
3123: }
3124: } catch (SerializableException e) {
3125: throw new DriverSQLException(e);
3126: }
3127: }
3128:
3129: //
3130: // Database Metadata methods
3131: //
3132:
3133: /**
3134: * Closes the remote ResultSet given its cursor name.
3135: *
3136: * @param cursorName cursor name of the ResultSet to close.
3137: * @throws SQLException if an error occurs
3138: */
3139: protected synchronized void closeRemoteResultSet(String cursorName)
3140: throws SQLException {
3141: throwSQLExceptionIfClosed();
3142: try {
3143: sendCommand(Commands.CloseRemoteResultSet);
3144: socketOutput.writeLongUTF(cursorName);
3145: socketOutput.flush();
3146: if (sequoiaUrl.isDebugEnabled())
3147: System.out.println("Closing remote ResultSet");
3148:
3149: receiveBooleanOrException();
3150: } catch (SerializableException se) {
3151: throw new DriverSQLException(se);
3152: } catch (IOException e) {
3153: throw wrapIOExceptionInDriverSQLException(
3154: getCurrentMethodName(), e);
3155: }
3156: }
3157:
3158: /**
3159: * @see java.sql.DatabaseMetaData#getAttributes(java.lang.String,
3160: * java.lang.String, java.lang.String, java.lang.String)
3161: */
3162: protected synchronized ResultSet getAttributes(String catalog,
3163: String schemaPattern, String typeNamePattern,
3164: String attributeNamePattern) throws DriverSQLException {
3165: throwSQLExceptionIfClosed();
3166: try {
3167: sendCommand(Commands.DatabaseMetaDataGetAttributes);
3168: socketOutput.writeLongUTF(catalog);
3169: socketOutput.writeLongUTF(schemaPattern);
3170: socketOutput.writeLongUTF(typeNamePattern);
3171: socketOutput.writeLongUTF(attributeNamePattern);
3172: socketOutput.flush();
3173:
3174: if (sequoiaUrl.isDebugEnabled())
3175: System.out.println(getCurrentMethodName() + "("
3176: + catalog + "," + schemaPattern + ","
3177: + typeNamePattern + "," + attributeNamePattern
3178: + ")");
3179:
3180: return receiveResultSet(getCurrentMethodName());
3181: } catch (SerializableException e) {
3182: throw new DriverSQLException(e);
3183: } catch (IOException e) {
3184: try {
3185: reconnect();
3186: return getAttributes(catalog, schemaPattern,
3187: typeNamePattern, attributeNamePattern);
3188: } catch (DriverSQLException e1) {
3189: throw new DriverSQLException(
3190: "Connection lost while executing "
3191: + getCurrentMethodName()
3192: + " and automatic reconnect failed ",
3193: e1);
3194: }
3195: }
3196: }
3197:
3198: /**
3199: * @see java.sql.DatabaseMetaData#getBestRowIdentifier(java.lang.String,
3200: * java.lang.String, java.lang.String, int, boolean)
3201: */
3202: protected synchronized ResultSet getBestRowIdentifier(
3203: String catalog, String schema, String table, int scope,
3204: boolean nullable) throws DriverSQLException {
3205: throwSQLExceptionIfClosed();
3206: try {
3207: sendCommand(Commands.DatabaseMetaDataGetBestRowIdentifier);
3208: socketOutput.writeLongUTF(catalog);
3209: socketOutput.writeLongUTF(schema);
3210: socketOutput.writeLongUTF(table);
3211: socketOutput.writeInt(scope);
3212: socketOutput.writeBoolean(nullable);
3213: socketOutput.flush();
3214:
3215: if (sequoiaUrl.isDebugEnabled())
3216: System.out.println(getCurrentMethodName() + "("
3217: + catalog + "," + schema + "," + table + ","
3218: + scope + "," + nullable + ")");
3219:
3220: return receiveResultSet(getCurrentMethodName());
3221: } catch (SerializableException e) {
3222: throw new DriverSQLException(e);
3223: } catch (IOException e) {
3224: try {
3225: reconnect();
3226: return getBestRowIdentifier(catalog, schema, table,
3227: scope, nullable);
3228: } catch (DriverSQLException e1) {
3229: throw new DriverSQLException(
3230: "Connection lost while executing "
3231: + getCurrentMethodName()
3232: + " and automatic reconnect failed ",
3233: e1);
3234: }
3235: }
3236: }
3237:
3238: /**
3239: * @see java.sql.DatabaseMetaData#getColumnPrivileges(java.lang.String,
3240: * java.lang.String, java.lang.String, java.lang.String)
3241: */
3242: protected synchronized ResultSet getColumnPrivileges(
3243: String catalog, String schemaPattern, String tableName,
3244: String columnNamePattern) throws DriverSQLException {
3245: throwSQLExceptionIfClosed();
3246: try {
3247: sendCommand(Commands.DatabaseMetaDataGetColumnPrivileges);
3248: socketOutput.writeLongUTF(catalog);
3249: socketOutput.writeLongUTF(schemaPattern);
3250: socketOutput.writeLongUTF(tableName);
3251: socketOutput.writeLongUTF(columnNamePattern);
3252: socketOutput.flush();
3253:
3254: if (sequoiaUrl.isDebugEnabled())
3255: System.out.println(getCurrentMethodName() + "("
3256: + catalog + "," + schemaPattern + ","
3257: + tableName + "," + columnNamePattern + ")");
3258:
3259: return receiveResultSet(getCurrentMethodName());
3260: } catch (SerializableException e) {
3261: throw new DriverSQLException(e);
3262: } catch (IOException e) {
3263: try {
3264: reconnect();
3265: return getColumnPrivileges(catalog, schemaPattern,
3266: tableName, columnNamePattern);
3267: } catch (DriverSQLException e1) {
3268: throw new DriverSQLException(
3269: "Connection lost while executing "
3270: + getCurrentMethodName()
3271: + " and automatic reconnect failed ",
3272: e1);
3273: }
3274: }
3275: }
3276:
3277: /**
3278: * @see java.sql.DatabaseMetaData#getColumns(java.lang.String,
3279: * java.lang.String, java.lang.String, java.lang.String)
3280: */
3281: protected synchronized ResultSet getColumns(String catalog,
3282: String schemaPattern, String tableNamePattern,
3283: String columnNamePattern) throws DriverSQLException {
3284: throwSQLExceptionIfClosed();
3285: try {
3286: sendCommand(Commands.DatabaseMetaDataGetColumns);
3287: socketOutput.writeLongUTF(catalog);
3288: socketOutput.writeLongUTF(schemaPattern);
3289: socketOutput.writeLongUTF(tableNamePattern);
3290: socketOutput.writeLongUTF(columnNamePattern);
3291: socketOutput.flush();
3292:
3293: if (sequoiaUrl.isDebugEnabled())
3294: System.out.println(getCurrentMethodName() + "("
3295: + catalog + "," + schemaPattern + ","
3296: + tableNamePattern + "," + columnNamePattern
3297: + ")");
3298:
3299: return receiveResultSet(getCurrentMethodName());
3300: } catch (SerializableException e) {
3301: throw new DriverSQLException(e);
3302: } catch (IOException e) {
3303: try {
3304: reconnect();
3305: return getColumns(catalog, schemaPattern,
3306: tableNamePattern, columnNamePattern);
3307: } catch (DriverSQLException e1) {
3308: throw new DriverSQLException(
3309: "Connection lost while executing "
3310: + getCurrentMethodName()
3311: + " and automatic reconnect failed ",
3312: e1);
3313: }
3314: }
3315: }
3316:
3317: /**
3318: * Get the Sequoia controller version number.
3319: *
3320: * @return a String containing the controller version
3321: * @exception DriverSQLException if an error occurs
3322: */
3323: protected synchronized String getControllerVersionNumber()
3324: throws DriverSQLException {
3325: throwSQLExceptionIfClosed();
3326: try {
3327: sendCommand(Commands.GetControllerVersionNumber);
3328: socketOutput.flush();
3329:
3330: if (sequoiaUrl.isDebugEnabled())
3331: System.out.println("Executing "
3332: + getCurrentMethodName());
3333:
3334: return receiveStringOrException();
3335: } catch (SerializableException e) {
3336: throw new DriverSQLException(e);
3337: } catch (IOException e) {
3338: try {
3339: reconnect();
3340: return getControllerVersionNumber();
3341: } catch (DriverSQLException e1) {
3342: throw new DriverSQLException(
3343: "Connection lost while executing "
3344: + getCurrentMethodName()
3345: + " and automatic reconnect failed ",
3346: e1);
3347: }
3348: }
3349: }
3350:
3351: /**
3352: * @see java.sql.DatabaseMetaData#getCrossReference(java.lang.String,
3353: * java.lang.String, java.lang.String, java.lang.String,
3354: * java.lang.String, java.lang.String)
3355: */
3356: protected synchronized ResultSet getCrossReference(
3357: String primaryCatalog, String primarySchema,
3358: String primaryTable, String foreignCatalog,
3359: String foreignSchema, String foreignTable)
3360: throws DriverSQLException {
3361: throwSQLExceptionIfClosed();
3362: try {
3363: sendCommand(Commands.DatabaseMetaDataGetCrossReference);
3364: socketOutput.writeLongUTF(primaryCatalog);
3365: socketOutput.writeLongUTF(primarySchema);
3366: socketOutput.writeLongUTF(primaryTable);
3367: socketOutput.writeLongUTF(foreignCatalog);
3368: socketOutput.writeLongUTF(foreignSchema);
3369: socketOutput.writeLongUTF(foreignTable);
3370: socketOutput.flush();
3371:
3372: if (sequoiaUrl.isDebugEnabled())
3373: System.out.println(getCurrentMethodName() + "("
3374: + primaryCatalog + "," + primarySchema + ","
3375: + primaryTable + "," + foreignCatalog + ","
3376: + foreignSchema + "," + foreignTable + ")");
3377:
3378: return receiveResultSet(getCurrentMethodName());
3379: } catch (SerializableException e) {
3380: throw new DriverSQLException(e);
3381: } catch (IOException e) {
3382: try {
3383: reconnect();
3384: return getCrossReference(primaryCatalog, primarySchema,
3385: primaryTable, foreignCatalog, foreignSchema,
3386: foreignTable);
3387: } catch (DriverSQLException e1) {
3388: throw new DriverSQLException(
3389: "Connection lost while executing "
3390: + getCurrentMethodName()
3391: + " and automatic reconnect failed ",
3392: e1);
3393: }
3394: }
3395: }
3396:
3397: /**
3398: * @see DatabaseMetaData#getDatabaseProductName()
3399: */
3400: protected synchronized String getDatabaseProductName()
3401: throws DriverSQLException {
3402: throwSQLExceptionIfClosed();
3403: try {
3404: sendCommand(Commands.DatabaseMetaDataGetDatabaseProductName);
3405: socketOutput.flush();
3406:
3407: if (sequoiaUrl.isDebugEnabled())
3408: System.out.println(getCurrentMethodName());
3409:
3410: return receiveStringOrException();
3411: } catch (SerializableException e) {
3412: throw new DriverSQLException(e);
3413: } catch (IOException e) {
3414: try {
3415: reconnect();
3416: return getDatabaseProductName();
3417: } catch (DriverSQLException e1) {
3418: throw new DriverSQLException(
3419: "Connection lost while executing "
3420: + getCurrentMethodName()
3421: + " and automatic reconnect failed ",
3422: e1);
3423: }
3424: }
3425: }
3426:
3427: /**
3428: * @see java.sql.DatabaseMetaData#getExportedKeys(java.lang.String,
3429: * java.lang.String, java.lang.String)
3430: */
3431: protected synchronized ResultSet getExportedKeys(String catalog,
3432: String schema, String table) throws DriverSQLException {
3433: throwSQLExceptionIfClosed();
3434: try {
3435: sendCommand(Commands.DatabaseMetaDataGetExportedKeys);
3436: socketOutput.writeLongUTF(catalog);
3437: socketOutput.writeLongUTF(schema);
3438: socketOutput.writeLongUTF(table);
3439: socketOutput.flush();
3440:
3441: if (sequoiaUrl.isDebugEnabled())
3442: System.out.println(getCurrentMethodName() + "("
3443: + catalog + "," + schema + "," + table + ")");
3444:
3445: return receiveResultSet(getCurrentMethodName());
3446: } catch (SerializableException e) {
3447: throw new DriverSQLException(e);
3448: } catch (IOException e) {
3449: try {
3450: reconnect();
3451: return getExportedKeys(catalog, schema, table);
3452: } catch (DriverSQLException e1) {
3453: throw new DriverSQLException(
3454: "Connection lost while executing "
3455: + getCurrentMethodName()
3456: + " and automatic reconnect failed ",
3457: e1);
3458: }
3459: }
3460: }
3461:
3462: /**
3463: * @see java.sql.DatabaseMetaData#getImportedKeys(java.lang.String,
3464: * java.lang.String, java.lang.String)
3465: */
3466: protected synchronized ResultSet getImportedKeys(String catalog,
3467: String schema, String table) throws DriverSQLException {
3468: throwSQLExceptionIfClosed();
3469: try {
3470: sendCommand(Commands.DatabaseMetaDataGetImportedKeys);
3471: socketOutput.writeLongUTF(catalog);
3472: socketOutput.writeLongUTF(schema);
3473: socketOutput.writeLongUTF(table);
3474: socketOutput.flush();
3475:
3476: if (sequoiaUrl.isDebugEnabled())
3477: System.out.println(getCurrentMethodName() + "("
3478: + catalog + "," + schema + "," + table + ")");
3479:
3480: return receiveResultSet(getCurrentMethodName());
3481: } catch (SerializableException e) {
3482: throw new DriverSQLException(e);
3483: } catch (IOException e) {
3484: try {
3485: reconnect();
3486: return getImportedKeys(catalog, schema, table);
3487: } catch (DriverSQLException e1) {
3488: throw new DriverSQLException(
3489: "Connection lost while executing "
3490: + getCurrentMethodName()
3491: + " and automatic reconnect failed ",
3492: e1);
3493: }
3494: }
3495: }
3496:
3497: /**
3498: * @see java.sql.DatabaseMetaData#getIndexInfo(java.lang.String,
3499: * java.lang.String, java.lang.String, boolean, boolean)
3500: */
3501: protected synchronized ResultSet getIndexInfo(String catalog,
3502: String schema, String table, boolean unique,
3503: boolean approximate) throws DriverSQLException {
3504: throwSQLExceptionIfClosed();
3505: try {
3506: sendCommand(Commands.DatabaseMetaDataGetIndexInfo);
3507: socketOutput.writeLongUTF(catalog);
3508: socketOutput.writeLongUTF(schema);
3509: socketOutput.writeLongUTF(table);
3510: socketOutput.writeBoolean(unique);
3511: socketOutput.writeBoolean(approximate);
3512: socketOutput.flush();
3513:
3514: if (sequoiaUrl.isDebugEnabled())
3515: System.out.println(getCurrentMethodName() + "("
3516: + catalog + "," + schema + "," + table + ","
3517: + unique + "," + approximate + ")");
3518:
3519: return receiveResultSet(getCurrentMethodName());
3520: } catch (SerializableException e) {
3521: throw new DriverSQLException(e);
3522: } catch (IOException e) {
3523: try {
3524: reconnect();
3525: return getIndexInfo(catalog, schema, table, unique,
3526: approximate);
3527: } catch (DriverSQLException e1) {
3528: throw new DriverSQLException(
3529: "Connection lost while executing "
3530: + getCurrentMethodName()
3531: + " and automatic reconnect failed ",
3532: e1);
3533: }
3534: }
3535: }
3536:
3537: /**
3538: * @param sqlTemplate sql template of the PreparedStatement
3539: * @see java.sql.PreparedStatement#getMetaData()
3540: */
3541: protected synchronized ResultSetMetaData preparedStatementGetMetaData(
3542: String sqlTemplate) throws DriverSQLException {
3543: throwSQLExceptionIfClosed();
3544: try {
3545: sendCommand(Commands.PreparedStatementGetMetaData);
3546: socketOutput.writeLongUTF(sqlTemplate);
3547: socketOutput.flush();
3548:
3549: if (sequoiaUrl.isDebugEnabled())
3550: System.out.println(getCurrentMethodName() + "()");
3551:
3552: return receiveResultSet(getCurrentMethodName())
3553: .getMetaData();
3554: } catch (SerializableException e) {
3555: throw new DriverSQLException(e);
3556: } catch (SQLException e) {
3557: throw new DriverSQLException(e);
3558: } catch (IOException e) {
3559: try {
3560: reconnect();
3561: return preparedStatementGetMetaData(sqlTemplate);
3562: } catch (DriverSQLException e1) {
3563: throw new DriverSQLException(
3564: "Connection lost while executing "
3565: + getCurrentMethodName()
3566: + " and automatic reconnect failed ",
3567: e1);
3568: }
3569: }
3570: }
3571:
3572: /**
3573: * @see java.sql.DatabaseMetaData#getPrimaryKeys(java.lang.String,
3574: * java.lang.String, java.lang.String)
3575: */
3576: protected synchronized ResultSet getPrimaryKeys(String catalog,
3577: String schemaPattern, String tableNamePattern)
3578: throws DriverSQLException {
3579: throwSQLExceptionIfClosed();
3580: try {
3581: sendCommand(Commands.DatabaseMetaDataGetPrimaryKeys);
3582: socketOutput.writeLongUTF(catalog);
3583: socketOutput.writeLongUTF(schemaPattern);
3584: socketOutput.writeLongUTF(tableNamePattern);
3585: socketOutput.flush();
3586:
3587: if (sequoiaUrl.isDebugEnabled())
3588: System.out.println(getCurrentMethodName() + "("
3589: + catalog + "," + schemaPattern + ","
3590: + tableNamePattern + ")");
3591:
3592: return receiveResultSet(getCurrentMethodName());
3593: } catch (SerializableException e) {
3594: throw new DriverSQLException(e);
3595: } catch (IOException e) {
3596: try {
3597: reconnect();
3598: return getPrimaryKeys(catalog, schemaPattern,
3599: tableNamePattern);
3600: } catch (DriverSQLException e1) {
3601: throw new DriverSQLException(
3602: "Connection lost while executing "
3603: + getCurrentMethodName()
3604: + " and automatic reconnect failed ",
3605: e1);
3606: }
3607: }
3608: }
3609:
3610: protected synchronized java.sql.ResultSet getProcedures(
3611: String catalog, String schemaPattern,
3612: String procedureNamePattern) throws DriverSQLException {
3613: throwSQLExceptionIfClosed();
3614: try {
3615: sendCommand(Commands.DatabaseMetaDataGetProcedures);
3616: socketOutput.writeLongUTF(catalog);
3617: socketOutput.writeLongUTF(schemaPattern);
3618: socketOutput.writeLongUTF(procedureNamePattern);
3619: socketOutput.flush();
3620:
3621: if (sequoiaUrl.isDebugEnabled())
3622: System.out.println(getCurrentMethodName() + "("
3623: + catalog + "," + schemaPattern + ","
3624: + procedureNamePattern + ")");
3625:
3626: return receiveResultSet(getCurrentMethodName());
3627: } catch (SerializableException e) {
3628: throw new DriverSQLException(e);
3629: } catch (IOException e) {
3630: try {
3631: reconnect();
3632: return getProcedures(catalog, schemaPattern,
3633: procedureNamePattern);
3634: } catch (DriverSQLException e1) {
3635: throw new DriverSQLException(
3636: "Connection lost while executing "
3637: + getCurrentMethodName()
3638: + " and automatic reconnect failed ",
3639: e1);
3640: }
3641: }
3642: }
3643:
3644: protected synchronized java.sql.ResultSet getProcedureColumns(
3645: String catalog, String schemaPattern,
3646: String procedureNamePattern, String columnNamePattern)
3647: throws DriverSQLException {
3648: throwSQLExceptionIfClosed();
3649: try {
3650: sendCommand(Commands.DatabaseMetaDataGetProcedureColumns);
3651: socketOutput.writeLongUTF(catalog);
3652: socketOutput.writeLongUTF(schemaPattern);
3653: socketOutput.writeLongUTF(procedureNamePattern);
3654: socketOutput.writeLongUTF(columnNamePattern);
3655: socketOutput.flush();
3656:
3657: if (sequoiaUrl.isDebugEnabled())
3658: System.out.println(getCurrentMethodName() + "("
3659: + catalog + "," + schemaPattern + ","
3660: + procedureNamePattern + ","
3661: + columnNamePattern + ")");
3662:
3663: return receiveResultSet(getCurrentMethodName());
3664: } catch (SerializableException e) {
3665: throw new DriverSQLException(e);
3666: } catch (IOException e) {
3667: try {
3668: reconnect();
3669: return getProcedureColumns(catalog, schemaPattern,
3670: procedureNamePattern, columnNamePattern);
3671: } catch (DriverSQLException e1) {
3672: throw new DriverSQLException(
3673: "Connection lost while executing "
3674: + getCurrentMethodName()
3675: + " and automatic reconnect failed ",
3676: e1);
3677: }
3678: }
3679: }
3680:
3681: /**
3682: * @see java.sql.DatabaseMetaData#getSchemas()
3683: */
3684: protected synchronized ResultSet getSchemas()
3685: throws DriverSQLException {
3686: throwSQLExceptionIfClosed();
3687:
3688: try {
3689: sendCommand(Commands.DatabaseMetaDataGetSchemas);
3690: socketOutput.flush();
3691:
3692: if (sequoiaUrl.isDebugEnabled())
3693: System.out.println(getCurrentMethodName());
3694:
3695: return receiveResultSet(getCurrentMethodName());
3696: } catch (SerializableException e) {
3697: throw new DriverSQLException(e);
3698: } catch (IOException e) {
3699: try {
3700: reconnect();
3701: return getSchemas();
3702: } catch (DriverSQLException e1) {
3703: throw new DriverSQLException(
3704: "Connection lost while executing "
3705: + getCurrentMethodName()
3706: + " and automatic reconnect failed ",
3707: e1);
3708: }
3709: }
3710: }
3711:
3712: /**
3713: * @see java.sql.DatabaseMetaData#getSuperTables(java.lang.String,
3714: * java.lang.String, java.lang.String)
3715: */
3716: protected synchronized ResultSet getSuperTables(String catalog,
3717: String schemaPattern, String tableNamePattern)
3718: throws DriverSQLException {
3719: throwSQLExceptionIfClosed();
3720: try {
3721: sendCommand(Commands.DatabaseMetaDataGetSuperTables);
3722: socketOutput.writeLongUTF(catalog);
3723: socketOutput.writeLongUTF(schemaPattern);
3724: socketOutput.writeLongUTF(tableNamePattern);
3725: socketOutput.flush();
3726:
3727: if (sequoiaUrl.isDebugEnabled())
3728: System.out.println(getCurrentMethodName() + "("
3729: + catalog + "," + schemaPattern + ","
3730: + tableNamePattern + ")");
3731:
3732: return receiveResultSet(getCurrentMethodName());
3733: } catch (SerializableException e) {
3734: throw new DriverSQLException(e);
3735: } catch (IOException e) {
3736: try {
3737: reconnect();
3738: return getSuperTables(catalog, schemaPattern,
3739: tableNamePattern);
3740: } catch (DriverSQLException e1) {
3741: throw new DriverSQLException(
3742: "Connection lost while executing "
3743: + getCurrentMethodName()
3744: + " and automatic reconnect failed ",
3745: e1);
3746: }
3747: }
3748: }
3749:
3750: /**
3751: * @see java.sql.DatabaseMetaData#getSuperTypes(java.lang.String,
3752: * java.lang.String, java.lang.String)
3753: */
3754: protected synchronized ResultSet getSuperTypes(String catalog,
3755: String schemaPattern, String typeNamePattern)
3756: throws DriverSQLException {
3757: throwSQLExceptionIfClosed();
3758: try {
3759: sendCommand(Commands.DatabaseMetaDataGetSuperTypes);
3760: socketOutput.writeLongUTF(catalog);
3761: socketOutput.writeLongUTF(schemaPattern);
3762: socketOutput.writeLongUTF(typeNamePattern);
3763: socketOutput.flush();
3764:
3765: if (sequoiaUrl.isDebugEnabled())
3766: System.out.println(getCurrentMethodName() + "("
3767: + catalog + "," + schemaPattern + ","
3768: + typeNamePattern + ")");
3769:
3770: return receiveResultSet(getCurrentMethodName());
3771: } catch (SerializableException e) {
3772: throw new DriverSQLException(e);
3773: } catch (IOException e) {
3774: try {
3775: reconnect();
3776: return getSuperTypes(catalog, schemaPattern,
3777: typeNamePattern);
3778: } catch (DriverSQLException e1) {
3779: throw new DriverSQLException(
3780: "Connection lost while executing getSuperTypes and automatic reconnect failed ",
3781: e1);
3782: }
3783: }
3784: }
3785:
3786: /**
3787: * Retrieve a static metadata from the controller.
3788: *
3789: * @param key the "getXXX(Y,Z,...)" hash key of the metadata query
3790: * @return an Object that will be an <tt>Integer</tt> or <tt>Boolean</tt>
3791: * or <tt>String</tt>
3792: * @throws DriverSQLException if fails
3793: * @see org.continuent.sequoia.controller.virtualdatabase.VirtualDatabaseWorkerThread#databaseStaticMetadata()
3794: * @see org.continuent.sequoia.controller.backend.DatabaseBackendMetaData#retrieveDatabaseMetadata()
3795: */
3796: protected synchronized Object getStaticMetadata(String key)
3797: throws DriverSQLException {
3798: throwSQLExceptionIfClosed();
3799: try {
3800: sendCommand(Commands.DatabaseStaticMetadata);
3801: socketOutput.writeLongUTF(key);
3802: socketOutput.flush();
3803: if (sequoiaUrl.isDebugEnabled())
3804: System.out.println("Getting " + key + " metadata");
3805:
3806: TypeTag tag = new TypeTag(socketInput);
3807:
3808: if (TypeTag.EXCEPTION.equals(tag))
3809: throw new DriverSQLException(receiveException());
3810: else {
3811: tag = new TypeTag(socketInput);
3812: Object result = SQLDataSerialization.getSerializer(tag)
3813: .receiveFromStream(socketInput);
3814: return result;
3815: }
3816: } catch (IOException e) {
3817: try {
3818: reconnect();
3819: return getStaticMetadata(key);
3820: } catch (DriverSQLException e1) {
3821: throw new DriverSQLException(
3822: "Connection lost while executing "
3823: + getCurrentMethodName()
3824: + " and automatic reconnect failed ",
3825: e1);
3826: }
3827: }
3828: }
3829:
3830: /**
3831: * Gets a description of the access rights for each table available in a
3832: * catalog. Note that a table privilege applies to one or more columns in the
3833: * table. It would be wrong to assume that this priviledge applies to all
3834: * columns (this may be true for some systems but is not true for all.) Only
3835: * privileges matching the schema and table name criteria are returned. They
3836: * are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE.
3837: *
3838: * @param catalog a catalog name; "" retrieves those without a catalog; null
3839: * means drop catalog name from the selection criteria
3840: * @param schemaPattern a schema name pattern; "" retrieves those without a
3841: * schema
3842: * @param tableNamePattern a table name pattern
3843: * @return <code>ResultSet</code> each row is a table privilege description
3844: * @throws DriverSQLException if a database access error occurs
3845: */
3846: protected synchronized ResultSet getTablePrivileges(String catalog,
3847: String schemaPattern, String tableNamePattern)
3848: throws DriverSQLException {
3849: throwSQLExceptionIfClosed();
3850: try {
3851: sendCommand(Commands.DatabaseMetaDataGetTablePrivileges);
3852: socketOutput.writeLongUTF(catalog);
3853: socketOutput.writeLongUTF(schemaPattern);
3854: socketOutput.writeLongUTF(tableNamePattern);
3855: socketOutput.flush();
3856:
3857: if (sequoiaUrl.isDebugEnabled())
3858: System.out.println(getCurrentMethodName() + "("
3859: + catalog + "," + schemaPattern + ","
3860: + tableNamePattern + ")");
3861:
3862: return receiveResultSet(getCurrentMethodName());
3863: } catch (SerializableException e) {
3864: throw new DriverSQLException(e);
3865: } catch (IOException e) {
3866: try {
3867: reconnect();
3868: return getTablePrivileges(catalog, schemaPattern,
3869: tableNamePattern);
3870: } catch (DriverSQLException e1) {
3871: throw new DriverSQLException(
3872: "Connection lost while executing "
3873: + getCurrentMethodName()
3874: + " and automatic reconnect failed ",
3875: e1);
3876: }
3877: }
3878: }
3879:
3880: /**
3881: * @see org.continuent.sequoia.driver.DatabaseMetaData#getTables(String,
3882: * String, String, String[])
3883: */
3884: protected synchronized ResultSet getTables(String catalog,
3885: String schemaPattern, String tableNamePattern,
3886: String[] types) throws DriverSQLException {
3887: throwSQLExceptionIfClosed();
3888: try {
3889: sendCommand(Commands.DatabaseMetaDataGetTables);
3890: socketOutput.writeLongUTF(catalog);
3891: socketOutput.writeLongUTF(schemaPattern);
3892: socketOutput.writeLongUTF(tableNamePattern);
3893:
3894: if (null == types)
3895: socketOutput.writeBoolean(false);
3896: else {
3897: socketOutput.writeBoolean(true);
3898: socketOutput.writeInt(types.length);
3899: for (int i = 0; i < types.length; i++)
3900: socketOutput.writeLongUTF(types[i]);
3901: }
3902: socketOutput.flush();
3903:
3904: if (sequoiaUrl.isDebugEnabled())
3905: System.out.println(getCurrentMethodName() + "("
3906: + catalog + "," + schemaPattern + ","
3907: + tableNamePattern + "," + types + ")");
3908:
3909: return receiveResultSet(getCurrentMethodName());
3910: } catch (SerializableException e) {
3911: throw new DriverSQLException(e);
3912: } catch (IOException e) {
3913: try {
3914: reconnect();
3915: return getTables(catalog, schemaPattern,
3916: tableNamePattern, types);
3917: } catch (DriverSQLException e1) {
3918: throw new DriverSQLException(
3919: "Connection lost while executing "
3920: + getCurrentMethodName()
3921: + " and automatic reconnect failed ",
3922: e1);
3923: }
3924: }
3925: }
3926:
3927: /**
3928: * Gets the table types available in this database. The results are ordered by
3929: * table type.
3930: *
3931: * @return <code>ResultSet</code> each row has a single String column that
3932: * is a catalog name
3933: * @throws SQLException if a database error occurs
3934: */
3935: protected synchronized ResultSet getTableTypes()
3936: throws SQLException {
3937: throwSQLExceptionIfClosed();
3938: try {
3939: sendCommand(Commands.DatabaseMetaDataGetTableTypes);
3940: socketOutput.flush();
3941:
3942: if (sequoiaUrl.isDebugEnabled())
3943: System.out.println(getCurrentMethodName());
3944:
3945: return receiveResultSet(getCurrentMethodName());
3946: } catch (SerializableException e) {
3947: throw new DriverSQLException(e);
3948: } catch (IOException e) {
3949: try {
3950: reconnect();
3951: return getTableTypes();
3952: } catch (DriverSQLException e1) {
3953: throw new DriverSQLException(
3954: "Connection lost while executing "
3955: + getCurrentMethodName()
3956: + " and automatic reconnect failed ",
3957: e1);
3958: }
3959: }
3960: }
3961:
3962: /**
3963: * @see java.sql.DatabaseMetaData#getTypeInfo()
3964: */
3965: protected synchronized ResultSet getTypeInfo()
3966: throws DriverSQLException {
3967: throwSQLExceptionIfClosed();
3968: try {
3969: sendCommand(Commands.DatabaseMetaDataGetTypeInfo);
3970: socketOutput.flush();
3971:
3972: if (sequoiaUrl.isDebugEnabled())
3973: System.out.println(getCurrentMethodName() + "()");
3974:
3975: return receiveResultSet(getCurrentMethodName());
3976: } catch (SerializableException e) {
3977: throw new DriverSQLException(e);
3978: } catch (IOException e) {
3979: try {
3980: reconnect();
3981: return getTypeInfo();
3982: } catch (DriverSQLException e1) {
3983: throw new DriverSQLException(
3984: "Connection lost while executing "
3985: + getCurrentMethodName()
3986: + " and automatic reconnect failed ",
3987: e1);
3988: }
3989: }
3990: }
3991:
3992: /**
3993: * @see java.sql.DatabaseMetaData#getUDTs(java.lang.String, java.lang.String,
3994: * java.lang.String, int[])
3995: */
3996: protected synchronized ResultSet getUDTs(String catalog,
3997: String schemaPattern, String typeNamePattern, int[] types)
3998: throws DriverSQLException {
3999: throwSQLExceptionIfClosed();
4000: try {
4001: sendCommand(Commands.DatabaseMetaDataGetUDTs);
4002: socketOutput.writeLongUTF(catalog);
4003: socketOutput.writeLongUTF(schemaPattern);
4004: socketOutput.writeLongUTF(typeNamePattern);
4005:
4006: if (null == types)
4007: socketOutput.writeBoolean(false);
4008: else {
4009: socketOutput.writeBoolean(true);
4010: socketOutput.writeInt(types.length);
4011: for (int i = 0; i < types.length; i++)
4012: socketOutput.writeInt(types[i]);
4013: }
4014: socketOutput.flush();
4015:
4016: if (sequoiaUrl.isDebugEnabled())
4017: System.out.println(getCurrentMethodName() + "("
4018: + catalog + "," + schemaPattern + ","
4019: + typeNamePattern + "," + types + ")");
4020:
4021: return receiveResultSet(getCurrentMethodName());
4022: } catch (SerializableException e) {
4023: throw new DriverSQLException(e);
4024: } catch (IOException e) {
4025: try {
4026: reconnect();
4027: return getUDTs(catalog, schemaPattern, typeNamePattern,
4028: types);
4029: } catch (DriverSQLException e1) {
4030: throw new DriverSQLException(
4031: "Connection lost while executing "
4032: + getCurrentMethodName()
4033: + " and automatic reconnect failed ",
4034: e1);
4035: }
4036: }
4037: }
4038:
4039: /**
4040: * @see java.sql.DatabaseMetaData#getVersionColumns(java.lang.String,
4041: * java.lang.String, java.lang.String)
4042: */
4043: protected synchronized ResultSet getVersionColumns(String catalog,
4044: String schema, String table) throws DriverSQLException {
4045: throwSQLExceptionIfClosed();
4046: try {
4047: sendCommand(Commands.DatabaseMetaDataGetVersionColumns);
4048: socketOutput.writeLongUTF(catalog);
4049: socketOutput.writeLongUTF(schema);
4050: socketOutput.writeLongUTF(table);
4051: socketOutput.flush();
4052:
4053: if (sequoiaUrl.isDebugEnabled())
4054: System.out.println(getCurrentMethodName() + "("
4055: + catalog + "," + schema + "," + table + ")");
4056:
4057: return receiveResultSet(getCurrentMethodName());
4058: } catch (SerializableException e) {
4059: throw new DriverSQLException(e);
4060: } catch (IOException e) {
4061: try {
4062: reconnect();
4063: return getVersionColumns(catalog, schema, table);
4064: } catch (DriverSQLException e1) {
4065: throw new DriverSQLException(
4066: "Connection lost while executing "
4067: + getCurrentMethodName()
4068: + " and automatic reconnect failed ",
4069: e1);
4070: }
4071: }
4072: }
4073:
4074: /**
4075: * Send "FetchNextResultSetRows" command to controller. Throws an SQL
4076: * exception if controller returns an error; else returns void and lets the
4077: * caller receive its new rows by itself.
4078: *
4079: * @see org.continuent.sequoia.controller.virtualdatabase.VirtualDatabaseWorkerThread#fetchNextResultSetRows()
4080: * @param cursorName name of the ResultSet cursor
4081: * @param fetchSize number of rows to fetch
4082: * @throws DriverSQLException if an error occurs
4083: */
4084: protected synchronized void tryFetchNext(String cursorName,
4085: int fetchSize) throws DriverSQLException {
4086: throwSQLExceptionIfClosed();
4087: try {
4088: sendCommand(Commands.FetchNextResultSetRows);
4089: socketOutput.writeLongUTF(cursorName);
4090: socketOutput.writeInt(fetchSize);
4091: socketOutput.flush();
4092: if (sequoiaUrl.isDebugEnabled())
4093: System.out.println("Fetching next " + fetchSize
4094: + " from " + cursorName);
4095:
4096: TypeTag tag = new TypeTag(socketInput);
4097:
4098: if (TypeTag.EXCEPTION.equals(tag))
4099: throw new DriverSQLException(receiveException());
4100:
4101: if (!TypeTag.NOT_EXCEPTION.equals(tag))
4102: throw new ProtocolException();
4103:
4104: // success, now we can let the DriverResultSet caller receive its data.
4105:
4106: } catch (IOException e) {
4107: throw wrapIOExceptionInDriverSQLException(
4108: getCurrentMethodName(), e);
4109: }
4110: }
4111:
4112: /**
4113: * Gets the caller's method name.
4114: *
4115: * @return method name of the calling function
4116: */
4117: static String getCurrentMethodName() {
4118: return new Throwable().getStackTrace()[1].getMethodName();
4119: }
4120:
4121: /**
4122: * @see java.lang.Object#toString()
4123: */
4124: public String toString() {
4125: // we could use println() instead of LINE_SEPARATOR here
4126: return "url:"
4127: + getUrl()
4128: + LINE_SEPARATOR
4129: + socket
4130: + LINE_SEPARATOR
4131: + ((sequoiaUrl != null)
4132: && (sequoiaUrl.getParameters() != null) ? "properties:"
4133: + sequoiaUrl.getParameters() + LINE_SEPARATOR
4134: : "") + "user:" + getUserName()
4135: + LINE_SEPARATOR + "connection pooling:"
4136: + connectionPooling + LINE_SEPARATOR
4137: + "escape backslash:" + escapeBackslash
4138: + LINE_SEPARATOR + "escape char:" + escapeChar
4139: + LINE_SEPARATOR + "escape single quote:"
4140: + escapeSingleQuote + LINE_SEPARATOR;
4141: }
4142: }
|