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

org.firebirdsql.gds.impl.wire.AbstractJavaGDSImpl Maven / Gradle / Ivy

There is a newer version: 2.2.7
Show newest version
/*
 * $Id: AbstractJavaGDSImpl.java 58723 2013-10-26 14:51:28Z mrotteveel $
 * 
 * Firebird Open Source J2ee connector - jdbc driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program 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
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a CVS history command.
 *
 * All rights reserved.
 */
/*
 * The Original Code is the Firebird Java GDS implementation.
 * The Initial Developer of the Original Code is Alejandro Alberola.
 * Portions created by Alejandro Alberola are Copyright (C) 2001
 * Boix i Oltra, S.L. All Rights Reserved.
 */
package org.firebirdsql.gds.impl.wire;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;

import org.firebirdsql.encodings.EncodingFactory;
import org.firebirdsql.gds.BlobParameterBuffer;
import org.firebirdsql.gds.DatabaseParameterBuffer;
import org.firebirdsql.gds.GDS;
import org.firebirdsql.gds.GDSException;
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.gds.IscBlobHandle;
import org.firebirdsql.gds.IscDbHandle;
import org.firebirdsql.gds.IscStmtHandle;
import org.firebirdsql.gds.IscSvcHandle;
import org.firebirdsql.gds.IscTrHandle;
import org.firebirdsql.gds.ServiceParameterBuffer;
import org.firebirdsql.gds.ServiceRequestBuffer;
import org.firebirdsql.gds.TransactionParameterBuffer;
import org.firebirdsql.gds.XSQLDA;
import org.firebirdsql.gds.XSQLVAR;
import org.firebirdsql.gds.EventHandle;
import org.firebirdsql.gds.EventHandler;
import org.firebirdsql.gds.impl.*;
import org.firebirdsql.logging.Logger;
import org.firebirdsql.logging.LoggerFactory;

/**
 * Describe class GDS_Impl here.
 * 
 * @author Alejandro Alberola
 * @author David Jencks
 * @version 1.0
 */
public abstract class AbstractJavaGDSImpl extends AbstractGDS implements GDS {

	public static final String PURE_JAVA_TYPE_NAME = "PURE_JAVA";

	private static Logger log = LoggerFactory.getLogger(
			AbstractJavaGDSImpl.class, false);

	/* Operation (packet) types */

	static final int op_void = 0; /* Packet has been voided */

	static final int op_connect = 1; /* Connect to remote server */

	static final int op_exit = 2; /* Remote end has exitted */

	static final int op_accept = 3; /* Server accepts connection */

	static final int op_reject = 4; /* Server rejects connection */

	static final int op_protocol = 5; /* Protocol selection */

	static final int op_disconnect = 6; /* Connect is going away */

	static final int op_credit = 7; /* Grant (buffer) credits */

	static final int op_continuation = 8; /* Continuation packet */

	static final int op_response = 9; /* Generic response block */

	/* Page server operations */

	static final int op_open_file = 10; /* Open file for page service */

	static final int op_create_file = 11; /* Create file for page service */

	static final int op_close_file = 12; /* Close file for page service */

	static final int op_read_page = 13; /* optionally lock and read page */

	static final int op_write_page = 14; /* write page and optionally release lock */

	static final int op_lock = 15; /* sieze lock */

	static final int op_convert_lock = 16; /* convert existing lock */

	static final int op_release_lock = 17; /* release existing lock */

	static final int op_blocking = 18; /* blocking lock message */

	/* Full context server operations */

	static final int op_attach = 19; /* Attach database */

	static final int op_create = 20; /* Create database */

	static final int op_detach = 21; /* Detach database */

	static final int op_compile = 22; /* Request based operations */

	static final int op_start = 23;

	static final int op_start_and_send = 24;

	static final int op_send = 25;

	static final int op_receive = 26;

	static final int op_unwind = 27;

	static final int op_release = 28;

	static final int op_transaction = 29; /* Transaction operations */

	static final int op_commit = 30;

	static final int op_rollback = 31;

	static final int op_prepare = 32;

	static final int op_reconnect = 33;

	static final int op_create_blob = 34; /* Blob operations */

	static final int op_open_blob = 35;

	static final int op_get_segment = 36;

	static final int op_put_segment = 37;

	static final int op_cancel_blob = 38;

	static final int op_close_blob = 39;

	static final int op_info_database = 40; /* Information services */

	static final int op_info_request = 41;

	static final int op_info_transaction = 42;

	static final int op_info_blob = 43;

	static final int op_batch_segments = 44; /* Put a bunch of blob segments */

	static final int op_mgr_set_affinity = 45; /* Establish server affinity */

	static final int op_mgr_clear_affinity = 46; /* Break server affinity */

	static final int op_mgr_report = 47; /* Report on server */

	static final int op_que_events = 48; /* Que event notification request */

	static final int op_cancel_events = 49; /* Cancel event notification request */

	static final int op_commit_retaining = 50; /* Commit retaining (what else) */

	static final int op_prepare2 = 51; /* Message form of prepare */

	static final int op_event = 52; /* Completed event request (asynchronous) */

	static final int op_connect_request = 53; /*
												 * Request to establish
												 * connection
												 */

	static final int op_aux_connect = 54; /* Establish auxiliary connection */

	static final int op_ddl = 55; /* DDL call */

	static final int op_open_blob2 = 56;

	static final int op_create_blob2 = 57;

	static final int op_get_slice = 58;

	static final int op_put_slice = 59;

	static final int op_slice = 60; /*
									 * Successful response to static final int
									 * op_get_slice
									 */

	static final int op_seek_blob = 61; /* Blob seek operation */

	/* DSQL operations */

	static final int op_allocate_statement = 62; /* allocate a statement handle */

	static final int op_execute = 63; /* execute a prepared statement */

	static final int op_exec_immediate = 64; /* execute a statement */

	static final int op_fetch = 65; /* fetch a record */

	static final int op_fetch_response = 66; /* response for record fetch */

	static final int op_free_statement = 67; /* free a statement */

	static final int op_prepare_statement = 68; /* prepare a statement */

	static final int op_set_cursor = 69; /* set a cursor name */

	static final int op_info_sql = 70;

	static final int op_dummy = 71; /* dummy packet to detect loss of client */

	static final int op_response_piggyback = 72; /* response block for piggybacked messages */

	static final int op_start_and_receive = 73;

	static final int op_start_send_and_receive = 74;

	static final int op_exec_immediate2 = 75; /* execute an immediate statement with msgs */

	static final int op_execute2 = 76; /* execute a statement with msgs */

	static final int op_insert = 77;

	static final int op_sql_response = 78; /* response from execute; exec immed; insert */

	static final int op_transact = 79;

	static final int op_transact_response = 80;

	static final int op_drop_database = 81;

	static final int op_service_attach = 82;

	static final int op_service_detach = 83;

	static final int op_service_info = 84;

	static final int op_service_start = 85;

	static final int op_rollback_retaining = 86;
	
	static final int op_cancel = 91;

	static final int MAX_BUFFER_SIZE = 1024;
	
	// TODO: sql_prepare_info* and describe_select_info* are identical, remove one?
	
    final static byte[] sql_prepare_info2 = new byte[] {
            ISCConstants.isc_info_sql_stmt_type,
            ISCConstants.isc_info_sql_select,
            ISCConstants.isc_info_sql_describe_vars,
            ISCConstants.isc_info_sql_sqlda_seq,
            ISCConstants.isc_info_sql_type, ISCConstants.isc_info_sql_sub_type,
            ISCConstants.isc_info_sql_scale, ISCConstants.isc_info_sql_length,
            ISCConstants.isc_info_sql_field,
            ISCConstants.isc_info_sql_relation,
            ISCConstants.isc_info_sql_relation_alias,
            ISCConstants.isc_info_sql_owner, ISCConstants.isc_info_sql_alias,
            ISCConstants.isc_info_sql_describe_end};

    /**
     * Info buffer for Firebird 1.5 and below - it does not support the relation
     * alias info item.
     */
    final static byte[] sql_prepare_info15 = new byte[] {
            ISCConstants.isc_info_sql_stmt_type,
            ISCConstants.isc_info_sql_select,
            ISCConstants.isc_info_sql_describe_vars,
            ISCConstants.isc_info_sql_sqlda_seq,
            ISCConstants.isc_info_sql_type, ISCConstants.isc_info_sql_sub_type,
            ISCConstants.isc_info_sql_scale, ISCConstants.isc_info_sql_length,
            ISCConstants.isc_info_sql_field,
            ISCConstants.isc_info_sql_relation,
            ISCConstants.isc_info_sql_owner, ISCConstants.isc_info_sql_alias,
            ISCConstants.isc_info_sql_describe_end};

    final static byte[] describe_select_info2 = new byte[] {
            ISCConstants.isc_info_sql_stmt_type,
            ISCConstants.isc_info_sql_select,
            ISCConstants.isc_info_sql_describe_vars,
            ISCConstants.isc_info_sql_sqlda_seq,
            ISCConstants.isc_info_sql_type, ISCConstants.isc_info_sql_sub_type,
            ISCConstants.isc_info_sql_scale, ISCConstants.isc_info_sql_length,
            ISCConstants.isc_info_sql_field,
            ISCConstants.isc_info_sql_relation,
            ISCConstants.isc_info_sql_relation_alias,
            ISCConstants.isc_info_sql_owner, ISCConstants.isc_info_sql_alias,
            ISCConstants.isc_info_sql_describe_end};

    /**
     * Info buffer for Firebird 1.5 and below - it does not support the relation
     * alias info item.
     */
    final static byte[] describe_select_info15 = new byte[] {
            ISCConstants.isc_info_sql_stmt_type,
            ISCConstants.isc_info_sql_select,
            ISCConstants.isc_info_sql_describe_vars,
            ISCConstants.isc_info_sql_sqlda_seq,
            ISCConstants.isc_info_sql_type, ISCConstants.isc_info_sql_sub_type,
            ISCConstants.isc_info_sql_scale, ISCConstants.isc_info_sql_length,
            ISCConstants.isc_info_sql_field,
            ISCConstants.isc_info_sql_relation,
            ISCConstants.isc_info_sql_owner, ISCConstants.isc_info_sql_alias,
            ISCConstants.isc_info_sql_describe_end};
    
    final static byte[] describe_bind_info2 = new byte[] {
            ISCConstants.isc_info_sql_stmt_type,
            ISCConstants.isc_info_sql_bind,
            ISCConstants.isc_info_sql_describe_vars,
            ISCConstants.isc_info_sql_sqlda_seq,
            ISCConstants.isc_info_sql_type, ISCConstants.isc_info_sql_sub_type,
            ISCConstants.isc_info_sql_scale, ISCConstants.isc_info_sql_length,
            ISCConstants.isc_info_sql_field,
            ISCConstants.isc_info_sql_relation,
            ISCConstants.isc_info_sql_relation_alias,
            ISCConstants.isc_info_sql_owner, ISCConstants.isc_info_sql_alias,
            ISCConstants.isc_info_sql_describe_end};

    /**
     * Info buffer for Firebird 1.5 and below - it does not support the relation
     * alias info item.
     */
    final static byte[] describe_bind_info15 = new byte[] {
            ISCConstants.isc_info_sql_stmt_type,
            ISCConstants.isc_info_sql_bind,
            ISCConstants.isc_info_sql_describe_vars,
            ISCConstants.isc_info_sql_sqlda_seq,
            ISCConstants.isc_info_sql_type, ISCConstants.isc_info_sql_sub_type,
            ISCConstants.isc_info_sql_scale, ISCConstants.isc_info_sql_length,
            ISCConstants.isc_info_sql_field,
            ISCConstants.isc_info_sql_relation,
            ISCConstants.isc_info_sql_owner, ISCConstants.isc_info_sql_alias,
            ISCConstants.isc_info_sql_describe_end};
    
	public AbstractJavaGDSImpl() {
		super(GDSType.getType(PURE_JAVA_TYPE_NAME));
	}

	// Database functions

	/**
	 * isc_create_database creates a database based on the file
	 * name and Clumplet of database properties supplied. The supplied db handle
	 * is attached to the newly created database.
	 * 
	 * @param file_name
	 *            a String the file name, including host and
	 *            port, for the database. The expected format is
	 *            host:port:path_to_file. The value for host is localhost if not
	 *            supplied. The value for port is 3050 if not supplied.
	 * @param db_handle
	 *            an isc_db_handle The db handle to attach to the
	 *            new database.
	 * @param databaseParameterBuffer
	 *            a Clumplet The parameters for the new database
	 *            and the attachment to it. See docs for dpb (database parameter
	 *            block.)
	 * @exception GDSException
	 *                if an error occurs
	 */
	public void iscCreateDatabase(String file_name, IscDbHandle db_handle,
			DatabaseParameterBuffer databaseParameterBuffer)
			throws GDSException {

	    DbAttachInfo dbai = new DbAttachInfo(file_name);
	    internalAttachDatabase(dbai, db_handle, databaseParameterBuffer, true);
	}

	/**
	 * @deprecated This method will be removed in Jaybird 2.3
	 */
	@Deprecated
	public void internalAttachDatabase(String host, Integer port,
			String file_name, IscDbHandle db_handle,
			DatabaseParameterBuffer databaseParameterBuffer)
			throws GDSException {
	    // TODO: Unused, remove?
		DbAttachInfo dbai = new DbAttachInfo(host, port, file_name);
		internalAttachDatabase(dbai, db_handle, databaseParameterBuffer, false);
	}

	public void iscAttachDatabase(String connectString, IscDbHandle db_handle,
			DatabaseParameterBuffer databaseParameterBuffer)
			throws GDSException {

		DbAttachInfo dbai = new DbAttachInfo(connectString);
		internalAttachDatabase(dbai, db_handle, databaseParameterBuffer, false);
	}

	final static byte[] describe_database_info = new byte[] {
			ISCConstants.isc_info_db_sql_dialect,
			ISCConstants.isc_info_firebird_version,
			ISCConstants.isc_info_ods_version,
			ISCConstants.isc_info_ods_minor_version,
			ISCConstants.isc_info_implementation,
			ISCConstants.isc_info_db_class, ISCConstants.isc_info_base_level,
			ISCConstants.isc_info_end };

	protected void internalAttachDatabase(DbAttachInfo dbai, IscDbHandle db_handle,
			DatabaseParameterBuffer databaseParameterBuffer, boolean create)
			throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_db_handle_impl db = validateHandle(db_handle);

		synchronized (db) {
			connect(db, dbai, databaseParameterBuffer);
            
            String filenameCharset = databaseParameterBuffer.getArgumentAsString(
                DatabaseParameterBufferExtension.FILENAME_CHARSET);
            
			try {
				if (debug)
					log.debug(create ? "op_create " : "op_attach ");
				db.out.writeInt(create ? op_create : op_attach);
				db.out.writeInt(0); // packet->p_atch->p_atch_database
				db.out.writeString(dbai.getFileName(), filenameCharset);

			    databaseParameterBuffer = ((DatabaseParameterBufferExtension)
			            databaseParameterBuffer).removeExtensionParams();
                addProcessId(databaseParameterBuffer);
                addProcessName(databaseParameterBuffer);
                if (!databaseParameterBuffer.hasArgument(DatabaseParameterBuffer.SQL_DIALECT)) {
                    databaseParameterBuffer.addArgument(DatabaseParameterBuffer.SQL_DIALECT, ISCConstants.SQL_DIALECT_CURRENT);
                }

				db.out.writeTyped(ISCConstants.isc_dpb_version1, (Xdrable) databaseParameterBuffer);
				db.out.flush();
				if (debug)
					log.debug("sent");

				try {
					receiveResponse(db, -1);
					db.setRdbId(db.getResp_object());
				} catch (GDSException ge) {
					disconnect(db);
					throw ge;
				}
				// read database information
				byte[] iscDatabaseInfo = iscDatabaseInfo(db, describe_database_info, 1024);
                parseAttachDatabaseInfo(iscDatabaseInfo, db);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_write_err);
			}
		}
	}

    /**
     * Adds the processId (pid) to the dpb, if available.
     * 
     * @param databaseParameterBuffer
     */
    protected void addProcessName(DatabaseParameterBuffer databaseParameterBuffer) {
        String processName = getSystemPropertyPrivileged("org.firebirdsql.jdbc.processName");
        if (processName != null) {
            databaseParameterBuffer.addArgument(DatabaseParameterBuffer.PROCESS_NAME, processName);
        }
    }

    /**
     * Adds the processName to the dpb, if available.
     * 
     * @param databaseParameterBuffer
     */
    protected void addProcessId(DatabaseParameterBuffer databaseParameterBuffer) {
        String pidStr = getSystemPropertyPrivileged("org.firebirdsql.jdbc.pid");
        if (pidStr != null) {
            try {
                int pid = Integer.parseInt(pidStr);
                databaseParameterBuffer.addArgument(DatabaseParameterBuffer.PROCESS_ID, pid);
            } catch(NumberFormatException ex) {
                // ignore
            }
        }
    }

	private String getSystemPropertyPrivileged(final String propertyName) {
	    return (String)AccessController.doPrivileged(new PrivilegedAction() {
	       public Object run() {
	           return System.getProperty(propertyName);
	       } 
	    });
	}
	
	public byte[] iscDatabaseInfo(IscDbHandle handle, byte[] items,
			int buffer_length) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_db_handle_impl db = validateHandle(handle);

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_info_database ");
				db.out.writeInt(op_info_database);
				db.out.writeInt(db.getRdbId());
				db.out.writeInt(0);
				db.out.writeBuffer(items);
				db.out.writeInt(buffer_length);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				// if (debug) log.debug("parseSqlInfo: first 2 bytes are " +
				// iscVaxInteger(db.getResp_data(), 0, 2) + " or: " +
				// db.getResp_data()[0] + ", " + db.getResp_data()[1]);
				return db.getResp_data_truncated();
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			}
		}
	}

	public byte[] iscBlobInfo(IscBlobHandle handle, byte[] items,
			int buffer_length) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_blob_handle_impl blob = validateHandle(handle);
		isc_db_handle_impl db = validateHandle(blob.getDb());

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_info_blob ");
				db.out.writeInt(op_info_blob);
				db.out.writeInt(blob.getRbl_id());
				db.out.writeInt(0);
				db.out.writeBuffer(items);
				db.out.writeInt(buffer_length);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				// if (debug) log.debug("parseSqlInfo: first 2 bytes are " +
				// iscVaxInteger(db.getResp_data(), 0, 2) + " or: " +
				// db.getResp_data()[0] + ", " + db.getResp_data()[1]);
				return db.getResp_data_truncated();
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			}
		}
	}

	public void iscSeekBlob(IscBlobHandle handle, int position, int seekMode)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_blob_handle_impl blob = validateHandle(handle);
		isc_db_handle_impl db = validateHandle(blob.getDb());

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_info_blob ");
				db.out.writeInt(op_seek_blob);
				db.out.writeInt(blob.getRbl_id());
				db.out.writeInt(seekMode);
				db.out.writeInt(position);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				blob.setPosition(db.getResp_object());
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			}
		}
	}

	/**
	 * Parse database info returned after attach. This method assumes that it is
	 * not truncated.
	 * 
	 * @param info
	 *            information returned by isc_database_info call
	 * @param handle
	 *            isc_db_handle to set connection parameters
	 * @throws GDSException
	 *             if something went wrong :))
	 */
	private void parseAttachDatabaseInfo(byte[] info, IscDbHandle handle)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		if (debug)
			log.debug("parseDatabaseInfo: first 2 bytes are "
					+ iscVaxInteger(info, 0, 2) + " or: " + info[0] + ", "
					+ info[1]);
		int value = 0;
		int len = 0;
		int i = 0;
		isc_db_handle_impl db = (isc_db_handle_impl) handle;
		while (info[i] != ISCConstants.isc_info_end) {
			switch (info[i++]) {
			case ISCConstants.isc_info_db_sql_dialect:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				value = iscVaxInteger(info, i, len);
				i += len;
				db.setDialect(value);
				if (debug)
					log.debug("isc_info_db_sql_dialect:" + value);
				break;
			case ISCConstants.isc_info_ods_version:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				value = iscVaxInteger(info, i, len);
				i += len;
				db.setODSMajorVersion(value);
				if (debug)
					log.debug("isc_info_ods_version:" + value);
				break;
			case ISCConstants.isc_info_ods_minor_version:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				value = iscVaxInteger(info, i, len);
				i += len;
				db.setODSMinorVersion(value);
				if (debug)
					log.debug("isc_info_ods_minor_version:" + value);
				break;
			case ISCConstants.isc_info_firebird_version:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				byte[] fb_vers = new byte[len - 2];
				System.arraycopy(info, i + 2, fb_vers, 0, len - 2);
				i += len;
				String fb_versS = new String(fb_vers);
				db.setVersion(fb_versS);
				if (debug)
					log.debug("isc_info_firebird_version:" + fb_versS);
				break;
			case ISCConstants.isc_info_implementation:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				byte[] impl = new byte[len - 2];
				System.arraycopy(info, i + 2, impl, 0, len - 2);
				i += len;
				break;
			case ISCConstants.isc_info_db_class:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				byte[] db_class = new byte[len - 2];
				System.arraycopy(info, i + 2, db_class, 0, len - 2);
				i += len;
				break;
			case ISCConstants.isc_info_base_level:
				len = iscVaxInteger(info, i, 2);
				i += 2;
				byte[] base_level = new byte[len - 2];
				System.arraycopy(info, i + 2, base_level, 0, len - 2);
				i += len;
				break;
			case ISCConstants.isc_info_truncated:
				if (debug)
					log.debug("isc_info_truncated ");
				return;
			default:
				throw new GDSException(ISCConstants.isc_dsql_sqlda_err);
			}
		}
	}

	public void iscDetachDatabase(IscDbHandle db_handle) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_db_handle_impl db = validateHandle(db_handle);

		synchronized (db) {
			try {
                if (db.eventCoordinator != null){
                    db.eventCoordinator.close();
                }

				if (debug)
					log.debug("op_detach ");
				db.out.writeInt(op_detach);
				db.out.writeInt(db.getRdbId());
				db.out.writeInt(op_disconnect);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);

			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			} finally {
				try {
					disconnect(db);
				} catch (IOException ex2) {
					throw new GDSException(ISCConstants.isc_network_error);
				}
			}
		}
	}

	public void iscDropDatabase(IscDbHandle db_handle) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_db_handle_impl db = validateHandle(db_handle);

		synchronized (db) {

			try {
				if (debug)
					log.debug("op_drop_database ");
				db.out.writeInt(op_drop_database);
				db.out.writeInt(db.getRdbId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			}
		}

	}

	public byte[] iscExpandDpb(byte[] dpb, int dpb_length, int param,
			Object[] params) throws GDSException {
		return dpb;
	}

	// Transaction functions

	public void iscStartTransaction(IscTrHandle tr_handle,
			IscDbHandle db_handle,
			// Set tpb
			// int tpb_length,
			TransactionParameterBuffer tpb) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(db_handle);

		TransactionParameterBufferImpl tpbImpl = (TransactionParameterBufferImpl) tpb;

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.NOTRANSACTION) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONSTARTING);

			try {
				if (debug)
					log.debug("op_transaction ");
				db.out.writeInt(op_transaction);
				db.out.writeInt(db.getRdbId());

				db.out.writeTyped(ISCConstants.isc_tpb_version3, tpbImpl);
				db.out.flush();
				if (debug)
					log.debug("sent");

				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			}
			tr.setTransactionId(db.getResp_object());

			tr.setDbHandle(db);
			tr.setState(AbstractIscTrHandle.TRANSACTIONSTARTED);
		}

	}

	public void iscReconnectTransaction(IscTrHandle tr_handle,
			IscDbHandle db_handle, long transactionId) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(db_handle);

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.NOTRANSACTION)
				throw new GDSException(ISCConstants.isc_tra_state);

			tr.setState(AbstractIscTrHandle.TRANSACTIONSTARTING);

			try {
				if (debug)
					log.debug("op_reconnect ");
				db.out.writeInt(op_reconnect);

				// TODO check if sending db handle is needed, most likely not
				db.out.writeInt(db.getRdbId());
				byte[] buf = new byte[4];
				for (int i = 0; i < 4; i++) {
					buf[i] = (byte) (transactionId >>> (i * 8));
				}
				db.out.writeBuffer(buf);
				db.out.flush();
				if (debug)
					log.debug("sent");
				// out.flush();
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_network_error, ex);
			}
			tr.setTransactionId(db.getResp_object());

			// tr.rtr_rdb = db;
			tr.setDbHandle(db);
			tr.setState(AbstractIscTrHandle.TRANSACTIONSTARTED);
			// db.rdb_transactions.addElement(tr);
		}
	}

	public void iscCommitTransaction(IscTrHandle tr_handle) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.TRANSACTIONSTARTED
					&& tr.getState() != AbstractIscTrHandle.TRANSACTIONPREPARED) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONCOMMITTING);

			try {
				if (debug) {
					log.debug("op_commit ");
					log.debug("tr.rtr_id: " + tr.getTransactionId());
				}
				db.out.writeInt(op_commit);
				db.out.writeInt(tr.getTransactionId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}

			tr.setState(AbstractIscTrHandle.NOTRANSACTION);
			// tr.rtr_rdb = null;
			// db.rdb_transactions.removeElement(tr);
			tr.unsetDbHandle();
		}

	}

	public void iscCommitRetaining(IscTrHandle tr_handle) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.TRANSACTIONSTARTED
					&& tr.getState() != AbstractIscTrHandle.TRANSACTIONPREPARED) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONCOMMITTING);

			try {
				if (debug)
					log.debug("op_commit_retaining ");
				db.out.writeInt(op_commit_retaining);
				db.out.writeInt(tr.getTransactionId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONSTARTED);
		}

	}

	public void iscPrepareTransaction(IscTrHandle tr_handle)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.TRANSACTIONSTARTED) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONPREPARING);
			try {
				if (debug)
					log.debug("op_prepare ");
				db.out.writeInt(op_prepare);
				db.out.writeInt(tr.getTransactionId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONPREPARED);
		}
	}

	public void iscPrepareTransaction2(IscTrHandle tr_handle, byte[] bytes)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.TRANSACTIONSTARTED) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONPREPARING);
			try {
				if (debug)
					log.debug("op_prepare2 ");
				db.out.writeInt(op_prepare2);
				db.out.writeInt(tr.getTransactionId());
				db.out.writeBuffer(bytes);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}

			tr.setState(AbstractIscTrHandle.TRANSACTIONPREPARED);
		}
	}

	public void iscRollbackTransaction(IscTrHandle tr_handle)
			throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			if (tr.getState() == AbstractIscTrHandle.NOTRANSACTION) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONROLLINGBACK);

			try {
				if (debug)
					log.debug("op_rollback ");
				db.out.writeInt(op_rollback);
				db.out.writeInt(tr.getTransactionId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			} finally {
				tr.setState(AbstractIscTrHandle.NOTRANSACTION);
				tr.unsetDbHandle();
			}
		}
	}

	public void iscRollbackRetaining(IscTrHandle tr_handle) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			if (tr.getState() != AbstractIscTrHandle.TRANSACTIONSTARTED
					&& tr.getState() != AbstractIscTrHandle.TRANSACTIONPREPARED) {
				throw new GDSException(ISCConstants.isc_tra_state);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONROLLINGBACK);

			try {
				if (debug)
					log.debug("op_rollback_retaining ");
				db.out.writeInt(op_rollback_retaining);
				db.out.writeInt(tr.getTransactionId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
			tr.setState(AbstractIscTrHandle.TRANSACTIONSTARTED);
		}

	}

	public byte[] iscTransactionInformation(IscTrHandle tr_handle,
			byte[] requestBuffer, int bufferLen) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(tr.getDbHandle());

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_info_transaction ");
				db.out.writeInt(op_info_transaction);
				db.out.writeInt(tr.getTransactionId());
				db.out.writeInt(0);
				db.out.writeBuffer(requestBuffer);
				db.out.writeInt(bufferLen);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				return db.getResp_data();
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	// Dynamic SQL

	public void iscDsqlAllocateStatement(IscDbHandle db_handle,
			IscStmtHandle stmt_handle) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_db_handle_impl db = validateHandle(db_handle);
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_allocate_statement ");
				db.out.writeInt(op_allocate_statement);
				db.out.writeInt(db.getRdbId());
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				stmt.setRsr_id(db.getResp_object());
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}

			stmt.setRsr_rdb(db);

			/** @todo implement statement handle tracking correctly */
			// db.rdb_sql_requests.addElement(stmt);
			stmt.setAllRowsFetched(false);
		}

	}

	public void isc_dsql_alloc_statement2(IscDbHandle db_handle,
			IscStmtHandle stmt_handle) throws GDSException {
		throw new GDSException(ISCConstants.isc_wish_list);
	}

	
	public XSQLDA iscDsqlDescribe(IscStmtHandle stmt_handle, int da_version) throws GDSException {
		byte[] describeSelectInfo = getDescribeSelectInfo(stmt_handle);
        byte[] buffer = iscDsqlSqlInfo(stmt_handle, describeSelectInfo, MAX_BUFFER_SIZE);
        return parseSqlInfo(stmt_handle, buffer, buffer.length, describeSelectInfo);
	}

	public XSQLDA iscDsqlDescribeBind(IscStmtHandle stmt_handle, int da_version) throws GDSException {
		byte[] describeBindInfo = getDescribeBindInfo(stmt_handle);
        byte[] buffer = iscDsqlSqlInfo(stmt_handle, describeBindInfo, MAX_BUFFER_SIZE);
        isc_stmt_handle_impl stmt = (isc_stmt_handle_impl) stmt_handle;
		stmt.setInSqlda(parseSqlInfo(stmt_handle, buffer, buffer.length, describeBindInfo));
		return stmt.getInSqlda();
	}

	public void iscDsqlExecute(IscTrHandle tr_handle,
			IscStmtHandle stmt_handle, int da_version, XSQLDA xsqlda)
			throws GDSException {

		iscDsqlExecute2(tr_handle, stmt_handle, da_version, xsqlda, null);
	}

	public void iscDsqlExecute2(IscTrHandle tr_handle,
			IscStmtHandle stmt_handle, int da_version, XSQLDA in_xsqlda,
			XSQLDA out_xsqlda) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);
		isc_db_handle_impl db = validateHandle(stmt.getRsr_rdb());

		synchronized (db) {
			XdrOutputStream out = db.out;
			try {
				if (debug)
					log.debug((out_xsqlda == null) ? "op_execute "
							: "op_execute2 ");

				out.writeInt((out_xsqlda == null) ? op_execute : op_execute2);
				out.writeInt(stmt.getRsr_id());
				out.writeInt(tr.getTransactionId());

				if (in_xsqlda != null) {
					out.writeBuffer(in_xsqlda.blr);
					out.writeInt(0); // message number = in_message_type
					out.writeInt(1); // stmt->rsr_bind_format
					out.writeSQLData(in_xsqlda);
				} else {
					out.writeBuffer(null);
					out.writeInt(0); // message number = in_message_type
					out.writeInt(0); // stmt->rsr_bind_format
				}

				if (out_xsqlda != null) {
					stmt.clearRows();
					// only need to clear if there is a
					out.writeBuffer(out_xsqlda.blr);
					out.writeInt(0); // out_message_number = out_message_type
				}
				out.flush();
				if (stmt.getOutSqlda() != null)
					stmt.notifyOpenResultSet();
				if (debug)
					log.debug("sent");
				int op = nextOperation(db.in);
				if (op == op_sql_response) {
					// this would be an Execute procedure
					stmt.ensureCapacity(1);
					receiveSqlResponse(db, out_xsqlda, stmt);
					op = nextOperation(db.in);
					stmt.setAllRowsFetched(true);
					stmt.setSingletonResult(true);
				} else {
					stmt.setSingletonResult(false);
					stmt.setAllRowsFetched(false);
				} // end of else
				receiveResponse(db, op);
                
                stmt.registerTransaction(tr);
                
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	public void iscDsqlExecuteImmediate(IscDbHandle db_handle,
			IscTrHandle tr_handle, String statement, int dialect, XSQLDA xsqlda)
			throws GDSException {
		iscDsqlExecImmed2(db_handle, tr_handle, statement, dialect, xsqlda,
				null);
	}

	public void iscDsqlExecuteImmediate(IscDbHandle db_handle,
			IscTrHandle tr_handle, String statement, String encoding,
			int dialect, XSQLDA xsqlda) throws GDSException {
		iscDsqlExecImmed2(db_handle, tr_handle, statement, encoding, dialect,
				xsqlda, null);
	}

	public void iscDsqlExecuteImmediate(IscDbHandle db_handle,
			IscTrHandle tr_handle, byte[] statement, int dialect, XSQLDA xsqlda)
			throws GDSException {

		iscDsqlExecImmed2(db_handle, tr_handle, statement, dialect, xsqlda,
				null);
	}

	public void iscDsqlExecImmed2(IscDbHandle db_handle, IscTrHandle tr_handle,
			String statement, int dialect, XSQLDA in_xsqlda, XSQLDA out_xsqlda)
			throws GDSException {
		iscDsqlExecImmed2(db_handle, tr_handle, statement, "NONE", dialect,
				in_xsqlda, out_xsqlda);
	}

	public void iscDsqlExecImmed2(IscDbHandle db_handle, IscTrHandle tr_handle,
			String statement, String encoding, int dialect, XSQLDA in_xsqlda,
			XSQLDA out_xsqlda) throws GDSException {
		try {
			iscDsqlExecImmed2(db_handle, tr_handle, getByteArrayForString(
					statement, encoding), dialect, in_xsqlda, out_xsqlda);
		} catch (UnsupportedEncodingException e) {
			throw new GDSException("Unsupported encoding: " + e.getMessage());
		}
	}

	public void iscDsqlExecImmed2(IscDbHandle db_handle, IscTrHandle tr_handle,
			byte[] statement, int dialect, XSQLDA in_xsqlda, XSQLDA out_xsqlda)
			throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_db_handle_impl db = validateHandle(db_handle);

		synchronized (db) {
			XdrOutputStream out = db.out;
			try {

				if (in_xsqlda == null && out_xsqlda == null) {
					if (debug)
						log.debug("op_exec_immediate ");
					out.writeInt(op_exec_immediate);
				} else {
					if (debug)
						log.debug("op_exec_immediate2 ");
					out.writeInt(op_exec_immediate2);

					if (in_xsqlda != null) {
						out.writeBuffer(in_xsqlda.blr);
						out.writeInt(0);
						out.writeInt(1);
						out.writeSQLData(in_xsqlda);
					} else {
						out.writeBuffer(null);
						out.writeInt(0);
						out.writeInt(0);
					}
					if (out_xsqlda != null)
						out.writeBuffer(out_xsqlda.blr);
					else
						out.writeBuffer(null);
					out.writeInt(0);
				}

				out.writeInt(tr.getTransactionId());
				out.writeInt(0);
				out.writeInt(dialect);
				out.writeBuffer(statement);
				out.writeString("");
				out.writeInt(0);
				out.flush();

				if (debug)
					log.debug("sent");

				int op = nextOperation(db.in);
				if (op == op_sql_response) {
					receiveSqlResponse(db, out_xsqlda, null);
					op = nextOperation(db.in);
				}
				receiveResponse(db, op);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	public void iscDsqlFetch(IscStmtHandle stmt_handle, int da_version,
			XSQLDA xsqlda, int fetchSize) throws GDSException {

	    if (xsqlda == null) {
            throw new GDSException(ISCConstants.isc_dsql_sqlda_err);
        }

		boolean debug = log != null && log.isDebugEnabled();
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);
		isc_db_handle_impl db = validateHandle(stmt.getRsr_rdb());

		if (fetchSize <= 0) {
			throw new GDSException(ISCConstants.isc_dsql_sqlda_err);
		}
		// Apply fetchSize
		synchronized (db) {
			XdrOutputStream out = db.out;
			XdrInputStream in = db.in;
			try {
				// Fetch next batch of rows
				stmt.ensureCapacity(fetchSize);
				if (debug)
					log.debug("op_fetch ");
				out.writeInt(op_fetch);
				out.writeInt(stmt.getRsr_id());
				out.writeBuffer(xsqlda.blr);
				out.writeInt(0); // p_sqldata_message_number
				out.writeInt(fetchSize); // p_sqldata_messages
				out.flush();
				if (debug)
					log.debug("sent");

				int op = nextOperation(db.in);
				stmt.notifyOpenResultSet();
				if (op == op_fetch_response) {

					int sqldata_status;
					int sqldata_messages;

					do {
						sqldata_status = in.readInt();
						sqldata_messages = in.readInt();

						if (sqldata_messages > 0 && sqldata_status == 0) {
							in.readSQLData(xsqlda.ioLength, stmt);
							do {
								op = nextOperation(db.in);
								if (op == op_response) {
									receiveResponse(db, op);
									continue;
								}
							} while (false);
						}

					} while (sqldata_messages > 0 && sqldata_status == 0);

					if (sqldata_status == 100) {
						if (debug)
							log.debug("all rows successfully fetched");
						stmt.setAllRowsFetched(true);
					}
				} else {
					receiveResponse(db, op);
				}
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	public static void calculateIOLength(XSQLDA xsqlda) {
		xsqlda.ioLength = new int[xsqlda.sqld];
		for (int i = 0; i < xsqlda.sqld; i++) {
			switch (xsqlda.sqlvar[i].sqltype & ~1) {
			case ISCConstants.SQL_TEXT:
				xsqlda.ioLength[i] = xsqlda.sqlvar[i].sqllen + 1;
				break;
			case ISCConstants.SQL_VARYING:
				xsqlda.ioLength[i] = 0;
				break;
			case ISCConstants.SQL_SHORT:
			case ISCConstants.SQL_LONG:
			case ISCConstants.SQL_FLOAT:
			case ISCConstants.SQL_TYPE_TIME:
			case ISCConstants.SQL_TYPE_DATE:
				xsqlda.ioLength[i] = -4;
				break;
			// case SQL_D_FLOAT:
			// break;
			case ISCConstants.SQL_DOUBLE:
			case ISCConstants.SQL_TIMESTAMP:
			case ISCConstants.SQL_BLOB:
			case ISCConstants.SQL_ARRAY:
			case ISCConstants.SQL_QUAD:
			case ISCConstants.SQL_INT64:
				xsqlda.ioLength[i] = -8;
				break;
			case ISCConstants.SQL_NULL:
			    xsqlda.ioLength[i] = 0;
                break;
            case ISCConstants.SQL_BOOLEAN:
                xsqlda.ioLength[i] = 1 + 1;
                break;
			}
		}
	}

	public void iscDsqlFreeStatement(IscStmtHandle stmt_handle, int option)
			throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);
		isc_db_handle_impl db = validateHandle(stmt.getRsr_rdb());

		// Does not seem to be possible or necessary to close
		// an execute procedure statement.
		if (stmt.isSingletonResult() && option == ISCConstants.DSQL_close) {
			return;
		}

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_free_statement ");
				db.out.writeInt(op_free_statement);
				db.out.writeInt(stmt.getRsr_id());
				db.out.writeInt(option);
				db.out.flush();
				if (debug)
					log.debug("sent");

				receiveResponse(db, -1);
				if (option == ISCConstants.DSQL_drop) {
					stmt.setInSqlda(null);
					stmt.setOutSqlda(null);
					stmt.setRsr_rdb(null);
				}
				// those rows are used by cachedFetcher don't clear
				stmt.clearRows();

                try {
                    AbstractIscTrHandle tr = stmt.getTransaction();
                    if (tr != null)
                        tr.unregisterStatementFromTransaction(stmt);
                } finally {
                    stmt.unregisterTransaction();
                }

				/** @todo implement statement handle tracking correctly */
				// db.rdb_sql_requests.remove(stmt);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}

	}

	public XSQLDA iscDsqlPrepare(IscTrHandle tr_handle,
			IscStmtHandle stmt_handle, String statement, int dialect/*
																	 * , xsqlda
																	 */) throws GDSException {
		return iscDsqlPrepare(tr_handle, stmt_handle, statement, "NONE",
				dialect);
	}

	public XSQLDA iscDsqlPrepare(IscTrHandle tr_handle,
			IscStmtHandle stmt_handle, String statement, String encoding,
			int dialect) throws GDSException {
		try {
			return iscDsqlPrepare(tr_handle, stmt_handle,
					getByteArrayForString(statement, encoding), dialect);
		} catch (UnsupportedEncodingException ex) {
			throw new GDSException("Unsupported encoding: " + ex.getMessage());
		}
	}

	public XSQLDA iscDsqlPrepare(IscTrHandle tr_handle,
			IscStmtHandle stmt_handle, byte[] statement, int dialect)
			throws GDSException {

	    boolean debug = log != null && log.isDebugEnabled();
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);
		isc_db_handle_impl db = validateHandle(stmt.getRsr_rdb());

		// reinitialize stmt SQLDA members.
		stmt.setInSqlda(null);
		stmt.setOutSqlda(null);

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_prepare_statement ");
				db.out.writeInt(op_prepare_statement);
				db.out.writeInt(tr.getTransactionId());
				db.out.writeInt(stmt.getRsr_id());
				db.out.writeInt(dialect);
				db.out.writeBuffer(statement);
				byte[] sqlPrepareInfo = getSqlPrepareInfo(stmt);
                db.out.writeBuffer(sqlPrepareInfo);
				db.out.writeInt(MAX_BUFFER_SIZE);
				db.out.flush();

				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				stmt.setOutSqlda(parseSqlInfo(stmt_handle, db.getResp_data(),
						db.getResp_data_len(), sqlPrepareInfo));
				return stmt.getOutSqlda();
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}

			// RSR_blob ??????????
		}

	}

	public void iscDsqlSetCursorName(IscStmtHandle stmt_handle,
			String cursor_name, int type) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);
		isc_db_handle_impl db = validateHandle(stmt.getRsr_rdb());

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_set_cursor ");
				db.out.writeInt(op_set_cursor);
				db.out.writeInt(stmt.getRsr_id());

				byte[] buffer = new byte[cursor_name.length() + 1];
				System.arraycopy(cursor_name.getBytes(), 0, buffer, 0,
						cursor_name.length());
				buffer[cursor_name.length()] = (byte) 0;

				db.out.writeBuffer(buffer);
				db.out.writeInt(0);
				db.out.flush();
				if (debug)
					log.debug("sent");

				receiveResponse(db, -1);
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}

	}

	public byte[] iscDsqlSqlInfo(IscStmtHandle stmt_handle, byte[] items,
			int buffer_length) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		isc_stmt_handle_impl stmt = validateHandle(stmt_handle);
		isc_db_handle_impl db = validateHandle(stmt.getRsr_rdb());

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_info_sql ");
				db.out.writeInt(op_info_sql);
				db.out.writeInt(stmt.getRsr_id());
				db.out.writeInt(0);
				db.out.writeBuffer(items);
				db.out.writeInt(buffer_length);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				return db.getResp_data_truncated();
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}

	}

	private static byte[] stmtInfo = new byte[] {
			ISCConstants.isc_info_sql_records,
			ISCConstants.isc_info_sql_stmt_type, ISCConstants.isc_info_end };

	private static int INFO_SIZE = 128;

	public void getSqlCounts(IscStmtHandle stmt_handle) throws GDSException {
		isc_stmt_handle_impl stmt = (isc_stmt_handle_impl) stmt_handle;
		
		stmt.setInsertCount(0);
		stmt.setUpdateCount(0);
		stmt.setDeleteCount(0);
		stmt.setSelectCount(0);
		
		byte[] buffer = iscDsqlSqlInfo(stmt, stmtInfo, INFO_SIZE);
		int pos = 0;
		int length;
		int type;
		while ((type = buffer[pos++]) != ISCConstants.isc_info_end) {
			length = iscVaxInteger(buffer, pos, 2);
			pos += 2;
			switch (type) {
			case ISCConstants.isc_info_sql_records:
				int l;
				int t;
				while ((t = buffer[pos++]) != ISCConstants.isc_info_end) {
					l = iscVaxInteger(buffer, pos, 2);
					pos += 2;
					switch (t) {
					case ISCConstants.isc_info_req_insert_count:
						stmt.setInsertCount(iscVaxInteger(buffer, pos, l));
						break;
					case ISCConstants.isc_info_req_update_count:
						stmt.setUpdateCount(iscVaxInteger(buffer, pos, l));
						break;
					case ISCConstants.isc_info_req_delete_count:
						stmt.setDeleteCount(iscVaxInteger(buffer, pos, l));
						break;
					case ISCConstants.isc_info_req_select_count:
						stmt.setSelectCount(iscVaxInteger(buffer, pos, l));
						break;
					default:
						break;
					}
					pos += l;
				}
				break;
			case ISCConstants.isc_info_sql_stmt_type:
				stmt.setStatementType(iscVaxInteger(buffer, pos, length));
				pos += length;
				break;
			default:
				pos += length;
				break;
			}
		}
	}

	public int iscVaxInteger(byte[] buffer, int pos, int length) {
		int value;
		int shift;

		value = shift = 0;

		int i = pos;
		while (--length >= 0) {
			value += (buffer[i++] & 0xff) << shift;
			shift += 8;
		}
		return value;
	}
	
	public int iscInteger(byte[] buffer, int pos, int length) {
        int value;

        value = 0;

        int i = pos;
        while (i < length) {
            value = value << 8;
            value += (buffer[i++] & 0xff);
        }
        return value;
    }

	// -----------------------------------------------
	// Blob methods
	// -----------------------------------------------

	public void iscCreateBlob2(IscDbHandle db_handle, IscTrHandle tr_handle,
			IscBlobHandle blob_handle, // contains blob_id
			BlobParameterBuffer blobParameterBuffer) throws GDSException {
		openOrCreateBlob(db_handle, tr_handle, blob_handle,
				blobParameterBuffer,
				(blobParameterBuffer == null) ? op_create_blob
						: op_create_blob2);
		((isc_blob_handle_impl) blob_handle)
				.rbl_flagsAdd(ISCConstants.RBL_create);
	}

	public void iscOpenBlob2(IscDbHandle db_handle, IscTrHandle tr_handle,
			IscBlobHandle blob_handle, // contains blob_id
			BlobParameterBuffer blobParameterBuffer) throws GDSException {
		openOrCreateBlob(db_handle, tr_handle, blob_handle,
				blobParameterBuffer,
				(blobParameterBuffer == null) ? op_open_blob : op_open_blob2);
	}

	private final void openOrCreateBlob(IscDbHandle db_handle,
			IscTrHandle tr_handle, IscBlobHandle blob_handle, // contains
			// blob_id
			BlobParameterBuffer blobParameterBuffer, int op)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_db_handle_impl db = validateHandle(db_handle);
		isc_tr_handle_impl tr = validateHandle(tr_handle);
		isc_blob_handle_impl blob = validateHandle(blob_handle);

		synchronized (db) {
			try {

				if (debug) {
					log.debug((blobParameterBuffer == null) ? "op_open/create_blob "
                            : "op_open/create_blob2 ");
					log.debug("op: " + op);
				}
				db.out.writeInt(op);
				if (blobParameterBuffer != null) {
					db.out.writeTyped(ISCConstants.isc_bpb_version1,
							(Xdrable) blobParameterBuffer);
				}
				db.out.writeInt(tr.getTransactionId()); // ??really a short?
				if (debug)
					log.debug("sending blob_id: " + blob.getBlobId());
				db.out.writeLong(blob.getBlobId());
				db.out.flush();

				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				blob.setDb(db);
				blob.setTr(tr);
				blob.setRbl_id(db.getResp_object());
				blob.setBlobId(db.getResp_blob_id());
				tr.addBlob(blob);
			} catch (IOException ioe) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	public byte[] iscGetSegment(IscBlobHandle blob_handle, int requested)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_blob_handle_impl blob = validateHandle(blob_handle);
		isc_db_handle_impl db = validateHandle(blob.getDb());
		isc_tr_handle_impl tr = validateHandle(blob.getTr());

		synchronized (db) {
			try {
				if (debug)
					log.debug("op_get_segment ");
				db.out.writeInt(op_get_segment);
				db.out.writeInt(blob.getRbl_id()); // short???
				if (debug)
					log.debug("trying to read bytes: " + Math.min(requested + 2, Short.MAX_VALUE));
				db.out.writeInt(Math.min(requested + 2, Short.MAX_VALUE));
				db.out.writeInt(0);// writeBuffer for put segment;
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
				blob.rbl_flagsRemove(ISCConstants.RBL_segment);
				if (db.getResp_object() == 1) {
					blob.rbl_flagsAdd(ISCConstants.RBL_segment);
				} else if (db.getResp_object() == 2) {
					blob.rbl_flagsAdd(ISCConstants.RBL_eof_pending);
				}

				if (db.getResp_data_len() == 0)
					return new byte[0];

				byte[] buffer = db.getResp_data();
				int bufferLength = db.getResp_data_len();
				// if (buffer.length == 0) {//previous segment was last, this
				// has no data
				// return buffer;
				// }
				int len = 0;
				int srcpos = 0;
				int destpos = 0;
				while (srcpos < bufferLength) {
					len = iscVaxInteger(buffer, srcpos, 2);
					srcpos += 2;
					System.arraycopy(buffer, srcpos, buffer, destpos, len);
					srcpos += len;
					destpos += len;
				}
				byte[] result = new byte[destpos];
				System.arraycopy(buffer, 0, result, 0, destpos);
				return result;

			} catch (IOException ioe) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	public void iscPutSegment(IscBlobHandle blob_handle, byte[] buffer)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		isc_blob_handle_impl blob = validateHandle(blob_handle);
		isc_db_handle_impl db = validateHandle(blob.getDb());
		isc_tr_handle_impl tr = validateHandle(blob.getTr());

		synchronized (db) {
			try {

				if (debug)
					log.debug("op_batch_segments ");
				db.out.writeInt(op_batch_segments);
				if (debug)
					log.debug("blob.rbl_id:  " + blob.getRbl_id());
				db.out.writeInt(blob.getRbl_id()); // short???
				if (debug)
					log.debug("buffer.length " + buffer.length);
				db.out.writeBlobBuffer(buffer);
				db.out.flush();
				if (debug)
					log.debug("sent");
				receiveResponse(db, -1);
			} catch (IOException ioe) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	public void iscCloseBlob(IscBlobHandle blob_handle) throws GDSException {
		isc_blob_handle_impl blob = validateHandle(blob_handle);
		isc_db_handle_impl db = validateHandle(blob.getDb());
		isc_tr_handle_impl tr = validateHandle(blob.getTr());

		releaseObject(db, op_close_blob, blob.getRbl_id());
		tr.removeBlob(blob);
	}

	private byte[] getByteArrayForString(String statement, String encoding)
			throws UnsupportedEncodingException {
		String javaEncoding = null;
		if (encoding != null && !"NONE".equals(encoding))
			javaEncoding = EncodingFactory.getJavaEncoding(encoding);

		final byte[] stringBytes;
		if (javaEncoding != null)
			stringBytes = statement.getBytes(javaEncoding);
		else
			stringBytes = statement.getBytes();

		return stringBytes;
	}

	// Handle declaration methods
	public IscDbHandle createIscDbHandle() {
		return new isc_db_handle_impl();
	}

	public IscTrHandle createIscTrHandle() {
		return new isc_tr_handle_impl();
	}

	public IscStmtHandle createIscStmtHandle() {
		return new isc_stmt_handle_impl();
	}

	public IscBlobHandle createIscBlobHandle() {
		return new isc_blob_handle_impl();
	}

	public void connect(isc_db_handle_impl db, String host, Integer port,
			String filename, DatabaseParameterBuffer databaseParameterBuffer)
			throws GDSException {
		DbAttachInfo dbai = new DbAttachInfo(host, port, filename);
		connect(db, dbai, databaseParameterBuffer);
	}

    protected void connect(isc_db_handle_impl db, DbAttachInfo dbai,
			DatabaseParameterBuffer databaseParameterBuffer)
			throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();

		final int socketBufferSize;
        if (databaseParameterBuffer.hasArgument(DatabaseParameterBufferExtension.SOCKET_BUFFER_SIZE)) {
            socketBufferSize = databaseParameterBuffer.getArgumentAsInt(DatabaseParameterBufferExtension.SOCKET_BUFFER_SIZE);
        } else {
            socketBufferSize = -1;
        }
        
        final int soTimeout;
        if (databaseParameterBuffer.hasArgument(DatabaseParameterBufferExtension.SO_TIMEOUT)) {
            soTimeout = databaseParameterBuffer.getArgumentAsInt(DatabaseParameterBufferExtension.SO_TIMEOUT);
        } else {
            soTimeout = -1;
        }
        
        final int connectTimeout;
        if (databaseParameterBuffer.hasArgument(DatabaseParameterBuffer.CONNECT_TIMEOUT)) {
            connectTimeout = databaseParameterBuffer.getArgumentAsInt(DatabaseParameterBuffer.CONNECT_TIMEOUT) * 1000;
        } else {
            connectTimeout = 0;
        }

		try {
			openSocket(db, dbai, debug, socketBufferSize, soTimeout, connectTimeout);
			
			XdrOutputStream out = db.out;
			XdrInputStream in = db.in;
			String fileName = dbai.getFileName();

			int nextOperation = sendConnectPacket(out, in, fileName);
			
			if (nextOperation == op_accept) {
				db.setProtocol(in.readInt()); // Protocol version number
				in.readInt(); // Architecture for protocol
				in.readInt(); // Minimum type
				if (debug)
					log.debug("received");
			} else {
				disconnect(db);
				if (debug)
					log.debug("not received");
				throw new GDSException(ISCConstants.isc_connect_reject);
			}
		} catch (IOException ex) {
			if (debug)
				log.debug("IOException while trying to connect to db:", ex);
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_network_error, dbai.getServer(), ex);
		}
	}

	protected int sendConnectPacket(XdrOutputStream out, XdrInputStream in,
			String fileName) throws IOException {
		
		boolean debug = log != null && log.isDebugEnabled();

		// Here we identify the user to the engine. This may or may not be
		// used as login info to a database.
		String user = System.getProperty("user.name");
		
		if (debug)
			log.debug("user.name: " + user);
		
		String host;
		try {
			host = InetAddress.getLocalHost().getHostName();
		} catch(UnknownHostException ex) {
			try {
				host = InetAddress.getLocalHost().getHostAddress();
			} catch(UnknownHostException ex1) {
				host = "127.0.0.1";
			}
		}

		byte[] userBytes = user.getBytes();
		byte[] hostBytes = host.getBytes();

		byte[] user_id = new byte[6 + userBytes.length + hostBytes.length];
		int n = 0;
		user_id[n++] = 1; // CNCT_user
		user_id[n++] = (byte) userBytes.length;
		System.arraycopy(userBytes, 0, user_id, n, userBytes.length);
		n += userBytes.length;

		/*
		 * String passwd = "masterkey"; user_id[n++] = 2; // CNCT_passwd
		 * user_id[n++] = (byte) passwd.length();
		 * System.arraycopy(passwd.getBytes(), 0, user_id, n,
		 * passwd.length()); n += passwd.length();
		 */

		user_id[n++] = 4; // CNCT_host
		user_id[n++] = (byte) hostBytes.length;
		System.arraycopy(hostBytes, 0, user_id, n, hostBytes.length);
		n += hostBytes.length;

		user_id[n++] = 6; // CNCT_user_verification
		user_id[n++] = 0;

		if (debug)
			log.debug("op_connect ");
		out.writeInt(op_connect);
		out.writeInt(op_attach);
		out.writeInt(2); // CONNECT_VERSION2
		out.writeInt(1); // arch_generic
		// db.out.writeString(file_name); // p_cnct_file
		out.writeString(fileName); // p_cnct_file
		out.writeInt(1); // p_cnct_count
		out.writeBuffer(user_id); // p_cnct_user_id

		out.writeInt(10); // PROTOCOL_VERSION10
		out.writeInt(1); // arch_generic
		out.writeInt(2); // ptype_rpc
		out.writeInt(3); // ptype_batch_send
		out.writeInt(2);
		out.flush();
		if (debug)
			log.debug("sent");

		if (debug)
			log.debug("op_accept ");
		
		int nextOperation = nextOperation(in);
		return nextOperation;
	}

	protected void openSocket(isc_db_handle_impl db, DbAttachInfo dbai,
			boolean debug, int socketBufferSize, int soTimeout, int connectTimeout) throws IOException,
			SocketException, GDSException {
		try {
			db.socket = new Socket();
			db.socket.setTcpNoDelay(true);
			
			if (soTimeout != -1)
			    db.socket.setSoTimeout(soTimeout);

			if (socketBufferSize != -1) {
				db.socket.setReceiveBufferSize(socketBufferSize);
				db.socket.setSendBufferSize(socketBufferSize);
			}
			db.socket.connect(new InetSocketAddress(dbai.getServer(), dbai.getPort()), connectTimeout);

			if (debug)
				log.debug("Got socket");
		} catch (UnknownHostException ex2) {
			String message = "Cannot resolve host " + dbai.getServer();
			if (debug)
				log.error(message, ex2);
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_network_error, dbai.getServer(), ex2);
		}

		db.out = new XdrOutputStream(db.socket.getOutputStream());
		db.in = new WireXdrInputStream(db.socket.getInputStream());
	}

	public void disconnect(isc_db_handle_impl db) throws IOException {
		if (log != null)
			log.debug("About to invalidate db handle");
		db.invalidate();
		if (log != null)
			log.debug("successfully invalidated db handle");
	}

	private void receiveSqlResponse(isc_db_handle_impl db, XSQLDA xsqlda,
			isc_stmt_handle_impl stmt) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		try {
			if (debug)
				log.debug("op_sql_response ");
			int messages = db.in.readInt();
			if (debug)
				log.debug("received");
			if (messages > 0) {
				db.in.readSQLData(xsqlda.ioLength, stmt);
			}
		} catch (IOException ex) {
			if (debug)
				log.warn("IOException in receiveSQLResponse", ex);
			// ex.getMessage() makes little sense here, it will not be displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ex.getMessage());
		}
	}

	public void receiveResponse(isc_db_handle_impl db, int op)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		// when used directly
		try {
			if (op == -1)
				op = nextOperation(db.in);
			if (debug)
				log.debug("op_response ");
			if (op == op_response) {
				db.setResp_object(db.in.readInt());
				db.setResp_blob_id(db.in.readLong());

				db.in.readBuffer(db);

				// // db.setResp_data(db.in.readBuffer());
				// if (debug) {
				// log.debug("op_response resp_object: " + db.getResp_object());
				// log.debug("op_response resp_blob_id: " +
				// db.getResp_blob_id());
				// log.debug("op_response resp_data size: " +
				// db.getResp_data().length);
				// }
				// for (int i = 0; i < ((r.resp_data.length< 16) ?
				// r.resp_data.length: 16) ; i++) {
				// if (debug) log.debug("byte: " + r.resp_data[i]);
				// }
				readStatusVector(db);
				if (debug) {
					log.debug("received");
					// checkAllRead(db.in);//DEBUG
				}
			} else {
				if (debug) {
					log.debug("not received: op is " + op);
					// checkAllRead(db.in);
				}
			}
		} catch (IOException ex) {
			if (debug)
				log.warn("IOException in receiveResponse", ex);
			// ex.getMessage() makes little sense here, it will not be displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ex.getMessage());
		}
	}

	protected int nextOperation(XdrInputStream in) throws IOException {
		boolean debug = log != null && log.isDebugEnabled();
		int op = 0;
		do {
			op = in.readInt();
			if (debug) {
				if (op == op_dummy) {
					log.debug("op_dummy received");
				}
			}
		} while (op == op_dummy);
		return op;
	}

	private void readStatusVector(isc_db_handle_impl db) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		try {
			GDSException head = null;
			GDSException tail = null;
			while (true) {
				int arg = db.in.readInt();
				switch (arg) {
				case ISCConstants.isc_arg_gds:
					int er = db.in.readInt();
					if (debug)
						log
								.debug("readStatusVector arg:isc_arg_gds int: "
										+ er);
					if (er != 0) {
						GDSException td = new GDSException(arg, er);
						if (head == null) {
							head = td;
							tail = td;
						} else {
							tail.setNext(td);
							tail = td;
						}
					}
					break;
				case ISCConstants.isc_arg_end:
					if (head != null && !head.isWarning())
						throw head;
					else if (head != null && head.isWarning())
						db.addWarning(head);

					return;
				case ISCConstants.isc_arg_interpreted:
				case ISCConstants.isc_arg_string:
					GDSException ts = new GDSException(arg, db.in.readString());
					if (debug)
						log
								.debug("readStatusVector string: "
										+ ts.getMessage());
					if (head == null) {
						head = ts;
						tail = ts;
					} else {
						tail.setNext(ts);
						tail = ts;
					}
					break;
				case ISCConstants.isc_arg_number: {
					int arg_value = db.in.readInt();
					if (debug)
						log.debug("readStatusVector arg:isc_arg_number int: "
								+ arg_value);
					GDSException td = new GDSException(arg, arg_value);
					if (head == null) {
						head = td;
						tail = td;
					} else {
						tail.setNext(td);
						tail = td;
					}
					break;
				}
				default:
					int e = db.in.readInt();
					if (debug)
						log
								.debug("readStatusVector arg: " + arg
										+ " int: " + e);
					if (e != 0) {
						GDSException td = new GDSException(arg, e);
						if (head == null) {
							head = td;
							tail = td;
						} else {
							tail.setNext(td);
							tail = td;
						}
					}
					break;
				}
			}
		} catch (IOException ioe) {
			// ioe.getMessage() makes little sense here, it will not be
			// displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ioe.getMessage());
		}
	}

	public static void calculateBLR(XSQLDA xsqlda) throws GDSException {
		int blr_len = 0;
		// byte[] blr = null;

		if (xsqlda != null) {
			// Determine the BLR length

			blr_len = 8;
			int par_count = 0;
			for (int i = 0; i < xsqlda.sqld; i++) {
				int dtype = xsqlda.sqlvar[i].sqltype & ~1;
				if (dtype == ISCConstants.SQL_VARYING
						|| dtype == ISCConstants.SQL_TEXT 
						|| dtype == ISCConstants.SQL_NULL) {
					blr_len += 3;
				} else if (dtype == ISCConstants.SQL_SHORT
						|| dtype == ISCConstants.SQL_LONG
						|| dtype == ISCConstants.SQL_INT64
						|| dtype == ISCConstants.SQL_QUAD
						|| dtype == ISCConstants.SQL_BLOB
						|| dtype == ISCConstants.SQL_ARRAY) {
					blr_len += 2;
				} else {
					blr_len++;
				}
				blr_len += 2;
				par_count += 2;
			}

			byte[] blr = new byte[blr_len];

			int n = 0;
			blr[n++] = 5; // blr_version5
			blr[n++] = 2; // blr_begin
			blr[n++] = 4; // blr_message
			blr[n++] = 0;

			blr[n++] = (byte) (par_count & 255);
			blr[n++] = (byte) (par_count >> 8);

			for (int i = 0; i < xsqlda.sqld; i++) {
				int dtype = xsqlda.sqlvar[i].sqltype & ~1;
				int len = xsqlda.sqlvar[i].sqllen;
                switch (dtype) {
                case ISCConstants.SQL_VARYING:
                    blr[n++] = 37; // blr_varying
                    blr[n++] = (byte) (len & 255);
                    blr[n++] = (byte) (len >> 8);
                    break;
                case ISCConstants.SQL_TEXT:
                    blr[n++] = 14; // blr_text
                    blr[n++] = (byte) (len & 255);
                    blr[n++] = (byte) (len >> 8);
                    break;
                case ISCConstants.SQL_NULL:
                    blr[n++] = 14; // blr_text
                    blr[n++] = 0;
                    blr[n++] = 0;
                    break;
                case ISCConstants.SQL_DOUBLE:
                    blr[n++] = 27; // blr_double
                    break;
                case ISCConstants.SQL_FLOAT:
                    blr[n++] = 10; // blr_float
                    break;
                case ISCConstants.SQL_D_FLOAT:
                    blr[n++] = 11; // blr_d_float
                    break;
                case ISCConstants.SQL_TYPE_DATE:
                    blr[n++] = 12; // blr_sql_date
                    break;
                case ISCConstants.SQL_TYPE_TIME:
                    blr[n++] = 13; // blr_sql_time
                    break;
                case ISCConstants.SQL_TIMESTAMP:
                    blr[n++] = 35; // blr_timestamp
                    break;
                case ISCConstants.SQL_BLOB:
                    blr[n++] = 9; // blr_quad
                    blr[n++] = 0;
                    break;
                case ISCConstants.SQL_ARRAY:
                    blr[n++] = 9; // blr_quad
                    blr[n++] = 0;
                    break;
                case ISCConstants.SQL_LONG:
                    blr[n++] = 8; // blr_long
                    blr[n++] = (byte) xsqlda.sqlvar[i].sqlscale;
                    break;
                case ISCConstants.SQL_SHORT:
                    blr[n++] = 7; // blr_short
                    blr[n++] = (byte) xsqlda.sqlvar[i].sqlscale;
                    break;
                case ISCConstants.SQL_INT64:
                    blr[n++] = 16; // blr_int64
                    blr[n++] = (byte) xsqlda.sqlvar[i].sqlscale;
                    break;
                case ISCConstants.SQL_QUAD:
                    blr[n++] = 9; // blr_quad
                    blr[n++] = (byte) xsqlda.sqlvar[i].sqlscale;
                    break;
                case ISCConstants.SQL_BOOLEAN:
                    blr[n++] = 23; // blr_bool
                    break;
                default:
                    throw new GDSException(ISCConstants.isc_dsql_sqlda_value_err);
                }

				blr[n++] = 7; // blr_short
				blr[n++] = 0;

			}

			blr[n++] = (byte) 255; // blr_end
			blr[n++] = 76; // blr_eoc
			// save
			xsqlda.blr = blr;
		}
	}

	private XSQLDA parseSqlInfo(IscStmtHandle stmt_handle, byte[] info,
			int infoLength, byte[] items) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
		if (debug)
			log.debug("parseSqlInfo started");

        // check the statement type
        int i = 0;
        if (info[i] == ISCConstants.isc_info_sql_stmt_type) {
            int dataLength = iscVaxInteger(info, ++i, 2);
            i += 2;
            int statementType = iscVaxInteger(info, i, dataLength);
            ((isc_stmt_handle_impl)stmt_handle).setStatementType(statementType);
            i += dataLength;
        }
        
		XSQLDA xsqlda = new XSQLDA();
		int lastindex = 0;
		int index = 0;
		while ((index = parseTruncSqlInfo(i + 2, info, infoLength, xsqlda, lastindex)) > 0) {
			byte[] new_items = new byte[4 + items.length - 1];
			new_items[0] = ISCConstants.isc_info_sql_sqlda_start;
			new_items[1] = 2;
			new_items[2] = (byte) (index & 255);
			new_items[3] = (byte) (index >> 8);
			System.arraycopy(items, 1, new_items, 4, items.length - 1);

			int size = infoLength;

			// this situation happens only if one XSQLVAR does not fit
			// the buffer. in this case we increase buffer twice and try
			// again
			if (index == lastindex)
				size = infoLength * 2;

			info = iscDsqlSqlInfo(stmt_handle, new_items, size);
			lastindex = index;
            i = 0;
		}
		if (debug)
			log.debug("parseSqlInfo ended");
		calculateBLR(xsqlda);
		calculateIOLength(xsqlda);
		return xsqlda;
	}

	private int parseTruncSqlInfo(int startAt, byte[] info, int infoLength, XSQLDA xsqlda,
			int lastindex) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		byte item;
		int index = 0;
		if (debug)
			log.debug("parseSqlInfo: first 2 bytes are "
					+ iscVaxInteger(info, 0, 2) + " or: " + info[0] + ", "
					+ info[1]);

		int i = startAt;

		int len = iscVaxInteger(info, i, 2);
		i += 2;
		int n = iscVaxInteger(info, i, len);
		i += len;
		if (xsqlda.sqlvar == null) {
			xsqlda.sqld = xsqlda.sqln = n;
			xsqlda.sqlvar = new XSQLVAR[xsqlda.sqln];
		}
		if (debug)
			log.debug("xsqlda.sqln read as " + xsqlda.sqln);
        if (debug)
            log.debug ("info: " + new String(info));

		while (info[i] != ISCConstants.isc_info_end) {
			while ((item = info[i++]) != ISCConstants.isc_info_sql_describe_end) {
				switch (item) {
				case ISCConstants.isc_info_sql_sqlda_seq:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					index = iscVaxInteger(info, i, len);
					i += len;
					xsqlda.sqlvar[index - 1] = new XSQLVAR();
					if (debug)
						log.debug("new xsqlvar " + (index - 1));
					break;
				case ISCConstants.isc_info_sql_type:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].sqltype = iscVaxInteger(info, i,
							len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_type "
								+ xsqlda.sqlvar[index - 1].sqltype);
					break;
				case ISCConstants.isc_info_sql_sub_type:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].sqlsubtype = iscVaxInteger(info,
							i, len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_sub_type "
								+ xsqlda.sqlvar[index - 1].sqlsubtype);
					break;
				case ISCConstants.isc_info_sql_scale:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].sqlscale = iscVaxInteger(info, i,
							len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_scale "
								+ xsqlda.sqlvar[index - 1].sqlscale);
					break;
				case ISCConstants.isc_info_sql_length:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].sqllen = iscVaxInteger(info, i,
							len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_length "
								+ xsqlda.sqlvar[index - 1].sqllen);
					break;
				case ISCConstants.isc_info_sql_field:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].sqlname = new String(info, i, len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_field "
								+ xsqlda.sqlvar[index - 1].sqlname);
					break;
				case ISCConstants.isc_info_sql_relation:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].relname = new String(info, i, len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_relation "
								+ xsqlda.sqlvar[index - 1].relname);
					break;
				case ISCConstants.isc_info_sql_relation_alias:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].relaliasname = new String(info, i, len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_relation_alias "
								+ xsqlda.sqlvar[index - 1].relaliasname);
					break;
	
				case ISCConstants.isc_info_sql_owner:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].ownname = new String(info, i, len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_owner "
								+ xsqlda.sqlvar[index - 1].ownname);
					break;
				case ISCConstants.isc_info_sql_alias:
					len = iscVaxInteger(info, i, 2);
					i += 2;
					xsqlda.sqlvar[index - 1].aliasname = new String(info, i,
							len);
					i += len;
					if (debug)
						log.debug("isc_info_sql_alias "
								+ xsqlda.sqlvar[index - 1].aliasname);
					break;
				case ISCConstants.isc_info_truncated:
					if (debug)
						log.debug("isc_info_truncated ");
					return index;
				default:
					throw new GDSException(ISCConstants.isc_dsql_sqlda_err);
				}
			}

			lastindex++;
		}
		return 0;
	}

	private void releaseObject(isc_db_handle_impl db, int op, int id)
			throws GDSException {
		synchronized (db) {
			try {
				db.out.writeInt(op);
				db.out.writeInt(id);
				db.out.flush();
				receiveResponse(db, -1);
			} catch (IOException ioe) {
				throw new GDSException(ISCConstants.isc_net_read_err);
			}
		}
	}

	// inner classes

	public DatabaseParameterBuffer createDatabaseParameterBuffer() {
		return new DatabaseParameterBufferImp();
	}

	public TransactionParameterBuffer newTransactionParameterBuffer() {
		return new TransactionParameterBufferImpl();
	}

	public BlobParameterBuffer createBlobParameterBuffer() {
		return new BlobParameterBufferImp();
	}

	// Services API methods - all currently un-implemented.
	public ServiceParameterBuffer createServiceParameterBuffer() {
		return new ServiceParameterBufferImp();
	}

	public ServiceRequestBuffer createServiceRequestBuffer(int taskIdentifier) {
		return new ServiceRequestBufferImp(taskIdentifier);
	}

	public void iscServiceAttach(String service, IscSvcHandle serviceHandle,
			ServiceParameterBuffer serviceParameterBuffer) throws GDSException {

		boolean debug = log != null && log.isDebugEnabled();
        isc_svc_handle_impl svc = (isc_svc_handle_impl) serviceHandle;

        if (svc == null) {
            throw new GDSException(ISCConstants.isc_bad_svc_handle);
        }

		String serviceMgrStr = "service_mgr";

		int mgrIndex = service.indexOf(serviceMgrStr);
		if (mgrIndex == -1
				|| mgrIndex + serviceMgrStr.length() != service.length())
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_svcnotdef, service);

        if (mgrIndex > 0 && service.charAt(mgrIndex - 1) != ':')
            throw new GDSException(ISCConstants.isc_arg_gds,
                ISCConstants.isc_svcnotdef, service);
        
        int port = 3050;
        String host = null;
        
        if (mgrIndex > 0) {
            String server = service.substring(0, mgrIndex - 1);
            host = server;

    		int portIndex = server.indexOf('/');
    		if (portIndex != -1) {
    			try {
    				port = Integer.parseInt(server.substring(portIndex + 1));
    				host = server.substring(0, portIndex);
    			} catch (NumberFormatException ex) {
    				// ignore, nothing happened, we try to connect directly
    			}
    
    		}
        }

		synchronized (svc) {
			try {
				try {
					svc.socket = new Socket();
					svc.socket.setTcpNoDelay(true);

					// if (socketBufferSize != -1) {
					// svc.socket.setReceiveBufferSize(socketBufferSize);
					// svc.socket.setSendBufferSize(socketBufferSize);
					// }
					svc.socket.connect(new InetSocketAddress(host, port));
					
					if (debug)
						log.debug("Got socket");
				} catch (UnknownHostException ex2) {
					String message = "Cannot resolve host " + host;
					if (debug)
						log.error(message, ex2);
					throw new GDSException(ISCConstants.isc_arg_gds,
							ISCConstants.isc_network_error, host, ex2);
				}

				svc.out = new XdrOutputStream(svc.socket.getOutputStream());
				svc.in = new XdrInputStream(svc.socket.getInputStream());

				int nextOperation = sendConnectPacket(svc.out, svc.in, serviceMgrStr);
				
				if (nextOperation == op_accept) {
					svc.in.readInt(); // Protocol version number
					svc.in.readInt(); // Architecture for protocol
					svc.in.readInt(); // Minimum type
					if (debug)
						log.debug("received");
				} else {
					svc.invalidate();
					if (debug)
						log.debug("not received");
					throw new GDSException(ISCConstants.isc_connect_reject);
				}

				
				if (debug)
					log.debug("op_service_attach ");
				svc.out.writeInt(op_service_attach);
				svc.out.writeInt(0);
                svc.out.writeString(serviceMgrStr);

				svc.out.writeTyped(ISCConstants.isc_spb_version,
						(Xdrable) serviceParameterBuffer);
				svc.out.flush();

				if (debug)
					log.debug("sent");

				try {
					receiveResponse(svc, -1);
					svc.setHandle(svc.getResp_object());
				} catch (GDSException ge) {
					if (log != null)
						log.debug("About to invalidate db handle");
					svc.invalidate();
					if (log != null)
						log.debug("successfully invalidated db handle");
					throw ge;
				}
			} catch (IOException ex) {
				throw new GDSException(ISCConstants.isc_net_write_err);
			}
		}

	}

	public void receiveResponse(isc_svc_handle_impl svc, int op)
			throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		// when used directly
		try {
			if (op == -1)
				op = nextOperation(svc.in);
			if (debug)
				log.debug("op_response ");
			if (op == op_response) {
				svc.setResp_object(svc.in.readInt());
				svc.setResp_blob_id(svc.in.readLong());
				svc.setResp_data(svc.in.readBuffer());
				if (debug) {
					log.debug("op_response resp_object: "
							+ svc.getResp_object());
				}
				readStatusVector(svc);
				if (debug) {
					log.debug("received");
				}
			} else {
				if (debug) {
					log.debug("not received: op is " + op);
				}
			}
		} catch (IOException ex) {
			if (debug)
				log.warn("IOException in receiveResponse", ex);
			// ex.getMessage() makes little sense here, it will not be displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ex.getMessage());
		}
	}

	private void readStatusVector(isc_svc_handle_impl svc) throws GDSException {
		boolean debug = log != null && log.isDebugEnabled();
		try {
			GDSException head = null;
			GDSException tail = null;
			while (true) {
				int arg = svc.in.readInt();
				switch (arg) {
				case ISCConstants.isc_arg_gds:
					int er = svc.in.readInt();
					if (debug)
						log
								.debug("readStatusVector arg:isc_arg_gds int: "
										+ er);
					if (er != 0) {
						GDSException td = new GDSException(arg, er);
						if (head == null) {
							head = td;
							tail = td;
						} else {
							tail.setNext(td);
							tail = td;
						}
					}
					break;
				case ISCConstants.isc_arg_end:
					if (head != null && !head.isWarning())
						throw head;
					else if (head != null && head.isWarning())
						svc.addWarning(head);

					return;
				case ISCConstants.isc_arg_interpreted:
				case ISCConstants.isc_arg_string:
					GDSException ts = new GDSException(arg, svc.in.readString());
					if (debug)
						log
								.debug("readStatusVector string: "
										+ ts.getMessage());
					if (head == null) {
						head = ts;
						tail = ts;
					} else {
						tail.setNext(ts);
						tail = ts;
					}
					break;
				case ISCConstants.isc_arg_number: {
					int arg_value = svc.in.readInt();
					if (debug)
						log.debug("readStatusVector arg:isc_arg_number int: "
								+ arg_value);
					GDSException td = new GDSException(arg, arg_value);
					if (head == null) {
						head = td;
						tail = td;
					} else {
						tail.setNext(td);
						tail = td;
					}
					break;
				}
				default:
					int e = svc.in.readInt();
					if (debug)
						log
								.debug("readStatusVector arg: " + arg
										+ " int: " + e);
					if (e != 0) {
						GDSException td = new GDSException(arg, e);
						if (head == null) {
							head = td;
							tail = td;
						} else {
							tail.setNext(td);
							tail = td;
						}
					}
					break;
				}
			}
		} catch (IOException ioe) {
			// ioe.getMessage() makes little sense here, it will not be
			// displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ioe.getMessage());
		}
	}

	public void iscServiceDetach(IscSvcHandle serviceHandle)
			throws GDSException {

		isc_svc_handle_impl svc = validateHandle(serviceHandle);

		if (svc.out == null)
			throw new GDSException(ISCConstants.isc_bad_svc_handle);

		try {
			try {
				svc.out.writeInt(op_service_detach);
				svc.out.writeInt(svc.getHandle());
				svc.out.writeInt(op_disconnect);
				svc.out.flush();

				receiveResponse(svc, -1);
			} finally {
				svc.invalidate();
			}
		} catch (IOException ex) {
			if (log != null && log.isDebugEnabled())
				log.warn("IOException in isc_service_detach", ex);
			// ex.getMessage() makes little sense here, it will not be displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ex.getMessage());
		}
	}

	public void iscServiceStart(IscSvcHandle serviceHandle,
			ServiceRequestBuffer serviceRequestBuffer) throws GDSException {

		isc_svc_handle_impl svc = validateHandle(serviceHandle);
		ServiceRequestBufferImp svcBuff = (ServiceRequestBufferImp) serviceRequestBuffer;

		if (svc.out == null)
			throw new GDSException(ISCConstants.isc_bad_svc_handle);
		try {
			svc.out.writeInt(op_service_start);
			svc.out.writeInt(svc.getHandle());
			svc.out.writeInt(0);

			svc.out.writeBuffer(svcBuff.toByteArray());
			svc.out.flush();

			receiveResponse(svc, -1);
		} catch (IOException ex) {
			if (log != null && log.isDebugEnabled())
				log.warn("IOException in isc_service_start", ex);
			// ex.getMessage() makes little sense here, it will not be displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ex.getMessage());
		}
	}

	public void iscServiceQuery(IscSvcHandle serviceHandle,
			ServiceParameterBuffer serviceParameterBuffer,
			ServiceRequestBuffer serviceRequestBuffer, byte[] resultBuffer)
			throws GDSException {

		isc_svc_handle_impl svc = validateHandle(serviceHandle);
		ServiceParameterBufferImp spb = (ServiceParameterBufferImp) serviceParameterBuffer;
		ServiceRequestBufferImp srb = (ServiceRequestBufferImp) serviceRequestBuffer;

		if (svc.out == null)
			throw new GDSException(ISCConstants.isc_bad_svc_handle);

		try {
			svc.out.writeInt(op_service_info);
			svc.out.writeInt(svc.getHandle());
			svc.out.writeInt(0);
			svc.out.writeBuffer(spb != null ? spb.toByteArray() : null);
			svc.out.writeBuffer(srb != null ? srb.toByteArray() : null);
			svc.out.writeInt(resultBuffer.length);
			svc.out.flush();

			receiveResponse(svc, -1);

			int toCopy = Math.min(resultBuffer.length,
					svc.getResp_data().length);
			System.arraycopy(svc.getResp_data(), 0, resultBuffer, 0, toCopy);
		} catch (IOException ex) {
			if (log != null && log.isDebugEnabled())
				log.warn("IOException in isc_service_query", ex);
			// ex.getMessage() makes little sense here, it will not be displayed
			// because error message for isc_net_read_err does not accept params
			throw new GDSException(ISCConstants.isc_arg_gds,
					ISCConstants.isc_net_read_err, ex.getMessage());
		}
	}

	public IscSvcHandle createIscSvcHandle() {
		return new isc_svc_handle_impl();
	}

    public int iscQueueEvents(IscDbHandle dbHandle, 
            EventHandle eventHandle, EventHandler eventHandler) 
            throws GDSException {

        boolean debug = log != null && log.isDebugEnabled();
        isc_db_handle_impl db = validateHandle(dbHandle);

        synchronized (db) {
            try {
                if (db.eventCoordinator == null){
                    if (debug)
                        log.debug("op_connect_request ");
                    
                    db.out.writeInt(op_connect_request);
                    db.out.writeInt(1);  // Connection type
                    db.out.writeInt(db.getRdbId());
                    db.out.writeInt(0);
                    db.out.flush();
                   
                    nextOperation(db.in); 

                    int auxHandle = db.in.readInt();
                    // garbage
                    db.in.readRawBuffer(8);

                    int respLen = db.in.readInt();
                    respLen += respLen % 4;

                    // sin family
                    db.in.readShort();
                    respLen -= 2;
                    
                    // sin port
                    int port = db.in.readShort();
                    respLen -= 2;

                    // IP address
                    byte[] ipBytes = db.in.readRawBuffer(4);
                    respLen -= 4;
                    
                    StringBuffer ipBuf = new StringBuffer();
                    for (int i = 0; i < 4; i++){
                        ipBuf.append((int)(ipBytes[i] & 0xff));
                        if (i < 3) ipBuf.append(".");
                    }
                    String ipAddress = ipBuf.toString();

                    // Ignore
                    db.in.readRawBuffer(respLen);
                    readStatusVector(db);

                    db.eventCoordinator = 
                        new EventCoordinatorImp(auxHandle, ipAddress, port);
                }

                db.eventCoordinator.queueEvents(
                    db,
                    (EventHandleImp)eventHandle, 
                    eventHandler);
            } catch (IOException ioe){
                throw new GDSException(
                        ISCConstants.isc_arg_gds, 
                        ISCConstants.isc_net_read_err, 
                        ioe.getMessage());
            }
        }

        return 0;
    }

    public void iscEventBlock(EventHandle eventHandle) 
            throws GDSException {

        // Don't need to do anything here, this method just exists
        // for the Type2 driver to map directly to the Interbase API
    }

    public void iscEventCounts(EventHandle eventHandle)
            throws GDSException {
        EventHandleImp handleImp = (EventHandleImp)eventHandle;
        handleImp.calculateCount();
    }


    public void iscCancelEvents(IscDbHandle dbHandle, EventHandle eventHandle)
            throws GDSException {
        isc_db_handle_impl db = validateHandle(dbHandle);
        EventHandleImp handleImp = (EventHandleImp)eventHandle;

        if (db.eventCoordinator != null && db.eventCoordinator.cancelEvents(handleImp)){
            synchronized (db){
                try {
                    db.out.writeInt(op_cancel_events);
                    db.out.writeInt(db.getRdbId());
                    db.out.writeInt(handleImp.getLocalId());
                    db.out.flush();
                    receiveResponse(db, -1);
                } catch (IOException ioe){
                    throw new GDSException(
                            ISCConstants.isc_arg_gds, 
                            ISCConstants.isc_net_read_err, 
                            ioe.getMessage());
                }
            }
        }
    }

    public EventHandle createEventHandle(String eventName){
        return new EventHandleImp(eventName);
    }

    class EventCoordinatorImp implements EventCoordinator, Runnable {

        private int handle;
        private String ipAddress;
        private int port;
        private int eventsId = 0;
        isc_db_handle_impl db;
        private Map globMap = new HashMap();
        private boolean running = true;

        public EventCoordinatorImp(int handle, String ipAddress, int port) 
                throws GDSException {
            this.handle = handle;
            this.ipAddress = ipAddress;
            this.port = port;
            connect();
            Thread eventThread = new Thread(this);
            eventThread.setDaemon(true);
            eventThread.start();
        }

        public boolean cancelEvents(EventHandleImp eventHandle){
            synchronized (globMap){
                return globMap.remove(
                    Integer.toString(eventHandle.getLocalId())) != null;
            }
        }

        public void run(){
            try {
                while (running){
                    int op = nextOperation(db.in);
                    switch (op){
                        case op_response:
                            receiveResponse(db, op);
                            break;

                        case op_exit:
                        case op_disconnect:
                            this.close();
                            break;

                        case op_event:
                            // db handle
                            db.in.readInt();
                            byte [] buffer = db.in.readBuffer();

                            // AST info, can be ignored
                            for (int i = 0; i < 8; i++){
                                db.in.read();
                            }

                            int eventId = db.in.readInt();
                            
                            int count = 0;
                            int shift = 0;
                            
                            if (buffer.length > 4) {
                                for (int i = buffer.length - 4; 
                                        i < buffer.length; i++){
                                    count += ((buffer[i] & 0xff) << shift);
                                    shift += 8;
                                }
                            }

                            EventGlob glob = null;
                            synchronized (globMap){
                                glob = (EventGlob)globMap.remove(
                                        Integer.toString(eventId));
                            }
                            if (glob != null){
                                glob.getEventHandle().setInternalCount(count);
                                glob.getEventHandler().eventOccurred();
                            }
                            break;
                    }
                }                 
            } catch (IOException ioe) {
                if (log != null) {
                    log.fatal("IOException in event loop: " + ioe.getMessage(), ioe);
                }
            } catch (GDSException gdse) {
                if (log != null) {
                    log.fatal("GDSException in event loop: " + gdse.getMessage(), gdse);
                }
            } finally {
                try {
                    doClose();
                } catch (IOException e) {
                    if (log != null && log.isDebugEnabled()) {
                        log.debug("IOException closing event connection", e);
                    }
                }
            }
        }
        
        /**
         * Connect to receive event callbacks
         */
        private void connect() throws GDSException {
            try {
                db = new isc_db_handle_impl();
                db.socket = new Socket(ipAddress, port);
                db.socket.setTcpNoDelay(true);
                db.out = new XdrOutputStream(db.socket.getOutputStream());
                db.in = new WireXdrInputStream(db.socket.getInputStream());
            } catch (UnknownHostException uhe){
                throw new GDSException(
                        ISCConstants.isc_arg_gds, 
                        ISCConstants.isc_network_error,
                        ipAddress, uhe);
            } catch (IOException ioe){
                throw new GDSException(
                        ISCConstants.isc_arg_gds, 
                        ISCConstants.isc_network_error, 
                        ipAddress, ioe);
            }
        }

        public void close() throws IOException {
            running = false;
        }

        private void doClose() throws IOException {
            db.invalidate();
        }

        public void queueEvents(isc_db_handle_impl mainDb, 
                EventHandleImp eventHandle, 
                EventHandler eventHandler) throws GDSException {
            validateHandle(mainDb);

            synchronized (mainDb){
                try {
                    synchronized (globMap){
                        eventHandle.setLocalId(++this.eventsId);
                        mainDb.out.writeInt(op_que_events);
                        mainDb.out.writeInt(this.handle);
                        byte [] epb = eventHandle.toByteArray();
                        mainDb.out.writeBuffer(epb);
                        mainDb.out.writeInt(0); // Address of ast
                        mainDb.out.writeInt(0); // Address of ast arg
                        mainDb.out.writeInt(eventHandle.getLocalId());
                        mainDb.out.flush();


                        receiveResponse(mainDb,-1);
                        int eventId = mainDb.getResp_object();
                        eventHandle.setEventId(eventId);
                        globMap.put(
                                Integer.toString(eventHandle.getLocalId()), 
                                new EventGlob(eventHandler, eventHandle));
                    }

                } catch (IOException ioe){
                    throw new GDSException(
                            ISCConstants.isc_arg_gds, 
                            ISCConstants.isc_net_read_err, 
                            ioe.getMessage());
                }
            }
        }
    }

    static class EventGlob {
        private final EventHandler eventHandler;
        private final EventHandleImp eventHandle;

        public EventGlob(EventHandler handler, EventHandleImp handle){
            this.eventHandler = handler;
            this.eventHandle = handle;
        }

        public EventHandler getEventHandler(){
            return this.eventHandler;
        }

        public EventHandleImp getEventHandle(){
            return this.eventHandle;
        }
    }

    public void fbCancelOperation(IscDbHandle dbHandle, int kind)
            throws GDSException {
        
        boolean debug = log != null && log.isDebugEnabled();
        isc_db_handle_impl db = validateHandle(dbHandle);

        //synchronized (db) {
        // TODO: Isn't this going to go wrong when this method and other method interleave?
            try {
                if (debug)
                    log.debug("op_cancel ");
                db.out.writeInt(op_cancel);
                db.out.writeInt(kind);
                db.out.flush();
                if (debug)
                    log.debug("sent");
                // receiveResponse(db, -1);

            } catch (IOException ex) {
                throw new GDSException(ISCConstants.isc_network_error, ex);
            } 
        //}
    }
    
    protected byte[] getDescribeSelectInfo(IscStmtHandle stmtHandle) {
        isc_stmt_handle_impl stmt = (isc_stmt_handle_impl) stmtHandle;
        isc_db_handle_impl db = stmt.getRsr_rdb();
        if (db.getDatabaseProductMajorVersion() == 1 && db.getDatabaseProductMinorVersion() <= 5) {
            return describe_select_info15;
        }
        return describe_select_info2;
    }
    
    protected byte[] getDescribeBindInfo(IscStmtHandle stmtHandle) {
        isc_stmt_handle_impl stmt = (isc_stmt_handle_impl) stmtHandle;
        isc_db_handle_impl db = stmt.getRsr_rdb();
        if (db.getDatabaseProductMajorVersion() == 1 && db.getDatabaseProductMinorVersion() <= 5) {
            return describe_bind_info15;
        }
        return describe_bind_info2;
    }
    
    protected byte[] getSqlPrepareInfo(IscStmtHandle stmtHandle) {
        isc_stmt_handle_impl stmt = (isc_stmt_handle_impl) stmtHandle;
        isc_db_handle_impl db = stmt.getRsr_rdb();
        if (db.getDatabaseProductMajorVersion() == 1 && db.getDatabaseProductMinorVersion() <= 5) {
            return sql_prepare_info15;
        }
        return sql_prepare_info2;
    }

    /**
     * Validates if the database handle is valid for use and casts it to {@link isc_db_handle_impl}
     *
     * @param dbHandle Database handle
     * @return dbHandle cast to isc_db_handle_impl
     * @throws GDSException If the dbHandle is null or invalid
     */
    protected final isc_db_handle_impl validateHandle(final IscDbHandle dbHandle) throws GDSException {
        if (dbHandle == null || !dbHandle.isValid()) {
            throw new GDSException(ISCConstants.isc_bad_db_handle);
        }
        return (isc_db_handle_impl) dbHandle;
    }

    /**
     * Validates if the blob handle is valid for use and casts it to {@link isc_blob_handle_impl}
     *
     * @param blobHandle Blob handle
     * @return blobHandle cast to isc_blob_handle_impl
     * @throws GDSException If the blobHandle is null
     */
    protected final isc_blob_handle_impl validateHandle(final IscBlobHandle blobHandle) throws GDSException {
        if (blobHandle == null) {
            throw new GDSException(ISCConstants.isc_bad_segstr_handle);
        }
        return (isc_blob_handle_impl) blobHandle;
    }

    /**
     * Validates if the transaction handle is valid for use and casts it to {@link isc_tr_handle_impl}
     *
     * @param transactionHandle Transaction handle
     * @return transactionHandle cast to isc_tr_handle_impl
     * @throws GDSException If the transactionHandle is null
     */
    protected final isc_tr_handle_impl validateHandle(final IscTrHandle transactionHandle) throws GDSException {
        if (transactionHandle == null) {
            throw new GDSException(ISCConstants.isc_bad_trans_handle);
        }
        return (isc_tr_handle_impl) transactionHandle;
    }

    /**
     * Validates if the statement handle is valid for use and casts it to {@link isc_stmt_handle_impl}
     *
     * @param statementHandle Statement handle
     * @return statementHandle cast to isc_stmt_handle_impl
     * @throws GDSException If the statementHandle is null
     */
    protected final isc_stmt_handle_impl validateHandle(final IscStmtHandle statementHandle) throws GDSException {
        // Not checking statementHandle.isValid() as that simply checks database validity which is done everywhere this is called as well
        if (statementHandle == null) {
            throw new GDSException(ISCConstants.isc_bad_stmt_handle);
        }
        return (isc_stmt_handle_impl) statementHandle;
    }

    /**
     * Validates if the service handle is valid for use and casts it to {@link isc_svc_handle_impl}
     *
     * @param svcHandle Service handle
     * @return svcHandle cast to isc_svc_handle_impl
     * @throws GDSException If the svcHandle is null or invalid
     */
    protected final isc_svc_handle_impl validateHandle(final IscSvcHandle svcHandle) throws GDSException {
        if (svcHandle == null || svcHandle.isNotValid()) {
            throw new GDSException(ISCConstants.isc_bad_svc_handle);
        }
        return (isc_svc_handle_impl) svcHandle;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy