/*


    Copyright (C) 2003 SAP AG

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    ========== licence end


*/
package com.sap.dbtech.jdbc;

import java.sql.*;
import com.sap.dbtech.rte.comm.*;
import com.sap.dbtech.util.*;
import com.sap.dbtech.util.security.Authentication;
import com.sap.dbtech.jdbc.exceptions.*;
import com.sap.dbtech.jdbc.packet.*;
import com.sap.dbtech.jdbc.translators.ConversionExceptionSapDB;
import com.sap.dbtech.vsp001.*;

import java.util.Map;
import java.util.Properties;
import java.lang.ref.WeakReference;
/**
 *
 */
public class ConnectionSapDB implements java.sql.Connection {

        /**
         * Control flag for garbage collection on execute. If this is
         * set, old cursors/parse ids are sent <i>together with</i> the current
         * statement for being dropped.
         */
        public static final int GC_ALLOWED = 1;

        /**
         * Control flag for garbage collection on execute. If this is
         * set, old cursors/parse ids are sent <i>after</i> the current
         * statement for being dropped.
         */
        public static final int GC_DELAYED = 2;

        /**
         * Control flag for garbage collection on execute. If this is
         * set, nothing is done to drop cursors or parse ids.
         */
        public static final int GC_NONE    = 3;

    JdbcCommunication session;
    boolean autocommit = true;
    private boolean inTransaction = false;
    java.util.Stack packetPool = new java.util.Stack ();
    java.util.Stack packetPoolUnicode = new java.util.Stack ();
//    private boolean isUnicode = false;
    private SQLWarning warningList;
    private java.util.Properties connectProperties;
    private UniqueID UniqueID = new UniqueID() ;
    private Map typeMap;
    private Object executingObject = null;
    private boolean inReconnect = false;
    private com.sap.dbtech.util.GarbageParseid garbageParseids = null;
    private com.sap.dbtech.util.GarbageCursor  garbageCursors  = null;
    java.util.ArrayList statementContainer = null;
    private boolean keepGarbage = false;
    private int isolationLevel = Connection.TRANSACTION_READ_COMMITTED;
    private int resultSetHoldability = StatementSapDB.defaultHoldability_C;
    ParseinfoCache parseCache = null;
    DbsCache dbsCache = null;
    int sessionID = -1;
    boolean isSQLModeOracle = false;
    boolean isSpaceoptionSet = false;
    private DatabaseMetaData DatabaseMetaData = null;
    private String cursorPrefix = "JDBC_CURSOR_";
    private static final String syncObj = "";
    private int nonRecyclingExecutions=0;
    private String applID = null;
    private int    kernelversion; // Version without patch level, e.g. 70402 or 70600.
    final private static byte defaultFeatureSet[] = {1,0,2,0,3,0,4,0,5,0,6,0};
    private byte kernelFeatures[] = new byte[defaultFeatureSet.length];

	private boolean releaseSavePointSupported;
    /**
     *
     * @param info java.util.Properties
     * @exception java.sql.SQLException The exception description.
     */
    public ConnectionSapDB(
        JdbcCommunication session,
        java.util.Properties info)
    throws SQLException
    {
        this.session = session;
        this.connectProperties = (java.util.Properties) info.clone ();
        this.isSQLModeOracle = (this.getConnectProperty(DriverSapDB.sqlmodeName_C).equalsIgnoreCase(DriverSapDB.sqlmodeOracle_C))
                               ?true:false;
//        this.isUnicode = DriverSapDB.getBooleanProperty(this.connectProperties,DriverSapDB.unicodeName_C, false);
        this.isSpaceoptionSet = DriverSapDB.getBooleanProperty(this.connectProperties,DriverSapDB.spaceoption_C, false);
//        if(this.isSpaceoptionSet) {
//            this.isUnicode = true;
//        }
        this.doConnect (this.connectProperties);
        this.statementContainer = new java.util.ArrayList();
    }
    /**
     *
     * @param warning java.sql.SQLWarning
     */
    final void addWarning (SQLWarning warning)
    {
        if (this.warningList == null) {
            this.warningList = warning;
        }
        else {
            this.warningList.setNextWarning (warning);
        }
    }
    /**
     * asserts that the current connection is still open.
     *
     * @exception ObjectIsClosedException
     */
    protected void assertOpen ()
        throws ObjectIsClosedException
    {
        if (this.session == null) {
            throw new ObjectIsClosedException (this);
        }
    }
    /**
     *
     * @exception java.sql.SQLException The exception description.
     */
    final public void cancel (Object requestingObject)
    throws SQLException
    {
        if (this.executingObject == requestingObject) {
            this.session.cancel ();
        }
    }
    /**
     * clearWarnings method comment.
     */
    final public void clearWarnings()
    throws java.sql.SQLException
    {
        this.warningList = null;
    }
    /**
     * close the current connection.
     * <P>
     * An implicit ROLLBACK is performed.
     *
     * @exception java.sql.SQLException
     */
    public synchronized void close()
    throws java.sql.SQLException
    {
        if (this.session != null) {
            try {
                if (this.garbageCursors!=null)
                  this.garbageCursors.emptyCan();
                if (this.garbageParseids!=null)
                  this.garbageParseids.emptyCan();
                this.executeSQLString ("ROLLBACK WORK RELEASE", ConnectionSapDB.GC_NONE);
            }
            catch (SQLException sqlExc) {
              TimeoutException.println("IGNORING EXCEPTION CLOSE:"+sqlExc.toString());
              // ignore
            }
            catch (RuntimeException sqlExc) {
              TimeoutException.println("IGNORING EXCEPTION CLOSE:"+sqlExc.toString());
              // ignore
            }finally{
                this.releaseSession();
            }
        }
    }

    void releaseSession(){
        this.session.release ();
        this.session = null;
        this.DatabaseMetaData = null;
    } 
    
    boolean closeCursorAtCommit()throws java.sql.SQLException{
        /*close all cursor at commit*/
        boolean forceGC = false;
        int sz = this.statementContainer.size();
        for (int i = 0; i < sz; i++) {
          StatementSapDB st = (StatementSapDB)((WeakReference)this.statementContainer.get(i)).get();
          if (st != null){
            ResultSet rs = st.currentResultSet;
            if (st.getResultSetHoldability()==ResultSet.CLOSE_CURSORS_AT_COMMIT
                && rs!= null){
              forceGC = true;
              rs.close();
            }
          }
        }
        this.statementContainer.clear();
        if (forceGC && this.garbageCursors != null)
          this.garbageCursors.forceGarbageCollection();
        /*send commit*/
        return forceGC;
        
    }
    /**
     * commits the current transaction.
     *
     * @exception java.sql.SQLException
     */
    public synchronized void commit () throws java.sql.SQLException {
        this.assertOpen();
        this.closeCursorAtCommit();
        this.executeSQLString("COMMIT WORK", ConnectionSapDB.GC_ALLOWED);
        this.inTransaction = false;
    }
    /**
     * createStatement method comment.
     */
    public java.sql.Statement createStatement() throws java.sql.SQLException {
        this.assertOpen ();
        return new StatementSapDB (this);
    }
    /**
     *
     * @return java.sql.Statement
     * @param resultSetType int
     * @param resultSetConcurrency int
     * @exception java.sql.SQLException The exception description.
     */
    public Statement createStatement(
        int resultSetType,
        int resultSetConcurrency)
    throws SQLException
    {
        this.assertOpen ();
        return new StatementSapDB (this, resultSetType, resultSetConcurrency, StatementSapDB.defaultHoldability_C);
    }
    
    private String getTermID(){
      StringBuffer termidsb = new StringBuffer("java@");
      termidsb.append(Integer.toHexString(hashCode())); 
      while (termidsb.length() < 18){
        termidsb.append(' ');          
      }      
      return termidsb.toString();  
    }
    private boolean initiateChallengeResponse(String user, Authentication auth, boolean isUserPasswdAscii) throws SQLException{
      RequestPacket requestPacket = this.getRequestPacket(! isUserPasswdAscii);  
      boolean initSuccess = requestPacket.initChallengeResponse(user, auth.getClientchallenge());
      if (initSuccess){
         ReplyPacket replyPacket = this.execute (requestPacket, this, ConnectionSapDB.GC_DELAYED); 
         auth.parseServerChallengeReply(replyPacket.getVarDataPart());
         return true;
      }  else {
         return false;
      }
    }
    
    /**
     *
     * @param info   java.util.Properties
     *
     * @exception SQLException
     */
    protected void doConnect (
        java.util.Properties info)
    throws SQLException
    {
        String user = info.getProperty (DriverSapDB.userName_C);
        if (user == null) {
            throw new SQLExceptionSapDB (MessageTranslator.translate(MessageKey.ERROR_NOUSER));
        }
        char firstChar = user.charAt (0);
        char lastChar = user.charAt (user.length () - 1);
        if (! ((firstChar == '"') && (lastChar == '"'))) {
            user = user.toUpperCase ();
            info.put (DriverSapDB.userName_C, user);
        }
        String passwd = info.getProperty (DriverSapDB.passwordName_C);
        if (passwd == null) {
            throw new SQLExceptionSapDB (MessageTranslator.translate(MessageKey.ERROR_NOPASSWORD));
        }
        boolean isUserPasswdAscii = StringUtil.isIso8859_1(passwd);
        if (isUserPasswdAscii){
            isUserPasswdAscii = StringUtil.isIso8859_1(user);  
        }
        byte[] passwdByte = NameHandling.preprocessPassword(passwd, !isUserPasswdAscii);
        String sqlMode = info.getProperty (DriverSapDB.sqlmodeName_C, "INTERNAL");
        String cacheLimit = info.getProperty (DriverSapDB.cachelimitName_C);
        String timeout = info.getProperty (DriverSapDB.timeoutName_C);
        String isolationLevel = info.getProperty (DriverSapDB.isolationName_C);
        String connectCmd;
        byte [] crypted;
        RequestPacket requestPacket = this.getRequestPacket (!isUserPasswdAscii);
        Authentication auth = null;
        boolean isChallengeResponseSupported = false;
        if  (this.session. isChallengeResponseSupported()){
            //suppress Challenge-Response for NIConnections because it needs 2 sockets
            //and you cannot reuse a SAP router string.
            try {
                auth = new Authentication();
                isChallengeResponseSupported = this.initiateChallengeResponse( user, auth, isUserPasswdAscii);
                if (passwd.length() > auth.getMaxpasswordLen() && auth.getMaxpasswordLen() > 0){
                    passwd = passwd.substring(0,auth.getMaxpasswordLen());  
                }
            } catch (java.security.NoSuchAlgorithmException e) {
                isChallengeResponseSupported = false;
            } catch (SQLExceptionSapDB e) {
                isChallengeResponseSupported = false;
                if (e.getErrorCode() == -5015){
                    try {
                        this.session.reconnect();
                    } catch (RTEException rteEx) {
                        throw new ConnectionException (rteEx);
                    }
                } else {
                    throw e;
                }
            }
        }
        if ( DriverSapDB.getBooleanProperty(info,DriverSapDB.authentication_C, false) && ! isChallengeResponseSupported){
            throw new SQLExceptionSapDB (MessageTranslator.translate(MessageKey.ERROR_CONNECTION_CHALLENGERESPONSENOTSUPPORTED));
        }

        /*
         * build connect statement
         */
        connectCmd = "Connect " + user + " identified by :PW "
            + "SQLMODE " + sqlMode;
        if (timeout != null) {
            connectCmd += " TIMEOUT " + timeout;
        }
        if (isolationLevel != null) {
            this.isolationLevel = DriverSapDB.isolevelString2Jdbc(isolationLevel);
            connectCmd += " ISOLATION LEVEL "
                + DriverSapDB.isolevelJdbc2native (this.isolationLevel);
        }
        if (cacheLimit != null) {
            connectCmd += " CACHELIMIT " + cacheLimit;
        }
        if (this.isSpaceoptionSet) {
            connectCmd += " SPACE OPTION ";
            this.setKernelFeatureRequest(Feature.sp1f_space_option);
        }
        requestPacket.initDbsCommand (false, connectCmd, ResultSet.TYPE_FORWARD_ONLY);
        
        if (!isChallengeResponseSupported){
	        try {
	            crypted = NameHandling.mangle (passwd, !isUserPasswdAscii);
	        }
	        catch (ArrayIndexOutOfBoundsException exc) {
	            throw new SQLExceptionSapDB (MessageTranslator.translate(MessageKey.ERROR_INVALIDPASSWORD));
	        }
	        requestPacket.newPart (PartKind.Data_C);
	        requestPacket.addDataBytes (crypted);
	        requestPacket.addDataString (this.getTermID());
	        requestPacket.incrPartArguments ();
        } else {
            requestPacket.addClientProofPart(auth.getClientProof(passwdByte)); 
            requestPacket.addClientIDPart(this.getTermID());
        }
        
         
        /*feature request part*/
        System.arraycopy( defaultFeatureSet,0, kernelFeatures,0,defaultFeatureSet.length );

        this.setKernelFeatureRequest(Feature.sp1f_variable_input);
        this.setKernelFeatureRequest(Feature.sp1f_multiple_drop_parseid);
        this.setKernelFeatureRequest(Feature.sp1f_check_scrollableoption);
        this.setKernelFeatureRequest(Feature.sp1f_ascii_in_and_output);
        requestPacket.addFeatureRequestPart(this.kernelFeatures);
        /*
         * execute
         */
        ReplyPacket replyPacket = this.execute (requestPacket, this, ConnectionSapDB.GC_DELAYED);
        this.sessionID = replyPacket.getSessionID ();
        VersionInfo vi = new VersionInfo(replyPacket.getKernelMajorVersion(),
                                         replyPacket.getKernelMinorVersion(),
                                         replyPacket.getKernelCorrectionLevel(),
                                         0, null);
        if(vi.getMajorVersion() >= 7 &&
           vi.getMinorVersion() >= 6) {
                        this.DatabaseMetaData = new DatabaseMetaDataMaxDB(this, vi);
        } else {
                        this.DatabaseMetaData = new DatabaseMetaDataSapDB(this, vi);
        }
       	this.releaseSavePointSupported = true;
        	
        this.kernelversion = vi.getMajorVersion() * 10000 + 100 * vi.getMinorVersion() + vi.getMinorMinorVersion();
        byte[] featureReturn = replyPacket.getFeatures();

        if (featureReturn != null){
          this.kernelFeatures = featureReturn;
        } else {
          System.arraycopy( defaultFeatureSet,0, kernelFeatures,0,defaultFeatureSet.length );
        }

//        if ( (10000 * vi.getMajorVersion()+
//             100 * vi.getMinorVersion() +
//             vi.getMinorMinorVersion())
//             >= 70400)
//             this.applID = "JDBC";
        /*
         * use remaining properties
         */
        this.autocommit = DriverSapDB.getBooleanProperty(
            info, DriverSapDB.autocommitName_C, this.autocommit);
        //this.keepGarbage = DriverSapDB.getBooleanProperty(
        //    info, "keepgarbage", false);
        if (info.containsKey(DriverSapDB.cacheName_C)) {
            this.parseCache = new ParseinfoCache (info);
            /*
             * don't use the dbsCache, the overhead is probably not worth it
             * this.dbsCache = new DbsCache (info);
             */

        }
//        TimeoutException.println("New Connection established:"+this+" Version: \""
//            +(10000 * vi.getMajorVersion()+
//             100 * vi.getMinorVersion() +
//             vi.getMinorMinorVersion()) +
//             ((this.autocommit)?"\" autocommit \"on\"":"\" autocommit \"off\"")+
//             ((this.autocommit)?"\" autocommit \"on\"":"\" autocommit \"off\"")+
//             ((this.parseCache !=null)?" PICache \"on\"":" PICache \"off\"")+
//             ((this.inTransaction)?" inTransaction \"true\"":" inTransaction \"false\"")
//             );
    }
    /**
     *
     * @exception java.sql.SQLException The exception description.
     */
    public ReplyPacket
    execute (
            RequestPacket requestPacket,
            Object executingObject,
            int gcFlags)
        throws SQLException
    {
        return this.execute (requestPacket, false, false, executingObject, gcFlags);
    }

    public ReplyPacket sendStreamErrorPacket(SQLException sqlEx)
    {
        try {
            RequestPacket requestPacket = getRequestPacket(false);
            requestPacket.initDbs(this.autocommit, ResultSet.TYPE_FORWARD_ONLY);
            if(sqlEx.getMessage() == null || sqlEx.getMessage().length()==0) {
                requestPacket.addErrorTextPart(MessageTranslator.translate(MessageKey.ERROR_MESSAGE_NOT_AVAILABLE));
            } else {
                requestPacket.addErrorTextPart(sqlEx.getMessage()); 
            }
            if(sqlEx.getErrorCode() == 0) {
                requestPacket.setErrorCode(-9999);
                requestPacket.setSQLState("S9999");
            } else {
                requestPacket.setErrorCode(sqlEx.getErrorCode());
                requestPacket.setSQLState(sqlEx.getSQLState());
            }
            return execute(requestPacket, this, GC_NONE);
            
        } catch(Exception ex) {
            // as this is already sent during an exception, don't 
            // send this exception
            return null;
        }
    }


    /**
     *
     * @exception java.sql.SQLException The exception description.
     */
    public synchronized ReplyPacket
    execute (
            RequestPacket requestPacket,
            boolean ignoreErrors,
            boolean isParse,
            Object executingObject,
            int    gcFlags)
        throws SQLException
    {
        int requestLen;
        ReplyPacket replyPacket = null;
        int localWeakReturnCode = 0;

        this.assertOpen ();

        if(gcFlags == GC_ALLOWED) {
            boolean spaceleft = true;
            if (this.garbageCursors != null
                && this.garbageCursors.isPending()) {
                spaceleft = this.garbageCursors.emptyCan(requestPacket);
            }
            if (spaceleft
                && this.garbageParseids != null
                && this.garbageParseids.isPending()) {
                this.garbageParseids.emptyCan(requestPacket);
            }
        } else {
             if((this.garbageParseids!=null && this.garbageParseids.isPending())) {
                nonRecyclingExecutions ++;
            }
        }
        requestPacket.closePacket ();

        requestLen = requestPacket.length ();
        if (Tracer.traceAny_C && Tracer.isOn (5)) {
            Tracer.traceObject (null, requestPacket, 6);
        }
        try {
            this.executingObject = executingObject;
              replyPacket = ReplyPacketFactory.getReplyPacket(
            this.session.execute (requestPacket.getBase (), requestLen), this.isKernelFeaturesupported(Feature.sp1f_ascii_in_and_output));

            /*get Returncode*/
            replyPacket.firstSegment ();
            localWeakReturnCode = replyPacket.weakReturnCode();

            if(localWeakReturnCode != -8) {
                this.freeRequestPacket(requestPacket);
            }

            if (! this.autocommit
                && ! isParse) {
                this.inTransaction = true;
            }

            // if it is not completely forbidden, we will send the drop
            if(gcFlags != GC_NONE) {
                if (this.garbageCursors != null
                        && this.garbageCursors.isPending()
                        && localWeakReturnCode == 0) {
                        this.garbageCursors.emptyCan(this);
                }

                if(nonRecyclingExecutions > 20
                        && localWeakReturnCode == 0) {
                        nonRecyclingExecutions=0;
                        if (this.garbageParseids != null
                        && this.garbageParseids.isPending()) {
                        this.garbageParseids.emptyCan(this);
                        }
                        nonRecyclingExecutions=0;
                }
                        }

        }
        catch (RTEException rteExc) {
            // if a reconnect is forbidden or we are in the process of a
            // reconnect or we are in a (now rolled back) transaction
            if (! DriverSapDB.getBooleanProperty(this.connectProperties,DriverSapDB.reconnect_C, true)
                || this.inReconnect || this.inTransaction) {
                throw new ConnectionException (rteExc);
            }
            else {
                //Tracer.println ("    trying reconnect"); //#print
                //Tracer.whereAmI ();
                this.tryReconnect (rteExc);
                this.inTransaction = false;
            }
        }
        finally {
            this.executingObject = null;
        }
        if (Tracer.traceAny_C && Tracer.isOn (5)) {
            Tracer.traceObject (null, replyPacket, 6);
        }
        if (!ignoreErrors && (localWeakReturnCode != 0)) {
            this.throwSQLError (replyPacket);
        }
        return replyPacket;
    }
    /**
     *
     * @param cmd java.lang.String
     * @exception java.sql.SQLException The exception description.
     */
    private void
    executeSQLString (
        String cmd,
        int gcFlags)
    throws SQLException
    {
        RequestPacket requestPacket = this.getRequestPacket (false);
        try {
            requestPacket.initDbs (this.autocommit, ResultSet.TYPE_FORWARD_ONLY);
            requestPacket.addStringThrowExc(cmd);
        } catch (ConversionExceptionSapDB e) {
            this.freeRequestPacket(requestPacket);
            requestPacket = this.getRequestPacket (true);
            requestPacket.initDbs (this.autocommit, ResultSet.TYPE_FORWARD_ONLY);
            requestPacket.addStringThrowExc(cmd);
        }
        try {
          this.execute (requestPacket, this, gcFlags);
        }
        catch (TimeoutException ignore) {
          TimeoutException.println(this.toString()+" Inner Timeout "+ignore.toString());
        }
    }
    
    /**
	 * The finalizer will clean up the connection, if it hadn't been done 
	 * yet. This will possibly even send a ROLLBACK to the database.
	 */
	public void finalize() throws Throwable {
		try {
			// avoid a reconnect during the ROLLBACK WORK RELEASE sent
			this.inReconnect = true;
			this.close();
		} catch (SQLException sqlExc) {
			// ignore
		}
		super.finalize();
	}
	
    /**
	 * 
	 * @param requestPacket
	 *            com.sap.dbtech.jdbc.packet.RequestPacket
	 */
    public void freeRequestPacket (RequestPacket requestPacket) {
        requestPacket.setAvailability(false);
        if (requestPacket.isUnicodePacket()){
            this.packetPoolUnicode.push (requestPacket);
        } else{
            this.packetPool.push (requestPacket);
        }
    }
    /**
     * getAutoCommit method comment.
     */
    final public boolean getAutoCommit() throws java.sql.SQLException {
        return this.autocommit;
    }
    /**
     * getCatalog method comment.
     */
    final public String getCatalog() throws java.sql.SQLException {
        return null;
    }
    /**
     *
     * @return java.lang.String
     * @param key java.lang.String
     */
    final String getConnectProperty (String key) {
        return this.connectProperties.getProperty (key, "");
    }
    /**
     * getMetaData method comment.
     */
    final public java.sql.DatabaseMetaData getMetaData() throws java.sql.SQLException {
        this.assertOpen ();
        return  this.DatabaseMetaData;
    }
    /**
     *
     * @return com.sap.dbtech.jdbc.packet.RequestPacket
     */
    final public synchronized RequestPacket getRequestPacket(
            boolean forceUnicode) throws SQLException {
        RequestPacket result;
        String applID = this.connectProperties.getProperty(
                DriverSapDB.application_C, null);
        String applVers = this.connectProperties.getProperty(
                DriverSapDB.appversion_C, null);

        try {
            if (forceUnicode) {
                if (this.packetPoolUnicode.isEmpty()) {
                    result = new RequestPacketUnicode(this.session
                            .getRequestPacket(), applID, applVers);
                } else {
                    result = (RequestPacket) this.packetPoolUnicode.pop();
                }
            } else {
                if (this.packetPool.isEmpty()) {
                    result = new RequestPacket(this.session.getRequestPacket(),
                            RteC.asciiClient_C, applID, applVers);
                } else {
                    result = (RequestPacket) this.packetPool.pop();
                }
            }
        } catch (RTEException rteExc) {
            throw new SQLExceptionSapDB(rteExc.toString());
        }
        result.setAvailability(true);
        return result;
    }
    /**
     * getTransactionIsolation method comment.
     */
    final public int
    getTransactionIsolation()
    throws java.sql.SQLException
    {
        return this.isolationLevel;
    }
    /**
     *
     * @return java.sql.Map
     * @exception java.sql.SQLException The exception description.
     */
    final public Map getTypeMap() throws SQLException {
        return this.typeMap;
    }
    /**
     * getWarnings method comment.
     */
    final public java.sql.SQLWarning getWarnings() throws java.sql.SQLException {
        return warningList;
    }
    /**
     * isClosed method comment.
     */
    public boolean isClosed() throws java.sql.SQLException {
        if (session == null) {
            return true;
        }
        try {
          RequestPacket requestPacket = this.getRequestPacket (false);
          requestPacket.initHello ();
          this.execute (requestPacket, this, ConnectionSapDB.GC_ALLOWED);
        }
        catch (TimeoutException ignore){
             TimeoutException.println(this.toString()+" Inner Timeout "+ignore.toString());
        }
        catch (SQLException exc) {
          TimeoutException.println(this.toString()+" Inner SQLException "+exc.toString());
          try{
            this.session.release ();
          } catch(Exception ignore){
            TimeoutException.println(this+exc.toString());
          }
          this.session = null;
          this.DatabaseMetaData = null;
          return true;
        }
        return session == null;
    }
    /**
     * isReadOnly method comment.
     */
    final public boolean isReadOnly() throws java.sql.SQLException {
        return false;
    }
    /**
     *
     */
    boolean
    isInTransaction ()
    {

        return (!this.autocommit && this.inTransaction);
    }
    /**
     *
     * @return int
     */
    final synchronized int maxStatementLength ()
    throws SQLException
    {
        RequestPacket requestPacket = this.getRequestPacket (false);
        int packetSize = requestPacket.size ();
        int result = packetSize - Packet.Segment_O - Segment.Part_O - Part.Data_O;
        this.freeRequestPacket(requestPacket);
        return result;
    }
    /**
     * nativeSQL method comment.
     */
    final public String
    nativeSQL(
        String sql)
    throws java.sql.SQLException
    {
        return sql;
    }
    /**
     *
     * @return java.lang.String
     */
    final String nextCursorName () {
      return this.UniqueID.getNextID(cursorPrefix);
    }
    /**
     *
     */
    final protected void setCursorPrefix (String prefix) {
        this.cursorPrefix = prefix;
    }
    /**
     * prepareCall method comment.
     */
    final public java.sql.CallableStatement prepareCall(String sql) throws java.sql.SQLException {
        this.assertOpen ();
        return new CallableStatementSapDB (this, sql);
    }
    /**
     *
     * @return java.sql.CallableStatement
     * @param sql java.lang.String
     * @param resultSetType int
     * @param resultSetConcurrency int
     * @exception java.sql.SQLException The exception description.
     */
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        this.assertOpen ();
        return new CallableStatementSapDB (this, sql, resultSetType, resultSetConcurrency, StatementSapDB.defaultHoldability_C);
    }
    /**
     * prepareStatement method comment.
     */
    public java.sql.PreparedStatement prepareStatement(
        String sql)
    throws java.sql.SQLException
    {
        this.assertOpen ();
        return new CallableStatementSapDB (this, sql);
    }
    /**
     *
     * @return java.sql.PreparedStatement
     * @param sql java.lang.String
     * @param resultSetType int
     * @param resultSetConcurrency int
     * @exception java.sql.SQLException The exception description.
     */
    public java.sql.PreparedStatement prepareStatement(
        String sql,
        int resultSetType,
        int resultSetConcurrency)
    throws SQLException
    {
        this.assertOpen ();
        return new CallableStatementSapDB (this, sql, resultSetType, resultSetConcurrency,StatementSapDB.defaultHoldability_C);
    }
    /**
     * rollback method comment.
     */
    public void rollback()
        throws java.sql.SQLException
    {
        this.assertOpen ();
        this.executeSQLString ("ROLLBACK WORK", ConnectionSapDB.GC_ALLOWED);
        this.inTransaction = false;
    }
    /**
     * setAutoCommit method comment.
     */
    public void setAutoCommit(boolean autoCommit)
        throws java.sql.SQLException
    {
        this.assertOpen ();
        if (autoCommit){
            this.commit();
        }
        this.autocommit = autoCommit;
    }
    /**
     * setCatalog method comment.
     */
    final public void setCatalog(
        String catalog)
    throws java.sql.SQLException
    {
    }
    /**
     * setReadOnly method comment.
     */
    final public void
    setReadOnly(
        boolean readOnly)
    throws java.sql.SQLException
    {
        this.assertOpen ();
    }
    /**
     * setTransactionIsolation method comment.
     */
    final public void
    setTransactionIsolation(
        int level)
    throws java.sql.SQLException
    {
        if (this.isolationLevel != level){
          String sapdbEncoding = DriverSapDB.isolevelJdbc2native (level);
          this.assertOpen ();
          String cmd = "SET ISOLATION LEVEL " + sapdbEncoding;
          Statement setIso = new InternalStatementSapDB (this);
          setIso.executeUpdate (cmd);
          this.isolationLevel = level;
        }
    }
    /**
     *
     * @param map java.sql.Map
     * @exception java.sql.SQLException The exception description.
     */
    final public void setTypeMap(Map map) throws SQLException {
        this.typeMap = map;
    }
    /**
     *
     * @exception com.sap.dbtech.jdbc.SQLExceptionSapDBTech The exception description.
     */
    final void
    throwBatchException (
        ReplyPacket replyPacket,
        int [] codes,
        int segmentsProcessed)
    throws java.sql.BatchUpdateException
    {
        String state = replyPacket.sqlState ();
        int rc = replyPacket.returnCode ();
        String errmsg = replyPacket.getErrorMsg ();
        int [] firstCodes = new int [segmentsProcessed];
        System.arraycopy(codes, 0, firstCodes, 0, segmentsProcessed);

        throw new java.sql.BatchUpdateException (errmsg, state, rc, firstCodes);
    }
    /**
     *
     * @exception com.sap.dbtech.jdbc.SQLExceptionSapDBTech The exception description.
     */
    final private void throwSQLError (
        ReplyPacket replyPacket)
    throws SQLExceptionSapDB
    {
        SQLExceptionSapDB exc = replyPacket.createException();
        throw exc;
    }
    /**
     *
     * @exception java.sql.SQLException The exception description.
     */
    protected void tryReconnect (
        com.sap.dbtech.rte.comm.RTEException outerRteExc)
    throws SQLException
    {
        Object localSync = (TimeoutException.extendedTrace==null)?syncObj:(Object)TimeoutException.extendedTrace;
        synchronized (localSync)
        {
            TimeoutException timeout = new TimeoutException (outerRteExc);
            if (this.parseCache != null) {
                this.parseCache.clear ();
            }
            if (this.dbsCache != null) {
                this.dbsCache.clear ();
            }
            this.packetPool.setSize (0);
            this.packetPoolUnicode.setSize (0);
            this.inReconnect = true;
            try {
                this.session.reconnect ();
                this.doConnect (this.connectProperties);
                    TimeoutException.println ("+++ connected again +++");
                }
            catch (RTEException rteExc) {
                    TimeoutException.println ("--- reconnect failed: "
                        + rteExc.getMessage () + " ---");
                throw new ConnectionException (rteExc);
            }
            finally {
                this.inReconnect = false;
            }
            throw timeout;
        }
    }




    /**
     * queues an old parseid for dropping it.
     * <UL>
     * <LI>dropping of parseids is mostly done
     * during garbage collection
     * <LI>when asynchronous garbage collection is
     * disabled, the garbage collector is called
     * during a blocking socket call
     * <LI>sending the DROP PARSEID command as
     * part of the <i>finalize</i> method will
     * interfere with the pending socket request
     * <LI>synchronization of the connection does
     * not work as both requests happen in the
     * same thread
     * <LI>parseids are therefor queued and dropped after a regular SQL request
     * succeeded
     * <LI>besides, sending multiple DROP PARSEIDs in one
     * request is more efficient
     * </UL>
     *
     * @param pid    the parseid
     */
    final public void
    dropParseid (byte [] pid)
    {
        if (!this.keepGarbage) {
            if (pid == null) {
                return;
            }
            if (this.garbageParseids == null) {
                this.garbageParseids = new com.sap.dbtech.util.GarbageParseid(this.isKernelFeaturesupported(Feature.sp1f_multiple_drop_parseid));
            }
            this.garbageParseids.throwIntoGarbageCan(pid);
        }
    }

    /**
     * queues an old cursor for dropping it.
     * <UL>
     * <LI>dropping of cursor is sometimes done
     * during garbage collection
     * <LI>when asynchronous garbage collection is
     * disabled, the garbage collector is called
     * during a blocking socket call
     * <LI>sending the CLOSE CURSOR command as
     * part of the <i>finalize</i> method will
     * interfere with the pending socket request
     * <LI>synchronization of the connection does
     * not work as both requests happen in the
     * same thread
     * <LI>cursors are therefor queued and dropped after a regular SQL request
     * succeeded
     * <LI>besides, sending multiple CLOSE CURSORs in one
     * request is more efficient
     * </UL>
     *
     * @param cursorname    the cursorname
     */
    final public void dropCursor (String cursorname) {
        synchronized(this) {
            if (this.garbageCursors == null) {
                this.garbageCursors = new com.sap.dbtech.util.GarbageCursor();
            }
            this.garbageCursors.throwIntoGarbageCan(cursorname);
        }
    }

    final public boolean restoreCursor(String cursorname) {
        synchronized(this) {
                if(this.garbageCursors != null) {
                        return this.garbageCursors.restoreFromGarbageCan(cursorname);
                } else {
                        return false;
                }
        }
    }

    /**
     *
     */
    public void
    printCacheStats (
        java.io.PrintStream stream)
    {
        if (this.parseCache == null) {
            stream.println ("no cache available");
        }
        else {
            this.parseCache.dumpStats (stream);
        }
    }
    /**
     *
     */
    public void
    printCacheStats (
        java.io.PrintWriter stream)
    {
        if (this.parseCache == null) {
            stream.println ("no cache available");
        }
        else {
            this.parseCache.dumpStats (stream);
        }
    }
    public boolean isSQLModeOracle (){
      return this.isSQLModeOracle;
    }

    /**
     * Changes the holdability of <code>ResultSet</code> objects
     * created using this <code>Connection</code> object to the given
     * holdability.
     *
     * @param holdability a <code>ResultSet</code> holdability constant; one of
     *        <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *        <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @throws SQLException if a database access occurs, the given parameter
     *         is not a <code>ResultSet</code> constant indicating holdability,
     *         or the given holdability is not supported
     * @see #getHoldability
     * @see ResultSet
     * @since 1.4
     */
    public void setHoldability (int holdability) throws SQLException {
        this.resultSetHoldability = holdability;
    }

    /**
     * Retrieves the current holdability of <code>ResultSet</code> objects
     * created using this <code>Connection</code> object.
     *
     * @return the holdability, one of
     *        <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *        <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @throws SQLException if a database access occurs
     * @see #setHoldability
     * @see ResultSet
     * @since 1.4
     */
    public int getHoldability () throws SQLException {
        return  this.resultSetHoldability;
    }

    /**
     * Creates an unnamed savepoint in the current transaction and
     * returns the new <code>Savepoint</code> object that represents it.
     *
     * @return the new <code>Savepoint</code> object
     * @exception SQLException if a database access error occurs
     *            or this <code>Connection</code> object is currently in
     *            auto-commit mode
     * @see Savepoint
     * @since 1.4
     */
    public java.sql.Savepoint setSavepoint () throws java.sql.SQLException {
        if (this.getAutoCommit())
            throw  new SQLExceptionSapDB(MessageTranslator.translate(MessageKey.ERROR_CONNECTION_AUTOCOMMIT));
        return  com.sap.dbtech.jdbc.SavepointSapDB.setSavepoint(this);
    }

    /**
     * Creates a savepoint with the given name in the current transaction
     * and returns the new <code>Savepoint</code> object that represents it.
     *
     * @param name a <code>String</code> containing the name of the savepoint
     * @return the new <code>Savepoint</code> object
     * @exception SQLException if a database access error occurs
     *            or this <code>Connection</code> object is currently in
     *            auto-commit mode
     * @see Savepoint
     * @since 1.4
     */
    public java.sql.Savepoint setSavepoint (String SavepointName) throws java.sql.SQLException {
        if (this.getAutoCommit())
            throw  new SQLExceptionSapDB(MessageTranslator.translate(MessageKey.ERROR_CONNECTION_AUTOCOMMIT));
        return  com.sap.dbtech.jdbc.SavepointSapDB.setSavepoint(SavepointName, this);
    }

    /**
     * Undoes all changes made after the given <code>Savepoint</code> object
     * was set.
     * <P>
     * This method should be used only when auto-commit has been disabled.
     *
     * @param savepoint the <code>Savepoint</code> object to roll back to
     * @exception SQLException if a database access error occurs,
     *            the <code>Savepoint</code> object is no longer valid,
     *            or this <code>Connection</code> object is currently in
     *            auto-commit mode
     * @see Savepoint
     * @see #rollback
     * @since 1.4
     */
    public void rollback (java.sql.Savepoint savepoint) throws java.sql.SQLException {
        if (this.getAutoCommit())
            throw  new SQLExceptionSapDB(MessageTranslator.translate(MessageKey.ERROR_CONNECTION_AUTOCOMMIT));
        com.sap.dbtech.jdbc.SavepointSapDB.rollback(savepoint);
    }

    /**
     * Removes the given <code>Savepoint</code> object from the current
     * transaction. Any reference to the savepoint after it have been removed
     * will cause an <code>SQLException</code> to be thrown.
     *
     * @param savepoint the <code>Savepoint</code> object to be removed
     * @exception SQLException if a database access error occurs or
     *            the given <code>Savepoint</code> object is not a valid
     *            savepoint in the current transaction
     * @since 1.4
     */
    public void releaseSavepoint (java.sql.Savepoint savepoint) throws java.sql.SQLException {
        com.sap.dbtech.jdbc.SavepointSapDB.releaseSavepoint(savepoint);
    }

    /**
     * Creates a <code>Statement</code> object that will generate
     * <code>ResultSet</code> objects with the given type, concurrency,
     * and holdability.
     * This method is the same as the <code>createStatement</code> method
     * above, but it allows the default result set
     * type, concurrency, and holdability to be overridden.
     *
     * @param resultSetType one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *         <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *         <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.CONCUR_READ_ONLY</code> or
     *         <code>ResultSet.CONCUR_UPDATABLE</code>
     * @param resultSetHoldability one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *         <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @return a new <code>Statement</code> object that will generate
     *         <code>ResultSet</code> objects with the given type,
     *         concurrency, and holdability
     * @exception SQLException if a database access error occurs
     *            or the given parameters are not <code>ResultSet</code>
     *            constants indicating type, concurrency, and holdability
     * @see ResultSet
     * @since 1.4
     */
    public Statement createStatement (int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        this.assertOpen();
        return  new StatementSapDB(this, resultSetType, resultSetConcurrency,
                resultSetHoldability);
    }

    /**
     * Creates a <code>PreparedStatement</code> object that will generate
     * <code>ResultSet</code> objects with the given type, concurrency,
     * and holdability.
     * <P>
     * This method is the same as the <code>prepareStatement</code> method
     * above, but it allows the default result set
     * type, concurrency, and holdability to be overridden.
     *
     * @param sql a <code>String</code> object that is the SQL statement to
     *            be sent to the database; may contain one or more ? IN
     *            parameters
     * @param resultSetType one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *         <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *         <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.CONCUR_READ_ONLY</code> or
     *         <code>ResultSet.CONCUR_UPDATABLE</code>
     * @param resultSetHoldability one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *         <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled SQL statement, that will generate
     *         <code>ResultSet</code> objects with the given type,
     *         concurrency, and holdability
     * @exception SQLException if a database access error occurs
     *            or the given parameters are not <code>ResultSet</code>
     *            constants indicating type, concurrency, and holdability
     * @see ResultSet
     * @since 1.4
     */
    public PreparedStatement prepareStatement (String sql, int resultSetType,
            int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        this.assertOpen();
        return  new CallableStatementSapDB(this, sql, resultSetType, resultSetConcurrency,
                resultSetHoldability);
    }

    /**
     * Creates a <code>CallableStatement</code> object that will generate
     * <code>ResultSet</code> objects with the given type and concurrency.
     * This method is the same as the <code>prepareCall</code> method
     * above, but it allows the default result set
     * type, result set concurrency type and holdability to be overridden.
     *
     * @param sql a <code>String</code> object that is the SQL statement to
     *            be sent to the database; may contain on or more ? parameters
     * @param resultSetType one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.TYPE_FORWARD_ONLY</code>,
     *         <code>ResultSet.TYPE_SCROLL_INSENSITIVE</code>, or
     *         <code>ResultSet.TYPE_SCROLL_SENSITIVE</code>
     * @param resultSetConcurrency one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.CONCUR_READ_ONLY</code> or
     *         <code>ResultSet.CONCUR_UPDATABLE</code>
     * @param resultSetHoldability one of the following <code>ResultSet</code>
     *        constants:
     *         <code>ResultSet.HOLD_CURSORS_OVER_COMMIT</code> or
     *         <code>ResultSet.CLOSE_CURSORS_AT_COMMIT</code>
     * @return a new <code>CallableStatement</code> object, containing the
     *         pre-compiled SQL statement, that will generate
     *         <code>ResultSet</code> objects with the given type,
     *         concurrency, and holdability
     * @exception SQLException if a database access error occurs
     *            or the given parameters are not <code>ResultSet</code>
     *            constants indicating type, concurrency, and holdability
     * @see ResultSet
     * @since 1.4
     */
    public CallableStatement prepareCall (String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        this.assertOpen();
        return  new CallableStatementSapDB(this, sql, resultSetType, resultSetConcurrency,
                resultSetHoldability);
    }

    /**
     * Creates a default <code>PreparedStatement</code> object that has
     * the capability to retrieve auto-generated keys. The given constant
     * tells the driver whether it should make auto-generated keys
     * available for retrieval.  This parameter is ignored if the SQL
     * statement is not an <code>INSERT</code> statement.
     * <P>
     * <B>Note:</B> This method is optimized for handling
     * parametric SQL statements that benefit from precompilation. If
     * the driver supports precompilation,
     * the method <code>prepareStatement</code> will send
     * the statement to the database for precompilation. Some drivers
     * may not support precompilation. In this case, the statement may
     * not be sent to the database until the <code>PreparedStatement</code>
     * object is executed.  This has no direct effect on users; however, it does
     * affect which methods throw certain SQLExceptions.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code>
     * and have a concurrency level of <code>CONCUR_READ_ONLY</code>.
     *
     * @param sql an SQL statement that may contain one or more '?' IN
     *        parameter placeholders
     * @param autoGeneratedKeys a flag indicating whether auto-generated keys
     *        should be returned; one of the following <code>Statement</code>
     *        constants:
     * @param autoGeneratedKeys a flag indicating that auto-generated keys should be returned, one of
     *        <code>Statement.RETURN_GENERATED_KEYS</code> or
     *	      <code>Statement.NO_GENERATED_KEYS</code>.
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled SQL statement, that will have the capability of
     *         returning auto-generated keys
     * @exception SQLException if a database access error occurs
     *         or the given parameter is not a <code>Statement</code>
     *         constant indicating whether auto-generated keys should be
     *         returned
     * @since 1.4
     */
    public PreparedStatement prepareStatement (String sql, int autoGeneratedKeys)
        throws SQLException
    {

        /**@todo: Implement this java.sql.Connection method*/
        throw  new UnsupportedOperationException
            (MessageTranslator.translate(MessageKey.ERROR_PREPARESTATEMENT_NOTIMPLEMENTED));
    }

    /**
     * Creates a default <code>PreparedStatement</code> object capable
     * of returning the auto-generated keys designated by the given array.
     * This array contains the indexes of the columns in the target
     * table that contain the auto-generated keys that should be made
     * available. This array is ignored if the SQL
     * statement is not an <code>INSERT</code> statement.
     * <P>
     * An SQL statement with or without IN parameters can be
     * pre-compiled and stored in a <code>PreparedStatement</code> object. This
     * object can then be used to efficiently execute this statement
     * multiple times.
     * <P>
     * <B>Note:</B> This method is optimized for handling
     * parametric SQL statements that benefit from precompilation. If
     * the driver supports precompilation,
     * the method <code>prepareStatement</code> will send
     * the statement to the database for precompilation. Some drivers
     * may not support precompilation. In this case, the statement may
     * not be sent to the database until the <code>PreparedStatement</code>
     * object is executed.  This has no direct effect on users; however, it does
     * affect which methods throw certain SQLExceptions.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code>
     * and have a concurrency level of <code>CONCUR_READ_ONLY</code>.
     *
     * @param sql an SQL statement that may contain one or more '?' IN
     *        parameter placeholders
     * @param columnIndexes an array of column indexes indicating the columns
     *        that should be returned from the inserted row or rows
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled statement, that is capable of returning the
     *         auto-generated keys designated by the given array of column
     *         indexes
     * @exception SQLException if a database access error occurs
     *
     * @since 1.4
     */
    public PreparedStatement prepareStatement (String sql, int columnIndexes[])
        throws SQLException
    {

        /**@todo: Implement this java.sql.Connection method*/
        throw  new UnsupportedOperationException
            (MessageTranslator.translate(MessageKey.ERROR_PREPARESTATEMENT_NOTIMPLEMENTED));
    }

    /**
     * Creates a default <code>PreparedStatement</code> object capable
     * of returning the auto-generated keys designated by the given array.
     * This array contains the names of the columns in the target
     * table that contain the auto-generated keys that should be returned.
     * This array is ignored if the SQL
     * statement is not an <code>INSERT</code> statement.
     * <P>
     * An SQL statement with or without IN parameters can be
     * pre-compiled and stored in a <code>PreparedStatement</code> object. This
     * object can then be used to efficiently execute this statement
     * multiple times.
     * <P>
     * <B>Note:</B> This method is optimized for handling
     * parametric SQL statements that benefit from precompilation. If
     * the driver supports precompilation,
     * the method <code>prepareStatement</code> will send
     * the statement to the database for precompilation. Some drivers
     * may not support precompilation. In this case, the statement may
     * not be sent to the database until the <code>PreparedStatement</code>
     * object is executed.  This has no direct effect on users; however, it does
     * affect which methods throw certain SQLExceptions.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code>
     * and have a concurrency level of <code>CONCUR_READ_ONLY</code>.
     *
     * @param sql an SQL statement that may contain one or more '?' IN
     *        parameter placeholders
     * @param columnNames an array of column names indicating the columns
     *        that should be returned from the inserted row or rows
     * @return a new <code>PreparedStatement</code> object, containing the
     *         pre-compiled statement, that is capable of returning the
     *         auto-generated keys designated by the given array of column
     *         names
     * @exception SQLException if a database access error occurs
     *
     * @since 1.4
     */
    public PreparedStatement prepareStatement (String sql, String columnNames[])
        throws SQLException
    {
        /**@todo: Implement this java.sql.Connection method*/
        throw  new UnsupportedOperationException
            (MessageTranslator.translate(MessageKey.ERROR_PREPARESTATEMENT_NOTIMPLEMENTED));
    }

    /**
     * Sets this connection back to 'factory settings', i.e. restores what
     * was either default setting or connection property setting.
     */
   public void reinitialize()
        throws SQLException
    {
        if(this.parseCache!=null) {
            Object[] parseInfos=this.parseCache.clearAll();
            for(int i=0; i<parseInfos.length; ++i) {
                ((Parseinfo)parseInfos[i]).dropParseIDs();
            }
        }

        // reset the isolation level
        String isolation=this.connectProperties.getProperty(DriverSapDB.isolationName_C);
        int    preset_isolationlevel= isolation==null
            ? Connection.TRANSACTION_READ_COMMITTED
            : DriverSapDB.isolevelString2Jdbc(isolation);
        if(preset_isolationlevel != getTransactionIsolation()) {
            setTransactionIsolation(preset_isolationlevel);
        }
        setAutoCommit(DriverSapDB.getBooleanProperty(this.connectProperties,
                                                     DriverSapDB.autocommitName_C,
                                                     true));
        this.resultSetHoldability = StatementSapDB.defaultHoldability_C;
        this.typeMap=null;
    }

    int getKernelVersion() {
        return this.kernelversion;
    }

    private void setKernelFeatureRequest(int feature){
       this.kernelFeatures[2*(feature-1)+1]=1;
    }

    public boolean isKernelFeaturesupported(int feature){
       boolean erg =  (this.kernelFeatures[2*(feature-1)+1]==1)?true:false;
       return erg;
    }
    
    void stopKernelTrace(){
        try {
            this.executeSQLString("DIAGNOSE VTRACE FLUSH", ConnectionSapDB.GC_ALLOWED);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
    
    public Properties getConnectProperties(){
        return this.connectProperties;   
     }

    public boolean isReleaseSavePointSupported() {
    	return this.releaseSavePointSupported;
	}
    
    public void setReleaseSavePointSupported(boolean value) {
    	this.releaseSavePointSupported = false;
    }
    void setInTransaction(boolean inTransaction) {
        this.inTransaction = inTransaction;
    }
}
