All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.ibm.as400.access.AS400JDBCConnectionImpl Maven / Gradle / Ivy

///////////////////////////////////////////////////////////////////////////////
//
// JTOpen (IBM Toolbox for Java - OSS version)
//
// Filename: AS400JDBCConnection.java
//
// The source code contained herein is licensed under the IBM Public License
// Version 1.0, which has been approved by the Open Source Initiative.
// Copyright (C) 1997-2006 International Business Machines Corporation and
// others. All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.access;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
/* ifdef JDBC40
import java.sql.ClientInfoStatus;
import java.sql.SQLClientInfoException;
import java.sql.SQLPermission;
endif */
import java.sql.Clob;
import java.sql.DatabaseMetaData;
import java.sql.DataTruncation;
/* ifdef JDBC40
import java.sql.NClob;
endif */
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
/* ifdef JDBC40
import java.sql.SQLXML;
endif */
import java.sql.Statement;
import java.sql.Savepoint;                        // @E10a
import java.sql.Struct;
import java.util.Enumeration;               // @DAA
/* ifdef JDBC40
import java.util.HashMap;
endif */
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
/* ifdef JDBC40
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.Executor;
endif */


/**

The AS400JDBCConnection class provides a JDBC connection to a specific DB2 for IBM i database. Use DriverManager.getConnection() to create new AS400JDBCConnection objects.

There are many optional properties that can be specified when the connection is created. Properties can be specified either as part of the URL or in a java.util.Properties object. See JDBC properties for a complete list of properties supported by the AS400JDBCDriver.

Note that a connection may contain at most 9999 open statements. **/ // // Implementation notes: // // 1. Each connection and statement has an "id" associated with // it. All ids are unique within a connection, and this // uniqueness is maintained by the id table for each // connection. // // The id is used as a convention for assigning each // connection and statement its own ORS (Operation Result // Set) on the IBM i as well as assigning each statement // its own RPB (Request Parameter Block). // // Every communication to the database requires a connection // and an id within that connection. // // 2. It is a requirement that no finalize() methods need to // receive a reply from the IBM i system. Because of the way the // AS400Server class is implemented, certain scenarios where // this is the case will result in deadlock. The AS400Server // class provides sendAndDiscardReply() specifically to avoid // this problem. // // Within the JDBC driver, finalize() usually calls one or more // close() methods. Therefore, this requirement is also // imposed on close() methods. // // 3. All requests for the connection and the related objects in // its context should be sent via a variation of one of the // sendXXX() methods. This makes debugging cleaner. // public class AS400JDBCConnectionImpl extends AS400JDBCConnection { private class CancelLock extends Object {} //@C7A private class HeldRequestsLock extends Object {} //@C7A // Turn this flag on to prevent this Connection object from establishing an actual connection to the IBM i system. This is useful when doing multi-threaded stress testing on the Toolbox's built-in JDBC connection pool manager, where we create/delete massive numbers of connections. // For production, this flag _must_ be set to 'false'. private static final boolean TESTING_THREAD_SAFETY = false; //@CPMa // This is a compile time flag for doing simple // communications traces. // // The choices are: // 0 = No communication trace (for production code). // 1 = Only request and reply ids. // 2 = Request and reply ids and contents. // // Note that the LL (length) and parameter count for // requests will not be accurate, since they have not yet // been set at the time when the request is dumped. // private static int DEBUG_COMM_TRACE_ = 0; // This is a compile time flag for temporarily disabling // request chaining. This can be useful when a request // is failing, but all we see is an error class == 7, // return code == -1000. This means a chain request // failed. // // The choices are: // true = Enable request chaining (for production code). // false = Disable request chaining. // // @E5D private static final boolean DEBUG_REQUEST_CHAINING_ = true; // This is a compile time flag for forcing the use of // extended datastream formats. This can be useful when // testing extended formats, but the IBM i system is not reporting // the correct VRM. // // The choices are: // true = Force extended datastream formats. // false = Decide based on system VRM (for production code). // // @E9D private static final boolean FORCE_EXTENDED_FORMATS_ = false; // @F8 -- the key change is to put a 1 in the 7th position. That 1 is the "ODBC" flag. // The IBM i passes it along to database to enable correct package caching of // "where current of" statements. This flag affects only package caching. // The 2nd and 4th digits are used to turn on variable field compression // @L3C private static final String CLIENT_FUNCTIONAL_LEVEL_= "V7R2M01 "; // @EDA F8c H2c pdc 610 private static final int DRDA_SCROLLABLE_CUTOFF_ = 129; // @B1A private static final int DRDA_SCROLLABLE_MAX_ = 255; // @DAA private static final int INITIAL_STATEMENT_TABLE_SIZE_ = 256; // @DAA static final int UNICODE_CCSID_ = 13488; // @E3C // The max number of open statements per connection. If this @DAA // changes, then change the relevant sentence in the javadoc, too. @DAA static final int MAX_STATEMENTS_ = 9999; // @DAC private final boolean[] assigned_ = new boolean[MAX_STATEMENTS_]; //@P0C // Private data. private AS400ImplRemote as400_; private AS400 as400PublicClassObj_; // Prevents garbage collection. //@P0D private BitSet assigned_; // @DAC private boolean cancelling_; // @E8A private CancelLock cancelLock_ = new CancelLock(); // @E8A@C7C private String catalog_; boolean checkStatementHoldability_ = false; // @F3A //@XAC private boolean closing_; // @D4A private boolean aborted_ = false; // @D7A // The aborting thread should be able to close resources when the aborted_ flag is set private Thread abortingThread_ = null; ConvTable converter_; //@P0C private int dataCompression_ = -1; // @ECA private boolean disableCompression_ = false; //@L9A private JDDataSourceURL dataSourceUrl_; private boolean drda_; // @B1A private String defaultSchema_; private boolean extendedFormats_; // @E2D private ConverterImplRemote graphicConverter_; // @E2D private boolean graphicConverterLoaded_; private Vector heldRequests_; // @E5A private HeldRequestsLock heldRequestsLock_ = new HeldRequestsLock(); // @E5A@C7C private int holdability_ = AS400JDBCResultSet.HOLDABILITY_NOT_SPECIFIED; // @G4A private int id_; private AS400JDBCDatabaseMetaData metaData_; private JDPackageManager packageManager_; private JDProperties properties_; // Make this visible to classes that pool @L16C boolean readOnly_; //@P0D private BitSet requestPending_; // @DAC //@P1Dprivate final boolean[] requestPending_ = new boolean[MAX_STATEMENTS_]; //@P0A private AS400Server server_; private int serverFunctionalLevel_; // @E7A private String serverJobIdentifier_ = null; // @E8A private SQLWarning sqlWarning_; private Vector statements_ = new Vector(INITIAL_STATEMENT_TABLE_SIZE_); // @DAC JDTransactionManager transactionManager_; // @E10c static final ConvTable unicodeConverter_ = new ConvTable13488(); // @E3A @P0C ConvTable packageCCSID_Converter = null; //Bidi-HCG int vrm_; // @D0A @E10c private int correlationID_ = 0; //@D2A - only used for multiple receives // declare the user-supplied value for server trace. The constants for // the definition of each bit in the bit map are defined in Trace.java private int traceServer_ = 0; // @j1a // set to true if database host server tracing is started via the setDBHostServerTrace method private boolean databaseHostServerTrace_ = false; // @2KR private boolean mustSpecifyForUpdate_ = true; // @j31 //counter to keep track of number of open statements private int statementCount_ = 0; //@K1A private boolean thousandStatements_ = false; //@K1A private String qaqqiniLibrary_ = null; //@K2A //@KBA Specifies level of autocommit support to use. // If V5R2 or earlier use old support of running SET TRANSACTION STATEMENTS (0) // If "true autocommit" connection property is false - run autocommit under *NONE isolation (1) // If "true autocommit" connection property is true - run with specified isolation (2) int newAutoCommitSupport_ = 1; //@KBA private boolean wrappedInsert_ = false; // @GKA //@pda 550 client info //Names for clientInfo identifiers. DatabaseMetadata also will use these names static final String applicationNamePropertyName_ = "ApplicationName"; static final String clientUserPropertyName_ = "ClientUser"; static final String clientHostnamePropertyName_ = "ClientHostname"; static final String clientAccountingPropertyName_ = "ClientAccounting"; static final String clientProgramIDPropertyName_ = "ClientProgramID"; //@pda //@pda 550 client info values private String applicationName_ = ""; //@pdc so can be added to Properties object in getClientInfo() private String clientUser_ = ""; //@pdc private String clientHostname_ = ""; //@pdc private String clientAccounting_ = ""; //@pdc private String clientProgramID_ = ""; //@pdc private String ignoreWarnings_ = ""; /*@Q1A*/ private int concurrentAccessResolution_ = AS400JDBCDataSource.CONCURRENTACCESS_NOT_SET; //@cc1 private boolean useBlockUpdate_ = false; //@A2A private int maximumBlockedInputRows_ = 32000; //@A6A protected final static int QUERY_TIMEOUT_QQRYTIMLMT = 0; protected final static int QUERY_TIMEOUT_CANCEL = 1; private int queryTimeoutMechanism_ = QUERY_TIMEOUT_QQRYTIMLMT; // @K3 determine variable field compression settings boolean variableFieldCompressionPropertyEvaluated_ = false; boolean useVariableFieldCompression_ = false; boolean useVariableFieldInsertCompression_ = false; // what should truncated query parameters be replaced with // null means that truncated query parameter should not be replaced String queryReplaceTruncatedParameter_ = null ; private static final int CHARACTER_TRUNCATION_DEFAULT = 0; private static final int CHARACTER_TRUNCATION_WARNING = 1; private static final int CHARACTER_TRUNCATION_NONE = 2; private int characterTruncation_ = CHARACTER_TRUNCATION_DEFAULT; private static final int NUMERIC_RANGE_ERROR_DEFAULT = 0; private static final int NUMERIC_RANGE_ERROR_WARNING = 1; private static final int NUMERIC_RANGE_ERROR_NONE = 2; private int numericRangeError_ = NUMERIC_RANGE_ERROR_DEFAULT; String lastServerSQLState_; // Remember the state associated with the connection @Q4A private String alternateServer_ = null ; // Alternate server returned from the host String portNumberString = "*N"; private String systemName_; /** Static initializer. Initializes the reply data streams that we expect to receive. **/ static { // The database server will only return 1 type of reply. //@P0D AS400Server.addReplyStream (new DBReplyRequestedDS (), AS400Server.addReplyStream(DBDSPool.getDBReplyRequestedDS(), //@P0A AS400.DATABASE); } // The default constructor reserved for use within the package. AS400JDBCConnectionImpl () //@A3A { } // @A3D Deleted constructor: // AS400JDBCConnection (JDDataSourceURL dataSourceUrl, JDProperties properties) // throws SQLException // @E8A /** Cancels a statement within this connection. @param id The ID of the statement. @exception SQLException If the statement cannot be executed. **/ public void cancel(int id) throws SQLException { // Lock out all other operations for this connection. synchronized(cancelLock_) { if (TESTING_THREAD_SAFETY) return; // in certain testing modes, don't contact the system cancelling_ = true; AS400JDBCConnectionImpl cancelConnection = null; try { // If the server job identifier was returned, and the system is at a // functional level 5 or greater, then use the job identifier to issue // the cancel from another connection. Otherwise, do nothing. if ((serverJobIdentifier_ != null) && (serverFunctionalLevel_ >= 5)) { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Cancelling statement " + id); // Create another connection to issue the cancel. cancelConnection = new AS400JDBCConnectionImpl(); //AS400 system = new AS400(as400PublicClassObj_); //cancelConnection.setSystem(system); cancelConnection.setProperties(dataSourceUrl_, properties_, as400_, true, /* new server */ false /* skip signon request */ ); // Send the cancel request. DBSQLRequestDS request = null; DBReplyRequestedDS cancelReply = null; try { request = DBDSPool.getDBSQLRequestDS(DBSQLRequestDS.FUNCTIONID_CANCEL, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA, 0); request.setJobIdentifier(serverJobIdentifier_, converter_); cancelReply = cancelConnection.sendAndReceive (request); int errorClass = cancelReply.getErrorClass(); int returnCode = cancelReply.getReturnCode(); if (errorClass != 0) JDError.throwSQLException(this, this, id_, errorClass, returnCode); } catch (DBDataStreamException e) { JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } finally { if (request != null) { request.returnToPool(); request = null; } if (cancelReply != null) { cancelReply.returnToPool(); cancelReply = null; } } } else { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Cancel of statement " + id + " requested, but is not supported by system"); } } finally { // always need to close the connection if (cancelConnection != null) { try { cancelConnection.close(); } catch (Throwable e) {} // ignore any exceptions } // Let others back in. cancelling_ = false; cancelLock_.notifyAll(); } } } /** Checks that the specified SQL statement can be executed. This decision is based on the access specified by the caller and the read only mode. @param sqlStatement The SQL statement. @exception SQLException If the statement cannot be executed. **/ public void checkAccess (JDSQLStatement sqlStatement) throws SQLException { String access = properties_.getString (JDProperties.ACCESS); // If we only have read only access, then anything other // than a SELECT can not be executed. if ((access.equalsIgnoreCase (JDProperties.ACCESS_READ_ONLY)) && (! sqlStatement.isSelect ())) { // Do not throw exception if we have a metadata call @K5A if (! sqlStatement.getIsMetaDataCall()) { JDError.throwSQLException (this, JDError.EXC_ACCESS_MISMATCH); } } // If we have read call access, then anything other than // a SELECT or CALL can not be executed. if (((readOnly_) || ((access.equalsIgnoreCase (JDProperties.ACCESS_READ_CALL)))) && (! sqlStatement.isSelect()) && (! sqlStatement.isProcedureCall())) JDError.throwSQLException (this, JDError.EXC_ACCESS_MISMATCH); } // @E8A /** Checks to see if we are cancelling a statement. If so, wait until the cancel is done. If not, go ahead. **/ public void checkCancel() { synchronized(cancelLock_) { while (cancelling_) { try { cancelLock_.wait(); } catch (InterruptedException e) { // Ignore. } } } } //@F3A /** Checks if what the user passed in for holdability is valid. **/ public boolean checkHoldabilityConstants (int holdability) { if ((holdability == AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT) || (holdability == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT) || (holdability == AS400JDBCResultSet.HOLDABILITY_NOT_SPECIFIED)) { return true; } return false; } /** Checks that the connection is open. Public methods that require an open connection should call this first. @exception SQLException If the connection is not open. **/ public void checkOpen () throws SQLException { if (TESTING_THREAD_SAFETY) return; // in certain testing modes, don't contact IBM i system if ((server_ == null)) JDError.throwSQLException (this, JDError.EXC_CONNECTION_NONE); if (aborted_) { if (Thread.currentThread() != abortingThread_) { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Returning closed since aborted_"); JDError.throwSQLException (this, JDError.EXC_CONNECTION_NONE); } } } /** Clears all warnings that have been reported for the connection. After this call, getWarnings() returns null until a new warning is reported for the connection. @exception SQLException If an error occurs. **/ public void clearWarnings () throws SQLException { sqlWarning_ = null; } /** Releases the connection's resources immediately instead of waiting for them to be automatically released. This rolls back any active transactions, closes all statements that are running in the context of the connection, and disconnects from the IBM i system. @exception SQLException If an error occurs. **/ // // Implementation notes: // // 1. We do not have to worry about thread synchronization here, // since the AS400Server object handles it. // // 2. It is a requirement to not get replies during a finalize() // method. Since finalize() calls this method, this requirement // applies here, too. // public void close () throws SQLException { // @D4A // Avoid recursion. When we close associated statements, they try // to close this connection. if (closing_) return; closing_ = true; // If this is already closed, then just do nothing. // // The spec does not define what happens when a connection // is closed multiple times. The official word from the Sun // JDBC team is that "the driver's behavior in this case // is implementation defined. Applications that do this are // non-portable." if (isClosed ()) return; // partial close (moved rollback and closing of all the statements). @E1 pseudoClose(); // Disconnect from the system. if (server_ != null) { // @B3 It turns out that we were closing the connection, // @B3 then the AS400Server object was in its disconnectServer() // @B3 method. Since the AS400Server object needs to do other // @B3 cleanup, we still need to call it. // @B3D try { // @B3D DBSQLEndCommDS request = new DBSQLEndCommDS ( // @B3D DBSQLEndCommDS.FUNCTIONID_END_COMMUNICATION, // @B3D id_, 0, 0); // @B3D send (request); // @B3D } // @B3D catch (Exception e) { // @B3D JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); // @B3D } as400_.disconnectServer (server_); server_ = null; } if (JDTrace.isTraceOn()) JDTrace.logClose (this); } /* * handle the processing of the abort. @D7A */ public void handleAbort() { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "handleAbort() called"); abortingThread_ = Thread.currentThread(); // Mark all statements as cancelled Vector statements = (Vector)statements_.clone(); Enumeration list = statements.elements(); while (list.hasMoreElements()) { AS400JDBCStatement statement = (AS400JDBCStatement)list.nextElement(); statement.cancelled_ = true; } // Send a cancel to the server. try { cancel(0); } catch (SQLException e ) { // Ingore any errors } closing_ = true; // partial close (moved rollback and closing of all the statements). try { pseudoClose(); } catch (SQLException e) { // Just ignore and continue } // Disconnect from the system. if (server_ != null) { as400_.disconnectServer (server_); server_ = null; } } // @E4C /** Commits all changes made since the previous commit or rollback and releases any database locks currently held by the connection. This has no effect when the connection is in auto-commit mode.

This method can not be called when the connection is part of a distributed transaction. See AS400JDBCXAResource for more information. @exception SQLException If the connection is not open or an error occurs. **/ public void commit () throws SQLException { checkOpen (); if (!transactionManager_.isLocalTransaction()) // @E4A JDError.throwSQLException (this, JDError.EXC_TXN_STATE_INVALID); // @E4A // Note: CPS 72CSHT support if (transactionManager_.getAutoCommit () && properties_.getBoolean(JDProperties.AUTOCOMMIT_EXCEPTION)) //@CE1 JDError.throwSQLException (this, JDError.EXC_FUNCTION_SEQUENCE); //@CE1 // Note: Intuitively, it seems like if we are in // auto-commit mode, that we should not need to // do anything for an explicit commit. However, // somewhere along the line, the system gets // confused, so we go ahead an send the commit // anyway. transactionManager_.commit (); // @F3 If cursor hold property is false, then mark the cursors closed. Don't worry here // @F3 about whether their statement level holdability is different; we will check that // @F3 within AS400JDBCStatement.markCursorsClosed(). // @F3 If the user has changed any statement's holdability, then we need to go through // @F3 the enumeration to see if there are ones where we may need to close our cursors // @F3 or internal result sets. // @F3 Passing true to markCursorsClosed means we called this method from rollback(). if (transactionManager_.getHoldIndicator() == JDTransactionManager.CURSOR_HOLD_FALSE // @B4A || (checkStatementHoldability_ && getVRM() >= JDUtilities.vrm520)) // @F3A markCursorsClosed(false); // @B4A if(!getAutoCommit() && properties_.getBoolean(JDProperties.HOLD_STATEMENTS )) //@KBL if auto commit is off, check to see if any statements have been partially closed //@PDA additional HOLD_STATEMENTS check markStatementsClosed(); //@KBL if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Transaction commit"); } //@F3A /** Sets a flag for whether the user has changed the holdability for any of the statements that came from this connection. As of JDBC 3.0, the user can specify a statement-level holdability that is different from the statement-level holdability. Rather than always going through all of the statements to see if any of their holidabilities is different, we will mark this flag if the user changes any of the statement holdabilities. **/ public void setCheckStatementHoldability(boolean check) { checkStatementHoldability_ = check; } /** Corrects the result set type based on the result set concurrency and posts a warning. @param resultSetType The result set type. @param resultSetConcurrency The result set concurrency. @return The correct result set type. **/ public int correctResultSetType (int resultSetType, int resultSetConcurrency) throws SQLException // @EGA { int newResultSetType = (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE) ? ResultSet.TYPE_SCROLL_SENSITIVE : ResultSet.TYPE_SCROLL_INSENSITIVE; postWarning (JDError.getSQLWarning (JDError.WARN_OPTION_VALUE_CHANGED)); return newResultSetType; } /** Creates a Statement object for executing SQL statements without parameters. If the same SQL statement is executed many times, it is more efficient to use prepareStatement().

Result sets created using the statement will be type ResultSet.TYPE_FORWARD_ONLY and concurrency ResultSet.CONCUR_READ_ONLY. @return The statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, or an error occurs. **/ public Statement createStatement () throws SQLException { return createStatement (this, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, getInternalHoldability()); //@G4C } public Statement createStatement (AS400JDBCConnection con) throws SQLException { return createStatement (con, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, getInternalHoldability()); //@G4C } // JDBC 2.0 /** Creates a Statement object for executing SQL statements without parameters. If the same SQL statement is executed many times, it is more efficient to use prepareStatement(). @param resultSetType The result set type. Valid values are:

  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE
@param resultSetConcurrency The result set concurrency. Valid values are:
  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE
@return The statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, the result type or currency is not supported, or an error occurs. **/ public Statement createStatement (int resultSetType, int resultSetConcurrency) throws SQLException { return createStatement (this, resultSetType, //@G4A resultSetConcurrency, getInternalHoldability()); //@G4A //@G4M Moved code to createStatement (int, int, int) } public Statement createStatement (AS400JDBCConnection con, int resultSetType, int resultSetConcurrency) throws SQLException { return createStatement (con, resultSetType, //@G4A resultSetConcurrency, getInternalHoldability()); //@G4A //@G4M Moved code to createStatement (int, int, int) } //@G4A JDBC 3.0 /** Creates a Statement object for executing SQL statements without parameters. If the same SQL statement is executed many times, it is more efficient to use prepareStatement().

Full functionality of this method requires support in OS/400 V5R2 or IBM i. If connecting to OS/400 V5R1 or earlier, the value for resultSetHoldability will be ignored. @param resultSetType The result set type. Valid values are:

  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE
@param resultSetConcurrency The result set concurrency. Valid values are:
  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE
@param resultSetHoldability The result set holdability. Valid values are:
  • ResultSet.HOLD_CURSORS_OVER_COMMIT
  • ResultSet.CLOSE_CURSORS_AT_COMMIT
@return The statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, the result type, currency, or holdability is not supported, or an error occurs. @since Modification 5 **/ public Statement createStatement (int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return createStatement(this, resultSetType, resultSetConcurrency, resultSetHoldability); } public Statement createStatement (AS400JDBCConnection con, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { // Validation. checkOpen (); if (! metaData_.supportsResultSetConcurrency (resultSetType, resultSetConcurrency)) resultSetType = correctResultSetType (resultSetType, resultSetConcurrency); if (!checkHoldabilityConstants (resultSetHoldability)) //@F3A JDError.throwSQLException (this, JDError.EXC_ATTRIBUTE_VALUE_INVALID); //@F3A // Create the statement. int statementId = getUnusedId (resultSetType); // @B1C AS400JDBCStatement statement = new AS400JDBCStatement (con, statementId, transactionManager_, packageManager_, properties_.getString (JDProperties.BLOCK_CRITERIA), properties_.getInt (JDProperties.BLOCK_SIZE), properties_.getBoolean (JDProperties.PREFETCH), properties_.getString (JDProperties.PACKAGE_CRITERIA), // @A2A resultSetType, resultSetConcurrency, resultSetHoldability, //@G4A AS400JDBCStatement.GENERATED_KEYS_NOT_SPECIFIED); //@G4A statements_.addElement(statement); // @DAC statementCount_++; //@K1A if(thousandStatements_ == false && statementCount_ == 1000) //@K1A { //@K1A thousandStatements_ = true; //@K1A //post warning //@K1A postWarning(JDError.getSQLWarning(JDError.WARN_1000_OPEN_STATEMENTS)); //@K1A } //@K1A if (JDTrace.isTraceOn()) //@F4A { //@F4A int size = statements_.size(); //@F4A if (size % 256 == 0) //@F4A { //@F4A JDTrace.logInformation (this, "Warning: Open handle count now: " + size); //@F4A } //@F4A } //@F4A return statement; } /** Outputs debug information for a request. This should only be used for debugging the JDBC driver and is not intended for production code. @param request The request. **/ public void debug (DBBaseRequestDS request) { if (DEBUG_COMM_TRACE_ >= 1) System.out.println ("Server request: " + Integer.toString (request.getServerID(), 16).toUpperCase() + ":" + Integer.toString (request.getReqRepID(), 16).toUpperCase() + "."); if (DEBUG_COMM_TRACE_ >= 2) request.dump (System.out); } /** Outputs debug information for a reply. This should only be used for debugging the JDBC driver and is not intended for production code. @param reply The reply. **/ public void debug (DBReplyRequestedDS reply) { if (DEBUG_COMM_TRACE_ >= 1) System.out.println ("Server reply: " + Integer.toString (reply.getServerID(), 16).toUpperCase() + ":" + Integer.toString (reply.getReturnDataFunctionId(), 16).toUpperCase() + "."); if (DEBUG_COMM_TRACE_ >= 2) reply.dump (System.out); if (DEBUG_COMM_TRACE_ >= 1) { int returnCode = ((DBReplyRequestedDS) reply).getReturnCode(); int errorClass = ((DBReplyRequestedDS) reply).getErrorClass(); if ((errorClass != 0) || (returnCode != 0)) System.out.println ("Server error = " + errorClass + ":" + returnCode + "."); } } /** Closes the connection if not explicitly closed by the caller. @exception Throwable If an error occurs. **/ public void finalize () throws Throwable { if (! isClosed ()) { JDTrace.logInformation (this, "WARNING: Finalizer thread closing connection object."); close (); } super.finalize (); } /** Returns the AS400 object for this connection. @return The AS400 object. **/ public AS400Impl getAS400 () throws SQLException // @EGA { return as400_; } /** Returns the auto-commit state. @return true if the connection is in auto-commit mode; false otherwise. @exception SQLException If the connection is not open. **/ public boolean getAutoCommit () throws SQLException { checkOpen (); return transactionManager_.getAutoCommit (); } /** Returns the catalog name. @return The catalog name. @exception SQLException If the connection is not open. **/ public String getCatalog () throws SQLException { checkOpen (); return catalog_; } //@cc1 /** * This method returns the concurrent access resolution setting. * This method has no effect on IBM i V6R1 or earlier. * The possible values for this property are {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_NOT_SET}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_USE_CURRENTLY_COMMITTED}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_WAIT_FOR_OUTCOME} and * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_SKIP_LOCKS}, * with the property defaulting to {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_NOT_SET}. * Setting this property to default exhibits the default behavior on the servers * i.e., the semantic applied for read * transactions to avoid locks will be determined by the server. * * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_USE_CURRENTLY_COMMITTED} specifies that driver will flow USE CURRENTLY COMMITTED * to server. Whether CURRENTLY COMMITTED will actually be in effect is * ultimately determined by server. * * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_WAIT_FOR_OUTCOME} specifies that driver will flow WAIT FOR OUTCOME * to server. This will disable the CURRENTLY COMMITTED behavior at the server, * if enabled, and the server will wait for the commit or rollback of data in the process of * being updated. * * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_SKIP_LOCKS} specifies that driver will flow SKIP LOCKS * to server. This directs the database manager to skip records in the case of record lock conflicts. * * @return The concurrent access resolution setting. Possible return valuse: * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_NOT_SET}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_USE_CURRENTLY_COMMITTED}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_WAIT_FOR_OUTCOME}, or * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_SKIP_LOCKS} */ public int getConcurrentAccessResolution () { return concurrentAccessResolution_; } /** Returns the converter for this connection. @return The converter. **/ //@P0D ConverterImplRemote () //@P0D throws SQLException // @EGA //@P0D { //@P0D return converter_; //@P0D } /** Returns the converter for the specified CCSID, unless it is 0 or 65535 (i.e. probably set for a non-text field), in which case it returns the converter for this connection. This is useful for code that handles all types of fields in a generic manner. @param ccsid The CCSID. @return The converter. @exception SQLException If the CCSID is not valid. **/ public ConvTable getConverter (int ccsid) //@P0C throws SQLException { try { if (ccsid == 0 || ccsid == 1 || ccsid == 65535 || ccsid == -1) return converter_; //@P0C //@P0D switch (ccsid) //@P0D { // @E3A //@P0D case 65535: //@ELC // @E3A //@P0D case 0: // @E3A //@P0D case 1: // @E3A //@P0D return converter_; // @E3A //@P0D case UNICODE_CCSID_: // @E3A //@P0D if (unicodeConverter_ == null) // @E3A //@P0D unicodeConverter_ = ConverterImplRemote.getConverter(13488, as400_); // @E3A //@P0D return unicodeConverter_; // @E3A //@P0D default: // @E3A //@P0D return ConverterImplRemote.getConverter (ccsid, as400_); // @E3C //@P0D } // @E3A return ConvTable.getTable(ccsid, null); //@P0A } catch (UnsupportedEncodingException e) { JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); return null; } } // @ECA /** Returns the style of data compression. @return The style of data compression. Possible values are DATA_COMPRESSION_NONE_, DATA_COMPRESSION_OLD_, and DATA_COMPRESSION_RLE_. **/ public int getDataCompression() // @ECA { // @ECA return dataCompression_; // @ECA } // @ECA /** Returns the default SQL schema. @return The default SQL schema, or QGPL if none was specified. **/ public String getDefaultSchema () throws SQLException // @EGA { return((defaultSchema_ == null) ? "QGPL" : defaultSchema_); } //@DELIMa /** Returns the default SQL schema. @param returnRawValue Indicates what to return if default SQL schema has not been set. If true, return raw value; if false, then return QGPL rather than null. @return The default SQL schema. If returnRawValue==false and no default SQL schema was specified, then return QGPL rather than null. **/ public String getDefaultSchema (boolean returnRawValue) throws SQLException { return((returnRawValue || defaultSchema_ != null) ? defaultSchema_ : "QGPL"); } //@G4A JDBC 3.0 /** Returns the holdability of ResultSets created from this connection. @return The cursor holdability. Valid values are ResultSet.HOLD_CURSORS_OVER_COMMIT and ResultSet.CLOSE_CURSORS_AT_COMMIT. The holdability is derived in this order of precedence:
  • 1. The holdability specified using the method setHoldability(int) if this method was called.
  • 2. The value of the cursor hold driver property.
Full functionality of #1 requires support in OS/400 V5R2 or IBM i. If connecting to OS/400 V5R1 or earlier, the value specified on this method will be ignored and the default holdability will be the value of #2. @exception SQLException If the connection is not open. @since Modification 5 **/ public int getHoldability () throws SQLException { checkOpen (); // If holdability has been set, return its value. if ((holdability_ == AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT) || (holdability_ == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT)) { return holdability_; } // Else, holdability either equals AS400JDBCResultSet.HOLDABILITY_NOT_SPECIFIED // or has an incorrect value (shouldn't be able to happen). // Return the holdability determined by seeing what the cursor hold driver property // was set to. Default is HOLD_CURSORS_AT_COMMIT. else { if (transactionManager_.getHoldIndicator() == JDTransactionManager.CURSOR_HOLD_TRUE) return AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT; else if (transactionManager_.getHoldIndicator() == JDTransactionManager.CURSOR_HOLD_FALSE) return AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT; // Hold indicator will be set to -1 if the user gave us a bad number in setHoldIndicator(). // We threw an exception there, so throw another exception here, then return default // value for driver. else { JDError.throwSQLException (this, JDError.EXC_INTERNAL); return AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT; } } } //@DELIMa /** Returns the ID of the connection. @return The connection ID. **/ public int getID() { return id_; } //@G4A JDBC 3.0 /** Returns the holdability of ResultSets created from this connection. Use this method internally to return the value specified if the user has called setHoldability(int), or HOLDABILITY_NOT_SPECIFIED if that method hasn't been called, meaning to use the old behavior and not the new code point for cursor holdability. @return The cursor holdability. Valid values are AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT, AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT, and AS400JDBCResultSet.HOLDABILITY_NOT_SPECIFIED. @since Modification 5 **/ public int getInternalHoldability () { return holdability_; } // @E2D /** // @E2D Returns the graphic converter for this connection. // @E2D // @E2D @return The graphic converter. // @E2D // @E2D @exception SQLException If no graphic converter was loaded. // @E2D **/ // @E2D // // @E2D // Implementation note: // @E2D // // @E2D // * Graphic data is pure double-byte, so we will need a // @E2D // different converter for that. If there is no associated // @E2D // double-byte CCSID, or the converter can not be loaded, // @E2D // then we should throw an exception. We wait to load this, // @E2D // since the majority of callers do not need this converter. // @E2D // // @E2D ConverterImplRemote getGraphicConverter () // @E2D throws SQLException // @E2D { // @E2D // If the graphic converter has not yet been loaded, // @E2D // then do so. // @E2D if (graphicConverterLoaded_ == false) { // @E2D int serverGraphicCCSID = ExecutionEnvironment.getAssociatedDbcsCcsid (converter_.getCcsid ()); // @E2D if (serverGraphicCCSID != -1) { // @E2D try { // @E2D graphicConverter_ = ConverterImplRemote.getConverter (serverGraphicCCSID, as400_); // @E2D } // @E2D catch (UnsupportedEncodingException e) { // @E2D graphicConverter_ = null; // @E2D } // @E2D } // @E2D // @E2D if (JDTrace.isTraceOn ()) { // @E2D if (graphicConverter_ != null) // @E2D JDTrace.logInformation (this, "Server graphic CCSID = " + serverGraphicCCSID); // @E2D else // @E2D JDTrace.logInformation (this, "No graphic CCSID was loaded"); // @E2D } // @E2D } // @E2D // @E2D // Return the graphic converter, or throw an exception. // @E2D if (graphicConverter_ == null) // @E2D JDError.throwSQLException (this, JDError.EXC_CCSID_INVALID); // @E2D return graphicConverter_; // @E2D } /** Returns the DatabaseMetaData object that describes the connection's tables, supported SQL grammar, stored procedures, capabilities and more. @return The metadata object. @exception SQLException If an error occurs. **/ public DatabaseMetaData getMetaData () throws SQLException { // We allow a user to get this object even if the // connection is closed. return metaData_; } /** Returns the connection properties. @return The connection properties. * @throws SQLException If a database error occurs. **/ public JDProperties getProperties () throws SQLException // @EGA { return properties_; } // @E8A /** Returns the job identifier of the host server job corresponding to this connection. Every JDBC connection is associated with a host server job on the IBM i system. The format is:
  • 10 character job name
  • 10 character user name
  • 6 character job number

Note: Since this method is not defined in the JDBC Connection interface, you typically need to cast a Connection object to AS400JDBCConnection in order to call this method:

    String serverJobIdentifier = ((AS400JDBCConnection)connection).getServerJobIdentifier();
    
@return The server job identifier, or null if not known. **/ public String getServerJobIdentifier() // @E8A { // @E8A return serverJobIdentifier_; // @E8A } // @E8A public int getServerFunctionalLevel() // @EEA { // @EEA return serverFunctionalLevel_; // @EEA } // @EEA // @EHA /** Returns the system object which is managing the connection to the system.

Note: Since this method is not defined in the JDBC Connection interface, you typically need to cast a Connection object to AS400JDBCConnection in order to call this method:

    AS400 system = ((AS400JDBCConnection)connection).getSystem();
    
@return The system. **/ // Implementation note: Don't use this object internally because we could be running in a proxy environment // The purpose of this method is to simply hold the full AS400 object so it can be retrieved from the Connection public AS400 getSystem() // @EHA { // @EHA return as400PublicClassObj_; // @EHA } // @EHA /** Returns the transaction isolation level. @return The transaction isolation level. Possible values are:
  • TRANSACTION_NONE
  • TRANSACTION_READ_UNCOMMITTED
  • TRANSACTION_READ_COMMITTED
  • TRANSACTION_REPEATABLE_READ
@exception SQLException If the connection is not open. **/ public int getTransactionIsolation () throws SQLException { checkOpen (); return transactionManager_.getIsolation (); } public JDTransactionManager getTransactionManager() // @E4A { // @E4A return transactionManager_; // @E4A } // @E4A // JDBC 2.0 /** Returns the type map.

This driver does not support the type map. @return The type map. @exception SQLException This exception is always thrown. **/ public Map getTypeMap () throws SQLException { JDError.throwSQLException (this, JDError.EXC_FUNCTION_NOT_SUPPORTED); return null; } // @B1C /** Returns the next unused id. @param resultSetType The result set type. This is relevant only when the connection is being used for DRDA. @return The next unused id. **/ // // Implementation note: This method needs to be synchronized // so that the same id does not get assigned twice. // public int getUnusedId (int resultSetType) //@P0C throws SQLException { synchronized(assigned_) //@P1A { // Note: We will always assume id 0 is being used, // since that represents the connection itself. // If this connection is being used for DRDA, then we // must use statement ids of 1-128 for non-scrollable // cursors and 129-255 for scrollable cursors. if (drda_) { if (resultSetType == ResultSet.TYPE_FORWARD_ONLY) { for (int i = 1; i < DRDA_SCROLLABLE_CUTOFF_; ++i) { //@P0Dif (assigned_.get(i) == false) //@P0D{ // @DAC //@P0D assigned_.set(i); // @DAC //@P0D return i; //@P0D} if (!assigned_[i]) //@P0A { assigned_[i] = true; //@P0A return i; //@P0A } } } else { for (int i = DRDA_SCROLLABLE_CUTOFF_; i < DRDA_SCROLLABLE_MAX_; ++i) { // @DAC //@P0Dif (assigned_.get(i) == false) //@P0D{ // @DAC //@P0D assigned_.set(i); // @DAC //@P0D return i; //@P0D} if (!assigned_[i]) //@P0A { assigned_[i] = true; //@P0A return i; //@P0A } } } } // If this connection is NOT being used for DRDA, then // we can use any statement id. else { for (int i = 1; i < MAX_STATEMENTS_; ++i) { //@P0Dif (assigned_.get(i) == false) //@P0D{ // @DAC //@P0D assigned_.set(i); // @DAC //@P0D return i; //@P0D} if (!assigned_[i]) //@P0A { assigned_[i] = true; //@P0A return i; //@P0A } } } // All ids are being used. JDError.throwSQLException (this, JDError.EXC_MAX_STATEMENTS_EXCEEDED); return -1; } } public // @j31a new method -- Must the user have "for update" on their // SQL statement to guarantee an updatable cursor? The answer is // no for v5r2 and v5r1 systems with a PTF. For V5R1 systems // without the PTF, v4r5, and earlier, the answer is yes. boolean getMustSpecifyForUpdate () { return mustSpecifyForUpdate_; } /** Returns the URL for the connection's database. @return The URL for the database. **/ public String getURL () throws SQLException // @EGA { return dataSourceUrl_.toString (); } /** Returns the user name as currently signed on to the system. @return The user name. **/ public String getUserName () throws SQLException // @EGA { if (TESTING_THREAD_SAFETY) // in certain testing modes, don't contact IBM i system { String userName = as400_.getUserId (); if (userName == null || userName.length() == 0) { userName = as400PublicClassObj_.getUserId(); } return userName; } return as400_.getUserId (); } public int getVRM() // @D0A throws SQLException // @EGA { // @D0A return vrm_; // @D0A } // @D0A /** Returns the first warning reported for the connection. Subsequent warnings may be chained to this warning. @return The first warning or null if no warnings have been reported. @exception SQLException If an error occurs. **/ public SQLWarning getWarnings () throws SQLException { return sqlWarning_; } /** Indicates if the specified cursor name is already used in the connection. @return true if the cursor name is already used; false otherwise. **/ public boolean isCursorNameUsed (String cursorName) throws SQLException // @EGA { // Make a clone of the vector, since it will be modified as each statement @FAA Vector statements = (Vector)statements_.clone(); Enumeration list = statements.elements(); // @DAA while (list.hasMoreElements()) { // @DAC try { if (((AS400JDBCStatement)list.nextElement()).getCursorName().equalsIgnoreCase(cursorName)) // @DAC return true; } catch (Exception e) { /*@FAA */ // ignore any exceptions if (JDTrace.isTraceOn()) { JDTrace.logException(this, "isCursorNameUsed caught exception", e); } } } return false; } /** Indicates if the connection is closed. @return true if the connection is closed; false otherwise. @exception SQLException If an error occurs. **/ public boolean isClosed () throws SQLException { if (aborted_ && (Thread.currentThread() != abortingThread_)) return true; /*@D7A*/ if (TESTING_THREAD_SAFETY) return false; // in certain testing modes, don't contact IBM i system if (server_ == null) // @EFC return true; // @EFA if (!server_.isConnected()) { // @EFA server_ = null; // @EFA return true; // @EFA } // @EFA else // @EFA return false; // @EFA } /** Indicates if the connection is in read-only mode. @return true if the connection is in read-only mode; false otherwise. @exception SQLException If the connection is not open. **/ public boolean isReadOnly () throws SQLException { checkOpen (); return((readOnly_) || isReadOnlyAccordingToProperties()); // @CPMc } // Called by AS400JDBCPooledConnection. public boolean isReadOnlyAccordingToProperties() throws SQLException { checkOpen (); return((properties_.getString (JDProperties.ACCESS).equalsIgnoreCase (JDProperties.ACCESS_READ_ONLY)) || (properties_.getString (JDProperties.ACCESS).equalsIgnoreCase (JDProperties.ACCESS_READ_CALL))); } // @B4A /** Marks all of the cursors as closed. @param isRollback True if we called this from rollback(), false if we called this from commit(). **/ public void markCursorsClosed(boolean isRollback) //@F3C //@XAC throws SQLException //@F2A { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Testing to see if cursors should be held."); //@F3C // Make a clone of the vector, since it will be modified as each statement // closes itself. @FAA Vector statements = (Vector)statements_.clone(); Enumeration list = statements.elements(); // @DAA while (list.hasMoreElements()) // @DAC { //@KBL AS400JDBCStatement statement = (AS400JDBCStatement)list.nextElement(); //@KBL //@KBLD ((AS400JDBCStatement)list.nextElement()).markCursorClosed(isRollback); // @DAC @F3C // If the statement is held open, all of the result sets have already been closed // If we happen to get an exception, just ignore it. There exists a // race condition where another thread could have closed the connected while // we were looping through the statement elements. @F7A try { if(!statement.isHoldStatement()) //@KBL statement.markCursorClosed(isRollback); //@KBL } catch (SQLException ex) { if (JDTrace.isTraceOn()) { JDTrace.logException(this, "markCursorsClosed caught exception", ex); } } } //@KBL } //@KBL /* If a statement associated with locators has been partially closed, finish closing the statement object. A statement may become partially closed if the user closed the statement and set the "hold statements" connection property to true when making the connection. Additionally, the statement must have been used to access a locator. */ public void markStatementsClosed() { if(!statements_.isEmpty()) { // Make a clone of the vector, since it will be modified as each statement // closes itself. // @KBL Close any statements the user called close on that were associated with locators. Vector statements = (Vector)statements_.clone(); Enumeration list = statements.elements(); while (list.hasMoreElements()) { AS400JDBCStatement statement = (AS400JDBCStatement)list.nextElement(); try { if(statement.isHoldStatement()) { statement.setAssociatedWithLocators(false); statement.finishClosing(); } } catch (SQLException e) { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Closing statement after rollback failed: " + e.getMessage()); } } } } //@GKA // Note: This method is used when the user supplies either the column indexes or names // to the execute/executeUpdate/prepareStatement method. /* * Prepares and executes the statement needed to retrieve generated keys. */ public String makeGeneratedKeySelectStatement(String sql, int[] columnIndexes, String[] columnNames) throws SQLException { if(columnIndexes != null) { //verify there is a column index in the specified array if(columnIndexes.length == 0) JDError.throwSQLException(JDError.EXC_ATTRIBUTE_VALUE_INVALID); //Prepare a statement in order to retrieve the column names associated with the indexes specified in the array //wrapper the statement with a select * from final table // @B4C. Use NEW TABLE instead of FINAL TABLE. With FINAL TABLE, the query will fail if // AFTER INSERT TRIGGERS are present. Since it is unlikely that AFTER INSERT triggers will // change the autogenerated keys, NEW TABLE is used. StringBuffer selectAll = new StringBuffer("SELECT * FROM NEW TABLE("); selectAll.append(sql); selectAll.append(")"); PreparedStatement genPrepStat = prepareStatement(selectAll.toString()); // retrieve the JDServerRow object associated with this statement. It contains the column name info. JDServerRow results = ((AS400JDBCPreparedStatement)genPrepStat).getResultRow(); columnNames = new String[columnIndexes.length]; try{ for(int j=0; jResult sets created using the statement will be type ResultSet.TYPE_FORWARD_ONLY and concurrency ResultSet.CONCUR_READ_ONLY. @param sql The SQL stored procedure call. @return The callable statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, or an error occurs. **/ public CallableStatement prepareCall (String sql) throws SQLException { return prepareCall (this, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, getInternalHoldability()); //@G4A } public CallableStatement prepareCall (AS400JDBCConnection con, String sql) throws SQLException { return prepareCall (con, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, getInternalHoldability()); //@G4A } // JDBC 2.0 /** Precompiles an SQL stored procedure call with optional input and output parameters and stores it in a CallableStatement object. This object can be used to efficiently call the SQL stored procedure multiple times. @param sql The SQL statement. @param resultSetType The result set type. Valid values are:

  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE
@param resultSetConcurrency The result set concurrency. Valid values are:
  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE
@return The prepared statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, the result type or currency is not valid, or an error occurs. **/ public CallableStatement prepareCall (String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return prepareCall(this, sql, resultSetType, resultSetConcurrency, getInternalHoldability()); //@G4A //@G4M Moved code below } public CallableStatement prepareCall(AS400JDBCConnection con, String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return prepareCall(con, sql, resultSetType, resultSetConcurrency, getInternalHoldability()); // @G4A // @G4M Moved code below } //@G4A JDBC 3.0 /** Precompiles an SQL stored procedure call with optional input and output parameters and stores it in a CallableStatement object. This object can be used to efficiently call the SQL stored procedure multiple times.

Full functionality of this method requires support in OS/400 V5R2 or IBM i. If connecting to OS/400 V5R1 or earlier, the value for resultSetHoldability will be ignored. @param sql The SQL statement. @param resultSetType The result set type. Valid values are:

  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE
@param resultSetConcurrency The result set concurrency. Valid values are:
  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE
@return The prepared statement object. @param resultSetHoldability The result set holdability. Valid values are:
  • ResultSet.HOLD_CURSORS_OVER_COMMIT
  • ResultSet.CLOSE_CURSORS_AT_COMMIT
@exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, the result type, currency, or holdability is not valid, or an error occurs. @since Modification 5 **/ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return prepareCall(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability); } public CallableStatement prepareCall(AS400JDBCConnection con, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { // Validation. checkOpen (); if (! metaData_.supportsResultSetConcurrency (resultSetType, resultSetConcurrency)) resultSetType = correctResultSetType (resultSetType, resultSetConcurrency); if (!checkHoldabilityConstants(resultSetHoldability)) //@F3A JDError.throwSQLException (this, JDError.EXC_ATTRIBUTE_VALUE_INVALID); //@F3A // Create the statement. JDSQLStatement sqlStatement = new JDSQLStatement (sql, properties_.getString (JDProperties.DECIMAL_SEPARATOR), true, properties_.getString (JDProperties.PACKAGE_CRITERIA), this); // @A2A @G4A int statementId = getUnusedId (resultSetType); // @B1C AS400JDBCCallableStatement statement = new AS400JDBCCallableStatement (con, statementId, transactionManager_, packageManager_, properties_.getString (JDProperties.BLOCK_CRITERIA), properties_.getInt (JDProperties.BLOCK_SIZE), sqlStatement, properties_.getString (JDProperties.PACKAGE_CRITERIA), resultSetType, resultSetConcurrency, resultSetHoldability, //@G4A AS400JDBCStatement.GENERATED_KEYS_NOT_SPECIFIED); //@G4A statements_.addElement(statement); // @DAC statementCount_++; //@K1A if(thousandStatements_ == false && statementCount_ == 1000) //@K1A { //@K1A thousandStatements_ = true; //@K1A //post warning //@K1A postWarning(JDError.getSQLWarning(JDError.WARN_1000_OPEN_STATEMENTS)); //@K1A } //@K1A if (JDTrace.isTraceOn()) //@F4A { //@F4A int size = statements_.size(); //@F4A if (size % 256 == 0) //@F4A { //@F4A JDTrace.logInformation (this, "Warning: Open handle count now: " + size); //@F4A } //@F4A } //@F4A return statement; } /** Precompiles an SQL statement with optional input parameters and stores it in a PreparedStatement object. This object can be used to efficiently execute this SQL statement multiple times.

Result sets created using the statement will be type ResultSet.TYPE_FORWARD_ONLY and concurrency ResultSet.CONCUR_READ_ONLY. @param sql The SQL statement. @return The prepared statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, or an error occurs. **/ public PreparedStatement prepareStatement (String sql) throws SQLException { return prepareStatement(this, sql); } public PreparedStatement prepareStatement (AS400JDBCConnection con, String sql) throws SQLException { return prepareStatement (con, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, getInternalHoldability()); //@G4A } //@G4A //JDBC 3.0 /** Precompiles an SQL statement with optional input parameters and stores it in a PreparedStatement object. This object can be used to efficiently execute this SQL statement multiple times.

This method requires OS/400 V5R2 or IBM i. If connecting to OS/400 V5R1 or earlier, an exception will be thrown.

Result sets created using the statement will be type ResultSet.TYPE_FORWARD_ONLY and concurrency ResultSet.CONCUR_READ_ONLY. @param sql The SQL statement. @param autoGeneratedKeys Whether to return auto generated keys. Valid values are:

  • Statement.RETURN_GENERATED_KEYS
  • Statement.NO_GENERATED_KEYS
@return The prepared statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, if connecting to OS/400 V5R1 or earlier, an error occurs. @since Modification 5 **/ public PreparedStatement prepareStatement (String sql, int autoGeneratedKeys) throws SQLException { return prepareStatement(this, sql, autoGeneratedKeys); } public PreparedStatement prepareStatement (AS400JDBCConnection con, String sql, int autoGeneratedKeys) throws SQLException { if (getVRM() < JDUtilities.vrm520) //@F5A JDError.throwSQLException(this, JDError.EXC_FUNCTION_NOT_SUPPORTED); //@F5A // Validation. checkOpen (); // Create the statement. JDSQLStatement sqlStatement = new JDSQLStatement (sql, properties_.getString (JDProperties.DECIMAL_SEPARATOR), true, properties_.getString (JDProperties.PACKAGE_CRITERIA), con); // @A2A @G4A if(getVRM() >= JDUtilities.vrm610 && autoGeneratedKeys==Statement.RETURN_GENERATED_KEYS) //@GKA added new generated key support { // check if it is an insert statement. // Note: this should be false if the statement was wrappered with a SELECT // when prepareStatement(String sql, int[] columnIndex) or // prepareStatement(String sql, String[] columnNames) was called. if(sqlStatement.isInsert_) { //wrapper the statement String selectStatement = makeGeneratedKeySelectStatement(sql); sqlStatement = new JDSQLStatement (selectStatement, properties_.getString(JDProperties.DECIMAL_SEPARATOR), true, properties_.getString(JDProperties.PACKAGE_CRITERIA), con); wrappedInsert_ = true; } } int statementId = getUnusedId (ResultSet.TYPE_FORWARD_ONLY); // @B1C if(wrappedInsert_) { sqlStatement.setSelectFromInsert(true); wrappedInsert_ = false; } AS400JDBCPreparedStatement statement = new AS400JDBCPreparedStatementImpl (con, statementId, transactionManager_, packageManager_, properties_.getString (JDProperties.BLOCK_CRITERIA), properties_.getInt (JDProperties.BLOCK_SIZE), properties_.getBoolean (JDProperties.PREFETCH), sqlStatement, false, properties_.getString (JDProperties.PACKAGE_CRITERIA), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, getInternalHoldability(), autoGeneratedKeys); //@G4A statements_.addElement(statement); // @DAC statementCount_++; //@K1A if(thousandStatements_ == false && statementCount_ == 1000) //@K1A { //@K1A thousandStatements_ = true; //@K1A //post warning //@K1A postWarning(JDError.getSQLWarning(JDError.WARN_1000_OPEN_STATEMENTS)); //@K1A } //@K1A if (JDTrace.isTraceOn()) //@F4A { //@F4A int size = statements_.size(); //@F4A if (size % 256 == 0) //@F4A { //@F4A JDTrace.logInformation (this, "Warning: Open handle count now: " + size); //@F4A } //@F4A } //@F4A return statement; } // JDBC 2.0 /** Precompiles an SQL statement with optional input parameters and stores it in a PreparedStatement object. This object can be used to efficiently execute this SQL statement multiple times.

Result sets created using the statement will be holdability ResultSet.CLOSE_CURSORS_AT_COMMIT. @param sql The SQL statement. @param resultSetType The result set type. Valid values are:

  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE
@param resultSetConcurrency The result set concurrency. Valid values are:
  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE
@return The prepared statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, the result type or currency is not valid, or an error occurs. **/ public PreparedStatement prepareStatement (String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return prepareStatement (this, sql, resultSetType, //@G4A resultSetConcurrency, getInternalHoldability()); //@G4A //@G4M Moved code to next method. } public PreparedStatement prepareStatement(AS400JDBCConnection con, String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return prepareStatement(con, sql, resultSetType, // @G4A resultSetConcurrency, getInternalHoldability()); // @G4A } //@G4A // JDBC 3.0 /** Precompiles an SQL statement with optional input parameters and stores it in a PreparedStatement object. This object can be used to efficiently execute this SQL statement multiple times. @param sql The SQL statement. @param resultSetType The result set type. Valid values are:
  • ResultSet.TYPE_FORWARD_ONLY
  • ResultSet.TYPE_SCROLL_INSENSITIVE
  • ResultSet.TYPE_SCROLL_SENSITIVE
@param resultSetConcurrency The result set concurrency. Valid values are:
  • ResultSet.CONCUR_READ_ONLY
  • ResultSet.CONCUR_UPDATABLE
@param resultSetHoldability The result set holdability. Valid values are:
  • ResultSet.HOLD_CURSORS_OVER_COMMIT
  • ResultSet.CLOSE_CURSORS_AT_COMMIT
@return The prepared statement object. @exception SQLException If the connection is not open, the maximum number of statements for this connection has been reached, the result type, currency, or holdability is not valid, or an error occurs. **/ public PreparedStatement prepareStatement (String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return prepareStatement(this, sql, resultSetType, resultSetConcurrency, resultSetHoldability); } public PreparedStatement prepareStatement (AS400JDBCConnection con, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { // Validation. checkOpen (); if (! metaData_.supportsResultSetConcurrency (resultSetType, resultSetConcurrency)) resultSetType = correctResultSetType (resultSetType, resultSetConcurrency); if (!checkHoldabilityConstants(resultSetHoldability)) //@F3A JDError.throwSQLException (this, JDError.EXC_ATTRIBUTE_VALUE_INVALID); //@F3A // Create the statement. JDSQLStatement sqlStatement = new JDSQLStatement (sql, properties_.getString (JDProperties.DECIMAL_SEPARATOR), true, properties_.getString (JDProperties.PACKAGE_CRITERIA), con); // @A2A @G4A int statementId = getUnusedId (resultSetType); // @B1C AS400JDBCPreparedStatementImpl statement = new AS400JDBCPreparedStatementImpl (con, statementId, transactionManager_, packageManager_, properties_.getString (JDProperties.BLOCK_CRITERIA), properties_.getInt (JDProperties.BLOCK_SIZE), properties_.getBoolean (JDProperties.PREFETCH), sqlStatement, false, properties_.getString (JDProperties.PACKAGE_CRITERIA), resultSetType, resultSetConcurrency, resultSetHoldability, //@G4A AS400JDBCStatement.GENERATED_KEYS_NOT_SPECIFIED); //@G4A statements_.addElement(statement); // @DAC statementCount_++; //@K1A if(thousandStatements_ == false && statementCount_ == 1000) //@K1A { //@K1A thousandStatements_ = true; //@K1A //post warning //@K1A postWarning(JDError.getSQLWarning(JDError.WARN_1000_OPEN_STATEMENTS)); //@K1A } //@K1A if (JDTrace.isTraceOn()) //@F4A { //@F4A int size = statements_.size(); //@F4A if (size % 256 == 0) //@F4A { //@F4A JDTrace.logInformation (this, "Warning: Open handle count now: " + size); //@F4A } //@F4A } //@F4A return statement; } // @G4 new method /** * Precompiles an SQL statement with optional input parameters * and stores it in a PreparedStatement object. This object can * be used to efficiently execute this SQL statement * multiple times. * *

This method is not supported when connecting to IBM i V5R4 or earlier systems. * * @param sql The SQL statement. * @param columnIndexes An array of column indexes indicating the columns that should be returned from the inserted row or rows. * @return The prepared statement object. * @exception java.sql.SQLException - If connecting to IBM i V5R4 or earlier systems, * the connection is not open, * the maximum number of statements for this connection has been reached, * or an error occurs. * @since Modification 5 **/ public PreparedStatement prepareStatement (String sql, int[] columnIndexes) throws SQLException { return prepareStatement(this, sql, columnIndexes); } public PreparedStatement prepareStatement (AS400JDBCConnection con, String sql, int[] columnIndexes) throws SQLException { if(getVRM() >= JDUtilities.vrm610) //@GKA added support for generated keys { // Validation checkOpen(); //Create a JDSQLStatement JDSQLStatement sqlStatement = new JDSQLStatement (sql, properties_.getString (JDProperties.DECIMAL_SEPARATOR), true, properties_.getString (JDProperties.PACKAGE_CRITERIA), con); //Check if the statement is an insert if(sqlStatement.isInsert_){ wrappedInsert_ = true; return prepareStatement(con, makeGeneratedKeySelectStatement(sql, columnIndexes, null), Statement.RETURN_GENERATED_KEYS); } else // treat like prepareStatement(sql) was called return prepareStatement(con, sql); } else //@GKA Throw an exception. V5R4 and earlier does not support retrieving generated keys by column index. { JDError.throwSQLException (this, JDError.EXC_FUNCTION_NOT_SUPPORTED); return null; } } // @G4 new method /** * Precompiles an SQL statement with optional input parameters * and stores it in a PreparedStatement object. This object can * be used to efficiently execute this SQL statement * multiple times. * *

This method is not supported when connecting to IBM i V5R4 or earlier systems. * * @param sql The SQL statement. * @param columnNames An array of column names indicating the columns that should be returned from the inserted row or rows. * @return The prepared statement object. * @exception java.sql.SQLException - If connecting to IBM i V5R4 or earlier systems, * the connection is not open, * the maximum number of statements for this connection has been reached, * or an error occurs. * @since Modification 5 **/ public PreparedStatement prepareStatement (String sql, String[] columnNames) throws SQLException { return prepareStatement(this, sql, columnNames); } public PreparedStatement prepareStatement (AS400JDBCConnection con, String sql, String[] columnNames) throws SQLException { if(getVRM() >= JDUtilities.vrm610) //@GKA added generated key support { //Validation checkOpen(); //Create a JDSQLStatement JDSQLStatement sqlStatement = new JDSQLStatement (sql, properties_.getString (JDProperties.DECIMAL_SEPARATOR), true, properties_.getString (JDProperties.PACKAGE_CRITERIA), con); //Check if the statement is an insert if(sqlStatement.isInsert_){ wrappedInsert_ = true; return prepareStatement(con, makeGeneratedKeySelectStatement(sql, null, columnNames), Statement.RETURN_GENERATED_KEYS); } else // treat like prepareStatement(sql) was called return prepareStatement(con, sql); } else //@GKA Throw an exception. V5R4 and earlier does not support retrieving generated keys by column name. { JDError.throwSQLException (this, JDError.EXC_FUNCTION_NOT_SUPPORTED); return null; } } //@E10a new method public void processSavepointRequest(String savepointStatement) throws SQLException { // must be OS/400 v5r2 or IBM i if (vrm_ < JDUtilities.vrm520) JDError.throwSQLException(this, JDError.EXC_FUNCTION_NOT_SUPPORTED); // cannot do savepoints on XA transactions if (!transactionManager_.isLocalTransaction()) JDError.throwSQLException (this, JDError.EXC_TXN_STATE_INVALID); // cannot do savepoints if autocommit on if (getAutoCommit()) JDError.throwSQLException(this, JDError.EXC_TXN_STATE_INVALID); Statement statement = null; //@scan1 try{ statement = createStatement(); statement.executeUpdate(savepointStatement); }finally //@scan1 { if(statement != null) //@scan1 statement.close(); } } /** Partial closing of the connection. @exception SQLException If a database error occurs. **/ public void pseudoClose() throws SQLException // @E1 { // Rollback before closing. if ((transactionManager_.isLocalTransaction()) && (transactionManager_.isLocalActive())) // @E4A rollback (); // Close all statements that are running in the context of this connection. // Make a clone of the vector, since it will be modified as each statement @DAA // closes itself. // @DAA // @j4 change -- close may throw a SQLException. Log that error and keep going. // Since the user called close we won't return until we tried to close all // statements. Vector statements = (Vector)statements_.clone(); // @DAA Enumeration list = statements.elements(); // @DAA while (list.hasMoreElements()) // @DAC { // @DAC AS400JDBCStatement statement = (AS400JDBCStatement)list.nextElement(); // @DAA try { // @J4a if(statement.isHoldStatement()) //@KBL user already called close, now completely close it { //@KBL statement.setAssociatedWithLocators(false); //@KBL statement.finishClosing(); //@KBL } //@KBL // @J4a if (! statement.isClosed()) // @DAC statement.close(); // @DAC } // @J4a catch (SQLException e) // @J4a { // @J4a if (JDTrace.isTraceOn()) // @J4a JDTrace.logInformation (this, "Closing statement while closing connection failed: " + e.getMessage()); // @j4a } // @J4a } // @j1a clean up any IBM i debug that is going on. This entire block // is new for @J1 if (traceServer_ > 0 || databaseHostServerTrace_) // @2KRC { // Get the job identifier because we need the id (it is part of some // of our trace files). I know I could have saved it from // the start-trace code but tracing is not performance critical so // why make the object bigger by storing trace stuff as member data. String serverJobIdentifier = getServerJobIdentifier(); // Same for this flag. Don't want to grow the object by saving // this as member data. boolean preV5R1 = true; boolean SQLNaming = properties_.getString(JDProperties.NAMING).equals(JDProperties.NAMING_SQL); try { preV5R1 = getVRM() <= JDUtilities.vrm450; } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end server job tracing failed, could not get server VRM"); } boolean endedTraceJob = false; //@540 Used to determine if ENDTRC has already been done. // End trace-job if ((traceServer_ & ServerTrace.JDBC_TRACE_SERVER_JOB) > 0) { try { if (preV5R1) JDUtilities.runCommand(this, "QSYS/TRCJOB SET(*OFF) OUTPUT(*PRINT)", SQLNaming); else { JDUtilities.runCommand(this, "QSYS/ENDTRC SSNID(QJT" + serverJobIdentifier.substring(20) + ") DTAOPT(*LIB) DTALIB(QUSRSYS) RPLDTA(*YES) PRTTRC(*YES)", SQLNaming ); JDUtilities.runCommand(this, "QSYS/DLTTRC DTAMBR(QJT" + serverJobIdentifier.substring(20) + ") DTALIB(QUSRSYS)", SQLNaming ); } endedTraceJob = true; //@540 } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end server job tracing failed"); } } //@540 End database host server trace job // Database Host Server Trace is supported on V5R3+ if(getVRM() >= JDUtilities.vrm530 && !endedTraceJob) { // Only issue ENDTRC if not already done. if(((traceServer_ & ServerTrace.JDBC_TRACE_DATABASE_HOST_SERVER) > 0) || databaseHostServerTrace_) // @2KRC { // end database host server trace try{ JDUtilities.runCommand(this, "QSYS/ENDTRC SSNID(QJT" + serverJobIdentifier.substring(20) + ") DTAOPT(*LIB) DTALIB(QUSRSYS) RPLDTA(*YES) PRTTRC(*YES)", SQLNaming ); JDUtilities.runCommand(this, "QSYS/DLTTRC DTAMBR(QJT" + serverJobIdentifier.substring(20) + ") DTALIB(QUSRSYS)", SQLNaming ); } catch(Exception e){ JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end database host server tracing failed."); } } } // End debug-job if ((traceServer_ & ServerTrace.JDBC_DEBUG_SERVER_JOB) > 0) { try { JDUtilities.runCommand(this, "QSYS/ENDDBG", SQLNaming); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end server job tracing failed, could not end debug on server job "); } } // End the database monitor if ((traceServer_ & ServerTrace.JDBC_START_DATABASE_MONITOR) > 0) { try { JDUtilities.runCommand(this, "QSYS/ENDDBMON", SQLNaming); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end server job tracing failed, could not end database monitor"); } } // Dump out SQL information if (((traceServer_ & ServerTrace.JDBC_SAVE_SQL_INFORMATION) > 0) && !preV5R1) { try { JDUtilities.runCommand(this, "QSYS/PRTSQLINF *JOB", SQLNaming); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end server job tracing failed, could not print SQL information"); } } // Dump the joblog if ((traceServer_ & ServerTrace.JDBC_SAVE_SERVER_JOBLOG) > 0) { try { JDUtilities.runCommand(this, "QSYS/DSPJOBLOG JOB(*) OUTPUT(*PRINT)", SQLNaming); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end server job tracing failed, could not save job log"); } } // If the user set our flag to turn on client tracing then turn it back off. // This may turn off tracing even though the user wanted it on for some other // reason but there is no way for this code to know why tracing was turned on. // It does know this interface is one reason it is on so we will assume it // is the only reason and will turn it off here. if ((traceServer_ & ServerTrace.JDBC_TRACE_CLIENT) > 0) JDTrace.setTraceOn(false); } } // @E10a new method /** * Removes the given Savepoint object from the current transaction. * Any reference to the savepoint after it has been removed will * cause an SQLException to be thrown. * * @param savepoint the savepoint to be removed. * * @exception SQLException if a database access error occurs or the given Savepoint * is not a valid savepoint in the current transaction. * * @since Modification 5 **/ public void releaseSavepoint(Savepoint savepoint) throws SQLException { if (savepoint == null) throw new NullPointerException("savepoint"); AS400JDBCSavepoint sp = (AS400JDBCSavepoint) savepoint; if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Releasing savepoint " + sp.getName()); if (sp.getStatus() != AS400JDBCSavepoint.ACTIVE) JDError.throwSQLException(this, JDError.EXC_SAVEPOINT_DOES_NOT_EXIST); String SQLCommand = "RELEASE SAVEPOINT " + sp.getName(); processSavepointRequest(SQLCommand); sp.setStatus(AS400JDBCSavepoint.CLOSED); if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Savepoint " + sp.getName() + " released."); } // @E4C /** Drops all changes made since the previous commit or rollback and releases any database locks currently held by the connection. This has no effect when the connection is in auto-commit mode.

This method can not be called when the connection is part of a distributed transaction. See AS400JDBCXAResource for more information. @exception SQLException If the connection is not open or an error occurs. **/ public void rollback () throws SQLException { checkOpen (); if (!transactionManager_.isLocalTransaction()) // @E4A JDError.throwSQLException (this, JDError.EXC_TXN_STATE_INVALID); // @E4A if (transactionManager_.getAutoCommit () && properties_.getBoolean(JDProperties.AUTOCOMMIT_EXCEPTION)) JDError.throwSQLException (this, JDError.EXC_FUNCTION_SEQUENCE); if (! transactionManager_.getAutoCommit ()) { transactionManager_.rollback (); // @F3 Mark all cursors closed on a rollback. Don't worry here // @F3 about whether their statement level holdability is different; we will check // @F3 that within Statement.markCursorsClosed(). // @F3D if (transactionManager_.getHoldIndicator() == JDTransactionManager.CURSOR_HOLD_FALSE // @B4A // @F3 Passing true means we called markCursorClosed from rollback. markCursorsClosed(true); // @B4A @F3C if(properties_.getBoolean(JDProperties.HOLD_STATEMENTS )) //@PDA additional HOLD_STATEMENTS check markStatementsClosed(); //@KBL if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Transaction rollback"); } } // @E10 new method /** * Undoes all changes made after the specified Savepoint was set. * * @param savepoint the savepoint to be rolled back to. * * @exception SQLException if a database access error occurs, the Savepoint * is no longer valid, or this Connection * is currently in auto-commit mode. * @since Modification 5 **/ public void rollback(Savepoint savepoint) throws SQLException { if (savepoint == null) throw new NullPointerException("savepoint"); AS400JDBCSavepoint sp = (AS400JDBCSavepoint) savepoint; if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Rollback with savepoint " + sp.getName()); if (sp.getStatus() != AS400JDBCSavepoint.ACTIVE) JDError.throwSQLException(this, JDError.EXC_SAVEPOINT_DOES_NOT_EXIST); String SQLCommand = "ROLLBACK TO SAVEPOINT " + sp.getName(); processSavepointRequest(SQLCommand); sp.setStatus(AS400JDBCSavepoint.CLOSED); if(properties_.getBoolean(JDProperties.HOLD_STATEMENTS )) //@PDA additional HOLD_STATEMENTS check markStatementsClosed(); //@KBL if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Rollback with savepoint " + sp.getName() + " complete."); } /** Sends a request data stream to the system using the connection's id and does not expect a reply. @param request The request. @exception SQLException If an error occurs. **/ // // See implementation notes for sendAndReceive(). // public void send (DBBaseRequestDS request) throws SQLException { send (request, id_, true); } /** Sends a request data stream to the system and does not expect a reply. @param request The request. @param id The id. @exception SQLException If an error occurs. **/ public // // See implementation notes for sendAndReceive(). // void send (DBBaseRequestDS request, int id) throws SQLException { send (request, id, true); } /** Sends a request data stream to the system and does not expect a reply. @param request The request. @param id The id. @param leavePending Indicates if the request should be left pending. This indicates whether or not to base the next request on this one. @exception SQLException If an error occurs. **/ public // // See implementation notes for sendAndReceive(). // void send (DBBaseRequestDS request, int id, boolean leavePending) throws SQLException { checkCancel(); // @E8A checkOpen(); // @W1a try { // Since we are just calling send() (instead of sendAndReceive()), // @EAA // make sure we are not asking the system for a reply. Otherwise, // @EAA // the reply will come back and it will get held in the AS400 // @EAA // read daemon indefinitely - - a memory leak. // @EAA if (JDTrace.isTraceOn()) { // @EAA if (request.getOperationResultBitmap() != 0) // @EAA JDTrace.logInformation (this, "Reply requested but not collected:" + request.getReqRepID()); // @EAA } // @EAA request.setBasedOnORSHandle (0); // @DAC @EKC // DBReplyRequestedDS reply = null; if (dataCompression_ == DATA_COMPRESSION_RLE_ && !disableCompression_) //@L9C { // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REQUEST_RLE_COMPRESSION); // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REPLY_RLE_COMPRESSION); // @ECA request.compress(); // @ECA } // @ECA DataStream actualRequest; // @E5A synchronized(heldRequestsLock_) { // @E5A if (heldRequests_ != null) // @E5A actualRequest = new DBConcatenatedRequestDS(heldRequests_, request); // @E5A else // @E5A actualRequest = request; // @E5A heldRequests_ = null; // @E5A server_.send(actualRequest); // @E5A @F7M //@P1D requestPending_[id] = leavePending; //@P0A @F7M } // @E5A // @E5D if (DEBUG_REQUEST_CHAINING_ == true) { //@P0D if (leavePending) // @DAA //@P0D requestPending_.set(id); // @DAC //@P0D else // @DAA //@P0D requestPending_.clear(id); // @DAA // @E5D } // @E5D else { // @E5D request.addOperationResultBitmap (DBBaseRequestDS.ORS_BITMAP_RETURN_DATA); // @E5D reply = (DBReplyRequestedDS) server_.sendAndReceive (request); // @E5D requestPending_[id] = false; // @E5D } if (DEBUG_COMM_TRACE_ > 0) { debug (request); // @E5D if (DEBUG_REQUEST_CHAINING_ == false) // @E5D debug (reply); } } // @J5D catch (ConnectionDroppedException e) { // @C1A // @J5D server_ = null; // @D8 // @J5D request.freeCommunicationsBuffer(); // @EMa // @J5D JDError.throwSQLException (this, JDError.EXC_CONNECTION_NONE, e); // @C1A // @J5D } // @C1A catch (IOException e) { // @J5A server_ = null; // @J5A //@P0D request.freeCommunicationsBuffer(); // @J5A JDError.throwSQLException (this, JDError.EXC_COMMUNICATION_LINK_FAILURE, e); // @J5A } // @J5A catch (Exception e) { //@P0D request.freeCommunicationsBuffer(); // @EMa JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } } // @EBD /** // @EBD Sends a request data stream to the system and discards // @EBD the reply. // @EBD // @EBD @param request The request. // @EBD @param id The id. // @EBD @param leavePending Indicates if the request should // @EBD be left pending. This indicates // @EBD whether or not to base the next // @EBD request on this one. // @EBD // @EBD @exception SQLException If an error occurs. // @EBD **/ // @EBD // // @EBD // See implementation notes for sendAndReceive(). // @EBD // // @EBD void sendAndDiscardReply (DBBaseRequestDS request, int id) // @EBD throws SQLException // @EBD { // @EBD checkCancel(); // @E8A // @EBD // @EBD try { // @EBD request.setBasedOnORSHandle (0); //@EKC // @EBD // @EBD DataStream actualRequest; // @E5A // @EBD synchronized(heldRequestsLock_) { // @E5A // @EBD if (heldRequests_ != null) // @E5A // @EBD actualRequest = new DBConcatenatedRequestDS(heldRequests_, request); // @E5A // @EBD else // @E5A // @EBD actualRequest = request; // @E5A // @EBD heldRequests_ = null; // @E5A // @EBD } // @E5A // @EBD // @EBD server_.sendAndDiscardReply(actualRequest); // @E5C // @EBD requestPending_[id] = false; // @EBD // @EBD if (DEBUG_COMM_TRACE_ > 0) // @EBD debug (request); // @EBD } // @EBD catch (ConnectionDroppedException e) { // @C1A // @EBD server_ = null; // @D8 // @EBD JDError.throwSQLException (this, JDError.EXC_CONNECTION_NONE, e); // @C1A // @EBD } // @C1A // @EBD catch (Exception e) { // @EBD JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); // @EBD } // @EBD } // @E5A /** Holds a request until the next explicit request. It will be concatenated at the beginning of the next request. @param request The request. @param id The id. @exception SQLException If an error occurs. **/ // // See implementation notes for sendAndReceive(). // public void sendAndHold(DBBaseRequestDS request, int id) throws SQLException { checkCancel(); // @E8A checkOpen(); // @W1a try { // Since we are just calling send() (instead of sendAndReceive()), // @EAA // make sure we are not asking the system for a reply. Otherwise, // @EAA // the reply will come back and it will get held in the AS400 // @EAA // read daemon indefinitely - - a memory leak. // @EAA if (JDTrace.isTraceOn()) { // @EAA if (request.getOperationResultBitmap() != 0) // @EAA JDTrace.logInformation (this, "Reply requested but not collected:" + request.getReqRepID()); // @EAA } // @EAA request.setBasedOnORSHandle(0); // @DAC @EKC if (dataCompression_ == DATA_COMPRESSION_RLE_ && !disableCompression_) //@L9C { // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REQUEST_RLE_COMPRESSION); // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REPLY_RLE_COMPRESSION); // @ECA request.compress(); // @ECA } // @ECA synchronized(heldRequestsLock_) { if (heldRequests_ == null) heldRequests_ = new Vector(); heldRequests_.addElement(request); } //@P0D requestPending_.set(id); // @DAC //@P1D requestPending_[id] = true; //@P0A if (DEBUG_COMM_TRACE_ > 0) { debug (request); System.out.println("This request was HELD."); } } // Note: No need to check for an IOException in this method, since we don't contact the system. @J5A catch (Exception e) { JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } } /** Sends a request data stream to the system using the connection's id and returns the corresponding reply from the system. @param request The request. @return The reply. @exception SQLException If an error occurs. **/ // // See implementation notes for sendAndReceive(). // public DBReplyRequestedDS sendAndReceive (DBBaseRequestDS request) throws SQLException { return sendAndReceive (request, id_); } /** Sends a request data stream to the system and returns the corresponding reply from the system. @param request The request. @param id The id. @return The reply. @exception SQLException If an error occurs. **/ // // Implementation notes: // // 1. We do not have to worry about thread synchronization // here, since the AS400Server object handles it. // // 2. The based on id is used to chain requests for the // same ORS without needing to get a reply for each one. // If a request fails, then all subsequent requests will // too, and the results from the original failure will // ultimately be returned. // // Initially, the based on id is set to 0. After a // request is sent the based on id is set to the statement's // id, so that subsequent requests will base on this id. // Finally, when a reply is retrieved, the based on id // is reset to 0. // // The status of the based on id depends on whether a // request is pending, which is maintained in the id table. // public DBReplyRequestedDS sendAndReceive (DBBaseRequestDS request, int id) throws SQLException { checkCancel(); // @E8A checkOpen(); // @W1a DBReplyRequestedDS reply = null; try { request.setBasedOnORSHandle (0); // @DAC @EKC if (dataCompression_ == DATA_COMPRESSION_RLE_ && !disableCompression_) //@L9C { // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REQUEST_RLE_COMPRESSION); // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REPLY_RLE_COMPRESSION); // @ECA request.compress(); // @ECA } // @ECA DataStream actualRequest; // @E5A synchronized(heldRequestsLock_) { // @E5A if (heldRequests_ != null) // @E5A actualRequest = new DBConcatenatedRequestDS(heldRequests_, request); // @E5A else // @E5A actualRequest = request; // @E5A heldRequests_ = null; // @E5A reply = (DBReplyRequestedDS)server_.sendAndReceive(actualRequest); // @E5C @F7M //@P0D requestPending_.clear(id); //@P1D requestPending_[id] = false; //@P0A @F7M } // @E5A reply.parse(dataCompression_); // @E5A // @DAC if (DEBUG_COMM_TRACE_ > 0) { debug (request); debug (reply); } } // @J5D catch (ConnectionDroppedException e) { // @C1A // @J5D server_ = null; // @D8 // @J5D request.freeCommunicationsBuffer(); // @EMa // @J5D JDError.throwSQLException (this, JDError.EXC_CONNECTION_NONE, e); // @C1A // @J5D } // @C1A catch (IOException e) { // @J5A server_ = null; // @J5A if (Trace.isTraceErrorOn()) { Trace.log(Trace.ERROR, "Communication Link Failure "); Trace.log(Trace.ERROR, e); Trace.log(Trace.ERROR, "Server job is "+serverJobIdentifier_); if (request != null && request.data_ != null ) { Trace.log(Trace.ERROR,"Request bytes", request.data_); } } //@P0D request.freeCommunicationsBuffer(); // @J5A JDError.throwSQLException (this, JDError.EXC_COMMUNICATION_LINK_FAILURE, e); // @J5A } // @J5A catch (Exception e) { //@P0D request.freeCommunicationsBuffer(); // @EMa if (Trace.isTraceErrorOn()) { Trace.log(Trace.ERROR, "Unexpected exception "); Trace.log(Trace.ERROR, e); Trace.log(Trace.ERROR, "Server job is "+serverJobIdentifier_); if (request != null && request.data_ != null ) { Trace.log(Trace.ERROR,"Request bytes", request.data_); } } else if (JDTrace.isTraceOn()) { JDTrace.logException(this, "Unexpected exception", e); JDTrace.logInformation(this, "Server job is "+serverJobIdentifier_); } JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } // if (DBDSPool.monitor) { // reply.setAllocatedLocation(); // } return(DBReplyRequestedDS) reply; } //@D2A public DBReplyRequestedDS sendAndMultiReceive (DBBaseRequestDS request) throws SQLException { checkCancel(); // @E8A checkOpen(); // @W1a DBReplyRequestedDS reply = null; try { request.setBasedOnORSHandle (0); // @DAC @EKC if (dataCompression_ == DATA_COMPRESSION_RLE_ && !disableCompression_) //@L9C { // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REQUEST_RLE_COMPRESSION); // @ECA request.addOperationResultBitmap(DBBaseRequestDS.ORS_BITMAP_REPLY_RLE_COMPRESSION); // @ECA request.compress(); // @ECA } // @ECA DataStream actualRequest; // @E5A synchronized(heldRequestsLock_) { // @E5A if (heldRequests_ != null) // @E5A actualRequest = new DBConcatenatedRequestDS(heldRequests_, request); // @E5A else // @E5A actualRequest = request; // @E5A heldRequests_ = null; // @E5A //D2A- allowed correlation ID to be stored and used on multiple receive calls correlationID_ = server_.send(actualRequest); reply = (DBReplyRequestedDS)server_.receive(correlationID_); } // @E5A reply.parse(dataCompression_); // @E5A // @DAC if (DEBUG_COMM_TRACE_ > 0) { debug (request); debug (reply); } } // @J5D catch (ConnectionDroppedException e) { // @C1A // @J5D server_ = null; // @D8 // @J5D request.freeCommunicationsBuffer(); // @EMa // @J5D JDError.throwSQLException (this, JDError.EXC_CONNECTION_NONE, e); // @C1A // @J5D } // @C1A catch (IOException e) { // @J5A server_ = null; // @J5A //@P0D request.freeCommunicationsBuffer(); // @J5A JDError.throwSQLException (this, JDError.EXC_COMMUNICATION_LINK_FAILURE, e); // @J5A } // @J5A catch (Exception e) { //@P0D request.freeCommunicationsBuffer(); // @EMa JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } return(DBReplyRequestedDS) reply; } //@DA2 - sew added new receive method. public DBReplyRequestedDS receiveMoreData() throws SQLException{ DBReplyRequestedDS reply = null; try{ if(correlationID_ > 0){ synchronized(heldRequestsLock_) { reply = (DBReplyRequestedDS)server_.receive(correlationID_); } reply.parse(dataCompression_); } }catch (Exception e) { JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } return reply; } // @E4C /** Sets the auto-commit mode. If the connection is in auto-commit mode, then all of its SQL statements are executed and committed as individual transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by either a commit or rollback.

By default, the connection is in auto-commit mode. The commit occurs when the statement execution completes or the next statement execute occurs, whichever comes first. In the case of statements returning a result set, the statement execution completes when the last row of the result set has been retrieved or the result set has been closed. In advanced cases, a single statement may return multiple results as well as output parameter values. Here the commit occurs when all results and output parameter values have been retrieved.

The auto-commit mode is always false when the connection is part of a distributed transaction. See AS400JDBCXAResource for more information. @param autoCommit true to turn on auto-commit mode, false to turn it off. @exception SQLException If the connection is not open or an error occurs. **/ public void setAutoCommit (boolean autoCommit) throws SQLException { checkOpen (); if (TESTING_THREAD_SAFETY) return; // in certain testing modes, don't contact IBM i system transactionManager_.setAutoCommit (autoCommit); if (JDTrace.isTraceOn()) JDTrace.logProperty (this, "setAutoCommit", "Auto commit", transactionManager_.getAutoCommit ()); } /** This method is not supported. @exception SQLException If the connection is not open. **/ public void setCatalog (String catalog) throws SQLException { checkOpen (); // No-op. } //@cc1 /** * This method sets concurrent access resolution. This method overrides the setting of ConcurrentAccessResolution on the datasource or connection * URL properties. This changes the setting for this connection only. This method has no effect on * IBM i V6R1 or earlier. * The possible values for this property are {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_NOT_SET}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_USE_CURRENTLY_COMMITTED}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_WAIT_FOR_OUTCOME} and * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_SKIP_LOCKS}, * with the property defaulting to {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_NOT_SET}. * Setting this property to default exhibits the default behavior on the servers * i.e., the semantic applied for read * transactions to avoid locks will be determined by the server. * * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_USE_CURRENTLY_COMMITTED} specifies that driver will flow USE CURRENTLY COMMITTED * to server. Whether CURRENTLY COMMITTED will actually be in effect is * ultimately determined by server. * * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_WAIT_FOR_OUTCOME} specifies that driver will flow WAIT FOR OUTCOME * to server. This will disable the CURRENTLY COMMITTED behavior at the server, * if enabled, and the server will wait for the commit or rollback of data in the process of * being updated. * * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_SKIP_LOCKS} specifies that driver will flow SKIP LOCKS * to server. This directs the database manager to skip records in the case of record lock conflicts. * * @param concurrentAccessResolution The current access resolution setting. Possible valuse: * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_NOT_SET}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_USE_CURRENTLY_COMMITTED}, * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_WAIT_FOR_OUTCOME}, or * {@link com.ibm.as400.access.AS400JDBCDataSource#CONCURRENTACCESS_SKIP_LOCKS} * @throws SQLException If a database error occurs. */ public void setConcurrentAccessResolution (int concurrentAccessResolution) throws SQLException { DBSQLAttributesDS request = null; DBReplyRequestedDS reply = null; try { if (getVRM() >= JDUtilities.vrm710) { request = DBDSPool.getDBSQLAttributesDS(DBSQLAttributesDS.FUNCTIONID_SET_ATTRIBUTES, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA + DBBaseRequestDS.ORS_BITMAP_SERVER_ATTRIBUTES, 0); //get value of concurrent access resolution. int car = properties_.getInt(JDProperties.CONCURRENT_ACCESS_RESOLUTION); //here, we also allow resetting back to default 0 //pass value of concurrent access resolution into the hostserver request. request.setConcurrentAccessResolution( car ); reply = sendAndReceive(request); int errorClass = reply.getErrorClass(); if (errorClass != 0) JDError.throwSQLException(this, this, id_, errorClass, reply.getReturnCode()); } } catch( Exception e) { JDError.throwSQLException( this, JDError.EXC_INTERNAL, e); } finally { if (request != null) { request.returnToPool(); request=null; } if (reply != null) { reply.returnToPool(); reply = null; // Can return -- only errorClass accessed } } concurrentAccessResolution_ = concurrentAccessResolution; } /** Sets the eWLM Correlator. It is assumed a valid correlator value is used. If the value is null, all ARM/eWLM implementation will be turned off. eWLM correlators require IBM i V5R3 or later systems. This request is ignored when running to OS/400 V5R2 or earlier systems. @param bytes The eWLM correlator value * @throws SQLException If a database error occurs. **/ public void setDB2eWLMCorrelator(byte[] bytes) throws SQLException //@eWLM { if(vrm_ >= JDUtilities.vrm530) { DBSQLAttributesDS request = null; DBReplyRequestedDS reply = null; try { if(bytes == null) { if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "Correlator is null"); } request = DBDSPool.getDBSQLAttributesDS (DBSQLAttributesDS.FUNCTIONID_SET_ATTRIBUTES, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA + DBBaseRequestDS.ORS_BITMAP_SERVER_ATTRIBUTES, 0); request.seteWLMCorrelator(bytes); reply = sendAndReceive(request); int errorClass = reply.getErrorClass(); if(errorClass != 0) JDError.throwSQLException(this, this, id_, errorClass, reply.getReturnCode()); } catch(DBDataStreamException e) { JDError.throwSQLException(JDError.EXC_INTERNAL, e); } finally { if (request != null) { request.returnToPool(); request = null; } if (reply != null) { reply.returnToPool(); reply = null; } // Return to pool since only errorClass accessed } } } // @B1A /** Sets whether the connection is being used for DRDA. @param drda true if the connection is being used for DRDA, false otherwise. **/ public void setDRDA (boolean drda) throws SQLException // @EGA { drda_ = drda; if (JDTrace.isTraceOn()) JDTrace.logProperty (this, "setDRDA", "DRDA", drda_); } //@G4A JDBC 3.0 /** Sets the holdability of ResultSets created from this connection.

Full functionality of this method requires OS/400 V5R2 or IBM i. If connecting to OS/400 V5R1 or earlier, all cursors for the connection will be changed to the value of the variable holdability. @param holdability The cursor holdability. Valid values are ResultSet.HOLD_CURSORS_OVER_COMMIT or ResultSet.CLOSE_CURSORS_AT_COMMIT. @exception SQLException If the connection is not open or the value passed in is not valid. @since Modification 5 **/ public void setHoldability (int holdability) throws SQLException { checkOpen (); if (TESTING_THREAD_SAFETY) return; // in certain testing modes, don't contact IBM i system if (!checkHoldabilityConstants(holdability)) //@F3A JDError.throwSQLException (this, JDError.EXC_ATTRIBUTE_VALUE_INVALID); //@F3A holdability_ = holdability; if (holdability == AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT) //@F5A transactionManager_.setHoldIndicator(JDProperties.CURSORHOLD_FALSE); //@F5A else if (holdability == AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT) //@F5A transactionManager_.setHoldIndicator(JDProperties.CURSORHOLD_TRUE); //@F5A if (JDTrace.isTraceOn()) JDTrace.logProperty (this, "setHoldability", "Holdability", holdability_); } //@D4A public void setProperties (JDDataSourceURL dataSourceUrl, JDProperties properties, AS400 as400, Properties info) throws SQLException { if (TESTING_THREAD_SAFETY) // in certain testing modes, don't contact IBM i system { as400PublicClassObj_ = as400; } else { try { int portNumber = 0; // Check to see if the port property is set. If so, // Then we must set the port in the PortMapper and // tell the as400 object not to use the signon server. // The port in the URL has precedence over the property. /*@V1A*/ if (dataSourceUrl.isPortSpecified()) { portNumber = dataSourceUrl.getPortNumber(); } if (portNumber == 0) { portNumber = properties.getInt(JDProperties.PORTNUMBER); } if (portNumber > 0) { as400.skipSignonServer = true; as400.connectService (AS400.DATABASE, portNumber); portNumberString = ""+portNumber; } else { as400.connectService (AS400.DATABASE); } systemName_ = as400.getSystemName(); } catch (AS400SecurityException e) { //@D5C JDError.throwSQLException (this, JDError.EXC_CONNECTION_REJECTED, e); } catch (IOException e) { //@D5C JDError.throwSQLException (this, JDError.EXC_CONNECTION_UNABLE, e); } finally //@dbldrvr { //@dbldrvr //Since driver is registered twice in DriverManager via DriverManager.registerDriver(new AS400JDBCDriver()), //remove extra driver references now so we don't waste resources by continuing to try, and also so we don't lock out id if pwd is not correct. Enumeration en = DriverManager.getDrivers(); //@dbldrvr Driver firstDriver = null; //@dbldrvr Driver nextDriver = null; //@dbldrvr while (en.hasMoreElements()) //@dbldrvr { //@dbldrvr nextDriver = (Driver) en.nextElement(); //@dbldrvr if(nextDriver instanceof AS400JDBCDriver) //@dbldrvr { //@dbldrvr if(firstDriver == null) //@dbldrvr firstDriver = nextDriver; //@dbldrvr else //@dbldrvr DriverManager.deregisterDriver(nextDriver); //@dbldrvr } //@dbldrvr } //@dbldrvr } //@dbldrvr } setProperties (dataSourceUrl, properties, as400.getImpl(), false, as400.skipSignonServer); } public void setProperties(JDDataSourceURL dataSourceUrl, JDProperties properties, AS400Impl as400) throws SQLException { setProperties(dataSourceUrl, properties, as400, false, false); } /* Should the warning be ignored @Q1A*/ public boolean ignoreWarning(String sqlState) { if (ignoreWarnings_.indexOf(sqlState ) >= 0) { return true; } else { return false; } } public boolean ignoreWarning(SQLWarning warning) { return ignoreWarning(warning.getSQLState()); } //@A3A - This logic formerly resided in the ctor. public void setProperties (JDDataSourceURL dataSourceUrl, JDProperties properties, AS400Impl as400, boolean newServer, boolean skipSignonServer) throws SQLException { // Initialization. as400_ = (AS400ImplRemote) as400; //@A3A //@P0D assigned_ = new BitSet(INITIAL_STATEMENT_TABLE_SIZE_); // @DAC dataSourceUrl_ = dataSourceUrl; extendedFormats_ = false; properties_ = properties; ignoreWarnings_ = properties_.getString(JDProperties.IGNORE_WARNINGS).toUpperCase(); /*@Q1A*/ //Set the real default for METADATA SOURCE property since we now know the hostsrvr version if(properties_.getString(JDProperties.METADATA_SOURCE).equals(JDProperties.METADATA_SOURCE_HOST_VERSION_DEFAULT)) //@mdsp { //@mdsp if(as400_.getVRM() < JDUtilities.vrm710) //@mdsp //@710 take effect after 710 (ie. not 615) properties_.setString(JDProperties.METADATA_SOURCE, JDProperties.METADATA_SOURCE_ROI); //@mdsp else //@mdsp properties_.setString(JDProperties.METADATA_SOURCE, JDProperties.METADATA_SOURCE_STORED_PROCEDURE); //@mdsp } //@mdsp //@P0D requestPending_ = new BitSet(INITIAL_STATEMENT_TABLE_SIZE_); // @DAC if(!TESTING_THREAD_SAFETY && as400_.getVRM() <= JDUtilities.vrm520) //@KBA //if V5R2 or less use old support of issuing set transaction statements newAutoCommitSupport_ = 0; //@KBA else if(!properties_.getBoolean(JDProperties.TRUE_AUTO_COMMIT)) //@KBA //@true //run autocommit with *NONE isolation level newAutoCommitSupport_ = 1; //@KBA else //@KBA newAutoCommitSupport_ = 2; //@KBA //run autocommit with specified isolation level if (as400_.getVRM() >= JDUtilities.vrm710) { useBlockUpdate_ = properties_.getBoolean(JDProperties.USE_BLOCK_UPDATE); //@A2A } maximumBlockedInputRows_ = properties_.getInt(JDProperties.MAXIMUM_BLOCKED_INPUT_ROWS); // @A6A if ( maximumBlockedInputRows_ > 32000 ) maximumBlockedInputRows_ = 32000; // @A6A if ( maximumBlockedInputRows_ < 1 ) maximumBlockedInputRows_ = 1; // @A6A // Issue any warnings. if (dataSourceUrl_.isExtraPathSpecified ()) postWarning (JDError.getSQLWarning (JDError.WARN_URL_EXTRA_IGNORED)); if (properties.isExtraPropertySpecified ()) postWarning (JDError.getSQLWarning (JDError.WARN_PROPERTY_EXTRA_IGNORED)); if (dataSourceUrl_.isPortSpecified ()) { if (dataSourceUrl_.getPortNumber() == 0) { postWarning (JDError.getSQLWarning (JDError.WARN_URL_EXTRA_IGNORED)); } } // Initialize the library list. String urlSchema = dataSourceUrl_.getSchema (); if (urlSchema == null) JDError.throwSQLException (this, JDError.WARN_URL_SCHEMA_INVALID); JDLibraryList libraryList = new JDLibraryList ( properties_.getString (JDProperties.LIBRARIES), urlSchema, properties_.getString (JDProperties.NAMING)); // @B2C defaultSchema_ = libraryList.getDefaultSchema (); // The connection gets an id automatically, but never // creates an RPB on the system. There should never be a need // to create an RPB on the system for a connection, but an // id is needed for retrieving Operational Result Sets (ORS) // for errors, etc. // Initialize a transaction manager for this connection. transactionManager_ = new JDTransactionManager (this, id_, properties_.getString (JDProperties.TRANSACTION_ISOLATION), properties_.getBoolean (JDProperties.AUTO_COMMIT)); //@AC1 transactionManager_.setHoldIndicator(properties_.getString(JDProperties.CURSOR_HOLD)); // @D9 // If the hold properties are specified, make sure they are set locally if (properties_.getString(JDProperties.CURSOR_HOLD) != null) { if (transactionManager_.getHoldIndicator() == JDTransactionManager.CURSOR_HOLD_TRUE) holdability_ = AS400JDBCResultSet.HOLD_CURSORS_OVER_COMMIT; else if (transactionManager_.getHoldIndicator() == JDTransactionManager.CURSOR_HOLD_FALSE) holdability_ = AS400JDBCResultSet.CLOSE_CURSORS_AT_COMMIT; } // Initialize the read-only mode to true if the access // property says read only. readOnly_ = (properties_.equals (JDProperties.ACCESS, JDProperties.ACCESS_READ_ONLY)); // Determine the amount of system tracing that should be started. Trace // can be started by either a JDBC property or the ServerTrace class. Our value // will be the combination of the two (instead of one overriding the other). traceServer_ = properties_.getInt(JDProperties.TRACE_SERVER) | ServerTrace.getJDBCServerTraceCategories(); // @j1a //@SSa logical OR // Determine if a QAQQINI library name was specified. The library can be set using //@K2A // a JDBC property. //@k2A qaqqiniLibrary_ = properties_.getString(JDProperties.QAQQINILIB); //@K2A String queryTimeoutMechanismString = properties_.getString(JDProperties.QUERY_TIMEOUT_MECHANISM); if (queryTimeoutMechanismString != null) { queryTimeoutMechanismString = queryTimeoutMechanismString.trim().toLowerCase(); if (queryTimeoutMechanismString.equals(JDProperties.QUERY_TIMEOUT_MECHANISM_CANCEL)) { queryTimeoutMechanism_ = QUERY_TIMEOUT_CANCEL; } else { queryTimeoutMechanism_ = QUERY_TIMEOUT_QQRYTIMLMT; } } String queryReplaceTruncatedParameterString = properties_.getString(JDProperties.QUERY_REPLACE_TRUNCATED_PARAMETER); if (queryReplaceTruncatedParameterString != null) { queryReplaceTruncatedParameterString = queryReplaceTruncatedParameterString.trim(); if (queryReplaceTruncatedParameterString.equals(JDProperties.QUERY_REPLACE_TRUNCATED_PARAMETER_STRING_DEFAULT)) { queryReplaceTruncatedParameter_ = null; } else { queryReplaceTruncatedParameter_ = queryReplaceTruncatedParameterString; } } // Default value of data truncation is "true" boolean dataTruncation = properties_.getBoolean( JDProperties.DATA_TRUNCATION); // Default value of data truncation is "default" String characterTruncation = properties_.getString( JDProperties.CHARACTER_TRUNCATION); // if characterTruncation is default, then use the dataTruncation setting // otherwise use the characterTruncation setting if (characterTruncation == null || characterTruncation == JDProperties.CHARACTER_TRUNCATION_DEFAULT) { if (dataTruncation) { characterTruncation_ = CHARACTER_TRUNCATION_DEFAULT; } else { characterTruncation_ = CHARACTER_TRUNCATION_NONE; } } else if (characterTruncation.equals(JDProperties.CHARACTER_TRUNCATION_WARNING)) { characterTruncation_ = CHARACTER_TRUNCATION_WARNING; } else if (characterTruncation.equals(JDProperties.CHARACTER_TRUNCATION_NONE)) { characterTruncation_ = CHARACTER_TRUNCATION_NONE; } String numericRangeError = properties_.getString(JDProperties.NUMERIC_RANGE_ERROR); if (numericRangeError == null) numericRangeError = JDProperties.NUMERIC_RANGE_ERROR_DEFAULT; if (numericRangeError.equals(JDProperties.NUMERIC_RANGE_ERROR_DEFAULT)) { numericRangeError_ = NUMERIC_RANGE_ERROR_DEFAULT; } else if (numericRangeError.equals(JDProperties.NUMERIC_RANGE_ERROR_WARNING)) { numericRangeError_ = NUMERIC_RANGE_ERROR_WARNING; } else if (numericRangeError.equals(JDProperties.NUMERIC_RANGE_ERROR_NONE)) { numericRangeError_ = NUMERIC_RANGE_ERROR_NONE; } //@A3D // Initialize the conversation. //open (); //@A3A // Connect. if (JDTrace.isTraceOn()) // @F6a { // @F6a JDTrace.logInformation("Toolbox for Java - " + Copyright.version); // @F6a JDTrace.logInformation("JDBC Level: " + JDUtilities.JDBCLevel_); // @F6a } // @F6a if (!TESTING_THREAD_SAFETY) // in certain testing modes, we don't contact IBM i system { try { server_ = as400_.getConnection (AS400.DATABASE, -1, newServer, skipSignonServer); } catch (AS400SecurityException e) { JDError.throwSQLException (this, JDError.EXC_CONNECTION_REJECTED, e); } catch (IOException e) { JDError.throwSQLException (this, JDError.EXC_CONNECTION_UNABLE, e); } } // Initialize the catalog name at this point to be the system // name. After we exchange attributes, we can change it to // the actual name. catalog_ = dataSourceUrl.getServerName(); // @D7A if (catalog_.length() == 0) // @D7A catalog_ = as400_.getSystemName ().toUpperCase (); // @A3A setServerAttributes (); libraryList.addOnServer (this, id_); boolean useDRDAversion = properties_.getBoolean(JDProperties.USE_DRDA_METADATA_VERSION); // @E7D // Initialize a transaction manager for this connection. Turn on @E7A // @E7D // new auto-commit support when the server functional level is @E7A // @E7D // greater than or equal to 3. @E7A // @E7D boolean newAutoCommitSupport = (serverFunctionalLevel_ >= 3); // @E7A // @E7D transactionManager_.setNewAutoCommitSupport(newAutoCommitSupport); // @E7A // We keep a metadata object around for quick access. // The metadata object should share the id of the // connection, since it operates on a connection-wide // scope. metaData_ = new AS400JDBCDatabaseMetaData (this, id_, useDRDAversion); // The conversation was initialized to a certain // transaction isolation. It is now time to turn on auto- // commit by default. if(newAutoCommitSupport_ == 0) //KBA V5R2 or less so do what we always have transactionManager_.setAutoCommit (true); // Initialize the package manager. packageManager_ = new JDPackageManager (this, id_, properties_, transactionManager_.getCommitMode ()); // Trace messages. if (JDTrace.isTraceOn()) { JDTrace.logOpen (this, null); // @J33a JDTrace.logProperty (this, "setProperties", "Auto commit", transactionManager_.getAutoCommit ()); JDTrace.logProperty (this, "setProperties", "Read only", readOnly_); JDTrace.logProperty (this, "setProperties", "Transaction isolation", transactionManager_.getIsolation ()); if (packageManager_.isEnabled ()) JDTrace.logInformation (this, "SQL package = " + packageManager_.getLibraryName() + "/" + packageManager_.getName ()); } // @j1a Trace the server job if the user asked us to. Tracing // can be turned on via a URL property, the Trace class, the DataSource // object, or a system property. if (traceServer_ > 0) { // Get the server job id. We will both dump this to the trace // and use it to uniquely label some of the files. String serverJobIdentifier = getServerJobIdentifier(); String serverJobId = serverJobIdentifier.substring(20).trim() + "/" + serverJobIdentifier.substring(10, 19).trim() + "/" + serverJobIdentifier.substring( 0, 10).trim(); // Dump the server job id JDTrace.logDataEvenIfTracingIsOff(this, Copyright.version); JDTrace.logDataEvenIfTracingIsOff(this, serverJobId); JDTrace.logDataEvenIfTracingIsOff(this, "Server functional level: " + getServerFunctionalLevel()); // @E7A // Determine system level. Some commands are slightly different // to v5r1 machines. boolean preV5R1 = true; boolean SQLNaming = properties_.getString(JDProperties.NAMING).equals(JDProperties.NAMING_SQL); try { preV5R1 = getVRM() <= JDUtilities.vrm450; } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start server job tracing failed, could not get server VRM"); } // Start client tracing if the flag is on and trace isn't already running if (((traceServer_ & ServerTrace.JDBC_TRACE_CLIENT) > 0) && (! JDTrace.isTraceOn())) JDTrace.setTraceOn(true); // No matter what type of tracing is turned on, alter the server // job so more stuff is saved in the job log. try { JDUtilities.runCommand(this, "QSYS/CHGJOB LOG(4 00 *SECLVL) LOGCLPGM(*YES)", SQLNaming); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start server job tracing failed, could not change log level"); } // Optionally start debug on the database server job if ((traceServer_ & ServerTrace.JDBC_DEBUG_SERVER_JOB) > 0) { try { JDUtilities.runCommand(this, "QSYS/STRDBG UPDPROD(*YES)", SQLNaming); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start server job tracing failed, could not start debug on server job "); } } // Optionally start the database monitor if ((traceServer_ & ServerTrace.JDBC_START_DATABASE_MONITOR) > 0) { try { JDUtilities.runCommand(this, "QSYS/STRDBMON OUTFILE(QUSRSYS/QJT" + serverJobIdentifier.substring(20) + ") JOB(*) TYPE(*DETAIL)", SQLNaming ); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start server job tracing failed, could not start database monitor"); } } boolean traceServerJob = ((traceServer_ & ServerTrace.JDBC_TRACE_SERVER_JOB) > 0); //@540 //@540 Database Host Server Trace is supported on V5R3 and later systems boolean traceDatabaseHostServer = ((getVRM() >= JDUtilities.vrm530) && ((traceServer_ & ServerTrace.JDBC_TRACE_DATABASE_HOST_SERVER) > 0)); //@540 // Optionally start trace on the database server job or database host server //@540D if ((traceServer_ & ServerTrace.JDBC_TRACE_SERVER_JOB) > 0) if(traceServerJob || traceDatabaseHostServer) //@540 { try { if (preV5R1 && traceServerJob) //@540 added check for traceServerJob JDUtilities.runCommand(this, "QSYS/TRCJOB MAXSTG(16000)", SQLNaming); else{ if(!traceDatabaseHostServer){ //@540 trace only server job JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + serverJobIdentifier.substring(20) + ") JOB(*) MAXSTG(128000)", SQLNaming); } else if(!traceServerJob){ //@540 trace only database host server if(getVRM() == JDUtilities.vrm530){ //@540 run command for V5R3 JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + //@540 serverJobIdentifier.substring(20) + //@540 ") JOB(*) MAXSTG(128000) JOBTRCTYPE(*TRCTYPE) " + //@540 "TRCTYPE((TESTA *INFO))", SQLNaming); //@540 } else{ //@540 run command for V5R4 and higher JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + //@540 serverJobIdentifier.substring(20) + //@540 ") JOB(*) MAXSTG(128000) JOBTRCTYPE(*TRCTYPE) " + //@540 "TRCTYPE((*DBHSVR *INFO))", SQLNaming); //@540 } } //@540 else{ //@540 start both server job and database host server trace if(getVRM() == JDUtilities.vrm530){ //@540 run command for V5R3 JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + //@540 serverJobIdentifier.substring(20) + //@540 ") JOB(*) MAXSTG(128000) JOBTRCTYPE(*ALL) " + //@540 "TRCTYPE((TESTA *INFO))", SQLNaming); //@540 } else{ //@540 run V5R4 and higher command JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + //@540 serverJobIdentifier.substring(20) + //@540 ") JOB(*) MAXSTG(128000) JOBTRCTYPE(*ALL) " + //@540 "TRCTYPE((*DBHSVR *INFO))", SQLNaming); //@540 } } } } catch (Exception e) { if(traceServerJob && !traceDatabaseHostServer) //@540 JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start server job tracing failed, could not trace server job"); else if(traceDatabaseHostServer && !traceServerJob) //@540 JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start database host server tracing failed, could not trace server job"); //@540 else //@540 JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start server job and database host server tracing failed, could not trace server job"); //@540 } } } //@K2A Issue Change Query Attributes command if user specified QAQQINI library name if(qaqqiniLibrary_.length() > 0 && !qaqqiniLibrary_.equals("null")) { boolean SQLNaming = properties_.getString(JDProperties.NAMING).equals(JDProperties.NAMING_SQL); try { JDUtilities.runCommand(this, "CHGQRYA QRYOPTLIB(" + qaqqiniLibrary_ + ")", SQLNaming ); } catch (Exception e) { JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to issue Change Query Attributes command using QAQQINI Library name failed."); } } } /** Sets the read-only mode. This will provide read-only access to the database. Read-only mode can be useful by enabling certain database optimizations. If the caller specified "read only" or "read call" for the "access" property, then the read-only mode cannot be set to false. The read-only mode cannot be changed while in the middle of a transaction.

This method can not be called when the connection is part of a distributed transaction. See AS400JDBCXAResource for more information. @exception SQLException If the connection is not open, a transaction is active, or the "access" property is set to "read only". **/ public void setReadOnly (boolean readOnly) throws SQLException { checkOpen (); if (transactionManager_.isLocalActive () || transactionManager_.isGlobalActive()) // @E4C JDError.throwSQLException (this, JDError.EXC_TXN_STATE_INVALID); // @E4C if ((readOnly == false) && ((properties_.getString (JDProperties.ACCESS).equalsIgnoreCase (JDProperties.ACCESS_READ_ONLY)) || (properties_.getString (JDProperties.ACCESS).equalsIgnoreCase (JDProperties.ACCESS_READ_CALL)))) JDError.throwSQLException (this, JDError.EXC_ACCESS_MISMATCH); readOnly_ = readOnly; if (JDTrace.isTraceOn()) JDTrace.logProperty (this, "setProperties", "Read only", readOnly_); } // @E10 new method /** * Creates an unnamed savepoint in the current transaction and returns the new Savepoint object that represents it. *

    *
  • Named savepoints must be unique. A savepoint name cannot be reused until the savepoint is released, committed, or rolled back. *
  • Savepoints are valid only if autocommit is off. An exception is thrown if autocommit is enabled. *
  • Savepoints are not valid across XA connections. An exception is thrown if the connection is an XA connection. *
  • Savepoints require OS/400 V5R2 or IBM i. An exception is thrown if connecting to OS/400 V5R1 or earlier. *
  • If the connection option is set to keep cursors open after a traditional rollback, cursors will remain open after a rollback to a savepoint. *
* * @return The new Savepoint object. * @exception SQLException if a database access error occurs or this Connection object is currently in auto-commit mode. * @since Modification 5 **/ public Savepoint setSavepoint() throws SQLException { return setSavepoint(null, AS400JDBCSavepoint.getNextId()); } // @E10 new method /** * Creates a named savepoint in the current transaction and returns the new Savepoint object that represents it. *
    *
  • Named savepoints must be unique. A savepoint name cannot be reused until the savepoint is released, committed, or rolled back. *
  • Savepoints are valid only if autocommit is off. An exception is thrown if autocommit is enabled. *
  • Savepoints are not valid across XA connections. An exception is thrown if the connection is an XA connection. *
  • Savepoints require OS/400 V5R2 or IBM i. An exception is thrown if connecting to OS/400 V5R1 or earlier. *
  • If the connection option is set to keep cursors open after a traditional rollback, cursors will remain open after a rollback to a savepoint. *
* @param name A String containing the name of the savepoint * @return The new Savepoint object. * @exception SQLException if a database access error occurs or this Connection object is currently in auto-commit mode. * @since Modification 5 **/ public Savepoint setSavepoint(String name) throws SQLException { if (name == null) throw new NullPointerException("name"); return setSavepoint(name, 0); } // @E10 new method public Savepoint setSavepoint(String name, int id) throws SQLException { if (id > 0) name = "T_JDBCINTERNAL_" + id; // When creating the savepoint specify retain cursors. That is the // only option supported by the IBM i system at this time. We have to specify // it because the SQL default is close cursors. Since we need to use // an option other than the default we have to specify it on the statement. // Plus, the system will return an error if we don't specify it. processSavepointRequest("SAVEPOINT " + name + " ON ROLLBACK RETAIN CURSORS" ); return(Savepoint)(Object) new AS400JDBCSavepoint(name, id); } /** Sets the server attributes. @exception SQLException If an error occurs. **/ public void setServerAttributes () throws SQLException { if (TESTING_THREAD_SAFETY) return; // in certain testing modes, don't contact IBM i system DBReplyRequestedDS reply = null; try { vrm_ = as400_.getVRM(); // @D0A @ECM //@P0C DBSQLAttributesDS request = null; int decimalSeparator, dateFormat, dateSeparator, timeFormat, timeSeparator; int decimalDataErrors; DBReplyServerAttributes serverAttributes = null; try { request = DBDSPool.getDBSQLAttributesDS (DBSQLAttributesDS.FUNCTIONID_SET_ATTRIBUTES, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA + DBBaseRequestDS.ORS_BITMAP_SERVER_ATTRIBUTES, 0); //@P0C // We need to set a temporary CCSID just for this // request, since we use this request to get the // actual CCSID. //@P0D ConverterImplRemote tempConverter = //@P0D ConverterImplRemote.getConverter (as400_.getCcsid(), as400_); // We can safely use ccsid 37 as long as the fields in the request are tagged with // CCSID 37. /*@V1A*/ int thisRequestCcsid = 37; ConvTable tempConverter = ConvTable.getTable(thisRequestCcsid, null); //@P0A // @E2D // Do not set the client CCSID. We do not want // @E2D // the system to convert data, since we are going // @E2D // to do all conersion on the client. By not telling // @E2D // the system our CCSID, then we achieve this. // @E2D // // @E2D // Note that the database host server documentation // @E2D // states that when we do this, the CCSID values // @E2D // in data formats may be incorrect and that we // @E2D // should always use the server job's CCSID. // Set the client CCSID to Unicode. // @E2A // @M0C - As of v5r3m0 we allow the client CCSID to be 1200 (UTF-16) which // will cause our statement to flow in 1200 and our package to be 1200 //Bidi-HCG allow any ccsid or "system" to use ccsid of AS400 object //Bidi-HCG start String sendCCSID = properties_.getString(JDProperties.PACKAGE_CCSID); int sendCCSIDInt; int default_ccsid = Integer.parseInt(JDProperties.PACKAGE_CCSID_UCS2); if( sendCCSID.equalsIgnoreCase("system")) { // Only look up host CCSID if needed int hostCCSID; if(as400PublicClassObj_ == null) //@pdcbidi hostCCSID = 37; else hostCCSID = as400PublicClassObj_.getCcsid(); sendCCSIDInt = hostCCSID; } else { try{ if((sendCCSIDInt = Integer.valueOf(sendCCSID).intValue()) <= 0) sendCCSIDInt = default_ccsid; if(vrm_ < JDUtilities.vrm530 && sendCCSIDInt == 1200) sendCCSIDInt = default_ccsid; } catch(Exception e) { sendCCSIDInt = default_ccsid; } } packageCCSID_Converter = ConvTable.getTable(sendCCSIDInt, null); properties_.setString(JDProperties.PACKAGE_CCSID, (new Integer(sendCCSIDInt)).toString()); request.setClientCCSID(sendCCSIDInt); if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "Client CCSID = " + sendCCSIDInt); //Bidi-HCG end // This language feature code is used to tell the // system what language to send error messages in. // If that language is not installed on the system, // we get messages back in the default language that // was installed on the system. // String nlv = as400_.getNLV(); // @F1C request.setLanguageFeatureCode(nlv); // @EDC if (JDTrace.isTraceOn ()) JDTrace.logInformation (this, "Setting server NLV = " + nlv); // Client functional level. request.setClientFunctionalLevel(CLIENT_FUNCTIONAL_LEVEL_); // @EDC if (JDTrace.isTraceOn ()) // @EDC JDTrace.logInformation (this, "Client functional level = " + CLIENT_FUNCTIONAL_LEVEL_); // @EDC // Sort sequence. // Moved below.. This must be set after setting the IASP, since the // table may be on the IASP. @M6C /* if (! properties_.equals (JDProperties.SORT, JDProperties.SORT_HEX)) //@pdc only send if not default (hex) { JDSortSequence sortSequence = new JDSortSequence ( properties_.getString (JDProperties.SORT), properties_.getString (JDProperties.SORT_LANGUAGE), properties_.getString (JDProperties.SORT_TABLE), properties_.getString (JDProperties.SORT_WEIGHT)); request.setNLSSortSequence (sortSequence.getType (), sortSequence.getTableFile (), sortSequence.getTableLibrary (), sortSequence.getLanguageId (), tempConverter); } */ request.setTranslateIndicator (0xF0); // @E2C request.setDRDAPackageSize (1); //Note: newAutoCommitSupport is trueAutoCommitSupport if(!(newAutoCommitSupport_ == 0)) //@KBA V5R3 or greater so run with new support { //@AC1 if(properties_.getBoolean(JDProperties.AUTO_COMMIT)) //@AC1 request.setAutoCommit(0xE8); //@KBA Turn on auto commit else //@AC1 request.setAutoCommit(0xD5); //@AC1 } //@AC1 if((newAutoCommitSupport_ == 1) && (properties_.getBoolean(JDProperties.AUTO_COMMIT))) //@KBA //@AC1 (only set to *NONE if autocommit is on) request.setCommitmentControlLevelParserOption(0); //@KBA Run under *NONE when in autocommit else //@KBA Run under default isolation level request.setCommitmentControlLevelParserOption (transactionManager_.getCommitMode ()); // Server attributes based on property values. // These all match the index within the property's // choices. dateFormat = properties_.getIndex (JDProperties.DATE_FORMAT); if (dateFormat != -1) request.setDateFormatParserOption (dateFormat); dateSeparator = properties_.getIndex (JDProperties.DATE_SEPARATOR); if (dateSeparator != -1) request.setDateSeparatorParserOption (dateSeparator); timeFormat = properties_.getIndex (JDProperties.TIME_FORMAT); if (timeFormat != -1) request.setTimeFormatParserOption (timeFormat); timeSeparator = properties_.getIndex (JDProperties.TIME_SEPARATOR); if (timeSeparator != -1) request.setTimeSeparatorParserOption (timeSeparator); decimalSeparator = properties_.getIndex (JDProperties.DECIMAL_SEPARATOR); if (decimalSeparator != -1) request.setDecimalSeparatorParserOption (decimalSeparator); request.setNamingConventionParserOption (properties_.getIndex (JDProperties.NAMING)); // Set the ignore decimal data error parser option. decimalDataErrors = properties_.getIndex (JDProperties.DECIMAL_DATA_ERRORS); if (decimalDataErrors != -1) request.setIgnoreDecimalDataErrorParserOption(decimalDataErrors); // If the system supports RLE data compression, then use it. @ECA // Otherwise, use the old-style data compression. @ECA if (properties_.getBoolean(JDProperties.DATA_COMPRESSION)) { // @ECA if (vrm_ >= JDUtilities.vrm510) { // @ECA dataCompression_ = DATA_COMPRESSION_RLE_; // @ECA request.setDataCompressionOption(0); // @ECA if (JDTrace.isTraceOn ()) // @ECA JDTrace.logInformation (this, "Data compression = RLE"); // @ECA } // @ECA else { // @ECA dataCompression_ = DATA_COMPRESSION_OLD_; // @ECA request.setDataCompressionOption(1); // @D3A @ECC if (JDTrace.isTraceOn ()) // @ECA JDTrace.logInformation (this, "Data compression = old"); // @ECA } // @ECA } // @ECA else { // @ECA dataCompression_ = DATA_COMPRESSION_NONE_; // @ECA request.setDataCompressionOption(0); // @ECA if (JDTrace.isTraceOn ()) // @ECA JDTrace.logInformation (this, "Data compression = none"); // @ECA } // @ECA // Default SQL schema. if (defaultSchema_ != null) request.setDefaultSQLLibraryName (defaultSchema_, tempConverter); // There is no need to tell the system what our code // page is, nor is there any reason to get a translation // table back from the system at this point. This // will be handled later by the Converter class. // I haven't found a good reason to set the ambiguous select // option. ODBC sets it only when block criteria is "unless // FOR UPDATE OF", but it causes some problems for JDBC. // The difference is that ODBC has the luxury of setting cursor // concurrency. request.setPackageAddStatementAllowed (properties_.getBoolean (JDProperties.PACKAGE_ADD) ? 1 : 0); // If the system is at V4R4 or later, then set some more attributes. if (vrm_ >= JDUtilities.vrm440) { // @D0C @E9C // @E9D || (FORCE_EXTENDED_FORMATS_)) { if(vrm_ >= JDUtilities.vrm540) //@540 use new Super Extended Formats request.setUseExtendedFormatsIndicator(0xF2); //@540 else //@540 request.setUseExtendedFormatsIndicator (0xF1); // Although we publish a max lob threshold of 16777216, @E6A // the system can only handle 15728640. We do it this @E6A // way to match ODBC. @E6A int lobThreshold = properties_.getInt (JDProperties.LOB_THRESHOLD); // @E6A if (lobThreshold <= 0) // @E6A request.setLOBFieldThreshold(0); // @E6A else if (lobThreshold >= 15728640) // @E6A request.setLOBFieldThreshold(15728640); // @E6A else // @E6A request.setLOBFieldThreshold(lobThreshold); // @E6C extendedFormats_ = true; } // Set the default select statement type to be read-only (OS/400 v5r1 // and earlier the default was updatable). If the app requests updatable // statements we will now specify "updatable" on the RPB. Do this // only to V5R1 systems with the needed PTF, and V5R2 and later systems // because they have the fix needed to support // altering the cursor type in the RPB. (AmbiguousSelectOption(1) // means read-only) if (vrm_ >= JDUtilities.vrm520) // @J3a { // @J3a request.setAmbiguousSelectOption(1); // @J3a mustSpecifyForUpdate_ = false; // @J31a if(vrm_ >= JDUtilities.vrm710){ //@710 //@128sch //@710 - Client support information - indicate our support for ROWID data type, true autocommit // and 128 byte column names and 128 length schemas request.setClientSupportInformation(0xF0000000); if(JDTrace.isTraceOn()){ JDTrace.logInformation(this, "ROWID supported = true"); JDTrace.logInformation(this, "True auto-commit supported = true"); JDTrace.logInformation(this, "128 byte column names supported = true"); JDTrace.logInformation(this, "128 length schema names supported = true"); } } else if(vrm_ >= JDUtilities.vrm540){ //@540 for IBM i V5R4 and later, 128 byte column names are supported //@540 - Client support information - indicate our support for ROWID data type, true autocommit // and 128 byte column names request.setClientSupportInformation(0xE0000000); if(JDTrace.isTraceOn()){ JDTrace.logInformation(this, "ROWID supported = true"); JDTrace.logInformation(this, "True auto-commit supported = true"); JDTrace.logInformation(this, "128 byte column names supported = true"); } } else if (vrm_ >= JDUtilities.vrm530) //@KBA For IBM i V5R3 and later true auto commit support is supported. { // @KBA - Client support information - indicate our support for ROWID data type and // true auto-commit request.setClientSupportInformation(0xC0000000); //@KBC if(JDTrace.isTraceOn()) //@KBA { //@KBA JDTrace.logInformation(this, "ROWID supported = true"); //@KBA JDTrace.logInformation(this, "True auto-commit supported = true"); //@KBA } //@KBA } //@KBA else //@KBA { //@KBA // @M0A - Client support information - indicate our support for ROWID data type request.setClientSupportInformation(0x80000000); if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "ROWID supported = true"); } //@KBA } // @M0A - added support for 63 digit decimal precision if(vrm_ >= JDUtilities.vrm530) { int maximumPrecision = properties_.getInt(JDProperties.MAXIMUM_PRECISION); int maximumScale = properties_.getInt(JDProperties.MAXIMUM_SCALE); int minimumDivideScale = properties_.getInt(JDProperties.MINIMUM_DIVIDE_SCALE); // make sure that if scale is >31 we set precision to 63 // this is a requirement of host server to avoid a PWS0009 if(maximumScale > 31) maximumPrecision = 63; request.setDecimalPrecisionIndicators(maximumPrecision, maximumScale, minimumDivideScale); if(JDTrace.isTraceOn()) { JDTrace.logInformation(this, "Maximum decimal precision = " + maximumPrecision); JDTrace.logInformation(this, "Maximum decimal scale = " + maximumScale); JDTrace.logInformation(this, "Minimum divide scale = " + minimumDivideScale); } // @M0A - added support of hex constant parser option int parserOption = properties_.getIndex(JDProperties.TRANSLATE_HEX); if(parserOption != -1) { request.setHexConstantParserOption(parserOption); if(JDTrace.isTraceOn()) { String msg = (parserOption == 0) ? "Translate hex = character" : "Translate hex = binary"; JDTrace.logInformation(this, msg); } } //@KBL - added support for hold/not hold locators // Specifies whether input locators should be allocated as type hold locators or not hold locators. // If the locators are of type hold, they will not be released when a commit is done. boolean holdLocators = properties_.getBoolean(JDProperties.HOLD_LOCATORS); if(!holdLocators) // Only need to set it if it is false, by default host server sets them to hold. { request.setInputLocatorType(0xD5); if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "Hold Locators = " + holdLocators); } //@KBL - added support for locator persistance. The JDBC specification says locators should be // scoped to the transaction (ie. commit, rollback, or connection.close()) if auto commit is off // host server added two options for the optional Locator Persistence ('3830'x') connection attribute: // 0 -- Locators without the hold property are freed when cursor closed (locators scoped to the cursor). // 1 -- Locators without the hold property are freed when the transaction is completed (locators scoped to the transaction). // // By default this is set to 0 by the host server, but to comply with the JDBC specification, // we should always set it to 1. // Note: this only applies when auto commit is off. The property has no effect if auto commit is on. // Locators are always scoped to the cursor when auto-commit is on. request.setLocatorPersistence(1); } //@540 if(vrm_ >= JDUtilities.vrm540){ //Set the query optimization goal // 0 = Optimize query for first block of data (*ALLIO) when extended dynamic packages are used; Optimize query for entire result set (*FIRSTIO) when packages are not used (default) //@PDC update comment to reflect host server default // 1 = Optimize query for first block of data (*FIRSTIO) // 2 = Optimize query for entire result set (*ALLIO) int queryOptimizeGoal = properties_.getInt (JDProperties.QUERY_OPTIMIZE_GOAL); if(queryOptimizeGoal != 0){ // Only need to send if we are not using the default if(queryOptimizeGoal == 1) request.setQueryOptimizeGoal(0xC6); else if(queryOptimizeGoal == 2) request.setQueryOptimizeGoal(0xC1); } if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "query optimize goal = " + queryOptimizeGoal); } //@X1A int enableClientAffinitiesList = properties_.getInt(JDProperties.ENABLE_CLIENT_AFFINITIES_LIST); if (enableClientAffinitiesList > 0) { String alternateServers = properties_.getString(JDProperties.CLIENT_REROUTE_ALTERNATE_SERVER_NAME); if (alternateServers == null || alternateServers.length() == 0 ) { // Request the list from the server request.setRequestAlternateServer(1); } } //@550 Query Storage Limit Support if(vrm_ >= JDUtilities.vrm610){ //Set the query storage limit int queryStorageLimit = properties_.getInt(JDProperties.QUERY_STORAGE_LIMIT); if(queryStorageLimit != -1) // Only need to send if we are not using the default of *NOMAX (-1) { if(queryStorageLimit < -1) request.setQueryStorageLimit(-1); else if(queryStorageLimit > AS400JDBCDataSource.MAX_STORAGE_LIMIT) // if larger than the max just set to max request.setQueryStorageLimit(2147352578); else request.setQueryStorageLimit(queryStorageLimit); } if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "query storage limit = " + queryStorageLimit); } if (JDTrace.isTraceOn ()) { if (extendedFormats_) JDTrace.logInformation (this, "Using extended datastreams"); else JDTrace.logInformation (this, "Using original datastreams"); } // Send an RDB name to the system only if connecting to // v5r2 and newer versions of IBM i if (vrm_ >= JDUtilities.vrm520) // @J2a { // @J2a StringBuffer RDBName = new StringBuffer(properties_.getString (JDProperties.DATABASE_NAME)); // @J2a if (RDBName.length() > 0) // @J2a { // @J2a RDBName.append(" "); // @J2a RDBName.setLength(18); // @J2a request.setRDBName(RDBName.toString().toUpperCase(), tempConverter); // @J2a if (JDTrace.isTraceOn ()) // @J2a JDTrace.logInformation (this, "RDB Name = -->" + RDBName + "<--"); // @J2a } // @J2a } // @J2a // Set the sort table after setting the RDB name @M6M // This allows the used of a sort table in the IASP // Sort sequence. if (! properties_.equals (JDProperties.SORT, JDProperties.SORT_HEX)) //@pdc only send if not default (hex) { JDSortSequence sortSequence = new JDSortSequence ( properties_.getString (JDProperties.SORT), properties_.getString (JDProperties.SORT_LANGUAGE), properties_.getString (JDProperties.SORT_TABLE), properties_.getString (JDProperties.SORT_WEIGHT)); request.setNLSSortSequence (sortSequence.getType (), sortSequence.getTableFile (), sortSequence.getTableLibrary (), sortSequence.getLanguageId (), tempConverter); } //@PDA 550 client interface info settings //These three settings cannot be updated by user apps. //This gives driver information to host server for any logging or future diagnostics. if (vrm_ >= JDUtilities.vrm610) { //these strings are not mri translated for future diagnostic tools, searching etc on host server request.setInterfaceType( "JDBC", tempConverter); request.setInterfaceName( "IBM Toolbox for Java", tempConverter); request.setInterfaceLevel( AS400JDBCDriver.DRIVER_LEVEL_, tempConverter); //@DFA 550 decfloat rounding mode short roundingMode = 0; //@DFA String roundingModeStr = properties_.getString(JDProperties.DECFLOAT_ROUNDING_MODE); //@DFA if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_HALF_EVEN)) //@DFA roundingMode = 0; //@DFA else if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_UP)) //@DFA roundingMode = 6; //@DFA else if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_DOWN)) //@DFA roundingMode = 2; //@DFA else if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_CEILING)) //@DFA roundingMode = 3; //@DFA else if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_FLOOR)) //@DFA roundingMode = 4; //@DFA else if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_HALF_UP)) //@DFA roundingMode = 1; //@DFA else if ( roundingModeStr.equals(JDProperties.DECFLOAT_ROUNDING_MODE_HALF_DOWN)) //@DFA roundingMode = 5; //@DFA //only need to send request if not default 0 (half even) if(roundingMode != 0) //@DFA request.setDecfloatRoundingMode(roundingMode); //@DFA //@eof Close on EOF request.setCloseEOF( 0xE8) ; } //@710 if (vrm_ >= JDUtilities.vrm710) { int car = properties_.getInt(JDProperties.CONCURRENT_ACCESS_RESOLUTION); //@cc1 if( !(properties_.getString(JDProperties.CONCURRENT_ACCESS_RESOLUTION)).equals( JDProperties.CONCURRENTACCESS_NOT_SET )) //@cc1 { //@cc1 request.setConcurrentAccessResolution( car ); //@cc1 //Use instance variable to to "current setting". //This will allow the Connection setting to override DataSource //setting for future updates to this property from the Connection object. //@cc1 concurrentAccessResolution_ = car; //@cc1 } //@cc1 } // Send the request and process the reply. reply = sendAndReceive (request); int errorClass = reply.getErrorClass(); int returnCode = reply.getReturnCode(); // Sort sequence attribute cannot be set. if ((errorClass == 7) && ((returnCode == 301) || (returnCode == 303))) postWarning (JDError.getSQLWarning (this, id_, errorClass, returnCode)); // Language feature code id was not changed. This is caused // when the secondary language can not be added to the library // list, and shows up as a PWS0003. else if ((errorClass == 7) && (returnCode == 304)) postWarning (JDError.getSQLWarning (this, id_, errorClass, returnCode)); // -704 is RDB (IASP) does not exist. We do not go back to the system to get // error info since they are sending an invalid attribute exception when the // IASP is not found. We can create a better error than that. else if ((errorClass == 7) && (returnCode == -704)) // @J2a { // @J2a try // @J2a { // @J2a close(); // @J2a } // @J2a catch (Exception e) {} // eat errors on close // @J2a JDError.throwSQLException(this, JDError.EXC_RDB_DOES_NOT_EXIST); // @J2a } // @J2a // Other system errors. else if (errorClass != 0) JDError.throwSQLException (this, this, id_, errorClass, returnCode); // Process the returned server attributes. serverAttributes = reply.getServerAttributes (); } finally { if (request != null) { request.returnToPool(); request = null; } // We cannot return the reply to the pool while it is still being used in the serverAttributes structure // if (reply != null) reply.returnToPool(); } // Check to see if we got back alternate server @X1A String alternateServer = reply.getAlternateServer(); if (alternateServer != null && alternateServer.length() > 0) { alternateServer_ = alternateServer; } else { alternateServer_ = null; } // The CCSID that comes back is a mixed CCSID (i.e. mixed // SBCS and DBCS). This will be the CCSID that all // non-graphic data will be returned as for this // connection, so we own the converter here. int serverCCSID = serverAttributes.getServerCCSID(); vrm_ = serverAttributes.getVRM(); //@P0D converter_ = ConverterImplRemote.getConverter (serverCCSID, as400_); converter_ = ConvTable.getTable(serverCCSID, null); //@P0A // If we did not user the signon server, fix the signon information // in the as400 object. /*@V1A*/ if (as400PublicClassObj_ != null) { if (as400PublicClassObj_.skipSignonServer) { as400PublicClassObj_.setSignonInfo(serverCCSID, vrm_, as400_.getUserId()) ; } } // Get the server functional level. It comes back as in the @E7A // format VxRxMx9999. @E7A String serverFunctionalLevelAsString = serverAttributes.getServerFunctionalLevel(converter_); // @E7A try { // @E7A serverFunctionalLevel_ = Integer.parseInt(serverFunctionalLevelAsString.substring(6)); // @E7A } // @E7A catch (NumberFormatException e) { // @E7A serverFunctionalLevel_ = 0; // @E7A } // @E7A // Get the job number, but only if . @E8A if (serverFunctionalLevel_ >= 5) // @E8A serverJobIdentifier_ = serverAttributes.getServerJobIdentifier(converter_); // @E8A // User no longer needs to specify "for update" on their SQL // statements if running to v5r1 with a PTF. (V5R2 and later // is handled in another piece of code) if ((vrm_ == JDUtilities.vrm510) && //@J31a ( serverFunctionalLevel_ >= 10)) //@J31a mustSpecifyForUpdate_ = false; //@J31a if (JDTrace.isTraceOn ()) { // @C2C int v = (vrm_ & 0xffff0000) >>> 16; // @D1A int r = (vrm_ & 0x0000ff00) >>> 8; // @D1A int m = (vrm_ & 0x000000ff); // @D1A JDTrace.logInformation (this, "JDBC driver major version = " // @C2A + AS400JDBCDriver.MAJOR_VERSION_); // @C2A //Check version - V5R2 and earlier run on OS/400, V5R3 and later run on IBM i if(((v==5) && (r>=3)) || (v>5)) JDTrace.logInformation(this, "IBM i VRM = V" + v + "R" + r + "M" + m); else JDTrace.logInformation (this, "OS/400 VRM = V" + v // @C2A + "R" + r + "M" + m); // @C2A JDTrace.logInformation (this, "Server CCSID = " + serverCCSID); JDTrace.logInformation(this, "Server functional level = " // @E7A + serverFunctionalLevelAsString // @E7A + " (" + serverFunctionalLevel_ + ")"); // @E7A StringBuffer buffer = new StringBuffer(); // @E8A if (serverJobIdentifier_ == null) // @E8A buffer.append("Not available"); // @E8A else { // @E8A buffer.append(serverJobIdentifier_.substring(20, 26).trim()); // job number // @E8A buffer.append('/'); // @E8A buffer.append(serverJobIdentifier_.substring(10, 20).trim()); // user name // @E8A buffer.append('/'); // @E8A buffer.append(serverJobIdentifier_.substring(0, 10).trim()); // job name // @E8A } // @E8A JDTrace.logInformation(this, "Server job identifier = " + buffer); // @E8A } // @C2A // @E2D // Wait to load graphic converter until it is needed. // @E2D graphicConverter_ = null; // @E2D graphicConverterLoaded_ = false; // Get the catalog name from the RDB entry. If no RDB entry is // set on the system, then use the system name from the AS400 object // (which originally came from the URL). String rdbEntry = serverAttributes.getRelationalDBName (converter_).trim(); if ((rdbEntry.length() > 0) && (! rdbEntry.equalsIgnoreCase ("*N"))) catalog_ = rdbEntry; // In the cases where defaults come from the server // job, get the defaults for properties that were not set. if (decimalSeparator == -1) { switch (serverAttributes.getDecimalSeparatorPO ()) { case 0: properties_.setString (JDProperties.DECIMAL_SEPARATOR, JDProperties.DECIMAL_SEPARATOR_PERIOD); break; case 1: properties_.setString (JDProperties.DECIMAL_SEPARATOR, JDProperties.DECIMAL_SEPARATOR_COMMA); break; } } if (dateFormat == -1) { switch (serverAttributes.getDateFormatPO ()) { case 0: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_JULIAN); break; case 1: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_MDY); break; case 2: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_DMY); break; case 3: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_YMD); break; case 4: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_USA); break; case 5: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_ISO); break; case 6: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_EUR); break; case 7: properties_.setString (JDProperties.DATE_FORMAT, JDProperties.DATE_FORMAT_JIS); break; } } if (dateSeparator == -1) { switch (serverAttributes.getDateSeparatorPO ()) { case 0: properties_.setString (JDProperties.DATE_SEPARATOR, JDProperties.DATE_SEPARATOR_SLASH); break; case 1: properties_.setString (JDProperties.DATE_SEPARATOR, JDProperties.DATE_SEPARATOR_DASH); break; case 2: properties_.setString (JDProperties.DATE_SEPARATOR, JDProperties.DATE_SEPARATOR_PERIOD); break; case 3: properties_.setString (JDProperties.DATE_SEPARATOR, JDProperties.DATE_SEPARATOR_COMMA); break; case 4: properties_.setString (JDProperties.DATE_SEPARATOR, JDProperties.DATE_SEPARATOR_SPACE); break; } } if (timeFormat == -1) { switch (serverAttributes.getTimeFormatPO ()) { case 0: properties_.setString (JDProperties.TIME_FORMAT, JDProperties.TIME_FORMAT_HMS); break; case 1: properties_.setString (JDProperties.TIME_FORMAT, JDProperties.TIME_FORMAT_USA); break; case 2: properties_.setString (JDProperties.TIME_FORMAT, JDProperties.TIME_FORMAT_ISO); break; case 3: properties_.setString (JDProperties.TIME_FORMAT, JDProperties.TIME_FORMAT_EUR); break; case 4: properties_.setString (JDProperties.TIME_FORMAT, JDProperties.TIME_FORMAT_JIS); break; } } if (timeSeparator == -1) { switch (serverAttributes.getTimeSeparatorPO ()) { case 0: properties_.setString (JDProperties.TIME_SEPARATOR, JDProperties.TIME_SEPARATOR_COLON); break; case 1: properties_.setString (JDProperties.TIME_SEPARATOR, JDProperties.TIME_SEPARATOR_PERIOD); break; case 2: properties_.setString (JDProperties.TIME_SEPARATOR, JDProperties.TIME_SEPARATOR_COMMA); break; case 3: properties_.setString (JDProperties.TIME_SEPARATOR, JDProperties.TIME_SEPARATOR_SPACE); break; } } } catch (DBDataStreamException e) { JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } // @J5D catch (IOException e) { catch (UnsupportedEncodingException e) { // @J5C JDError.throwSQLException (this, JDError.EXC_INTERNAL, e); } finally { // Don't return the reply to the pool until the very end, // as it is used by the DBReplyServerAttributes object if (reply != null) { reply.returnToPool(); reply = null; } } } //@A3A // Implementation note: Don't use this object internally because we could be running in a proxy environment // The purpose of this method is to simply hold the full AS400 object so it can be retrieved from the Connection public void setSystem (AS400 as400) throws SQLException // @EGA { as400PublicClassObj_ = as400; } // @D2C /** Sets the transaction isolation level. The transaction isolation level cannot be changed while in the middle of a transaction.

JDBC and DB2 for IBM i use different terminology for transaction isolation levels. The following table provides a terminology mapping:

IBM i isolation levelJDBC transaction isolation level
*CHG TRANSACTION_READ_UNCOMMITTED
*CS TRANSACTION_READ_COMMITTED
*ALL TRANSACTION_READ_REPEATABLE_READ
*RR TRANSACTION_SERIALIZABLE
@param level The transaction isolation level. Possible values are:
  • TRANSACTION_READ_UNCOMMITTED
  • TRANSACTION_READ_COMMITTED
  • TRANSACTION_REPEATABLE_READ
  • TRANSACTION_SERIALIZABLE
@exception SQLException If the connection is not open, the input level is not valid or unsupported, or a transaction is active. **/ public void setTransactionIsolation (int level) throws SQLException { checkOpen (); transactionManager_.setIsolation (level); if (JDTrace.isTraceOn()) JDTrace.logProperty (this, "setTransactionIsolation", "Transaction isolation", transactionManager_.getIsolation ()); } // JDBC 2.0 /** Sets the type map to be used for distinct and structured types.

Note: Distinct types are supported by DB2 for IBM i, but are not externalized by the IBM Toolbox for Java JDBC driver. In other words, distinct types behave as if they are the underlying type. Structured types are not supported by DB2 for IBM i. Consequently, this driver does not support the type map. @param typeMap The type map. @exception SQLException This exception is always thrown. **/ public void setTypeMap (Map typeMap) throws SQLException { JDError.throwSQLException (this, JDError.EXC_FUNCTION_NOT_SUPPORTED); } /** Returns the connection's catalog name. This is the name of the IBM i system. @return The catalog name. **/ public String toString () { return catalog_; } /** Indicates if the connection is using extended formats. @return true if the connection is using extended formats, false otherwise. **/ public boolean useExtendedFormats () throws SQLException // @EGA { return extendedFormats_; } //@pda jdbc40 public String[] getValidWrappedList() { return new String[] { "com.ibm.as400.access.AS400JDBCConnection", "java.sql.Connection" }; } //@PDA jdbc40 //JDBC40DOC /** //JDBC40DOC * Returns true if the connection has not been closed and is still valid. //JDBC40DOC * The driver shall submit a query on the connection or use some other //JDBC40DOC * mechanism that positively verifies the connection is still valid when //JDBC40DOC * this method is called. //JDBC40DOC *

//JDBC40DOC * The query submitted by the driver to validate the connection shall be //JDBC40DOC * executed in the context of the current transaction. //JDBC40DOC * //JDBC40DOC * @param timeout - The time in seconds to wait for the database operation //JDBC40DOC * used to validate the connection to complete. If //JDBC40DOC * the timeout period expires before the operation //JDBC40DOC * completes, this method returns false. A value of //JDBC40DOC * 0 indicates a timeout is not applied to the //JDBC40DOC * database operation. //JDBC40DOC *

//JDBC40DOC * @return true if the connection is valid, false otherwise //JDBC40DOC * @exception SQLException if a database access error occurs. //JDBC40DOC */ /* ifdef JDBC40 public boolean isValid(int timeout) throws SQLException { DBSQLRequestDS request = null; DBReplyRequestedDS reply = null; int errorClass = 0; int returnCode = 0; ReentrantLock lock = new ReentrantLock(); // Return exception if timeout is less than 0.. @D6A if (timeout < 0) { JDError.throwSQLException( this, JDError.EXC_ATTRIBUTE_VALUE_INVALID); } try { // inner class to run timer in sep thread class CommTimer implements Runnable { Thread otherThread; ReentrantLock lock; int timeout; public void run() { try { Thread.sleep(timeout * 1000); lock.lockInterruptibly(); //lock, so only one thread can call interrupt otherThread.interrupt(); lock.unlock(); }catch(InterruptedException ie) { //interrupted from notifyThread because request/reply is done. just return from run() if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Connection.isValid timer interrupted and stopped"); } } public CommTimer(Thread otherThread, int timeout, ReentrantLock lock ) { this.otherThread = otherThread; this.timeout = timeout; this.lock = lock; } }; CommTimer timer = null; Thread t = null; // Only use timeout if > 0. @D6A if (timeout > 0) { timer = new CommTimer( Thread.currentThread(), timeout, lock); //pass in ref to main thread so timer can interrupt if blocked on IO t = new Thread(timer); t.start(); //sleeps for timeout and then interrupts main thread if it is still blocked on IO } try { request = DBDSPool.getDBSQLRequestDS(DBSQLRequestDS.FUNCTIONID_TEST_CONNECTION, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA, 0); reply = sendAndReceive(request); lock.lockInterruptibly(); //lock, so only one thread can call interrupt if (t != null) t.interrupt(); //stop timer thread @D6C lock.unlock(); errorClass = reply.getErrorClass(); returnCode = reply.getReturnCode(); } catch(Exception ex) { try { // Make sure timeout thread is stopped @D6A lock.lockInterruptibly(); //lock, so only one thread can call interrupt if (t != null) t.interrupt(); //stop timer thread lock.unlock(); } catch (Exception ex2) { } //interruptedException is wrapped in sqlException //if exception occurs, just return false since connection is not valid //this happens if timer ends before sendAndReceive returns if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Connection.isValid timed out or could not verify valid connection"); return false; } if(errorClass == 7 && returnCode == -201) return true; else return false; } catch(Exception e) { //implmentation note: if any exception happens, just return false, since conn is not valid return false; } finally { if (request != null) { request.returnToPool(); request = null; } if (reply != null) { reply.returnToPool(); reply = null; // commented out code } if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "Connection.isValid call complete"); } } endif */ //@PDA 550 client info /** * Sets the value of the client info property specified by name to the * value specified by value. *

* Applications may use the DatabaseMetaData.getClientInfoProperties * method to determine the client info properties supported by the driver * and the maximum length that may be specified for each property. *

* The driver stores the value specified in a suitable location in the * database. For example in a special register, session parameter, or * system table column. For efficiency the driver may defer setting the * value in the database until the next time a statement is executed or * prepared. Other than storing the client information in the appropriate * place in the database, these methods shall not alter the behavior of * the connection in anyway. The values supplied to these methods are * used for accounting, diagnostics and debugging purposes only. *

* The driver shall generate a warning if the client info name specified * is not recognized by the driver. *

* If the value specified to this method is greater than the maximum * length for the property the driver may either truncate the value and * generate a warning or generate a SQLException. If the driver * generates a SQLException, the value specified was not set on the * connection. *

* The following client info properties are supported in Toobox for Java. *

    *
  • ApplicationName - The name of the application currently utilizing * the connection
  • *
  • ClientUser - The name of the user that the application using * the connection is performing work for. This may * not be the same as the user name that was used * in establishing the connection.
  • *
  • ClientHostname - The hostname of the computer the application * using the connection is running on.
  • *
  • ClientAccounting - Client accounting information.
  • *
  • ClientProgramID - The client program identification.
  • *
*

* @param name The name of the client info property to set * @param value The value to set the client info property to. If the * value is null, the current value of the specified * property is cleared. *

//JDBC40DOC * @throws SQLClientInfoException if the database returns an error while //JDBC40DOC * setting the client info value on the database server. */ public void setClientInfo(String name, String value) /* ifdef JDBC40 throws SQLClientInfoException endif */ /* ifndef JDBC40 */ throws SQLException /* endif */ { DBSQLAttributesDS request = null; DBReplyRequestedDS setClientInfoReply = null; ConvTable tempConverter = null; String oldValue = null; //save in case we get error from host db // in order to reset if null value is passed in, use empty string if (value == null) value = ""; try { if (getVRM() >= JDUtilities.vrm610) { request = DBDSPool.getDBSQLAttributesDS(DBSQLAttributesDS.FUNCTIONID_SET_ATTRIBUTES, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA + DBBaseRequestDS.ORS_BITMAP_SERVER_ATTRIBUTES, 0); tempConverter = ConvTable.getTable(as400_.getCcsid(), null); } if (name.equals(applicationNamePropertyName_)) { oldValue = applicationName_; applicationName_ = value; if (request != null ) request.setClientInfoApplicationName(value, tempConverter); } else if (name.equals(clientUserPropertyName_)) { oldValue = clientUser_; clientUser_ = value; if (request != null ) request.setClientInfoClientUser(value, tempConverter); } else if (name.equals(clientAccountingPropertyName_)) { oldValue = clientAccounting_; clientAccounting_ = value; if (request != null ) request.setClientInfoClientAccounting(value, tempConverter); } else if (name.equals(clientHostnamePropertyName_)) { oldValue = clientHostname_; clientHostname_ = value; if (request != null) request.setClientInfoClientHostname(value, tempConverter); } else if (name.equals(clientProgramIDPropertyName_)) //@PDA add block for ProgramID { oldValue = clientProgramID_; clientProgramID_ = value; if (request != null) request.setClientInfoProgramID(value, tempConverter); } else { oldValue = null; // post generic syntax error for invalid clientInfo name postWarning(JDError.getSQLWarning(JDError.EXC_SYNTAX_ERROR)); } if ((getVRM() >= JDUtilities.vrm610) && (oldValue != null)) { setClientInfoReply = sendAndReceive(request); int errorClass = setClientInfoReply.getErrorClass(); //throw SQLException if (errorClass != 0) JDError.throwSQLException(this, this, id_, errorClass, setClientInfoReply.getReturnCode()); } } catch (Exception e) { //reset old value if (name.equals(applicationNamePropertyName_)) applicationName_ = oldValue; else if (name.equals(clientUserPropertyName_)) clientUser_ = oldValue; else if (name.equals(clientAccountingPropertyName_)) clientAccounting_ = oldValue; else if (name.equals(clientHostnamePropertyName_)) clientHostname_ = oldValue; else if (name.equals(clientProgramIDPropertyName_)) //@pda clientProgramID_ = oldValue; /* ifdef JDBC40 //@PDD jdbc40 merge HashMap m = new HashMap(); HashMap m = new HashMap(); m.put(name, ClientInfoStatus.REASON_UNKNOWN); JDError.throwSQLClientInfoException( this, JDError.EXC_INTERNAL, e, m ); endif */ /* ifndef JDBC40 */ JDError.throwSQLException( this, JDError.EXC_INTERNAL, e); /* endif */ } finally { if (request != null) { request.returnToPool(); request = null; } if (setClientInfoReply != null) { setClientInfoReply.returnToPool(); setClientInfoReply = null; // only error class used } } } //@PDA 550 client info /** * Sets the value of the connection's client info properties. The * Properties object contains the names and values of the * client info properties to be set. The set of client info properties * contained in the properties list replaces the current set of client info * properties on the connection. If a property that is currently set on the * connection is not present in the properties list, that property is * cleared. Specifying an empty properties list will clear all of the * properties on the connection. See * setClientInfo (String, String) for more information. *

* If an error occurs in setting any of the client info properties, a * ClientInfoException is thrown. The * ClientInfoException contains information indicating which * client info properties were not set. The state of the client information * is unknown because some databases do not allow multiple client info * properties to be set atomically. For those databases, one or more * properties may have been set before the error occurred. *

* * The following client info properties are supported in Toobox for Java. *

    *
  • ApplicationName - The name of the application currently utilizing * the connection
  • *
  • ClientUser - The name of the user that the application using * the connection is performing work for. This may * not be the same as the user name that was used * in establishing the connection.
  • *
  • ClientHostname - The hostname of the computer the application * using the connection is running on.
  • *
  • ClientAccounting - Client accounting information.
  • *
  • ClientProgramID - The client program identification.
  • *
*

* * @param properties * the list of client info properties to set *

//JDBC40DOC * @throws SQLClientInfoException If there is a problem with the Client Info. //JDBC40DOC * if the database returns an error while setting the //JDBC40DOC * clientInfo values on the database *

*/ public void setClientInfo(Properties properties) /* ifdef JDBC40 throws SQLClientInfoException endif */ /* ifndef JDBC40 */ throws SQLException /* endif */ { String newApplicationName = properties.getProperty(applicationNamePropertyName_); String newClientHostname = properties.getProperty(clientHostnamePropertyName_); String newClientUser = properties.getProperty(clientUserPropertyName_); String newClientAccounting = properties.getProperty(clientAccountingPropertyName_); String newClientProgramID = properties.getProperty(clientProgramIDPropertyName_); //@pda //In order to reset if null value is passed in, use empty string //per javadoc, clear its value if not specified in properties if (newApplicationName == null) newApplicationName = ""; if (newClientHostname == null) newClientHostname = ""; if (newClientUser == null) newClientUser = ""; if (newClientAccounting == null) newClientAccounting = ""; if (newClientProgramID == null) //@PDA newClientProgramID = ""; DBSQLAttributesDS request = null; DBReplyRequestedDS setClientInfoReply = null; ConvTable tempConverter = null; try { if (getVRM() >= JDUtilities.vrm610) { request = DBDSPool.getDBSQLAttributesDS(DBSQLAttributesDS.FUNCTIONID_SET_ATTRIBUTES, id_, DBBaseRequestDS.ORS_BITMAP_RETURN_DATA + DBBaseRequestDS.ORS_BITMAP_SERVER_ATTRIBUTES, 0); tempConverter = ConvTable.getTable(as400_.getCcsid(), null); request.setClientInfoApplicationName(newApplicationName, tempConverter); request.setClientInfoClientUser(newClientUser, tempConverter); request.setClientInfoClientAccounting(newClientAccounting, tempConverter); request.setClientInfoClientHostname(newClientHostname, tempConverter); request.setClientInfoProgramID(newClientProgramID, tempConverter); //@pda setClientInfoReply = sendAndReceive(request); int errorClass = setClientInfoReply.getErrorClass(); if (errorClass != 0) JDError.throwSQLException(this, this, id_, errorClass, setClientInfoReply.getReturnCode()); } //update local values after request/reply in case of exception applicationName_ = newApplicationName; clientHostname_ = newClientHostname; clientUser_ = newClientUser; clientAccounting_ = newClientAccounting; clientProgramID_ = newClientProgramID; } catch( Exception e) { /* ifdef JDBC40 //create Map for exception constructor //@PDD jdbc40 merge HashMap m = new HashMap(); HashMap m = new HashMap(); Enumeration clientInfoNames = properties.keys(); while( clientInfoNames.hasMoreElements()) { String clientInfoName = (String)clientInfoNames.nextElement(); m.put(clientInfoName, ClientInfoStatus.REASON_UNKNOWN); } JDError.throwSQLClientInfoException( this, JDError.EXC_INTERNAL, e, m); endif */ /* ifndef JDBC40 */ JDError.throwSQLException( this, JDError.EXC_INTERNAL, e); /* endif */ } finally { if (request != null) { request.returnToPool(); request = null; } if (setClientInfoReply != null) { setClientInfoReply.returnToPool(); setClientInfoReply=null; // only error class used } } } //@PDA 550 client info /** * Returns the value of the client info property specified by name. This * method may return null if the specified client info property has not * been set and does not have a default value. This method will also * return null if the specified client info property name is not supported * by the driver. *

* Applications may use the DatabaseMetaData.getClientInfoProperties * method to determine the client info properties supported by the driver. *

* * The following client info properties are supported in Toobox for Java. *

    *
  • ApplicationName - The name of the application currently utilizing * the connection
  • *
  • ClientUser - The name of the user that the application using * the connection is performing work for. This may * not be the same as the user name that was used * in establishing the connection.
  • *
  • ClientHostname - The hostname of the computer the application * using the connection is running on.
  • *
  • ClientAccounting - Client accounting information.
  • *
  • ClientProgramID - The client program identification.
  • *
*

* @param name The name of the client info property to retrieve *

* @return The value of the client info property specified *

* @throws SQLException if the database returns an error when * fetching the client info value from the database. *

* see java.sql.DatabaseMetaData#getClientInfoProperties */ public String getClientInfo(String name) throws SQLException { if (name.equals(applicationNamePropertyName_)) return applicationName_; else if (name.equals(clientUserPropertyName_)) return clientUser_; else if (name.equals(clientAccountingPropertyName_)) return clientAccounting_; else if (name.equals(clientHostnamePropertyName_)) return clientHostname_; else if (name.equals(clientProgramIDPropertyName_)) //@pda return clientProgramID_; else { //post generic syntax error for invalid clientInfo name //since javadoc for setClientInfo(String,String) says to generate warning, we will do same here and return null postWarning(JDError.getSQLWarning(JDError.EXC_SYNTAX_ERROR)); return null; } } //@PDA 550 client info /** * Returns a list containing the name and current value of each client info * property supported by the driver. The value of a client info property * may be null if the property has not been set and does not have a * default value. *

* * The following client info properties are supported in Toobox for Java. *

    *
  • ApplicationName - The name of the application currently utilizing * the connection
  • *
  • ClientUser - The name of the user that the application using * the connection is performing work for. This may * not be the same as the user name that was used * in establishing the connection.
  • *
  • ClientHostname - The hostname of the computer the application * using the connection is running on.
  • *
  • ClientAccounting - Client accounting information.
  • *
  • ClientProgramID - The client program identification.
  • *
*

* @return A Properties object that contains the name and current value of * each of the client info properties supported by the driver. *

* @throws SQLException if the database returns an error when * fetching the client info values from the database */ public Properties getClientInfo() throws SQLException { Properties props = new Properties(); props.setProperty(applicationNamePropertyName_, applicationName_); props.setProperty(clientAccountingPropertyName_, clientAccounting_); props.setProperty(clientHostnamePropertyName_, clientHostname_); props.setProperty(clientUserPropertyName_, clientUser_); props.setProperty(clientProgramIDPropertyName_, clientProgramID_); //@pda return props; } //@PDA jdbc40 /** * Constructs an object that implements the Clob interface. The object * returned initially contains no data. The setAsciiStream, * setCharacterStream and setString methods of * the Clob interface may be used to add data to the Clob. * @return An object that implements the Clob interface * @throws SQLException if an object that implements the * Clob interface can not be constructed. * */ public Clob createClob() throws SQLException { return new AS400JDBCClob("", AS400JDBCClob.MAX_LOB_SIZE); } //@PDA jdbc40 /** * Constructs an object that implements the Blob interface. The object * returned initially contains no data. The setBinaryStream and * setBytes methods of the Blob interface may be used to add data to * the Blob. * @return An object that implements the Blob interface * @throws SQLException if an object that implements the * Blob interface can not be constructed * */ public Blob createBlob() throws SQLException { return new AS400JDBCBlob(new byte[0], AS400JDBCBlob.MAX_LOB_SIZE); //@pdc 0 len array } //@PDA jdbc40 //JDBC40DOC /** //JDBC40DOC * Constructs an object that implements the NClob interface. The object //JDBC40DOC * returned initially contains no data. The setAsciiStream, //JDBC40DOC * setCharacterStream and setString methods of the NClob interface may //JDBC40DOC * be used to add data to the NClob. //JDBC40DOC * @return An object that implements the NClob interface //JDBC40DOC * @throws SQLException if an object that implements the //JDBC40DOC * NClob interface can not be constructed. //JDBC40DOC * //JDBC40DOC */ /*ifdef JDBC40 public NClob createNClob() throws SQLException { return new AS400JDBCNClob("", AS400JDBCNClob.MAX_LOB_SIZE); } endif */ //@PDA jdbc40 //JDBC40DOC /** //JDBC40DOC * Constructs an object that implements the SQLXML interface. The object //JDBC40DOC * returned initially contains no data. The createXMLStreamWriter object and //JDBC40DOC * setString method of the SQLXML interface may be used to add data to the SQLXML //JDBC40DOC * object. //JDBC40DOC * @return An object that implements the SQLXML interface //JDBC40DOC * @throws SQLException if an object that implements the SQLXML interface can not //JDBC40DOC * be constructed //JDBC40DOC */ /*ifdef JDBC40 public SQLXML createSQLXML() throws SQLException { return new AS400JDBCSQLXML(AS400JDBCSQLXML.MAX_XML_SIZE); } endif */ //@PDA //@array /** * Factory method for creating Array objects. * * @param typeName the SQL name of the type the elements of the array map to. The typeName is a * database-specific name which may be the name of a built-in type, a user-defined type or a standard SQL type supported by this database. This * is the value returned by Array.getBaseTypeName * For Toolbox, the typeName will correspond to a typename in java.sql.Types. * * @param elements the elements that populate the returned object * @return an Array object whose elements map to the specified SQL type * @throws SQLException if a database error occurs, the typeName is null or this method is called on a closed connection */ public Array createArrayOf(String typeName, Object[] elements) throws SQLException { //@array return new AS400JDBCArray(typeName, elements, this.vrm_, this); } //@PDA jdbc40 /** * Factory method for creating Struct objects. * * @param typeName the SQL type name of the SQL structured type that this Struct * object maps to. The typeName is the name of a user-defined type that * has been defined for this database. It is the value returned by * Struct.getSQLTypeName. * @param attributes the attributes that populate the returned object * @return a Struct object that maps to the given SQL type and is populated with the given attributes * @throws SQLException if a database error occurs, the typeName is null or this method is called on a closed connection */ public Struct createStruct(String typeName, Object[] attributes) throws SQLException { JDError.throwSQLException (this, JDError.EXC_FUNCTION_NOT_SUPPORTED); return null; } //@2KRA /** * Starts or stops the Database Host Server trace for this connection. * Note: This method is only supported when running to IBM i V5R3 or later * and is ignored if you specified to turn on database host server tracing * using the 'server trace' connection property. * @param trace true to start database host server tracing, false to end it. */ public void setDBHostServerTrace(boolean trace){ try{ if(getVRM() >= JDUtilities.vrm530){ // See if tracing was specified by server trace property // Server Job Trace boolean traceServerJob = ((traceServer_ & ServerTrace.JDBC_TRACE_SERVER_JOB) > 0); // Database Host Server Trace boolean traceDatabaseHostServer = (((traceServer_ & ServerTrace.JDBC_TRACE_DATABASE_HOST_SERVER) > 0)); String serverJobIdentifier = getServerJobIdentifier(); boolean SQLNaming = properties_.getString(JDProperties.NAMING).equals(JDProperties.NAMING_SQL); if(!traceDatabaseHostServer){ // database host server trace was not already started if(trace) // user requested tracing be turned on { try{ if(getVRM() == JDUtilities.vrm530){ // run command for V5R3 JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + serverJobIdentifier.substring(20) + ") JOB(*) MAXSTG(128000) JOBTRCTYPE(*TRCTYPE) " + "TRCTYPE((TESTA *INFO))", SQLNaming); } else{ // run command for V5R4 and higher JDUtilities.runCommand(this, "QSYS/STRTRC SSNID(QJT" + serverJobIdentifier.substring(20) + ") JOB(*) MAXSTG(128000) JOBTRCTYPE(*TRCTYPE) " + "TRCTYPE((*DBHSVR *INFO))", SQLNaming); } databaseHostServerTrace_ = true; }catch(Exception e){ JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to start database host server tracing failed, could not trace server job"); } } else // user requested tracing be turned off { // Only issue ENDTRC if not already done. if(!traceServerJob) // turn off it we don't have to wait to turn off server job tracing { try{ JDUtilities.runCommand(this, "QSYS/ENDTRC SSNID(QJT" + serverJobIdentifier.substring(20) + ") DTAOPT(*LIB) DTALIB(QUSRSYS) RPLDTA(*YES) PRTTRC(*YES)", SQLNaming ); JDUtilities.runCommand(this, "QSYS/DLTTRC DTAMBR(QJT" + serverJobIdentifier.substring(20) + ") DTALIB(QUSRSYS)", SQLNaming ); databaseHostServerTrace_ = false; } catch(Exception e){ JDTrace.logDataEvenIfTracingIsOff(this, "Attempt to end database host server tracing failed."); } } } } } }catch(SQLException e){ if(JDTrace.isTraceOn()) JDTrace.logInformation(this, "Attempt to start/stop database host server tracing failed."); } } //@A2A public boolean doUpdateDeleteBlocking() { return useBlockUpdate_; } // @A6A public int getMaximumBlockedInputRows() { return maximumBlockedInputRows_; } //JDBC40DOC /** //JDBC40DOC * Terminates an open connection. Calling abort results in: //JDBC40DOC *

    //JDBC40DOC *
  • The connection marked as closed //JDBC40DOC *
  • Closes any physical connection to the database //JDBC40DOC *
  • Releases resources used by the connection //JDBC40DOC *
  • Insures that any thread that is currently accessing the connection will //JDBC40DOC * either progress to completion or throw an SQLException. //JDBC40DOC *
//JDBC40DOC *

//JDBC40DOC * Calling abort marks the connection closed and releases any resources. //JDBC40DOC * Calling abort on a closed connection is a no-op. //JDBC40DOC *

It is possible that the aborting and releasing of the resources that are //JDBC40DOC * held by the connection can take an extended period of time. //JDBC40DOC * When the abort method returns, the connection will have been marked as closed //JDBC40DOC * and the Executor that was passed as a parameter to abort may still be executing //JDBC40DOC * tasks to release resources. //JDBC40DOC *

//JDBC40DOC * This method checks to see that there is an SQLPermission object before //JDBC40DOC * allowing the method to proceed. If a SecurityManager exists and its //JDBC40DOC * checkPermission method denies calling abort, this method throws a //JDBC40DOC * java.lang.SecurityException. //JDBC40DOC * @param executor The Executor implementation which will be used by abort. //JDBC40DOC * @throws SQLException - if a database access error occurs or the executor is null //JDBC40DOC * @throws SecurityException - if a security manager exists and its checkPermission //JDBC40DOC * method denies calling abort //JDBC40DOC */ /* ifdef JDBC40 public void abort(Executor executor) throws SQLException { if (JDTrace.isTraceOn()) JDTrace.logInformation (this, "abort() called"); // Check for null executor if (executor == null) { JDError.throwSQLException(JDError.EXC_PARAMETER_TYPE_INVALID); } // Check for authority SecurityManager security = System.getSecurityManager(); if (security != null) { SQLPermission sqlPermission = new SQLPermission("callAbort"); security.checkPermission(sqlPermission); } // Calling on a close connection is a no-op if (aborted_ || (server_ == null)) { return; } // // Mark the connection as aborted. Any call to closed will see that it is closed. // aborted_ = true; // // Prepare and start the executor to clean everything up. // Runnable runnable = new AS400JDBCConnectionAbortRunnable(this); executor.execute(runnable); } endif */ /** * Retrieves this Connection object's current schema name. * @return the current schema name or null if there is none * @throws SQLException if a database access error occurs or this method is called on a closed connection */ public String getSchema() throws SQLException { Statement s = createStatement(); String query; boolean SQLNaming = properties_.getString(JDProperties.NAMING).equals(JDProperties.NAMING_SQL); if (SQLNaming) { query = "SELECT CURRENT SCHEMA FROM SYSIBM.SYSDUMMY1"; } else { query = "SELECT CURRENT SCHEMA FROM SYSIBM/SYSDUMMY1"; } ResultSet rs = s.executeQuery(query); rs.next(); String schema = rs.getString(1); rs.close(); s.close(); return schema; } /** * Sets the maximum period a Connection or objects created from the * Connection will wait for the database to reply to any one request. If * any request remains unanswered, the waiting method will return with a * SQLException, and the Connection or objects created from the * Connection will be marked as closed. Any subsequent use of the objects, * with the exception of the close, isClosed or Connection.isValid methods, * will result in a SQLException. * *

In the JTOpen JDBC driver, this is implemented by setting the SoTimeout * of the underlying socket. *

Currently, setting the network timeout is only supported when the * "thread used" property is false. *

When the driver determines that the setNetworkTimeout timeout value * has expired, the JDBC driver marks the connection closed and releases * any resources held by the connection. *

This method checks to see that there is an SQLPermission object before * allowing the method to proceed. * If a SecurityManager exists and its checkPermission method denies calling * setNetworkTimeout, this method throws a java.lang.SecurityException. *@param timeout - The time in milliseconds to wait for the database * operation to complete. If the JDBC driver does not support milliseconds, * the JDBC driver will round the value up to the nearest second. * If the timeout period expires before the operation completes, a * SQLException will be thrown. A value of 0 indicates that there is no * timeout for database operations. * @throws SQLException - if a database access error occurs, this method is * called on a closed connection,or the value specified for seconds is less * than 0. * @throws SecurityException - if a security manager exists and its * checkPermission method denies calling setNetworkTimeout. * @see SecurityManager#checkPermission(java.security.Permission) * @see Statement#setQueryTimeout(int) * @see #getNetworkTimeout() //JDBC40DOC * @see #abort(java.util.concurrent.Executor) //JDBC40DOC * @see Executor */ public void setNetworkTimeout(int timeout) throws SQLException { /* ifdef JDBC40 SecurityManager security = System.getSecurityManager(); if (security != null) { SQLPermission sqlPermission = new SQLPermission("setNetworkTimeout"); security.checkPermission(sqlPermission); } endif */ // Make sure that the THREAD_USED property is false. The default is true String threadUsedProperty = properties_.getString(JDProperties.THREAD_USED); if (threadUsedProperty == null) { if (timeout > 0 ) { JDError.throwSQLException(JDError.EXC_FUNCTION_NOT_SUPPORTED); } } else { if (threadUsedProperty.equalsIgnoreCase("true")) { if (timeout > 0 ) { JDError.throwSQLException(JDError.EXC_FUNCTION_NOT_SUPPORTED); } } } // Make sure value is not negative if (timeout < 0) { JDError.throwSQLException(JDError.EXC_PARAMETER_TYPE_INVALID); } // Calling on a closed connection is a no-op checkOpen (); try { server_.setSoTimeout(timeout); } catch (SocketException e) { JDError.throwSQLException(JDError.EXC_COMMUNICATION_LINK_FAILURE, e); } } /** * Retrieves the number of milliseconds the driver will wait for a database request to complete. If the limit is exceeded, a SQLException is thrown. * @return The current timeout limit in milliseconds; zero means there is no limit * @throws SQLException - if a database access error occurs or this method is called on a closed Connection * @since JTOpen 7.X //JDBC40DOC * @see #setNetworkTimeout(java.util.concurrent.Executor, int) */ public int getNetworkTimeout() throws SQLException { checkOpen (); try { return server_.getSoTimeout(); } catch (SocketException e) { JDError.throwSQLException(JDError.EXC_COMMUNICATION_LINK_FAILURE, e); return 0; } } //JDBC40DOC /** //JDBC40DOC * Sets the maximum period a Connection or objects created from the Connection will wait for the database to //JDBC40DOC * reply to any one request. If any request remains unanswered, the waiting method will return with a //JDBC40DOC * SQLException, and the Connection or objects created from the Connection will be marked as closed. //JDBC40DOC * Any subsequent use of the objects, with the exception of the close, isClosed or Connection.isValid methods, //JDBC40DOC * will result in a SQLException. //JDBC40DOC *

Note: This method is intended to address a rare but serious condition where network partitions can //JDBC40DOC * cause threads issuing JDBC calls to hang uninterruptedly in socket reads, until the OS TCP-TIMEOUT //JDBC40DOC * (typically 10 minutes). This method is related to the abort() method which provides an administrator //JDBC40DOC * thread a means to free any such threads in cases where the JDBC connection is accessible to the //JDBC40DOC * administrator thread. The setNetworkTimeout method will cover cases where there is no administrator //JDBC40DOC * thread, or it has no access to the connection. This method is severe in it's effects, and should be //JDBC40DOC * given a high enough value so it is never triggered before any more normal timeouts, //JDBC40DOC * such as transaction timeouts. //JDBC40DOC *

JDBC driver implementations may also choose to support the setNetworkTimeout method to impose //JDBC40DOC * a limit on database response time, in environments where no network is present. //JDBC40DOC *

Drivers may internally implement some or all of their API calls with multiple internal driver-database //JDBC40DOC * transmissions, and it is left to the driver implementation to determine whether the limit will be //JDBC40DOC * applied always to the response to the API call, or to any single request made during the API call. //JDBC40DOC *

This method can be invoked more than once, such as to set a limit for an area of JDBC code, //JDBC40DOC * and to reset to the default on exit from this area. Invocation of this method has no impact on //JDBC40DOC * already outstanding requests. //JDBC40DOC *

The Statement.setQueryTimeout() timeout value is independent of the timeout value specified in //JDBC40DOC * setNetworkTimeout. If the query timeout expires before the network timeout then the statement execution //JDBC40DOC * will be canceled. If the network is still active the result will be that both the statement and connection //JDBC40DOC * are still usable. However if the network timeout expires before the query timeout or if the statement timeout //JDBC40DOC * fails due to network problems, the connection will be marked as closed, any resources held by the connection //JDBC40DOC * will be released and both the connection and statement will be unusable. //JDBC40DOC *

When the driver determines that the setNetworkTimeout timeout value has expired, the JDBC driver marks //JDBC40DOC * the connection closed and releases any resources held by the connection. //JDBC40DOC *

This method checks to see that there is an SQLPermission object before allowing the method to proceed. //JDBC40DOC * If a SecurityManager exists and its checkPermission method denies calling setNetworkTimeout, this method //JDBC40DOC * throws a java.lang.SecurityException. //JDBC40DOC *@param executor - The Executor implementation which will be used by setNetworkTimeout. //JDBC40DOC *@param milliseconds - The time in milliseconds to wait for the database operation to complete. If the //JDBC40DOC * JDBC driver does not support milliseconds, the JDBC driver will round the value up to the nearest second. //JDBC40DOC * If the timeout period expires before the operation completes, a SQLException will be thrown. A value of //JDBC40DOC * 0 indicates that there is not timeout for database operations. //JDBC40DOC * @throws SQLException - if a database access error occurs, this method is called on a closed connection, //JDBC40DOC * the executor is null, or the value specified for seconds is less than 0. //JDBC40DOC * @throws SecurityException - if a security manager exists and its checkPermission method denies calling //JDBC40DOC * setNetworkTimeout. //JDBC40DOC * @see SecurityManager#checkPermission(java.security.Permission) //JDBC40DOC * @see Statement#setQueryTimeout(int) //JDBC40DOC * @see #getNetworkTimeout() //JDBC40DOC * @see #abort(java.util.concurrent.Executor) //JDBC40DOC * @see Executor //JDBC40DOC **/ /* ifdef JDBC40 public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { // TODO JDBC41 Auto-generated method stub // Make sure value is not negative if (milliseconds < 0) { JDError.throwSQLException(JDError.EXC_PARAMETER_TYPE_INVALID); } // Check for null executor if (executor == null) { JDError.throwSQLException(JDError.EXC_PARAMETER_TYPE_INVALID); } // Check for authority SecurityManager security = System.getSecurityManager(); if (security != null) { SQLPermission sqlPermission = new SQLPermission("setNetworkTimeout"); security.checkPermission(sqlPermission); } checkOpen (); try { server_.setSoTimeout(milliseconds); } catch (java.net.SocketException socketException) { JDError.throwSQLException(JDError.EXC_COMMUNICATION_LINK_FAILURE, socketException); } } endif */ /** *Sets the given schema name to access. *

* Calling setSchema has no effect on previously created or prepared Statement objects. * For the toolbox driver, the DBMS prepare operation takes place immediately when the * Connection method prepareStatement or prepareCall is invoked. * For maximum portability, setSchema should be called before a Statement is created or prepared. * * @param schema The name of the schema to use for the connection * @throws SQLException If a database access error occurs or this method is * called on a closed connection * */ public void setSchema(String schema) throws SQLException { checkOpen (); if (schema.length() > 0 && schema.charAt(0) == '"') { // If delimited name pass as is } else { // Name is not delimited, make sure it is upper case schema = schema.toUpperCase().trim(); } PreparedStatement ps; // If system naming is in used and *LIBL is passed, change the schema // to DEFAULT. // This is to prevent problem with connection pools that call getSchema (which returns *LIBL) // followed by setSchema(*LIBL) @S6A boolean SQLNaming = properties_.getString(JDProperties.NAMING).equals(JDProperties.NAMING_SQL); if ("DEFAULT".equals(schema) || ((!SQLNaming) && ("*LIBL".equals(schema)))) { ps = prepareStatement("SET CURRENT SCHEMA DEFAULT "); } else { ps = prepareStatement("SET CURRENT SCHEMA ? "); ps.setString(1, schema); } ps.executeUpdate(); ps.close(); } /** * Is SQL cancel used for the query timeout mechanism * @return true if cancel will be used as the query timeout mechanism */ public boolean isQueryTimeoutMechanismCancel() { return queryTimeoutMechanism_ == QUERY_TIMEOUT_CANCEL; } /** * Setup the variableFieldCompression flags @K3A */ public void setupVariableFieldCompression() { if (!variableFieldCompressionPropertyEvaluated_) { boolean variableFieldInsertCompressionAvailable = false; if (serverFunctionalLevel_ >= 16) { variableFieldInsertCompressionAvailable = true; } if (serverFunctionalLevel_ >= 14) { String property = null; try { property = getProperties().getString( JDProperties.VARIABLE_FIELD_COMPRESSION); } catch (Exception e) { // Just use defaults } if (property == null) property = "default"; property = property.toLowerCase().trim(); if ("false".equals(property)) { useVariableFieldCompression_ = false; useVariableFieldInsertCompression_ = false; variableFieldCompressionPropertyEvaluated_ = true; } else if ("true".equals(property)) { useVariableFieldCompression_ = true; useVariableFieldInsertCompression_ = false; variableFieldCompressionPropertyEvaluated_ = true; } else if ("insert".equals(property)) { useVariableFieldCompression_ = false; useVariableFieldInsertCompression_ = variableFieldInsertCompressionAvailable; variableFieldCompressionPropertyEvaluated_ = true; } else { // Default is to use all possible compression useVariableFieldCompression_ = true; useVariableFieldInsertCompression_ = variableFieldInsertCompressionAvailable; variableFieldCompressionPropertyEvaluated_ = true; } } else { // server does not support any form of compression useVariableFieldCompression_ = false; useVariableFieldInsertCompression_ = false; variableFieldCompressionPropertyEvaluated_ = true; } } } public boolean useVariableFieldCompression() { if (!variableFieldCompressionPropertyEvaluated_) { setupVariableFieldCompression(); } return useVariableFieldCompression_; } public boolean useVariableFieldInsertCompression() { if (!variableFieldCompressionPropertyEvaluated_) { setupVariableFieldCompression(); } return useVariableFieldInsertCompression_; } //@L9A public void setDisableCompression(boolean disableCompression_) { this.disableCompression_ = disableCompression_; } public void dumpStatementCreationLocation() { if (JDTrace.isTraceOn()) { JDTrace.logInformation(this, "Dumping creation information for statements"); Vector statements = (Vector)statements_.clone(); Enumeration list = statements.elements(); while (list.hasMoreElements()) { AS400JDBCStatement statement = (AS400JDBCStatement)list.nextElement(); if (statement.creationLocation_ == null) { JDTrace.logInformation(statement, "No creation information"); } else { JDTrace.logException(statement, "Creation information", statement.creationLocation_); } } } } /** * Tests if a DataTruncation occurred on the write of a piece of data and * throws a DataTruncation exception if so. The data truncation flag is also * taken into consideration for string data. The rules are: * 1) If updating or querying database with numeric data and data truncated, * throw exception * 2) If string data and suppress truncation, return * 3) If updating database with string data and check truncation and data * truncated, throw exception * 4) If string data is part of a query and check truncation and data truncated, * post warning * * @param parameterIndex * The index (1-based). * @param data * The data that was written or null for SQL NULL. **/ public boolean testDataTruncation(AS400JDBCStatement statementWarningObject, AS400JDBCResultSet resultSetWarningObject, int parameterIndex, boolean isParameter, SQLData data, JDSQLStatement sqlStatement) throws SQLException // @trunc @X4C { boolean checkRawBytes = false; if (data != null) { if (data.getOutOfBounds()) { // Clear the error so it is not reported twice data.clearOutOfBounds(); switch (numericRangeError_) { case NUMERIC_RANGE_ERROR_DEFAULT: // We now always throw a data type mismatch for a range error. // In some cases, a truncation exception was being thrown. // To be consistent, we will now throw the same error. JDError.throwSQLException(this, JDError.EXC_DATA_TYPE_MISMATCH, "P#="+parameterIndex); break; case NUMERIC_RANGE_ERROR_WARNING: if (statementWarningObject != null) { statementWarningObject.postWarning( JDError.getSQLWarning(JDError.EXC_DATA_TYPE_MISMATCH)); } else if (resultSetWarningObject != null ) { resultSetWarningObject.postWarning( JDError.getSQLWarning(JDError.EXC_DATA_TYPE_MISMATCH)); } break; case NUMERIC_RANGE_ERROR_NONE: break; } } } if (data != null && (characterTruncation_ != CHARACTER_TRUNCATION_NONE ) && data.isText()) { // The SQLData object determined if data was truncated as part of the // setValue() processing. int truncated = data.getTruncated(); if (truncated > 0) { // Clear the truncation so it is not reported twice data.clearTruncated(); int actualSize = data.getActualSize(); // boolean isRead = sqlStatement_.isSelect(); //@pda jdbc40 //@pdc same // as native (only select is read) //@trunc //@pdc match native DataTruncation dt = new DataTruncation(parameterIndex, isParameter, false, actualSize + truncated, actualSize); // @pdc jdbc40 //@trunc //@pdc // match native // if 610 and number data type, then throw DataTruncation // if text, then use old code path and post/throw DataTruncation if ((getVRM() >= JDUtilities.vrm610) && (data.isText() == false)) // @trunc2 { // @trunc2 if (characterTruncation_ == CHARACTER_TRUNCATION_WARNING) { if (statementWarningObject != null) { statementWarningObject.postWarning( dt); } else if (resultSetWarningObject != null ) { resultSetWarningObject.postWarning( dt); } } else { throw dt; // @trunc2 } } // @trunc2 else if ((sqlStatement != null) && (sqlStatement.isSelect()) && (!sqlStatement.isSelectFromInsert())) // @trunc2 //@selins1 { if (statementWarningObject != null) { statementWarningObject.postWarning( dt); } else if (resultSetWarningObject != null ) { resultSetWarningObject.postWarning( dt); } // If we want the data replace on a warning. Go ahead and // do the replacement. if (queryReplaceTruncatedParameter_ != null) { data.set(queryReplaceTruncatedParameter_, null, 0); } } else { if (characterTruncation_ == CHARACTER_TRUNCATION_WARNING) { if (statementWarningObject != null ) { statementWarningObject.postWarning( dt); } else if (resultSetWarningObject != null ) { resultSetWarningObject.postWarning( dt); } } else { throw dt; } } } /* @X4A */ } else if (data != null && (characterTruncation_ == CHARACTER_TRUNCATION_NONE ) && data.isText()) { int truncated = data.getTruncated(); if (truncated > 0) { // Clear the truncation so it is not reported twice data.clearTruncated(); checkRawBytes = true; } } return checkRawBytes; /*@X4A*/ } public ConvTable getConverter() { return converter_; } public void setLastServerSQLState(String lastSqlState) { lastServerSQLState_ = lastSqlState; } public String getLastServerSQLState() { return lastServerSQLState_; } public ConvTable getPackageCCSID_Converter() { return packageCCSID_Converter; } /** * transfer objects associated with this connection to a new connection. * @param newConnection */ void transferObjects(AS400JDBCConnectionImpl newConnection) { for (int i =0; i< MAX_STATEMENTS_; i++) { newConnection.assigned_[i] = assigned_[i]; } newConnection.statements_ = statements_; statements_ = new Vector(); } /** * close all the current result sets in preparation for moving the * statements to another connection. */ void closeAllResultSets() { if (statements_ != null) { Enumeration statementsEnum = statements_.elements(); while (statementsEnum.hasMoreElements()){ AS400JDBCStatement statement = (AS400JDBCStatement) statementsEnum.nextElement(); try { statement.closeResultSet(JDCursor.REUSE_NO); } catch (SQLException e) { } } } } /** * mark the statements so they know that the connection was reset. * If the connection is reset, any prepared statements will be * lazily reprepared. * Also see the transaction manager for the statement. */ void resetStatements(JDTransactionManager tm) { if (statements_ != null) { Enumeration statementsEnum = statements_.elements(); while (statementsEnum.hasMoreElements()){ AS400JDBCStatement statement = (AS400JDBCStatement) statementsEnum.nextElement(); statement.setConnectionReset(true); statement.transactionManager_ = tm; } } } public boolean getReadOnly() { return readOnly_; } public boolean getCheckStatementHoldability() { return checkStatementHoldability_; } int getNewAutoCommitSupport() { return newAutoCommitSupport_; } /** * Can the operation be retried after EXC_CONNECTION_REESTABLISHED. * * The rules for being able to retry are the following. *

    *
  • The connection was not being used for a transaction at the time the failure occurred. *
  • There are no outstanding global resources, such as global temporary tables or open, held cursors, or connection states that prevent a seamless failover to another server. *
*/ boolean canSeamlessFailover() { if (transactionManager_ != null) { if (transactionManager_.isLocalActive()) { return false; } if (transactionManager_.isGlobalActive()) { return false; } } return true; } String[] emptyStringArray = null; public String[] getReconnectURLs() { if (emptyStringArray == null) { emptyStringArray = new String[0]; } return emptyStringArray; } // @X1A public String getAlternateServer() { return alternateServer_; } public String getHostName() { return systemName_; } public String getPort() { return portNumberString; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy