0001: /*
0002: Derby - Class org.apache.derby.impl.drda.DRDAConnThread
0003:
0004: Licensed to the Apache Software Foundation (ASF) under one or more
0005: contributor license agreements. See the NOTICE file distributed with
0006: this work for additional information regarding copyright ownership.
0007: The ASF licenses this file to You under the Apache License, Version 2.0
0008: (the "License"); you may not use this file except in compliance with
0009: the License. You may obtain a copy of the License at
0010:
0011: http://www.apache.org/licenses/LICENSE-2.0
0012:
0013: Unless required by applicable law or agreed to in writing, software
0014: distributed under the License is distributed on an "AS IS" BASIS,
0015: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016: See the License for the specific language governing permissions and
0017: limitations under the License.
0018:
0019: */
0020: /**
0021: * This class translates DRDA protocol from an application requester to JDBC
0022: * for Cloudscape and then translates the results from Cloudscape to DRDA
0023: * for return to the application requester.
0024: * @author ge, marsden, peachey
0025: */package org.apache.derby.impl.drda;
0026:
0027: import java.io.ByteArrayInputStream;
0028: import java.io.IOException;
0029: import java.io.InputStream;
0030: import java.io.OutputStream;
0031: import java.io.IOException;
0032: import java.io.UnsupportedEncodingException;
0033: import java.math.BigDecimal;
0034: import java.sql.CallableStatement;
0035: import java.sql.Connection;
0036: import java.sql.Driver;
0037: import java.sql.PreparedStatement;
0038: import java.sql.ResultSet;
0039: import java.sql.ResultSetMetaData;
0040: import java.sql.SQLException;
0041: import java.sql.SQLWarning;
0042: import java.sql.Statement;
0043: import java.sql.Types;
0044: import java.util.ArrayList;
0045: import java.util.Date;
0046: import java.util.Properties;
0047: import java.util.Vector;
0048:
0049: import org.apache.derby.catalog.SystemProcedures;
0050: import org.apache.derby.iapi.error.ExceptionSeverity;
0051: import org.apache.derby.iapi.reference.Attribute;
0052: import org.apache.derby.iapi.reference.DRDAConstants;
0053: import org.apache.derby.iapi.reference.JDBC30Translation;
0054: import org.apache.derby.iapi.reference.Property;
0055: import org.apache.derby.iapi.reference.SQLState;
0056: import org.apache.derby.iapi.services.info.JVMInfo;
0057: import org.apache.derby.iapi.services.monitor.Monitor;
0058: import org.apache.derby.iapi.services.sanity.SanityManager;
0059: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
0060: import org.apache.derby.iapi.tools.i18n.LocalizedResource;
0061: import org.apache.derby.iapi.jdbc.AuthenticationService;
0062: import org.apache.derby.iapi.jdbc.EngineParameterMetaData;
0063: import org.apache.derby.impl.jdbc.EmbedSQLException;
0064: import org.apache.derby.impl.jdbc.Util;
0065: import org.apache.derby.jdbc.InternalDriver;
0066:
0067: class DRDAConnThread extends Thread {
0068:
0069: private static final String leftBrace = "{";
0070: private static final String rightBrace = "}";
0071: private static final byte NULL_VALUE = (byte) 0xff;
0072: private static final String SYNTAX_ERR = "42X01";
0073:
0074: // Manager Level 3 constant.
0075: private static final int MGRLVL_3 = 0x03;
0076:
0077: // Manager Level 4 constant.
0078: private static final int MGRLVL_4 = 0x04;
0079:
0080: // Manager Level 5 constant.
0081: private static final int MGRLVL_5 = 0x05;
0082:
0083: // Manager level 6 constant.
0084: private static final int MGRLVL_6 = 0x06;
0085:
0086: // Manager Level 7 constant.
0087: private static final int MGRLVL_7 = 0x07;
0088:
0089: // Commit or rollback UOWDSP values
0090: private static final int COMMIT = 1;
0091: private static final int ROLLBACK = 2;
0092:
0093: protected CcsidManager ccsidManager = new EbcdicCcsidManager();
0094: private int correlationID;
0095: private InputStream sockis;
0096: private OutputStream sockos;
0097: private DDMReader reader;
0098: private DDMWriter writer;
0099: private DRDAXAProtocol xaProto;
0100:
0101: private static int[] ACCRDB_REQUIRED = { CodePoint.RDBACCCL,
0102: CodePoint.CRRTKN, CodePoint.PRDID, CodePoint.TYPDEFNAM,
0103: CodePoint.TYPDEFOVR };
0104:
0105: private static int MAX_REQUIRED_LEN = 5;
0106:
0107: private int currentRequiredLength = 0;
0108: private int[] required = new int[MAX_REQUIRED_LEN];
0109:
0110: private NetworkServerControlImpl server; // server who created me
0111: private Session session; // information about the session
0112: private long timeSlice; // time slice for this thread
0113: private Object timeSliceSync = new Object(); // sync object for updating time slice
0114: private boolean logConnections; // log connections to databases
0115:
0116: private boolean sendWarningsOnCNTQRY = false; // Send Warnings for SELECT if true
0117: private Object logConnectionsSync = new Object(); // sync object for log connect
0118: private boolean close; // end this thread
0119: private Object closeSync = new Object(); // sync object for parent to close us down
0120: private static HeaderPrintWriter logStream;
0121: private AppRequester appRequester; // pointer to the application requester
0122: // for the session being serviced
0123: private Database database; // pointer to the current database
0124: private int sqlamLevel; // SQLAM Level - determines protocol
0125:
0126: // DRDA diagnostic level, DIAGLVL0 by default
0127: private byte diagnosticLevel = (byte) 0xF0;
0128:
0129: // manager processing
0130: private Vector unknownManagers;
0131: private Vector knownManagers;
0132: private Vector errorManagers;
0133: private Vector errorManagersLevel;
0134:
0135: // database accessed failed
0136: private SQLException databaseAccessException;
0137:
0138: // these fields are needed to feed back to jcc about a statement/procedure's PKGNAMCSN
0139: /** The value returned by the previous call to
0140: * <code>parsePKGNAMCSN()</code>. */
0141: private Pkgnamcsn prevPkgnamcsn = null;
0142: /** Current RDB Package Name. */
0143: private DRDAString rdbnam = new DRDAString(ccsidManager);
0144: /** Current RDB Collection Identifier. */
0145: private DRDAString rdbcolid = new DRDAString(ccsidManager);
0146: /** Current RDB Package Identifier. */
0147: private DRDAString pkgid = new DRDAString(ccsidManager);
0148: /** Current RDB Package Consistency Token. */
0149: private DRDAString pkgcnstkn = new DRDAString(ccsidManager);
0150: /** Current RDB Package Section Number. */
0151: private int pkgsn;
0152:
0153: private final static String TIMEOUT_STATEMENT = "SET STATEMENT_TIMEOUT ";
0154:
0155: private int pendingStatementTimeout; // < 0 means no pending timeout to set
0156:
0157: // this flag is for an execute statement/procedure which actually returns a result set;
0158: // do not commit the statement, otherwise result set is closed
0159:
0160: // for decryption
0161: private static DecryptionManager decryptionManager;
0162:
0163: // public key generated by Deffie-Hellman algorithm, to be passed to the encrypter,
0164: // as well as used to initialize the cipher
0165: private byte[] myPublicKey;
0166:
0167: // generated target seed to be used to generate the password substitute
0168: // as part of SECMEC_USRSSBPWD security mechanism
0169: private byte[] myTargetSeed;
0170:
0171: // Some byte[] constants that are frequently written into messages. It is more efficient to
0172: // use these constants than to convert from a String each time
0173: // (This replaces the qryscraft_ and notQryscraft_ static exception objects.)
0174: private static final byte[] eod00000 = { '0', '0', '0', '0', '0' };
0175: private static final byte[] eod02000 = { '0', '2', '0', '0', '0' };
0176: private static final byte[] nullSQLState = { ' ', ' ', ' ', ' ',
0177: ' ' };
0178: private static final byte[] errD4_D6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
0179: 0, 0, 0 }; // 12x0
0180: private static final byte[] warn0_warnA = { ' ', ' ', ' ', ' ',
0181: ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; // 11x ' '
0182:
0183: private final static String AUTHENTICATION_PROVIDER_BUILTIN_CLASS = "org.apache.derby.impl.jdbc.authentication.BasicAuthenticationServiceImpl";
0184:
0185: private final static String AUTHENTICATION_PROVIDER_NONE_CLASS = "org.apache.derby.impl.jdbc.authentication.NoneAuthenticationServiceImpl";
0186:
0187: // Work around a classloader bug involving interrupt handling during
0188: // class loading. If the first request to load the
0189: // DRDAProtocolExceptionInfo class occurs during shutdown, the
0190: // loading of the class may be aborted when the Network Server calls
0191: // Thread.interrupt() on the DRDAConnThread. By including a static
0192: // reference to the DRDAProtocolExceptionInfo class here, we ensure
0193: // that it is loaded as soon as the DRDAConnThread class is loaded,
0194: // and therefore we know we won't be trying to load the class during
0195: // shutdown. See DERBY-1338 for more background, including pointers
0196: // to the apparent classloader bug in the JVM.
0197: private static final DRDAProtocolExceptionInfo dummy = new DRDAProtocolExceptionInfo(
0198: 0, 0, 0, false);
0199:
0200: // constructor
0201: /**
0202: * Create a new Thread for processing session requests
0203: *
0204: * @param session Session requesting processing
0205: * @param server Server starting thread
0206: * @param timeSlice timeSlice for thread
0207: * @param logConnections
0208: **/
0209:
0210: DRDAConnThread(Session session, NetworkServerControlImpl server,
0211: long timeSlice, boolean logConnections) {
0212:
0213: super ();
0214:
0215: // Create a more meaningful name for this thread (but preserve its
0216: // thread id from the default name).
0217: NetworkServerControlImpl.setUniqueThreadName(this ,
0218: "DRDAConnThread");
0219:
0220: this .session = session;
0221: this .server = server;
0222: this .timeSlice = timeSlice;
0223: this .logConnections = logConnections;
0224: this .pendingStatementTimeout = -1;
0225: initialize();
0226: }
0227:
0228: /**
0229: * Main routine for thread, loops until the thread is closed
0230: * Gets a session, does work for the session
0231: */
0232: public void run() {
0233: if (SanityManager.DEBUG)
0234: trace("Starting new connection thread");
0235:
0236: Session prevSession;
0237: while (!closed()) {
0238:
0239: // get a new session
0240: prevSession = session;
0241: session = server.getNextSession(session);
0242: if (session == null)
0243: close();
0244:
0245: if (closed())
0246: break;
0247: if (session != prevSession) {
0248: initializeForSession();
0249: }
0250: try {
0251: long timeStart = System.currentTimeMillis();
0252:
0253: switch (session.state) {
0254: case Session.INIT:
0255: sessionInitialState();
0256: if (session == null)
0257: break;
0258: // else fallthrough
0259: case Session.ATTEXC:
0260: case Session.SECACC:
0261: case Session.CHKSEC:
0262: long currentTimeSlice;
0263:
0264: do {
0265: processCommands();
0266: currentTimeSlice = getTimeSlice();
0267: } while ((currentTimeSlice == 0)
0268: || (System.currentTimeMillis() - timeStart < currentTimeSlice));
0269:
0270: break;
0271: default:
0272: // this is an error
0273: agentError("Session in invalid state:"
0274: + session.state);
0275: }
0276: } catch (Exception e) {
0277: if (e instanceof DRDAProtocolException
0278: && ((DRDAProtocolException) e)
0279: .isDisconnectException()) {
0280: // client went away - this is O.K. here
0281: closeSession();
0282: } else {
0283: handleException(e);
0284: }
0285: }
0286: }
0287: if (SanityManager.DEBUG)
0288: trace("Ending connection thread");
0289: server.removeThread(this );
0290:
0291: }
0292:
0293: /**
0294: * Get input stream
0295: *
0296: * @return input stream
0297: */
0298: protected InputStream getInputStream() {
0299: return sockis;
0300: }
0301:
0302: /**
0303: * Get output stream
0304: *
0305: * @return output stream
0306: */
0307: protected OutputStream getOutputStream() {
0308: return sockos;
0309: }
0310:
0311: /**
0312: * get DDMReader
0313: * @return DDMReader for this thread
0314: */
0315: protected DDMReader getReader() {
0316: return reader;
0317: }
0318:
0319: /**
0320: * get DDMWriter
0321: * @return DDMWriter for this thread
0322: */
0323: protected DDMWriter getWriter() {
0324: return writer;
0325: }
0326:
0327: /**
0328: * Get correlation id
0329: *
0330: * @return correlation id
0331: */
0332: protected int getCorrelationID() {
0333: return correlationID;
0334: }
0335:
0336: /**
0337: * Get session we are working on
0338: *
0339: * @return session
0340: */
0341: protected Session getSession() {
0342: return session;
0343: }
0344:
0345: /**
0346: * Get Database we are working on
0347: *
0348: * @return database
0349: */
0350: protected Database getDatabase() {
0351: return database;
0352: }
0353:
0354: /**
0355: * Get server
0356: *
0357: * @return server
0358: */
0359: protected NetworkServerControlImpl getServer() {
0360: return server;
0361: }
0362:
0363: /**
0364: * Get correlation token
0365: *
0366: * @return crrtkn
0367: */
0368: protected byte[] getCrrtkn() {
0369: if (database != null)
0370: return database.crrtkn;
0371: return null;
0372: }
0373:
0374: /**
0375: * Get database name
0376: *
0377: * @return database name
0378: */
0379: protected String getDbName() {
0380: if (database != null)
0381: return database.dbName;
0382: return null;
0383: }
0384:
0385: /**
0386: * Close DRDA connection thread
0387: */
0388: protected void close() {
0389: synchronized (closeSync) {
0390: close = true;
0391: }
0392: }
0393:
0394: /**
0395: * Set logging of connections
0396: *
0397: * @param value value to set for logging connections
0398: */
0399: protected void setLogConnections(boolean value) {
0400: synchronized (logConnectionsSync) {
0401: logConnections = value;
0402: }
0403: }
0404:
0405: /**
0406: * Set time slice value
0407: *
0408: * @param value new value for time slice
0409: */
0410: protected void setTimeSlice(long value) {
0411: synchronized (timeSliceSync) {
0412: timeSlice = value;
0413: }
0414: }
0415:
0416: /**
0417: * Indicate a communications failure
0418: *
0419: * @param arg1 - info about the communications failure
0420: * @param arg2 - info about the communications failure
0421: * @param arg3 - info about the communications failure
0422: * @param arg4 - info about the communications failure
0423: *
0424: * @exception DRDAProtocolException disconnect exception always thrown
0425: */
0426: protected void markCommunicationsFailure(String arg1, String arg2,
0427: String arg3, String arg4) throws DRDAProtocolException {
0428: Object[] oa = { arg1, arg2, arg3, arg4 };
0429: throw DRDAProtocolException.newDisconnectException(this , oa);
0430:
0431: }
0432:
0433: /**
0434: * Syntax error
0435: *
0436: * @param errcd Error code
0437: * @param cpArg code point value
0438: * @exception DRDAProtocolException
0439: */
0440:
0441: protected void throwSyntaxrm(int errcd, int cpArg)
0442: throws DRDAProtocolException {
0443: throw new DRDAProtocolException(
0444: DRDAProtocolException.DRDA_Proto_SYNTAXRM, this , cpArg,
0445: errcd);
0446: }
0447:
0448: /**
0449: * Agent error - something very bad happened
0450: *
0451: * @param msg Message describing error
0452: *
0453: * @exception DRDAProtocolException newAgentError always thrown
0454: */
0455: protected void agentError(String msg) throws DRDAProtocolException {
0456:
0457: String dbname = null;
0458: if (database != null)
0459: dbname = database.dbName;
0460: throw DRDAProtocolException.newAgentError(this ,
0461: CodePoint.SVRCOD_PRMDMG, dbname, msg);
0462: }
0463:
0464: /**
0465: * Missing code point
0466: *
0467: * @param codePoint code point value
0468: * @exception DRDAProtocolException
0469: */
0470: protected void missingCodePoint(int codePoint)
0471: throws DRDAProtocolException {
0472: throwSyntaxrm(CodePoint.SYNERRCD_REQ_OBJ_NOT_FOUND, codePoint);
0473: }
0474:
0475: /**
0476: * Print a line to the DB2j log
0477: *
0478: * @param dbname database name
0479: * @param drdaID DRDA identifier
0480: * @param msg message
0481: */
0482: protected static void println2Log(String dbname, String drdaID,
0483: String msg) {
0484: if (logStream == null)
0485: logStream = Monitor.getStream();
0486:
0487: if (dbname != null) {
0488: int endOfName = dbname.indexOf(';');
0489: if (endOfName != -1)
0490: dbname = dbname.substring(0, endOfName);
0491: }
0492: logStream.printlnWithHeader("(DATABASE = " + dbname
0493: + "), (DRDAID = " + drdaID + "), " + msg);
0494: }
0495:
0496: /**
0497: * Write RDBNAM
0498: *
0499: * @param rdbnam database name
0500: * @exception DRDAProtocolException
0501: */
0502: protected void writeRDBNAM(String rdbnam)
0503: throws DRDAProtocolException {
0504: int len = rdbnam.length();
0505: if (len < CodePoint.RDBNAM_LEN)
0506: len = CodePoint.RDBNAM_LEN;
0507: writer.writeScalarHeader(CodePoint.RDBNAM, len);
0508: try {
0509: writer.writeScalarPaddedBytes(rdbnam
0510: .getBytes(server.DEFAULT_ENCODING), len,
0511: server.SPACE_CHAR);
0512: } catch (UnsupportedEncodingException e) {
0513: agentError("Unsupported coding exception for server encoding "
0514: + server.DEFAULT_ENCODING);
0515: }
0516: }
0517:
0518: /***************************************************************************
0519: * Private methods
0520: ***************************************************************************/
0521:
0522: /**
0523: * Initialize class
0524: */
0525: private void initialize() {
0526: // set input and output sockets
0527: // this needs to be done before creating reader
0528: sockis = session.sessionInput;
0529: sockos = session.sessionOutput;
0530:
0531: reader = new DDMReader(this , session.dssTrace);
0532: writer = new DDMWriter(ccsidManager, this , session.dssTrace);
0533: }
0534:
0535: /**
0536: * Initialize for a new session
0537: */
0538: private void initializeForSession() {
0539: // set input and output sockets
0540: sockis = session.sessionInput;
0541: sockos = session.sessionOutput;
0542:
0543: // intialize reader and writer
0544: reader.initialize(this , session.dssTrace);
0545: writer.reset(session.dssTrace);
0546:
0547: // initialize local pointers to session info
0548: database = session.database;
0549: appRequester = session.appRequester;
0550:
0551: // set sqlamLevel
0552: if (session.state == Session.ATTEXC)
0553: sqlamLevel = appRequester.getManagerLevel(CodePoint.SQLAM);
0554:
0555: }
0556:
0557: /**
0558: * In initial state for a session,
0559: * determine whether this is a command
0560: * session or a DRDA protocol session. A command session is for changing
0561: * the configuration of the Net server, e.g., turning tracing on
0562: * If it is a command session, process the command and close the session.
0563: * If it is a DRDA session, exchange server attributes and change session
0564: * state.
0565: */
0566: private void sessionInitialState() throws Exception {
0567: // process NetworkServerControl commands - if it is not either valid protocol let the
0568: // DRDA error handling handle it
0569: if (reader.isCmd()) {
0570: try {
0571: server.processCommands(reader, writer, session);
0572: // reset reader and writer
0573: reader.initialize(this , null);
0574: writer.reset(null);
0575: closeSession();
0576: } catch (Throwable t) {
0577: if (t instanceof InterruptedException)
0578: throw (InterruptedException) t;
0579: else {
0580: server.consoleExceptionPrintTrace(t);
0581: }
0582: }
0583:
0584: } else {
0585: // exchange attributes with application requester
0586: exchangeServerAttributes();
0587: }
0588: }
0589:
0590: /**
0591: * Cleans up and closes a result set if an exception is thrown
0592: * when collecting QRYDTA in response to OPNQRY or CNTQRY.
0593: *
0594: * @param stmt the DRDA statement to clean up
0595: * @param sqle the exception that was thrown
0596: * @param writerMark start index for the first DSS to clear from
0597: * the output buffer
0598: * @exception DRDAProtocolException if a DRDA protocol error is
0599: * detected
0600: */
0601: private void cleanUpAndCloseResultSet(DRDAStatement stmt,
0602: SQLException sqle, int writerMark)
0603: throws DRDAProtocolException {
0604: if (stmt != null) {
0605: writer.clearDSSesBackToMark(writerMark);
0606: if (!stmt.rsIsClosed()) {
0607: try {
0608: stmt.rsClose();
0609: } catch (SQLException ec) {
0610: if (SanityManager.DEBUG) {
0611: trace("Warning: Error closing result set");
0612: }
0613: }
0614: writeABNUOWRM();
0615: writeSQLCARD(sqle, CodePoint.SVRCOD_ERROR, 0, 0);
0616: }
0617: } else {
0618: writeSQLCARDs(sqle, 0);
0619: }
0620: errorInChain(sqle);
0621: }
0622:
0623: /**
0624: * Process DRDA commands we can receive once server attributes have been
0625: * exchanged.
0626: *
0627: * @exception DRDAProtocolException
0628: */
0629: private void processCommands() throws DRDAProtocolException {
0630: DRDAStatement stmt = null;
0631: int updateCount = 0;
0632: boolean PRPSQLSTTfailed = false;
0633: boolean checkSecurityCodepoint = session
0634: .requiresSecurityCodepoint();
0635: do {
0636: correlationID = reader.readDssHeader();
0637: int codePoint = reader.readLengthAndCodePoint();
0638: int writerMark = writer.markDSSClearPoint();
0639:
0640: if (checkSecurityCodepoint)
0641: verifyInOrderACCSEC_SECCHK(codePoint, session
0642: .getRequiredSecurityCodepoint());
0643:
0644: switch (codePoint) {
0645: case CodePoint.CNTQRY:
0646: try {
0647: stmt = parseCNTQRY();
0648: if (stmt != null) {
0649: writeQRYDTA(stmt);
0650: if (stmt.rsIsClosed()) {
0651: writeENDQRYRM(CodePoint.SVRCOD_WARNING);
0652: writeNullSQLCARDobject();
0653: }
0654: // Send any warnings if JCC can handle them
0655: checkWarning(null, null, stmt.getResultSet(),
0656: 0, false, sendWarningsOnCNTQRY);
0657: }
0658: } catch (SQLException e) {
0659: // if we got a SQLException we need to clean up and
0660: // close the result set Beetle 4758
0661: cleanUpAndCloseResultSet(stmt, e, writerMark);
0662: }
0663: break;
0664: case CodePoint.EXCSQLIMM:
0665: try {
0666: updateCount = parseEXCSQLIMM();
0667: // RESOLVE: checking updateCount is not sufficient
0668: // since it will be 0 for creates, we need to know when
0669: // any logged changes are made to the database
0670: // Not getting this right for JCC is probably O.K., this
0671: // will probably be a problem for ODBC and XA
0672: // The problem is that JDBC doesn't provide this information
0673: // so we would have to expand the JDBC API or call a
0674: // builtin method to check(expensive)
0675: // For now we will assume that every execute immediate
0676: // does an update (that is the most conservative thing)
0677: if (database.RDBUPDRM_sent == false) {
0678: writeRDBUPDRM();
0679: }
0680:
0681: // we need to set update count in SQLCARD
0682: checkWarning(null, database.getDefaultStatement()
0683: .getStatement(), null, updateCount, true,
0684: true);
0685: } catch (SQLException e) {
0686: writer.clearDSSesBackToMark(writerMark);
0687: writeSQLCARDs(e, 0);
0688: errorInChain(e);
0689: }
0690: break;
0691:
0692: case CodePoint.EXCSQLSET:
0693: try {
0694: if (parseEXCSQLSET())
0695: // all went well.
0696: writeSQLCARDs(null, 0);
0697: } catch (SQLWarning w) {
0698: writeSQLCARD(w, CodePoint.SVRCOD_WARNING, 0, 0);
0699: } catch (SQLException e) {
0700: writer.clearDSSesBackToMark(writerMark);
0701: writeSQLCARDs(e, 0);
0702: errorInChain(e);
0703: }
0704: break;
0705:
0706: case CodePoint.PRPSQLSTT:
0707: int sqldaType;
0708: PRPSQLSTTfailed = false;
0709: try {
0710: database.getConnection().clearWarnings();
0711: sqldaType = parsePRPSQLSTT();
0712: if (sqldaType > 0) // do write SQLDARD
0713: writeSQLDARD(
0714: database.getCurrentStatement(),
0715: (sqldaType == CodePoint.TYPSQLDA_LIGHT_OUTPUT),
0716: database.getConnection().getWarnings());
0717: else
0718: checkWarning(database.getConnection(), null,
0719: null, 0, true, true);
0720:
0721: } catch (SQLException e) {
0722: writer.clearDSSesBackToMark(writerMark);
0723: writeSQLCARDs(e, 0, true);
0724: PRPSQLSTTfailed = true;
0725: errorInChain(e);
0726: }
0727: break;
0728: case CodePoint.OPNQRY:
0729: PreparedStatement ps = null;
0730: try {
0731: if (PRPSQLSTTfailed) {
0732: // read the command objects
0733: // for ps with parameter
0734: // Skip objects/parameters
0735: skipRemainder(true);
0736:
0737: // If we failed to prepare, then we fail
0738: // to open, which means OPNQFLRM.
0739: writeOPNQFLRM(null);
0740: break;
0741: }
0742: Pkgnamcsn pkgnamcsn = parseOPNQRY();
0743: if (pkgnamcsn != null) {
0744: stmt = database.getDRDAStatement(pkgnamcsn);
0745: ps = stmt.getPreparedStatement();
0746: ps.clearWarnings();
0747: if (pendingStatementTimeout >= 0) {
0748: ps.setQueryTimeout(pendingStatementTimeout);
0749: pendingStatementTimeout = -1;
0750: }
0751: stmt.execute();
0752: writeOPNQRYRM(false, stmt);
0753: checkWarning(null, ps, null, 0, false, true);
0754:
0755: writeQRYDSC(stmt, false);
0756:
0757: stmt.rsSuspend();
0758:
0759: if (stmt.getQryprctyp() == CodePoint.LMTBLKPRC
0760: && stmt.getQryrowset() != 0) {
0761: // The DRDA spec allows us to send
0762: // QRYDTA here if there are no LOB
0763: // columns.
0764: DRDAResultSet drdars = stmt
0765: .getCurrentDrdaResultSet();
0766: try {
0767: if (drdars != null
0768: && !drdars.hasLobColumns()) {
0769: writeQRYDTA(stmt);
0770: }
0771: } catch (SQLException sqle) {
0772: cleanUpAndCloseResultSet(stmt, sqle,
0773: writerMark);
0774: }
0775: }
0776: }
0777: } catch (SQLException e) {
0778: writer.clearDSSesBackToMark(writerMark);
0779: // The fix for DERBY-1196 removed code
0780: // here to close the prepared statement
0781: // if OPNQRY failed.
0782: writeOPNQFLRM(e);
0783: }
0784: break;
0785: case CodePoint.RDBCMM:
0786: try {
0787: if (SanityManager.DEBUG)
0788: trace("Received commit");
0789: if (!database.getConnection().getAutoCommit()) {
0790: database.getConnection().clearWarnings();
0791: database.commit();
0792: writeENDUOWRM(COMMIT);
0793: checkWarning(database.getConnection(), null,
0794: null, 0, true, true);
0795: }
0796: // we only want to write one of these per transaction
0797: // so set to false in preparation for next command
0798: database.RDBUPDRM_sent = false;
0799: } catch (SQLException e) {
0800: writer.clearDSSesBackToMark(writerMark);
0801: // Even in case of error, we have to write the ENDUOWRM.
0802: writeENDUOWRM(COMMIT);
0803: writeSQLCARDs(e, 0);
0804: errorInChain(e);
0805: }
0806: break;
0807: case CodePoint.RDBRLLBCK:
0808: try {
0809: if (SanityManager.DEBUG)
0810: trace("Received rollback");
0811: database.getConnection().clearWarnings();
0812: database.rollback();
0813: writeENDUOWRM(ROLLBACK);
0814: checkWarning(database.getConnection(), null, null,
0815: 0, true, true);
0816: // we only want to write one of these per transaction
0817: // so set to false in preparation for next command
0818: database.RDBUPDRM_sent = false;
0819: } catch (SQLException e) {
0820: writer.clearDSSesBackToMark(writerMark);
0821: // Even in case of error, we have to write the ENDUOWRM.
0822: writeENDUOWRM(ROLLBACK);
0823: writeSQLCARDs(e, 0);
0824: errorInChain(e);
0825: }
0826: break;
0827: case CodePoint.CLSQRY:
0828: try {
0829: stmt = parseCLSQRY();
0830: stmt.rsClose();
0831: writeSQLCARDs(null, 0);
0832: } catch (SQLException e) {
0833: writer.clearDSSesBackToMark(writerMark);
0834: writeSQLCARDs(e, 0);
0835: errorInChain(e);
0836: }
0837: break;
0838: case CodePoint.EXCSAT:
0839: parseEXCSAT();
0840: writeEXCSATRD();
0841: break;
0842: case CodePoint.ACCSEC:
0843: int securityCheckCode = parseACCSEC();
0844: writeACCSECRD(securityCheckCode);
0845: checkSecurityCodepoint = true;
0846: break;
0847: case CodePoint.SECCHK:
0848: if (parseDRDAConnection())
0849: // security all checked and connection ok
0850: checkSecurityCodepoint = false;
0851: break;
0852: /* since we don't support sqlj, we won't get bind commands from jcc, we
0853: * might get it from ccc; just skip them.
0854: */
0855: case CodePoint.BGNBND:
0856: reader.skipBytes();
0857: writeSQLCARDs(null, 0);
0858: break;
0859: case CodePoint.BNDSQLSTT:
0860: reader.skipBytes();
0861: parseSQLSTTDss();
0862: writeSQLCARDs(null, 0);
0863: break;
0864: case CodePoint.SQLSTTVRB:
0865: // optional
0866: reader.skipBytes();
0867: break;
0868: case CodePoint.ENDBND:
0869: reader.skipBytes();
0870: writeSQLCARDs(null, 0);
0871: break;
0872: case CodePoint.DSCSQLSTT:
0873: if (PRPSQLSTTfailed) {
0874: reader.skipBytes();
0875: writeSQLCARDs(null, 0);
0876: break;
0877: }
0878: try {
0879: boolean rtnOutput = parseDSCSQLSTT();
0880: writeSQLDARD(database.getCurrentStatement(),
0881: rtnOutput, null);
0882:
0883: } catch (SQLException e) {
0884: writer.clearDSSesBackToMark(writerMark);
0885: server.consoleExceptionPrint(e);
0886: try {
0887: writeSQLDARD(database.getCurrentStatement(),
0888: true, e);
0889: } catch (SQLException e2) { // should not get here since doing nothing with ps
0890: agentError("Why am I getting another SQLException?");
0891: }
0892: errorInChain(e);
0893: }
0894: break;
0895: case CodePoint.EXCSQLSTT:
0896: if (PRPSQLSTTfailed) {
0897: // Skip parameters too if they are chained Beetle 4867
0898: skipRemainder(true);
0899: writeSQLCARDs(null, 0);
0900: break;
0901: }
0902: try {
0903: parseEXCSQLSTT();
0904:
0905: DRDAStatement curStmt = database
0906: .getCurrentStatement();
0907: if (curStmt != null)
0908: curStmt.rsSuspend();
0909: } catch (SQLException e) {
0910: skipRemainder(true);
0911: writer.clearDSSesBackToMark(writerMark);
0912: if (SanityManager.DEBUG) {
0913: server.consoleExceptionPrint(e);
0914: }
0915: writeSQLCARDs(e, 0);
0916: errorInChain(e);
0917: }
0918: break;
0919: case CodePoint.SYNCCTL:
0920: if (xaProto == null)
0921: xaProto = new DRDAXAProtocol(this );
0922: xaProto.parseSYNCCTL();
0923: break;
0924: default:
0925: codePointNotSupported(codePoint);
0926: }
0927:
0928: // Set the correct chaining bits for whatever
0929: // reply DSS(es) we just wrote. If we've reached
0930: // the end of the chain, this method will send
0931: // the DSS(es) across.
0932: finalizeChain();
0933:
0934: } while (reader.isChainedWithSameID()
0935: || reader.isChainedWithDiffID());
0936: }
0937:
0938: /**
0939: * If there's a severe error in the DDM chain, and if the header indicates
0940: * "terminate chain on error", we stop processing further commands in the chain
0941: * nor do we send any reply for them. In accordance to this, a SQLERRRM message
0942: * indicating the severe error must have been sent! (otherwise application requestor,
0943: * such as JCC, would not terminate the receiving of chain replies.)
0944: *
0945: * Each DRDA command is processed independently. DRDA defines no interdependencies
0946: * across chained commands. A command is processed the same when received within
0947: * a set of chained commands or received separately. The chaining was originally
0948: * defined as a way to save network costs.
0949: *
0950: * @param e the SQLException raised
0951: * @exception DRDAProtocolException
0952: */
0953: private void errorInChain(SQLException e)
0954: throws DRDAProtocolException {
0955: if (reader.terminateChainOnErr()
0956: && (getExceptionSeverity(e) > CodePoint.SVRCOD_ERROR)) {
0957: if (SanityManager.DEBUG)
0958: trace("terminating the chain on error...");
0959: skipRemainder(false);
0960: }
0961: }
0962:
0963: /**
0964: * Exchange server attributes with application requester
0965: *
0966: * @exception DRDAProtocolException
0967: */
0968: private void exchangeServerAttributes()
0969: throws DRDAProtocolException {
0970: int codePoint;
0971: correlationID = reader.readDssHeader();
0972: if (SanityManager.DEBUG) {
0973: if (correlationID == 0) {
0974: SanityManager
0975: .THROWASSERT("Unexpected value for correlationId = "
0976: + correlationID);
0977: }
0978: }
0979:
0980: codePoint = reader.readLengthAndCodePoint();
0981:
0982: // The first code point in the exchange of attributes must be EXCSAT
0983: if (codePoint != CodePoint.EXCSAT) {
0984: //Throw PRCCNVRM
0985: throw new DRDAProtocolException(
0986: DRDAProtocolException.DRDA_Proto_PRCCNVRM, this ,
0987: codePoint,
0988: CodePoint.PRCCNVCD_EXCSAT_FIRST_AFTER_CONN);
0989: }
0990:
0991: parseEXCSAT();
0992: writeEXCSATRD();
0993: finalizeChain();
0994: session.setState(session.ATTEXC);
0995: }
0996:
0997: private boolean parseDRDAConnection() throws DRDAProtocolException {
0998: int codePoint;
0999: boolean sessionOK = true;
1000:
1001: int securityCheckCode = parseSECCHK();
1002: if (SanityManager.DEBUG)
1003: trace("*** SECCHKRM securityCheckCode is: "
1004: + securityCheckCode);
1005: writeSECCHKRM(securityCheckCode);
1006: //at this point if the security check failed, we're done, the session failed
1007: if (securityCheckCode != 0) {
1008: return false;
1009: }
1010:
1011: correlationID = reader.readDssHeader();
1012: codePoint = reader.readLengthAndCodePoint();
1013: verifyRequiredObject(codePoint, CodePoint.ACCRDB);
1014: int svrcod = parseACCRDB();
1015:
1016: //If network server gets a null connection form InternalDriver, reply with
1017: //RDBAFLRM and SQLCARD with null SQLException
1018: if (database.getConnection() == null
1019: && databaseAccessException == null) {
1020: writeRDBfailure(CodePoint.RDBAFLRM);
1021: return false;
1022: }
1023:
1024: //if earlier we couldn't access the database
1025: if (databaseAccessException != null) {
1026:
1027: //if the Database was not found we will try DS
1028: int failureType = getRdbAccessErrorCodePoint();
1029: if (failureType == CodePoint.RDBNFNRM
1030: || failureType == CodePoint.RDBATHRM) {
1031: writeRDBfailure(failureType);
1032: } else {
1033: writeRDBfailure(CodePoint.RDBAFLRM);
1034: }
1035: return false;
1036: } else if (database.accessCount > 1) // already in conversation with database
1037: {
1038: writeRDBfailure(CodePoint.RDBACCRM);
1039: return false;
1040: } else
1041: // everything is fine
1042: writeACCRDBRM(svrcod);
1043:
1044: // compare this application requester with previously stored
1045: // application requesters and if we have already seen this one
1046: // use stored application requester
1047: session.appRequester = server.getAppRequester(appRequester);
1048: return sessionOK;
1049: }
1050:
1051: /**
1052: * Write RDB Failure
1053: *
1054: * Instance Variables
1055: * SVRCOD - Severity Code - required
1056: * RDBNAM - Relational Database name - required
1057: * SRVDGN - Server Diagnostics - optional (not sent for now)
1058: *
1059: * @param codePoint codepoint of failure
1060: */
1061: private void writeRDBfailure(int codePoint)
1062: throws DRDAProtocolException {
1063: writer.createDssReply();
1064: writer.startDdm(codePoint);
1065: writer.writeScalar2Bytes(CodePoint.SVRCOD,
1066: CodePoint.SVRCOD_ERROR);
1067: writeRDBNAM(database.dbName);
1068: writer.endDdmAndDss();
1069:
1070: switch (codePoint) {
1071: case CodePoint.RDBAFLRM:
1072: //RDBAFLRM requires TYPDEFNAM and TYPDEFOVR
1073: writer.createDssObject();
1074: writer.writeScalarString(CodePoint.TYPDEFNAM,
1075: CodePoint.TYPDEFNAM_QTDSQLASC);
1076: writeTYPDEFOVR();
1077: writer.endDss();
1078: case CodePoint.RDBNFNRM:
1079: case CodePoint.RDBATHRM:
1080: writeSQLCARD(databaseAccessException,
1081: CodePoint.SVRCOD_ERROR, 0, 0);
1082: case CodePoint.RDBACCRM:
1083: //Ignore anything that was chained to the ACCRDB.
1084: skipRemainder(false);
1085:
1086: // Finalize chain state for whatever we wrote in
1087: // response to ACCRDB.
1088: finalizeChain();
1089: break;
1090: }
1091:
1092: }
1093:
1094: /* Check the database access exception and return the appropriate
1095: error codepoint.
1096: RDBNFNRM - Database not found
1097: RDBATHRM - Not Authorized
1098: RDBAFLRM - Access failure
1099: @return RDB Access codepoint
1100:
1101: */
1102:
1103: private int getRdbAccessErrorCodePoint() {
1104: String sqlState = databaseAccessException.getSQLState();
1105: if (sqlState
1106: .regionMatches(0, SQLState.DATABASE_NOT_FOUND, 0, 5)
1107: | sqlState.regionMatches(0, SQLState.NO_SUCH_DATABASE,
1108: 0, 5))
1109: return CodePoint.RDBNFNRM;
1110: else if (sqlState.regionMatches(0, SQLState.LOGIN_FAILED, 0, 5)
1111: || sqlState.regionMatches(0,
1112: SQLState.AUTH_INVALID_USER_NAME, 0, 5))
1113: return CodePoint.RDBATHRM;
1114: else
1115: return CodePoint.RDBAFLRM;
1116: }
1117:
1118: /**
1119: * Verify userId and password
1120: *
1121: * Username and password is verified by making a connection to the
1122: * database
1123: *
1124: * @return security check code, 0 is O.K.
1125: * @exception DRDAProtocolException
1126: */
1127: private int verifyUserIdPassword() throws DRDAProtocolException {
1128: databaseAccessException = null;
1129: int retSecChkCode = 0;
1130:
1131: String realName = database.dbName; //first strip off properties
1132: int endOfName = realName.indexOf(';');
1133: if (endOfName != -1)
1134: realName = realName.substring(0, endOfName);
1135: retSecChkCode = getConnFromDatabaseName();
1136: return retSecChkCode;
1137: }
1138:
1139: /**
1140: * Get connection from a database name
1141: *
1142: * Username and password is verified by making a connection to the
1143: * database
1144: *
1145: * @return security check code, 0 is O.K.
1146: * @exception DRDAProtocolException
1147: */
1148: private int getConnFromDatabaseName() throws DRDAProtocolException {
1149: Properties p = new Properties();
1150: databaseAccessException = null;
1151: //if we haven't got the correlation token yet, use session number for drdaID
1152: if (session.drdaID == null)
1153: session.drdaID = leftBrace + session.connNum + rightBrace;
1154: p.put(Attribute.DRDAID_ATTR, session.drdaID);
1155:
1156: // We pass extra property information for the authentication provider
1157: // to successfully re-compute the substitute (hashed) password and
1158: // compare it with what we've got from the requester (source).
1159: //
1160: // If a password attribute appears as part of the connection URL
1161: // attributes, we then don't use the substitute hashed password
1162: // to authenticate with the engine _as_ the one (if any) as part
1163: // of the connection URL attributes, will be used to authenticate
1164: // against Derby's BUILT-IN authentication provider - As a reminder,
1165: // Derby allows password to be mentioned as part of the connection
1166: // URL attributes, as this extra capability could be useful to pass
1167: // passwords to external authentication providers for Derby; hence
1168: // a password defined as part of the connection URL attributes cannot
1169: // be substituted (single-hashed) as it is not recoverable.
1170: if ((database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)
1171: && (database.dbName.indexOf(Attribute.PASSWORD_ATTR) == -1)) {
1172: p.put(Attribute.CLIENT_SECURITY_MECHANISM, String
1173: .valueOf(database.securityMechanism));
1174: p.put(Attribute.DRDA_SECTKN_IN, DecryptionManager
1175: .toHexString(database.secTokenIn, 0,
1176: database.secTokenIn.length));
1177: p.put(Attribute.DRDA_SECTKN_OUT, DecryptionManager
1178: .toHexString(database.secTokenOut, 0,
1179: database.secTokenOut.length));
1180: }
1181:
1182: try {
1183: database.makeConnection(p);
1184: } catch (SQLException se) {
1185: String sqlState = se.getSQLState();
1186: // need to set the security check code based on the reason the connection
1187: // was denied, Cloudscape doesn't say whether the userid or password caused
1188: // the problem, so we will just return userid invalid
1189: databaseAccessException = se;
1190: for (; se != null; se = se.getNextException()) {
1191: if (SanityManager.DEBUG)
1192: trace(se.getMessage());
1193: println2Log(database.dbName, session.drdaID, se
1194: .getMessage());
1195: }
1196:
1197: if (sqlState.regionMatches(0, SQLState.LOGIN_FAILED, 0, 5))
1198: return CodePoint.SECCHKCD_USERIDINVALID;
1199:
1200: return 0;
1201: } catch (Exception e) {
1202: // If cloudscape has shut down for some reason,
1203: // we will send an agent error and then try to
1204: // get the driver loaded again. We have to get
1205: // rid of the client first in case they are holding
1206: // the DriverManager lock.
1207: println2Log(database.dbName, session.drdaID,
1208: "Driver not loaded" + e.getMessage());
1209: try {
1210: agentError("Driver not loaded");
1211: } catch (DRDAProtocolException dpe) {
1212: // Retry starting the server before rethrowing
1213: // the protocol exception. Then hopfully all
1214: // will be well when they try again.
1215: try {
1216: server.startNetworkServer();
1217: } catch (Exception re) {
1218: println2Log(database.dbName, session.drdaID,
1219: "Failed attempt to reload driver "
1220: + re.getMessage());
1221: }
1222: throw dpe;
1223: }
1224: }
1225:
1226: // Everything worked so log connection to the database.
1227: if (getLogConnections())
1228: println2Log(database.dbName, session.drdaID,
1229: "Apache Derby Network Server connected to database "
1230: + database.dbName);
1231: return 0;
1232: }
1233:
1234: /**
1235: * Parses EXCSAT (Exchange Server Attributes)
1236: * Instance variables
1237: * EXTNAM(External Name) - optional
1238: * MGRLVLLS(Manager Levels) - optional
1239: * SPVNAM(Supervisor Name) - optional
1240: * SRVCLSNM(Server Class Name) - optional
1241: * SRVNAM(Server Name) - optional, ignorable
1242: * SRVRLSLV(Server Product Release Level) - optional, ignorable
1243: *
1244: * @exception DRDAProtocolException
1245: */
1246: private void parseEXCSAT() throws DRDAProtocolException {
1247: int codePoint;
1248: String strVal;
1249:
1250: // There are three kinds of EXCSAT's we might get.
1251: // 1) Initial Exchange attributes.
1252: // For this we need to initialize the apprequester.
1253: // Session state is set to ATTEXC and then the AR must
1254: // follow up with ACCSEC and SECCHK to get the connection.
1255: // 2) Send of EXCSAT as ping or mangager level adjustment.
1256: // (see parseEXCSAT2())
1257: // For this we just ignore the EXCSAT objects that
1258: // are already set.
1259: // 3) Send of EXCSAT for connection reset. (see parseEXCSAT2())
1260: // This is treated just like ping and will be followed up
1261: // by an ACCSEC request if in fact it is a connection reset.
1262:
1263: // If we have already exchanged attributes once just
1264: // process any new manager levels and return (case 2 and 3 above)
1265: if (appRequester != null) {
1266: parseEXCSAT2();
1267: return;
1268: }
1269:
1270: // set up a new Application Requester to store information about the
1271: // application requester for this session
1272:
1273: appRequester = new AppRequester();
1274:
1275: reader.markCollection();
1276:
1277: codePoint = reader.getCodePoint();
1278: while (codePoint != -1) {
1279: switch (codePoint) {
1280: // optional
1281: case CodePoint.EXTNAM:
1282: appRequester.extnam = reader.readString();
1283: if (SanityManager.DEBUG)
1284: trace("extName = " + appRequester.extnam);
1285: if (appRequester.extnam.length() > CodePoint.MAX_NAME)
1286: tooBig(CodePoint.EXTNAM);
1287: break;
1288: // optional
1289: case CodePoint.MGRLVLLS:
1290: parseMGRLVLLS(1);
1291: break;
1292: // optional
1293: case CodePoint.SPVNAM:
1294: appRequester.spvnam = reader.readString();
1295: // This is specified as a null parameter so length should
1296: // be zero
1297: if (appRequester.spvnam != null)
1298: badObjectLength(CodePoint.SPVNAM);
1299: break;
1300: // optional
1301: case CodePoint.SRVNAM:
1302: appRequester.srvnam = reader.readString();
1303: if (SanityManager.DEBUG)
1304: trace("serverName = " + appRequester.srvnam);
1305: if (appRequester.srvnam.length() > CodePoint.MAX_NAME)
1306: tooBig(CodePoint.SRVNAM);
1307: break;
1308: // optional
1309: case CodePoint.SRVRLSLV:
1310: appRequester.srvrlslv = reader.readString();
1311: if (SanityManager.DEBUG)
1312: trace("serverlslv = " + appRequester.srvrlslv);
1313: if (appRequester.srvrlslv.length() > CodePoint.MAX_NAME)
1314: tooBig(CodePoint.SRVRLSLV);
1315: break;
1316: // optional
1317: case CodePoint.SRVCLSNM:
1318: appRequester.srvclsnm = reader.readString();
1319: if (SanityManager.DEBUG)
1320: trace("serverClassName = " + appRequester.srvclsnm);
1321: if (appRequester.srvclsnm.length() > CodePoint.MAX_NAME)
1322: tooBig(CodePoint.SRVCLSNM);
1323: break;
1324: default:
1325: invalidCodePoint(codePoint);
1326: }
1327: codePoint = reader.getCodePoint();
1328: }
1329: }
1330:
1331: /**
1332: * Parses EXCSAT2 (Exchange Server Attributes)
1333: * Instance variables
1334: * EXTNAM(External Name) - optional
1335: * MGRLVLLS(Manager Levels) - optional
1336: * SPVNAM(Supervisor Name) - optional
1337: * SRVCLSNM(Server Class Name) - optional
1338: * SRVNAM(Server Name) - optional, ignorable
1339: * SRVRLSLV(Server Product Release Level) - optional, ignorable
1340: *
1341: * @exception DRDAProtocolException
1342: *
1343: * This parses a second occurrence of an EXCSAT command
1344: * The target must ignore the values for extnam, srvclsnm, srvnam and srvrlslv.
1345: * I am also going to ignore spvnam since it should be null anyway.
1346: * Only new managers can be added.
1347: */
1348: private void parseEXCSAT2() throws DRDAProtocolException {
1349: int codePoint;
1350: reader.markCollection();
1351:
1352: codePoint = reader.getCodePoint();
1353: while (codePoint != -1) {
1354: switch (codePoint) {
1355: // optional
1356: case CodePoint.EXTNAM:
1357: case CodePoint.SRVNAM:
1358: case CodePoint.SRVRLSLV:
1359: case CodePoint.SRVCLSNM:
1360: case CodePoint.SPVNAM:
1361: reader.skipBytes();
1362: break;
1363: // optional
1364: case CodePoint.MGRLVLLS:
1365: parseMGRLVLLS(2);
1366: break;
1367: default:
1368: invalidCodePoint(codePoint);
1369: }
1370: codePoint = reader.getCodePoint();
1371: }
1372: }
1373:
1374: /**
1375: * Parse manager levels
1376: * Instance variables
1377: * MGRLVL - repeatable, required
1378: * CODEPOINT
1379: * CCSIDMGR - CCSID Manager
1380: * CMNAPPC - LU 6.2 Conversational Communications Manager
1381: * CMNSYNCPT - SNA LU 6.2 SyncPoint Conversational Communications Manager
1382: * CMNTCPIP - TCP/IP Communication Manager
1383: * DICTIONARY - Dictionary
1384: * RDB - Relational Database
1385: * RSYNCMGR - Resynchronization Manager
1386: * SECMGR - Security Manager
1387: * SQLAM - SQL Application Manager
1388: * SUPERVISOR - Supervisor
1389: * SYNCPTMGR - Sync Point Manager
1390: * VALUE
1391: *
1392: * On the second appearance of this codepoint, it can only add managers
1393: *
1394: * @param time 1 for first time this is seen, 2 for subsequent ones
1395: * @exception DRDAProtocolException
1396: *
1397: */
1398: private void parseMGRLVLLS(int time) throws DRDAProtocolException {
1399: int manager, managerLevel;
1400: int currentLevel;
1401: // set up vectors to keep track of manager information
1402: unknownManagers = new Vector();
1403: knownManagers = new Vector();
1404: errorManagers = new Vector();
1405: errorManagersLevel = new Vector();
1406: if (SanityManager.DEBUG)
1407: trace("Manager Levels");
1408:
1409: while (reader.moreDdmData()) {
1410: manager = reader.readNetworkShort();
1411: managerLevel = reader.readNetworkShort();
1412: if (CodePoint.isKnownManager(manager)) {
1413: knownManagers.addElement(new Integer(manager));
1414: //if the manager level hasn't been set, set it
1415: currentLevel = appRequester.getManagerLevel(manager);
1416: if (currentLevel == appRequester.MGR_LEVEL_UNKNOWN)
1417: appRequester.setManagerLevel(manager, managerLevel);
1418: else {
1419: //if the level is still the same we'll ignore it
1420: if (currentLevel != managerLevel) {
1421: //keep a list of conflicting managers
1422: errorManagers.addElement(new Integer(manager));
1423: errorManagersLevel.addElement(new Integer(
1424: managerLevel));
1425: }
1426: }
1427:
1428: } else
1429: unknownManagers.addElement(new Integer(manager));
1430: if (SanityManager.DEBUG)
1431: trace("Manager = "
1432: + java.lang.Integer.toHexString(manager)
1433: + " ManagerLevel " + managerLevel);
1434: }
1435: sqlamLevel = appRequester.getManagerLevel(CodePoint.SQLAM);
1436: // did we have any errors
1437: if (errorManagers.size() > 0) {
1438: Object[] oa = new Object[errorManagers.size() * 2];
1439: int j = 0;
1440: for (int i = 0; i < errorManagers.size(); i++) {
1441: oa[j++] = errorManagers.elementAt(i);
1442: oa[j++] = errorManagersLevel.elementAt(i);
1443: }
1444: throw new DRDAProtocolException(
1445: DRDAProtocolException.DRDA_Proto_MGRLVLRM, this , 0,
1446: 0, oa);
1447: }
1448: }
1449:
1450: /**
1451: * Write reply to EXCSAT command
1452: * Instance Variables
1453: * EXTNAM - External Name (optional)
1454: * MGRLVLLS - Manager Level List (optional)
1455: * SRVCLSNM - Server Class Name (optional) - used by JCC
1456: * SRVNAM - Server Name (optional)
1457: * SRVRLSLV - Server Product Release Level (optional)
1458: *
1459: * @exception DRDAProtocolException
1460: */
1461: private void writeEXCSATRD() throws DRDAProtocolException {
1462: writer.createDssReply();
1463: writer.startDdm(CodePoint.EXCSATRD);
1464: writer.writeScalarString(CodePoint.EXTNAM, server.att_extnam);
1465: //only reply with manager levels if we got sent some
1466: if (knownManagers != null && knownManagers.size() > 0)
1467: writeMGRLEVELS();
1468: writer.writeScalarString(CodePoint.SRVCLSNM,
1469: server.att_srvclsnm);
1470: writer.writeScalarString(CodePoint.SRVNAM, server.ATT_SRVNAM);
1471: writer.writeScalarString(CodePoint.SRVRLSLV,
1472: server.att_srvrlslv);
1473: writer.endDdmAndDss();
1474: }
1475:
1476: /**
1477: * Write manager levels
1478: * The target server must not provide information for any target
1479: * managers unless the source explicitly requests it.
1480: * For each manager class, if the target server's support level
1481: * is greater than or equal to the source server's level, then the source
1482: * server's level is returned for that class if the target server can operate
1483: * at the source's level; otherwise a level 0 is returned. If the target
1484: * server's support level is less than the source server's level, the
1485: * target server's level is returned for that class. If the target server
1486: * does not recognize the code point of a manager class or does not support
1487: * that class, it returns a level of 0. The target server then waits
1488: * for the next command or for the source server to terminate communications.
1489: * When the source server receives EXCSATRD, it must compare each of the entries
1490: * in the mgrlvlls parameter it received to the corresponding entries in the mgrlvlls
1491: * parameter it sent. If any level mismatches, the source server must decide
1492: * whether it can use or adjust to the lower level of target support for that manager
1493: * class. There are no architectural criteria for making this decision.
1494: * The source server can terminate communications or continue at the target
1495: * servers level of support. It can also attempt to use whatever
1496: * commands its user requests while receiving error reply messages for real
1497: * functional mismatches.
1498: * The manager levels the source server specifies or the target server
1499: * returns must be compatible with the manager-level dependencies of the specified
1500: * manangers. Incompatible manager levels cannot be specified.
1501: * Instance variables
1502: * MGRLVL - repeatable, required
1503: * CODEPOINT
1504: * CCSIDMGR - CCSID Manager
1505: * CMNAPPC - LU 6.2 Conversational Communications Manager
1506: * CMNSYNCPT - SNA LU 6.2 SyncPoint Conversational Communications Manager
1507: * CMNTCPIP - TCP/IP Communication Manager
1508: * DICTIONARY - Dictionary
1509: * RDB - Relational Database
1510: * RSYNCMGR - Resynchronization Manager
1511: * SECMGR - Security Manager
1512: * SQLAM - SQL Application Manager
1513: * SUPERVISOR - Supervisor
1514: * SYNCPTMGR - Sync Point Manager
1515: * XAMGR - XA manager
1516: * VALUE
1517: */
1518: private void writeMGRLEVELS() throws DRDAProtocolException {
1519: int manager;
1520: int appLevel;
1521: int serverLevel;
1522: writer.startDdm(CodePoint.MGRLVLLS);
1523: for (int i = 0; i < knownManagers.size(); i++) {
1524: manager = ((Integer) knownManagers.elementAt(i)).intValue();
1525: appLevel = appRequester.getManagerLevel(manager);
1526: serverLevel = server.getManagerLevel(manager);
1527: if (serverLevel >= appLevel) {
1528: //Note appLevel has already been set to 0 if we can't support
1529: //the original app Level
1530: writer.writeCodePoint4Bytes(manager, appLevel);
1531: } else {
1532: writer.writeCodePoint4Bytes(manager, serverLevel);
1533: // reset application manager level to server level
1534: appRequester.setManagerLevel(manager, serverLevel);
1535: }
1536: }
1537: // write 0 for all unknown managers
1538: for (int i = 0; i < unknownManagers.size(); i++) {
1539: manager = ((Integer) unknownManagers.elementAt(i))
1540: .intValue();
1541: writer.writeCodePoint4Bytes(manager, 0);
1542: }
1543: writer.endDdm();
1544: }
1545:
1546: /**
1547: * Parse Access Security
1548: *
1549: * If the target server supports the SECMEC requested by the application requester
1550: * then a single value is returned and it is identical to the SECMEC value
1551: * in the ACCSEC command. If the target server does not support the SECMEC
1552: * requested, then one or more values are returned and the application requester
1553: * must choose one of these values for the security mechanism.
1554: * We currently support
1555: * - user id and password (default for JCC)
1556: * - encrypted user id and password
1557: * - strong password substitute (USRSSBPWD w/
1558: * Derby network client only)
1559: *
1560: * Instance variables
1561: * SECMGRNM - security manager name - optional
1562: * SECMEC - security mechanism - required
1563: * RDBNAM - relational database name - optional
1564: * SECTKN - security token - optional, (required if sec mech. needs it)
1565: *
1566: * @return security check code - 0 if everything O.K.
1567: */
1568: private int parseACCSEC() throws DRDAProtocolException {
1569: int securityCheckCode = 0;
1570: int securityMechanism = 0;
1571: byte[] secTokenIn = null;
1572:
1573: reader.markCollection();
1574: int codePoint = reader.getCodePoint();
1575: while (codePoint != -1) {
1576: switch (codePoint) {
1577: //optional
1578: case CodePoint.SECMGRNM:
1579: // this is defined to be 0 length
1580: if (reader.getDdmLength() != 0)
1581: badObjectLength(CodePoint.SECMGRNM);
1582: break;
1583: //required
1584: case CodePoint.SECMEC:
1585: checkLength(CodePoint.SECMEC, 2);
1586: securityMechanism = reader.readNetworkShort();
1587: if (SanityManager.DEBUG)
1588: trace("parseACCSEC - Security mechanism = "
1589: + securityMechanism);
1590:
1591: // if Property.DRDA_PROP_SECURITYMECHANISM has been set, then
1592: // network server only accepts connections which use that
1593: // security mechanism. No other types of connections
1594: // are accepted.
1595: // Make check to see if this property has been set.
1596: // if set, and if the client requested security mechanism
1597: // is not the same, then return a security check code
1598: // that the server does not support/allow this security
1599: // mechanism
1600: if ((server.getSecurityMechanism() != NetworkServerControlImpl.INVALID_OR_NOTSET_SECURITYMECHANISM)
1601: && securityMechanism != server
1602: .getSecurityMechanism()) {
1603: securityCheckCode = CodePoint.SECCHKCD_NOTSUPPORTED;
1604: if (SanityManager.DEBUG) {
1605: trace("parseACCSEC - SECCHKCD_NOTSUPPORTED [1] - "
1606: + securityMechanism
1607: + " <> "
1608: + server.getSecurityMechanism() + "\n");
1609: }
1610: } else {
1611: // for plain text userid,password USRIDPWD, and USRIDONL
1612: // no need of decryptionManager
1613: if (securityMechanism != CodePoint.SECMEC_USRIDPWD
1614: && securityMechanism != CodePoint.SECMEC_USRIDONL) {
1615: // These are the only other mechanisms we understand
1616: if (((securityMechanism != CodePoint.SECMEC_EUSRIDPWD) || (securityMechanism == CodePoint.SECMEC_EUSRIDPWD && !server
1617: .supportsEUSRIDPWD()))
1618: && (securityMechanism != CodePoint.SECMEC_USRSSBPWD))
1619: //securityCheckCode = CodePoint.SECCHKCD_NOTSUPPORTED;
1620: {
1621: securityCheckCode = CodePoint.SECCHKCD_NOTSUPPORTED;
1622: if (SanityManager.DEBUG) {
1623: trace("parseACCSEC - SECCHKCD_NOTSUPPORTED [2]\n");
1624: }
1625: } else {
1626: // We delay the initialization and required
1627: // processing for SECMEC_USRSSBPWD as we need
1628: // to ensure the database is booted so that
1629: // we can verify that the current auth scheme
1630: // is set to BUILT-IN or NONE. For this we need
1631: // to have the RDBNAM codepoint available.
1632: //
1633: // See validateSecMecUSRSSBPWD() call below
1634: if (securityMechanism == CodePoint.SECMEC_USRSSBPWD)
1635: break;
1636:
1637: // SECMEC_EUSRIDPWD initialization
1638: try {
1639: if (decryptionManager == null)
1640: decryptionManager = new DecryptionManager();
1641: myPublicKey = decryptionManager
1642: .obtainPublicKey();
1643: } catch (SQLException e) {
1644: println2Log(null, session.drdaID, e
1645: .getMessage());
1646: // Local security service non-retryable error.
1647: securityCheckCode = CodePoint.SECCHKCD_0A;
1648: }
1649: }
1650: }
1651: }
1652: break;
1653: //optional (currently required for Cloudscape - needed for
1654: // DERBY-528 as well)
1655: case CodePoint.RDBNAM:
1656: String dbname = parseRDBNAM();
1657: Database d = session.getDatabase(dbname);
1658: if (d == null)
1659: addDatabase(dbname);
1660: else {
1661: // reset database for connection re-use
1662: d.reset();
1663: database = d;
1664: }
1665: break;
1666: //optional - depending on security Mechanism
1667: case CodePoint.SECTKN:
1668: secTokenIn = reader.readBytes();
1669: break;
1670: default:
1671: invalidCodePoint(codePoint);
1672: }
1673: codePoint = reader.getCodePoint();
1674: }
1675:
1676: // check for required CodePoint's
1677: if (securityMechanism == 0)
1678: missingCodePoint(CodePoint.SECMEC);
1679:
1680: // RESOLVE - when we look further into security we might want to
1681: // handle this part of the protocol at the session level without
1682: // requiring a database for when authentication is used but there
1683: // is no database level security
1684: if (database == null)
1685: missingCodePoint(CodePoint.RDBNAM);
1686:
1687: database.securityMechanism = securityMechanism;
1688: database.secTokenIn = secTokenIn;
1689:
1690: // If security mechanism is SECMEC_USRSSBPWD, then ensure it can be
1691: // used for the database or system based on the client's connection
1692: // URL and its identity.
1693: if (securityCheckCode == 0
1694: && (database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)) {
1695: if (SanityManager.DEBUG)
1696: SanityManager
1697: .ASSERT(
1698: (securityCheckCode == 0),
1699: "SECMEC_USRSSBPWD: securityCheckCode should not "
1700: + "already be set, found it initialized with "
1701: + "a value of '"
1702: + securityCheckCode + "'.");
1703: securityCheckCode = validateSecMecUSRSSBPWD();
1704: }
1705:
1706: // need security token
1707: if (securityCheckCode == 0
1708: && (database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD || database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)
1709: && database.secTokenIn == null)
1710: securityCheckCode = CodePoint.SECCHKCD_SECTKNMISSING_OR_INVALID;
1711:
1712: // shouldn't have security token
1713: if (securityCheckCode == 0
1714: && (database.securityMechanism == CodePoint.SECMEC_USRIDPWD || database.securityMechanism == CodePoint.SECMEC_USRIDONL)
1715: && database.secTokenIn != null)
1716: securityCheckCode = CodePoint.SECCHKCD_SECTKNMISSING_OR_INVALID;
1717:
1718: if (SanityManager.DEBUG)
1719: trace("** ACCSECRD securityCheckCode is: "
1720: + securityCheckCode);
1721:
1722: // If the security check was successful set the session state to
1723: // security accesseed. Otherwise go back to attributes exchanged so we
1724: // require another ACCSEC
1725: if (securityCheckCode == 0)
1726: session.setState(session.SECACC);
1727: else
1728: session.setState(session.ATTEXC);
1729:
1730: return securityCheckCode;
1731: }
1732:
1733: /**
1734: * Parse OPNQRY
1735: * Instance Variables
1736: * RDBNAM - relational database name - optional
1737: * PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
1738: * QRYBLKSZ - Query Block Size - required
1739: * QRYBLKCTL - Query Block Protocol Control - optional
1740: * MAXBLKEXT - Maximum Number of Extra Blocks - optional - default value 0
1741: * OUTOVROPT - Output Override Option
1742: * QRYROWSET - Query Rowset Size - optional - level 7
1743: * MONITOR - Monitor events - optional.
1744: *
1745: * @return RDB Package Name, Consistency Token, and Section Number
1746: * @exception DRDAProtocolException
1747: */
1748: private Pkgnamcsn parseOPNQRY() throws DRDAProtocolException,
1749: SQLException {
1750: Pkgnamcsn pkgnamcsn = null;
1751: boolean gotQryblksz = false;
1752: int blksize = 0;
1753: int qryblkctl = CodePoint.QRYBLKCTL_DEFAULT;
1754: int maxblkext = CodePoint.MAXBLKEXT_DEFAULT;
1755: int qryrowset = CodePoint.QRYROWSET_DEFAULT;
1756: int qryclsimp = DRDAResultSet.QRYCLSIMP_DEFAULT;
1757: int outovropt = CodePoint.OUTOVRFRS;
1758: reader.markCollection();
1759: int codePoint = reader.getCodePoint();
1760: while (codePoint != -1) {
1761: switch (codePoint) {
1762: //optional
1763: case CodePoint.RDBNAM:
1764: setDatabase(CodePoint.OPNQRY);
1765: break;
1766: //required
1767: case CodePoint.PKGNAMCSN:
1768: pkgnamcsn = parsePKGNAMCSN();
1769: break;
1770: //required
1771: case CodePoint.QRYBLKSZ:
1772: blksize = parseQRYBLKSZ();
1773: gotQryblksz = true;
1774: break;
1775: //optional
1776: case CodePoint.QRYBLKCTL:
1777: qryblkctl = reader.readNetworkShort();
1778: //The only type of query block control we can specify here
1779: //is forced fixed row
1780: if (qryblkctl != CodePoint.FRCFIXROW)
1781: invalidCodePoint(qryblkctl);
1782: if (SanityManager.DEBUG)
1783: trace("!!qryblkctl = "
1784: + Integer.toHexString(qryblkctl));
1785: gotQryblksz = true;
1786: break;
1787: //optional
1788: case CodePoint.MAXBLKEXT:
1789: maxblkext = reader.readSignedNetworkShort();
1790: if (SanityManager.DEBUG)
1791: trace("maxblkext = " + maxblkext);
1792: break;
1793: // optional
1794: case CodePoint.OUTOVROPT:
1795: outovropt = parseOUTOVROPT();
1796: break;
1797: //optional
1798: case CodePoint.QRYROWSET:
1799: //Note minimum for OPNQRY is 0
1800: qryrowset = parseQRYROWSET(0);
1801: break;
1802: case CodePoint.QRYCLSIMP:
1803: // Implicitly close non-scrollable cursor
1804: qryclsimp = parseQRYCLSIMP();
1805: break;
1806: case CodePoint.QRYCLSRLS:
1807: // Ignore release of read locks. Nothing we can do here
1808: parseQRYCLSRLS();
1809: break;
1810: // optional
1811: case CodePoint.MONITOR:
1812: parseMONITOR();
1813: break;
1814: default:
1815: invalidCodePoint(codePoint);
1816: }
1817: codePoint = reader.getCodePoint();
1818: }
1819: // check for required variables
1820: if (pkgnamcsn == null)
1821: missingCodePoint(CodePoint.PKGNAMCSN);
1822: if (!gotQryblksz)
1823: missingCodePoint(CodePoint.QRYBLKSZ);
1824:
1825: // get the statement we are opening
1826: DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
1827: if (stmt == null) {
1828: //XXX should really throw a SQL Exception here
1829: invalidValue(CodePoint.PKGNAMCSN);
1830: }
1831:
1832: // check that this statement is not already open
1833: // commenting this check out for now
1834: // it turns out that JCC doesn't send a close if executeQuery is
1835: // done again without closing the previous result set
1836: // this check can't be done since the second executeQuery should work
1837: //if (stmt.state != DRDAStatement.NOT_OPENED)
1838: //{
1839: // writeQRYPOPRM();
1840: // pkgnamcsn = null;
1841: //}
1842: //else
1843: //{
1844: stmt.setOPNQRYOptions(blksize, qryblkctl, maxblkext, outovropt,
1845: qryrowset, qryclsimp);
1846: //}
1847:
1848: // read the command objects
1849: // for ps with parameter
1850: if (reader.isChainedWithSameID()) {
1851: if (SanityManager.DEBUG)
1852: trace("&&&&&& parsing SQLDTA");
1853: parseOPNQRYobjects(stmt);
1854: }
1855: return pkgnamcsn;
1856: }
1857:
1858: /**
1859: * Parse OPNQRY objects
1860: * Objects
1861: * TYPDEFNAM - Data type definition name - optional
1862: * TYPDEFOVR - Type defintion overrides - optional
1863: * SQLDTA- SQL Program Variable Data - optional
1864: *
1865: * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
1866: * sent with the statement. Once the statement is over, the default values
1867: * sent in the ACCRDB are once again in effect. If no values are supplied,
1868: * the values sent in the ACCRDB are used.
1869: * Objects may follow in one DSS or in several DSS chained together.
1870: *
1871: * @throws DRDAProtocolException
1872: * @throws SQLException
1873: */
1874: private void parseOPNQRYobjects(DRDAStatement stmt)
1875: throws DRDAProtocolException, SQLException {
1876: int codePoint;
1877: do {
1878: correlationID = reader.readDssHeader();
1879: while (reader.moreDssData()) {
1880: codePoint = reader.readLengthAndCodePoint();
1881: switch (codePoint) {
1882: // optional
1883: case CodePoint.TYPDEFNAM:
1884: setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
1885: break;
1886: // optional
1887: case CodePoint.TYPDEFOVR:
1888: parseTYPDEFOVR(stmt);
1889: break;
1890: // optional
1891: case CodePoint.SQLDTA:
1892: parseSQLDTA(stmt);
1893: break;
1894: // optional
1895: case CodePoint.EXTDTA:
1896: readAndSetAllExtParams(stmt, false);
1897: break;
1898: default:
1899: invalidCodePoint(codePoint);
1900: }
1901: }
1902: } while (reader.isChainedWithSameID());
1903:
1904: }
1905:
1906: /**
1907: * Parse OUTOVROPT - this indicates whether output description can be
1908: * overridden on just the first CNTQRY or on any CNTQRY
1909: *
1910: * @return output override option
1911: * @exception DRDAProtocolException
1912: */
1913: private int parseOUTOVROPT() throws DRDAProtocolException {
1914: checkLength(CodePoint.OUTOVROPT, 1);
1915: int outovropt = reader.readUnsignedByte();
1916: if (SanityManager.DEBUG)
1917: trace("output override option: " + outovropt);
1918: if (outovropt != CodePoint.OUTOVRFRS
1919: && outovropt != CodePoint.OUTOVRANY)
1920: invalidValue(CodePoint.OUTOVROPT);
1921: return outovropt;
1922: }
1923:
1924: /**
1925: * Parse QRYBLSZ - this gives the maximum size of the query blocks that
1926: * can be returned to the requester
1927: *
1928: * @return query block size
1929: * @exception DRDAProtocolException
1930: */
1931: private int parseQRYBLKSZ() throws DRDAProtocolException {
1932: checkLength(CodePoint.QRYBLKSZ, 4);
1933: int blksize = reader.readNetworkInt();
1934: if (SanityManager.DEBUG)
1935: trace("qryblksz = " + blksize);
1936: if (blksize < CodePoint.QRYBLKSZ_MIN
1937: || blksize > CodePoint.QRYBLKSZ_MAX)
1938: invalidValue(CodePoint.QRYBLKSZ);
1939: return blksize;
1940: }
1941:
1942: /**
1943: * Parse QRYROWSET - this is the number of rows to return
1944: *
1945: * @param minVal - minimum value
1946: * @return query row set size
1947: * @exception DRDAProtocolException
1948: */
1949: private int parseQRYROWSET(int minVal) throws DRDAProtocolException {
1950: checkLength(CodePoint.QRYROWSET, 4);
1951: int qryrowset = reader.readNetworkInt();
1952: if (SanityManager.DEBUG)
1953: trace("qryrowset = " + qryrowset);
1954: if (qryrowset < minVal || qryrowset > CodePoint.QRYROWSET_MAX)
1955: invalidValue(CodePoint.QRYROWSET);
1956: return qryrowset;
1957: }
1958:
1959: /** Parse a QRYCLSIMP - Implicitly close non-scrollable cursor
1960: * after end of data.
1961: * @return true to close on end of data
1962: */
1963: private int parseQRYCLSIMP() throws DRDAProtocolException {
1964:
1965: checkLength(CodePoint.QRYCLSIMP, 1);
1966: int qryclsimp = reader.readUnsignedByte();
1967: if (SanityManager.DEBUG)
1968: trace("qryclsimp = " + qryclsimp);
1969: if (qryclsimp != CodePoint.QRYCLSIMP_SERVER_CHOICE
1970: && qryclsimp != CodePoint.QRYCLSIMP_YES
1971: && qryclsimp != CodePoint.QRYCLSIMP_NO)
1972: invalidValue(CodePoint.QRYCLSIMP);
1973: return qryclsimp;
1974: }
1975:
1976: private int parseQRYCLSRLS() throws DRDAProtocolException {
1977: reader.skipBytes();
1978: return 0;
1979: }
1980:
1981: /**
1982: * Write a QRYPOPRM - Query Previously opened
1983: * Instance Variables
1984: * SVRCOD - Severity Code - required - 8 ERROR
1985: * RDBNAM - Relational Database Name - required
1986: * PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
1987: *
1988: * @exception DRDAProtocolException
1989: */
1990: private void writeQRYPOPRM() throws DRDAProtocolException {
1991: writer.createDssReply();
1992: writer.startDdm(CodePoint.QRYPOPRM);
1993: writer.writeScalar2Bytes(CodePoint.SVRCOD,
1994: CodePoint.SVRCOD_ERROR);
1995: writeRDBNAM(database.dbName);
1996: writePKGNAMCSN();
1997: writer.endDdmAndDss();
1998: }
1999:
2000: /**
2001: * Write a QRYNOPRM - Query Not Opened
2002: * Instance Variables
2003: * SVRCOD - Severity Code - required - 4 Warning 8 ERROR
2004: * RDBNAM - Relational Database Name - required
2005: * PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
2006: *
2007: * @param svrCod Severity Code
2008: * @exception DRDAProtocolException
2009: */
2010: private void writeQRYNOPRM(int svrCod) throws DRDAProtocolException {
2011: writer.createDssReply();
2012: writer.startDdm(CodePoint.QRYNOPRM);
2013: writer.writeScalar2Bytes(CodePoint.SVRCOD, svrCod);
2014: writeRDBNAM(database.dbName);
2015: writePKGNAMCSN();
2016: writer.endDdmAndDss();
2017: }
2018:
2019: /**
2020: * Write a OPNQFLRM - Open Query Failure
2021: * Instance Variables
2022: * SVRCOD - Severity Code - required - 8 ERROR
2023: * RDBNAM - Relational Database Name - required
2024: *
2025: * @param e Exception describing failure
2026: *
2027: * @exception DRDAProtocolException
2028: */
2029: private void writeOPNQFLRM(SQLException e)
2030: throws DRDAProtocolException {
2031: writer.createDssReply();
2032: writer.startDdm(CodePoint.OPNQFLRM);
2033: writer.writeScalar2Bytes(CodePoint.SVRCOD,
2034: CodePoint.SVRCOD_ERROR);
2035: writeRDBNAM(database.dbName);
2036: writer.endDdm();
2037: writer.startDdm(CodePoint.SQLCARD);
2038: writeSQLCAGRP(e, getSqlCode(getExceptionSeverity(e)), 0, 0);
2039: writer.endDdmAndDss();
2040: }
2041:
2042: /**
2043: * Write PKGNAMCSN
2044: * Instance Variables
2045: * NAMESYMDR - database name - not validated
2046: * RDBCOLID - RDB Collection Identifier
2047: * PKGID - RDB Package Identifier
2048: * PKGCNSTKN - RDB Package Consistency Token
2049: * PKGSN - RDB Package Section Number
2050: *
2051: * There are two possible formats, fixed and extended which includes length
2052: * information for the strings
2053: *
2054: * @throws DRDAProtocolException
2055: */
2056: private void writePKGNAMCSN(byte[] pkgcnstkn)
2057: throws DRDAProtocolException {
2058: writer.startDdm(CodePoint.PKGNAMCSN);
2059: if (rdbnam.length() <= CodePoint.RDBNAM_LEN
2060: && rdbcolid.length() <= CodePoint.RDBCOLID_LEN
2061: && pkgid.length() <= CodePoint.PKGID_LEN) { // if none of RDBNAM, RDBCOLID and PKGID have a length of
2062: // more than 18, use fixed format
2063: writer
2064: .writeScalarPaddedString(rdbnam,
2065: CodePoint.RDBNAM_LEN);
2066: writer.writeScalarPaddedString(rdbcolid,
2067: CodePoint.RDBCOLID_LEN);
2068: writer.writeScalarPaddedString(pkgid, CodePoint.PKGID_LEN);
2069: writer.writeScalarPaddedBytes(pkgcnstkn,
2070: CodePoint.PKGCNSTKN_LEN, (byte) 0);
2071: writer.writeShort(pkgsn);
2072: } else // extended format
2073: {
2074: int len = Math.max(CodePoint.RDBNAM_LEN, rdbnam.length());
2075: writer.writeShort(len);
2076: writer.writeScalarPaddedString(rdbnam, len);
2077: len = Math.max(CodePoint.RDBCOLID_LEN, rdbcolid.length());
2078: writer.writeShort(len);
2079: writer.writeScalarPaddedString(rdbcolid, len);
2080: len = Math.max(CodePoint.PKGID_LEN, pkgid.length());
2081: writer.writeShort(len);
2082: writer.writeScalarPaddedString(pkgid, len);
2083: writer.writeScalarPaddedBytes(pkgcnstkn,
2084: CodePoint.PKGCNSTKN_LEN, (byte) 0);
2085: writer.writeShort(pkgsn);
2086: }
2087: writer.endDdm();
2088: }
2089:
2090: private void writePKGNAMCSN() throws DRDAProtocolException {
2091: writePKGNAMCSN(pkgcnstkn.getBytes());
2092: }
2093:
2094: /**
2095: * Parse CNTQRY - Continue Query
2096: * Instance Variables
2097: * RDBNAM - Relational Database Name - optional
2098: * PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
2099: * QRYBLKSZ - Query Block Size - required
2100: * QRYRELSCR - Query Relative Scrolling Action - optional
2101: * QRYSCRORN - Query Scroll Orientation - optional - level 7
2102: * QRYROWNBR - Query Row Number - optional
2103: * QRYROWSNS - Query Row Sensitivity - optional - level 7
2104: * QRYBLKRST - Query Block Reset - optional - level 7
2105: * QRYRTNDTA - Query Returns Data - optional - level 7
2106: * QRYROWSET - Query Rowset Size - optional - level 7
2107: * QRYRFRTBL - Query Refresh Answer Set Table - optional
2108: * NBRROW - Number of Fetch or Insert Rows - optional
2109: * MAXBLKEXT - Maximum number of extra blocks - optional
2110: * RTNEXTDTA - Return of EXTDTA Option - optional
2111: * MONITOR - Monitor events - optional.
2112: *
2113: * @return DRDAStatement we are continuing
2114: * @throws DRDAProtocolException
2115: * @throws SQLException
2116: */
2117: private DRDAStatement parseCNTQRY() throws DRDAProtocolException,
2118: SQLException {
2119: byte val;
2120: Pkgnamcsn pkgnamcsn = null;
2121: boolean gotQryblksz = false;
2122: boolean qryrelscr = true;
2123: long qryrownbr = 1;
2124: boolean qryrfrtbl = false;
2125: int nbrrow = 1;
2126: int blksize = 0;
2127: int maxblkext = -1;
2128: long qryinsid;
2129: boolean gotQryinsid = false;
2130: int qryscrorn = CodePoint.QRYSCRREL;
2131: boolean qryrowsns = false;
2132: boolean gotQryrowsns = false;
2133: boolean qryblkrst = false;
2134: boolean qryrtndta = true;
2135: int qryrowset = CodePoint.QRYROWSET_DEFAULT;
2136: int rtnextdta = CodePoint.RTNEXTROW;
2137: reader.markCollection();
2138: int codePoint = reader.getCodePoint();
2139: while (codePoint != -1) {
2140: switch (codePoint) {
2141: //optional
2142: case CodePoint.RDBNAM:
2143: setDatabase(CodePoint.CNTQRY);
2144: break;
2145: //required
2146: case CodePoint.PKGNAMCSN:
2147: pkgnamcsn = parsePKGNAMCSN();
2148: break;
2149: //required
2150: case CodePoint.QRYBLKSZ:
2151: blksize = parseQRYBLKSZ();
2152: gotQryblksz = true;
2153: break;
2154: //optional
2155: case CodePoint.QRYRELSCR:
2156: qryrelscr = readBoolean(CodePoint.QRYRELSCR);
2157: if (SanityManager.DEBUG)
2158: trace("qryrelscr = " + qryrelscr);
2159: break;
2160: //optional
2161: case CodePoint.QRYSCRORN:
2162: checkLength(CodePoint.QRYSCRORN, 1);
2163: qryscrorn = reader.readUnsignedByte();
2164: if (SanityManager.DEBUG)
2165: trace("qryscrorn = " + qryscrorn);
2166: switch (qryscrorn) {
2167: case CodePoint.QRYSCRREL:
2168: case CodePoint.QRYSCRABS:
2169: case CodePoint.QRYSCRAFT:
2170: case CodePoint.QRYSCRBEF:
2171: break;
2172: default:
2173: invalidValue(CodePoint.QRYSCRORN);
2174: }
2175: break;
2176: //optional
2177: case CodePoint.QRYROWNBR:
2178: checkLength(CodePoint.QRYROWNBR, 8);
2179: qryrownbr = reader.readNetworkLong();
2180: if (SanityManager.DEBUG)
2181: trace("qryrownbr = " + qryrownbr);
2182: break;
2183: //optional
2184: case CodePoint.QRYROWSNS:
2185: checkLength(CodePoint.QRYROWSNS, 1);
2186: qryrowsns = readBoolean(CodePoint.QRYROWSNS);
2187: if (SanityManager.DEBUG)
2188: trace("qryrowsns = " + qryrowsns);
2189: gotQryrowsns = true;
2190: break;
2191: //optional
2192: case CodePoint.QRYBLKRST:
2193: checkLength(CodePoint.QRYBLKRST, 1);
2194: qryblkrst = readBoolean(CodePoint.QRYBLKRST);
2195: if (SanityManager.DEBUG)
2196: trace("qryblkrst = " + qryblkrst);
2197: break;
2198: //optional
2199: case CodePoint.QRYRTNDTA:
2200: qryrtndta = readBoolean(CodePoint.QRYRTNDTA);
2201: if (SanityManager.DEBUG)
2202: trace("qryrtndta = " + qryrtndta);
2203: break;
2204: //optional
2205: case CodePoint.QRYROWSET:
2206: //Note minimum for CNTQRY is 1
2207: qryrowset = parseQRYROWSET(1);
2208: if (SanityManager.DEBUG)
2209: trace("qryrowset = " + qryrowset);
2210: break;
2211: //optional
2212: case CodePoint.QRYRFRTBL:
2213: qryrfrtbl = readBoolean(CodePoint.QRYRFRTBL);
2214: if (SanityManager.DEBUG)
2215: trace("qryrfrtbl = " + qryrfrtbl);
2216: break;
2217: //optional
2218: case CodePoint.NBRROW:
2219: checkLength(CodePoint.NBRROW, 4);
2220: nbrrow = reader.readNetworkInt();
2221: if (SanityManager.DEBUG)
2222: trace("nbrrow = " + nbrrow);
2223: break;
2224: //optional
2225: case CodePoint.MAXBLKEXT:
2226: checkLength(CodePoint.MAXBLKEXT, 2);
2227: maxblkext = reader.readSignedNetworkShort();
2228: if (SanityManager.DEBUG)
2229: trace("maxblkext = " + maxblkext);
2230: break;
2231: //optional
2232: case CodePoint.RTNEXTDTA:
2233: checkLength(CodePoint.RTNEXTDTA, 1);
2234: rtnextdta = reader.readUnsignedByte();
2235: if (rtnextdta != CodePoint.RTNEXTROW
2236: && rtnextdta != CodePoint.RTNEXTALL)
2237: invalidValue(CodePoint.RTNEXTDTA);
2238: if (SanityManager.DEBUG)
2239: trace("rtnextdta = " + rtnextdta);
2240: break;
2241: // required for SQLAM >= 7
2242: case CodePoint.QRYINSID:
2243: checkLength(CodePoint.QRYINSID, 8);
2244: qryinsid = reader.readNetworkLong();
2245: gotQryinsid = true;
2246: if (SanityManager.DEBUG)
2247: trace("qryinsid = " + qryinsid);
2248: break;
2249: // optional
2250: case CodePoint.MONITOR:
2251: parseMONITOR();
2252: break;
2253: default:
2254: invalidCodePoint(codePoint);
2255: }
2256: codePoint = reader.getCodePoint();
2257: }
2258: // check for required variables
2259: if (pkgnamcsn == null)
2260: missingCodePoint(CodePoint.PKGNAMCSN);
2261: if (!gotQryblksz)
2262: missingCodePoint(CodePoint.QRYBLKSZ);
2263: if (sqlamLevel >= MGRLVL_7 && !gotQryinsid)
2264: missingCodePoint(CodePoint.QRYINSID);
2265:
2266: // get the statement we are continuing
2267: DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
2268: if (stmt == null) {
2269: //XXX should really throw a SQL Exception here
2270: invalidValue(CodePoint.CNTQRY);
2271: }
2272:
2273: if (stmt.rsIsClosed()) {
2274: writeQRYNOPRM(CodePoint.SVRCOD_ERROR);
2275: skipRemainder(true);
2276: return null;
2277: }
2278: stmt.setQueryOptions(blksize, qryrelscr, qryrownbr, qryrfrtbl,
2279: nbrrow, maxblkext, qryscrorn, qryrowsns, qryblkrst,
2280: qryrtndta, qryrowset, rtnextdta);
2281:
2282: if (reader.isChainedWithSameID())
2283: parseCNTQRYobjects(stmt);
2284: return stmt;
2285: }
2286:
2287: /**
2288: * Skip remainder of current DSS and all chained DSS'es
2289: *
2290: * @param onlySkipSameIds True if we _only_ want to skip DSS'es
2291: * that are chained with the SAME id as the current DSS.
2292: * False means skip ALL chained DSSes, whether they're
2293: * chained with same or different ids.
2294: * @exception DRDAProtocolException
2295: */
2296: private void skipRemainder(boolean onlySkipSameIds)
2297: throws DRDAProtocolException {
2298: reader.skipDss();
2299: while (reader.isChainedWithSameID()
2300: || (!onlySkipSameIds && reader.isChainedWithDiffID())) {
2301: reader.readDssHeader();
2302: reader.skipDss();
2303: }
2304: }
2305:
2306: /**
2307: * Parse CNTQRY objects
2308: * Instance Variables
2309: * OUTOVR - Output Override Descriptor - optional
2310: *
2311: * @param stmt DRDA statement we are working on
2312: * @exception DRDAProtocolException
2313: */
2314: private void parseCNTQRYobjects(DRDAStatement stmt)
2315: throws DRDAProtocolException, SQLException {
2316: int codePoint;
2317: do {
2318: correlationID = reader.readDssHeader();
2319: while (reader.moreDssData()) {
2320: codePoint = reader.readLengthAndCodePoint();
2321: switch (codePoint) {
2322: // optional
2323: case CodePoint.OUTOVR:
2324: parseOUTOVR(stmt);
2325: break;
2326: default:
2327: invalidCodePoint(codePoint);
2328: }
2329: }
2330: } while (reader.isChainedWithSameID());
2331:
2332: }
2333:
2334: /**
2335: * Parse OUTOVR - Output Override Descriptor
2336: * This specifies the output format for data to be returned as output to a SQL
2337: * statement or as output from a query.
2338: *
2339: * @param stmt DRDA statement this applies to
2340: * @exception DRDAProtocolException
2341: */
2342: private void parseOUTOVR(DRDAStatement stmt)
2343: throws DRDAProtocolException, SQLException {
2344: boolean first = true;
2345: int numVars;
2346: int dtaGrpLen;
2347: int tripType;
2348: int tripId;
2349: int precision;
2350: int start = 0;
2351: while (true) {
2352: dtaGrpLen = reader.readUnsignedByte();
2353: tripType = reader.readUnsignedByte();
2354: tripId = reader.readUnsignedByte();
2355: // check if we have reached the end of the data
2356: if (tripType == FdocaConstants.RLO_TRIPLET_TYPE) {
2357: //read last part of footer
2358: reader.skipBytes();
2359: break;
2360: }
2361: numVars = (dtaGrpLen - 3) / 3;
2362: if (SanityManager.DEBUG)
2363: trace("num of vars is: " + numVars);
2364: int[] outovr_drdaType = null;
2365: if (first) {
2366: outovr_drdaType = new int[numVars];
2367: first = false;
2368: } else {
2369: int[] oldoutovr_drdaType = stmt.getOutovr_drdaType();
2370: int oldlen = oldoutovr_drdaType.length;
2371: // create new array and copy over already read stuff
2372: outovr_drdaType = new int[oldlen + numVars];
2373: System.arraycopy(oldoutovr_drdaType, 0,
2374: outovr_drdaType, 0, oldlen);
2375: start = oldlen;
2376: }
2377: for (int i = start; i < numVars + start; i++) {
2378: outovr_drdaType[i] = reader.readUnsignedByte();
2379: if (SanityManager.DEBUG)
2380: trace("drdaType is: " + outovr_drdaType[i]);
2381: precision = reader.readNetworkShort();
2382: if (SanityManager.DEBUG)
2383: trace("drdaLength is: " + precision);
2384: outovr_drdaType[i] |= (precision << 8);
2385: }
2386: stmt.setOutovr_drdaType(outovr_drdaType);
2387: }
2388: }
2389:
2390: /**
2391: * Write OPNQRYRM - Open Query Complete
2392: * Instance Variables
2393: * SVRCOD - Severity Code - required
2394: * QRYPRCTYP - Query Protocol Type - required
2395: * SQLCSRHLD - Hold Cursor Position - optional
2396: * QRYATTSCR - Query Attribute for Scrollability - optional - level 7
2397: * QRYATTSNS - Query Attribute for Sensitivity - optional - level 7
2398: * QRYATTUPD - Query Attribute for Updatability -optional - level 7
2399: * QRYINSID - Query Instance Identifier - required - level 7
2400: * SRVDGN - Server Diagnostic Information - optional
2401: *
2402: * @param isDssObject - return as a DSS object (part of a reply)
2403: * @param stmt - DRDA statement we are processing
2404: *
2405: * @exception DRDAProtocolException
2406: */
2407: private void writeOPNQRYRM(boolean isDssObject, DRDAStatement stmt)
2408: throws DRDAProtocolException, SQLException {
2409: if (SanityManager.DEBUG)
2410: trace("WriteOPNQRYRM");
2411:
2412: if (isDssObject)
2413: writer.createDssObject();
2414: else
2415: writer.createDssReply();
2416: writer.startDdm(CodePoint.OPNQRYRM);
2417: writer.writeScalar2Bytes(CodePoint.SVRCOD,
2418: CodePoint.SVRCOD_INFO);
2419:
2420: // There is currently a problem specifying LMTBLKPRC for LOBs with JCC
2421: // JCC will throw an ArrayOutOfBounds exception. Once this is fixed, we
2422: // don't need to pass the two arguments for getQryprctyp.
2423: int prcType = stmt.getQryprctyp();
2424: if (SanityManager.DEBUG)
2425: trace("sending QRYPRCTYP: " + prcType);
2426: writer.writeScalar2Bytes(CodePoint.QRYPRCTYP, prcType);
2427:
2428: //pass the SQLCSRHLD codepoint only if statement producing the ResultSet has
2429: //hold cursors over commit set. In case of stored procedures which use server-side
2430: //JDBC, the holdability of the ResultSet will be the holdability of the statement
2431: //in the stored procedure, not the holdability of the calling statement.
2432: if (stmt.getCurrentDrdaResultSet().withHoldCursor == JDBC30Translation.HOLD_CURSORS_OVER_COMMIT)
2433: writer
2434: .writeScalar1Byte(CodePoint.SQLCSRHLD,
2435: CodePoint.TRUE);
2436: if (sqlamLevel >= MGRLVL_7) {
2437: writer.writeScalarHeader(CodePoint.QRYINSID, 8);
2438: //This is implementer defined. DB2 uses this for the nesting level
2439: //of the query. A query from an application would be nesting level 0,
2440: //from a stored procedure, nesting level 1, from a recursive call of
2441: //a stored procedure, nesting level 2, etc.
2442: writer.writeInt(0);
2443: //This is a unique sequence number per session
2444: writer.writeInt(session.qryinsid++);
2445: //Write the scroll attributes if they are set
2446: if (stmt.isScrollable()) {
2447: writer.writeScalar1Byte(CodePoint.QRYATTSCR,
2448: CodePoint.TRUE);
2449: if ((stmt.getConcurType() == ResultSet.CONCUR_UPDATABLE)
2450: && (stmt.getResultSet().getType() == ResultSet.TYPE_SCROLL_INSENSITIVE)) {
2451: writer.writeScalar1Byte(CodePoint.QRYATTSNS,
2452: CodePoint.QRYSNSSTC);
2453: } else {
2454: writer.writeScalar1Byte(CodePoint.QRYATTSNS,
2455: CodePoint.QRYINS);
2456: }
2457: }
2458: if (stmt.getConcurType() == ResultSet.CONCUR_UPDATABLE) {
2459: if (stmt.getResultSet() != null) {
2460: // Resultset concurrency can be less than statement
2461: // concurreny if the underlying language resultset
2462: // is not updatable.
2463: if (stmt.getResultSet().getConcurrency() == ResultSet.CONCUR_UPDATABLE) {
2464: writer.writeScalar1Byte(CodePoint.QRYATTUPD,
2465: CodePoint.QRYUPD);
2466: } else {
2467: writer.writeScalar1Byte(CodePoint.QRYATTUPD,
2468: CodePoint.QRYRDO);
2469: }
2470: } else {
2471: writer.writeScalar1Byte(CodePoint.QRYATTUPD,
2472: CodePoint.QRYUPD);
2473: }
2474: } else {
2475: writer.writeScalar1Byte(CodePoint.QRYATTUPD,
2476: CodePoint.QRYRDO);
2477: }
2478: }
2479: writer.endDdmAndDss();
2480: }
2481:
2482: /**
2483: * Write ENDQRYRM - query process has terminated in such a manner that the
2484: * query or result set is now closed. It cannot be resumed with the CNTQRY
2485: * command or closed with the CLSQRY command
2486: * @param svrCod Severity code - WARNING or ERROR
2487: * @exception DRDAProtocolException
2488: */
2489: private void writeENDQRYRM(int svrCod) throws DRDAProtocolException {
2490: writer.createDssReply();
2491: writer.startDdm(CodePoint.ENDQRYRM);
2492: writer.writeScalar2Bytes(CodePoint.SVRCOD, svrCod);
2493: writer.endDdmAndDss();
2494: }
2495:
2496: /**
2497: * Write ABNUOWRM - query process has terminated in an error condition
2498: * such as deadlock or lock timeout.
2499: * Severity code is always error
2500: * * @exception DRDAProtocolException
2501: */
2502: private void writeABNUOWRM() throws DRDAProtocolException {
2503: writer.createDssReply();
2504: writer.startDdm(CodePoint.ABNUOWRM);
2505: writer.writeScalar2Bytes(CodePoint.SVRCOD,
2506: CodePoint.SVRCOD_ERROR);
2507: writeRDBNAM(database.dbName);
2508: writer.endDdmAndDss();
2509: }
2510:
2511: /**
2512: * Parse database name
2513: *
2514: * @return database name
2515: *
2516: * @exception DRDAProtocolException
2517: */
2518: private String parseRDBNAM() throws DRDAProtocolException {
2519: String name;
2520: byte[] rdbName = reader.readBytes();
2521: if (rdbName.length == 0) {
2522: // throw RDBNFNRM
2523: rdbNotFound(null);
2524: }
2525: //SQLAM level 7 allows db name up to 255, level 6 fixed len 18
2526: if (rdbName.length < CodePoint.RDBNAM_LEN
2527: || rdbName.length > CodePoint.MAX_NAME)
2528: badObjectLength(CodePoint.RDBNAM);
2529: name = reader.convertBytes(rdbName);
2530: // trim trailing blanks from the database name
2531: name = name.trim();
2532: if (SanityManager.DEBUG)
2533: trace("RdbName " + name);
2534: return name;
2535: }
2536:
2537: /**
2538: * Write ACCSECRD
2539: * If the security mechanism is known, we just send it back along with
2540: * the security token if encryption is going to be used.
2541: * If the security mechanism is not known, we send a list of the ones
2542: * we know.
2543: * Instance Variables
2544: * SECMEC - security mechanism - required
2545: * SECTKN - security token - optional (required if security mechanism
2546: * uses encryption)
2547: * SECCHKCD - security check code - error occurred in processing ACCSEC
2548: *
2549: * @param securityCheckCode
2550: *
2551: * @exception DRDAProtocolException
2552: */
2553: private void writeACCSECRD(int securityCheckCode)
2554: throws DRDAProtocolException {
2555: writer.createDssReply();
2556: writer.startDdm(CodePoint.ACCSECRD);
2557:
2558: if (securityCheckCode != CodePoint.SECCHKCD_NOTSUPPORTED)
2559: writer.writeScalar2Bytes(CodePoint.SECMEC,
2560: database.securityMechanism);
2561: else {
2562: // if server doesnt recognize or allow the client requested security mechanism,
2563: // then need to return the list of security mechanisms supported/allowed by the server
2564:
2565: // check if server is set to accept connections from client at a certain
2566: // security mechanism, if so send only the security mechanism that the
2567: // server will accept, to the client
2568: if (server.getSecurityMechanism() != NetworkServerControlImpl.INVALID_OR_NOTSET_SECURITYMECHANISM)
2569: writer.writeScalar2Bytes(CodePoint.SECMEC, server
2570: .getSecurityMechanism());
2571: else {
2572: // note: per the DDM manual , ACCSECRD response is of
2573: // form SECMEC (value{value..})
2574: // Need to fix the below to send a list of supported security
2575: // mechanisms for value of one SECMEC codepoint (JIRA 926)
2576: // these are the ones we know about
2577: writer.writeScalar2Bytes(CodePoint.SECMEC,
2578: CodePoint.SECMEC_USRIDPWD);
2579: // include EUSRIDPWD in the list of supported secmec only if
2580: // server can truely support it in the jvm that is running in
2581: if (server.supportsEUSRIDPWD())
2582: writer.writeScalar2Bytes(CodePoint.SECMEC,
2583: CodePoint.SECMEC_EUSRIDPWD);
2584: writer.writeScalar2Bytes(CodePoint.SECMEC,
2585: CodePoint.SECMEC_USRIDONL);
2586: writer.writeScalar2Bytes(CodePoint.SECMEC,
2587: CodePoint.SECMEC_USRSSBPWD);
2588: }
2589: }
2590:
2591: if (securityCheckCode != 0) {
2592: writer.writeScalar1Byte(CodePoint.SECCHKCD,
2593: securityCheckCode);
2594: } else {
2595: // we need to send back the key if encryption is being used
2596: if (database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD)
2597: writer.writeScalarBytes(CodePoint.SECTKN, myPublicKey);
2598: else if (database.securityMechanism == CodePoint.SECMEC_USRSSBPWD)
2599: writer.writeScalarBytes(CodePoint.SECTKN, myTargetSeed);
2600: }
2601: writer.endDdmAndDss();
2602:
2603: if (securityCheckCode != 0) {
2604: // then we have an error and so can ignore the rest of the
2605: // DSS request chain.
2606: skipRemainder(false);
2607: }
2608:
2609: finalizeChain();
2610: }
2611:
2612: /**
2613: * Parse security check
2614: * Instance Variables
2615: * SECMGRNM - security manager name - optional, ignorable
2616: * SECMEC - security mechanism - required
2617: * SECTKN - security token - optional, (required if encryption used)
2618: * PASSWORD - password - optional, (required if security mechanism uses it)
2619: * NEWPASSWORD - new password - optional, (required if sec mech. uses it)
2620: * USRID - user id - optional, (required if sec mec. uses it)
2621: * RDBNAM - database name - optional (required if databases can have own sec.)
2622: *
2623: *
2624: * @return security check code
2625: * @exception DRDAProtocolException
2626: */
2627: private int parseSECCHK() throws DRDAProtocolException {
2628: int codePoint, securityCheckCode = 0;
2629: int securityMechanism = 0;
2630: databaseAccessException = null;
2631: reader.markCollection();
2632: codePoint = reader.getCodePoint();
2633: while (codePoint != -1) {
2634: switch (codePoint) {
2635: //optional, ignorable
2636: case CodePoint.SECMGRNM:
2637: reader.skipBytes();
2638: break;
2639: //required
2640: case CodePoint.SECMEC:
2641: checkLength(CodePoint.SECMEC, 2);
2642: securityMechanism = reader.readNetworkShort();
2643: if (SanityManager.DEBUG)
2644: trace("parseSECCHK - Security mechanism = "
2645: + securityMechanism);
2646: //RESOLVE - spec is not clear on what should happen
2647: //in this case
2648: if (securityMechanism != database.securityMechanism)
2649: invalidValue(CodePoint.SECMEC);
2650: break;
2651: //optional - depending on security Mechanism
2652: case CodePoint.SECTKN:
2653: if ((database.securityMechanism != CodePoint.SECMEC_EUSRIDPWD)
2654: && (database.securityMechanism != CodePoint.SECMEC_USRSSBPWD)) {
2655: securityCheckCode = CodePoint.SECCHKCD_SECTKNMISSING_OR_INVALID;
2656: reader.skipBytes();
2657: } else if (database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD) {
2658: if (database.decryptedUserId == null) {
2659: try {
2660: database.decryptedUserId = reader
2661: .readEncryptedString(
2662: decryptionManager,
2663: database.securityMechanism,
2664: myPublicKey,
2665: database.secTokenIn);
2666: } catch (SQLException se) {
2667: println2Log(database.dbName,
2668: session.drdaID, se.getMessage());
2669: if (securityCheckCode == 0)
2670: //userid invalid
2671: securityCheckCode = CodePoint.SECCHKCD_13;
2672: }
2673: database.userId = database.decryptedUserId;
2674: if (SanityManager.DEBUG)
2675: trace("**decrypted userid is: "
2676: + database.userId);
2677: } else if (database.decryptedPassword == null) {
2678: try {
2679: database.decryptedPassword = reader
2680: .readEncryptedString(
2681: decryptionManager,
2682: database.securityMechanism,
2683: myPublicKey,
2684: database.secTokenIn);
2685: } catch (SQLException se) {
2686: println2Log(database.dbName,
2687: session.drdaID, se.getMessage());
2688: if (securityCheckCode == 0)
2689: //password invalid
2690: securityCheckCode = CodePoint.SECCHKCD_0F;
2691: }
2692: database.password = database.decryptedPassword;
2693: if (SanityManager.DEBUG)
2694: trace("**decrypted password is: "
2695: + database.password);
2696: }
2697: } else if (database.securityMechanism == CodePoint.SECMEC_USRSSBPWD) {
2698: if (database.passwordSubstitute == null) {
2699: database.passwordSubstitute = reader
2700: .readBytes();
2701: if (SanityManager.DEBUG)
2702: trace("** Substitute Password is:"
2703: + DecryptionManager
2704: .toHexString(
2705: database.passwordSubstitute,
2706: 0,
2707: database.passwordSubstitute.length));
2708: database.password = DecryptionManager
2709: .toHexString(
2710: database.passwordSubstitute,
2711: 0,
2712: database.passwordSubstitute.length);
2713: }
2714: } else {
2715: tooMany(CodePoint.SECTKN);
2716: }
2717: break;
2718: //optional - depending on security Mechanism
2719: case CodePoint.PASSWORD:
2720: database.password = reader.readString();
2721: if (SanityManager.DEBUG)
2722: trace("PASSWORD " + database.password);
2723: break;
2724: //optional - depending on security Mechanism
2725: //we are not supporting this method so we'll skip bytes
2726: case CodePoint.NEWPASSWORD:
2727: reader.skipBytes();
2728: break;
2729: //optional - depending on security Mechanism
2730: case CodePoint.USRID:
2731: database.userId = reader.readString();
2732: if (SanityManager.DEBUG)
2733: trace("USERID " + database.userId);
2734: break;
2735: //optional - depending on security Mechanism
2736: case CodePoint.RDBNAM:
2737: String dbname = parseRDBNAM();
2738: if (database != null) {
2739: if (!database.dbName.equals(dbname))
2740: rdbnamMismatch(CodePoint.SECCHK);
2741: } else {
2742: // we should already have added the database in ACCSEC
2743: // added code here in case we make the SECMEC session rather
2744: // than database wide
2745: addDatabase(dbname);
2746: }
2747: break;
2748: default:
2749: invalidCodePoint(codePoint);
2750:
2751: }
2752: codePoint = reader.getCodePoint();
2753: }
2754: // check for SECMEC which is required
2755: if (securityMechanism == 0)
2756: missingCodePoint(CodePoint.SECMEC);
2757:
2758: //check if we have a userid and password when we need it
2759: if (securityCheckCode == 0
2760: && (database.securityMechanism == CodePoint.SECMEC_USRIDPWD || database.securityMechanism == CodePoint.SECMEC_USRIDONL)) {
2761: if (database.userId == null)
2762: securityCheckCode = CodePoint.SECCHKCD_USERIDMISSING;
2763: else if (database.securityMechanism == CodePoint.SECMEC_USRIDPWD) {
2764: if (database.password == null)
2765: securityCheckCode = CodePoint.SECCHKCD_PASSWORDMISSING;
2766: }
2767: //Note, we'll ignore encryptedUserId and encryptedPassword if they
2768: //are also set
2769: }
2770:
2771: if (securityCheckCode == 0
2772: && database.securityMechanism == CodePoint.SECMEC_USRSSBPWD) {
2773: if (database.userId == null)
2774: securityCheckCode = CodePoint.SECCHKCD_USERIDMISSING;
2775: else if (database.passwordSubstitute == null)
2776: securityCheckCode = CodePoint.SECCHKCD_PASSWORDMISSING;
2777: }
2778:
2779: if (securityCheckCode == 0
2780: && database.securityMechanism == CodePoint.SECMEC_EUSRIDPWD) {
2781: if (database.decryptedUserId == null)
2782: securityCheckCode = CodePoint.SECCHKCD_USERIDMISSING;
2783: else if (database.decryptedPassword == null)
2784: securityCheckCode = CodePoint.SECCHKCD_PASSWORDMISSING;
2785: }
2786: // RESOLVE - when we do security we need to decrypt encrypted userid & password
2787: // before proceeding
2788:
2789: // verify userid and password, if we haven't had any errors thus far.
2790: if ((securityCheckCode == 0)
2791: && (databaseAccessException == null)) {
2792: securityCheckCode = verifyUserIdPassword();
2793: }
2794:
2795: // Security all checked
2796: if (securityCheckCode == 0)
2797: session.setState(session.CHKSEC);
2798:
2799: return securityCheckCode;
2800:
2801: }
2802:
2803: /**
2804: * Write security check reply
2805: * Instance variables
2806: * SVRCOD - serverity code - required
2807: * SECCHKCD - security check code - required
2808: * SECTKN - security token - optional, ignorable
2809: * SVCERRNO - security service error number
2810: * SRVDGN - Server Diagnostic Information
2811: *
2812: * @exception DRDAProtocolException
2813: */
2814: private void writeSECCHKRM(int securityCheckCode)
2815: throws DRDAProtocolException {
2816: writer.createDssReply();
2817: writer.startDdm(CodePoint.SECCHKRM);
2818: writer.writeScalar2Bytes(CodePoint.SVRCOD,
2819: svrcodFromSecchkcd(securityCheckCode));
2820: writer.writeScalar1Byte(CodePoint.SECCHKCD, securityCheckCode);
2821: writer.endDdmAndDss();
2822:
2823: if (securityCheckCode != 0) {
2824: // then we have an error and are going to end up ignoring the rest
2825: // of the DSS request chain.
2826: skipRemainder(false);
2827: }
2828:
2829: finalizeChain();
2830:
2831: }
2832:
2833: /**
2834: * Calculate SVRCOD value from SECCHKCD
2835: *
2836: * @param securityCheckCode
2837: * @return SVRCOD value
2838: */
2839: private int svrcodFromSecchkcd(int securityCheckCode) {
2840: if (securityCheckCode == 0 || securityCheckCode == 2
2841: || securityCheckCode == 5 || securityCheckCode == 8)
2842: return CodePoint.SVRCOD_INFO;
2843: else
2844: return CodePoint.SVRCOD_ERROR;
2845: }
2846:
2847: /**
2848: * Parse access RDB
2849: * Instance variables
2850: * RDBACCCL - RDB Access Manager Class - required must be SQLAM
2851: * CRRTKN - Correlation Token - required
2852: * RDBNAM - Relational database name -required
2853: * PRDID - Product specific identifier - required
2854: * TYPDEFNAM - Data Type Definition Name -required
2855: * TYPDEFOVR - Type definition overrides -required
2856: * RDBALWUPD - RDB Allow Updates optional
2857: * PRDDTA - Product Specific Data - optional - ignorable
2858: * STTDECDEL - Statement Decimal Delimiter - optional
2859: * STTSTRDEL - Statement String Delimiter - optional
2860: * TRGDFTRT - Target Default Value Return - optional
2861: *
2862: * @return severity code
2863: *
2864: * @exception DRDAProtocolException
2865: */
2866: private int parseACCRDB() throws DRDAProtocolException {
2867: int codePoint;
2868: int svrcod = 0;
2869: copyToRequired(ACCRDB_REQUIRED);
2870: reader.markCollection();
2871: codePoint = reader.getCodePoint();
2872: while (codePoint != -1) {
2873: switch (codePoint) {
2874: //required
2875: case CodePoint.RDBACCCL:
2876: checkLength(CodePoint.RDBACCCL, 2);
2877: int sqlam = reader.readNetworkShort();
2878: if (SanityManager.DEBUG)
2879: trace("RDBACCCL = " + sqlam);
2880: // required to be SQLAM
2881:
2882: if (sqlam != CodePoint.SQLAM)
2883: invalidValue(CodePoint.RDBACCCL);
2884: removeFromRequired(CodePoint.RDBACCCL);
2885: break;
2886: //required
2887: case CodePoint.CRRTKN:
2888: database.crrtkn = reader.readBytes();
2889: if (SanityManager.DEBUG)
2890: trace("crrtkn "
2891: + convertToHexString(database.crrtkn));
2892: removeFromRequired(CodePoint.CRRTKN);
2893: int l = database.crrtkn.length;
2894: if (l > CodePoint.MAX_NAME)
2895: tooBig(CodePoint.CRRTKN);
2896: // the format of the CRRTKN is defined in the DRDA reference
2897: // x.yz where x is 1 to 8 bytes (variable)
2898: // y is 1 to 8 bytes (variable)
2899: // x is 6 bytes fixed
2900: // size is variable between 9 and 23
2901: if (l < 9 || l > 23)
2902: invalidValue(CodePoint.CRRTKN);
2903: byte[] part1 = new byte[l - 6];
2904: for (int i = 0; i < part1.length; i++)
2905: part1[i] = database.crrtkn[i];
2906: long time = SignedBinary.getLong(database.crrtkn,
2907: l - 8, SignedBinary.BIG_ENDIAN); // as "long" as unique
2908: session.drdaID = reader.convertBytes(part1) + time
2909: + leftBrace + session.connNum + rightBrace;
2910: if (SanityManager.DEBUG)
2911: trace("******************************************drdaID is: "
2912: + session.drdaID);
2913: database.setDrdaID(session.drdaID);
2914:
2915: break;
2916: //required
2917: case CodePoint.RDBNAM:
2918: String dbname = parseRDBNAM();
2919: if (database != null) {
2920: if (!database.dbName.equals(dbname))
2921: rdbnamMismatch(CodePoint.ACCRDB);
2922: } else {
2923: //first time we have seen a database name
2924: Database d = session.getDatabase(dbname);
2925: if (d == null)
2926: addDatabase(dbname);
2927: else {
2928: database = d;
2929: database.accessCount++;
2930: }
2931: }
2932: removeFromRequired(CodePoint.RDBNAM);
2933: break;
2934: //required
2935: case CodePoint.PRDID:
2936: appRequester.setClientVersion(reader.readString());
2937: if (SanityManager.DEBUG)
2938: trace("prdId " + appRequester.prdid);
2939: if (appRequester.prdid.length() > CodePoint.PRDID_MAX)
2940: tooBig(CodePoint.PRDID);
2941:
2942: /* If JCC version is 1.5 or later, send SQLWarning on CNTQRY */
2943: if (((appRequester.getClientType() == appRequester.JCC_CLIENT) && (appRequester
2944: .greaterThanOrEqualTo(1, 5, 0)))
2945: || (appRequester.getClientType() == appRequester.DNC_CLIENT)) {
2946: sendWarningsOnCNTQRY = true;
2947: } else
2948: sendWarningsOnCNTQRY = false;
2949:
2950: // The client can not request DIAGLVL because when run with
2951: // an older server it will cause an exception. Older version
2952: // of the server do not recognize requests for DIAGLVL.
2953: if ((appRequester.getClientType() == appRequester.DNC_CLIENT)
2954: && appRequester.greaterThanOrEqualTo(10, 2, 0)) {
2955: diagnosticLevel = CodePoint.DIAGLVL1;
2956: }
2957:
2958: removeFromRequired(CodePoint.PRDID);
2959: break;
2960: //required
2961: case CodePoint.TYPDEFNAM:
2962: setStmtOrDbByteOrder(true, null, parseTYPDEFNAM());
2963: removeFromRequired(CodePoint.TYPDEFNAM);
2964: break;
2965: //required
2966: case CodePoint.TYPDEFOVR:
2967: parseTYPDEFOVR(null);
2968: removeFromRequired(CodePoint.TYPDEFOVR);
2969: break;
2970: //optional
2971: case CodePoint.RDBALWUPD:
2972: checkLength(CodePoint.RDBALWUPD, 1);
2973: database.rdbAllowUpdates = readBoolean(CodePoint.RDBALWUPD);
2974: if (SanityManager.DEBUG)
2975: trace("rdbAllowUpdates = "
2976: + database.rdbAllowUpdates);
2977: break;
2978: //optional, ignorable
2979: case CodePoint.PRDDTA:
2980: // check that it fits in maximum but otherwise ignore for now
2981: if (reader.getDdmLength() > CodePoint.MAX_NAME)
2982: tooBig(CodePoint.PRDDTA);
2983: reader.skipBytes();
2984: break;
2985: case CodePoint.TRGDFTRT:
2986: byte b = reader.readByte();
2987: if (b == 0xF1)
2988: database.sendTRGDFTRT = true;
2989: break;
2990: //optional - not used in JCC so skip for now
2991: case CodePoint.STTDECDEL:
2992: case CodePoint.STTSTRDEL:
2993: codePointNotSupported(codePoint);
2994: break;
2995: default:
2996: invalidCodePoint(codePoint);
2997: }
2998: codePoint = reader.getCodePoint();
2999: }
3000: checkRequired(CodePoint.ACCRDB);
3001: // check that we can support the double-byte and mixed-byte CCSIDS
3002: // set svrcod to warning if they are not supported
3003: if ((database.ccsidDBC != 0 && !server
3004: .supportsCCSID(database.ccsidDBC))
3005: || (database.ccsidMBC != 0 && !server
3006: .supportsCCSID(database.ccsidMBC)))
3007: svrcod = CodePoint.SVRCOD_WARNING;
3008: return svrcod;
3009: }
3010:
3011: /**
3012: * Parse TYPDEFNAM
3013: *
3014: * @return typdefnam
3015: * @exception DRDAProtocolException
3016: */
3017: private String parseTYPDEFNAM() throws DRDAProtocolException {
3018: String typDefNam = reader.readString();
3019: if (SanityManager.DEBUG)
3020: trace("typeDefName " + typDefNam);
3021: if (typDefNam.length() > CodePoint.MAX_NAME)
3022: tooBig(CodePoint.TYPDEFNAM);
3023: checkValidTypDefNam(typDefNam);
3024: // check if the typedef is one we support
3025: if (!typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLASC)
3026: && !typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLJVM)
3027: && !typDefNam.equals(CodePoint.TYPDEFNAM_QTDSQLX86))
3028: valueNotSupported(CodePoint.TYPDEFNAM);
3029: return typDefNam;
3030: }
3031:
3032: /**
3033: * Set a statement or the database' byte order, depending on the arguments
3034: *
3035: * @param setDatabase if true, set database' byte order, otherwise set statement's
3036: * @param stmt DRDAStatement, used when setDatabase is false
3037: * @param typDefNam TYPDEFNAM value
3038: */
3039: private void setStmtOrDbByteOrder(boolean setDatabase,
3040: DRDAStatement stmt, String typDefNam) {
3041: int byteOrder = (typDefNam
3042: .equals(CodePoint.TYPDEFNAM_QTDSQLX86) ? SignedBinary.LITTLE_ENDIAN
3043: : SignedBinary.BIG_ENDIAN);
3044: if (setDatabase) {
3045: database.typDefNam = typDefNam;
3046: database.byteOrder = byteOrder;
3047: } else {
3048: stmt.typDefNam = typDefNam;
3049: stmt.byteOrder = byteOrder;
3050: }
3051: }
3052:
3053: /**
3054: * Write Access to RDB Completed
3055: * Instance Variables
3056: * SVRCOD - severity code - 0 info, 4 warning -required
3057: * PRDID - product specific identifier -required
3058: * TYPDEFNAM - type definition name -required
3059: * TYPDEFOVR - type definition overrides - required
3060: * RDBINTTKN - token which can be used to interrupt DDM commands - optional
3061: * CRRTKN - correlation token - only returned if we didn't get one from requester
3062: * SRVDGN - server diagnostic information - optional
3063: * PKGDFTCST - package default character subtype - optional
3064: * USRID - User ID at the target system - optional
3065: * SRVLST - Server List
3066: *
3067: * @exception DRDAProtocolException
3068: */
3069: private void writeACCRDBRM(int svrcod) throws DRDAProtocolException {
3070: writer.createDssReply();
3071: writer.startDdm(CodePoint.ACCRDBRM);
3072: writer.writeScalar2Bytes(CodePoint.SVRCOD, svrcod);
3073: writer.writeScalarString(CodePoint.PRDID, server.prdId);
3074: //TYPDEFNAM -required - JCC doesn't support QTDSQLJVM so for now we
3075: // just use ASCII, though we should eventually be able to use QTDSQLJVM
3076: // at level 7
3077: writer.writeScalarString(CodePoint.TYPDEFNAM,
3078: CodePoint.TYPDEFNAM_QTDSQLASC);
3079: writeTYPDEFOVR();
3080: writer.endDdmAndDss();
3081: finalizeChain();
3082: }
3083:
3084: private void writeTYPDEFOVR() throws DRDAProtocolException {
3085: //TYPDEFOVR - required - only single byte and mixed byte are specified
3086: writer.startDdm(CodePoint.TYPDEFOVR);
3087: writer.writeScalar2Bytes(CodePoint.CCSIDSBC, server.CCSIDSBC);
3088: writer.writeScalar2Bytes(CodePoint.CCSIDMBC, server.CCSIDMBC);
3089: // PKGDFTCST - Send character subtype and userid if requested
3090: if (database.sendTRGDFTRT) {
3091: // default to multibyte character
3092: writer.startDdm(CodePoint.PKGDFTCST);
3093: writer.writeShort(CodePoint.CSTMBCS);
3094: writer.endDdm();
3095: // userid
3096: writer.startDdm(CodePoint.USRID);
3097: writer.writeString(database.userId);
3098: writer.endDdm();
3099: }
3100: writer.endDdm();
3101:
3102: }
3103:
3104: /**
3105: * Parse Type Defintion Overrides
3106: * TYPDEF Overrides specifies the Coded Character SET Identifiers (CCSIDs)
3107: * that are in a named TYPDEF.
3108: * Instance Variables
3109: * CCSIDSBC - CCSID for Single-Byte - optional
3110: * CCSIDDBC - CCSID for Double-Byte - optional
3111: * CCSIDMBC - CCSID for Mixed-byte characters -optional
3112: *
3113: * @param st Statement this TYPDEFOVR applies to
3114: *
3115: * @exception DRDAProtocolException
3116: */
3117: private void parseTYPDEFOVR(DRDAStatement st)
3118: throws DRDAProtocolException {
3119: int codePoint;
3120: int ccsidSBC = 0;
3121: int ccsidDBC = 0;
3122: int ccsidMBC = 0;
3123: String ccsidSBCEncoding = null;
3124: String ccsidDBCEncoding = null;
3125: String ccsidMBCEncoding = null;
3126:
3127: reader.markCollection();
3128:
3129: codePoint = reader.getCodePoint();
3130: // at least one of the following instance variable is required
3131: // if the TYPDEFOVR is specified in a command object
3132: if (codePoint == -1 && st != null)
3133: missingCodePoint(CodePoint.CCSIDSBC);
3134:
3135: while (codePoint != -1) {
3136: switch (codePoint) {
3137: case CodePoint.CCSIDSBC:
3138: checkLength(CodePoint.CCSIDSBC, 2);
3139: ccsidSBC = reader.readNetworkShort();
3140: try {
3141: ccsidSBCEncoding = CharacterEncodings
3142: .getJavaEncoding(ccsidSBC);
3143: } catch (Exception e) {
3144: valueNotSupported(CodePoint.CCSIDSBC);
3145: }
3146: if (SanityManager.DEBUG)
3147: trace("ccsidsbc = " + ccsidSBC + " encoding = "
3148: + ccsidSBCEncoding);
3149: break;
3150: case CodePoint.CCSIDDBC:
3151: checkLength(CodePoint.CCSIDDBC, 2);
3152: ccsidDBC = reader.readNetworkShort();
3153: try {
3154: ccsidDBCEncoding = CharacterEncodings
3155: .getJavaEncoding(ccsidDBC);
3156: } catch (Exception e) {
3157: // we write a warning later for this so no error
3158: // unless for a statement
3159: ccsidDBCEncoding = null;
3160: if (st != null)
3161: valueNotSupported(CodePoint.CCSIDSBC);
3162: }
3163: if (SanityManager.DEBUG)
3164: trace("ccsiddbc = " + ccsidDBC + " encoding = "
3165: + ccsidDBCEncoding);
3166: break;
3167: case CodePoint.CCSIDMBC:
3168: checkLength(CodePoint.CCSIDMBC, 2);
3169: ccsidMBC = reader.readNetworkShort();
3170: try {
3171: ccsidMBCEncoding = CharacterEncodings
3172: .getJavaEncoding(ccsidMBC);
3173: } catch (Exception e) {
3174: // we write a warning later for this so no error
3175: ccsidMBCEncoding = null;
3176: if (st != null)
3177: valueNotSupported(CodePoint.CCSIDMBC);
3178: }
3179: if (SanityManager.DEBUG)
3180: trace("ccsidmbc = " + ccsidMBC + " encoding = "
3181: + ccsidMBCEncoding);
3182: break;
3183: default:
3184: invalidCodePoint(codePoint);
3185:
3186: }
3187: codePoint = reader.getCodePoint();
3188: }
3189: if (st == null) {
3190: if (ccsidSBC != 0) {
3191: database.ccsidSBC = ccsidSBC;
3192: database.ccsidSBCEncoding = ccsidSBCEncoding;
3193: }
3194: if (ccsidDBC != 0) {
3195: database.ccsidDBC = ccsidDBC;
3196: database.ccsidDBCEncoding = ccsidDBCEncoding;
3197: }
3198: if (ccsidMBC != 0) {
3199: database.ccsidMBC = ccsidMBC;
3200: database.ccsidMBCEncoding = ccsidMBCEncoding;
3201: }
3202: } else {
3203: if (ccsidSBC != 0) {
3204: st.ccsidSBC = ccsidSBC;
3205: st.ccsidSBCEncoding = ccsidSBCEncoding;
3206: }
3207: if (ccsidDBC != 0) {
3208: st.ccsidDBC = ccsidDBC;
3209: st.ccsidDBCEncoding = ccsidDBCEncoding;
3210: }
3211: if (ccsidMBC != 0) {
3212: st.ccsidMBC = ccsidMBC;
3213: st.ccsidMBCEncoding = ccsidMBCEncoding;
3214: }
3215: }
3216: }
3217:
3218: /**
3219: * Parse PRPSQLSTT - Prepare SQL Statement
3220: * Instance Variables
3221: * RDBNAM - Relational Database Name - optional
3222: * PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number - required
3223: * RTNSQLDA - Return SQL Descriptor Area - optional
3224: * MONITOR - Monitor events - optional.
3225: *
3226: * @return return 0 - don't return sqlda, 1 - return input sqlda,
3227: * 2 - return output sqlda
3228: * @throws DRDAProtocolException
3229: * @throws SQLException
3230: */
3231: private int parsePRPSQLSTT() throws DRDAProtocolException,
3232: SQLException {
3233: int codePoint;
3234: boolean rtnsqlda = false;
3235: boolean rtnOutput = true; // Return output SQLDA is default
3236: String typdefnam;
3237: Pkgnamcsn pkgnamcsn = null;
3238:
3239: DRDAStatement stmt = null;
3240: Database databaseToSet = null;
3241:
3242: reader.markCollection();
3243:
3244: codePoint = reader.getCodePoint();
3245: while (codePoint != -1) {
3246: switch (codePoint) {
3247: // optional
3248: case CodePoint.RDBNAM:
3249: setDatabase(CodePoint.PRPSQLSTT);
3250: databaseToSet = database;
3251: break;
3252: // required
3253: case CodePoint.PKGNAMCSN:
3254: pkgnamcsn = parsePKGNAMCSN();
3255: break;
3256: //optional
3257: case CodePoint.RTNSQLDA:
3258: // Return SQLDA with description of statement
3259: rtnsqlda = readBoolean(CodePoint.RTNSQLDA);
3260: break;
3261: //optional
3262: case CodePoint.TYPSQLDA:
3263: rtnOutput = parseTYPSQLDA();
3264: break;
3265: //optional
3266: case CodePoint.MONITOR:
3267: parseMONITOR();
3268: break;
3269: default:
3270: invalidCodePoint(codePoint);
3271:
3272: }
3273: codePoint = reader.getCodePoint();
3274: }
3275:
3276: stmt = database.newDRDAStatement(pkgnamcsn);
3277: String sqlStmt = parsePRPSQLSTTobjects(stmt);
3278: if (databaseToSet != null)
3279: stmt.setDatabase(database);
3280: stmt.explicitPrepare(sqlStmt);
3281: // set the statement as the current statement
3282: database.setCurrentStatement(stmt);
3283:
3284: if (!rtnsqlda)
3285: return 0;
3286: else if (rtnOutput)
3287: return 2;
3288: else
3289: return 1;
3290: }
3291:
3292: /**
3293: * Parse PRPSQLSTT objects
3294: * Objects
3295: * TYPDEFNAM - Data type definition name - optional
3296: * TYPDEFOVR - Type defintion overrides - optional
3297: * SQLSTT - SQL Statement required
3298: * SQLATTR - Cursor attributes on prepare - optional - level 7
3299: *
3300: * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
3301: * sent with the statement. Once the statement is over, the default values
3302: * sent in the ACCRDB are once again in effect. If no values are supplied,
3303: * the values sent in the ACCRDB are used.
3304: * Objects may follow in one DSS or in several DSS chained together.
3305: *
3306: * @return SQL statement
3307: * @throws DRDAProtocolException
3308: * @throws SQLException
3309: */
3310: private String parsePRPSQLSTTobjects(DRDAStatement stmt)
3311: throws DRDAProtocolException, SQLException {
3312: String sqlStmt = null;
3313: int codePoint;
3314: do {
3315: correlationID = reader.readDssHeader();
3316: while (reader.moreDssData()) {
3317: codePoint = reader.readLengthAndCodePoint();
3318: switch (codePoint) {
3319: // required
3320: case CodePoint.SQLSTT:
3321: sqlStmt = parseEncodedString();
3322: if (SanityManager.DEBUG)
3323: trace("sqlStmt = " + sqlStmt);
3324: break;
3325: // optional
3326: case CodePoint.TYPDEFNAM:
3327: setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
3328: break;
3329: // optional
3330: case CodePoint.TYPDEFOVR:
3331: parseTYPDEFOVR(stmt);
3332: break;
3333: // optional
3334: case CodePoint.SQLATTR:
3335: parseSQLATTR(stmt);
3336: break;
3337: default:
3338: invalidCodePoint(codePoint);
3339: }
3340: }
3341: } while (reader.isChainedWithSameID());
3342: if (sqlStmt == null)
3343: missingCodePoint(CodePoint.SQLSTT);
3344:
3345: return sqlStmt;
3346: }
3347:
3348: /**
3349: * Parse TYPSQLDA - Type of the SQL Descriptor Area
3350: *
3351: * @return true if for output; false otherwise
3352: * @exception DRDAProtocolException
3353: */
3354: private boolean parseTYPSQLDA() throws DRDAProtocolException {
3355: checkLength(CodePoint.TYPSQLDA, 1);
3356: byte sqldaType = reader.readByte();
3357: if (SanityManager.DEBUG)
3358: trace("typSQLDa " + sqldaType);
3359: if (sqldaType == CodePoint.TYPSQLDA_STD_OUTPUT
3360: || sqldaType == CodePoint.TYPSQLDA_LIGHT_OUTPUT
3361: || sqldaType == CodePoint.TYPSQLDA_X_OUTPUT)
3362: return true;
3363: else if (sqldaType == CodePoint.TYPSQLDA_STD_INPUT
3364: || sqldaType == CodePoint.TYPSQLDA_LIGHT_INPUT
3365: || sqldaType == CodePoint.TYPSQLDA_X_INPUT)
3366: return false;
3367: else
3368: invalidValue(CodePoint.TYPSQLDA);
3369:
3370: // shouldn't get here but have to shut up compiler
3371: return false;
3372: }
3373:
3374: /**
3375: * Parse SQLATTR - Cursor attributes on prepare
3376: * This is an encoded string. Can have combination of following, eg INSENSITIVE SCROLL WITH HOLD
3377: * Possible strings are
3378: * SENSITIVE DYNAMIC SCROLL [FOR UPDATE]
3379: * SENSITIVE STATIC SCROLL [FOR UPDATE]
3380: * INSENSITIVE SCROLL
3381: * FOR UPDATE
3382: * WITH HOLD
3383: *
3384: * @param stmt DRDAStatement
3385: * @exception DRDAProtocolException
3386: */
3387: protected void parseSQLATTR(DRDAStatement stmt)
3388: throws DRDAProtocolException {
3389: String attrs = parseEncodedString();
3390: if (SanityManager.DEBUG)
3391: trace("sqlattr = '" + attrs + "'");
3392: //let Cloudscape handle any errors in the types it doesn't support
3393: //just set the attributes
3394:
3395: boolean validAttribute = false;
3396: if (attrs.indexOf("INSENSITIVE SCROLL") != -1
3397: || attrs.indexOf("SCROLL INSENSITIVE") != -1) //CLI
3398: {
3399: stmt.scrollType = ResultSet.TYPE_SCROLL_INSENSITIVE;
3400: stmt.concurType = ResultSet.CONCUR_READ_ONLY;
3401: validAttribute = true;
3402: }
3403: if ((attrs.indexOf("SENSITIVE DYNAMIC SCROLL") != -1)
3404: || (attrs.indexOf("SENSITIVE STATIC SCROLL") != -1)) {
3405: stmt.scrollType = ResultSet.TYPE_SCROLL_SENSITIVE;
3406: validAttribute = true;
3407: }
3408:
3409: if ((attrs.indexOf("FOR UPDATE") != -1)) {
3410: validAttribute = true;
3411: stmt.concurType = ResultSet.CONCUR_UPDATABLE;
3412: }
3413:
3414: if (attrs.indexOf("WITH HOLD") != -1) {
3415: stmt.withHoldCursor = JDBC30Translation.HOLD_CURSORS_OVER_COMMIT;
3416: validAttribute = true;
3417: }
3418:
3419: if (!validAttribute) {
3420: invalidValue(CodePoint.SQLATTR);
3421: }
3422: }
3423:
3424: /**
3425: * Parse DSCSQLSTT - Describe SQL Statement previously prepared
3426: * Instance Variables
3427: * TYPSQLDA - sqlda type expected (output or input)
3428: * RDBNAM - relational database name - optional
3429: * PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
3430: * MONITOR - Monitor events - optional.
3431: *
3432: * @return expect "output sqlda" or not
3433: * @throws DRDAProtocolException
3434: * @throws SQLException
3435: */
3436: private boolean parseDSCSQLSTT() throws DRDAProtocolException,
3437: SQLException {
3438: int codePoint;
3439: boolean rtnOutput = true; // default
3440: Pkgnamcsn pkgnamcsn = null;
3441: reader.markCollection();
3442:
3443: codePoint = reader.getCodePoint();
3444: while (codePoint != -1) {
3445: switch (codePoint) {
3446: // optional
3447: case CodePoint.TYPSQLDA:
3448: rtnOutput = parseTYPSQLDA();
3449: break;
3450: // optional
3451: case CodePoint.RDBNAM:
3452: setDatabase(CodePoint.DSCSQLSTT);
3453: break;
3454: // required
3455: case CodePoint.PKGNAMCSN:
3456: pkgnamcsn = parsePKGNAMCSN();
3457: DRDAStatement stmt = database
3458: .getDRDAStatement(pkgnamcsn);
3459: if (stmt == null) {
3460: invalidValue(CodePoint.PKGNAMCSN);
3461: }
3462: break;
3463: //optional
3464: case CodePoint.MONITOR:
3465: parseMONITOR();
3466: break;
3467: default:
3468: invalidCodePoint(codePoint);
3469: }
3470: codePoint = reader.getCodePoint();
3471: }
3472: if (pkgnamcsn == null)
3473: missingCodePoint(CodePoint.PKGNAMCSN);
3474: return rtnOutput;
3475: }
3476:
3477: /**
3478: * Parse EXCSQLSTT - Execute non-cursor SQL Statement previously prepared
3479: * Instance Variables
3480: * RDBNAM - relational database name - optional
3481: * PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
3482: * OUTEXP - Output expected
3483: * NBRROW - Number of rows to be inserted if it's an insert
3484: * PRCNAM - procedure name if specified by host variable, not needed for Cloudscape
3485: * QRYBLKSZ - query block size
3486: * MAXRSLCNT - max resultset count
3487: * MAXBLKEXT - Max number of extra blocks
3488: * RSLSETFLG - resultset flag
3489: * RDBCMTOK - RDB Commit Allowed - optional
3490: * OUTOVROPT - output override option
3491: * QRYROWSET - Query Rowset Size - Level 7
3492: * MONITOR - Monitor events - optional.
3493: *
3494: * @throws DRDAProtocolException
3495: * @throws SQLException
3496: */
3497: private void parseEXCSQLSTT() throws DRDAProtocolException,
3498: SQLException {
3499: int codePoint;
3500: String strVal;
3501: reader.markCollection();
3502:
3503: codePoint = reader.getCodePoint();
3504: boolean outputExpected = false;
3505: Pkgnamcsn pkgnamcsn = null;
3506: int numRows = 1; // default value
3507: int blkSize = 0;
3508: int maxrslcnt = 0; // default value
3509: int maxblkext = CodePoint.MAXBLKEXT_DEFAULT;
3510: int qryrowset = CodePoint.QRYROWSET_DEFAULT;
3511: int outovropt = CodePoint.OUTOVRFRS;
3512: byte[] rslsetflg = null;
3513: String procName = null;
3514:
3515: while (codePoint != -1) {
3516: switch (codePoint) {
3517: // optional
3518: case CodePoint.RDBNAM:
3519: setDatabase(CodePoint.EXCSQLSTT);
3520: break;
3521: // required
3522: case CodePoint.PKGNAMCSN:
3523: pkgnamcsn = parsePKGNAMCSN();
3524: break;
3525: // optional
3526: case CodePoint.OUTEXP:
3527: outputExpected = readBoolean(CodePoint.OUTEXP);
3528: if (SanityManager.DEBUG)
3529: trace("outexp = " + outputExpected);
3530: break;
3531: // optional
3532: case CodePoint.NBRROW:
3533: checkLength(CodePoint.NBRROW, 4);
3534: numRows = reader.readNetworkInt();
3535: if (SanityManager.DEBUG)
3536: trace("# of rows: " + numRows);
3537: break;
3538: // optional
3539: case CodePoint.PRCNAM:
3540: procName = reader.readString();
3541: if (SanityManager.DEBUG)
3542: trace("Procedure Name = " + procName);
3543: break;
3544: // optional
3545: case CodePoint.QRYBLKSZ:
3546: blkSize = parseQRYBLKSZ();
3547: break;
3548: // optional
3549: case CodePoint.MAXRSLCNT:
3550: // this is the maximum result set count
3551: // values are 0 - requester is not capabable of receiving result
3552: // sets as reply data in the response to EXCSQLSTT
3553: // -1 - requester is able to receive all result sets
3554: checkLength(CodePoint.MAXRSLCNT, 2);
3555: maxrslcnt = reader.readNetworkShort();
3556: if (SanityManager.DEBUG)
3557: trace("max rs count: " + maxrslcnt);
3558: break;
3559: // optional
3560: case CodePoint.MAXBLKEXT:
3561: // number of extra qury blocks of answer set data per result set
3562: // 0 - no extra query blocks
3563: // -1 - can receive entire result set
3564: checkLength(CodePoint.MAXBLKEXT, 2);
3565: maxblkext = reader.readNetworkShort();
3566: if (SanityManager.DEBUG)
3567: trace("max extra blocks: " + maxblkext);
3568: break;
3569: // optional
3570: case CodePoint.RSLSETFLG:
3571: //Result set flags
3572: rslsetflg = reader.readBytes();
3573: for (int i = 0; i < rslsetflg.length; i++)
3574: if (SanityManager.DEBUG)
3575: trace("rslsetflg: " + rslsetflg[i]);
3576: break;
3577: // optional
3578: case CodePoint.RDBCMTOK:
3579: parseRDBCMTOK();
3580: break;
3581: // optional
3582: case CodePoint.OUTOVROPT:
3583: outovropt = parseOUTOVROPT();
3584: break;
3585: // optional
3586: case CodePoint.QRYROWSET:
3587: //Note minimum for OPNQRY is 0, we'll assume it is the same
3588: //for EXCSQLSTT though the standard doesn't say
3589: qryrowset = parseQRYROWSET(0);
3590: break;
3591: //optional
3592: case CodePoint.MONITOR:
3593: parseMONITOR();
3594: break;
3595: default:
3596: invalidCodePoint(codePoint);
3597: }
3598: codePoint = reader.getCodePoint();
3599: }
3600:
3601: if (pkgnamcsn == null)
3602: missingCodePoint(CodePoint.PKGNAMCSN);
3603:
3604: DRDAStatement stmt;
3605: boolean needPrepareCall = false;
3606:
3607: stmt = database.getDRDAStatement(pkgnamcsn);
3608: boolean isProcedure = (procName != null || (stmt != null
3609: && stmt.wasExplicitlyPrepared() && stmt.isCall));
3610:
3611: if (isProcedure) // stored procedure call
3612: {
3613: if (stmt == null || !(stmt.wasExplicitlyPrepared())) {
3614: stmt = database.newDRDAStatement(pkgnamcsn);
3615: stmt.setQryprctyp(CodePoint.QRYBLKCTL_DEFAULT);
3616: needPrepareCall = true;
3617: }
3618:
3619: stmt.procName = procName;
3620: stmt.outputExpected = outputExpected;
3621: } else {
3622: // we can't find the statement
3623: if (stmt == null) {
3624: invalidValue(CodePoint.PKGNAMCSN);
3625: }
3626: stmt.setQryprctyp(CodePoint.QRYBLKCTL_DEFAULT);
3627: }
3628:
3629: stmt.nbrrow = numRows;
3630: stmt.qryrowset = qryrowset;
3631: stmt.blksize = blkSize;
3632: stmt.maxblkext = maxblkext;
3633: stmt.maxrslcnt = maxrslcnt;
3634: stmt.outovropt = outovropt;
3635: stmt.rslsetflg = rslsetflg;
3636: if (pendingStatementTimeout >= 0) {
3637: stmt.getPreparedStatement().setQueryTimeout(
3638: pendingStatementTimeout);
3639: pendingStatementTimeout = -1;
3640: }
3641:
3642: // set the statement as the current statement
3643: database.setCurrentStatement(stmt);
3644:
3645: boolean hasResultSet;
3646: if (reader.isChainedWithSameID()) {
3647: hasResultSet = parseEXCSQLSTTobjects(stmt);
3648: } else {
3649: if (isProcedure && (needPrepareCall)) {
3650: // if we had parameters the callable statement would
3651: // be prepared with parseEXCQLSTTobjects, otherwise we
3652: // have to do it here
3653: String prepareString = "call " + stmt.procName + "()";
3654: if (SanityManager.DEBUG)
3655: trace("$$$prepareCall is: " + prepareString);
3656: database.getConnection().clearWarnings();
3657: CallableStatement cs = (CallableStatement) stmt
3658: .prepare(prepareString);
3659: }
3660: stmt.ps.clearWarnings();
3661: hasResultSet = stmt.execute();
3662: }
3663:
3664: ResultSet rs = null;
3665: if (hasResultSet) {
3666: rs = stmt.getResultSet();
3667: }
3668: // temp until ps.execute() return value fixed
3669: hasResultSet = (rs != null);
3670: int numResults = 0;
3671: if (hasResultSet) {
3672: numResults = stmt.getNumResultSets();
3673: writeRSLSETRM(stmt);
3674: }
3675:
3676: // First of all, we send if there really are output params. Otherwise
3677: // CLI (.Net driver) fails. DRDA spec (page 151,152) says send SQLDTARD
3678: // if server has output param data to send.
3679: boolean sendSQLDTARD = stmt.hasOutputParams() && outputExpected;
3680: if (isProcedure) {
3681: if (sendSQLDTARD) {
3682: writer.createDssObject();
3683: writer.startDdm(CodePoint.SQLDTARD);
3684: writer.startDdm(CodePoint.FDODSC);
3685: writeQRYDSC(stmt, true);
3686: writer.endDdm();
3687: writer.startDdm(CodePoint.FDODTA);
3688: writeFDODTA(stmt);
3689: writer.endDdm();
3690: writer.endDdmAndDss();
3691: } else if (hasResultSet)
3692: // DRDA spec says that we MUST return either an
3693: // SQLDTARD or an SQLCARD--the former when we have
3694: // output parameters, the latter when we don't.
3695: // If we have a result set, then we have to write
3696: // the SQLCARD _now_, since it is expected before
3697: // we send the result set info below; if we don't
3698: // have a result set and we don't send SQLDTARD,
3699: // then we can wait until we reach the call to
3700: // checkWarning() below, which will write an
3701: // SQLCARD for us.
3702: writeNullSQLCARDobject();
3703: }
3704:
3705: //We need to marke that params are finished so that we know we
3706: // are ready to send resultset info.
3707: stmt.finishParams();
3708:
3709: PreparedStatement ps = stmt.getPreparedStatement();
3710: int rsNum = 0;
3711: do {
3712: if (hasResultSet) {
3713: stmt.setCurrentDrdaResultSet(rsNum);
3714: //indicate that we are going to return data
3715: stmt.setQryrtndta(true);
3716: if (!isProcedure)
3717: checkWarning(null, ps, null, -1, true, true);
3718: if (rsNum == 0)
3719: writeSQLRSLRD(stmt);
3720: writeOPNQRYRM(true, stmt);
3721: writeSQLCINRD(stmt);
3722: writeQRYDSC(stmt, false);
3723: stmt.rsSuspend();
3724:
3725: /* Currently, if LMTBLKPRC is used, a pre-condition is that no lob columns.
3726: * But in the future, when we do support LOB in LMTBLKPRC, the drda spec still
3727: * does not allow LOB to be sent with OPNQRYRM. So this "if" here will have
3728: * to add "no lob columns".
3729: */
3730: if (stmt.getQryprctyp() == CodePoint.LMTBLKPRC)
3731: writeQRYDTA(stmt);
3732: } else if (!sendSQLDTARD) {
3733: int updateCount = ps.getUpdateCount();
3734: if (false && (database.RDBUPDRM_sent == false)
3735: && !isProcedure) {
3736: writeRDBUPDRM();
3737: }
3738:
3739: checkWarning(database.getConnection(), stmt.ps, null,
3740: updateCount, true, true);
3741: }
3742:
3743: } while (hasResultSet && (++rsNum < numResults));
3744:
3745: return; // we are done
3746: }
3747:
3748: /**
3749: * Parse RDBCMTOK - tells the database whether to allow commits or rollbacks
3750: * to be executed as part of the command
3751: * Since we don't have a SQL commit or rollback command, we will just ignore
3752: * this for now
3753: *
3754: * @exception DRDAProtocolException
3755: */
3756: private void parseRDBCMTOK() throws DRDAProtocolException {
3757: boolean rdbcmtok = readBoolean(CodePoint.RDBCMTOK);
3758: if (SanityManager.DEBUG)
3759: trace("rdbcmtok = " + rdbcmtok);
3760: }
3761:
3762: /**
3763: * Parse EXCSQLSTT command objects
3764: * Command Objects
3765: * TYPDEFNAM - Data Type Definition Name - optional
3766: * TYPDEFOVR - TYPDEF Overrides -optional
3767: * SQLDTA - optional, variable data, specified if prpared statement has input parameters
3768: * EXTDTA - optional, externalized FD:OCA data
3769: * OUTOVR - output override descriptor, not allowed for stored procedure calls
3770: *
3771: * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
3772: * sent with the statement. Once the statement is over, the default values
3773: * sent in the ACCRDB are once again in effect. If no values are supplied,
3774: * the values sent in the ACCRDB are used.
3775: * Objects may follow in one DSS or in several DSS chained together.
3776: *
3777: * @param stmt the DRDAStatement to execute
3778: * @throws DRDAProtocolException
3779: * @throws SQLException
3780: */
3781: private boolean parseEXCSQLSTTobjects(DRDAStatement stmt)
3782: throws DRDAProtocolException, SQLException {
3783: int codePoint;
3784: boolean gotSQLDTA = false, gotEXTDTA = false;
3785: boolean result = false;
3786: do {
3787: correlationID = reader.readDssHeader();
3788: while (reader.moreDssData()) {
3789: codePoint = reader.readLengthAndCodePoint();
3790: switch (codePoint) {
3791: // optional
3792: case CodePoint.TYPDEFNAM:
3793: setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
3794: stmt.setTypDefValues();
3795: break;
3796: // optional
3797: case CodePoint.TYPDEFOVR:
3798: parseTYPDEFOVR(stmt);
3799: stmt.setTypDefValues();
3800: break;
3801: // required
3802: case CodePoint.SQLDTA:
3803: parseSQLDTA(stmt);
3804: gotSQLDTA = true;
3805: break;
3806: // optional
3807: case CodePoint.EXTDTA:
3808: readAndSetAllExtParams(stmt, true);
3809: stmt.ps.clearWarnings();
3810: result = stmt.execute();
3811: gotEXTDTA = true;
3812: break;
3813: // optional
3814: case CodePoint.OUTOVR:
3815: parseOUTOVR(stmt);
3816: break;
3817: default:
3818: invalidCodePoint(codePoint);
3819: }
3820: }
3821: } while (reader.isChainedWithSameID());
3822:
3823: // SQLDTA is required
3824: if (!gotSQLDTA)
3825: missingCodePoint(CodePoint.SQLDTA);
3826:
3827: if (!gotEXTDTA) {
3828: stmt.ps.clearWarnings();
3829: result = stmt.execute();
3830: }
3831:
3832: return result;
3833: }
3834:
3835: /**
3836: * Write SQLCINRD - result set column information
3837: *
3838: * @throws DRDAProtocolException
3839: * @throws SQLException
3840: */
3841: private void writeSQLCINRD(DRDAStatement stmt)
3842: throws DRDAProtocolException, SQLException {
3843: ResultSet rs = null;
3844: // todo ps is never used or closed - could this be a memory leak?
3845: PreparedStatement ps = stmt.getPreparedStatement();
3846:
3847: if (!stmt.needsToSendParamData)
3848: rs = stmt.getResultSet();
3849:
3850: writer.createDssObject();
3851: writer.startDdm(CodePoint.SQLCINRD);
3852: if (sqlamLevel >= MGRLVL_7)
3853: writeSQLDHROW(stmt);
3854:
3855: ResultSetMetaData rsmeta = rs.getMetaData();
3856: int ncols = rsmeta.getColumnCount();
3857: writer.writeShort(ncols); // num of columns
3858: if (sqlamLevel >= MGRLVL_7) {
3859: for (int i = 0; i < ncols; i++)
3860: writeSQLDAGRP(rsmeta, null, i, true);
3861: } else {
3862: for (int i = 0; i < ncols; i++) {
3863: writeVCMorVCS(rsmeta.getColumnName(i + 1));
3864: writeVCMorVCS(rsmeta.getColumnLabel(i + 1));
3865: writeVCMorVCS(null);
3866: }
3867: }
3868: writer.endDdmAndDss();
3869: }
3870:
3871: /**
3872: * Write SQLRSLRD - result set reply data
3873: *
3874: * @throws DRDAProtocolException
3875: * @throws SQLException
3876: */
3877: private void writeSQLRSLRD(DRDAStatement stmt)
3878: throws DRDAProtocolException, SQLException {
3879: int numResults = stmt.getNumResultSets();
3880:
3881: writer.createDssObject();
3882: writer.startDdm(CodePoint.SQLRSLRD);
3883: writer.writeShort(numResults); // num of result sets
3884:
3885: for (int i = 0; i < numResults; i++) {
3886: writer.writeInt(i); // rsLocator
3887: writeVCMorVCS(stmt.getResultSetCursorName(i));
3888: writer.writeInt(1); // num of rows XXX resolve, it doesn't matter for now
3889:
3890: }
3891: writer.endDdmAndDss();
3892: }
3893:
3894: /**
3895: * Write RSLSETRM
3896: * Instance variables
3897: * SVRCOD - Severity code - Information only - required
3898: * PKGSNLST - list of PKGNAMCSN -required
3899: * SRVDGN - Server Diagnostic Information -optional
3900: *
3901: * @throws DRDAProtocolException
3902: * @throws SQLException
3903: */
3904: private void writeRSLSETRM(DRDAStatement stmt)
3905: throws DRDAProtocolException, SQLException {
3906: int numResults = stmt.getNumResultSets();
3907: writer.createDssReply();
3908: writer.startDdm(CodePoint.RSLSETRM);
3909: writer.writeScalar2Bytes(CodePoint.SVRCOD, 0);
3910: writer.startDdm(CodePoint.PKGSNLST);
3911:
3912: for (int i = 0; i < numResults; i++)
3913: writePKGNAMCSN(stmt.getResultSetPkgcnstkn(i).getBytes());
3914: writer.endDdm();
3915: writer.endDdmAndDss();
3916: }
3917:
3918: /**
3919: * Parse SQLDTA - SQL program variable data
3920: * and handle exception.
3921: * @see #parseSQLDTA_work
3922: */
3923:
3924: private void parseSQLDTA(DRDAStatement stmt)
3925: throws DRDAProtocolException, SQLException {
3926: try {
3927: parseSQLDTA_work(stmt);
3928: } catch (SQLException se) {
3929: skipRemainder(true);
3930: throw se;
3931: }
3932: }
3933:
3934: /**
3935: * Parse SQLDTA - SQL program variable data
3936: * Instance Variables
3937: * FDODSC - FD:OCA data descriptor - required
3938: * FDODTA - FD:OCA data - optional
3939: *
3940: * @throws DRDAProtocolException
3941: * @throws SQLException
3942: */
3943: private void parseSQLDTA_work(DRDAStatement stmt)
3944: throws DRDAProtocolException, SQLException {
3945: String strVal;
3946: PreparedStatement ps = stmt.getPreparedStatement();
3947: int codePoint;
3948: EngineParameterMetaData pmeta = null;
3949: Vector paramDrdaTypes = new Vector();
3950: Vector paramLens = new Vector();
3951: ArrayList paramExtPositions = null;
3952: int numVars = 0;
3953: boolean rtnParam = false;
3954:
3955: reader.markCollection();
3956: codePoint = reader.getCodePoint();
3957: while (codePoint != -1) {
3958: switch (codePoint) {
3959: // required
3960: case CodePoint.FDODSC:
3961: while (reader.getDdmLength() > 6) //we get parameter info til last 6 byte
3962: {
3963: int dtaGrpLen = reader.readUnsignedByte();
3964: int numVarsInGrp = (dtaGrpLen - 3) / 3;
3965: if (SanityManager.DEBUG)
3966: trace("num of vars in this group is: "
3967: + numVarsInGrp);
3968: reader.readByte(); // tripletType
3969: reader.readByte(); // id
3970: for (int j = 0; j < numVarsInGrp; j++) {
3971: paramDrdaTypes.addElement(new Byte(reader
3972: .readByte()));
3973: if (SanityManager.DEBUG)
3974: trace("drdaType is: "
3975: + "0x"
3976: + Integer
3977: .toHexString(((Byte) paramDrdaTypes
3978: .lastElement())
3979: .byteValue()));
3980: int drdaLength = reader.readNetworkShort();
3981: if (SanityManager.DEBUG)
3982: trace("drdaLength is: " + drdaLength);
3983: paramLens.addElement(new Integer(drdaLength));
3984: }
3985: }
3986: numVars = paramDrdaTypes.size();
3987: if (SanityManager.DEBUG)
3988: trace("numVars = " + numVars);
3989: if (ps == null) // it is a CallableStatement under construction
3990: {
3991: String marks = "(?"; // construct parameter marks
3992: for (int i = 1; i < numVars; i++)
3993: marks += ", ?";
3994: String prepareString = "call " + stmt.procName
3995: + marks + ")";
3996: if (SanityManager.DEBUG)
3997: trace("$$ prepareCall is: " + prepareString);
3998: CallableStatement cs = null;
3999: try {
4000: cs = (CallableStatement) stmt
4001: .prepare(prepareString);
4002: stmt.registerAllOutParams();
4003: } catch (SQLException se) {
4004: if (!stmt.outputExpected
4005: || (!se.getSQLState().equals(
4006: SQLState.LANG_NO_METHOD_FOUND)))
4007: throw se;
4008: if (SanityManager.DEBUG)
4009: trace("****** second try with return parameter...");
4010: // Save first SQLException most likely suspect
4011: if (numVars == 1)
4012: prepareString = "? = call " + stmt.procName
4013: + "()";
4014: else
4015: prepareString = "? = call " + stmt.procName
4016: + "(" + marks.substring(3) + ")";
4017: if (SanityManager.DEBUG)
4018: trace("$$ prepareCall is: " + prepareString);
4019: try {
4020: cs = (CallableStatement) stmt
4021: .prepare(prepareString);
4022: } catch (SQLException se2) {
4023: // The first exception is the most likely suspect
4024: throw se;
4025: }
4026: rtnParam = true;
4027: }
4028: ps = cs;
4029: stmt.ps = ps;
4030: }
4031:
4032: pmeta = stmt.getParameterMetaData();
4033:
4034: reader.readBytes(6); // descriptor footer
4035: break;
4036: // optional
4037: case CodePoint.FDODTA:
4038: reader.readByte(); // row indicator
4039: for (int i = 0; i < numVars; i++) {
4040:
4041: if ((((Byte) paramDrdaTypes.elementAt(i))
4042: .byteValue() & 0x1) == 0x1) // nullable
4043: {
4044: int nullData = reader.readUnsignedByte();
4045: if ((nullData & 0xFF) == FdocaConstants.NULL_DATA) {
4046: if (SanityManager.DEBUG)
4047: trace("******param null");
4048: if (pmeta.getParameterMode(i + 1) != JDBC30Translation.PARAMETER_MODE_OUT)
4049: ps.setNull(i + 1, pmeta
4050: .getParameterType(i + 1));
4051: if (stmt.isOutputParam(i + 1))
4052: stmt.registerOutParam(i + 1);
4053: continue;
4054: }
4055: }
4056:
4057: // not null, read and set it
4058: paramExtPositions = readAndSetParams(i, stmt,
4059: ((Byte) paramDrdaTypes.elementAt(i))
4060: .byteValue(), pmeta,
4061: paramExtPositions, ((Integer) (paramLens
4062: .elementAt(i))).intValue());
4063: }
4064: stmt.cliParamExtPositions = paramExtPositions;
4065: stmt.cliParamDrdaTypes = paramDrdaTypes;
4066: stmt.cliParamLens = paramLens;
4067: break;
4068: case CodePoint.EXTDTA:
4069: readAndSetAllExtParams(stmt, false);
4070: break;
4071: default:
4072: invalidCodePoint(codePoint);
4073:
4074: }
4075: codePoint = reader.getCodePoint();
4076: }
4077:
4078: }
4079:
4080: private int getByteOrder() {
4081: DRDAStatement stmt = database.getCurrentStatement();
4082: return ((stmt != null && stmt.typDefNam != null) ? stmt.byteOrder
4083: : database.byteOrder);
4084: }
4085:
4086: /**
4087: * Read different types of input parameters and set them in PreparedStatement
4088: * @param i index of the parameter
4089: * @param stmt drda statement
4090: * @param drdaType drda type of the parameter
4091: * @param pmeta parameter meta data
4092: * @param paramExtPositions ArrayList of parameters with extdta
4093: * @param paramLenNumBytes Number of bytes for encoding LOB Length
4094: *
4095: * @return updated paramExtPositions
4096: * @throws DRDAProtocolException
4097: * @throws SQLException
4098: */
4099: private ArrayList readAndSetParams(int i, DRDAStatement stmt,
4100: int drdaType, EngineParameterMetaData pmeta,
4101: ArrayList paramExtPositions, int paramLenNumBytes)
4102: throws DRDAProtocolException, SQLException {
4103: PreparedStatement ps = stmt.getPreparedStatement();
4104: // mask out null indicator
4105: drdaType = ((drdaType | 0x01) & 0x000000ff);
4106:
4107: if (ps instanceof CallableStatement) {
4108: if (stmt.isOutputParam(i + 1)) {
4109: CallableStatement cs = (CallableStatement) ps;
4110: cs.registerOutParameter(i + 1, stmt
4111: .getOutputParamType(i + 1));
4112: }
4113: }
4114:
4115: switch (drdaType) {
4116: case DRDAConstants.DRDA_TYPE_NSMALL: {
4117: short paramVal = (short) reader.readShort(getByteOrder());
4118: if (SanityManager.DEBUG)
4119: trace("short parameter value is: " + paramVal);
4120: // DB2 does not have a BOOLEAN java.sql.bit type, it's sent as small
4121: if (pmeta.getParameterType(i + 1) == JDBC30Translation.BOOLEAN)
4122: ps.setBoolean(i + 1, (paramVal == 1));
4123: else
4124: ps.setShort(i + 1, paramVal);
4125: break;
4126: }
4127: case DRDAConstants.DRDA_TYPE_NINTEGER: {
4128: int paramVal = reader.readInt(getByteOrder());
4129: if (SanityManager.DEBUG)
4130: trace("integer parameter value is: " + paramVal);
4131: ps.setInt(i + 1, paramVal);
4132: break;
4133: }
4134: case DRDAConstants.DRDA_TYPE_NINTEGER8: {
4135: long paramVal = reader.readLong(getByteOrder());
4136: if (SanityManager.DEBUG)
4137: trace("parameter value is: " + paramVal);
4138: ps.setLong(i + 1, paramVal);
4139: break;
4140: }
4141: case DRDAConstants.DRDA_TYPE_NFLOAT4: {
4142: float paramVal = reader.readFloat(getByteOrder());
4143: if (SanityManager.DEBUG)
4144: trace("parameter value is: " + paramVal);
4145: ps.setFloat(i + 1, paramVal);
4146: break;
4147: }
4148: case DRDAConstants.DRDA_TYPE_NFLOAT8: {
4149: double paramVal = reader.readDouble(getByteOrder());
4150: if (SanityManager.DEBUG)
4151: trace("nfloat8 parameter value is: " + paramVal);
4152: ps.setDouble(i + 1, paramVal);
4153: break;
4154: }
4155: case DRDAConstants.DRDA_TYPE_NDECIMAL: {
4156: int precision = (paramLenNumBytes >> 8) & 0xff;
4157: int scale = paramLenNumBytes & 0xff;
4158: BigDecimal paramVal = reader.readBigDecimal(precision,
4159: scale);
4160: if (SanityManager.DEBUG)
4161: trace("ndecimal parameter value is: " + paramVal);
4162: ps.setBigDecimal(i + 1, paramVal);
4163: break;
4164: }
4165: case DRDAConstants.DRDA_TYPE_NDATE: {
4166: String paramVal = reader.readStringData(10).trim(); //parameter may be char value
4167: if (SanityManager.DEBUG)
4168: trace("ndate parameter value is: \"" + paramVal + "\"");
4169: try {
4170: ps.setDate(i + 1, java.sql.Date.valueOf(paramVal));
4171: } catch (java.lang.IllegalArgumentException e) {
4172: // Just use SQLSTATE as message since, if user wants to
4173: // retrieve it, the message will be looked up by the
4174: // sqlcamessage() proc, which will get the localized
4175: // message based on SQLSTATE, and will ignore the
4176: // the message we use here...
4177: throw new SQLException(
4178: SQLState.LANG_DATE_SYNTAX_EXCEPTION,
4179: SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(
4180: 0, 5));
4181: }
4182: break;
4183: }
4184: case DRDAConstants.DRDA_TYPE_NTIME: {
4185: String paramVal = reader.readStringData(8).trim(); //parameter may be char value
4186: if (SanityManager.DEBUG)
4187: trace("ntime parameter value is: " + paramVal);
4188: try {
4189: ps.setTime(i + 1, java.sql.Time.valueOf(paramVal));
4190: } catch (java.lang.IllegalArgumentException e) {
4191: throw new SQLException(
4192: SQLState.LANG_DATE_SYNTAX_EXCEPTION,
4193: SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(
4194: 0, 5));
4195: }
4196: break;
4197: }
4198: case DRDAConstants.DRDA_TYPE_NTIMESTAMP: {
4199: // JCC represents ts in a slightly different format than Java standard, so
4200: // we do the conversion to Java standard here.
4201: String paramVal = reader.readStringData(26).trim(); //parameter may be char value
4202: if (SanityManager.DEBUG)
4203: trace("ntimestamp parameter value is: " + paramVal);
4204: try {
4205: String tsString = paramVal.substring(0, 10) + " "
4206: + paramVal.substring(11, 19).replace('.', ':')
4207: + paramVal.substring(19);
4208: if (SanityManager.DEBUG)
4209: trace("tsString is: " + tsString);
4210: ps.setTimestamp(i + 1, java.sql.Timestamp
4211: .valueOf(tsString));
4212: } catch (java.lang.IllegalArgumentException e1) {
4213: // thrown by Timestamp.valueOf(...) for bad syntax...
4214: throw new SQLException(
4215: SQLState.LANG_DATE_SYNTAX_EXCEPTION,
4216: SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(
4217: 0, 5));
4218: } catch (java.lang.StringIndexOutOfBoundsException e2) {
4219: // can be thrown by substring(...) if syntax is invalid...
4220: throw new SQLException(
4221: SQLState.LANG_DATE_SYNTAX_EXCEPTION,
4222: SQLState.LANG_DATE_SYNTAX_EXCEPTION.substring(
4223: 0, 5));
4224: }
4225: break;
4226: }
4227: case DRDAConstants.DRDA_TYPE_NCHAR:
4228: case DRDAConstants.DRDA_TYPE_NVARCHAR:
4229: case DRDAConstants.DRDA_TYPE_NLONG:
4230: case DRDAConstants.DRDA_TYPE_NVARMIX:
4231: case DRDAConstants.DRDA_TYPE_NLONGMIX: {
4232: String paramVal = reader
4233: .readLDStringData(stmt.ccsidMBCEncoding);
4234: if (SanityManager.DEBUG)
4235: trace("char/varchar parameter value is: " + paramVal);
4236: ps.setString(i + 1, paramVal);
4237: break;
4238: }
4239: case DRDAConstants.DRDA_TYPE_NFIXBYTE: {
4240: byte[] paramVal = reader.readBytes();
4241: if (SanityManager.DEBUG)
4242: trace("fix bytes parameter value is: "
4243: + convertToHexString(paramVal));
4244: ps.setBytes(i + 1, paramVal);
4245: break;
4246: }
4247: case DRDAConstants.DRDA_TYPE_NVARBYTE:
4248: case DRDAConstants.DRDA_TYPE_NLONGVARBYTE: {
4249: int length = reader.readNetworkShort(); //protocol control data always follows big endian
4250: if (SanityManager.DEBUG)
4251: trace("===== binary param length is: " + length);
4252: byte[] paramVal = reader.readBytes(length);
4253: ps.setBytes(i + 1, paramVal);
4254: break;
4255: }
4256: case DRDAConstants.DRDA_TYPE_NLOBBYTES:
4257: case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
4258: case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
4259: case DRDAConstants.DRDA_TYPE_NLOBCDBCS: {
4260: long length = readLobLength(paramLenNumBytes);
4261: if (length != 0) //can be -1 for CLI if "data at exec" mode, see clifp/exec test
4262: {
4263: if (paramExtPositions == null)
4264: paramExtPositions = new ArrayList();
4265: paramExtPositions.add(new Integer(i));
4266: } else /* empty */
4267: {
4268: if (drdaType == DRDAConstants.DRDA_TYPE_NLOBBYTES)
4269: ps.setBytes(i + 1, new byte[0]);
4270: else
4271: ps.setString(i + 1, "");
4272: }
4273: break;
4274: }
4275: default: {
4276: String paramVal = reader
4277: .readLDStringData(stmt.ccsidMBCEncoding);
4278: if (SanityManager.DEBUG)
4279: trace("default type parameter value is: " + paramVal);
4280: ps.setObject(i + 1, paramVal);
4281: }
4282: }
4283: return paramExtPositions;
4284: }
4285:
4286: private long readLobLength(int extLenIndicator)
4287: throws DRDAProtocolException {
4288: switch (extLenIndicator) {
4289: case 0x8002:
4290: return (long) reader.readNetworkShort();
4291: case 0x8004:
4292: return (long) reader.readNetworkInt();
4293: case 0x8006:
4294: return (long) reader.readNetworkSixByteLong();
4295: case 0x8008:
4296: return (long) reader.readNetworkLong();
4297: default:
4298: throwSyntaxrm(CodePoint.SYNERRCD_INCORRECT_EXTENDED_LEN,
4299: extLenIndicator);
4300: return 0L;
4301: }
4302:
4303: }
4304:
4305: private void readAndSetAllExtParams(final DRDAStatement stmt,
4306: final boolean streamLOB) throws SQLException,
4307: DRDAProtocolException {
4308: int numExt = stmt.cliParamExtPositions.size();
4309: for (int i = 0; i < stmt.cliParamExtPositions.size(); i++) {
4310: int paramPos = ((Integer) (stmt.cliParamExtPositions)
4311: .get(i)).intValue();
4312: final boolean doStreamLOB = (streamLOB && i == numExt - 1);
4313: readAndSetExtParam(paramPos, stmt,
4314: ((Byte) stmt.cliParamDrdaTypes.elementAt(paramPos))
4315: .intValue(), ((Integer) (stmt.cliParamLens
4316: .elementAt(paramPos))).intValue(),
4317: doStreamLOB);
4318: // Each extdta in it's own dss
4319: if (i < numExt - 1) {
4320: correlationID = reader.readDssHeader();
4321: int codePoint = reader.readLengthAndCodePoint();
4322: }
4323: }
4324:
4325: }
4326:
4327: /**
4328: * Read different types of input parameters and set them in PreparedStatement
4329: * @param i index of the parameter
4330: * @param stmt associated ps
4331: * @param drdaType drda type of the parameter
4332: *
4333: * @throws DRDAProtocolException
4334: * @throws SQLException
4335: */
4336: private void readAndSetExtParam(int i, DRDAStatement stmt,
4337: int drdaType, int extLen, boolean streamLOB)
4338: throws DRDAProtocolException, SQLException {
4339: PreparedStatement ps = stmt.getPreparedStatement();
4340: drdaType = (drdaType & 0x000000ff); // need unsigned value
4341: boolean checkNullability = false;
4342: if (sqlamLevel >= MGRLVL_7
4343: && FdocaConstants.isNullable(drdaType))
4344: checkNullability = true;
4345:
4346: try {
4347: final byte[] paramBytes;
4348: final String paramString;
4349:
4350: switch (drdaType) {
4351: case DRDAConstants.DRDA_TYPE_LOBBYTES:
4352: case DRDAConstants.DRDA_TYPE_NLOBBYTES:
4353: paramString = "";
4354: final boolean useSetBinaryStream = stmt
4355: .getParameterMetaData().getParameterType(i + 1) == Types.BLOB;
4356:
4357: if (streamLOB && useSetBinaryStream) {
4358: paramBytes = null;
4359: final EXTDTAReaderInputStream stream = reader
4360: .getEXTDTAReaderInputStream(checkNullability);
4361: if (stream == null) {
4362: ps.setBytes(i + 1, null);
4363: } else {
4364: ps.setBinaryStream(i + 1, stream, (int) stream
4365: .getLength());
4366: }
4367:
4368: if (SanityManager.DEBUG) {
4369: if (stream == null) {
4370: trace("parameter value : NULL");
4371: } else {
4372: trace("parameter value will be streamed");
4373: }
4374: }
4375: } else {
4376: paramBytes = reader.getExtData(checkNullability);
4377: if (paramBytes == null || !useSetBinaryStream) {
4378: ps.setBytes(i + 1, paramBytes);
4379: } else {
4380: ps.setBinaryStream(i + 1,
4381: new ByteArrayInputStream(paramBytes),
4382: paramBytes.length);
4383: }
4384: if (SanityManager.DEBUG) {
4385: if (paramBytes == null) {
4386: trace("parameter value : NULL");
4387: } else {
4388: trace("parameter value is a LOB with length:"
4389: + paramBytes.length);
4390: }
4391: }
4392: }
4393: break;
4394: case DRDAConstants.DRDA_TYPE_LOBCSBCS:
4395: case DRDAConstants.DRDA_TYPE_NLOBCSBCS:
4396: paramBytes = reader.getExtData(checkNullability);
4397: paramString = new String(paramBytes,
4398: stmt.ccsidSBCEncoding);
4399: if (SanityManager.DEBUG)
4400: trace("parameter value is: " + paramString);
4401: ps.setString(i + 1, paramString);
4402: break;
4403: case DRDAConstants.DRDA_TYPE_LOBCDBCS:
4404: case DRDAConstants.DRDA_TYPE_NLOBCDBCS:
4405: paramBytes = reader.getExtData(checkNullability);
4406: paramString = new String(paramBytes,
4407: stmt.ccsidDBCEncoding);
4408: if (SanityManager.DEBUG)
4409: trace("parameter value is: " + paramString);
4410: ps.setString(i + 1, paramString);
4411: break;
4412: case DRDAConstants.DRDA_TYPE_LOBCMIXED:
4413: case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
4414: paramBytes = reader.getExtData(checkNullability);
4415: paramString = new String(paramBytes,
4416: stmt.ccsidMBCEncoding);
4417: if (SanityManager.DEBUG)
4418: trace("parameter value is: " + paramString);
4419: ps.setString(i + 1, paramString);
4420: break;
4421: default:
4422: paramBytes = null;
4423: paramString = "";
4424:
4425: invalidValue(drdaType);
4426: }
4427:
4428: } catch (java.io.UnsupportedEncodingException e) {
4429: throw new SQLException(e.getMessage());
4430: }
4431: }
4432:
4433: /**
4434: * Parse EXCSQLIMM - Execute Immediate Statement
4435: * Instance Variables
4436: * RDBNAM - relational database name - optional
4437: * PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
4438: * RDBCMTOK - RDB Commit Allowed - optional
4439: * MONITOR - Monitor Events - optional
4440: *
4441: * Command Objects
4442: * TYPDEFNAM - Data Type Definition Name - optional
4443: * TYPDEFOVR - TYPDEF Overrides -optional
4444: * SQLSTT - SQL Statement -required
4445: *
4446: * @return update count
4447: * @throws DRDAProtocolException
4448: * @throws SQLException
4449: */
4450: private int parseEXCSQLIMM() throws DRDAProtocolException,
4451: SQLException {
4452: int codePoint;
4453: reader.markCollection();
4454: Pkgnamcsn pkgnamcsn = null;
4455: codePoint = reader.getCodePoint();
4456: while (codePoint != -1) {
4457: switch (codePoint) {
4458: // optional
4459: case CodePoint.RDBNAM:
4460: setDatabase(CodePoint.EXCSQLIMM);
4461: break;
4462: // required
4463: case CodePoint.PKGNAMCSN:
4464: pkgnamcsn = parsePKGNAMCSN();
4465: break;
4466: case CodePoint.RDBCMTOK:
4467: parseRDBCMTOK();
4468: break;
4469: //optional
4470: case CodePoint.MONITOR:
4471: parseMONITOR();
4472: break;
4473: default:
4474: invalidCodePoint(codePoint);
4475:
4476: }
4477: codePoint = reader.getCodePoint();
4478: }
4479: DRDAStatement drdaStmt = database
4480: .getDefaultStatement(pkgnamcsn);
4481: // initialize statement for reuse
4482: drdaStmt.initialize();
4483: String sqlStmt = parseEXECSQLIMMobjects();
4484: Statement statement = drdaStmt.getStatement();
4485: statement.clearWarnings();
4486: if (pendingStatementTimeout >= 0) {
4487: statement.setQueryTimeout(pendingStatementTimeout);
4488: pendingStatementTimeout = -1;
4489: }
4490: int updCount = statement.executeUpdate(sqlStmt);
4491: return updCount;
4492: }
4493:
4494: /**
4495: * Parse EXCSQLSET - Execute Set SQL Environment
4496: * Instance Variables
4497: * RDBNAM - relational database name - optional
4498: * PKGNAMCT - RDB Package Name, Consistency Token - optional
4499: * MONITOR - Monitor Events - optional
4500: *
4501: * Command Objects
4502: * TYPDEFNAM - Data Type Definition Name - required
4503: * TYPDEFOVR - TYPDEF Overrides - required
4504: * SQLSTT - SQL Statement - required (at least one; may be more)
4505: *
4506: * @throws DRDAProtocolException
4507: * @throws SQLException
4508: */
4509: private boolean parseEXCSQLSET() throws DRDAProtocolException,
4510: SQLException {
4511:
4512: int codePoint;
4513: reader.markCollection();
4514:
4515: codePoint = reader.getCodePoint();
4516: while (codePoint != -1) {
4517: switch (codePoint) {
4518: // optional
4519: case CodePoint.RDBNAM:
4520: setDatabase(CodePoint.EXCSQLSET);
4521: break;
4522: // optional
4523: case CodePoint.PKGNAMCT:
4524: // we are going to ignore this for EXCSQLSET
4525: // since we are just going to reuse an existing statement
4526: String pkgnamct = parsePKGNAMCT();
4527: break;
4528: // optional
4529: case CodePoint.MONITOR:
4530: parseMONITOR();
4531: break;
4532: // required
4533: case CodePoint.PKGNAMCSN:
4534: // we are going to ignore this for EXCSQLSET.
4535: // since we are just going to reuse an existing statement.
4536: // NOTE: This codepoint is not in the DDM spec for 'EXCSQLSET',
4537: // but since it DOES get sent by jcc1.2, we have to have
4538: // a case for it...
4539: Pkgnamcsn pkgnamcsn = parsePKGNAMCSN();
4540: break;
4541: default:
4542: invalidCodePoint(codePoint);
4543:
4544: }
4545: codePoint = reader.getCodePoint();
4546: }
4547:
4548: parseEXCSQLSETobjects();
4549: return true;
4550: }
4551:
4552: /**
4553: * Parse EXCSQLIMM objects
4554: * Objects
4555: * TYPDEFNAM - Data type definition name - optional
4556: * TYPDEFOVR - Type defintion overrides
4557: * SQLSTT - SQL Statement required
4558: *
4559: * If TYPDEFNAM and TYPDEFOVR are supplied, they apply to the objects
4560: * sent with the statement. Once the statement is over, the default values
4561: * sent in the ACCRDB are once again in effect. If no values are supplied,
4562: * the values sent in the ACCRDB are used.
4563: * Objects may follow in one DSS or in several DSS chained together.
4564: *
4565: * @return SQL Statement
4566: * @throws DRDAProtocolException
4567: * @throws SQLException
4568: */
4569: private String parseEXECSQLIMMobjects()
4570: throws DRDAProtocolException, SQLException {
4571: String sqlStmt = null;
4572: int codePoint;
4573: DRDAStatement stmt = database.getDefaultStatement();
4574: do {
4575: correlationID = reader.readDssHeader();
4576: while (reader.moreDssData()) {
4577: codePoint = reader.readLengthAndCodePoint();
4578: switch (codePoint) {
4579: // optional
4580: case CodePoint.TYPDEFNAM:
4581: setStmtOrDbByteOrder(false, stmt, parseTYPDEFNAM());
4582: break;
4583: // optional
4584: case CodePoint.TYPDEFOVR:
4585: parseTYPDEFOVR(stmt);
4586: break;
4587: // required
4588: case CodePoint.SQLSTT:
4589: sqlStmt = parseEncodedString();
4590: if (SanityManager.DEBUG)
4591: trace("sqlStmt = " + sqlStmt);
4592: break;
4593: default:
4594: invalidCodePoint(codePoint);
4595: }
4596: }
4597: } while (reader.isChainedWithSameID());
4598:
4599: // SQLSTT is required
4600: if (sqlStmt == null)
4601: missingCodePoint(CodePoint.SQLSTT);
4602: return sqlStmt;
4603: }
4604:
4605: /**
4606: * Parse EXCSQLSET objects
4607: * Objects
4608: * TYPDEFNAM - Data type definition name - optional
4609: * TYPDEFOVR - Type defintion overrides - optional
4610: * SQLSTT - SQL Statement - required (a list of at least one)
4611: *
4612: * Objects may follow in one DSS or in several DSS chained together.
4613: *
4614: * @throws DRDAProtocolException
4615: * @throws SQLException
4616: */
4617: private void parseEXCSQLSETobjects() throws DRDAProtocolException,
4618: SQLException {
4619:
4620: boolean gotSqlStt = false;
4621: boolean hadUnrecognizedStmt = false;
4622:
4623: String sqlStmt = null;
4624: int codePoint;
4625: DRDAStatement drdaStmt = database.getDefaultStatement();
4626: drdaStmt.initialize();
4627:
4628: do {
4629: correlationID = reader.readDssHeader();
4630: while (reader.moreDssData()) {
4631:
4632: codePoint = reader.readLengthAndCodePoint();
4633:
4634: switch (codePoint) {
4635: // optional
4636: case CodePoint.TYPDEFNAM:
4637: setStmtOrDbByteOrder(false, drdaStmt,
4638: parseTYPDEFNAM());
4639: break;
4640: // optional
4641: case CodePoint.TYPDEFOVR:
4642: parseTYPDEFOVR(drdaStmt);
4643: break;
4644: // required
4645: case CodePoint.SQLSTT:
4646: sqlStmt = parseEncodedString();
4647: if (sqlStmt != null)
4648: // then we have at least one SQL Statement.
4649: gotSqlStt = true;
4650:
4651: if (sqlStmt.startsWith(TIMEOUT_STATEMENT)) {
4652: String timeoutString = sqlStmt
4653: .substring(TIMEOUT_STATEMENT.length());
4654: pendingStatementTimeout = Integer.valueOf(
4655: timeoutString).intValue();
4656: break;
4657: }
4658:
4659: if (canIgnoreStmt(sqlStmt)) {
4660: // We _know_ Cloudscape doesn't recognize this
4661: // statement; don't bother trying to execute it.
4662: // NOTE: at time of writing, this only applies
4663: // to "SET CLIENT" commands, and it was decided
4664: // that throwing a Warning for these commands
4665: // would confuse people, so even though the DDM
4666: // spec says to do so, we choose not to (but
4667: // only for SET CLIENT cases). If this changes
4668: // at some point in the future, simply remove
4669: // the follwing line; we will then throw a
4670: // warning.
4671: // hadUnrecognizedStmt = true;
4672: break;
4673: }
4674:
4675: if (SanityManager.DEBUG)
4676: trace("sqlStmt = " + sqlStmt);
4677:
4678: // initialize statement for reuse
4679: drdaStmt.initialize();
4680: drdaStmt.getStatement().clearWarnings();
4681: try {
4682: drdaStmt.getStatement().executeUpdate(sqlStmt);
4683: } catch (SQLException e) {
4684:
4685: // if this is a syntax error, then we take it
4686: // to mean that the given SET statement is not
4687: // recognized; take note (so we can throw a
4688: // warning later), but don't interfere otherwise.
4689: if (e.getSQLState().equals(SYNTAX_ERR))
4690: hadUnrecognizedStmt = true;
4691: else
4692: // something else; assume it's serious.
4693: throw e;
4694: }
4695: break;
4696: default:
4697: invalidCodePoint(codePoint);
4698: }
4699: }
4700:
4701: } while (reader.isChainedWithSameID());
4702:
4703: // SQLSTT is required.
4704: if (!gotSqlStt)
4705: missingCodePoint(CodePoint.SQLSTT);
4706:
4707: // Now that we've processed all SET statements (assuming no
4708: // severe exceptions), check for warnings and, if we had any,
4709: // note this in the SQLCARD reply object (but DON'T cause the
4710: // EXCSQLSET statement to fail).
4711: if (hadUnrecognizedStmt) {
4712: SQLWarning warn = new SQLWarning(
4713: "One or more SET statements " + "not recognized.",
4714: "01000");
4715: throw warn;
4716: } // end if.
4717:
4718: return;
4719: }
4720:
4721: private boolean canIgnoreStmt(String stmt) {
4722: if (stmt.indexOf("SET CLIENT") != -1)
4723: return true;
4724: return false;
4725: }
4726:
4727: /**
4728: * Write RDBUPDRM
4729: * Instance variables
4730: * SVRCOD - Severity code - Information only - required
4731: * RDBNAM - Relational database name -required
4732: * SRVDGN - Server Diagnostic Information -optional
4733: *
4734: * @exception DRDAProtocolException
4735: */
4736: private void writeRDBUPDRM() throws DRDAProtocolException {
4737: database.RDBUPDRM_sent = true;
4738: writer.createDssReply();
4739: writer.startDdm(CodePoint.RDBUPDRM);
4740: writer.writeScalar2Bytes(CodePoint.SVRCOD,
4741: CodePoint.SVRCOD_INFO);
4742: writeRDBNAM(database.dbName);
4743: writer.endDdmAndDss();
4744: }
4745:
4746: private String parsePKGNAMCT() throws DRDAProtocolException {
4747: reader.skipBytes();
4748: return null;
4749: }
4750:
4751: /**
4752: * Parse PKGNAMCSN - RDB Package Name, Consistency Token, and Section Number
4753: * Instance Variables
4754: * NAMESYMDR - database name - not validated
4755: * RDBCOLID - RDB Collection Identifier
4756: * PKGID - RDB Package Identifier
4757: * PKGCNSTKN - RDB Package Consistency Token
4758: * PKGSN - RDB Package Section Number
4759: *
4760: * @return <code>Pkgnamcsn</code> value
4761: * @throws DRDAProtocolException
4762: */
4763: private Pkgnamcsn parsePKGNAMCSN() throws DRDAProtocolException {
4764: if (reader.getDdmLength() == CodePoint.PKGNAMCSN_LEN) {
4765: // This is a scalar object with the following fields
4766: reader.readString(rdbnam, CodePoint.RDBNAM_LEN, true);
4767: if (SanityManager.DEBUG)
4768: trace("rdbnam = " + rdbnam);
4769:
4770: reader.readString(rdbcolid, CodePoint.RDBCOLID_LEN, true);
4771: if (SanityManager.DEBUG)
4772: trace("rdbcolid = " + rdbcolid);
4773:
4774: reader.readString(pkgid, CodePoint.PKGID_LEN, true);
4775: if (SanityManager.DEBUG)
4776: trace("pkgid = " + pkgid);
4777:
4778: // we need to use the same UCS2 encoding, as this can be
4779: // bounced back to jcc (or keep the byte array)
4780: reader
4781: .readString(pkgcnstkn, CodePoint.PKGCNSTKN_LEN,
4782: false);
4783: if (SanityManager.DEBUG)
4784: trace("pkgcnstkn = " + pkgcnstkn);
4785:
4786: pkgsn = reader.readNetworkShort();
4787: if (SanityManager.DEBUG)
4788: trace("pkgsn = " + pkgsn);
4789: } else // extended format
4790: {
4791: int length = reader.readNetworkShort();
4792: if (length < CodePoint.RDBNAM_LEN
4793: || length > CodePoint.MAX_NAME)
4794: badObjectLength(CodePoint.RDBNAM);
4795: reader.readString(rdbnam, length, true);
4796: if (SanityManager.DEBUG)
4797: trace("rdbnam = " + rdbnam);
4798:
4799: //RDBCOLID can be variable length in this format
4800: length = reader.readNetworkShort();
4801: reader.readString(rdbcolid, length, true);
4802: if (SanityManager.DEBUG)
4803: trace("rdbcolid = " + rdbcolid);
4804:
4805: length = reader.readNetworkShort();
4806: if (length != CodePoint.PKGID_LEN)
4807: badObjectLength(CodePoint.PKGID);
4808: reader.readString(pkgid, CodePoint.PKGID_LEN, true);
4809: if (SanityManager.DEBUG)
4810: trace("pkgid = " + pkgid);
4811:
4812: reader
4813: .readString(pkgcnstkn, CodePoint.PKGCNSTKN_LEN,
4814: false);
4815: if (SanityManager.DEBUG)
4816: trace("pkgcnstkn = " + pkgcnstkn);
4817:
4818: pkgsn = reader.readNetworkShort();
4819: if (SanityManager.DEBUG)
4820: trace("pkgsn = " + pkgsn);
4821: }
4822:
4823: // In most cases, the pkgnamcsn object is equal to the
4824: // previously returned object. To avoid allocation of a new
4825: // object in these cases, we first check to see if the old
4826: // object can be reused.
4827: if ((prevPkgnamcsn == null) || rdbnam.wasModified()
4828: || rdbcolid.wasModified() || pkgid.wasModified()
4829: || pkgcnstkn.wasModified()
4830: || (prevPkgnamcsn.getPkgsn() != pkgsn)) {
4831: // The byte array returned by pkgcnstkn.getBytes() might
4832: // be modified by DDMReader.readString() later, so we have
4833: // to create a copy of the array.
4834: byte[] token = new byte[pkgcnstkn.length()];
4835: System.arraycopy(pkgcnstkn.getBytes(), 0, token, 0,
4836: token.length);
4837:
4838: prevPkgnamcsn = new Pkgnamcsn(rdbnam.toString(), rdbcolid
4839: .toString(), pkgid.toString(), pkgsn,
4840: new ConsistencyToken(token));
4841: }
4842:
4843: return prevPkgnamcsn;
4844: }
4845:
4846: /**
4847: * Parse SQLSTT Dss
4848: * @exception DRDAProtocolException
4849: */
4850: private String parseSQLSTTDss() throws DRDAProtocolException {
4851: correlationID = reader.readDssHeader();
4852: int codePoint = reader.readLengthAndCodePoint();
4853: String strVal = parseEncodedString();
4854: if (SanityManager.DEBUG)
4855: trace("SQL Statement = " + strVal);
4856: return strVal;
4857: }
4858:
4859: /**
4860: * Parse an encoded data string from the Application Requester
4861: *
4862: * @return string value
4863: * @exception DRDAProtocolException
4864: */
4865: private String parseEncodedString() throws DRDAProtocolException {
4866: if (sqlamLevel < 7)
4867: return parseVCMorVCS();
4868: else
4869: return parseNOCMorNOCS();
4870: }
4871:
4872: /**
4873: * Parse variable character mixed byte or variable character single byte
4874: * Format
4875: * I2 - VCM Length
4876: * N bytes - VCM value
4877: * I2 - VCS Length
4878: * N bytes - VCS value
4879: * Only 1 of VCM length or VCS length can be non-zero
4880: *
4881: * @return string value
4882: */
4883: private String parseVCMorVCS() throws DRDAProtocolException {
4884: String strVal = null;
4885: int vcm_length = reader.readNetworkShort();
4886: if (vcm_length > 0)
4887: strVal = parseCcsidMBC(vcm_length);
4888: int vcs_length = reader.readNetworkShort();
4889: if (vcs_length > 0) {
4890: if (strVal != null)
4891: agentError("Both VCM and VCS have lengths > 0");
4892: strVal = parseCcsidSBC(vcs_length);
4893: }
4894: return strVal;
4895: }
4896:
4897: /**
4898: * Parse nullable character mixed byte or nullable character single byte
4899: * Format
4900: * 1 byte - null indicator
4901: * I4 - mixed character length
4902: * N bytes - mixed character string
4903: * 1 byte - null indicator
4904: * I4 - single character length
4905: * N bytes - single character length string
4906: *
4907: * @return string value
4908: * @exception DRDAProtocolException
4909: */
4910: private String parseNOCMorNOCS() throws DRDAProtocolException {
4911: byte nocm_nullByte = reader.readByte();
4912: String strVal = null;
4913: int length;
4914: if (nocm_nullByte != NULL_VALUE) {
4915: length = reader.readNetworkInt();
4916: strVal = parseCcsidMBC(length);
4917: }
4918: byte nocs_nullByte = reader.readByte();
4919: if (nocs_nullByte != NULL_VALUE) {
4920: if (strVal != null)
4921: agentError("Both CM and CS are non null");
4922: length = reader.readNetworkInt();
4923: strVal = parseCcsidSBC(length);
4924: }
4925: return strVal;
4926: }
4927:
4928: /**
4929: * Parse mixed character string
4930: *
4931: * @return string value
4932: * @exception DRDAProtocolException
4933: */
4934: private String parseCcsidMBC(int length)
4935: throws DRDAProtocolException {
4936: String strVal = null;
4937: DRDAStatement currentStatement;
4938:
4939: currentStatement = database.getCurrentStatement();
4940: if (currentStatement == null) {
4941: currentStatement = database.getDefaultStatement();
4942: currentStatement.initialize();
4943: }
4944: String ccsidMBCEncoding = currentStatement.ccsidMBCEncoding;
4945:
4946: if (length == 0)
4947: return null;
4948: byte[] byteStr = reader.readBytes(length);
4949: if (ccsidMBCEncoding != null) {
4950: try {
4951: strVal = new String(byteStr, 0, length,
4952: ccsidMBCEncoding);
4953: } catch (UnsupportedEncodingException e) {
4954: agentError("Unsupported encoding " + ccsidMBCEncoding
4955: + "in parseCcsidMBC");
4956: }
4957: } else
4958: agentError("Attempt to decode mixed byte string without CCSID being set");
4959: return strVal;
4960: }
4961:
4962: /**
4963: * Parse single byte character string
4964: *
4965: * @return string value
4966: * @exception DRDAProtocolException
4967: */
4968: private String parseCcsidSBC(int length)
4969: throws DRDAProtocolException {
4970: String strVal = null;
4971: DRDAStatement currentStatement;
4972:
4973: currentStatement = database.getCurrentStatement();
4974: if (currentStatement == null) {
4975: currentStatement = database.getDefaultStatement();
4976: currentStatement.initialize();
4977: }
4978: String ccsidSBCEncoding = currentStatement.ccsidSBCEncoding;
4979: System.out.println("ccsidSBCEncoding - " + ccsidSBCEncoding);
4980:
4981: if (length == 0)
4982: return null;
4983: byte[] byteStr = reader.readBytes(length);
4984: if (ccsidSBCEncoding != null) {
4985: try {
4986: strVal = new String(byteStr, 0, length,
4987: ccsidSBCEncoding);
4988: } catch (UnsupportedEncodingException e) {
4989: agentError("Unsupported encoding " + ccsidSBCEncoding
4990: + "in parseCcsidSBC");
4991: }
4992: } else
4993: agentError("Attempt to decode single byte string without CCSID being set");
4994: return strVal;
4995: }
4996:
4997: /**
4998: * Parse CLSQRY
4999: * Instance Variables
5000: * RDBNAM - relational database name - optional
5001: * PKGNAMCSN - RDB Package Name, Consistency Token and Section Number - required
5002: * QRYINSID - Query Instance Identifier - required - level 7
5003: * MONITOR - Monitor events - optional.
5004: *
5005: * @return DRDAstatement being closed
5006: * @throws DRDAProtocolException
5007: * @throws SQLException
5008: */
5009: private DRDAStatement parseCLSQRY() throws DRDAProtocolException,
5010: SQLException {
5011: Pkgnamcsn pkgnamcsn = null;
5012: reader.markCollection();
5013: long qryinsid = 0;
5014: boolean gotQryinsid = false;
5015:
5016: int codePoint = reader.getCodePoint();
5017: while (codePoint != -1) {
5018: switch (codePoint) {
5019: // optional
5020: case CodePoint.RDBNAM:
5021: setDatabase(CodePoint.CLSQRY);
5022: break;
5023: // required
5024: case CodePoint.PKGNAMCSN:
5025: pkgnamcsn = parsePKGNAMCSN();
5026: break;
5027: case CodePoint.QRYINSID:
5028: qryinsid = reader.readNetworkLong();
5029: gotQryinsid = true;
5030: break;
5031: // optional
5032: case CodePoint.MONITOR:
5033: parseMONITOR();
5034: break;
5035: default:
5036: invalidCodePoint(codePoint);
5037: }
5038: codePoint = reader.getCodePoint();
5039: }
5040: // check for required variables
5041: if (pkgnamcsn == null)
5042: missingCodePoint(CodePoint.PKGNAMCSN);
5043: if (sqlamLevel >= MGRLVL_7 && !gotQryinsid)
5044: missingCodePoint(CodePoint.QRYINSID);
5045:
5046: DRDAStatement stmt = database.getDRDAStatement(pkgnamcsn);
5047: if (stmt == null) {
5048: //XXX should really throw a SQL Exception here
5049: invalidValue(CodePoint.PKGNAMCSN);
5050: }
5051:
5052: if (stmt.wasExplicitlyClosed()) {
5053: // JCC still sends a CLSQRY even though we have
5054: // implicitly closed the resultSet.
5055: // Then complains if we send the writeQRYNOPRM
5056: // So for now don't send it
5057: // Also metadata calls seem to get bound to the same
5058: // PGKNAMCSN, so even for explicit closes we have
5059: // to ignore.
5060: //writeQRYNOPRM(CodePoint.SVRCOD_ERROR);
5061: pkgnamcsn = null;
5062: }
5063:
5064: stmt.CLSQRY();
5065:
5066: return stmt;
5067: }
5068:
5069: /**
5070: * Parse MONITOR
5071: * DRDA spec says this is optional. Since we
5072: * don't currently support it, we just ignore.
5073: */
5074: private void parseMONITOR() throws DRDAProtocolException {
5075:
5076: // Just ignore it.
5077: reader.skipBytes();
5078: return;
5079:
5080: }
5081:
5082: private void writeSQLCARDs(SQLException e, int updateCount)
5083: throws DRDAProtocolException {
5084: writeSQLCARDs(e, updateCount, false);
5085: }
5086:
5087: private void writeSQLCARDs(SQLException e, int updateCount,
5088: boolean sendSQLERRRM) throws DRDAProtocolException {
5089:
5090: int severity = CodePoint.SVRCOD_INFO;
5091: if (e == null) {
5092: writeSQLCARD(e, severity, updateCount, 0);
5093: return;
5094: }
5095:
5096: // instead of writing a chain of sql error or warning, we send the first one, this is
5097: // jcc/db2 limitation, see beetle 4629
5098:
5099: // If it is a real SQL Error write a SQLERRRM first
5100: severity = getExceptionSeverity(e);
5101: if (severity > CodePoint.SVRCOD_ERROR) {
5102: // For a session ending error > CodePoint.SRVCOD_ERROR you cannot
5103: // send a SQLERRRM. A CMDCHKRM is required. In XA if there is a
5104: // lock timeout it ends the whole session. I am not sure this
5105: // is the correct behaviour but if it occurs we have to send
5106: // a CMDCHKRM instead of SQLERRM
5107: writeCMDCHKRM(severity);
5108: } else if (sendSQLERRRM) {
5109: writeSQLERRRM(severity);
5110: }
5111: writeSQLCARD(e, severity, updateCount, 0);
5112: }
5113:
5114: private int getSqlCode(int severity) {
5115: if (severity == CodePoint.SVRCOD_WARNING) // warning
5116: return 100; //CLI likes it
5117: else if (severity == CodePoint.SVRCOD_INFO)
5118: return 0;
5119: else
5120: return -1;
5121: }
5122:
5123: private void writeSQLCARD(SQLException e, int severity,
5124: int updateCount, long rowCount)
5125: throws DRDAProtocolException {
5126: writer.createDssObject();
5127: writer.startDdm(CodePoint.SQLCARD);
5128: writeSQLCAGRP(e, getSqlCode(severity), updateCount, rowCount);
5129: writer.endDdmAndDss();
5130:
5131: // If we have a shutdown exception, restart the server.
5132: if (e != null) {
5133: String sqlState = e.getSQLState();
5134: if (sqlState.regionMatches(0,
5135: SQLState.CLOUDSCAPE_SYSTEM_SHUTDOWN, 0, 5)) {
5136: // then we're here because of a shutdown exception;
5137: // "clean up" by restarting the server.
5138: try {
5139: server.startNetworkServer();
5140: } catch (Exception restart)
5141: // any error messages should have already been printed,
5142: // so we ignore this exception here.
5143: {
5144: }
5145: }
5146: }
5147:
5148: }
5149:
5150: /**
5151: * Write a null SQLCARD as an object
5152: *
5153: * @exception DRDAProtocolException
5154: */
5155: private void writeNullSQLCARDobject() throws DRDAProtocolException {
5156: writer.createDssObject();
5157: writer.startDdm(CodePoint.SQLCARD);
5158: writeSQLCAGRP(nullSQLState, 0, 0, 0);
5159: writer.endDdmAndDss();
5160: }
5161:
5162: /**
5163: * Write SQLERRRM
5164: *
5165: * Instance Variables
5166: * SVRCOD - Severity Code - required
5167: *
5168: * @param severity severity of error
5169: *
5170: * @exception DRDAProtocolException
5171: */
5172: private void writeSQLERRRM(int severity)
5173: throws DRDAProtocolException {
5174: writer.createDssReply();
5175: writer.startDdm(CodePoint.SQLERRRM);
5176: writer.writeScalar2Bytes(CodePoint.SVRCOD, severity);
5177: writer.endDdmAndDss();
5178:
5179: }
5180:
5181: /**
5182: * Write CMDCHKRM
5183: *
5184: * Instance Variables
5185: * SVRCOD - Severity Code - required
5186: *
5187: * @param severity severity of error
5188: *
5189: * @exception DRDAProtocolException
5190: */
5191: private void writeCMDCHKRM(int severity)
5192: throws DRDAProtocolException {
5193: writer.createDssReply();
5194: writer.startDdm(CodePoint.CMDCHKRM);
5195: writer.writeScalar2Bytes(CodePoint.SVRCOD, severity);
5196: writer.endDdmAndDss();
5197:
5198: }
5199:
5200: /**
5201: * Translate from Cloudscape exception severity to SVRCOD
5202: *
5203: * @param e SQLException
5204: */
5205: private int getExceptionSeverity(SQLException e) {
5206: int severity = CodePoint.SVRCOD_INFO;
5207:
5208: if (e == null)
5209: return severity;
5210:
5211: int ec = e.getErrorCode();
5212: switch (ec) {
5213: case ExceptionSeverity.STATEMENT_SEVERITY:
5214: case ExceptionSeverity.TRANSACTION_SEVERITY:
5215: severity = CodePoint.SVRCOD_ERROR;
5216: break;
5217: case ExceptionSeverity.WARNING_SEVERITY:
5218: severity = CodePoint.SVRCOD_WARNING;
5219: break;
5220: case ExceptionSeverity.SESSION_SEVERITY:
5221: case ExceptionSeverity.DATABASE_SEVERITY:
5222: case ExceptionSeverity.SYSTEM_SEVERITY:
5223: severity = CodePoint.SVRCOD_SESDMG;
5224: break;
5225: default:
5226: String sqlState = e.getSQLState();
5227: if (sqlState != null && sqlState.startsWith("01")) // warning
5228: severity = CodePoint.SVRCOD_WARNING;
5229: else
5230: severity = CodePoint.SVRCOD_ERROR;
5231: }
5232:
5233: return severity;
5234:
5235: }
5236:
5237: /**
5238: * Write SQLCAGRP
5239: *
5240: * SQLCAGRP : FDOCA EARLY GROUP
5241: * SQL Communcations Area Group Description
5242: *
5243: * FORMAT FOR SQLAM <= 6
5244: * SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5245: * SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
5246: * SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
5247: * SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
5248: *
5249: * FORMAT FOR SQLAM >= 7
5250: * SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5251: * SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
5252: * SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
5253: * SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
5254: * SQLDIAGGRP; DRDA TYPE N-GDA; ENVLID 0x56; Length Override 0
5255: *
5256: * @param e SQLException encountered
5257: * @param sqlcode sqlcode
5258: *
5259: * @exception DRDAProtocolException
5260: */
5261: private void writeSQLCAGRP(SQLException e, int sqlcode,
5262: int updateCount, long rowCount)
5263: throws DRDAProtocolException {
5264: if (e == null) {
5265: // Forwarding to the optimized version when there is no
5266: // exception object
5267: writeSQLCAGRP(nullSQLState, sqlcode, updateCount, rowCount);
5268: return;
5269: }
5270:
5271: if (rowCount < 0 && updateCount < 0) {
5272: writer.writeByte(CodePoint.NULLDATA);
5273: return;
5274: }
5275:
5276: if (SanityManager.DEBUG && server.debugOutput && sqlcode < 0) {
5277: trace("handle SQLException here");
5278: trace("reason is: " + e.getMessage());
5279: trace("SQLState is: " + e.getSQLState());
5280: trace("vendorCode is: " + e.getErrorCode());
5281: trace("nextException is: " + e.getNextException());
5282: server.consoleExceptionPrint(e);
5283: trace("wrapping SQLException into SQLCARD...");
5284: }
5285:
5286: //null indicator
5287: writer.writeByte(0);
5288:
5289: // SQLCODE
5290: writer.writeInt(sqlcode);
5291:
5292: // SQLSTATE
5293: writer.writeString(e.getSQLState());
5294:
5295: // SQLERRPROC
5296: // Write the byte[] constant rather than the string, for efficiency
5297: writer.writeBytes(server.prdIdBytes_);
5298:
5299: // SQLCAXGRP
5300: writeSQLCAXGRP(updateCount, rowCount, buildSqlerrmc(e), e
5301: .getNextException());
5302: }
5303:
5304: /**
5305: * Same as writeSQLCAGRP, but optimized for the case
5306: * when there is no real exception, i.e. the exception is null, or "End
5307: * of data"
5308: *
5309: * SQLCAGRP : FDOCA EARLY GROUP
5310: * SQL Communcations Area Group Description
5311: *
5312: * FORMAT FOR SQLAM <= 6
5313: * SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5314: * SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
5315: * SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
5316: * SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
5317: *
5318: * FORMAT FOR SQLAM >= 7
5319: * SQLCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5320: * SQLSTATE; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
5321: * SQLERRPROC; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
5322: * SQLCAXGRP; DRDA TYPE N-GDA; ENVLID 0x52; Length Override 0
5323: * SQLDIAGGRP; DRDA TYPE N-GDA; ENVLID 0x56; Length Override 0
5324: *
5325: * @param sqlState SQLState (already converted to UTF8)
5326: * @param sqlcode sqlcode
5327: * @param updateCount
5328: * @param rowCount
5329: *
5330: * @exception DRDAProtocolException
5331: */
5332:
5333: private void writeSQLCAGRP(byte[] sqlState, int sqlcode,
5334: int updateCount, long rowCount)
5335: throws DRDAProtocolException {
5336: if (rowCount < 0 && updateCount < 0) {
5337: writer.writeByte(CodePoint.NULLDATA);
5338: return;
5339: }
5340:
5341: //null indicator
5342: writer.writeByte(0);
5343:
5344: // SQLCODE
5345: writer.writeInt(sqlcode);
5346:
5347: // SQLSTATE
5348: writer.writeBytes(sqlState);
5349:
5350: // SQLERRPROC
5351: writer.writeBytes(server.prdIdBytes_);
5352:
5353: // SQLCAXGRP (Uses null as sqlerrmc since there is no error)
5354: writeSQLCAXGRP(updateCount, rowCount, null, null);
5355: }
5356:
5357: // Delimiters for SQLERRMC values.
5358: // The token delimiter value will be used to parse the MessageId from the
5359: // SQLERRMC in MessageService.getLocalizedMessage and the MessageId will be
5360: // used to retrive the localized message. If this delimiter value is changed
5361: // please make sure to make appropriate changes in
5362: // MessageService.getLocalizedMessage that gets called from
5363: // SystemProcedures.SQLCAMESSAGE
5364: /**
5365: * <code>SQLERRMC_TOKEN_DELIMITER</code> separates message argument tokens
5366: */
5367: private static String SQLERRMC_TOKEN_DELIMITER = new String(
5368: new char[] { (char) 20 });
5369:
5370: /**
5371: * <code>SQLERRMC_PREFORMATTED_MESSAGE_DELIMITER</code>, When full message text is
5372: * sent for severe errors. This value separates the messages.
5373: */
5374: private static String SQLERRMC_PREFORMATTED_MESSAGE_DELIMITER = new String(
5375: "::");
5376:
5377: /**
5378: * Create error message or message argements to return to client.
5379: * The SQLERRMC will normally be passed back to the server in a call
5380: * to the SYSIBM.SQLCAMESSAGE but for severe exceptions the stored procedure
5381: * call cannot be made. So for Severe messages we will just send the message text.
5382: *
5383: * This method will also truncate the value according the client capacity.
5384: * CCC can only handle 70 characters.
5385: *
5386: * Server sends the sqlerrmc using UTF8 encoding to the client.
5387: * To get the message, client sends back information to the server
5388: * calling SYSIBM.SQLCAMESSAGE (see Sqlca.getMessage). Several parameters
5389: * are sent to this procedure including the locale, the sqlerrmc that the
5390: * client received from the server.
5391: * On server side, the procedure SQLCAMESSAGE in SystemProcedures then calls
5392: * the MessageService.getLocalizedMessage to retrieve the localized error message.
5393: * In MessageService.getLocalizedMessage the sqlerrmc that is passed in,
5394: * is parsed to retrieve the message id. The value it uses to parse the MessageId
5395: * is char value of 20, otherwise it uses the entire sqlerrmc as the message id.
5396: * This messageId is then used to retrieve the localized message if present, to
5397: * the client.
5398: *
5399: * @param se SQLException to build SQLERRMC
5400: *
5401: * @return String which is either the message arguments to be passed to
5402: * SYSIBM.SQLCAMESSAGE or just message text for severe errors.
5403: */
5404: private String buildSqlerrmc(SQLException se) {
5405: boolean severe = (se.getErrorCode() >= ExceptionSeverity.SESSION_SEVERITY);
5406: String sqlerrmc = null;
5407:
5408: // get exception which carries Derby messageID and args, per DERBY-1178
5409: se = Util.getExceptionFactory().getArgumentFerry(se);
5410:
5411: if (se instanceof EmbedSQLException && !severe)
5412: sqlerrmc = buildTokenizedSqlerrmc((EmbedSQLException) se);
5413: else {
5414: // If this is not an EmbedSQLException or is a severe excecption where
5415: // we have no hope of succussfully calling the SYSIBM.SQLCAMESSAGE send
5416: // preformatted message using the server locale
5417: sqlerrmc = buildPreformattedSqlerrmc(se);
5418: }
5419: // Truncate the sqlerrmc to a length that the client can support.
5420: int maxlen = (sqlerrmc == null) ? -1 : Math.min(sqlerrmc
5421: .length(), appRequester.supportedMessageParamLength());
5422: if ((maxlen >= 0) && (sqlerrmc.length() > maxlen))
5423: // have to truncate so the client can handle it.
5424: sqlerrmc = sqlerrmc.substring(0, maxlen);
5425: return sqlerrmc;
5426: }
5427:
5428: /**
5429: * Build preformatted SQLException text
5430: * for severe exceptions or SQLExceptions that are not EmbedSQLExceptions.
5431: * Just send the message text localized to the server locale.
5432: *
5433: * @param se SQLException for which to build SQLERRMC
5434: * @return preformated message text
5435: * with messages separted by SQLERRMC_PREFORMATED_MESSAGE_DELIMITER
5436: *
5437: */
5438: private String buildPreformattedSqlerrmc(SQLException se) {
5439: if (se == null)
5440: return "";
5441:
5442: StringBuffer sb = new StringBuffer();
5443: // String buffer to build up message
5444: do {
5445: sb.append(se.getLocalizedMessage());
5446: se = se.getNextException();
5447: if (se != null)
5448: sb.append(SQLERRMC_PREFORMATTED_MESSAGE_DELIMITER
5449: + "SQLSTATE: " + se.getSQLState());
5450: } while (se != null);
5451: return sb.toString();
5452: }
5453:
5454: /**
5455: * Build Tokenized SQLERRMC to just send the tokenized arguments to the client.
5456: * for a Derby SQLException
5457: * Message argument tokens are separated by SQLERRMC_TOKEN_DELIMITER
5458: * Multiple messages are separated by SystemProcedures.SQLERRMC_MESSAGE_DELIMITER
5459: *
5460: * ...
5461: * @param se SQLException to print
5462: *
5463: */
5464: private String buildTokenizedSqlerrmc(EmbedSQLException se) {
5465:
5466: String sqlerrmc = "";
5467: do {
5468: String messageId = se.getMessageId();
5469: // arguments are variable part of a message
5470: Object[] args = se.getArguments();
5471: for (int i = 0; args != null && i < args.length; i++)
5472: sqlerrmc += args[i] + SQLERRMC_TOKEN_DELIMITER;
5473: sqlerrmc += messageId;
5474: se = (EmbedSQLException) se.getNextException();
5475: if (se != null) {
5476: sqlerrmc += SystemProcedures.SQLERRMC_MESSAGE_DELIMITER
5477: + se.getSQLState() + ":";
5478: }
5479: } while (se != null);
5480: return sqlerrmc;
5481: }
5482:
5483: /**
5484: * Write SQLCAXGRP
5485: *
5486: * SQLCAXGRP : EARLY FDOCA GROUP
5487: * SQL Communications Area Exceptions Group Description
5488: *
5489: * FORMAT FOR SQLAM <= 6
5490: * SQLRDBNME; DRDA TYPE FCS; ENVLID 0x30; Length Override 18
5491: * SQLERRD1; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5492: * SQLERRD2; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5493: * SQLERRD3; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5494: * SQLERRD4; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5495: * SQLERRD5; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5496: * SQLERRD6; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5497: * SQLWARN0; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5498: * SQLWARN1; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5499: * SQLWARN2; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5500: * SQLWARN3; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5501: * SQLWARN4; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5502: * SQLWARN5; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5503: * SQLWARN6; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5504: * SQLWARN7; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5505: * SQLWARN8; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5506: * SQLWARN9; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5507: * SQLWARNA; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5508: * SQLERRMSG_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 70
5509: * SQLERRMSG_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 70
5510: *
5511: * FORMAT FOR SQLAM >= 7
5512: * SQLERRD1; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5513: * SQLERRD2; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5514: * SQLERRD3; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5515: * SQLERRD4; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5516: * SQLERRD5; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5517: * SQLERRD6; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5518: * SQLWARN0; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5519: * SQLWARN1; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5520: * SQLWARN2; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5521: * SQLWARN3; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5522: * SQLWARN4; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5523: * SQLWARN5; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5524: * SQLWARN6; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5525: * SQLWARN7; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5526: * SQLWARN8; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5527: * SQLWARN9; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5528: * SQLWARNA; DRDA TYPE FCS; ENVLID 0x30; Length Override 1
5529: * SQLRDBNAME; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
5530: * SQLERRMSG_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 70
5531: * SQLERRMSG_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 70
5532: * @param nextException SQLException encountered
5533: * @param sqlerrmc sqlcode
5534: *
5535: * @exception DRDAProtocolException
5536: */
5537: private void writeSQLCAXGRP(int updateCount, long rowCount,
5538: String sqlerrmc, SQLException nextException)
5539: throws DRDAProtocolException {
5540: writer.writeByte(0); // SQLCAXGRP INDICATOR
5541: if (sqlamLevel < 7) {
5542: writeRDBNAM(database.dbName);
5543: writeSQLCAERRWARN(updateCount, rowCount);
5544: } else {
5545: // SQL ERRD1 - D6, WARN0-WARNA (35 bytes)
5546: writeSQLCAERRWARN(updateCount, rowCount);
5547: writer.writeShort(0); //CCC on Win does not take RDBNAME
5548: }
5549: writeVCMorVCS(sqlerrmc);
5550: if (sqlamLevel >= 7)
5551: writeSQLDIAGGRP(nextException);
5552: }
5553:
5554: /**
5555: * Write the ERR and WARN part of the SQLCA
5556: *
5557: * @param updateCount
5558: * @param rowCount
5559: */
5560: private void writeSQLCAERRWARN(int updateCount, long rowCount) {
5561: // SQL ERRD1 - ERRD2 - row Count
5562: writer.writeInt((int) ((rowCount >>> 32)));
5563: writer.writeInt((int) (rowCount & 0x0000000ffffffffL));
5564: // SQL ERRD3 - updateCount
5565: writer.writeInt(updateCount);
5566: // SQL ERRD4 - D6 (12 bytes)
5567: writer.writeBytes(errD4_D6); // byte[] constant
5568: // WARN0-WARNA (11 bytes)
5569: writer.writeBytes(warn0_warnA); // byte[] constant
5570: }
5571:
5572: /**
5573: * Write SQLDIAGGRP: SQL Diagnostics Group Description - Identity 0xD1
5574: * Nullable Group
5575: * SQLDIAGSTT; DRDA TYPE N-GDA; ENVLID 0xD3; Length Override 0
5576: * SQLDIAGCN; DRFA TYPE N-RLO; ENVLID 0xF6; Length Override 0
5577: * SQLDIAGCI; DRDA TYPE N-RLO; ENVLID 0xF5; Length Override 0
5578: */
5579: private void writeSQLDIAGGRP(SQLException nextException)
5580: throws DRDAProtocolException {
5581: // for now we only want to send ROW_DELETED and ROW_UPDATED warnings
5582: // as extended diagnostics
5583: // move to first ROW_DELETED or ROW_UPDATED exception. These have been
5584: // added to the end of the warning chain.
5585: while (nextException != null
5586: && nextException.getSQLState() != SQLState.ROW_UPDATED
5587: && nextException.getSQLState() != SQLState.ROW_DELETED) {
5588: nextException = nextException.getNextException();
5589: }
5590:
5591: if ((nextException == null)
5592: || (diagnosticLevel == CodePoint.DIAGLVL0)) {
5593: writer.writeByte(CodePoint.NULLDATA);
5594: return;
5595: }
5596: writer.writeByte(0); // SQLDIAGGRP indicator
5597:
5598: writeSQLDIAGSTT();
5599: writeSQLDIAGCI(nextException);
5600: writeSQLDIAGCN();
5601: }
5602:
5603: /*
5604: * writeSQLDIAGSTT: Write NULLDATA for now
5605: */
5606: private void writeSQLDIAGSTT() throws DRDAProtocolException {
5607: writer.writeByte(CodePoint.NULLDATA);
5608: return;
5609: }
5610:
5611: /**
5612: * writeSQLDIAGCI: SQL Diagnostics Condition Information Array - Identity 0xF5
5613: * SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
5614: * SQLDCIROW; ROW LID 0xE5; ELEMENT TAKEN 0(all); REP FACTOR 0(all)
5615: */
5616: private void writeSQLDIAGCI(SQLException nextException)
5617: throws DRDAProtocolException {
5618: SQLException se = nextException;
5619: long rowNum = 1;
5620:
5621: /* Write the number of next exceptions to expect */
5622: writeSQLNUMROW(se);
5623:
5624: while (se != null) {
5625: String sqlState = se.getSQLState();
5626:
5627: // SQLCode > 0 -> Warning
5628: // SQLCode = 0 -> Info
5629: // SQLCode < 0 -> Error
5630: int severity = getExceptionSeverity(se);
5631: int sqlCode = -1;
5632: if (severity == CodePoint.SVRCOD_WARNING)
5633: sqlCode = 1;
5634: else if (severity == CodePoint.SVRCOD_INFO)
5635: sqlCode = 0;
5636:
5637: String sqlerrmc = "";
5638: if (diagnosticLevel == CodePoint.DIAGLVL1) {
5639: sqlerrmc = se.getLocalizedMessage();
5640: }
5641:
5642: // arguments are variable part of a message
5643: // only send arguments for diagnostic level 0
5644: if (diagnosticLevel == CodePoint.DIAGLVL0) {
5645: // we are only able to get arguments of EmbedSQLException
5646: if (se instanceof EmbedSQLException) {
5647: Object[] args = ((EmbedSQLException) se)
5648: .getArguments();
5649: for (int i = 0; args != null && i < args.length; i++)
5650: sqlerrmc += args[i].toString()
5651: + SQLERRMC_TOKEN_DELIMITER;
5652: }
5653: }
5654:
5655: String dbname = null;
5656: if (database != null)
5657: dbname = database.dbName;
5658:
5659: writeSQLDCROW(rowNum++, sqlCode, sqlState, dbname, sqlerrmc);
5660:
5661: se = se.getNextException();
5662: }
5663:
5664: return;
5665: }
5666:
5667: /**
5668: * writeSQLNUMROW: Writes SQLNUMROW : FDOCA EARLY ROW
5669: * SQL Number of Elements Row Description
5670: * FORMAT FOR SQLAM LEVELS
5671: * SQLNUMGRP; GROUP LID 0x58; ELEMENT TAKEN 0(all); REP FACTOR 1
5672: */
5673: private void writeSQLNUMROW(SQLException nextException)
5674: throws DRDAProtocolException {
5675: writeSQLNUMGRP(nextException);
5676: }
5677:
5678: /**
5679: * writeSQLNUMGRP: Writes SQLNUMGRP : FDOCA EARLY GROUP
5680: * SQL Number of Elements Group Description
5681: * FORMAT FOR ALL SQLAM LEVELS
5682: * SQLNUM; DRDA TYPE I2; ENVLID 0x04; Length Override 2
5683: */
5684: private void writeSQLNUMGRP(SQLException nextException)
5685: throws DRDAProtocolException {
5686: int i = 0;
5687: SQLException se;
5688:
5689: /* Count the number of chained exceptions to be sent */
5690: for (se = nextException; se != null; se = se.getNextException())
5691: i++;
5692: writer.writeShort(i);
5693: }
5694:
5695: /**
5696: * writeSQLDCROW: SQL Diagnostics Condition Row - Identity 0xE5
5697: * SQLDCGRP; GROUP LID 0xD5; ELEMENT TAKEN 0(all); REP FACTOR 1
5698: */
5699: private void writeSQLDCROW(long rowNum, int sqlCode,
5700: String sqlState, String dbname, String sqlerrmc)
5701: throws DRDAProtocolException {
5702: writeSQLDCGRP(rowNum, sqlCode, sqlState, dbname, sqlerrmc);
5703: }
5704:
5705: /**
5706: * writeSQLDCGRP: SQL Diagnostics Condition Group Description
5707: *
5708: * SQLDCCODE; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5709: * SQLDCSTATE; DRDA TYPE FCS; ENVLID Ox30; Lengeh Override 5
5710: * SQLDCREASON; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5711: * SQLDCLINEN; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5712: * SQLDCROWN; DRDA TYPE FD; ENVLID 0x0E; Lengeh Override 31
5713: * SQLDCER01; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5714: * SQLDCER02; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5715: * SQLDCER03; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5716: * SQLDCER04; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5717: * SQLDCPART; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5718: * SQLDCPPOP; DRDA TYPE I4; ENVLID 0x02; Length Override 4
5719: * SQLDCMSGID; DRDA TYPE FCS; ENVLID 0x30; Length Override 10
5720: * SQLDCMDE; DRDA TYPE FCS; ENVLID 0x30; Length Override 8
5721: * SQLDCPMOD; DRDA TYPE FCS; ENVLID 0x30; Length Override 5
5722: * SQLDCRDB; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
5723: * SQLDCTOKS; DRDA TYPE N-RLO; ENVLID 0xF7; Length Override 0
5724: * SQLDCMSG_m; DRDA TYPE NVMC; ENVLID 0x3F; Length Override 32672
5725: * SQLDCMSG_S; DRDA TYPE NVCS; ENVLID 0x33; Length Override 32672
5726: * SQLDCCOLN_m; DRDA TYPE NVCM ; ENVLID 0x3F; Length Override 255
5727: * SQLDCCOLN_s; DRDA TYPE NVCS; ENVLID 0x33; Length Override 255
5728: * SQLDCCURN_m; DRDA TYPE NVCM; ENVLID 0x3F; Length Override 255
5729: * SQLDCCURN_s; DRDA TYPE NVCS; ENVLID 0x33; Length Override 255
5730: * SQLDCPNAM_m; DRDA TYPE NVCM; ENVLID 0x3F; Length Override 255
5731: * SQLDCPNAM_s; DRDA TYPE NVCS; ENVLID 0x33; Length Override 255
5732: * SQLDCXGRP; DRDA TYPE N-GDA; ENVLID 0xD3; Length Override 1
5733: */
5734: private void writeSQLDCGRP(long rowNum, int sqlCode,
5735: String sqlState, String dbname, String sqlerrmc)
5736: throws DRDAProtocolException {
5737: // SQLDCCODE
5738: writer.writeInt(sqlCode);
5739:
5740: // SQLDCSTATE
5741: writer.writeString(sqlState);
5742:
5743: writer.writeInt(0); // REASON_CODE
5744: writer.writeInt(0); // LINE_NUMBER
5745: writer.writeLong(rowNum); // ROW_NUMBER
5746:
5747: byte[] byteArray = new byte[1];
5748: writer.writeScalarPaddedBytes(byteArray, 47, (byte) 0);
5749:
5750: writer.writeShort(0); // CCC on Win does not take RDBNAME
5751: writer.writeByte(CodePoint.NULLDATA); // MESSAGE_TOKENS
5752: writer.writeLDString(sqlerrmc); // MESSAGE_TEXT
5753:
5754: writeVCMorVCS(null); // COLUMN_NAME
5755: writeVCMorVCS(null); // PARAMETER_NAME
5756: writeVCMorVCS(null); // EXTENDED_NAME
5757: writer.writeByte(CodePoint.NULLDATA); // SQLDCXGRP
5758: }
5759:
5760: /*
5761: * writeSQLDIAGCN: Write NULLDATA for now
5762: */
5763: private void writeSQLDIAGCN() throws DRDAProtocolException {
5764: writer.writeByte(CodePoint.NULLDATA);
5765: return;
5766: }
5767:
5768: /**
5769: * Write SQLDARD
5770: *
5771: * SQLDARD : FDOCA EARLY ARRAY
5772: * SQL Descriptor Area Row Description with SQL Communications Area
5773: *
5774: * FORMAT FOR SQLAM <= 6
5775: * SQLCARD; ROW LID 0x64; ELEMENT TAKEN 0(all); REP FACTOR 1
5776: * SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
5777: * SQLDAROW; ROW LID 0x60; ELEMENT TAKEN 0(all); REP FACTOR 0(all)
5778: *
5779: * FORMAT FOR SQLAM >= 7
5780: * SQLCARD; ROW LID 0x64; ELEMENT TAKEN 0(all); REP FACTOR 1
5781: * SQLDHROW; ROW LID 0xE0; ELEMENT TAKEN 0(all); REP FACTOR 1
5782: * SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
5783: *
5784: * @param stmt prepared statement
5785: *
5786: * @throws DRDAProtocolException
5787: * @throws SQLException
5788: */
5789: private void writeSQLDARD(DRDAStatement stmt, boolean rtnOutput,
5790: SQLException e) throws DRDAProtocolException, SQLException {
5791: PreparedStatement ps = stmt.getPreparedStatement();
5792: ResultSetMetaData rsmeta = ps.getMetaData();
5793: EngineParameterMetaData pmeta = stmt.getParameterMetaData();
5794: int numElems = 0;
5795: if (e == null || e instanceof SQLWarning) {
5796: if (rtnOutput && (rsmeta != null))
5797: numElems = rsmeta.getColumnCount();
5798: else if ((!rtnOutput) && (pmeta != null))
5799: numElems = pmeta.getParameterCount();
5800: }
5801:
5802: writer.createDssObject();
5803:
5804: // all went well we will just write a null SQLCA
5805: writer.startDdm(CodePoint.SQLDARD);
5806: writeSQLCAGRP(e, getSqlCode(getExceptionSeverity(e)), 0, 0);
5807:
5808: if (sqlamLevel >= MGRLVL_7)
5809: writeSQLDHROW(stmt);
5810:
5811: //SQLNUMROW
5812: if (SanityManager.DEBUG)
5813: trace("num Elements = " + numElems);
5814: writer.writeShort(numElems);
5815:
5816: for (int i = 0; i < numElems; i++)
5817: writeSQLDAGRP(rsmeta, pmeta, i, rtnOutput);
5818: writer.endDdmAndDss();
5819:
5820: }
5821:
5822: /**
5823: * Write QRYDSC - Query Answer Set Description
5824: *
5825: * @param stmt DRDAStatement we are working on
5826: * @param FDODSConly simply the FDODSC, without the wrap
5827: *
5828: * Instance Variables
5829: * SQLDTAGRP - required
5830: *
5831: * Only 84 columns can be sent in a single QRYDSC. If there are more columns
5832: * they must be sent in subsequent QRYDSC.
5833: * If the QRYDSC will not fit into the current block, as many columns as can
5834: * fit are sent and then the remaining are sent in the following blocks.
5835: *
5836: * @throws DRDAProtocolException
5837: * @throws SQLException
5838: */
5839: private void writeQRYDSC(DRDAStatement stmt, boolean FDODSConly)
5840: throws DRDAProtocolException, SQLException {
5841:
5842: ResultSet rs = null;
5843: ResultSetMetaData rsmeta = null;
5844: EngineParameterMetaData pmeta = null;
5845: if (!stmt.needsToSendParamData)
5846: rs = stmt.getResultSet();
5847: if (rs == null) // this is a CallableStatement, use parameter meta data
5848: pmeta = stmt.getParameterMetaData();
5849: else
5850: rsmeta = rs.getMetaData();
5851:
5852: int numCols = (rsmeta != null ? rsmeta.getColumnCount() : pmeta
5853: .getParameterCount());
5854: int numGroups = 1;
5855: int colStart = 1;
5856: int colEnd = numCols;
5857: int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize()
5858: : CodePoint.QRYBLKSZ_MAX;
5859:
5860: // check for remaining space in current query block
5861: // Need to mod with blksize so remaining doesn't go negative. 4868
5862: int remaining = blksize - (writer.getDSSLength() % blksize)
5863: - (3 + FdocaConstants.SQLCADTA_SQLDTARD_RLO_SIZE);
5864:
5865: // calcuate how may columns can be sent in the current query block
5866: int firstcols = remaining
5867: / FdocaConstants.SQLDTAGRP_COL_DSC_SIZE;
5868:
5869: // check if it doesn't all fit into the first block and
5870: // under FdocaConstants.MAX_VARS_IN_NGDA
5871: if (firstcols < numCols
5872: || numCols > FdocaConstants.MAX_VARS_IN_NGDA) {
5873: // we are limited to FdocaConstants.MAX_VARS_IN_NGDA
5874: if (firstcols > FdocaConstants.MAX_VARS_IN_NGDA) {
5875: if (SanityManager.DEBUG)
5876: SanityManager.ASSERT(
5877: numCols > FdocaConstants.MAX_VARS_IN_NGDA,
5878: "Number of columns " + numCols
5879: + " is less than MAX_VARS_IN_NGDA");
5880: numGroups = numCols / FdocaConstants.MAX_VARS_IN_NGDA;
5881: // some left over
5882: if (FdocaConstants.MAX_VARS_IN_NGDA * numGroups < numCols)
5883: numGroups++;
5884: colEnd = FdocaConstants.MAX_VARS_IN_NGDA;
5885: } else {
5886: colEnd = firstcols;
5887: numGroups += (numCols - firstcols)
5888: / FdocaConstants.MAX_VARS_IN_NGDA;
5889: if (FdocaConstants.MAX_VARS_IN_NGDA * numGroups < numCols)
5890: numGroups++;
5891: }
5892: }
5893:
5894: if (!FDODSConly) {
5895: writer.createDssObject();
5896: writer.startDdm(CodePoint.QRYDSC);
5897: }
5898:
5899: for (int i = 0; i < numGroups; i++) {
5900: writeSQLDTAGRP(stmt, rsmeta, pmeta, colStart, colEnd,
5901: (i == 0 ? true : false));
5902: colStart = colEnd + 1;
5903: // 4868 - Limit range to MAX_VARS_IN_NGDA (used to have extra col)
5904: colEnd = colEnd + FdocaConstants.MAX_VARS_IN_NGDA;
5905: if (colEnd > numCols)
5906: colEnd = numCols;
5907: }
5908: writer.writeBytes(FdocaConstants.SQLCADTA_SQLDTARD_RLO);
5909: if (!FDODSConly)
5910: writer.endDdmAndDss();
5911: }
5912:
5913: /**
5914: * Write SQLDTAGRP
5915: * SQLDAGRP : Late FDOCA GROUP
5916: * SQL Data Value Group Descriptor
5917: * LENGTH - length of the SQLDTAGRP
5918: * TRIPLET_TYPE - NGDA for first, CPT for following
5919: * ID - SQLDTAGRP_LID for first, NULL_LID for following
5920: * For each column
5921: * DRDA TYPE
5922: * LENGTH OVERRIDE
5923: * For numeric/decimal types
5924: * PRECISON
5925: * SCALE
5926: * otherwise
5927: * LENGTH or DISPLAY_WIDTH
5928: *
5929: * @param stmt drda statement
5930: * @param rsmeta resultset meta data
5931: * @param pmeta parameter meta data for CallableStatement
5932: * @param colStart starting column for group to send
5933: * @param colEnd end column to send
5934: * @param first is this the first group
5935: *
5936: * @throws DRDAProtocolException
5937: * @throws SQLException
5938: */
5939: private void writeSQLDTAGRP(DRDAStatement stmt,
5940: ResultSetMetaData rsmeta, EngineParameterMetaData pmeta,
5941: int colStart, int colEnd, boolean first)
5942: throws DRDAProtocolException, SQLException {
5943:
5944: int length = (FdocaConstants.SQLDTAGRP_COL_DSC_SIZE * ((colEnd + 1) - colStart)) + 3;
5945: writer.writeByte(length);
5946: if (first) {
5947:
5948: writer.writeByte(FdocaConstants.NGDA_TRIPLET_TYPE);
5949: writer.writeByte(FdocaConstants.SQLDTAGRP_LID);
5950: } else {
5951: //continued
5952: writer.writeByte(FdocaConstants.CPT_TRIPLET_TYPE);
5953: writer.writeByte(FdocaConstants.NULL_LID);
5954:
5955: }
5956:
5957: boolean hasRs = (rsmeta != null); // if don't have result, then we look at parameter meta
5958:
5959: for (int i = colStart; i <= colEnd; i++) {
5960: boolean nullable = (hasRs ? (rsmeta.isNullable(i) == rsmeta.columnNullable)
5961: : (pmeta.isNullable(i) == JDBC30Translation.PARAMETER_NULLABLE));
5962: int colType = (hasRs ? rsmeta.getColumnType(i) : pmeta
5963: .getParameterType(i));
5964: int[] outlen = { -1 };
5965: int drdaType = (hasRs ? FdocaConstants
5966: .mapJdbcTypeToDrdaType(colType, nullable, outlen)
5967: : stmt.getParamDRDAType(i));
5968:
5969: boolean isDecimal = ((drdaType | 1) == DRDAConstants.DRDA_TYPE_NDECIMAL);
5970: int precision = 0, scale = 0;
5971: if (hasRs) {
5972: precision = rsmeta.getPrecision(i);
5973: scale = rsmeta.getScale(i);
5974: stmt.setRsDRDAType(i, drdaType);
5975: stmt.setRsPrecision(i, precision);
5976: stmt.setRsScale(i, scale);
5977: }
5978:
5979: else if (isDecimal) {
5980: if (stmt.isOutputParam(i))
5981: ((CallableStatement) stmt.ps).registerOutParameter(
5982: i, Types.DECIMAL);
5983: precision = pmeta.getPrecision(i);
5984: scale = pmeta.getScale(i);
5985:
5986: }
5987:
5988: if (SanityManager.DEBUG)
5989: trace("jdbcType=" + colType + " \tdrdaType="
5990: + Integer.toHexString(drdaType));
5991:
5992: // Length or precision and scale for decimal values.
5993: writer.writeByte(drdaType);
5994: if (isDecimal) {
5995: writer.writeByte(precision);
5996: writer.writeByte(scale);
5997: } else if (outlen[0] != -1)
5998: writer.writeShort(outlen[0]);
5999: else if (hasRs)
6000: writer.writeShort(rsmeta.getColumnDisplaySize(i));
6001: else
6002: writer.writeShort(stmt.getParamLen(i));
6003: }
6004: }
6005:
6006: //pass PreparedStatement here so we can send correct holdability on the wire for jdk1.3 and higher
6007: //For jdk1.3, we provide hold cursor support through reflection.
6008: private void writeSQLDHROW(DRDAStatement stmt)
6009: throws DRDAProtocolException, SQLException {
6010: if (JVMInfo.JDK_ID < 2) //write null indicator for SQLDHROW because there is no holdability support prior to jdk1.3
6011: {
6012: writer.writeByte(CodePoint.NULLDATA);
6013: return;
6014: }
6015:
6016: writer.writeByte(0); // SQLDHROW INDICATOR
6017:
6018: //SQLDHOLD
6019: writer.writeShort(stmt.getResultSetHoldability());
6020:
6021: //SQLDRETURN
6022: writer.writeShort(0);
6023: //SQLDSCROLL
6024: writer.writeShort(0);
6025: //SQLDSENSITIVE
6026: writer.writeShort(0);
6027: //SQLDFCODE
6028: writer.writeShort(0);
6029: //SQLDKEYTYPE
6030: writer.writeShort(0);
6031: //SQLRDBNAME
6032: writer.writeShort(0); //CCC on Windows somehow does not take any dbname
6033: //SQLDSCHEMA
6034: writeVCMorVCS(null);
6035:
6036: }
6037:
6038: /**
6039: * Write QRYDTA - Query Answer Set Data
6040: * Contains some or all of the answer set data resulting from a query
6041: * If the client is not using rowset processing, this routine attempts
6042: * to pack as much data into the QRYDTA as it can. This may result in
6043: * splitting the last row across the block, in which case when the
6044: * client calls CNTQRY we will return the remainder of the row.
6045: *
6046: * Splitting a QRYDTA block is expensive, for several reasons:
6047: * - extra logic must be run, on both client and server side
6048: * - more network round-trips are involved
6049: * - the QRYDTA block which contains the continuation of the split
6050: * row is generally wasteful, since it contains the remainder of
6051: * the split row but no additional rows.
6052: * Since splitting is expensive, the server makes some attempt to
6053: * avoid it. Currently, the server's algorithm for this is to
6054: * compute the length of the current row, and to stop trying to pack
6055: * more rows into this buffer if another row of that length would
6056: * not fit. However, since rows can vary substantially in length,
6057: * this algorithm is often ineffective at preventing splits. For
6058: * example, if a short row near the end of the buffer is then
6059: * followed by a long row, that long row will be split. It is possible
6060: * to improve this algorithm substantially:
6061: * - instead of just using the length of the previous row as a guide
6062: * for whether to attempt packing another row in, use some sort of
6063: * overall average row size computed over multiple rows (e.g., all
6064: * the rows we've placed into this QRYDTA block, or all the rows
6065: * we've process for this result set)
6066: * - when we discover that the next row will not fit, rather than
6067: * splitting the row across QRYDTA blocks, if it is relatively
6068: * small, we could just hold the entire row in a buffer to place
6069: * it entirely into the next QRYDTA block, or reset the result
6070: * set cursor back one row to "unread" this row.
6071: * - when splitting a row across QRYDTA blocks, we tend to copy
6072: * data around multiple times. Careful coding could remove some
6073: * of these copies.
6074: * However, it is important not to over-complicate this code: it is
6075: * better to be correct than to be efficient, and there have been
6076: * several bugs in the split logic already.
6077: *
6078: * Instance Variables
6079: * Byte string
6080: *
6081: * @param stmt DRDA statement we are processing
6082: * @throws DRDAProtocolException
6083: * @throws SQLException
6084: */
6085: private void writeQRYDTA(DRDAStatement stmt)
6086: throws DRDAProtocolException, SQLException {
6087: boolean getMoreData = true;
6088: boolean sentExtData = false;
6089: int startLength = 0;
6090: writer.createDssObject();
6091:
6092: if (SanityManager.DEBUG)
6093: trace("Write QRYDTA");
6094: writer.startDdm(CodePoint.QRYDTA);
6095: // Check to see if there was leftover data from splitting
6096: // the previous QRYDTA for this result set. If there was, and
6097: // if we have now sent all of it, send any EXTDTA for that row
6098: // and increment the rowCount which we failed to increment in
6099: // writeFDODTA when we realized the row needed to be split.
6100: if (processLeftoverQRYDTA(stmt)) {
6101: if (stmt.getSplitQRYDTA() == null) {
6102: stmt.rowCount += 1;
6103: if (stmt.getExtDtaObjects() != null)
6104: writeEXTDTA(stmt);
6105: }
6106: return;
6107: }
6108:
6109: while (getMoreData) {
6110: sentExtData = false;
6111: getMoreData = writeFDODTA(stmt);
6112:
6113: if (stmt.getExtDtaObjects() != null
6114: && stmt.getSplitQRYDTA() == null) {
6115: writer.endDdmAndDss();
6116: writeEXTDTA(stmt);
6117: getMoreData = false;
6118: sentExtData = true;
6119: }
6120:
6121: // if we don't have enough room for a row of the
6122: // last row's size, don't try to cram it in.
6123: // It would get split up but it is not very efficient.
6124: if (getMoreData == true) {
6125: int endLength = writer.getDSSLength();
6126: int rowsize = endLength - startLength;
6127: if ((stmt.getBlksize() - endLength) < rowsize)
6128: getMoreData = false;
6129:
6130: startLength = endLength;
6131: }
6132:
6133: }
6134: // If we sent extDta we will rely on
6135: // writeScalarStream to end the dss with the proper chaining.
6136: // otherwise end it here.
6137: if (!sentExtData)
6138: writer.endDdmAndDss();
6139:
6140: if (!stmt.hasdata()) {
6141: final boolean qryclsOnLmtblkprc = appRequester
6142: .supportsQryclsimpForLmtblkprc();
6143: if (stmt.isRSCloseImplicit(qryclsOnLmtblkprc)) {
6144: stmt.rsClose();
6145: }
6146: }
6147: }
6148:
6149: /**
6150: * This routine places some data into the current QRYDTA block using
6151: * FDODTA (Formatted Data Object DaTA rules).
6152: *
6153: * There are 3 basic types of processing flow for this routine:
6154: * - In normal non-rowset, non-scrollable cursor flow, this routine
6155: * places a single row into the QRYDTA block and returns TRUE,
6156: * indicating that the caller can call us back to place another
6157: * row into the result set if he wishes. (The caller may need to
6158: * send Externalized Data, which would be a reason for him NOT to
6159: * place any more rows into the QRYDTA).
6160: * - In ROWSET processing, this routine places an entire ROWSET of
6161: * rows into the QRYDTA block and returns FALSE, indicating that
6162: * the QRYDTA block is full and should now be sent.
6163: * - In callable statement processing, this routine places the
6164: * results from the output parameters of the called procedure into
6165: * the QRYDTA block. This code path is really dramatically
6166: * different from the other two paths and shares only a very small
6167: * amount of common code in this routine.
6168: *
6169: * In all cases, it is possible that the data we wish to return may
6170: * not fit into the QRYDTA block, in which case we call splitQRYDTA
6171: * to split the data and remember the remainder data in the result set.
6172: * Splitting the data is relatively rare in the normal cursor case,
6173: * because our caller (writeQRYDTA) uses a coarse estimation
6174: * technique to avoid calling us if he thinks a split is likely.
6175: *
6176: * The overall structure of this routine is implemented as two
6177: * loops:
6178: * - the outer "do ... while ... " loop processes a ROWSET, one row
6179: * at a time. For non-ROWSET cursors, and for callable statements,
6180: * this loop executes only once.
6181: * - the inner "for ... i < numCols ..." loop processes each column
6182: * in the current row, or each output parmeter in the procedure.
6183: *
6184: * Most column data is written directly inline in the QRYDTA block.
6185: * Some data, however, is written as Externalized Data. This is
6186: * commonly used for Large Objects. In that case, an Externalized
6187: * Data Pointer is written into the QRYDTA block, and the actual
6188: * data flows in separate EXTDTA blocks which are returned
6189: * after this QRYDTA block.
6190: */
6191: private boolean writeFDODTA(DRDAStatement stmt)
6192: throws DRDAProtocolException, SQLException {
6193: boolean hasdata = false;
6194: int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize()
6195: : CodePoint.QRYBLKSZ_MAX;
6196: long rowCount = 0;
6197: ResultSet rs = null;
6198: boolean moreData = (stmt.getQryprctyp() == CodePoint.LMTBLKPRC);
6199: int numCols;
6200:
6201: if (!stmt.needsToSendParamData) {
6202: rs = stmt.getResultSet();
6203: }
6204:
6205: if (rs != null) {
6206: numCols = stmt.getNumRsCols();
6207: if (stmt.isScrollable())
6208: hasdata = positionCursor(stmt, rs);
6209: else
6210: hasdata = rs.next();
6211: } else // it's for a CallableStatement
6212: {
6213: hasdata = stmt.hasOutputParams();
6214: numCols = stmt.getNumParams();
6215: }
6216:
6217: do {
6218: if (!hasdata) {
6219: doneData(stmt, rs);
6220: moreData = false;
6221: return moreData;
6222: }
6223:
6224: // Send ResultSet warnings if there are any
6225: SQLWarning sqlw = (rs != null) ? rs.getWarnings() : null;
6226: if (rs != null) {
6227: rs.clearWarnings();
6228: }
6229:
6230: // for updatable, insensitive result sets we signal the
6231: // row updated condition to the client via a warning to be
6232: // popped by client onto its rowUpdated state, i.e. this
6233: // warning should not reach API level.
6234: if (rs != null && rs.rowUpdated()) {
6235: SQLWarning w = new SQLWarning("", SQLState.ROW_UPDATED,
6236: ExceptionSeverity.WARNING_SEVERITY);
6237: if (sqlw != null) {
6238: sqlw.setNextWarning(w);
6239: } else {
6240: sqlw = w;
6241: }
6242: }
6243: // Delete holes are manifest as a row consisting of a non-null
6244: // SQLCARD and a null data group. The SQLCARD has a warning
6245: // SQLSTATE of 02502
6246: if (rs != null && rs.rowDeleted()) {
6247: SQLWarning w = new SQLWarning("", SQLState.ROW_DELETED,
6248: ExceptionSeverity.WARNING_SEVERITY);
6249: if (sqlw != null) {
6250: sqlw.setNextWarning(w);
6251: } else {
6252: sqlw = w;
6253: }
6254: }
6255:
6256: if (sqlw == null)
6257: writeSQLCAGRP(nullSQLState, 0, -1, -1);
6258: else
6259: writeSQLCAGRP(sqlw, sqlw.getErrorCode(), 1, -1);
6260:
6261: // if we were asked not to return data, mark QRYDTA null; do not
6262: // return yet, need to make rowCount right
6263: // if the row has been deleted return QRYDTA null (delete hole)
6264: boolean noRetrieveRS = (rs != null && (!stmt.getQryrtndta() || rs
6265: .rowDeleted()));
6266: if (noRetrieveRS)
6267: writer.writeByte(0xFF); //QRYDTA null indicator: IS NULL
6268: else
6269: writer.writeByte(0); //QRYDTA null indicator: not null
6270:
6271: for (int i = 1; i <= numCols; i++) {
6272: if (noRetrieveRS)
6273: break;
6274:
6275: int drdaType;
6276: int ndrdaType;
6277: int precision;
6278: int scale;
6279:
6280: Object val = null;
6281: boolean valNull;
6282: if (rs != null) {
6283: drdaType = stmt.getRsDRDAType(i) & 0xff;
6284: precision = stmt.getRsPrecision(i);
6285: scale = stmt.getRsScale(i);
6286: ndrdaType = drdaType | 1;
6287:
6288: if (SanityManager.DEBUG)
6289: trace("!!drdaType = "
6290: + java.lang.Integer
6291: .toHexString(drdaType)
6292: + "precision = " + precision
6293: + " scale = " + scale);
6294: switch (ndrdaType) {
6295: case DRDAConstants.DRDA_TYPE_NLOBBYTES:
6296: case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
6297: EXTDTAInputStream extdtaStream = EXTDTAInputStream
6298: .getEXTDTAStream(rs, i, drdaType);
6299: writeFdocaVal(i, extdtaStream, drdaType,
6300: precision, scale, rs.wasNull(), stmt);
6301: break;
6302: case DRDAConstants.DRDA_TYPE_NINTEGER:
6303: int ival = rs.getInt(i);
6304: valNull = rs.wasNull();
6305: if (SanityManager.DEBUG)
6306: trace("====== writing int: " + ival
6307: + " is null: " + valNull);
6308: writeNullability(drdaType, valNull);
6309: if (!valNull)
6310: writer.writeInt(ival);
6311: break;
6312: case DRDAConstants.DRDA_TYPE_NSMALL:
6313: short sval = rs.getShort(i);
6314: valNull = rs.wasNull();
6315: if (SanityManager.DEBUG)
6316: trace("====== writing small: " + sval
6317: + " is null: " + valNull);
6318: writeNullability(drdaType, valNull);
6319: if (!valNull)
6320: writer.writeShort(sval);
6321: break;
6322: case DRDAConstants.DRDA_TYPE_NINTEGER8:
6323: long lval = rs.getLong(i);
6324: valNull = rs.wasNull();
6325: if (SanityManager.DEBUG)
6326: trace("====== writing long: " + lval
6327: + " is null: " + valNull);
6328: writeNullability(drdaType, valNull);
6329: if (!valNull)
6330: writer.writeLong(lval);
6331: break;
6332: case DRDAConstants.DRDA_TYPE_NFLOAT4:
6333: float fval = rs.getFloat(i);
6334: valNull = rs.wasNull();
6335: if (SanityManager.DEBUG)
6336: trace("====== writing float: " + fval
6337: + " is null: " + valNull);
6338: writeNullability(drdaType, valNull);
6339: if (!valNull)
6340: writer.writeFloat(fval);
6341: break;
6342: case DRDAConstants.DRDA_TYPE_NFLOAT8:
6343: double dval = rs.getDouble(i);
6344: valNull = rs.wasNull();
6345: if (SanityManager.DEBUG)
6346: trace("====== writing double: " + dval
6347: + " is null: " + valNull);
6348: writeNullability(drdaType, valNull);
6349: if (!valNull)
6350: writer.writeDouble(dval);
6351: break;
6352: case DRDAConstants.DRDA_TYPE_NCHAR:
6353: case DRDAConstants.DRDA_TYPE_NVARCHAR:
6354: case DRDAConstants.DRDA_TYPE_NVARMIX:
6355: case DRDAConstants.DRDA_TYPE_NLONG:
6356: case DRDAConstants.DRDA_TYPE_NLONGMIX:
6357: String valStr = rs.getString(i);
6358: if (SanityManager.DEBUG)
6359: trace("====== writing char/varchar/mix :"
6360: + valStr + ":");
6361: writeFdocaVal(i, valStr, drdaType, precision,
6362: scale, rs.wasNull(), stmt);
6363: break;
6364: default:
6365: writeFdocaVal(i, rs.getObject(i), drdaType,
6366: precision, scale, rs.wasNull(), stmt);
6367: }
6368: } else {
6369: drdaType = stmt.getParamDRDAType(i) & 0xff;
6370: precision = stmt.getParamPrecision(i);
6371: scale = stmt.getParamScale(i);
6372: ndrdaType = drdaType | 1;
6373:
6374: if (stmt.isOutputParam(i)) {
6375: if (SanityManager.DEBUG)
6376: trace("***getting Object " + i);
6377: val = ((CallableStatement) stmt.ps)
6378: .getObject(i);
6379: valNull = (val == null);
6380: writeFdocaVal(i, val, drdaType, precision,
6381: scale, valNull, stmt);
6382: } else
6383: writeFdocaVal(i, null, drdaType, precision,
6384: scale, true, stmt);
6385:
6386: }
6387: }
6388: // does all this fit in one QRYDTA
6389: if (writer.getDSSLength() > blksize) {
6390: splitQRYDTA(stmt, blksize);
6391: return false;
6392: }
6393:
6394: if (rs == null)
6395: return moreData;
6396:
6397: //get the next row
6398: rowCount++;
6399: if (rowCount < stmt.getQryrowset()) {
6400: hasdata = rs.next();
6401: }
6402: /*(1) scrollable we return at most a row set; OR (2) no retrieve data
6403: */
6404: else if (stmt.isScrollable() || noRetrieveRS)
6405: moreData = false;
6406:
6407: } while (hasdata && rowCount < stmt.getQryrowset());
6408:
6409: // add rowCount to statement row count
6410: // for non scrollable cursors
6411: if (!stmt.isScrollable())
6412: stmt.rowCount += rowCount;
6413:
6414: if (!hasdata) {
6415: doneData(stmt, rs);
6416: moreData = false;
6417: }
6418:
6419: if (!stmt.isScrollable())
6420: stmt.setHasdata(hasdata);
6421: return moreData;
6422: }
6423:
6424: /**
6425: * Split QRYDTA into blksize chunks
6426: *
6427: * This routine is called if the QRYDTA data will not fit. It writes
6428: * as much data as it can, then stores the remainder in the result
6429: * set. At some later point, when the client returns with a CNTQRY,
6430: * we will call processLeftoverQRYDTA to handle that data.
6431: *
6432: * The interaction between DRDAConnThread and DDMWriter is rather
6433: * complicated here. This routine gets called because DRDAConnThread
6434: * realizes that it has constructed a QRYDTA message which is too
6435: * large. At that point, we need to reclaim the "extra" data and
6436: * hold on to it. To aid us in that processing, DDMWriter provides
6437: * the routines getDSSLength, copyDSSDataToEnd, and truncateDSS.
6438: * For some additional detail on this complex sub-protocol, the
6439: * interested reader should study bug DERBY-491 and 492 at:
6440: * http://issues.apache.org/jira/browse/DERBY-491 and
6441: * http://issues.apache.org/jira/browse/DERBY-492
6442: *
6443: * @param stmt DRDA statment
6444: * @param blksize size of query block
6445: *
6446: * @throws SQLException
6447: * @throws DRDAProtocolException
6448: */
6449: private void splitQRYDTA(DRDAStatement stmt, int blksize)
6450: throws SQLException, DRDAProtocolException {
6451: // make copy of extra data
6452: byte[] temp = writer.copyDSSDataToEnd(blksize);
6453: // truncate to end of blocksize
6454: writer.truncateDSS(blksize);
6455: if (temp.length == 0)
6456: agentError("LMTBLKPRC violation: splitQRYDTA was "
6457: + "called to split a QRYDTA block, but the "
6458: + "entire row fit successfully into the "
6459: + "current block. Server rowsize computation "
6460: + "was probably incorrect (perhaps an off-by-"
6461: + "one bug?). QRYDTA blocksize: " + blksize);
6462: stmt.setSplitQRYDTA(temp);
6463: }
6464:
6465: /*
6466: * Process remainder data resulting from a split.
6467: *
6468: * This routine is called at the start of building each QRYDTA block.
6469: * Normally, it observes that there is no remainder data from the
6470: * previous QRYDTA block, and returns FALSE, indicating that there
6471: * was nothing to do.
6472: *
6473: * However, if it discovers that the previous QRYDTA block was split,
6474: * then it retrieves the remainder data from the result set, writes
6475: * as much of it as will fit into the QRYDTA block (hopefully all of
6476: * it will fit, but the row may be very long), and returns TRUE,
6477: * indicating that this QRYDTA block has been filled with remainder
6478: * data and should now be sent immediately.
6479: */
6480: private boolean processLeftoverQRYDTA(DRDAStatement stmt)
6481: throws SQLException, DRDAProtocolException {
6482: byte[] leftovers = stmt.getSplitQRYDTA();
6483: if (leftovers == null)
6484: return false;
6485: int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize()
6486: : CodePoint.QRYBLKSZ_MAX;
6487: blksize = blksize - 10; //DSS header + QRYDTA and length
6488: if (leftovers.length < blksize) {
6489: writer.writeBytes(leftovers, 0, leftovers.length);
6490: stmt.setSplitQRYDTA(null);
6491: } else {
6492: writer.writeBytes(leftovers, 0, blksize);
6493: byte[] newLeftovers = new byte[leftovers.length - blksize];
6494: for (int i = 0; i < newLeftovers.length; i++)
6495: newLeftovers[i] = leftovers[blksize + i];
6496: stmt.setSplitQRYDTA(newLeftovers);
6497: }
6498: // finish off query block and send
6499: writer.endDdmAndDss();
6500: return true;
6501: }
6502:
6503: /**
6504: * Done data
6505: * Send SQLCARD for the end of the data
6506: *
6507: * @param stmt DRDA statement
6508: * @param rs Result set
6509: * @throws DRDAProtocolException
6510: * @throws SQLException
6511: */
6512: private void doneData(DRDAStatement stmt, ResultSet rs)
6513: throws DRDAProtocolException, SQLException {
6514: if (SanityManager.DEBUG)
6515: trace("*****NO MORE DATA!!");
6516: int blksize = stmt.getBlksize() > 0 ? stmt.getBlksize()
6517: : CodePoint.QRYBLKSZ_MAX;
6518: if (rs != null) {
6519: if (stmt.isScrollable()) {
6520: //keep isAfterLast and isBeforeFirst to be able
6521: //to reposition after counting rows
6522: boolean isAfterLast = rs.isAfterLast();
6523: boolean isBeforeFirst = rs.isBeforeFirst();
6524:
6525: // for scrollable cursors - calculate the row count
6526: // since we may not have gone through each row
6527: rs.last();
6528: stmt.rowCount = rs.getRow();
6529:
6530: // reposition after last or before first
6531: if (isAfterLast) {
6532: rs.afterLast();
6533: }
6534: if (isBeforeFirst) {
6535: rs.beforeFirst();
6536: }
6537: } else // non-scrollable cursor
6538: {
6539: final boolean qryclsOnLmtblkprc = appRequester
6540: .supportsQryclsimpForLmtblkprc();
6541: if (stmt.isRSCloseImplicit(qryclsOnLmtblkprc)) {
6542: stmt.rsClose();
6543: stmt.rsSuspend();
6544: }
6545:
6546: }
6547: }
6548:
6549: // For scrollable cursor's QRYSCRAFT, when we reach here, DRDA spec says sqlstate
6550: // is 00000, sqlcode is not mentioned. But DB2 CLI code expects sqlcode to be 0.
6551: // We return sqlcode 0 in this case, as the DB2 server does.
6552: boolean isQRYSCRAFT = (stmt.getQryscrorn() == CodePoint.QRYSCRAFT);
6553:
6554: // Using sqlstate 00000 or 02000 for end of data.
6555: writeSQLCAGRP((isQRYSCRAFT ? eod00000 : eod02000),
6556: (isQRYSCRAFT ? 0 : 100), 0, stmt.rowCount);
6557:
6558: writer.writeByte(CodePoint.NULLDATA);
6559: // does all this fit in one QRYDTA
6560: if (writer.getDSSLength() > blksize) {
6561: splitQRYDTA(stmt, blksize);
6562: }
6563: }
6564:
6565: /**
6566: * Position cursor for insensitive scrollable cursors
6567: *
6568: * @param stmt DRDA statement
6569: * @param rs Result set
6570: */
6571: private boolean positionCursor(DRDAStatement stmt, ResultSet rs)
6572: throws SQLException, DRDAProtocolException {
6573: boolean retval = false;
6574: switch (stmt.getQryscrorn()) {
6575: case CodePoint.QRYSCRREL:
6576: int rows = (int) stmt.getQryrownbr();
6577: if ((rs.isAfterLast() && rows > 0)
6578: || (rs.isBeforeFirst() && rows < 0)) {
6579: retval = false;
6580: } else {
6581: retval = rs.relative(rows);
6582: }
6583: break;
6584: case CodePoint.QRYSCRABS:
6585: // JCC uses an absolute value of 0 which is not allowed in JDBC
6586: // We translate it into beforeFirst which seems to work.
6587: if (stmt.getQryrownbr() == 0) {
6588: rs.beforeFirst();
6589: retval = false;
6590: } else {
6591: retval = rs.absolute((int) stmt.getQryrownbr());
6592: }
6593: break;
6594: case CodePoint.QRYSCRAFT:
6595: rs.afterLast();
6596: retval = false;
6597: break;
6598: case CodePoint.QRYSCRBEF:
6599: rs.beforeFirst();
6600: retval = false;
6601: break;
6602: default:
6603: agentError("Invalid value for cursor orientation "
6604: + stmt.getQryscrorn());
6605: }
6606: return retval;
6607: }
6608:
6609: /**
6610: * Write SQLDAGRP
6611: * SQLDAGRP : EARLY FDOCA GROUP
6612: * SQL Data Area Group Description
6613: *
6614: * FORMAT FOR SQLAM <= 6
6615: * SQLPRECISION; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6616: * SQLSCALE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6617: * SQLLENGTH; DRDA TYPE I4; ENVLID 0x02; Length Override 4
6618: * SQLTYPE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6619: * SQLCCSID; DRDA TYPE FB; ENVLID 0x26; Length Override 2
6620: * SQLNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
6621: * SQLNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
6622: * SQLLABEL_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
6623: * SQLLABEL_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
6624: * SQLCOMMENTS_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 254
6625: * SQLCOMMENTS_m; DRDA TYPE VCS; ENVLID 0x32; Length Override 254
6626: *
6627: * FORMAT FOR SQLAM == 6
6628: * SQLPRECISION; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6629: * SQLSCALE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6630: * SQLLENGTH; DRDA TYPE I8; ENVLID 0x16; Length Override 8
6631: * SQLTYPE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6632: * SQLCCSID; DRDA TYPE FB; ENVLID 0x26; Length Override 2
6633: * SQLNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
6634: * SQLNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
6635: * SQLLABEL_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 30
6636: * SQLLABEL_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 30
6637: * SQLCOMMENTS_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 254
6638: * SQLCOMMENTS_m; DRDA TYPE VCS; ENVLID 0x32; Length Override 254
6639: * SQLUDTGRP; DRDA TYPE N-GDA; ENVLID 0x51; Length Override 0
6640: *
6641: * FORMAT FOR SQLAM >= 7
6642: * SQLPRECISION; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6643: * SQLSCALE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6644: * SQLLENGTH; DRDA TYPE I8; ENVLID 0x16; Length Override 8
6645: * SQLTYPE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6646: * SQLCCSID; DRDA TYPE FB; ENVLID 0x26; Length Override 2
6647: * SQLDOPTGRP; DRDA TYPE N-GDA; ENVLID 0xD2; Length Override 0
6648: *
6649: * @param rsmeta resultset meta data
6650: * @param pmeta parameter meta data
6651: * @param elemNum column number we are returning (in case of result set), or,
6652: * parameter number (in case of parameter)
6653: * @param rtnOutput whether this is for a result set
6654: *
6655: * @throws DRDAProtocolException
6656: * @throws SQLException
6657: */
6658: private void writeSQLDAGRP(ResultSetMetaData rsmeta,
6659: EngineParameterMetaData pmeta, int elemNum,
6660: boolean rtnOutput) throws DRDAProtocolException,
6661: SQLException {
6662: //jdbc uses offset of 1
6663:
6664: int jdbcElemNum = elemNum + 1;
6665: // length to be retreived as output parameter
6666: int[] outlen = { -1 };
6667:
6668: int elemType = rtnOutput ? rsmeta.getColumnType(jdbcElemNum)
6669: : pmeta.getParameterType(jdbcElemNum);
6670:
6671: int precision = rtnOutput ? rsmeta.getPrecision(jdbcElemNum)
6672: : pmeta.getPrecision(jdbcElemNum);
6673: if (precision > FdocaConstants.NUMERIC_MAX_PRECISION)
6674: precision = FdocaConstants.NUMERIC_MAX_PRECISION;
6675:
6676: // 2-byte precision
6677: writer.writeShort(precision);
6678: // 2-byte scale
6679: int scale = (rtnOutput ? rsmeta.getScale(jdbcElemNum) : pmeta
6680: .getScale(jdbcElemNum));
6681: writer.writeShort(scale);
6682:
6683: boolean nullable = rtnOutput ? (rsmeta.isNullable(jdbcElemNum) == ResultSetMetaData.columnNullable)
6684: : (pmeta.isNullable(jdbcElemNum) == JDBC30Translation.PARAMETER_NULLABLE);
6685:
6686: int sqlType = SQLTypes.mapJdbcTypeToDB2SqlType(elemType,
6687: nullable, outlen);
6688:
6689: if (outlen[0] == -1) //some types not set
6690: {
6691: switch (elemType) {
6692: case Types.DECIMAL:
6693: case Types.NUMERIC:
6694: scale = rtnOutput ? rsmeta.getScale(jdbcElemNum)
6695: : pmeta.getScale(jdbcElemNum);
6696: outlen[0] = ((precision << 8) | (scale << 0));
6697: if (SanityManager.DEBUG)
6698: trace("\n\nprecision =" + precision + " scale ="
6699: + scale);
6700: break;
6701: default:
6702: outlen[0] = Math.min(
6703: FdocaConstants.LONGVARCHAR_MAX_LEN,
6704: (rtnOutput ? rsmeta
6705: .getColumnDisplaySize(jdbcElemNum)
6706: : pmeta.getPrecision(jdbcElemNum)));
6707: }
6708: }
6709:
6710: switch (elemType) {
6711: case Types.BINARY:
6712: case Types.VARBINARY:
6713: case Types.LONGVARBINARY:
6714: case Types.BLOB: //for CLI describe to be correct
6715: case Types.CLOB:
6716: outlen[0] = (rtnOutput ? rsmeta.getPrecision(jdbcElemNum)
6717: : pmeta.getPrecision(jdbcElemNum));
6718: }
6719:
6720: if (SanityManager.DEBUG)
6721: trace("SQLDAGRP len ="
6722: + java.lang.Integer.toHexString(outlen[0])
6723: + "for type:" + elemType);
6724:
6725: // 8 or 4 byte sqllength
6726: if (sqlamLevel >= MGRLVL_6)
6727: writer.writeLong(outlen[0]);
6728: else
6729: writer.writeInt(outlen[0]);
6730:
6731: String typeName = rtnOutput ? rsmeta
6732: .getColumnTypeName(jdbcElemNum) : pmeta
6733: .getParameterTypeName(jdbcElemNum);
6734: if (SanityManager.DEBUG)
6735: trace("jdbcType =" + typeName + " sqlType =" + sqlType
6736: + "len =" + outlen[0]);
6737:
6738: writer.writeShort(sqlType);
6739:
6740: // CCSID
6741: // CCSID should be 0 for Binary Types.
6742:
6743: if (elemType == java.sql.Types.CHAR
6744: || elemType == java.sql.Types.VARCHAR
6745: || elemType == java.sql.Types.LONGVARCHAR
6746: || elemType == java.sql.Types.CLOB)
6747: writer.writeScalar2Bytes(1208);
6748: else
6749: writer.writeScalar2Bytes(0);
6750:
6751: if (sqlamLevel < MGRLVL_7) {
6752:
6753: //SQLName
6754: writeVCMorVCS(rtnOutput ? rsmeta.getColumnName(jdbcElemNum)
6755: : null);
6756: //SQLLabel
6757: writeVCMorVCS(null);
6758: //SQLComments
6759: writeVCMorVCS(null);
6760:
6761: if (sqlamLevel == MGRLVL_6)
6762: writeSQLUDTGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
6763: } else {
6764: writeSQLDOPTGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
6765: }
6766:
6767: }
6768:
6769: /**
6770: * Write variable character mixed byte or single byte
6771: * The preference is to write mixed byte if it is defined for the server,
6772: * since that is our default and we don't allow it to be changed, we always
6773: * write mixed byte.
6774: *
6775: * @param s string to write
6776: * @exception DRDAProtocolException
6777: */
6778: private void writeVCMorVCS(String s) throws DRDAProtocolException {
6779: //Write only VCM and 0 length for VCS
6780:
6781: if (s == null) {
6782: writer.writeShort(0);
6783: writer.writeShort(0);
6784: return;
6785: }
6786:
6787: // VCM
6788: writer.writeLDString(s);
6789: // VCS
6790: writer.writeShort(0);
6791: }
6792:
6793: private void writeSQLUDTGRP(ResultSetMetaData rsmeta,
6794: EngineParameterMetaData pmeta, int jdbcElemNum,
6795: boolean rtnOutput) throws DRDAProtocolException,
6796: SQLException {
6797: writer.writeByte(CodePoint.NULLDATA);
6798:
6799: }
6800:
6801: private void writeSQLDOPTGRP(ResultSetMetaData rsmeta,
6802: EngineParameterMetaData pmeta, int jdbcElemNum,
6803: boolean rtnOutput) throws DRDAProtocolException,
6804: SQLException {
6805:
6806: writer.writeByte(0);
6807: //SQLUNAMED
6808: writer.writeShort(0);
6809: //SQLName
6810: writeVCMorVCS(rtnOutput ? rsmeta.getColumnName(jdbcElemNum)
6811: : null);
6812: //SQLLabel
6813: writeVCMorVCS(null);
6814: //SQLComments
6815: writeVCMorVCS(null);
6816: //SQLDUDTGRP
6817: writeSQLUDTGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
6818: //SQLDXGRP
6819: writeSQLDXGRP(rsmeta, pmeta, jdbcElemNum, rtnOutput);
6820: }
6821:
6822: private void writeSQLDXGRP(ResultSetMetaData rsmeta,
6823: EngineParameterMetaData pmeta, int jdbcElemNum,
6824: boolean rtnOutput) throws DRDAProtocolException,
6825: SQLException {
6826: // Null indicator indicates we have data
6827: writer.writeByte(0);
6828: // SQLXKEYMEM; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6829: // Hard to get primary key info. Send 0 for now
6830: writer.writeShort(0);
6831: // SQLXUPDATEABLE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6832: writer.writeShort(rtnOutput ? rsmeta.isWritable(jdbcElemNum)
6833: : false);
6834:
6835: // SQLXGENERATED; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6836: if (rtnOutput && rsmeta.isAutoIncrement(jdbcElemNum))
6837: writer.writeShort(2);
6838: else
6839: writer.writeShort(0);
6840:
6841: // SQLXPARMMODE; DRDA TYPE I2; ENVLID 0x04; Length Override 2
6842: if (pmeta != null && !rtnOutput) {
6843: int mode = pmeta.getParameterMode(jdbcElemNum);
6844: if (mode == JDBC30Translation.PARAMETER_MODE_UNKNOWN) {
6845: // For old style callable statements. We assume in/out if it
6846: // is an output parameter.
6847: int type = DRDAStatement
6848: .getOutputParameterTypeFromClassName(pmeta
6849: .getParameterClassName(jdbcElemNum));
6850: if (type != DRDAStatement.NOT_OUTPUT_PARAM)
6851: mode = JDBC30Translation.PARAMETER_MODE_IN_OUT;
6852: }
6853: writer.writeShort(mode);
6854: } else {
6855: writer.writeShort(0);
6856: }
6857:
6858: // SQLXRDBNAM; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
6859: // JCC uses this as the catalog name so we will send null.
6860: writer.writeShort(0);
6861:
6862: // SQLXCORNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
6863: // SQLXCORNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
6864: writeVCMorVCS(null);
6865:
6866: // SQLXBASENAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
6867: // SQLXBASENAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
6868: writeVCMorVCS(rtnOutput ? rsmeta.getTableName(jdbcElemNum)
6869: : null);
6870:
6871: // SQLXSCHEMA_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
6872: // SQLXSCHEMA_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
6873: writeVCMorVCS(rtnOutput ? rsmeta.getSchemaName(jdbcElemNum)
6874: : null);
6875:
6876: // SQLXNAME_m; DRDA TYPE VCM; ENVLID 0x3E; Length Override 255
6877: // SQLXNAME_s; DRDA TYPE VCS; ENVLID 0x32; Length Override 255
6878: writeVCMorVCS(rtnOutput ? rsmeta.getColumnName(jdbcElemNum)
6879: : null);
6880:
6881: }
6882:
6883: /**
6884: * Write Fdoca Value to client
6885: * @param index Index of column being returned
6886: * @param val Value to write to client
6887: * @param drdaType FD:OCA DRDA Type from FdocaConstants
6888: * @param precision Precision
6889: * @param stmt Statement being processed
6890: *
6891: * @exception DRDAProtocolException
6892: * @exception SQLException
6893: *
6894: * @see FdocaConstants
6895: */
6896:
6897: protected void writeFdocaVal(int index, Object val, int drdaType,
6898: int precision, int scale, boolean valNull,
6899:
6900: DRDAStatement stmt) throws DRDAProtocolException,
6901: SQLException {
6902: writeNullability(drdaType, valNull);
6903:
6904: if (!valNull) {
6905: int ndrdaType = drdaType | 1;
6906: long valLength = 0;
6907: switch (ndrdaType) {
6908: case DRDAConstants.DRDA_TYPE_NSMALL:
6909: // DB2 does not have a BOOLEAN java.sql.bit type,
6910: // so we need to send it as a small
6911: if (val instanceof Boolean) {
6912: writer.writeShort(((Boolean) val).booleanValue());
6913: } else if (val instanceof Short)
6914: writer.writeShort(((Short) val).shortValue());
6915: else if (val instanceof Byte)
6916: writer.writeShort(((Byte) val).byteValue());
6917: else
6918: writer.writeShort(((Integer) val).shortValue());
6919: break;
6920: case DRDAConstants.DRDA_TYPE_NINTEGER:
6921: writer.writeInt(((Integer) val).intValue());
6922: break;
6923: case DRDAConstants.DRDA_TYPE_NINTEGER8:
6924: writer.writeLong(((Long) val).longValue());
6925: break;
6926: case DRDAConstants.DRDA_TYPE_NFLOAT4:
6927: writer.writeFloat(((Float) val).floatValue());
6928: break;
6929: case DRDAConstants.DRDA_TYPE_NFLOAT8:
6930: writer.writeDouble(((Double) val).doubleValue());
6931: break;
6932: case DRDAConstants.DRDA_TYPE_NDECIMAL:
6933: if (precision == 0)
6934: precision = FdocaConstants.NUMERIC_DEFAULT_PRECISION;
6935: BigDecimal bd = (java.math.BigDecimal) val;
6936: writer.writeBigDecimal(bd, precision, scale);
6937: break;
6938: case DRDAConstants.DRDA_TYPE_NDATE:
6939: writer.writeString(((java.sql.Date) val).toString());
6940: break;
6941: case DRDAConstants.DRDA_TYPE_NTIME:
6942: writer.writeString(((java.sql.Time) val).toString());
6943: break;
6944: case DRDAConstants.DRDA_TYPE_NTIMESTAMP:
6945: // we need to send it in a slightly different format, and pad it
6946: // up to or truncate it into 26 chars
6947: String ts1 = ((java.sql.Timestamp) val).toString();
6948: String ts2 = ts1.replace(' ', '-').replace(':', '.');
6949: int tsLen = ts2.length();
6950: if (tsLen < 26) {
6951: for (int i = 0; i < 26 - tsLen; i++)
6952: ts2 += "0";
6953: } else if (tsLen > 26)
6954: ts2 = ts2.substring(0, 26);
6955: writer.writeString(ts2);
6956: break;
6957: case DRDAConstants.DRDA_TYPE_NCHAR:
6958: writer.writeString(((String) val).toString());
6959: break;
6960: case DRDAConstants.DRDA_TYPE_NVARCHAR:
6961: case DRDAConstants.DRDA_TYPE_NVARMIX:
6962: case DRDAConstants.DRDA_TYPE_NLONG:
6963: case DRDAConstants.DRDA_TYPE_NLONGMIX:
6964: //WriteLDString and generate warning if truncated
6965: // which will be picked up by checkWarning()
6966: writer.writeLDString(val.toString(), index);
6967: break;
6968: case DRDAConstants.DRDA_TYPE_NLOBBYTES:
6969: case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
6970:
6971: // do not send EXTDTA for lob of length 0, beetle 5967
6972: if (!((EXTDTAInputStream) val).isEmptyStream()) {
6973: stmt.addExtDtaObject(val, index);
6974:
6975: //indicate externalized and size is unknown.
6976: writer.writeExtendedLength(0x8000);
6977:
6978: } else {
6979: writer.writeExtendedLength(0);
6980:
6981: }
6982:
6983: break;
6984:
6985: case DRDAConstants.DRDA_TYPE_NFIXBYTE:
6986: writer.writeBytes((byte[]) val);
6987: break;
6988: case DRDAConstants.DRDA_TYPE_NVARBYTE:
6989: case DRDAConstants.DRDA_TYPE_NLONGVARBYTE:
6990: writer.writeLDBytes((byte[]) val, index);
6991: break;
6992: default:
6993: if (SanityManager.DEBUG)
6994: trace("ndrdaType is: " + ndrdaType);
6995: writer.writeLDString(val.toString(), index);
6996: }
6997: }
6998: }
6999:
7000: /**
7001: * write nullability if this is a nullable drdatype and FDOCA null
7002: * value if appropriate
7003: * @param drdaType FDOCA type
7004: * @param valNull true if this is a null value. False otherwise
7005: *
7006: **/
7007: private void writeNullability(int drdaType, boolean valNull) {
7008: if (FdocaConstants.isNullable(drdaType)) {
7009: if (valNull)
7010: writer.writeByte(FdocaConstants.NULL_DATA);
7011: else {
7012: writer.writeByte(FdocaConstants.INDICATOR_NULLABLE);
7013: }
7014: }
7015:
7016: }
7017:
7018: /**
7019: * Methods to keep track of required codepoints
7020: */
7021: /**
7022: * Copy a list of required code points to template for checking
7023: *
7024: * @param req list of required codepoints
7025: */
7026: private void copyToRequired(int[] req) {
7027: currentRequiredLength = req.length;
7028: if (currentRequiredLength > required.length)
7029: required = new int[currentRequiredLength];
7030: for (int i = 0; i < req.length; i++)
7031: required[i] = req[i];
7032: }
7033:
7034: /**
7035: * Remove codepoint from required list
7036: *
7037: * @param codePoint - code point to be removed
7038: */
7039: private void removeFromRequired(int codePoint) {
7040: for (int i = 0; i < currentRequiredLength; i++)
7041: if (required[i] == codePoint)
7042: required[i] = 0;
7043:
7044: }
7045:
7046: /**
7047: * Check whether we have seen all the required code points
7048: *
7049: * @param codePoint code point for which list of code points is required
7050: */
7051: private void checkRequired(int codePoint)
7052: throws DRDAProtocolException {
7053: int firstMissing = 0;
7054: for (int i = 0; i < currentRequiredLength; i++) {
7055: if (required[i] != 0) {
7056: firstMissing = required[i];
7057: break;
7058: }
7059: }
7060: if (firstMissing != 0)
7061: missingCodePoint(firstMissing);
7062: }
7063:
7064: /**
7065: * Error routines
7066: */
7067: /**
7068: * Seen too many of this code point
7069: *
7070: * @param codePoint code point which has been duplicated
7071: *
7072: * @exception DRDAProtocolException
7073: */
7074: private void tooMany(int codePoint) throws DRDAProtocolException {
7075: throwSyntaxrm(CodePoint.SYNERRCD_TOO_MANY, codePoint);
7076: }
7077:
7078: /**
7079: * Object too big
7080: *
7081: * @param codePoint code point with too big object
7082: * @exception DRDAProtocolException
7083: */
7084: private void tooBig(int codePoint) throws DRDAProtocolException {
7085: throwSyntaxrm(CodePoint.SYNERRCD_TOO_BIG, codePoint);
7086: }
7087:
7088: /**
7089: * Object length not allowed
7090: *
7091: * @param codePoint code point with bad object length
7092: * @exception DRDAProtocolException
7093: */
7094: private void badObjectLength(int codePoint)
7095: throws DRDAProtocolException {
7096: throwSyntaxrm(CodePoint.SYNERRCD_OBJ_LEN_NOT_ALLOWED, codePoint);
7097: }
7098:
7099: /**
7100: * RDB not found
7101: *
7102: * @param rdbnam name of database
7103: * @exception DRDAProtocolException
7104: */
7105: private void rdbNotFound(String rdbnam)
7106: throws DRDAProtocolException {
7107: Object[] oa = { rdbnam };
7108: throw new DRDAProtocolException(
7109: DRDAProtocolException.DRDA_Proto_RDBNFNRM, this , 0,
7110: DRDAProtocolException.NO_ASSOC_ERRCD, oa);
7111: }
7112:
7113: /**
7114: * Invalid value for this code point
7115: *
7116: * @param codePoint code point value
7117: * @exception DRDAProtocolException
7118: */
7119: private void invalidValue(int codePoint)
7120: throws DRDAProtocolException {
7121: throwSyntaxrm(CodePoint.SYNERRCD_REQ_VAL_NOT_FOUND, codePoint);
7122: }
7123:
7124: /**
7125: * Invalid codepoint for this command
7126: *
7127: * @param codePoint code point value
7128: *
7129: * @exception DRDAProtocolException
7130: */
7131: protected void invalidCodePoint(int codePoint)
7132: throws DRDAProtocolException {
7133: throwSyntaxrm(CodePoint.SYNERRCD_INVALID_CP_FOR_CMD, codePoint);
7134: }
7135:
7136: /**
7137: * Don't support this code point
7138: *
7139: * @param codePoint code point value
7140: * @exception DRDAProtocolException
7141: */
7142: protected void codePointNotSupported(int codePoint)
7143: throws DRDAProtocolException {
7144: throw new DRDAProtocolException(
7145: DRDAProtocolException.DRDA_Proto_CMDNSPRM, this ,
7146: codePoint, DRDAProtocolException.NO_ASSOC_ERRCD);
7147: }
7148:
7149: /**
7150: * Don't support this value
7151: *
7152: * @param codePoint code point value
7153: * @exception DRDAProtocolException
7154: */
7155: private void valueNotSupported(int codePoint)
7156: throws DRDAProtocolException {
7157: throw new DRDAProtocolException(
7158: DRDAProtocolException.DRDA_Proto_VALNSPRM, this ,
7159: codePoint, DRDAProtocolException.NO_ASSOC_ERRCD);
7160: }
7161:
7162: /**
7163: * Verify that the code point is the required code point
7164: *
7165: * @param codePoint code point we have
7166: * @param reqCodePoint code point required at this time
7167: *
7168: * @exception DRDAProtocolException
7169: */
7170: private void verifyRequiredObject(int codePoint, int reqCodePoint)
7171: throws DRDAProtocolException {
7172: if (codePoint != reqCodePoint) {
7173: throwSyntaxrm(CodePoint.SYNERRCD_REQ_OBJ_NOT_FOUND,
7174: codePoint);
7175: }
7176: }
7177:
7178: /**
7179: * Verify that the code point is in the right order
7180: *
7181: * @param codePoint code point we have
7182: * @param reqCodePoint code point required at this time
7183: *
7184: * @exception DRDAProtocolException
7185: */
7186: private void verifyInOrderACCSEC_SECCHK(int codePoint,
7187: int reqCodePoint) throws DRDAProtocolException {
7188: if (codePoint != reqCodePoint) {
7189: throw new DRDAProtocolException(
7190: DRDAProtocolException.DRDA_Proto_PRCCNVRM, this ,
7191: codePoint,
7192: CodePoint.PRCCNVCD_ACCSEC_SECCHK_WRONG_STATE);
7193: }
7194: }
7195:
7196: /**
7197: * Database name given under code point doesn't match previous database names
7198: *
7199: * @param codePoint codepoint where the mismatch occurred
7200: *
7201: * @exception DRDAProtocolException
7202: */
7203: private void rdbnamMismatch(int codePoint)
7204: throws DRDAProtocolException {
7205: throw new DRDAProtocolException(
7206: DRDAProtocolException.DRDA_Proto_PRCCNVRM, this ,
7207: codePoint, CodePoint.PRCCNVCD_RDBNAM_MISMATCH);
7208: }
7209:
7210: /**
7211: * Close the current session
7212: */
7213: private void closeSession() {
7214: if (session == null)
7215: return;
7216: server.removeFromSessionTable(session.connNum);
7217: try {
7218: session.close();
7219: } catch (SQLException se) {
7220: // If something went wrong closing down the session.
7221: // Print an error to the console and close this
7222: //thread. (6013)
7223: sendUnexpectedException(se);
7224: close();
7225: } finally {
7226: session = null;
7227: database = null;
7228: appRequester = null;
7229: sockis = null;
7230: sockos = null;
7231: databaseAccessException = null;
7232: }
7233: }
7234:
7235: /**
7236: * Handle Exceptions - write error protocol if appropriate and close session
7237: * or thread as appropriate
7238: */
7239: private void handleException(Exception e) {
7240: try {
7241: if (e instanceof DRDAProtocolException) {
7242: // protocol error - write error message
7243: sendProtocolException((DRDAProtocolException) e);
7244: } else {
7245: // something unexpected happened
7246: sendUnexpectedException(e);
7247: server.consoleExceptionPrintTrace(e);
7248: }
7249: } finally {
7250: // always close the session and stop the thread after handling
7251: // these exceptions
7252: closeSession();
7253: close();
7254: }
7255: }
7256:
7257: /**
7258: * Notice the client about a protocol error.
7259: *
7260: * @param de <code>DRDAProtocolException</code> to be sent
7261: */
7262: private void sendProtocolException(DRDAProtocolException de) {
7263: String dbname = null;
7264: if (database != null) {
7265: dbname = database.dbName;
7266: }
7267:
7268: try {
7269: println2Log(dbname, session.drdaID, de.getMessage());
7270: server.consoleExceptionPrintTrace(de);
7271: reader.clearBuffer();
7272: de.write(writer);
7273: finalizeChain();
7274: } catch (DRDAProtocolException ioe) {
7275: // There may be an IO exception in the write.
7276: println2Log(dbname, session.drdaID, de.getMessage());
7277: server.consoleExceptionPrintTrace(ioe);
7278: }
7279: }
7280:
7281: /**
7282: * Send unpexpected error to the client
7283: * @param e Exception to be sent
7284: */
7285: private void sendUnexpectedException(Exception e) {
7286:
7287: DRDAProtocolException unExpDe;
7288: String dbname = null;
7289: try {
7290: if (database != null)
7291: dbname = database.dbName;
7292: println2Log(dbname, session.drdaID, e.getMessage());
7293: server.consoleExceptionPrintTrace(e);
7294: unExpDe = DRDAProtocolException.newAgentError(this ,
7295: CodePoint.SVRCOD_PRMDMG, dbname, e.getMessage());
7296:
7297: reader.clearBuffer();
7298: unExpDe.write(writer);
7299: finalizeChain();
7300: } catch (DRDAProtocolException nde) {
7301: // we can't tell the client, but we tried.
7302: }
7303:
7304: }
7305:
7306: /**
7307: * Test if DRDA connection thread is closed
7308: *
7309: * @return true if close; false otherwise
7310: */
7311: private boolean closed() {
7312: synchronized (closeSync) {
7313: return close;
7314: }
7315: }
7316:
7317: /**
7318: * Get whether connections are logged
7319: *
7320: * @return true if connections are being logged; false otherwise
7321: */
7322: private boolean getLogConnections() {
7323: synchronized (logConnectionsSync) {
7324: return logConnections;
7325: }
7326: }
7327:
7328: /**
7329: * Get time slice value for length of time to work on a session
7330: *
7331: * @return time slice
7332: */
7333: private long getTimeSlice() {
7334: synchronized (timeSliceSync) {
7335: return timeSlice;
7336: }
7337: }
7338:
7339: /**
7340: * Send string to console
7341: *
7342: * @param value - value to print on console
7343: */
7344: protected void trace(String value) {
7345: if (SanityManager.DEBUG && server.debugOutput == true)
7346: server.consoleMessage(value);
7347: }
7348:
7349: /***
7350: * Show runtime memory
7351: *
7352: ***/
7353: public static void showmem() {
7354: Runtime rt = null;
7355: Date d = null;
7356: rt = Runtime.getRuntime();
7357: rt.gc();
7358: d = new Date();
7359: System.out.println("total memory: " + rt.totalMemory()
7360: + " free: " + rt.freeMemory() + " " + d.toString());
7361:
7362: }
7363:
7364: /**
7365: * convert byte array to a Hex string
7366: *
7367: * @param buf buffer to convert
7368: * @return hex string representation of byte array
7369: */
7370: private String convertToHexString(byte[] buf) {
7371: StringBuffer str = new StringBuffer();
7372: str.append("0x");
7373: String val;
7374: int byteVal;
7375: for (int i = 0; i < buf.length; i++) {
7376: byteVal = buf[i] & 0xff;
7377: val = Integer.toHexString(byteVal);
7378: if (val.length() < 2)
7379: str.append("0");
7380: str.append(val);
7381: }
7382: return str.toString();
7383: }
7384:
7385: /**
7386: * check that the given typdefnam is acceptable
7387: *
7388: * @param typdefnam
7389: *
7390: * @exception DRDAProtocolException
7391: */
7392: private void checkValidTypDefNam(String typdefnam)
7393: throws DRDAProtocolException {
7394: if (typdefnam.equals("QTDSQL370"))
7395: return;
7396: if (typdefnam.equals("QTDSQL400"))
7397: return;
7398: if (typdefnam.equals("QTDSQLX86"))
7399: return;
7400: if (typdefnam.equals("QTDSQLASC"))
7401: return;
7402: if (typdefnam.equals("QTDSQLVAX"))
7403: return;
7404: if (typdefnam.equals("QTDSQLJVM"))
7405: return;
7406: invalidValue(CodePoint.TYPDEFNAM);
7407: }
7408:
7409: /**
7410: * Check that the length is equal to the required length for this codepoint
7411: *
7412: * @param codepoint codepoint we are checking
7413: * @param reqlen required length
7414: *
7415: * @exception DRDAProtocolException
7416: */
7417: private void checkLength(int codepoint, int reqlen)
7418: throws DRDAProtocolException {
7419: long len = reader.getDdmLength();
7420: if (len < reqlen)
7421: badObjectLength(codepoint);
7422: else if (len > reqlen)
7423: tooBig(codepoint);
7424: }
7425:
7426: /**
7427: * Read and check a boolean value
7428: *
7429: * @param codepoint codePoint to be used in error reporting
7430: * @return true or false depending on boolean value read
7431: *
7432: * @exception DRDAProtocolException
7433: */
7434: private boolean readBoolean(int codepoint)
7435: throws DRDAProtocolException {
7436: checkLength(codepoint, 1);
7437: byte val = reader.readByte();
7438: if (val == CodePoint.TRUE)
7439: return true;
7440: else if (val == CodePoint.FALSE)
7441: return false;
7442: else
7443: invalidValue(codepoint);
7444: return false; //to shut the compiler up
7445: }
7446:
7447: /**
7448: * Add a database to the current session
7449: *
7450: */
7451: private void addDatabase(String dbname) {
7452: Database db;
7453: if (appRequester.isXARequester()) {
7454: db = new XADatabase(dbname);
7455: } else
7456: db = new Database(dbname);
7457: session.addDatabase(db);
7458: session.database = db;
7459: database = db;
7460: }
7461:
7462: /**
7463: * Set the current database
7464: *
7465: * @param codePoint codepoint we are processing
7466: *
7467: * @exception DRDAProtocolException
7468: */
7469: private void setDatabase(int codePoint)
7470: throws DRDAProtocolException {
7471: String rdbnam = parseRDBNAM();
7472: // using same database so we are done
7473: if (database != null && database.dbName.equals(rdbnam))
7474: return;
7475: Database d = session.getDatabase(rdbnam);
7476: if (d == null)
7477: rdbnamMismatch(codePoint);
7478: else
7479: database = d;
7480: session.database = d;
7481: }
7482:
7483: /**
7484: * Write ENDUOWRM
7485: * Instance Variables
7486: * SVCOD - severity code - WARNING - required
7487: * UOWDSP - Unit of Work Disposition - required
7488: * RDBNAM - Relational Database name - optional
7489: * SRVDGN - Server Diagnostics information - optional
7490: *
7491: * @param opType - operation type 1 - commit, 2 -rollback
7492: */
7493: private void writeENDUOWRM(int opType) {
7494: writer.createDssReply();
7495: writer.startDdm(CodePoint.ENDUOWRM);
7496: writer.writeScalar2Bytes(CodePoint.SVRCOD,
7497: CodePoint.SVRCOD_WARNING);
7498: writer.writeScalar1Byte(CodePoint.UOWDSP, opType);
7499: writer.endDdmAndDss();
7500: }
7501:
7502: void writeEXTDTA(DRDAStatement stmt) throws SQLException,
7503: DRDAProtocolException {
7504:
7505: ArrayList extdtaValues = stmt.getExtDtaObjects();
7506: // build the EXTDTA data, if necessary
7507: if (extdtaValues == null)
7508: return;
7509: boolean chainFlag, chainedWithSameCorrelator;
7510: boolean writeNullByte = false;
7511:
7512: for (int i = 0; i < extdtaValues.size(); i++) {
7513: // is this the last EXTDTA to be built?
7514: if (i != extdtaValues.size() - 1) { // no
7515: chainFlag = true;
7516: chainedWithSameCorrelator = true;
7517: } else { // yes
7518: chainFlag = false; //last blob DSS stream itself is NOT chained with the NEXT DSS
7519: chainedWithSameCorrelator = false;
7520: }
7521:
7522: if (sqlamLevel >= MGRLVL_7)
7523: if (stmt.isExtDtaValueNullable(i))
7524: writeNullByte = true;
7525:
7526: Object o = extdtaValues.get(i);
7527: if (o instanceof EXTDTAInputStream) {
7528: EXTDTAInputStream stream = (EXTDTAInputStream) o;
7529: try {
7530: writer.writeScalarStream(chainedWithSameCorrelator,
7531: CodePoint.EXTDTA, stream, writeNullByte);
7532:
7533: } finally {
7534: // close the stream when done
7535: closeStream(stream);
7536: }
7537:
7538: }
7539: }
7540: // reset extdtaValues after sending
7541: stmt.clearExtDtaObjects();
7542:
7543: }
7544:
7545: /**
7546: * Check SQLWarning and write SQLCARD as needed.
7547: *
7548: * @param conn connection to check
7549: * @param stmt statement to check
7550: * @param rs result set to check
7551: * @param updateCount update count to include in SQLCARD
7552: * @param alwaysSend whether always send SQLCARD regardless of
7553: * the existance of warnings
7554: * @param sendWarn whether to send any warnings or not.
7555: *
7556: * @exception DRDAProtocolException
7557: */
7558: private void checkWarning(Connection conn, Statement stmt,
7559: ResultSet rs, int updateCount, boolean alwaysSend,
7560: boolean sendWarn) throws DRDAProtocolException,
7561: SQLException {
7562: // instead of writing a chain of sql warning, we send the first one, this is
7563: // jcc/db2 limitation, see beetle 4629
7564: SQLWarning warning = null;
7565: SQLWarning reportWarning = null;
7566: try {
7567: if (stmt != null) {
7568: warning = stmt.getWarnings();
7569: if (warning != null) {
7570: stmt.clearWarnings();
7571: reportWarning = warning;
7572: }
7573: }
7574: if (rs != null) {
7575: warning = rs.getWarnings();
7576: if (warning != null) {
7577: rs.clearWarnings();
7578: if (reportWarning == null)
7579: reportWarning = warning;
7580: }
7581: }
7582: if (conn != null) {
7583: warning = conn.getWarnings();
7584: if (warning != null) {
7585: conn.clearWarnings();
7586: if (reportWarning == null)
7587: reportWarning = warning;
7588: }
7589: }
7590:
7591: } catch (SQLException se) {
7592: if (SanityManager.DEBUG)
7593: trace("got SQLException while trying to get warnings.");
7594: }
7595:
7596: if ((alwaysSend || reportWarning != null) && sendWarn)
7597: writeSQLCARDs(reportWarning, updateCount);
7598: }
7599:
7600: protected String buildRuntimeInfo(String indent,
7601: LocalizedResource localLangUtil) {
7602: String s = "";
7603: if (session == null)
7604: return s;
7605: else
7606: s += session.buildRuntimeInfo("", localLangUtil);
7607: s += "\n";
7608: return s;
7609: }
7610:
7611: /**
7612: * Finalize the current DSS chain and send it if
7613: * needed.
7614: */
7615: private void finalizeChain() throws DRDAProtocolException {
7616:
7617: writer.finalizeChain(reader.getCurrChainState(),
7618: getOutputStream());
7619: return;
7620:
7621: }
7622:
7623: /**
7624: * Validate SECMEC_USRSSBPWD (Strong Password Substitute) can be used as
7625: * DRDA security mechanism.
7626: *
7627: * Here we check that the target server can support SECMEC_USRSSBPWD
7628: * security mechanism based on the environment, application
7629: * requester's identity (PRDID) and connection URL.
7630: *
7631: * IMPORTANT NOTE:
7632: * --------------
7633: * SECMEC_USRSSBPWD is ONLY supported by the target server if:
7634: * - current authentication provider is Derby BUILTIN or
7635: * NONE. (database / system level) (Phase I)
7636: * - Application requester is 'DNC' (Derby Network Client)
7637: * (Phase I)
7638: *
7639: * @return security check code - 0 if everything O.K.
7640: */
7641: private int validateSecMecUSRSSBPWD() throws DRDAProtocolException {
7642: String dbName = null;
7643: AuthenticationService authenticationService = null;
7644: org.apache.derby.iapi.db.Database databaseObj = null;
7645: String srvrlslv = appRequester.srvrlslv;
7646:
7647: // Check if application requester is the Derby Network Client (DNC)
7648: //
7649: // We use a trick here - as the product ID is not yet available
7650: // since ACCRDB message is only coming later, we check the server
7651: // release level field sent as part of the initial EXCSAT message;
7652: // indeed, the product ID (PRDID) is prefixed to in the field.
7653: // Derby always sets it as part of the EXCSAT message so if it is
7654: // not available, we stop here and inform the requester that
7655: // SECMEC_USRSSBPWD cannot be supported for this connection.
7656: if ((srvrlslv == null)
7657: || (srvrlslv.length() == 0)
7658: || (srvrlslv.length() < CodePoint.PRDID_MAX)
7659: || (srvrlslv
7660: .indexOf(DRDAConstants.DERBY_DRDA_CLIENT_ID) == -1))
7661: return CodePoint.SECCHKCD_NOTSUPPORTED; // Not Supported
7662:
7663: // Client product version is extracted from the srvrlslv field.
7664: // srvrlslv has the format <PRDID>/<ALTERNATE VERSION FORMAT>
7665: // typically, a known Derby client has a four part version number
7666: // with a pattern such as DNC10020/10.2.0.3 alpha. If the alternate
7667: // version format is not specified, clientProductVersion_ will just
7668: // be set to the srvrlslvl. Final fallback will be the product id.
7669: //
7670: // SECMEC_USRSSBPWD is only supported by the Derby engine and network
7671: // server code starting at version major '10' and minor '02'. Hence,
7672: // as this is the same for the derby client driver, we need to ensure
7673: // our DNC client is at version and release level of 10.2 at least.
7674: // We set the client version in the application requester and check
7675: // if it is at the level we require at a minimum.
7676: appRequester.setClientVersion(srvrlslv.substring(0,
7677: (int) CodePoint.PRDID_MAX));
7678:
7679: if (appRequester.supportsSecMecUSRSSBPWD() == false)
7680: return CodePoint.SECCHKCD_NOTSUPPORTED; // Not Supported
7681:
7682: dbName = database.shortDbName;
7683: // Check if the database is available (booted)
7684: //
7685: // First we need to have the database name available and it should
7686: // have been set as part of the ACCSEC request (in the case of a Derby
7687: // 'DNC' client)
7688: if ((dbName == null) || (dbName.length() == 0)) {
7689: // No database specified in the connection URL attributes
7690: //
7691: // In this case, we get the authentication service handle from the
7692: // local driver, as the requester may simply be trying to shutdown
7693: // the engine.
7694: authenticationService = ((InternalDriver) NetworkServerControlImpl
7695: .getDriver()).getAuthenticationService();
7696: } else {
7697: // We get the authentication service from the database as this
7698: // last one might have specified its own auth provider (at the
7699: // database level).
7700: //
7701: // if monitor is never setup by any ModuleControl, getMonitor
7702: // returns null and no cloudscape database has been booted.
7703: if (Monitor.getMonitor() != null)
7704: databaseObj = (org.apache.derby.iapi.db.Database) Monitor
7705: .findService(Property.DATABASE_MODULE, dbName);
7706:
7707: if (databaseObj == null) {
7708: // If database is not found, try connecting to it.
7709: database.makeDummyConnection();
7710:
7711: // now try to find it again
7712: databaseObj = (org.apache.derby.iapi.db.Database) Monitor
7713: .findService(Property.DATABASE_MODULE, dbName);
7714: }
7715:
7716: // If database still could not be found, it means the database
7717: // does not exist - we just return security mechanism not
7718: // supported down below as we could not verify we can handle
7719: // it.
7720: if (databaseObj != null)
7721: authenticationService = databaseObj
7722: .getAuthenticationService();
7723: }
7724:
7725: // Now we check if the authentication provider is NONE or BUILTIN
7726: if (authenticationService != null) {
7727: String authClassName = authenticationService.getClass()
7728: .getName();
7729:
7730: if (!authClassName
7731: .equals(AUTHENTICATION_PROVIDER_BUILTIN_CLASS)
7732: && !authClassName
7733: .equals(AUTHENTICATION_PROVIDER_NONE_CLASS))
7734: return CodePoint.SECCHKCD_NOTSUPPORTED; // Not Supported
7735: }
7736:
7737: // SECMEC_USRSSBPWD target initialization
7738: try {
7739: myTargetSeed = decryptionManager.generateSeed();
7740: database.secTokenOut = myTargetSeed;
7741: } catch (SQLException se) {
7742: println2Log(null, session.drdaID, se.getMessage());
7743: // Local security service non-retryable error.
7744: return CodePoint.SECCHKCD_0A;
7745: }
7746:
7747: return 0; // SECMEC_USRSSBPWD is supported
7748: }
7749:
7750: private static int peekStream(EXTDTAInputStream is)
7751: throws IOException {
7752:
7753: is.mark(1);
7754:
7755: try {
7756: return is.read();
7757:
7758: } finally {
7759: is.reset();
7760: }
7761:
7762: }
7763:
7764: private static void closeStream(InputStream stream) {
7765:
7766: try {
7767: if (stream != null)
7768: stream.close();
7769:
7770: } catch (IOException e) {
7771: Util.javaException(e);
7772:
7773: }
7774:
7775: }
7776: }
|