text: var
      net = require('net'),
      os = require('os'),
      Events = require('events'),
      stream = require('stream'),
      serialize = require('./serialize.js'),
      XdrReader = serialize.XdrReader,
      BlrReader = serialize.BlrReader,
      XdrWriter = serialize.XdrWriter,
      BlrWriter = serialize.BlrWriter,
      messages = require('./messages.js')
  
  if (typeof(setImmediate) === 'undefined') {
      global.setImmediate = function(cb) {
          process.nextTick(cb);
      };
  }
  
  /**
   * Parse date from string
   * @return {Date}
   */
  if (String.prototype.parseDate === undefined) {
      String.prototype.parseDate = function() {
          var self = this.trim();
          var arr = self.indexOf(' ') === -1 ? self.split('T') : self.split(' ');
          var index = arr[0].indexOf(':');
          var length = arr[0].length;
  
          if (index !== -1) {
              var tmp = arr[1];
              arr[1] = arr[0];
              arr[0] = tmp;
          }
  
          if (arr[0] === undefined)
              arr[0] = '';
  
          var noTime = arr[1] === undefined ? true : arr[1].length === 0;
  
          for (var i = 0; i < length; i++) {
              var c = arr[0].charCodeAt(i);
              if (c > 47 && c < 58)
                  continue;
              if (c === 45 || c === 46)
                  continue;
  
              if (noTime)
                  return new Date(self);
          }
  
          if (arr[1] === undefined)
              arr[1] = '00:00:00';
  
          var firstDay = arr[0].indexOf('-') === -1;
  
          var date = (arr[0] || '').split(firstDay ? '.' : '-');
          var time = (arr[1] || '').split(':');
          var parsed = [];
  
          if (date.length < 4 && time.length < 2)
              return new Date(self);
  
          index = (time[2] || '').indexOf('.');
  
          // milliseconds
          if (index !== -1) {
              time[3] = time[2].substring(index + 1);
              time[2] = time[2].substring(0, index);
          } else
              time[3] = '0';
  
          parsed.push(parseInt(date[firstDay ? 2 : 0], 10)); // year
          parsed.push(parseInt(date[1], 10)); // month
          parsed.push(parseInt(date[firstDay ? 0 : 2], 10)); // day
          parsed.push(parseInt(time[0], 10)); // hours
          parsed.push(parseInt(time[1], 10)); // minutes
          parsed.push(parseInt(time[2], 10)); // seconds
          parsed.push(parseInt(time[3], 10)); // miliseconds
  
          var def = new Date();
  
          for (var i = 0, length = parsed.length; i < length; i++) {
              if (isNaN(parsed[i]))
                  parsed[i] = 0;
  
              var value = parsed[i];
              if (value !== 0)
                  continue;
  
              switch (i) {
                  case 0:
                      if (value <= 0)
                          parsed[i] = def.getFullYear();
                      break;
                  case 1:
                      if (value <= 0)
                          parsed[i] = def.getMonth() + 1;
                      break;
                  case 2:
                      if (value <= 0)
                          parsed[i] = def.getDate();
                      break;
              }
          }
  
          return new Date(parsed[0], parsed[1] - 1, parsed[2], parsed[3], parsed[4], parsed[5]);
      };
  }
  
  function noop() {}
  
  const
      MAX_BUFFER_SIZE = 8192;
  
  const
      op_void                   = 0,  // Packet has been voided
      op_connect                = 1,  // Connect to remote server
      op_exit                   = 2,  // Remote end has exitted
      op_accept                 = 3,  // Server accepts connection
      op_reject                 = 4,  // Server rejects connection
      op_disconnect             = 6,  // Connect is going away
      op_response               = 9,  // Generic response block
  
      // Full context server operations
  
      op_attach                 = 19, // Attach database
      op_create                 = 20, // Create database
      op_detach                 = 21, // Detach database
      op_compile                = 22, // Request based operations
      op_start                  = 23,
      op_start_and_send         = 24,
      op_send                   = 25,
      op_receive                = 26,
      op_unwind                 = 27, // apparently unused, see protocol.cpp's case op_unwind
      op_release                = 28,
  
      op_transaction            = 29, // Transaction operations
      op_commit                 = 30,
      op_rollback               = 31,
      op_prepare                = 32,
      op_reconnect              = 33,
  
      op_create_blob            = 34, // Blob operations
      op_open_blob              = 35,
      op_get_segment            = 36,
      op_put_segment            = 37,
      op_cancel_blob            = 38,
      op_close_blob             = 39,
  
      op_info_database          = 40, // Information services
      op_info_request           = 41,
      op_info_transaction       = 42,
      op_info_blob              = 43,
  
      op_batch_segments         = 44, // Put a bunch of blob segments
  
      op_que_events             = 48, // Que event notification request
      op_cancel_events          = 49, // Cancel event notification request
      op_commit_retaining       = 50, // Commit retaining (what else)
      op_prepare2               = 51, // Message form of prepare
      op_event                  = 52, // Completed event request (asynchronous)
      op_connect_request        = 53, // Request to establish connection
      op_aux_connect            = 54, // Establish auxiliary connection
      op_ddl                    = 55, // DDL call
      op_open_blob2             = 56,
      op_create_blob2           = 57,
      op_get_slice              = 58,
      op_put_slice              = 59,
      op_slice                  = 60, // Successful response to op_get_slice
      op_seek_blob              = 61, // Blob seek operation
  
  // DSQL operations
  
      op_allocate_statement     = 62, // allocate a statment handle
      op_execute                = 63, // execute a prepared statement
      op_exec_immediate         = 64, // execute a statement
      op_fetch                  = 65, // fetch a record
      op_fetch_response         = 66, // response for record fetch
      op_free_statement         = 67, // free a statement
      op_prepare_statement      = 68, // prepare a statement
      op_set_cursor             = 69, // set a cursor name
      op_info_sql               = 70,
  
      op_dummy                  = 71, // dummy packet to detect loss of client
      op_response_piggyback     = 72, // response block for piggybacked messages
      op_start_and_receive      = 73,
      op_start_send_and_receive = 74,
      op_exec_immediate2        = 75, // execute an immediate statement with msgs
      op_execute2               = 76, // execute a statement with msgs
      op_insert                 = 77,
      op_sql_response           = 78, // response from execute, exec immed, insert
      op_transact               = 79,
      op_transact_response      = 80,
      op_drop_database          = 81,
      op_service_attach         = 82,
      op_service_detach         = 83,
      op_service_info           = 84,
      op_service_start          = 85,
      op_rollback_retaining     = 86,
      op_partial                = 89, // packet is not complete - delay processing
      op_trusted_auth           = 90,
      op_cancel                 = 91,
      op_cont_auth              = 92,
      op_ping                   = 93,
      op_accept_data            = 94, // Server accepts connection and returns some data to client
      op_abort_aux_connection   = 95, // Async operation - stop waiting for async connection to arrive
      op_crypt                  = 96,
      op_crypt_key_callback     = 97,
      op_cond_accept            = 98; // Server accepts connection, returns some data to client
                                      // and asks client to continue authentication before attach call
  
  const
      CONNECT_VERSION2          = 2;
      ARCHITECTURE_GENERIC      = 1;
  
  const
  // Protocol 10 includes support for warnings and removes the requirement for
  // encoding and decoding status codes
      PROTOCOL_VERSION10  = 10,
  
  // Since protocol 11 we must be separated from Borland Interbase.
  // Therefore always set highmost bit in protocol version to 1.
  // For unsigned protocol version this does not break version's compare.
  
      FB_PROTOCOL_FLAG    = 0x8000,
  
  // Protocol 11 has support for user authentication related
  // operations (op_update_account_info, op_authenticate_user and
  // op_trusted_auth). When specific operation is not supported,
  // we say "sorry".
  
      PROTOCOL_VERSION11  = (FB_PROTOCOL_FLAG | 11),
  
  // Protocol 12 has support for asynchronous call op_cancel.
  // Currently implemented asynchronously only for TCP/IP.
  
      PROTOCOL_VERSION12  = (FB_PROTOCOL_FLAG | 12),
  
  // Protocol 13 has support for authentication plugins (op_cont_auth).
  
      PROTOCOL_VERSION13  = (FB_PROTOCOL_FLAG | 13);
  
  
  const
      DSQL_close      = 1,
      DSQL_drop       = 2,
      DSQL_unprepare  = 4; // >= 2.5
  
  const
      ptype_batch_send = 3;
  
  const
      SQL_TEXT      = 452, // Array of char
      SQL_VARYING   = 448,
      SQL_SHORT     = 500,
      SQL_LONG      = 496,
      SQL_FLOAT     = 482,
      SQL_DOUBLE    = 480,
      SQL_D_FLOAT   = 530,
      SQL_TIMESTAMP = 510,
      SQL_BLOB      = 520,
      SQL_ARRAY     = 540,
      SQL_QUAD      = 550,
      SQL_TYPE_TIME = 560,
      SQL_TYPE_DATE = 570,
      SQL_INT64     = 580,
      SQL_BOOLEAN   = 32764, // >= 3.0
      SQL_NULL      = 32766; // >= 2.5
  
  /***********************/
  /*   ISC Services      */
  /***********************/
  const
  	isc_action_svc_backup = 1, /* Starts database backup process on the server	*/
  	isc_action_svc_restore = 2, /* Starts database restore process on the server */
  	isc_action_svc_repair = 3, /* Starts database repair process on the server	*/
  	isc_action_svc_add_user = 4, /* Adds	a new user to the security database	*/
  	isc_action_svc_delete_user = 5, /* Deletes a user record from the security database	*/
  	isc_action_svc_modify_user = 6, /* Modifies	a user record in the security database */
  	isc_action_svc_display_user = 7, /* Displays	a user record from the security	database */
  	isc_action_svc_properties = 8, /* Sets	database properties	*/
  	isc_action_svc_add_license = 9, /* Adds	a license to the license file */
  	isc_action_svc_remove_license = 10, /* Removes a license from the license file */
  	isc_action_svc_db_stats = 11, /* Retrieves database statistics */
  	isc_action_svc_get_ib_log = 12, /* Retrieves the InterBase log file	from the server	*/  
      isc_action_svc_get_fb_log = isc_action_svc_get_ib_log, /* Retrieves the Firebird log file	from the server	*/ 
      isc_action_svc_nbak = 20, /* start nbackup */
      isc_action_svc_nrest = 21,  /* start nrestore */
      isc_action_svc_trace_start = 22,
      isc_action_svc_trace_stop = 23,
      isc_action_svc_trace_suspend = 24,
      isc_action_svc_trace_resume = 25,
      isc_action_svc_trace_list = 26;
  
  const
  	isc_info_svc_svr_db_info = 50, /* Retrieves the number	of attachments and databases */
  	isc_info_svc_get_license = 51, /* Retrieves all license keys and IDs from the license file	*/
  	isc_info_svc_get_license_mask = 52, /* Retrieves a bitmask representing	licensed options on	the	server */
  	isc_info_svc_get_config = 53, /* Retrieves the parameters	and	values for IB_CONFIG */
  	isc_info_svc_version = 54, /* Retrieves the version of	the	services manager */
  	isc_info_svc_server_version = 55, /* Retrieves the version of	the	InterBase server */
  	isc_info_svc_implementation = 56, /* Retrieves the implementation	of the InterBase server	*/
  	isc_info_svc_capabilities = 57, /* Retrieves a bitmask representing	the	server's capabilities */
  	isc_info_svc_user_dbpath = 58, /* Retrieves the path to the security database in use by the server	*/
  	isc_info_svc_get_env = 59, /* Retrieves the setting of	$INTERBASE */
  	isc_info_svc_get_env_lock = 60, /* Retrieves the setting of	$INTERBASE_LCK */
  	isc_info_svc_get_env_msg = 61, /* Retrieves the setting of	$INTERBASE_MSG */
  	isc_info_svc_line = 62, /* Retrieves 1 line	of service output per call */
  	isc_info_svc_to_eof = 63, /* Retrieves as much of	the	server output as will fit in the supplied buffer */
  	isc_info_svc_timeout = 64, /* Sets	/ signifies	a timeout value	for	reading	service	information	*/
  	isc_info_svc_get_licensed_users = 65, /* Retrieves the number	of users licensed for accessing	the	server */
  	isc_info_svc_limbo_trans = 66, /* Retrieve	the	limbo transactions */
  	isc_info_svc_running = 67, /* Checks to see if	a service is running on	an attachment */
  	isc_info_svc_get_users = 68, /* Returns the user	information	from isc_action_svc_display_users */
      isc_info_svc_stdin = 78;  
  
  /* Services Properties */	
  const
  	isc_spb_prp_page_buffers = 5,
  	isc_spb_prp_sweep_interval = 6,
  	isc_spb_prp_shutdown_db = 7,
  	isc_spb_prp_deny_new_attachments = 9,
  	isc_spb_prp_deny_new_transactions = 10,
  	isc_spb_prp_reserve_space = 11,
  	isc_spb_prp_write_mode = 12,
  	isc_spb_prp_access_mode = 13,
  	isc_spb_prp_set_sql_dialect = 14,
      isc_spb_num_att = 5,
      isc_spb_num_db = 6,
      // SHUTDOWN OPTION FOR 2.0
      isc_spb_prp_force_shutdown = 41,
      isc_spb_prp_attachments_shutdown = 42,
      isc_spb_prp_transactions_shutdown = 43,
      isc_spb_prp_shutdown_mode = 44,
      isc_spb_prp_online_mode = 45,
  
      isc_spb_prp_sm_normal = 0,
      isc_spb_prp_sm_multi = 1,
      isc_spb_prp_sm_single = 2,
      isc_spb_prp_sm_full = 3;
  
  
  		// WRITE_MODE_PARAMETERS
  	isc_spb_prp_wm_async = 37,
  	isc_spb_prp_wm_sync = 38,
  
  		// ACCESS_MODE_PARAMETERS
  	isc_spb_prp_am_readonly = 39,
  	isc_spb_prp_am_readwrite = 40,
  
  		// RESERVE_SPACE_PARAMETERS
  	isc_spb_prp_res_use_full = 35,
  	isc_spb_prp_res = 36,
  
  		// Option Flags		
  	isc_spb_prp_activate = 0x0100,
  	isc_spb_prp_db_online = 0x0200;
  
      // SHUTDOWN MODE
  
  /* · Backup Service ·*/
  const
  	isc_spb_bkp_file = 5,
  	isc_spb_bkp_factor = 6,
  	isc_spb_bkp_length = 7,
      isc_spb_bkp_ignore_checksums = 0x01,
      isc_spb_bkp_ignore_limbo = 0x02,
      isc_spb_bkp_metadata_only = 0x04,
      isc_spb_bkp_no_garbage_collect = 0x08,
      isc_spb_bkp_old_descriptions = 0x10,
      isc_spb_bkp_non_transportable = 0x20,
      isc_spb_bkp_convert = 0x40,
      isc_spb_bkp_expand = 0x80,
      isc_spb_bkp_no_triggers = 0x8000,
      // nbackup
      isc_spb_nbk_level = 5,
      isc_spb_nbk_file = 6,
      isc_spb_nbk_direct = 7,
      isc_spb_nbk_no_triggers = 0x01;
  
  /*	Restore Service ·*/
  const
  	isc_spb_res_buffers = 9,
  	isc_spb_res_page_size = 10,
  	isc_spb_res_length = 11,
  	isc_spb_res_access_mode = 12,
      isc_spb_res_fix_fss_data = 13,
      isc_spb_res_fix_fss_metadata = 14,
  	isc_spb_res_am_readonly = isc_spb_prp_am_readonly,
  	isc_spb_res_am_readwrite = isc_spb_prp_am_readwrite,
      isc_spb_res_deactivate_idx = 0x0100,
      isc_spb_res_no_shadow = 0x0200,
      isc_spb_res_no_validity = 0x0400,
      isc_spb_res_one_at_a_time = 0x0800,
      isc_spb_res_replace = 0x1000,
      isc_spb_res_create = 0x2000,
      isc_spb_res_use_all_space = 0x4000;
      
  
  /* · Repair Service ·*/
  const
      isc_spb_rpr_commit_trans = 15,
  	isc_spb_rpr_rollback_trans = 34,
  	isc_spb_rpr_recover_two_phase = 17,
  	isc_spb_tra_id = 18,
  	isc_spb_single_tra_id = 19,
  	isc_spb_multi_tra_id = 20,
  	isc_spb_tra_state = 21,
  	isc_spb_tra_state_limbo = 22,
  	isc_spb_tra_state_commit = 23,
  	isc_spb_tra_state_rollback = 24,
  	isc_spb_tra_state_unknown = 25,
  	isc_spb_tra_host_site = 26,
  	isc_spb_tra_remote_site = 27,
  	isc_spb_tra_db_path = 28,
  	isc_spb_tra_advise = 29,
  	isc_spb_tra_advise_commit = 30,
  	isc_spb_tra_advise_rollback = 31,
  	isc_spb_tra_advise_unknown = 33,
      isc_spb_rpr_validate_db = 0x01,
      isc_spb_rpr_sweep_db = 0x02,
      isc_spb_rpr_mend_db = 0x04,
      isc_spb_rpr_list_limbo_trans = 0x08,
      isc_spb_rpr_check_db = 0x10,
      isc_spb_rpr_ignore_checksum = 0x20,
      isc_spb_rpr_kill_shadows = 0x40,
      isc_spb_rpr_full = 0x80,
      isc_spb_rpr_icu = 0x0800;
  
  /* · Security Service ·*/
  const
  	isc_spb_sec_userid = 5,
  	isc_spb_sec_groupid = 6,
  	isc_spb_sec_username = 7,
  	isc_spb_sec_password = 8,
  	isc_spb_sec_groupname = 9,
  	isc_spb_sec_firstname = 10,
  	isc_spb_sec_middlename = 11,
  	isc_spb_sec_lastname = 12,
      isc_spb_sec_admin = 13;
  
  /* License Service */
  const
      isc_spb_lic_key = 5,
      isc_spb_lic_id = 6,
      isc_spb_lic_desc = 7;
  
  /* Statistics Service */
  const
      isc_spb_sts_data_pages = 0x01,
      isc_spb_sts_db_log = 0x02,
      isc_spb_sts_hdr_pages = 0x04,
      isc_spb_sts_idx_pages = 0x08,
      isc_spb_sts_sys_relations = 0x10,
      isc_spb_sts_record_versions = 0x20,
      isc_spb_sts_table = 0x40,
      isc_spb_sts_nocreation = 0x80;
  
  /* Trace Service */
  const
      isc_spb_trc_id = 1,
      isc_spb_trc_name = 2,
      isc_spb_trc_cfg = 3;
  
  
  /***********************/
  /*   ISC Error Codes   */
  /***********************/
  const
      isc_arg_end                     = 0,  // end of argument list
      isc_arg_gds                     = 1,  // generic DSRI status value
      isc_arg_string                  = 2,  // string argument
      isc_arg_cstring                 = 3,  // count & string argument
      isc_arg_number                  = 4,  // numeric argument (long)
      isc_arg_interpreted             = 5,  // interpreted status code (string)
      isc_arg_unix                    = 7,  // UNIX error code
      isc_arg_next_mach               = 15, // NeXT/Mach error code
      isc_arg_win32                   = 17, // Win32 error code
      isc_arg_warning                 = 18, // warning argument
      isc_arg_sql_state               = 19; // SQLSTATE
  
  const
      isc_sqlerr = 335544436;
  
  /**********************************/
  /* Database parameter block stuff */
  /**********************************/
  const
      isc_dpb_version1                = 1,
      isc_dpb_version2                = 2, // >= FB30
      isc_dpb_cdd_pathname            = 1,
      isc_dpb_allocation              = 2,
      isc_dpb_journal                 = 3,
      isc_dpb_page_size               = 4,
      isc_dpb_num_buffers             = 5,
      isc_dpb_buffer_length           = 6,
      isc_dpb_debug                   = 7,
      isc_dpb_garbage_collect         = 8,
      isc_dpb_verify                  = 9,
      isc_dpb_sweep                   = 10,
      isc_dpb_enable_journal          = 11,
      isc_dpb_disable_journal         = 12,
      isc_dpb_dbkey_scope             = 13,
      isc_dpb_number_of_users         = 14,
      isc_dpb_trace                   = 15,
      isc_dpb_no_garbage_collect      = 16,
      isc_dpb_damaged                 = 17,
      isc_dpb_license                 = 18,
      isc_dpb_sys_user_name           = 19,
      isc_dpb_encrypt_key             = 20,
      isc_dpb_activate_shadow         = 21,
      isc_dpb_sweep_interval          = 22,
      isc_dpb_delete_shadow           = 23,
      isc_dpb_force_write             = 24,
      isc_dpb_begin_log               = 25,
      isc_dpb_quit_log                = 26,
      isc_dpb_no_reserve              = 27,
      isc_dpb_user_name               = 28,
      isc_dpb_password                = 29,
      isc_dpb_password_enc            = 30,
      isc_dpb_sys_user_name_enc       = 31,
      isc_dpb_interp                  = 32,
      isc_dpb_online_dump             = 33,
      isc_dpb_old_file_size           = 34,
      isc_dpb_old_num_files           = 35,
      isc_dpb_old_file                = 36,
      isc_dpb_old_start_page          = 37,
      isc_dpb_old_start_seqno         = 38,
      isc_dpb_old_start_file          = 39,
      isc_dpb_old_dump_id             = 41,
      isc_dpb_lc_messages             = 47,
      isc_dpb_lc_ctype                = 48,
      isc_dpb_cache_manager           = 49,
      isc_dpb_shutdown                = 50,
      isc_dpb_online                  = 51,
      isc_dpb_shutdown_delay          = 52,
      isc_dpb_reserved                = 53,
      isc_dpb_overwrite               = 54,
      isc_dpb_sec_attach              = 55,
      isc_dpb_connect_timeout         = 57,
      isc_dpb_dummy_packet_interval   = 58,
      isc_dpb_gbak_attach             = 59,
      isc_dpb_sql_role_name           = 60,
      isc_dpb_set_page_buffers        = 61,
      isc_dpb_working_directory       = 62,
      isc_dpb_sql_dialect             = 63,
      isc_dpb_set_db_readonly         = 64,
      isc_dpb_set_db_sql_dialect      = 65,
      isc_dpb_gfix_attach             = 66,
      isc_dpb_gstat_attach            = 67,
      isc_dpb_set_db_charset          = 68,
      isc_dpb_gsec_attach             = 69,
      isc_dpb_address_path            = 70,
      isc_dpb_process_id              = 71,
      isc_dpb_no_db_triggers          = 72,
      isc_dpb_trusted_auth            = 73,
      isc_dpb_process_name            = 74,
      isc_dpb_trusted_role            = 75,
      isc_dpb_org_filename            = 76,
      isc_dpb_utf8_filename           = 77,
      isc_dpb_ext_call_depth          = 78;
  
  /*************************************/
  /* Services parameter block stuff    */
  /*************************************/
  const
      isc_spb_version1 = 1,
      isc_spb_current_version = 2,
      isc_spb_version = isc_spb_current_version,
      isc_spb_user_name = isc_dpb_user_name,
      isc_spb_sys_user_name = isc_dpb_sys_user_name,
      isc_spb_sys_user_name_enc = isc_dpb_sys_user_name_enc,
      isc_spb_password = isc_dpb_password,
      isc_spb_password_enc = isc_dpb_password_enc,
      isc_spb_command_line = 105,
      isc_spb_dbname = 106,
      isc_spb_verbose = 107,
      isc_spb_options = 108;
  
  /*************************************/
  /* Transaction parameter block stuff */
  /*************************************/
  const
      isc_tpb_version1                =  1,
      isc_tpb_version3                =  3,
      isc_tpb_consistency             =  1,
      isc_tpb_concurrency             =  2,
      isc_tpb_shared                  =  3, // < FB21
      isc_tpb_protected               =  4, // < FB21
      isc_tpb_exclusive               =  5, // < FB21
      isc_tpb_wait                    =  6,
      isc_tpb_nowait                  =  7,
      isc_tpb_read                    =  8,
      isc_tpb_write                   =  9,
      isc_tpb_lock_read               =  10,
      isc_tpb_lock_write              =  11,
      isc_tpb_verb_time               =  12,
      isc_tpb_commit_time             =  13,
      isc_tpb_ignore_limbo            =  14,
      isc_tpb_read_committed          =  15,
      isc_tpb_autocommit              =  16,
      isc_tpb_rec_version             =  17,
      isc_tpb_no_rec_version          =  18,
      isc_tpb_restart_requests        =  19,
      isc_tpb_no_auto_undo            =  20,
      isc_tpb_lock_timeout            =  21; // >= FB20
  
  /****************************/
  /* Common, structural codes */
  /****************************/
  const
      isc_info_end                    = 1,
      isc_info_truncated              = 2,
      isc_info_error                  = 3,
      isc_info_data_not_ready         = 4,
      isc_info_length                 = 126,
      isc_info_flag_end               = 127;
  
  /*************************/
  /* SQL information items */
  /*************************/
  const
      isc_info_sql_select             = 4,
      isc_info_sql_bind               = 5,
      isc_info_sql_num_variables      = 6,
      isc_info_sql_describe_vars      = 7,
      isc_info_sql_describe_end       = 8,
      isc_info_sql_sqlda_seq          = 9,
      isc_info_sql_message_seq        = 10,
      isc_info_sql_type               = 11,
      isc_info_sql_sub_type           = 12,
      isc_info_sql_scale              = 13,
      isc_info_sql_length             = 14,
      isc_info_sql_null_ind           = 15,
      isc_info_sql_field              = 16,
      isc_info_sql_relation           = 17,
      isc_info_sql_owner              = 18,
      isc_info_sql_alias              = 19,
      isc_info_sql_sqlda_start        = 20,
      isc_info_sql_stmt_type          = 21,
      isc_info_sql_get_plan           = 22,
      isc_info_sql_records            = 23,
      isc_info_sql_batch_fetch        = 24,
      isc_info_sql_relation_alias     = 25, // >= 2.0
      isc_info_sql_explain_plan       = 26; // >= 3.0
  
  /*******************/
  /* Blr definitions */
  /*******************/
  const
      blr_text            = 14,
      blr_text2           = 15,
      blr_short           = 7,
      blr_long            = 8,
      blr_quad            = 9,
      blr_float           = 10,
      blr_double          = 27,
      blr_d_float         = 11,
      blr_timestamp       = 35,
      blr_varying         = 37,
      blr_varying2        = 38,
      blr_blob            = 261,
      blr_cstring         = 40,
      blr_cstring2        = 41,
      blr_blob_id         = 45,
      blr_sql_date        = 12,
      blr_sql_time        = 13,
      blr_int64           = 16,
      blr_blob2           = 17, // >= 2.0
      blr_domain_name     = 18, // >= 2.1
      blr_domain_name2    = 19, // >= 2.1
      blr_not_nullable    = 20, // >= 2.1
      blr_column_name     = 21, // >= 2.5
      blr_column_name2    = 22, // >= 2.5
      blr_bool            = 23, // >= 3.0
  
      blr_version4        = 4,
      blr_version5        = 5, // dialect 3
      blr_eoc             = 76,
      blr_end             = 255,
  
      blr_assignment      = 1,
      blr_begin           = 2,
      blr_dcl_variable    = 3,
      blr_message         = 4;
  
  const
      isc_info_sql_stmt_select          = 1,
      isc_info_sql_stmt_insert          = 2,
      isc_info_sql_stmt_update          = 3,
      isc_info_sql_stmt_delete          = 4,
      isc_info_sql_stmt_ddl             = 5,
      isc_info_sql_stmt_get_segment     = 6,
      isc_info_sql_stmt_put_segment     = 7,
      isc_info_sql_stmt_exec_procedure  = 8,
      isc_info_sql_stmt_start_trans     = 9,
      isc_info_sql_stmt_commit          = 10,
      isc_info_sql_stmt_rollback        = 11,
      isc_info_sql_stmt_select_for_upd  = 12,
      isc_info_sql_stmt_set_generator   = 13,
      isc_info_sql_stmt_savepoint       = 14;
  
  const
      isc_blob_text = 1;
  
  const
      DESCRIBE =
          [isc_info_sql_stmt_type,
          isc_info_sql_select,
              isc_info_sql_describe_vars,
              isc_info_sql_sqlda_seq,
              isc_info_sql_type,
              isc_info_sql_sub_type,
              isc_info_sql_scale,
              isc_info_sql_length,
              isc_info_sql_field,
              isc_info_sql_relation,
              //isc_info_sql_owner,
              isc_info_sql_alias,
              isc_info_sql_describe_end,
              isc_info_sql_bind,
              isc_info_sql_describe_vars,
              isc_info_sql_sqlda_seq,
              isc_info_sql_type,
              isc_info_sql_sub_type,
              isc_info_sql_scale,
              isc_info_sql_length,
              isc_info_sql_describe_end];
  
  const
      ISOLATION_READ_UNCOMMITTED          = [isc_tpb_version3, isc_tpb_write, isc_tpb_wait, isc_tpb_read_committed, isc_tpb_rec_version],
      ISOLATION_READ_COMMITED             = [isc_tpb_version3, isc_tpb_write, isc_tpb_wait, isc_tpb_read_committed, isc_tpb_no_rec_version],
      ISOLATION_REPEATABLE_READ           = [isc_tpb_version3, isc_tpb_write, isc_tpb_wait, isc_tpb_concurrency],
      ISOLATION_SERIALIZABLE              = [isc_tpb_version3, isc_tpb_write, isc_tpb_wait, isc_tpb_consistency],
      ISOLATION_READ_COMMITED_READ_ONLY   = [isc_tpb_version3, isc_tpb_read, isc_tpb_wait, isc_tpb_read_committed, isc_tpb_no_rec_version];
  
  const
      DEFAULT_HOST = '127.0.0.1',
      DEFAULT_PORT = 3050,
      DEFAULT_USER = 'SYSDBA',
      DEFAULT_PASSWORD = 'masterkey',
      DEFAULT_PAGE_SIZE = 4096,
      DEFAULT_SVC_NAME = 'service_mgr';
  
  exports.ISOLATION_READ_UNCOMMITTED = ISOLATION_READ_UNCOMMITTED;
  exports.ISOLATION_READ_COMMITED = ISOLATION_READ_COMMITED;
  exports.ISOLATION_REPEATABLE_READ = ISOLATION_REPEATABLE_READ;
  exports.ISOLATION_SERIALIZABLE = ISOLATION_SERIALIZABLE;
  exports.ISOLATION_READ_COMMITED_READ_ONLY = ISOLATION_READ_COMMITED_READ_ONLY;
  
  if (!String.prototype.padLeft) {
      String.prototype.padLeft = function(max, c) {
          var self = this;
          return new Array(Math.max(0, max - self.length + 1)).join(c || ' ') + self;
      };
  }
  
  /**
   * Escape value
   * @param {Object} value
   * @return {String}
   */
  exports.escape = function(value) {
  
      if (value === null || value === undefined)
          return 'NULL';
  
      switch (typeof(value)) {
          case 'boolean':
              return value ? '1' : '0';
          case 'number':
              return value.toString();
          case 'string':
              return "'" + value.replace(/'/g, "''").replace(/\\/g, '\\\\') + "'";
      }
  
      if (value instanceof Date)
          return "'" + value.getFullYear() + '-' + value.getMonth().toString().padLeft(2, '0') + '-' + value.getDate().toString().padLeft(2, '0') + ' ' + value.getHours().toString().padLeft(2, '0') + ':' + value.getMinutes().toString().padLeft(2, '0') + ':' + value.getSeconds().toString().padLeft(2, '0') + "'";
  
      throw new Error('Escape supports only primitive values.');
  };
  
  const
      DEFAULT_ENCODING = 'utf8';
      DEFAULT_FETCHSIZE = 200;
  
  const
      MAX_INT = Math.pow(2, 31) - 1;
      MIN_INT = - Math.pow(2, 31);
  
  /***************************************
   *
   *   SQLVar
   *
   ***************************************/
  
  
  const
      ScaleDivisor = [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000, 100000000000,1000000000000,10000000000000,100000000000000,1000000000000000];
  const
      DateOffset = 40587,
      TimeCoeff = 86400000;
      MsPerMinute = 60000;
  
  //------------------------------------------------------
  
  function SQLVarText() {}
  
  SQLVarText.prototype.decode = function(data) {
      var ret = data.readString(DEFAULT_ENCODING);
      if (!data.readInt())
          return ret;
      return null;
  };
  
  SQLVarText.prototype.calcBlr = function(blr) {
      blr.addByte(blr_text);
      blr.addWord(this.length);
  };
  
  //------------------------------------------------------
  
  function SQLVarNull() {}
  SQLVarNull.prototype = new SQLVarText();
  SQLVarNull.prototype.constructor = SQLVarNull;
  
  //------------------------------------------------------
  
  function SQLVarString() {}
  
  SQLVarString.prototype.decode = function(data) {
      var ret = data.readString(DEFAULT_ENCODING);
      if (!data.readInt())
          return ret;
      return null;
  };
  
  SQLVarString.prototype.calcBlr = function(blr) {
      blr.addByte(blr_varying);
      blr.addWord(this.length);
  };
  
  //------------------------------------------------------
  
  function SQLVarQuad() {}
  
  SQLVarQuad.prototype.decode = function(data) {
      var ret = data.readQuad();
      if (!data.readInt()) {
          return ret;
      }
      return null;
  };
  
  SQLVarQuad.prototype.calcBlr = function(blr) {
      blr.addByte(blr_quad);
      blr.addShort(this.scale);
  };
  
  //------------------------------------------------------
  
  function SQLVarBlob() {}
  SQLVarBlob.prototype = new SQLVarQuad();
  SQLVarBlob.prototype.constructor = SQLVarBlob;
  
  SQLVarBlob.prototype.calcBlr = function(blr) {
      blr.addByte(blr_quad);
      blr.addShort(0);
  };
  
  //------------------------------------------------------
  
  function SQLVarArray() {}
  SQLVarArray.prototype = new SQLVarQuad();
  SQLVarArray.prototype.constructor = SQLVarArray;
  
  SQLVarArray.prototype.calcBlr = function(blr) {
      blr.addByte(blr_quad);
      blr.addShort(0);
  };
  
  //------------------------------------------------------
  
  function SQLVarInt() {}
  
  SQLVarInt.prototype.decode = function(data) {
      var ret = data.readInt();
      if (!data.readInt()) {
          if (this.scale) {
              ret = ret / ScaleDivisor[Math.abs(this.scale)];
          }
          return ret;
      }
      return null;
  };
  
  SQLVarInt.prototype.calcBlr = function(blr) {
      blr.addByte(blr_long);
      blr.addShort(this.scale);
  };
  
  //------------------------------------------------------
  
  function SQLVarShort() {}
  SQLVarShort.prototype = new SQLVarInt();
  SQLVarShort.prototype.constructor = SQLVarShort;
  
  SQLVarShort.prototype.calcBlr = function(blr) {
      blr.addByte(blr_short);
      blr.addShort(this.scale);
  };
  
  //------------------------------------------------------
  
  function SQLVarInt64() {}
  
  SQLVarInt64.prototype.decode = function(data) {
      var ret = data.readInt64();
      if (!data.readInt()) {
          if (this.scale) {
              ret = ret / ScaleDivisor[Math.abs(this.scale)];
          }
          return ret;
      }
      return null;
  };
  
  SQLVarInt64.prototype.calcBlr = function(blr) {
      blr.addByte(blr_int64);
      blr.addShort(this.scale);
  };
  
  //------------------------------------------------------
  
  function SQLVarFloat() {}
  
  SQLVarFloat.prototype.decode = function(data) {
      var ret = data.readFloat();
      if (!data.readInt()) {
          return ret;
      }
      return null;
  };
  
  SQLVarFloat.prototype.calcBlr = function(blr) {
      blr.addByte(blr_float);
  };
  
  //------------------------------------------------------
  
  function SQLVarDouble() {}
  
  SQLVarDouble.prototype.decode = function(data) {
      var ret = data.readDouble();
      if (!data.readInt()) {
          return ret;
      }
      return null;
  };
  
  SQLVarDouble.prototype.calcBlr = function(blr) {
      blr.addByte(blr_double);
  };
  
  //------------------------------------------------------
  
  function SQLVarDate() {}
  
  SQLVarDate.prototype.decode = function(data) {
      var ret = data.readInt();
      if (!data.readInt()) {
          var d = new Date(0);
          d.setMilliseconds((ret - DateOffset) * TimeCoeff + d.getTimezoneOffset() * MsPerMinute);
          return d;
      }
      return null;
  };
  
  SQLVarDate.prototype.calcBlr = function(blr) {
      blr.addByte(blr_sql_date);
  };
  
  //------------------------------------------------------
  
  function SQLVarTime() {}
  
  SQLVarTime.prototype.decode = function(data) {
      var ret = data.readUInt();
      if (!data.readInt()) {
          var d = new Date(0);
          d.setMilliseconds(Math.floor(ret / 10) + d.getTimezoneOffset() * MsPerMinute);
          return d;
      }
      return null;
  };
  
  SQLVarTime.prototype.calcBlr = function(blr) {
      blr.addByte(blr_sql_time);
  };
  
  //------------------------------------------------------
  
  function SQLVarTimeStamp() {}
  
  SQLVarTimeStamp.prototype.decode = function(data) {
      var date = data.readInt();
      var time = data.readUInt();
      if (!data.readInt()) {
          var d = new Date(0);
          d.setMilliseconds((date - DateOffset) * TimeCoeff + Math.floor(time / 10) + d.getTimezoneOffset() * MsPerMinute);
          return d;
      }
      return null;
  };
  
  SQLVarTimeStamp.prototype.calcBlr = function(blr) {
      blr.addByte(blr_timestamp);
  };
  
  //------------------------------------------------------
  
  function SQLVarBoolean() {}
  
  SQLVarBoolean.prototype.decode = function(data) {
      var ret = data.readInt();
      if (!data.readInt()) {
          return Boolean(ret);
      }
      return null;
  };
  
  SQLVarBoolean.prototype.calcBlr = function(blr) {
      blr.addByte(blr_bool);
  };
  
  //------------------------------------------------------
  
  function SQLParamInt(value){
      this.value = value;
  }
  
  SQLParamInt.prototype.calcBlr = function(blr) {
      blr.addByte(blr_long);
      blr.addShort(0);
  };
  
  SQLParamInt.prototype.encode = function(data) {
      if (this.value != null) {
          data.addInt(this.value);
          data.addInt(0);
      } else {
          data.addInt(0);
          data.addInt(1);
      }
  };
  
  //------------------------------------------------------
  
  function SQLParamInt64(value){
      this.value = value;
  }
  
  SQLParamInt64.prototype.calcBlr = function(blr) {
      blr.addByte(blr_int64);
      blr.addShort(0);
  };
  
  SQLParamInt64.prototype.encode = function(data) {
      if (this.value != null) {
          data.addInt64(this.value);
          data.addInt(0);
      } else {
          data.addInt64(0);
          data.addInt(1);
      }
  };
  
  //------------------------------------------------------
  
  function SQLParamDouble(value) {
      this.value = value;
  }
  
  SQLParamDouble.prototype.encode = function(data) {
      if (this.value != null) {
          data.addDouble(this.value);
          data.addInt(0);
      } else {
          data.addDouble(0);
          data.addInt(1);
      }
  };
  
  SQLParamDouble.prototype.calcBlr = function(blr) {
      blr.addByte(blr_double);
  };
  
  //------------------------------------------------------
  
  function SQLParamString(value) {
      this.value = value;
  }
  
  SQLParamString.prototype.encode = function(data) {
      if (this.value != null) {
          data.addText(this.value, DEFAULT_ENCODING);
          data.addInt(0);
      } else {
          data.addInt(1);
      }
  };
  
  SQLParamString.prototype.calcBlr = function(blr) {
      blr.addByte(blr_text);
      var len = this.value ? Buffer.byteLength(this.value, DEFAULT_ENCODING) : 0;
      blr.addWord(len);
  };
  
  //------------------------------------------------------
  
  function SQLParamQuad(value) {
      this.value = value;
  }
  
  SQLParamQuad.prototype.encode = function(data) {
      if (this.value != null) {
          data.addInt(this.value.high);
          data.addInt(this.value.low);
          data.addInt(0);
      } else {
          data.addInt(0);
          data.addInt(0);
          data.addInt(1);
      }
  };
  
  SQLParamQuad.prototype.calcBlr = function(blr) {
      blr.addByte(blr_quad);
      blr.addShort(0);
  };
  
  //------------------------------------------------------
  
  function SQLParamDate(value) {
      this.value = value;
  }
  
  SQLParamDate.prototype.encode = function(data) {
      if (this.value != null) {
  
          var value = this.value.getTime() - this.value.getTimezoneOffset() * MsPerMinute;
          var time = value % TimeCoeff;
          var date = (value - time) / TimeCoeff + DateOffset;
          time *= 10;
          data.addInt(date);
          data.addUInt(time);
          data.addInt(0);
      } else {
          data.addInt(0);
          data.addUInt(0);
          data.addInt(1);
      }
  };
  
  SQLParamDate.prototype.calcBlr = function(blr) {
      blr.addByte(blr_timestamp);
  };
  
  //------------------------------------------------------
  
  function SQLParamBool(value) {
      this.value = value;
  }
  
  SQLParamBool.prototype.encode = function(data) {
      if (this.value != null) {
          data.addInt(this.value ? 1 : 0);
          data.addInt(0);
      } else {
          data.addInt(0);
          data.addInt(1);
      }
  };
  
  SQLParamBool.prototype.calcBlr = function(blr) {
      blr.addByte(blr_short);
      blr.addShort(0);
  };
  
  
  /***************************************
   *
   *   Error handling
   *
   ***************************************/
  
  function isError(obj) {
      return (obj instanceof Object && obj.status);
  }
  
  function doCallback(obj, callback) {
  
      if (!callback)
          return;
  
      if (obj instanceof Error) {
          callback(obj);
          return;
      }
  
      if (isError(obj)) {
          // callback(new Error(obj.message));
          callback(obj);
          return;
      }
  
      callback(undefined, obj);
  
  }
  
  function doError(obj, callback) {
      if (callback)
          callback(obj)
  }
  
  /***************************************
   *
   *   Statement
   *
   ***************************************/
  
  function Statement(connection) {
      this.connection = connection;
  }
  
  Statement.prototype.close = function(callback) {
      this.connection.closeStatement(this, callback);
  };
  
  Statement.prototype.drop = function(callback) {
      this.connection.dropStatement(this, callback);
  };
  
  Statement.prototype.execute = function(transaction, params, callback, custom) {
  
      if (params instanceof Function) {
          custom = callback;
          callback = params;
          params = undefined;
      }
  
      this.custom = custom;
      this.connection.executeStatement(transaction, this, params, callback, custom);
  };
  
  Statement.prototype.fetch = function(transaction, count, callback) {
      this.connection.fetch(this, transaction, count, callback);
  };
  
  Statement.prototype.fetchAll = function(transaction, callback) {
      this.connection.fetchAll(this, transaction, callback);
  };
  
  /***************************************
   *
   *   Transaction
   *
   ***************************************/
  
  function Transaction(connection) {
      this.connection = connection;
      this.db = connection.db;
  }
  
  Transaction.prototype.newStatement = function(query, callback) {
      var cnx = this.connection;
      var self = this;
  
      cnx.allocateStatement(function(err, statement) {
          if (err) {
              doError(err, callback);
              return;
          }
          cnx.prepareStatement(self, statement, query, false, callback);
      });
  };
  
  Transaction.prototype.execute = function(query, params, callback, custom) {
  
      if (params instanceof Function) {
          callback = params;
          params = undefined;
      }
  
      var self = this;
      this.newStatement(query, function(err, statement) {
  
          if (err) {
              doError(err, callback);
              return;
          }
  
          function dropError(err) {
              statement.drop();
              doCallback(err, callback);
          }
  
          statement.execute(self, params, function(err) {
  
              if (err) {
                  dropError(err);
                  return;
              }
  
              switch (statement.type) {
  
                  case isc_info_sql_stmt_select:
                      statement.fetchAll(self, function(err, ret) {
  
                          if (err) {
                              dropError(err);
                              return;
                          }
  
                          statement.drop();
  
                          if (callback)
                              callback(undefined, ret, statement.output, true);
  
                      });
  
                      break;
  
                  case isc_info_sql_stmt_exec_procedure:
                      if (statement.output.length) {
                          statement.fetch(self, 1, function(err, ret) {
                              if (err) {
                                  dropError(err);
                                  return;
                              }
  
                              statement.drop();
  
                              if (callback)
                                  callback(undefined, ret.data[0], statement.output, false);
                          });
  
                          break;
                      }
  
                  // Fall through is normal
                  default:
                      statement.drop();
                      if (callback)
                          callback()
                      break;
              }
  
          }, custom);
      });
  };
  
  Transaction.prototype.query = function(query, params, callback) {
  
      if (params instanceof Function) {
          callback = params;
          params = undefined;
      }
  
      if (callback === undefined)
          callback = noop;
  
      this.execute(query, params, callback, { asObject: true, asStream: callback === undefined || callback === null });
  
  };
  
  Transaction.prototype.commit = function(callback) {
      this.connection.commit(this, callback);
  };
  
  Transaction.prototype.rollback = function(callback) {
      this.connection.rollback(this, callback);
  };
  
  Transaction.prototype.commitRetaining = function(callback) {
      this.connection.commitRetaining(this, callback);
  };
  
  Transaction.prototype.rollbackRetaining = function(callback) {
      this.connection.rollbackRetaining(this, callback);
  };
  
  /***************************************
   *
   *   Database
   *
   ***************************************/
  
  function Database(connection) {
      this.connection = connection;
      connection.db = this;
  }
  
  Database.prototype.__proto__ = Object.create(Events.EventEmitter.prototype, {
      constructor: {
          value: Database,
          enumberable: false
      }
  });
  
  Database.prototype.escape = function(value) {
      return exports.escape(value);
  };
  
  Database.prototype.detach = function(callback, force) {
  
      var self = this;
  
      if (!force && self.connection._pending.length > 0) {
          self.connection._detachAuto = true;
          self.connection._detachCallback = callback;
          return self;
      }
  
      self.connection.detach(function(err, obj) {
  
          self.connection.disconnect();
          self.emit('detach', false);
  
          if (callback)
              callback(err, obj);
  
      }, force);
  
      return self;
  };
  
  Database.prototype.transaction = function(isolation, callback) {
      return this.startTransaction(isolation, callback);
  };
  
  Database.prototype.startTransaction = function(isolation, callback) {
      this.connection.startTransaction(isolation, callback);
      return this;
  };
  
  Database.prototype.newStatement = function (query, callback) {
  
      this.startTransaction(function(err, transaction) {
  
          if (err) {
              callback(err);
              return;
          }
  
          transaction.newStatement(query, function(err, statement) {
  
              if (err) {
                  callback(err);
                  return;
              }
  
              transaction.commit(function(err) {
                  callback(err, statement);
              });
          });
      });
  
      return this;
  };
  
  Database.prototype.execute = function(query, params, callback, custom) {
  
      if (params instanceof Function) {
          callback = params;
          params = undefined;
      }
  
      var self = this;
  
      self.connection.startTransaction(function(err, transaction) {
  
          if (err) {
              doError(err, callback);
              return;
          }
  
          transaction.execute(query, params, function(err, result, meta, isSelect) {
              if (err) {
                  transaction.rollback(function() {
                      doError(err, callback);
                  });
                  return;
              }
  
              transaction.commit(function(err) {
                  if (callback)
                      callback(err, result, meta, isSelect);
              });
  
          }, custom);
      });
  
      return self;
  };
  
  Database.prototype.sequentially = function(query, params, on, callback, asArray) {
  
      if (params instanceof Function) {
          asArray = callback;
          callback = on;
          on = params;
          params = undefined;
      }
  
      if (on === undefined)
          throw new Error('Expected "on" delegate.');
  
      var self = this;
      self.execute(query, params, callback, { asObject: !asArray, asStream: true, on: on });
      return self;
  };
  
  Database.prototype.query = function(query, params, callback) {
  
      if (params instanceof Function) {
          callback = params;
          params = undefined;
      }
  
      var self = this;
      self.execute(query, params, callback, { asObject: true, asStream: callback === undefined || callback === null });
      return self;
  };
  
  exports.attach = function(options, callback) {
  
      var host = options.host || DEFAULT_HOST;
      var port = options.port || DEFAULT_PORT;
      var manager = options.manager || false;
      var cnx = this.connection = new Connection(host, port, function(err) {
  
          if (err) {
              doError(err, callback);
              return;
          }
  
          cnx.connect(options.database || options.filename, function(err) {
              if (err) {
                  doError(err, callback);
              } else {
                  if (manager)
                      cnx.svcattach(options, callback);
                  else
                      cnx.attach(options, callback);
              }
          });
  
      }, options);
  };
  
  exports.create = function(options, callback) {
      var host = options.host || DEFAULT_HOST;
      var port = options.port || DEFAULT_PORT;
      var cnx = this.connection = new Connection(host, port, function(err) {
          cnx.connect(options.database || options.filename, function(err) {
  
              if (err) {
                  self.db.emit('error', err);
                  doError(err, callback);
                  return;
              }
  
              cnx.createDatabase(options, callback);
          });
      }, options);
  };
  
  exports.attachOrCreate = function(options, callback) {
  
      var host = options.host || DEFAULT_HOST;
      var port = options.port || DEFAULT_PORT;
  
      var cnx = this.connection = new Connection(host, port, function(err) {
  
          var self = cnx;
  
          if (err) {
              callback({ error: err, message: "Connect error" });
              return;
          }
  
          cnx.connect(options.database || options.filename, function(err) {
  
              if (err) {
                  doError(err, callback);
                  return;
              }
  
              cnx.attach(options, function(err, ret) {
  
                  if (!err) {
                      if (self.db)
                          self.db.emit('connect', ret);
                      doCallback(ret, callback);
                      return;
                  }
  
                  cnx.createDatabase(options, callback);
              });
          });
  
      }, options);
  };
  
  /***************************************
   *
   *   Service Manager
   *
   ***************************************/
  
  function ServiceManager(connection) {
      this.connection = connection;
      connection.svc = this;
  }
  
  ServiceManager.prototype.__proto__ = Object.create(Events.EventEmitter.prototype, {
      constructor: {
          value: ServiceManager,
          enumberable: false
      }
  });
  
  ServiceManager.prototype._createOutputStream = function (optread, buffersize, callback) {
      var self = this;
      optread = optread || 'byline';
      var t = new stream.Readable({ objectMode: optread == 'byline'?true:false }); // chunk by line
      t.__proto__._read = function () {
          var selfread = this;
          var fct = optread == 'byline'?self.readline:self.readeof;
          fct.call(self, { buffersize: buffersize }, function (err, data) {
              if (err) {
                  selfread.push(err.message, DEFAULT_ENCODING);
                  return;
              }
              if (data.line && data.line.length)
                  selfread.push(data.line, DEFAULT_ENCODING);
              else
                  selfread.push(null);
          });
      }
      
      callback(null, t);
  }
  
  
  ServiceManager.prototype._infosmapping = {
      "50"/*isc_info_svc_svr_db_info*/ : "dbinfo",        
      "51"/*isc_info_svc_get_license*/ : "licenses",
      "52"/*isc_info_svc_get_license_mask*/ : "licenseoptions",
      "53"/*isc_info_svc_get_config*/ : "fbconfig",   
      "54"/*isc_info_svc_version*/ : "svcversion",
      "55"/*isc_info_svc_server_version*/ : "fbversion",
      "56"/*isc_info_svc_implementation*/ : "fbimplementation",
      "57"/*isc_info_svc_capabilities*/ : "fbcapatibilities",
      "58"/*isc_info_svc_user_dbpath*/ : "pathsecuritydb",
      "59"/*isc_info_svc_get_env*/ : "fbenv",
      "60"/*isc_info_svc_get_env_lock*/ : "fbenvlock",
      "61"/*isc_info_svc_get_env_msg*/ : "fbenvmsg",
      "62"/*isc_info_svc_line*/ : "",
      "63"/*isc_info_svc_to_eof*/ : "",
      "64"/*isc_info_svc_timeout*/ : "",
      "65"/*isc_info_svc_get_licensed_users*/ : "",
      "66"/*isc_info_svc_limbo_trans*/ : "",
      "67"/*isc_info_svc_running*/ : "",
      "68"/*isc_info_svc_get_users*/ : "fbusers",
      "78"/*isc_info_svc_stdin*/ : ""
  };
  
  ServiceManager.prototype._processcapabilities = function (blr, res) {
      var capArray = [
          "WAL_SUPPORT", 
          "MULTI_CLIENT_SUPPORT", 
          "REMOTE_HOP_SUPPORT", 
          "NO_SVR_STATS_SUPPORT", 
          "NO_DB_STATS_SUPPORT", 
          "LOCAL_ENGINE_SUPPORT", 
          "NO_FORCED_WRITE_SUPPORT", 
          "NO_SHUTDOWN_SUPPORT", 
          "NO_SERVER_SHUTDOWN_SUPPORT",
          "SERVER_CONFIG_SUPPORT",
          "QUOTED_FILENAME_SUPPORT"
      ];
      var dbcapa = res[this._infosmapping[57]] = [];
      var caps = blr.readInt32();
  	
  	for (var i = 0; i < capArray.length; ++i)
  		if (caps & (1 << i))
              dbcapa.push(capArray[i]);
  }
  
  ServiceManager.prototype._processdbinfo = function (blr, res) {
      var tinfo = blr.readByteCode();
      var dbinfo = res[this._infosmapping[50]] = {};
       
      dbinfo.database = [];
      for (; tinfo != isc_info_flag_end; tinfo = blr.readByteCode()) {
          switch (tinfo) {
              case isc_spb_dbname:
                  dbinfo.database.push(blr.readString());
                  break;
              case isc_spb_num_att:
                  dbinfo.nbattachment = blr.readInt32();
                  break;
              case isc_spb_num_db:
                  dbinfo.nbdatabase = blr.readInt32();
                  break;
          }
      }
  }
  
  ServiceManager.prototype._processquery = function (buffer, callback) {
      //console.log(buffer);
      var br = new BlrReader(buffer);
      var tinfo = br.readByteCode();
      var res = {};
      res.result = 0;
      for (; tinfo != isc_info_end; tinfo = br.readByteCode()) {
          switch (tinfo) {
              case isc_info_svc_server_version:
              case isc_info_svc_implementation:
              case isc_info_svc_user_dbpath:
              case isc_info_svc_get_env:
              case isc_info_svc_get_env_lock:
              case isc_info_svc_get_env_msg:
                  res[this._infosmapping[tinfo]] = br.readString();
                  break;
              case isc_info_svc_version:
                  res[this._infosmapping[tinfo]] = br.readInt32();
                  break;
              case isc_info_svc_svr_db_info:
                  this._processdbinfo(br, res);
                  break;
              case isc_info_svc_limbo_trans:
                  // not implemented
                  for (; tinfo != isc_info_flag_end; tinfo = br.readByteCode())
                  break;
              case isc_info_svc_get_users:
                  br.pos += 2
                  res[this._infosmapping[tinfo]] = [];
                  break;
              case isc_spb_sec_username:
                  var tuser = res[this._infosmapping[68]];
                  tuser.push({});
                  tuser[tuser.length - 1].username = br.readString();
                  break;
              case isc_spb_sec_firstname:
                  var tuser = res[this._infosmapping[68]];    
                  var user = tuser[tuser.length-1];
                  user.firstname = br.readString();
                  break;
              case isc_spb_sec_middlename:
                  var tuser = res[this._infosmapping[68]];
                  var user = tuser[tuser.length-1];
                  user.middlename = br.readString();
                  break;
              case isc_spb_sec_lastname:
                  var tuser = res[this._infosmapping[68]];
                  var user = tuser[tuser.length-1];
                  user.lastname = br.readString();
                  break;
              case isc_spb_sec_groupid:
                  var tuser = res[this._infosmapping[68]];
                  var user = tuser[tuser.length-1];
                  user.groupid = br.readInt32();
                  break;
              case isc_spb_sec_userid:
                  var tuser = res[this._infosmapping[68]];
                  var user = tuser[tuser.length-1];
                  user.userid = br.readInt32();
                  
                  break;
              case isc_spb_sec_admin:
                  var tuser = res[this._infosmapping[68]];
                  var user = tuser[tuser.length-1];
                  user.admin = br.readInt32();
                  break;
  
              case isc_info_svc_line:
                  res.line = br.readString();
                  break;
  
              case isc_info_svc_to_eof:
                  res.line = br.readString();
                  break;
  
              case isc_info_truncated:
                  res.result = 1; // too much data for the result buffer increase size of it (buffersize parameter))
                  break;
  
              case isc_info_data_not_ready:
                  res.result = 2;
                  break;
  
              case isc_info_svc_timeout:
                  res.result = 3;
                  break;            
  
              case isc_info_svc_stdin:
                  
                  break;
  
              case isc_info_svc_capabilities:
                  this._processcapabilities(br, res);
                  break; 
          }
      }
      callback(null, res);
  }
  
  ServiceManager.prototype.detach = function(callback, force) {
      var self = this;
      
      if (!force && self.connection._pending.length > 0) {
          self.connection._detachAuto = true;
          self.connection._detachCallback = callback;
          return self;
      }
      
      self.connection.svcdetach(function (err, obj) {
          
          self.connection.disconnect();
          self.emit('detach', false);
          
          if (callback)
              callback(err, obj);
  
      }, force);
      
      return self;
  }
  
  ServiceManager.prototype.backup = function (options, callback) {
      var dbpath = options.database || this.connection.options.filename || this.connection.options.database;
      var verbose = options.verbose || false;
      // format of bckfile {filename:'name', sizefile:''} sizefile is length of part in bytes
      var bckfiles = options.backupfiles || options.files || null;
      // for convenience
      if (bckfiles) bckfiles = bckfiles.constructor !== Array?[{ filename: bckfiles, sizefile: '0' }]:bckfiles;
      var factor = options.factor || 0; //If backing up to a physical tape device, this switch lets you specify the tape's blocking factor
      var ignorechecksums = options.ignorechecksums || false;
      var ignorelimbo = options.ignorelimbo || false;
      var metadataonly = options.metadataonly || false;
      var nogarbagecollect = options.nogarbasecollect || false;
      var olddescriptions = options.olddescriptions || false;
      var nontransportable = options.nontransportable || false;
      var convert = options.convert || false;
      var expand = options.expand || false;
      var notriggers = options.notriggers || false;
  
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      if (bckfiles == null || bckfiles.length == 0) {
          doError(new Error('No backup path specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_backup);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      for (var i = 0; i < bckfiles.length; i++) {
          blr.addString2(isc_spb_bkp_file, bckfiles[i].filename, DEFAULT_ENCODING);
          if (i != bckfiles.length - 1) // not the end, so we need to write the size of this part (gsplit)
              blr.addString2(isc_spb_bkp_length, bckfiles[i].sizefile, DEFAULT_ENCODING);
      }
      if (factor)
          blr.addByteInt32(isc_spb_bkp_factor, factor);
          
      var opts = 0;
      if (ignorechecksums) opts = opts | isc_spb_bkp_ignore_checksums;
      if (ignorelimbo) opts = opts | isc_spb_bkp_ignore_limbo;
      if (metadataonly) opts = opts | isc_spb_bkp_metadata_only;
      if (nogarbagecollect) opts = opts | isc_spb_bkp_no_garbage_collect;
      if (olddescriptions) opts = opts | isc_spb_bkp_old_descriptions;
      if (nontransportable) opts = opts | isc_spb_bkp_non_transportable;
      if (convert) opts = opts | isc_spb_bkp_convert;
      if (expand) opts = opts | isc_spb_bkp_expand;
      if (notriggers) opts = opts | isc_spb_bkp_no_triggers;
      if (opts)
          blr.addByteInt32(isc_spb_options, opts);
      if (verbose)
          blr.addByte(isc_spb_verbose);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback); 
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.nbackup = function (options, callback) {
      var dbpath = options.database || this.connection.options.filename || this.connection.options.database;
      var bckfile = options.backupfile || options.file || null;
      var level = options.level || 0; // nb day for incremental
      var notriggers = options.notriggers || false;
      var direct = options.direct || 'on'; // on or off direct write I/O
      
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      if (bckfile == null || bckfile.length == 0) {
          doError(new Error('No backup path specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_nba);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      blr.addString2(isc_spb_dbname, bckfile, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_nbk_level, level);
      blr.addString2(isc_spb_nbk_direct, direct, DEFAULT_ENCODING);
      var opts = 0;
      if (notriggers) opts = opts | isc_spb_nbk_no_triggers;
      blr.addByteInt32(isc_spb_options, opts);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.restore = function(options, callback) {
      var bckfiles = options.backupfiles || options.files || null; // format bckfiles ['file1', 'file2', 'file3']
      // for convenience
      if (bckfiles) bckfiles = bckfiles.constructor !== Array?[bckfiles]:bckfiles;
      var dbfile = options.database || this.connection.options.filename || this.connection.options.database;; 
      var verbose = options.verbose || false;
      var cachebuffers = options.cachebuffers || 2048; // gbak -buffers
      var pagesize = options.pagesize || 4096; // gbak -page_size
      var readonly = options.readonly || false; // gbak -mode
      var deactivateindexes = options.deactivateindexes || false;
      var	noshadow = options.noshadow || false;
      var	novalidity = options.novalidity || false;
      var	individualcommit = options.individualcommit || true; // otherwise no data
      var	replace = options.replace || false;
      var	create = options.create || true;
      var useallspace = options.useallspace || false;
      var metadataonly = options.metadataonly || false;
      var fixfssdata = options.fixfssdata || null;
      var fixfssmetadata = options.fixfssmetadata || null;
  
      if (bckfiles == null || bckfiles.length == 0) {
          doError(new Error('No backup file specified'), callback);
          return;
      }
      
      if (dbfile == null || dbfile.length == 0) {
          doError(new Error('No database path specified'), callback);
          return;
      }
  
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_restore);
      for (var i = 0; i < bckfiles.length; i++) {
          blr.addString2(isc_spb_bkp_file, bckfiles[i], DEFAULT_ENCODING);
      }
      blr.addString2(isc_spb_dbname, dbfile, DEFAULT_ENCODING);
      blr.addByte(isc_spb_res_buffers);
      blr.addInt32(cachebuffers);
      blr.addByte(isc_spb_res_page_size);
      blr.addInt32(pagesize);
      blr.addByte(isc_spb_res_access_mode);
      if (readonly)
          blr.addByte(isc_spb_prp_am_readonly);
      else
          blr.addByte(isc_spb_prp_am_readwrite);
      if (fixfssdata) blr.addString2(isc_spb_res_fix_fss_data, fixfssdata, DEFAULT_ENCODING);
      if (fixfssmetadata) blr.addString2(isc_spb_res_fix_fss_metadata, fixfssmetadata, DEFAULT_ENCODING);
      var opts = 0;
      if (deactivateindexes) opts = opts | isc_spb_res_deactivate_idx;
      if (noshadow) opts = opts | isc_spb_res_no_shadow;
      if (novalidity) opts = opts | isc_spb_res_no_validity;
      if (individualcommit) opts = opts | isc_spb_res_one_at_a_time;
      if (replace) opts = opts | isc_spb_res_replace;
      if (create) opts = opts | isc_spb_res_create;
      if (useallspace) opts = opts | isc_spb_res_use_all_space;
      if (metadataonly) opts = opts | isc_spb_res_fix_fss_metadata;
      if (opts) 
          blr.addByteInt32(isc_spb_options, opts);
      if (verbose)
          blr.addByte(isc_spb_verbose);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.nrestore = function (options, callback) {
      var bckfiles = options.backupfiles || options.files || null; // format bckfiles ['file1', 'file2', 'file3']
      // for convenience
      if (bckfiles) bckfiles = bckfiles.constructor !== Array?[bckfiles]:bckfiles;
      var dbpath = options.database || this.connection.options.filename || this.connection.options.database;;
      
      if (bckfiles == null || bckfiles.length == 0) {
          doError(new Error('No backup file specified'), callback);
          return;
      }
      
      if (dbpath == null || dbfile.length == 0) {
          doError(new Error('No database path specified'), callback);
          return;
      }
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_nrest);
      for (var i = 0; i < bckfiles.length; i++) {
          blr.addString2(isc_spb_nbk_file, bckfiles[i], DEFAULT_ENCODING);
      }
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  // only one at time don't use this function directly
  ServiceManager.prototype._fixpropertie = function (options, callback) {
      var dbpath = options.database || this.connection.options.filename || this.connection.options.database;
      var dialect = options.dialect || null;
      var sweep = options.sweepinterval || null;
      var pagebuffers = options.nbpagebuffers || null;
      var online = options.bringonline || false;
      var shutdown = options.shutdown || null; // 0 Forced, 1 deny transaction, 2 deny attachment
      var shutdowndelay = options.shutdowndelay || 0;
      var shutdownmode = options.shutdownmode || null; // 0 normal 1 multi 2 single 3 full
      var shadow = options.activateshadow || false;
      var forcewrite = options.forcewrite!=null?options.forcewrite:null;
      var reservespace = options.reservespace!=null?options.reservespace:null;
      var accessmode = options.accessmode!=null?options.accesmode:null; // 0 readonly 1 readwrite
  
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
  
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_properties);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      if (dialect) blr.addByteInt32(isc_spb_prp_set_sql_dialect, dialect);
      if (sweep) blr.addByteInt32(isc_spb_prp_sweep_interval, sweep);
      if (pagebuffers) blr.addByteInt32(isc_spb_prp_page_buffers, pagebuffers);
      if (shutdown) {
          switch (shutdown) { 
              case 0:
                  blr.addByteInt32(isc_spb_prp_shutdown_db, shutdowndelay);
                  break;
              case 1:
                  blr.addByteInt32(isc_spb_prp_deny_new_transactions, shutdowndelay);
                  break;
              case 2:
                  blr.addByteInt32(isc_spb_prp_deny_new_attachments, shutdowndelay);                
                  break;
          }
          if (shutdownmode != null) {
              switch (shutdownmode) {
                  case 0:
                      blr.addByteInt32(isc_spb_prp_shutdown_mode, isc_spb_prp_sm_normal);
                      break;
                  case 1:
                      blr.addByteInt32(isc_spb_prp_shutdown_mode, isc_spb_prp_sm_multi);
                      break;
                  case 2:
                      blr.addByteInt32(isc_spb_prp_shutdown_mode, isc_spb_prp_sm_single);
                      break;
                  case 3:
                      blr.addByteInt32(isc_spb_prp_shutdown_mode, isc_spb_prp_sm_full);
                      break;
              }
          }
      }
      if (forcewrite) blr.addBytes([isc_spb_prp_write_mode, isc_spb_prp_wm_sync]);
      if (forcewrite != null && !forcewrite) blr.addBytes([isc_spb_prp_write_mode, isc_spb_prp_wm_async]);
      if (accessmode) blr.addBytes([isc_spb_prp_access_mode, isc_spb_prp_am_readwrite]);
      if (accessmode != null && !accessmode) blr.addBytes([isc_spb_prp_access_mode, isc_spb_prp_am_readonly]);
      if (reservespace) blr.addBytes([isc_spb_prp_reserve_space, sc_spb_prp_res]);
      if (reservespace != null && !reservespace) blr.addBytes([isc_spb_prp_reserve_space, isc_spb_prp_res_use_full]);
      var opts = 0;
      if (shadow) opts = opts | sc_spb_prp_activate;
      if (online) opts = opts | isc_spb_prp_db_online;
      if (opts)
          blr.addByteInt32(isc_spb_options, opts);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });    
  }
  
  ServiceManager.prototype.setDialect = function (db, dialect, callback) {
      this._fixpropertie({ database: db, dialect: dialect }, callaback);
  }
  
  ServiceManager.prototype.setSweepinterval = function (db, sweepinterval, callback) {
      this._fixpropertie({ database: db, sweepinterval: sweepinterval }, callback);
  }
  
  ServiceManager.prototype.setCachebuffer = function (db, nbpages, callback) {
      this._fixpropertie({ database: db, nbpagebuffers: nbpages }, callback);
  }
  
  ServiceManager.prototype.BringOnline = function (db, callback) {
      this._fixpropertie({ database: db, bringonline: true }, callback);
  }
  
  ServiceManager.prototype.Shutdown = function (db, kind, delay, mode, callback) {
      // mode parameter is for server version >= 2.0
      this._fixpropertie({ database: db, shutdown: kind, shutdowndelay: delay, shutdownmode: mode }, callback);
  }
  
  ServiceManager.prototype.setShadow = function (db, val, callback) {
      this._fixpropertie({ database: db, activateshadow : val }, callback);
  }
  
  ServiceManager.prototype.setForcewrite = function (db, val, callback) {
      this._fixpropertie({ database: db, forcewrite : val }, callback);
  }
  
  ServiceManager.prototype.setReservespace = function (db, val, callback) {
      this._fixpropertie({ database: db, reservespace : val }, callback);
  }
  
  ServiceManager.prototype.setReadonlyMode = function (db, callback) {
      this._fixpropertie({ database: db, accessmode : 0 }, callback);
  }
  
  ServiceManager.prototype.setReadwriteMode = function (db, callback) {
      this._fixpropertie({ database: db, accessmode : 1 }, callback);
  }
  
  ServiceManager.prototype.validate = function (options, callback) {
      var dbpath = options.database || this.connection.options.filename || this.connection.options.database;
      var checkdb = options.checkdb || false;
      var ignorechecksums = options.ignorechecksums || false;
      var killshadows = options.killshadows || false;
      var mend = options.mend || false;
      var validate = options.validate || false;
      var full = options.full || false;
      var sweep = options.sweep || false;
      var listlimbo = options.listlimbo || false;
      var icu = options.icu || false;
  
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_repair);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      var opts = 0;
      if (checkdb) opts = opts | isc_spb_rpr_check_db;
      if (ignorechecksums) opts = opts | isc_spb_rpr_ignore_checksum;
      if (killshadows) opts = opts | isc_spb_rpr_kill_shadows;
      if (mend) opts = opts | isc_spb_rpr_mend_db;
      if (validate) opts = opts | isc_spb_rpr_validate_db;
      if (full) opts = opts | isc_spb_rpr_full;
      if (sweep) opts = opts | isc_spb_rpr_sweep_db;
      if (listlimbo) opts = opts | isc_spb_rpr_list_limbo_trans;
      if (icu) opts = opts | isc_spb_rpr_icu;
      blr.addByteInt32(isc_spb_options, opts);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      }); 
  }
  
  ServiceManager.prototype.commit = function(db, transactid, callback) {
      var dbpath = db || this.connection.options.filename || this.connection.options.database;
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_repair);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_rpr_commit_trans, transactid);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      }); 
  }
  
  ServiceManager.prototype.rollback = function (db, transactid, callback) {
      var dbpath = db || this.connection.options.filename || this.connection.options.database;
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_repair);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_rpr_rollback_trans, transactid);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      }); 
  }
  
  ServiceManager.prototype.recover = function (db, transactid, callback) {
      var dbpath = db || this.connection.options.filename || this.connection.options.database;
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_repair);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_rpr_recover_two_phase, transactid);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      }); 
  }
  
  ServiceManager.prototype.getStats = function (options, callback) {
      var dbpath = options.database || this.connection.options.filename || this.connection.options.database;
      var record = options.record || false;
      var nocreation = options.nocreation || false;
      var tables = options.tables || false;
      var pages = options.pages || false;
      var header = options.header || false;
      var indexes = options.indexes || false;
      var tablesystem = options.tablesystem || false;
      var encryption = options.encryption || false;
      var objects = options.objects || null; // space-separated list of object index,table,systemtable
      if (dbpath == null || dbpath.length == 0) {
          doError(new Error('No database specified'), callback);
          return;
      }
      
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_db_stats);
      blr.addString2(isc_spb_dbname, dbpath, DEFAULT_ENCODING);
      var opts = 0;
      if (record) opts = opts | isc_spb_sts_record_versions;
      if (nocreation) opts = opts | isc_spb_sts_nocreation;
      if (tables) opts = opts | isc_spb_sts_table;
      if (pages) opts = opts | isc_spb_sts_data_pages;
      if (header) opts = opts | isc_spb_sts_hdr_pages;
      if (indexes) opts = opts | isc_spb_sts_idx_pages;
      if (tablesystem) opts = opts | isc_spb_sts_sys_relations;
      if (encryption) opts = opts | isc_spb_sts_encryption;
      if (opts)
          blr.addByteInt32(isc_spb_options, opts);
      if (objects) blr.addString2(isc_spb_command_line, objects, DEFAULT_ENCODING);
      var self = this;
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
      
  }
  
  ServiceManager.prototype.getLog = function (options, callback) {
      var self = this;
      var blr = this.connection._blr;
      var optread = options.optread || 'byline';
      blr.pos = 0;
      blr.addByte(isc_action_svc_get_fb_log);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.getUsers = function (username, callback) {
      var self = this;
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_display_user);
      if (username) blr.addString2(isc_spb_sec_username, username, DEFAULT_ENCODING);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self.readusers({}, callback);
      });
  }
  
  ServiceManager.prototype.addUser = function (username, password, options, callback) {
      var rolename = options.rolename || null;
      var groupname = options.groupname || null;
      var firsname = options.firstname || null;
      var middlename = options.middlename || null;
      var lastname = options.lastname || null;
      var userid = options.userid || null;
      var groupid = options.groupid || null;
      var admin = options.admin || null;
  
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_add_user);
      blr.addString2(isc_spb_sec_username, username, DEFAULT_ENCODING);
      blr.addString2(isc_spb_sec_password, password, DEFAULT_ENCODING);
      if (rolename) blr.addString2(isc_dpb_sql_role_name, rolename, DEFAULT_ENCODING);
      if (groupname) blr.addString2(isc_spb_sec_groupname, groupname, DEFAULT_ENCODING);
      if (firsname) blr.addString2(isc_spb_sec_firstname, firsname, DEFAULT_ENCODING);
      if (middlename) blr.addString2(isc_spb_sec_middlename, middlename, DEFAULT_ENCODING);
      if (lastname) blr.addString2(isc_spb_sec_lastname, lastname, DEFAULT_ENCODING);
      if (userid != null) blr.addByteInt32(isc_spb_sec_userid, userid);
      if (groupid != null) blr.addByteInt32(isc_spb_sec_groupid, groupid);
      if (admin != null) blr.addByteInt32(isc_spb_sec_admin, admin);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.editUser = function (username, options, callback) {
      var rolename = options.rolename || null;
      var groupname = options.groupname || null;
      var firsname = options.firstname || null;
      var middlename = options.middlename || null;
      var lastname = options.lastname || null;
      var userid = options.userid || null;
      var groupid = options.groupid || null;
      var admin = options.admin || null;
  	var password = options.password || null;
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_modify_user);
      blr.addString2(isc_spb_sec_username, username, DEFAULT_ENCODING);
      if (password) blr.addString2(isc_spb_sec_password, password, DEFAULT_ENCODING);
      if (rolename) blr.addString2(isc_dpb_sql_role_name, rolename, DEFAULT_ENCODING);
      if (groupname) blr.addString2(isc_spb_sec_groupname, groupname, DEFAULT_ENCODING);
      if (firsname) blr.addString2(isc_spb_sec_firstname, firsname, DEFAULT_ENCODING);
      if (middlename) blr.addString2(isc_spb_sec_middlename, middlename, DEFAULT_ENCODING);
      if (lastname) blr.addString2(isc_spb_sec_lastname, lastname, DEFAULT_ENCODING);
      if (userid != null) blr.addByteInt32(isc_spb_sec_userid, userid);
      if (groupid != null) blr.addByteInt32(isc_spb_sec_groupid, groupid);
      if (admin != null) blr.addByteInt32(isc_spb_sec_admin, admin);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.removeUser = function (username, rolename, callback) {
      var blr = this.connection._blr;
      blr.pos = 0;
      blr.addByte(isc_action_svc_delete_user);
      blr.addString2(isc_spb_sec_username, username, DEFAULT_ENCODING);
      if (rolename) blr.addString2(isc_dpb_sql_role_name, rolename, DEFAULT_ENCODING);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.getFbserverInfos = function (infos, options, callback) {
      var buffersize = options.buffersize || 2048;
      var timeout = options.timeout || 1;
      var opts = {
          "dbinfo" : isc_info_svc_svr_db_info,
          "fbconfig" : isc_info_svc_get_config,
          "svcversion" : isc_info_svc_version,
          "fbversion" : isc_info_svc_server_version,
          "fbimplementation" : isc_info_svc_implementation,
          "fbcapatibilities" : isc_info_svc_capabilities,
          "pathsecuritydb" : isc_info_svc_user_dbpath,
          "fbenv" : isc_info_svc_get_env,
          "fbenvlock" : isc_info_svc_get_env_lock,
          "fbenvmsg" : isc_info_svc_get_env_msg
      };
      // if infos is empty all options are asked to the service
  
      var tops = [];
      for (popts in opts)
          if (infos[popts] || infos.length == 0)
              tops.push(opts[popts]);
      
      
      var self = this;
      this.connection.svcquery(tops, buffersize, timeout, function (err, data) {
          if (err || !data.buffer) {
              doError(new Error(err||'Bad query return'), callback);
              return;
          }
          self._processquery(data.buffer, callback);
      });
  }
  
  ServiceManager.prototype.startTrace = function (options, callback) {
      var self = this;
      var blr = this.connection._blr;
      var configfile = options.configfile || '';
      var tracename = options.tracename || '';
      
      if (configfile.length == 0) {
          doError(new Error('No config filename specified'), callback);
          return;
      }
      if (tracename.length == 0) {
          doError(new Error('No tracename specified'), callback);
          return;
      }
  
      blr.pos = 0;
      blr.addByte(isc_action_svc_trace_start);
      blr.addString2(isc_spb_trc_cfg, configfile, DEFAULT_ENCODING);
      blr.addString2(isc_spb_trc_name, tracename, DEFAULT_ENCODING);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.suspendTrace = function (options, callback) {
      var self = this;
      var blr = this.connection._blr;
      var traceid = options.traceid || null;
      var tracename = options.tracename || '';
      
      if (traceid == null) {
          doError(new Error('No traceid specified'), callback);
          return;
      }
      if (tracename.length == 0) {
          doError(new Error('No tracename specified'), callback);
          return;
      }
      blr.pos = 0;
      blr.addByte(isc_action_svc_trace_suspend);
      blr.addString2(isc_spb_trc_name, tracename, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_trc_id, traceid);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.resumeTrace = function (options, callback) {
      var self = this;
      var blr = this.connection._blr;
      var traceid = options.traceid || null;
      var tracename = options.tracename || '';
      
      if (traceid == null) {
          doError(new Error('No traceid specified'), callback);
          return;
      }   
      if (tracename.length == 0) {
          doError(new Error('No tracename specified'), callback);
          return;
      }
      blr.pos = 0;
      blr.addByte(isc_action_svc_trace_resume);
      blr.addString2(isc_spb_trc_name, tracename, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_trc_id, traceid);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error('Invalid RESUMETRACE Action'), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.stopTrace = function (options, callback) {
      var self = this;
      var blr = this.connection._blr;
      var traceid = options.traceid || null;
      var tracename = options.tracename || '';
      
      if (traceid == null) {
          doError(new Error('No traceid specified'), callback);
          return;
      }
      
      if (tracename.length == 0) {
          doError(new Error('No tracename specified'), callback);
          return;
      }
      blr.pos = 0;
      blr.addByte(isc_action_svc_trace_stop);
      blr.addString2(isc_spb_trc_name, tracename, DEFAULT_ENCODING);
      blr.addByteInt32(isc_spb_trc_id, traceid);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.getTraceList = function (options, callback) {
      var self = this;
      var blr = this.connection._blr;
      var optread = options.optread || 'byline';
      blr.pos = 0;
      blr.addByte(isc_action_svc_trace_list);
      this.connection.svcstart(blr, function (err, data) {
          if (err) {
              doError(new Error(err), callback);
              return;
          }
          self._createOutputStream(options.optread, options.buffersize, callback);
      });
  }
  
  ServiceManager.prototype.readline = function (options, callback) {
      var buffersize = options.buffersize || 2048;
      var timeout = options.timeout || 60;
      var self = this;
      this.connection.svcquery([isc_info_svc_line], buffersize, timeout, function (err, data) {
          if (err || !data.buffer) {
              doError(new Error(err||'Bad query return'), callback);
              return;
          }
          self._processquery(data.buffer, callback);
      });
  }
  
  ServiceManager.prototype.readeof = function (options, callback) {
      var buffersize = options.buffersize || (8 * 1024);
      var timeout = options.timeout || 60;
      var self = this;
      this.connection.svcquery([isc_info_svc_to_eof], buffersize, timeout, function (err, data) {
          if (err || !data.buffer) {
              doError(new Error(err||'Bad query return'), callback);
              return;
          }
          self._processquery(data.buffer, callback);
      });
  }
  
  ServiceManager.prototype.hasRunningAction = function (options, callback) {
      var buffersize = options.buffersize || 2048;
      var timeout = options.timeout || 60;
      var self = this;
      this.connection.svcquery([isc_info_svc_running], buffersize, timeout, function (err, data) {
          if (err || !data.buffer) {
              doError(new Error(err||'Bad query return'), callback);
              return;
          }
          self._processquery(data.buffer, callback);
      });
  }
  
  ServiceManager.prototype.readusers = function (options, callback) {
      var buffersize = options.buffersize || 2048;
      var timeout = options.timeout || 60;
      var self = this;
      this.connection.svcquery([isc_info_svc_get_users], buffersize, timeout, function (err, data) {
          if (err || !data.buffer) {
              doError(new Error(err||'Bad query return'), callback);
              return;
          }
          self._processquery(data.buffer, callback);
      });
  }
  
  ServiceManager.prototype.readlimbo = function (options, callback) {
      var buffersize = options.buffersize || 2048;
      var timeout = options.timeout || 60;
      var self = this;
      this.connection.svcquery([isc_info_svc_limbo_trans], buffersize, timeout, function (err, data) {
          if (err || !data.buffer) {
              doError(new Error(err||'Bad query return'), callback);
              return;
          }
          self._processquery(data.buffer, callback);
      });
  }
  
  // Pooling
  exports.pool = function(max, options, callback) {
      var pool = new Pool(max, options);
      options.isPool = true;
      return pool;
  };
  
  /***************************************
   *
   *   Simple Pooling
   *
   ***************************************/
  
  function Pool(max, options) {
      this.db = 0;
      this.max = max || 4;
      this.pending = [];
      this.options = options;
  }
  
  Pool.prototype.get = function(callback) {
      var self = this;
      self.pending.push(callback);
      self.check();
      return self;
  };
  
  Pool.prototype.check = function() {
  
      var self = this;
      if (self.db >= self.max)
          return self;
  
      var cb = self.pending.shift();
      if (!cb)
          return self;
  
      self.db++;
      exports.attach(self.options, function(err, db) {
          if (!err) {
              db.on('detach', function() { self.db--; self.check(); });
          } else
              self.db--;
          cb(err, db);
      });
  
      setImmediate(function() {
          self.check();
      });
  
      return self;
  };
  
  Pool.prototype.destroy = function() {};
  
  /***************************************
   *
   *   Connection
   *
   ***************************************/
  
  var Connection = exports.Connection = function (host, port, callback, options, db, svc) {
      var self = this;
      this.db = db;
      this.svc = svc
      this._msg = new XdrWriter(32);
      this._blr = new BlrWriter(32);
      this._queue = [];
      this._detachTimeout;
      this._detachCallback;
      this._detachAuto;
      this._socket = net.createConnection(port, host);
      this._pending = [];
      this._isClosed = false;
      this._isDetach = false;
      this._isUsed = false;
      this.options = options;
      this._bind_events(host, port, callback);
      this.error;
  };
  
  exports.Connection.prototype._bind_events = function(host, port, callback) {
  
      var self = this;
  
      self._socket.on('close', function() {
  
          self._isClosed = true;
  
          if (self._isDetach)
              return;
  
          if (!self.db) {
              if (callback)
                  callback(self.error);
              return;
          }
  
          setImmediate(function() {
  
              self._socket = null;
              self._msg = null;
              self._blr = null;
  
              var ctx = new Connection(host, port, function(err) {
                  ctx.connect(self.options.filename, function(err) {
  
                      if (err) {
                          self.emit('error', err);
                          return;
                      }
  
                      ctx.attach(self.options, function(err) {
  
                          if (err) {
                              self.emit('error', err);
                              return;
                          }
  
                          ctx._queue = ctx._queue.concat(self._queue);
                          ctx._pending = ctx._pending.concat(self._pending);
                          self.db.emit('reconnect');
  
                      }, self.db);
                  });
  
              }, self.options, self.db);
          });
  
      });
  
      self._socket.on('error', function(e) {
  
          self.error = e;
  
          if (self.db)
              self.db.emit('error', e)
  
          if (callback)
              callback(e);
  
      });
  
      self._socket.on('connect', function() {
          self._isClosed = false;
          self._isOpened = true;
          if (callback)
              callback();
      });
  
      self._socket.on('data', function(data) {
          var obj, cb, pos, xdr, buf;
  
          if (!self._xdr) {
              xdr = new XdrReader(data);
          } else {
              xdr = self._xdr;
              delete(self._xdr);
              buf = new Buffer(data.length + xdr.buffer.length);
              xdr.buffer.copy(buf);
              data.copy(buf, xdr.buffer.length);
              xdr.buffer = buf;
          }
  
          while (xdr.pos < xdr.buffer.length) {
  
              pos = xdr.pos;
  
              try {
                  cb = self._queue[0];
                  obj = decodeResponse(xdr, cb, self.db);
              } catch(err) {
                  buf = new Buffer(xdr.buffer.length - pos);
                  xdr.buffer.copy(buf, 0, pos);
                  xdr.buffer = buf;
                  xdr.pos = 0;
                  self._xdr = xdr;
                  return;
              }
  
              self._queue.shift();
              self._pending.shift();
  
              if (obj && obj.status) {
  
                  messages.lookupMessages(obj.status, function(message) {
                      obj.message = message;
                      doCallback(obj, cb);
                  });
  
              } else
                  doCallback(obj, cb);
          }
  
          if (!self._detachAuto || self._pending.length !== 0)
              return;
  
          clearTimeout(self._detachTimeout);
          self._detachTimeout = setTimeout(function() {
              self.db.detach(self._detachCallback);
              self._detachAuto = false;
          }, 100);
  
      });
  }
  
  exports.Connection.prototype.disconnect = function() {
      this._socket.end();
  };
  
  function decodeResponse(data, callback, db){
  
      do {
          var r = data.readInt();
      } while (r === op_dummy);
  
      var item, op;
  
      switch (r) {
          case op_response:
  
              var response;
  
              if (callback)
                  response = callback.response || {};
              else
                  response = {};
  
              response.handle = data.readInt();
              var oid = data.readQuad();
              if (oid.low || oid.high)
                  response.oid = oid
  
              var buf = data.readArray();
              if (buf)
                  response.buffer = buf;
  
              var num;
              while (true) {
                  op = data.readInt();
                  switch (op){
                      case isc_arg_end:
                          return response;
                      case isc_arg_gds:
                          num = data.readInt();
                          if (!num)
                              break;
                          item = { gdscode: num };
                          if (response.status)
                              response.status.push(item);
                          else
                              response.status = [item];
                          break;
                      case isc_arg_string:
                      case isc_arg_interpreted:
                      case isc_arg_sql_state:
  
                          if (item.params) {
                              var str = data.readString(DEFAULT_ENCODING);
                              item.params.push(str);
                          } else
                              item.params = [data.readString(DEFAULT_ENCODING)];
  
                          break;
  
                      case isc_arg_number:
                          num = data.readInt();
  
                          if (item.params)
                              item.params.push(num);
                          else
                              item.params = [num];
  
                          if (item.gdscode === isc_sqlerr)
                              response.sqlcode = num;
  
                          break;
  
                      default:
                          throw new Error('Unexpected: ' + op);
                  }
              }
              break;
  
          case op_fetch_response:
  
              var status = data.readInt();
              var count = data.readInt();
              var statement = callback.statement;
              var output = statement.output;
              var custom = statement.custom || {};
              var cols = null;
              var rows = custom.asStream ? null : [];
              var index = 0;
  
              if (custom.asObject) {
                  cols = [];
                  for (var i = 0, length = output.length; i < length; i++)
                      cols.push(output[i].alias.toLowerCase());
              }
  
              while (count && (status !== 100)) {
  
                  var row = custom.asObject ? {} : new Array(output.length);
  
                  for (var i = 0, length = output.length; i < length; i++) {
  
                      item = output[i];
                      var value = item.decode(data);
  
                      if (custom.asObject) {
                          if (item.type === SQL_BLOB)
                              value = fetch_blob_async(statement, value, cols[i]);
                          row[cols[i]] = value;
                      }
                      else {
                          if (item.type === SQL_BLOB)
                              value = fetch_blob_async(statement, value, i);
                          row[i] = value;
                      }
                  }
  
                  statement.connection.db.emit('row', row, index, custom.asObject);
  
                  op = data.readInt(); // ??
                  status = data.readInt();
                  count = data.readInt();
  
                  if (!custom.asStream)
                      rows.push(row);
  
                  if (custom.on)
                      custom.on(row, index);
  
                  index++;
              }
  
              statement.connection.db.emit('result', rows);
              return { data: rows, fetched: Boolean(status === 100) };
  
          case op_accept:
              if (data.readInt() !== PROTOCOL_VERSION10 || data.readInt() !== ARCHITECTURE_GENERIC || data.readInt() !== ptype_batch_send)
                  throw new Error('Invalid connect result');
              return {};
  
          default:
              throw new Error('Unexpected:' + r);
      }
  }
  
  Connection.prototype._queueEvent = function(callback){
      var self = this;
  
      if (self._isClosed) {
          if (callback)
              callback(new Error('Connection is closed.'));
          return;
      }
  
      self._queue.push(callback);
      self._socket.write(self._msg.getData());
  };
  
  Connection.prototype.connect = function (database, callback) {
  
      var msg = this._msg;
      var blr = this._blr;
  
      msg.pos = 0;
      blr.pos = 0;
  
      msg.addInt(op_connect);
      msg.addInt(op_attach);
      msg.addInt(CONNECT_VERSION2);
      msg.addInt(ARCHITECTURE_GENERIC);
      msg.addString(database || '', DEFAULT_ENCODING);
      msg.addInt(1);  // Protocol version understood count.
  
      blr.addString(1, process.env['USER'] || process.env['USERNAME'] || 'Unknown', DEFAULT_ENCODING);
      var hostname = os.hostname();
      blr.addString(4, hostname, DEFAULT_ENCODING);
      blr.addBytes([6, 0]);
      msg.addBlr(this._blr);
  
      msg.addInt(PROTOCOL_VERSION10);
      msg.addInt(ARCHITECTURE_GENERIC);
      msg.addInt(2);  // Min type
      msg.addInt(3);  // Max type
      msg.addInt(2);  // Preference weight
  
      this._queueEvent(callback);
  };
  
  Connection.prototype.attach = function (options, callback, db) {
  
      var database = options.database || options.filename;
      var user = options.user || DEFAULT_USER;
      var password = options.password || DEFAULT_PASSWORD;
      var role = options.role;
      var self = this;
      var msg = this._msg;
      var blr = this._blr;
      msg.pos = 0;
      blr.pos = 0;
  
      blr.addByte(1);
      blr.addString(isc_dpb_lc_ctype, 'UTF8', DEFAULT_ENCODING);
      blr.addString(isc_dpb_user_name, user, DEFAULT_ENCODING);
      blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
  
      if (role)
          blr.addString(isc_dpb_sql_role_name, role, DEFAULT_ENCODING);
  
      msg.addInt(op_attach);
      msg.addInt(0);  // Database Object ID
      msg.addString(database, DEFAULT_ENCODING);
      msg.addBlr(this._blr);
  
      var self = this;
  
      function cb(err, ret) {
  
          if (err) {
              doError(err, callback);
              return;
          }
  
          self.dbhandle = ret.handle;
          if (callback)
              callback(undefined, ret);
      }
  
      // For reconnect
      if (db) {
          db.connection = this;
          cb.response = db;
      } else {
          cb.response = new Database(this);
          cb.response.removeAllListeners('error');
          cb.response.on('error', noop);
      }
  
      this._queueEvent(cb);
  };
  
  Connection.prototype.detach = function (callback) {
  
      var self = this;
  
      if (self._isClosed)
          return;
  
      self._isUsed = false;
      self._isDetach = true;
  
      var msg = self._msg;
  
      msg.pos = 0;
      msg.addInt(op_detach);
      msg.addInt(0); // Database Object ID
  
      self._queueEvent(function(err, ret) {
          delete(self.dbhandle);
          if (callback)
              callback(err, ret);
      });
  };
  
  Connection.prototype.createDatabase = function (options, callback) {
  
      var database = options.database || options.filename;
      var user = options.user || DEFAULT_USER;
      var password = options.password || DEFAULT_PASSWORD;
      var pageSize = options.pageSize || DEFAULT_PAGE_SIZE;
      var role = options.role;
      var blr = this._blr;
  
      blr.pos = 0;
      blr.addByte(1);
      blr.addString(isc_dpb_set_db_charset, 'UTF8', DEFAULT_ENCODING);
      blr.addString(isc_dpb_lc_ctype, 'UTF8', DEFAULT_ENCODING);
      blr.addString(isc_dpb_user_name, user, DEFAULT_ENCODING);
      blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
  
      if (role)
          blr.addString(isc_dpb_sql_role_name, role, DEFAULT_ENCODING);
  
      blr.addNumeric(isc_dpb_sql_dialect, 3);
      blr.addNumeric(isc_dpb_force_write, 1);
      blr.addNumeric(isc_dpb_overwrite, 1);
      blr.addNumeric(isc_dpb_page_size, pageSize);
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_create);  // op_create
      msg.addInt(0);          // Database Object ID
      msg.addString(database, DEFAULT_ENCODING);
      msg.addBlr(blr);
  
      var self = this;
  
      function cb(err, ret) {
  
          if (ret)
              self.dbhandle = ret.handle;
  
          setImmediate(function() {
              if (self.db)
                  self.db.emit('attach', ret);
          });
  
          if (callback)
              callback(err, ret);
      }
  
      cb.response = new Database(this);
      this._queueEvent(cb);
  };
  
  Connection.prototype.throwClosed = function(callback) {
      var err = new Error('Connection is closed.');
      this.db.emit('error', err);
      if (callback)
          callback(err);
      return this;
  };
  
  Connection.prototype.startTransaction = function(isolation, callback) {
  
      if (typeof(isolation) === 'function') {
          var tmp = isolation;
          isolation = callback;
          callback = tmp;
      }
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('startTransaction');
  
      var blr = this._blr;
      var msg = this._msg;
  
      blr.pos = 0;
      msg.pos = 0;
  
      if (isolation instanceof Function) {
          callback = isolation;
          isolation = null;
      }
  
      blr.addBytes(isolation || ISOLATION_REPEATABLE_READ);
      msg.addInt(op_transaction);
      msg.addInt(this.dbhandle);
      msg.addBlr(blr);
      callback.response = new Transaction(this);
  
      this.db.emit('transaction', isolation);
      this._queueEvent(callback);
  };
  
  Connection.prototype.commit = function (transaction, callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('commit');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_commit);
      msg.addInt(transaction.handle);
      this.db.emit('commit');
      this._queueEvent(callback);
  };
  
  Connection.prototype.rollback = function (transaction, callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('rollback');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_rollback);
      msg.addInt(transaction.handle);
      this.db.emit('rollback');
      this._queueEvent(callback);
  };
  
  Connection.prototype.commitRetaining = function (transaction, callback) {
  
      if (this._isClosed)
          throw new Error('Connection is closed.');
  
      // for auto detach
      this._pending.push('commitRetaining');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_commit_retaining);
      msg.addInt(transaction.handle);
      this._queueEvent(callback);
  };
  
  Connection.prototype.rollbackRetaining = function (transaction, callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('rollbackRetaining');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_rollback_retaining);
      msg.addInt(transaction.handle);
      this._queueEvent(callback);
  };
  
  Connection.prototype.allocateStatement = function (callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('allocateStatement');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_allocate_statement);
      msg.addInt(this.dbhandle);
      callback.response = new Statement(this);
      this._queueEvent(callback);
  };
  
  Connection.prototype.dropStatement = function (statement, callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('dropStatement');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_free_statement);
      msg.addInt(statement.handle);
      msg.addInt(DSQL_drop);
      this._queueEvent(callback);
  };
  
  Connection.prototype.closeStatement = function (statement, callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('closeStatement');
  
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_free_statement);
      msg.addInt(statement.handle);
      msg.addInt(DSQL_close);
      this._queueEvent(callback);
  };
  
  function describe(ret, statement){
  
      var br = new BlrReader(ret.buffer);
      var parameters = null;
      var type, param;
  
      while (br.pos < br.buffer.length) {
          switch (br.readByteCode()) {
              case isc_info_sql_stmt_type:
                  statement.type = br.readInt();
                  break;
              case isc_info_sql_get_plan:
                  statement.plan = br.readString(DEFAULT_ENCODING);
                  break;
              case isc_info_sql_select:
                  statement.output = parameters = [];
                  break;
              case isc_info_sql_bind:
                  statement.input = parameters = [];
                  break;
              case isc_info_sql_num_variables:
                  br.readInt(); // eat int
                  break;
              case isc_info_sql_describe_vars:
                  if (!parameters) {return}
                  br.readInt(); // eat int ?
                  var finishDescribe = false;
                  param = null;
                  while (!finishDescribe){
                      switch (br.readByteCode()) {
                          case isc_info_sql_describe_end:
                              break;
                          case isc_info_sql_sqlda_seq:
                              var num = br.readInt();
                              break;
                          case isc_info_sql_type:
                              type = br.readInt();
                              switch (type&~1) {
                                  case SQL_VARYING:   param = new SQLVarString(); break;
                                  case SQL_NULL:      param = new SQLVarNull(); break;
                                  case SQL_TEXT:      param = new SQLVarText(); break;
                                  case SQL_DOUBLE:    param = new SQLVarDouble(); break;
                                  case SQL_FLOAT:
                                  case SQL_D_FLOAT:   param = new SQLVarFloat(); break;
                                  case SQL_TYPE_DATE: param = new SQLVarDate(); break;
                                  case SQL_TYPE_TIME: param = new SQLVarTime(); break;
                                  case SQL_TIMESTAMP: param = new SQLVarTimeStamp(); break;
                                  case SQL_BLOB:      param = new SQLVarBlob(); break;
                                  case SQL_ARRAY:     param = new SQLVarArray(); break;
                                  case SQL_QUAD:      param = new SQLVarQuad(); break;
                                  case SQL_LONG:      param = new SQLVarInt(); break;
                                  case SQL_SHORT:     param = new SQLVarShort(); break;
                                  case SQL_INT64:     param = new SQLVarInt64(); break;
                                  case SQL_BOOLEAN:   param = new SQLVarBoolean(); break;
                                  default:
                                      throw new Error('Unexpected');
                              }
                              parameters[num-1] = param;
                              param.type = type;
                              param.nullable = Boolean(param.type & 1);
                              param.type &= ~1;
                              break;
                          case isc_info_sql_sub_type:
                              param.subType = br.readInt();
                              break;
                          case isc_info_sql_scale:
                              param.scale = br.readInt();
                              break;
                          case isc_info_sql_length:
                              param.length = br.readInt();
                              break;
                          case isc_info_sql_null_ind:
                              param.nullable = Boolean(br.readInt());
                              break;
                          case isc_info_sql_field:
                              param.field = br.readString(DEFAULT_ENCODING);
                              break;
                          case isc_info_sql_relation:
                              param.relation = br.readString(DEFAULT_ENCODING);
                              break;
                          case isc_info_sql_owner:
                              param.owner = br.readString(DEFAULT_ENCODING);
                              break;
                          case isc_info_sql_alias:
                              param.alias = br.readString(DEFAULT_ENCODING);
                              break;
                          case isc_info_sql_relation_alias:
                              param.relationAlias = br.readString(DEFAULT_ENCODING);
                              break;
                          case isc_info_truncated:
                              throw new Error('Truncated');
                          default:
                              finishDescribe = true;
                              br.pos--;
                      }
                  }
          }
      }
  }
  
  Connection.prototype.prepareStatement = function (transaction, statement, query, plan, callback) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      var msg = this._msg;
      var blr = this._blr;
  
      msg.pos = 0;
      blr.pos = 0;
  
      if (plan instanceof Function) {
          callback = plan;
          plan = false;
      }
  
      blr.addBytes(DESCRIBE);
  
      if (plan)
          blr.addByte(isc_info_sql_get_plan);
  
      msg.addInt(op_prepare_statement);
      msg.addInt(transaction.handle);
      msg.addInt(statement.handle);
      msg.addInt(3); // dialect = 3
      msg.addString(query, DEFAULT_ENCODING);
      msg.addBlr(blr);
      msg.addInt(65535); // buffer_length
  
      var self = this;
  
      this._queueEvent(function(err, ret) {
  
          if (!err) {
              describe(ret, statement);
              statement.query = query;
              self.db.emit('query', query);
              ret = statement;
          }
  
          if (callback)
              callback(err, ret);
      });
  
  };
  
  function CalcBlr(blr, xsqlda) {
      blr.addBytes([blr_version5, blr_begin, blr_message, 0]); // + message number
      blr.addWord(xsqlda.length * 2);
  
      for (var i = 0, length = xsqlda.length; i < length; i++) {
          xsqlda[i].calcBlr(blr);
          blr.addByte(blr_short);
          blr.addByte(0);
      }
  
      blr.addByte(blr_end);
      blr.addByte(blr_eoc);
  }
  
  Connection.prototype.executeStatement = function(transaction, statement, params, callback, custom) {
  
      if (this._isClosed)
          return this.throwClosed(callback);
  
      // for auto detach
      this._pending.push('executeStatement');
  
      if (params instanceof Function) {
          callback = params;
          params = undefined;
      }
  
      var self = this;
  
      function PrepareParams(params, input, callback) {
  
          var value, meta;
          var ret = new Array(params.length);
          var wait = params.length;
  
          function done() {
              wait--;
              if (wait === 0)
                  callback(ret);
          }
  
          function putBlobData(index, value, callback) {
  
              self.createBlob2(transaction, function(err, blob) {
  
                  var b;
                  var isStream = value.readable;
  
                  if (Buffer.isBuffer(value))
                      b = value;
                  else if (typeof(value) === 'string')
                      b = new Buffer(value, DEFAULT_ENCODING)
                  else if (!isStream)
                      b = new Buffer(JSON.stringify(value), DEFAULT_ENCODING)
  
                  if (Buffer.isBuffer(b)) {
                      bufferReader(b, 1024, function(b, next) {
                          self.batchSegments(blob, b, next);
                      }, function() {
                          ret[index] = new SQLParamQuad(blob.oid);
                          self.closeBlob(blob, callback);
                      });
                      return;
                  }
  
                  var isReading = false;
                  var isEnd = false;
  
                  value.on('data', function(chunk) {
                      value.pause();
                      isReading = true;
                      bufferReader(chunk, 1024, function(b, next) {
                          self.batchSegments(blob, b, next);
                      }, function() {
                          isReading = false;
  
                          if (isEnd) {
                              ret[index] = new SQLParamQuad(blob.oid);
                              self.closeBlob(blob, callback);
                          } else
                              value.resume();
                      });
                  });
  
                  value.on('end', function() {
                      isEnd = true;
                      if (isReading)
                          return;
                      ret[index] = new SQLParamQuad(blob.oid);
                      self.closeBlob(blob, callback);
                  });
              });
          }
  
          for (var i = 0, length = params.length; i < length; i++) {
              value = params[i];
              meta = input[i];
  
              if (value === null) {
                  switch (meta.type) {
                      case SQL_VARYING:
                      case SQL_NULL:
                      case SQL_TEXT:
                          ret[i] = new SQLParamString(null);
                          break;
                      case SQL_DOUBLE:
                      case SQL_FLOAT:
                      case SQL_D_FLOAT:
                          ret[i] = new SQLParamDouble(null);
                          break;
                      case SQL_TYPE_DATE:
                      case SQL_TYPE_TIME:
                      case SQL_TIMESTAMP:
                          ret[i] = new SQLParamDate(null);
                          break;
                      case SQL_BLOB:
                      case SQL_ARRAY:
                      case SQL_QUAD:
                          ret[i] = new SQLParamQuad(null);
                          break;
                      case SQL_LONG:
                      case SQL_SHORT:
                      case SQL_INT64:
                      case SQL_BOOLEAN:
                          ret[i] = new SQLParamInt(null);
                          break;
                      default:
                          ret[i] = null;
                  }
                  done();
              } else {
                  switch (meta.type) {
                      case SQL_BLOB:
                          if (value === undefined || value === null) {
                              ret[i] = new SQLParamString(null);
                              done();
                          }
                          putBlobData(i, value, done);
                          break;
  
                      case SQL_TIMESTAMP:
                      case SQL_TYPE_DATE:
                      case SQL_TYPE_TIME:
  
                          if (value instanceof Date)
                              ret[i] = new SQLParamDate(value);
                          else if (typeof(value) === 'string')
                              ret[i] = new SQLParamDate(value.parseDate());
                          else
                              ret[i] = new SQLParamDate(new Date(value));
  
                          done();
                          break;
  
                      default:
                          switch (typeof value) {
                              case 'number':
                                  if (value % 1 === 0) {
                                      if (value >= MIN_INT && value <= MAX_INT)
                                          ret[i] = new SQLParamInt(value);
                                      else
                                          ret[i] = new SQLParamInt64(value);
                                  } else
                                      ret[i] = new SQLParamDouble(value);
                                  break;
                              case 'string':
                                  ret[i] = new SQLParamString(value);
                                  break;
                              case 'boolean':
                                  ret[i] = new SQLParamBool(value);
                                  break;
                              default:
                                  //throw new Error('Unexpected parametter: ' + JSON.stringify(params) + ' - ' + JSON.stringify(input));
                                  ret[i] = value === null || value === undefined ? new SQLVarNull() : new SQLParamString(value.toString());
                                  break;
                          }
                          done();
                  }
              }
          }
      }
  
      var input = statement.input;
  
      if (input.length) {
  
          if (!(params instanceof Array)) {
              if (params !== undefined)
                  params = [params];
              else
                  params = [];
          }
  
          if (params === undefined || params.length !== input.length) {
              self._pending.pop();
              callback(new Error('Expected parameters: (params=' + params.length + ' vs. expected=' + input.length + ') - ' + statement.query));
              return;
          }
  
          PrepareParams(params, input, function(prms) {
  
              var msg = self._msg;
              var blr = self._blr;
              msg.pos = 0;
              blr.pos = 0;
              CalcBlr(blr, prms);
  
              msg.addInt(op_execute);
              msg.addInt(statement.handle);
              msg.addInt(transaction.handle);
              msg.addBlr(blr);
              msg.addInt(0); // message number
              msg.addInt(1); // param count
  
              for(var i = 0, length = prms.length; i < length; i++)
                  prms[i].encode(msg);
  
              self._queueEvent(callback);
          });
  
          return;
      }
  
      var msg = this._msg;
      var blr = this._blr;
      msg.pos = 0;
      blr.pos = 0;
  
      msg.addInt(op_execute);
      msg.addInt(statement.handle);
      msg.addInt(transaction.handle);
  
      msg.addBlr(blr); // empty
      msg.addInt(0); // message number
      msg.addInt(0); // param count
  
      this._queueEvent(callback);
  };
  
  function fetch_blob_async(statement, id, name) {
  
      if (!id)
          return null;
  
      return function(callback) {
          // callback(err, buffer, name);
          statement.connection.startTransaction(ISOLATION_READ_UNCOMMITTED, function(err, transaction) {
  
              if (err) {
                  callback(err);
                  return;
              }
  
              statement.connection._pending.push('openBlob');
              statement.connection.openBlob(id, transaction, function(err, blob) {
  
                  var e = new Events.EventEmitter();
  
                  e.pipe = function(stream) {
                      e.on('data', function(chunk) {
                          stream.write(chunk);
                      });
                      e.on('end', function() {
                          stream.end();
                      });
                  };
  
                  if (err) {
                      callback(err, name, e);
                      return;
                  }
  
                  function read() {
                      statement.connection.getSegment(blob, function(err, ret) {
  
                          if (err) {
                              e.emit('error', err);
                              return;
                          }
  
                          var blr = new BlrReader(ret.buffer);
                          var data = blr.readSegment();
  
                          e.emit('data', data);
  
                          if (ret.handle !== 2) {
                              read();
                              return;
                          }
  
                          e.emit('end');
                          e = null;
                          statement.connection.closeBlob(blob);
  
                      });
                  }
  
                  callback(err, name, e);
                  read();
  
              });
          });
      };
  }
  
  Connection.prototype.fetch = function(statement, transaction, count, callback) {
  
      var msg = this._msg;
      var blr = this._blr;
  
      msg.pos = 0;
      blr.pos = 0;
  
      if (count instanceof Function) {
          callback = count;
          count = DEFAULT_FETCHSIZE;
      }
  
      msg.addInt(op_fetch);
      msg.addInt(statement.handle);
      CalcBlr(blr, statement.output);
      msg.addBlr(blr);
      msg.addInt(0); // message number
      msg.addInt(count || DEFAULT_FETCHSIZE); // fetch count
  
      if (!transaction) {
          callback.statement = statement;
          this._queueEvent(callback);
          return;
      }
  
      callback.statement = statement;
      this._queueEvent(callback);
  };
  
  Connection.prototype.fetchAll = function(statement, transaction, callback) {
  
      var self = this;
      var data;
      var loop = function(err, ret) {
  
          if (err) {
              callback(err);
              return;
          }
  
          if (!data) {
              data = ret.data;
          } else {
              for (var i = 0, length = ret.data.length; i < length; i++)
                  data.push(ret.data[i]);
          }
  
          if (ret.fetched)
              callback(undefined, data);
          else
              self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
      }
  
      this.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
  };
  
  Connection.prototype.openBlob = function(blob, transaction, callback) {
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_open_blob);
      msg.addInt(transaction.handle);
      msg.addQuad(blob);
      this._queueEvent(callback);
  };
  
  Connection.prototype.closeBlob = function(blob, callback) {
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_close_blob);
      msg.addInt(blob.handle);
      this._queueEvent(callback);
  };
  
  Connection.prototype.getSegment = function(blob, callback) {
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_get_segment);
      msg.addInt(blob.handle);
      msg.addInt(1024); // buffer length
      msg.addInt(0); // ???
      this._queueEvent(callback);
  };
  
  Connection.prototype.createBlob2 = function (transaction, callback) {
      var msg = this._msg;
      msg.pos = 0;
      msg.addInt(op_create_blob2);
      msg.addInt(0);
      msg.addInt(transaction.handle);
      msg.addInt(0);
      msg.addInt(0);
      this._queueEvent(callback);
  };
  
  Connection.prototype.batchSegments = function(blob, buffer, callback){
      var msg = this._msg;
      var blr = this._blr;
      msg.pos = 0;
      blr.pos = 0;
      msg.addInt(op_batch_segments);
      msg.addInt(blob.handle);
      msg.addInt(buffer.length + 2);
      blr.addBuffer(buffer);
      msg.addBlr(blr);
      this._queueEvent(callback);
  };
  
  Connection.prototype.svcattach = function (options, callback, svc) {
      var database = options.database || options.filename;
      var user = options.user || DEFAULT_USER;
      var password = options.password || DEFAULT_PASSWORD;
      var role = options.role;
      var self = this;
      var msg = this._msg;
      var blr = this._blr;
      msg.pos = 0;
      blr.pos = 0;
      
      blr.addBytes([isc_dpb_version2, isc_dpb_version2]);
      blr.addString(isc_dpb_lc_ctype, 'UTF8', DEFAULT_ENCODING);
      blr.addString(isc_dpb_user_name, user, DEFAULT_ENCODING);
      blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
      blr.addByte(isc_dpb_dummy_packet_interval);
      blr.addByte(4);
      blr.addBytes([120, 10, 0, 0]); // FROM DOT NET PROVIDER
      if (role)
          blr.addString(isc_dpb_sql_role_name, role, DEFAULT_ENCODING);
  
      msg.addInt(op_service_attach); 
      msg.addInt(0);
      msg.addString(DEFAULT_SVC_NAME, DEFAULT_ENCODING); // only local for moment
      msg.addBlr(this._blr);
      
      var self = this;
      
      function cb(err, ret) {
          
          if (err) {
              doError(err, callback);
              return;
          }
          
          self.svchandle = ret.handle;
          if (callback)
              callback(undefined, ret);
      }
      
      // For reconnect
      if (svc) {
          svc.connection = this;
          cb.response = svc;
      } else {
          cb.response = new ServiceManager(this);
          cb.response.removeAllListeners('error');
          cb.response.on('error', noop);
      }
      
      this._queueEvent(cb);        
  }
  
  Connection.prototype.svcstart = function (spbaction, callback) {
      var msg = this._msg;
      var blr = this._blr;
      msg.pos = 0;
      msg.addInt(op_service_start);
      msg.addInt(this.svchandle);
      msg.addInt(0)
      msg.addBlr(spbaction);
      this._queueEvent(callback);
  }
  
  Connection.prototype.svcquery = function (spbquery, resultbuffersize, timeout,callback) {
      if (resultbuffersize > MAX_BUFFER_SIZE) {
          doError(new Error('Buffer is too big'), callback);
          return;
      }
      
      var msg = this._msg;
      var blr = this._blr;
      msg.pos = 0;
      blr.pos = 0;
      blr.addByte(isc_spb_current_version);
      //blr.addByteInt32(isc_info_svc_timeout, timeout);
      msg.addInt(op_service_info);
      msg.addInt(this.svchandle);
      msg.addInt(0)
      msg.addBlr(blr);
      blr.pos = 0
      blr.addBytes(spbquery);
      msg.addBlr(blr);
      msg.addInt(resultbuffersize);
      this._queueEvent(callback);
  }
  
  Connection.prototype.svcdetach = function (callback) {
      var self = this;
      
      if (self._isClosed)
          return;
      
      self._isUsed = false;
      self._isDetach = true;
      
      var msg = self._msg;
      
      msg.pos = 0;
      msg.addInt(op_service_detach);
      msg.addInt(this.svchandle); // Database Object ID
      
      self._queueEvent(function (err, ret) {
          delete (self.svchandle);
          if (callback)
              callback(err, ret);
      });
  }
  
  function bufferReader(buffer, max, writer, cb, beg, end) {
  
      if (!beg)
          beg = 0;
  
      if (!end)
          end = max;
  
      if (end >= buffer.length)
          end = undefined;
  
      var b = buffer.slice(beg, end);
  
      writer(b, function() {
  
          if (end === undefined) {
              cb();
              return;
          }
  
          bufferReader(buffer, max, writer, cb, beg + max, end + max);
      });
  }