]> granicus.if.org Git - php/commitdiff
More divide & conquer
authorArd Biesheuvel <abies@php.net>
Mon, 5 Apr 2004 13:22:34 +0000 (13:22 +0000)
committerArd Biesheuvel <abies@php.net>
Mon, 5 Apr 2004 13:22:34 +0000 (13:22 +0000)
ext/interbase/config.m4
ext/interbase/config.w32
ext/interbase/ibase_blobs.c
ext/interbase/ibase_events.c
ext/interbase/ibase_query.c [new file with mode: 0644]
ext/interbase/ibase_service.c
ext/interbase/interbase.c
ext/interbase/php_ibase_includes.h

index dca634d34dec497094077882a7ad510b76a0f7ed..e61bf49adc78058dba2de832b802c324c8bb1fa2 100644 (file)
@@ -53,6 +53,6 @@ if test "$PHP_INTERBASE" != "no"; then
   PHP_ADD_LIBRARY_WITH_PATH($IBASE_LIBNAME, $IBASE_LIBDIR, INTERBASE_SHARED_LIBADD)
   PHP_ADD_INCLUDE($IBASE_INCDIR)
   AC_DEFINE(HAVE_IBASE,1,[ ])
-  PHP_NEW_EXTENSION(interbase, interbase.c ibase_service.c ibase_events.c ibase_blobs.c, $ext_shared)
+  PHP_NEW_EXTENSION(interbase, interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c, $ext_shared)
   PHP_SUBST(INTERBASE_SHARED_LIBADD)
 fi
index 9eab6a4599d635beb5cef9e11621e9e16ec5e624..0019861c59a55e8987bff1260beee65dc99007ab 100644 (file)
@@ -9,7 +9,7 @@ if (PHP_INTERBASE != "no" || PHP_INTERBASE5 != "no") {
        if (CHECK_HEADER_ADD_INCLUDE("ibase.h", "CFLAGS_INTERBASE", PHP_PHP_BUILD + "\\interbase\\include;" + PHP_INTERBASE) &&
                        (CHECK_LIB("fbclient_ms.lib", "interbase", PHP_PHP_BUILD + "\\interbase\\lib_ms;" + PHP_INTERBASE) ||
                         CHECK_LIB("gds32_ms.lib", "interbase", PHP_PHP_BUILD + "\\interbase\\lib_ms;" + PHP_INTERBASE))) {
-               EXTENSION("interbase", "interbase.c ibase_service.c ibase_events.c ibase_blobs.c");
+               EXTENSION("interbase", "interbase.c ibase_query.c ibase_service.c ibase_events.c ibase_blobs.c");
                AC_DEFINE('HAVE_IBASE', 1, 'Have interbase library');
 
                if (PHP_INTERBASE != "no") {
index 304d9d7a2dea9d4e1cc29022b6d3a72b093e75af..eae2f74ebb5c3fc87695a1123d7676b1524cd48f 100644 (file)
 #define BLOB_CLOSE             1
 #define BLOB_CANCEL            2
 
-int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd) /* {{{ */
+static int le_blob;
+
+static void _php_ibase_free_blob(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+{
+       ibase_blob *ib_blob = (ibase_blob *)rsrc->ptr;
+
+       if (ib_blob->bl_handle != NULL) { /* blob open*/
+               if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
+                       _php_ibase_module_error("You can lose data. Close any blob after reading from or "
+                               "writing to it. Use ibase_blob_close() before calling ibase_close()" TSRMLS_CC);
+               }
+       }
+       efree(ib_blob);
+}
+/* }}} */
+
+void php_ibase_blobs_minit(INIT_FUNC_ARGS) /* {{{ */
+{
+       le_blob = zend_register_list_destructors_ex(_php_ibase_free_blob, NULL, 
+           "interbase blob", module_number);
+}
+/* }}} */
+
+PHPAPI int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd) /* {{{ */
 {
        /* shortcut for most common case */
        if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) {
@@ -49,13 +72,13 @@ int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd) /* {{{ */
 }
 /* }}} */
 
-char *_php_ibase_quad_to_string(ISC_QUAD const qd) /* {{{ */
+PHPAPI char *_php_ibase_quad_to_string(ISC_QUAD const qd) /* {{{ */
 {
        char *result = (char *) emalloc(BLOB_ID_LEN+1);
 
        /* shortcut for most common case */
        if (sizeof(ISC_QUAD) == sizeof(ISC_UINT64)) {
-               sprintf(result, BLOB_ID_MASK, *(ISC_UINT64*)(void *) &qd); 
+               sprintf(result, BLOB_ID_MASK, *(ISC_UINT64*)(void *) &qd);
        } else {
                ISC_UINT64 res = ((ISC_UINT64) qd.gds_quad_high << 0x20) | qd.gds_quad_low;
                sprintf(result, BLOB_ID_MASK, res);
@@ -73,25 +96,25 @@ typedef struct { /* {{{ */
 /* }}} */
 } IBASE_BLOBINFO;
 
-int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC) /* {{{ */
+PHPAPI int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC) /* {{{ */
 {
        if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
-               
+
                ISC_STATUS stat;
                char *bl_data;
                unsigned long cur_len;
                unsigned short seg_len;
-       
+
                bl_data = emalloc(max_len + 1);
-       
+
                for (cur_len = stat = 0; (stat == 0 || stat == isc_segment) && cur_len < max_len; cur_len += seg_len) {
-       
-                       unsigned short chunk_size = (max_len-cur_len) > USHRT_MAX ? USHRT_MAX 
+
+                       unsigned short chunk_size = (max_len-cur_len) > USHRT_MAX ? USHRT_MAX
                                : (unsigned short)(max_len-cur_len);
-       
-                       stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, chunk_size, &bl_data[cur_len]); 
+
+                       stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, chunk_size, &bl_data[cur_len]);
                }
-       
+
                bl_data[cur_len] = '\0';
                if (IB_STATUS[0] == 1 && (stat != 0 && stat != isc_segstr_eof && stat != isc_segment)) {
                        efree(bl_data);
@@ -106,7 +129,7 @@ int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long m
 }
 /* }}} */
 
-int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC) /* {{{ */
+PHPAPI int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC) /* {{{ */
 {
        unsigned long put_cnt = 0, rem_cnt;
        unsigned short chunk_size;
@@ -114,7 +137,7 @@ int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC) /* {{{
        convert_to_string_ex(string_arg);
 
        for (rem_cnt = Z_STRLEN_PP(string_arg); rem_cnt > 0; rem_cnt -= chunk_size)  {
-               
+
                chunk_size = rem_cnt > USHRT_MAX ? USHRT_MAX : (unsigned short)rem_cnt;
 
                if (isc_put_segment(IB_STATUS, &ib_blob->bl_handle, chunk_size, &Z_STRVAL_PP(string_arg)[put_cnt] )) {
@@ -124,7 +147,7 @@ int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC) /* {{{
                put_cnt += chunk_size;
        }
        return SUCCESS;
-}      
+}
 /* }}} */
 
 static int _php_ibase_blob_info(isc_blob_handle bl_handle, IBASE_BLOBINFO *bl_info TSRMLS_DC) /* {{{ */
@@ -135,7 +158,7 @@ static int _php_ibase_blob_info(isc_blob_handle bl_handle, IBASE_BLOBINFO *bl_in
                isc_info_blob_total_length,
                isc_info_blob_type
        };
-       
+
        char bl_inf[sizeof(long)*8], *p;
 
        bl_info->max_segment = 0;
@@ -190,23 +213,23 @@ PHP_FUNCTION(ibase_blob_create)
        ibase_blob *ib_blob;
 
        RESET_ERRMSG;
-       
+
        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &link)) {
                RETURN_FALSE;
        }
 
        PHP_IBASE_LINK_TRANS(link, ib_link, trans);
-       
+
        ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
        ib_blob->bl_handle = NULL;
        ib_blob->type = BLOB_INPUT;
-       
+
        if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob->bl_handle, &ib_blob->bl_qd)) {
                _php_ibase_error(TSRMLS_C);
                efree(ib_blob);
                RETURN_FALSE;
        }
-               
+
        ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob);
 }
 /* }}} */
@@ -226,7 +249,7 @@ PHP_FUNCTION(ibase_blob_open)
 
        switch (ZEND_NUM_ARGS()) {
                default:
-                       WRONG_PARAM_COUNT;              
+                       WRONG_PARAM_COUNT;
                case 1:
                        if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
                                RETURN_FALSE;
@@ -238,13 +261,13 @@ PHP_FUNCTION(ibase_blob_open)
                        }
                        break;
        }
-       
+
        PHP_IBASE_LINK_TRANS(link, ib_link, trans);
 
        ib_blob = (ibase_blob *) emalloc(sizeof(ibase_blob));
        ib_blob->bl_handle = NULL;
        ib_blob->type = BLOB_OUTPUT;
-       
+
        do {
                if (! _php_ibase_string_to_quad(blob_id, &ib_blob->bl_qd)) {
                        _php_ibase_module_error("String is not a BLOB ID" TSRMLS_CC);
@@ -256,7 +279,7 @@ PHP_FUNCTION(ibase_blob_open)
                        _php_ibase_error(TSRMLS_C);
                        break;
                }
-       
+
                ZEND_REGISTER_RESOURCE(return_value, ib_blob, le_blob);
                return;
 
@@ -327,13 +350,13 @@ static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end) /* {{{
        ibase_blob *ib_blob;
 
        RESET_ERRMSG;
-       
+
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &blob_arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
 
-       ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);    
-       
+       ZEND_FETCH_RESOURCE(ib_blob, ibase_blob *, blob_arg, -1, "Interbase blob", le_blob);
+
        if (bl_end == BLOB_CLOSE) { /* return id here */
 
                if (ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low) { /*not null ?*/
@@ -389,7 +412,7 @@ PHP_FUNCTION(ibase_blob_info)
 
        switch (ZEND_NUM_ARGS()) {
                default:
-                       WRONG_PARAM_COUNT;              
+                       WRONG_PARAM_COUNT;
                case 1:
                        if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
                                RETURN_FALSE;
@@ -431,7 +454,7 @@ PHP_FUNCTION(ibase_blob_info)
        }
 
        array_init(return_value);
-       
+
        add_index_long(return_value, 0, bl_info.total_length);
        add_assoc_long(return_value, "length", bl_info.total_length);
 
@@ -457,16 +480,16 @@ PHP_FUNCTION(ibase_blob_echo)
        int blob_id_len;
        zval *link = NULL;
        ibase_db_link *ib_link;
-       ibase_trans *trans = NULL;              
+       ibase_trans *trans = NULL;
        ibase_blob ib_blob_id = { NULL, BLOB_OUTPUT  };
-       char bl_data[IBASE_BLOB_SEG]; 
+       char bl_data[IBASE_BLOB_SEG];
        unsigned short seg_len;
 
        RESET_ERRMSG;
 
        switch (ZEND_NUM_ARGS()) {
                default:
-                       WRONG_PARAM_COUNT;              
+                       WRONG_PARAM_COUNT;
                case 1:
                        if (FAILURE == zend_parse_parameters(1 TSRMLS_CC, "s", &blob_id, &blob_id_len)) {
                                RETURN_FALSE;
@@ -491,16 +514,16 @@ PHP_FUNCTION(ibase_blob_echo)
                                &ib_blob_id.bl_qd)) {
                        break;
                }
-       
+
                while (!isc_get_segment(IB_STATUS, &ib_blob_id.bl_handle, &seg_len, sizeof(bl_data), bl_data)
                                || IB_STATUS[1] == isc_segment) {
                        PHPWRITE(bl_data, seg_len);
                }
-       
+
                if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)) {
                        break;
                }
-       
+
                if (isc_close_blob(IB_STATUS, &ib_blob_id.bl_handle)) {
                        break;
                }
@@ -527,27 +550,27 @@ PHP_FUNCTION(ibase_blob_import)
 
        RESET_ERRMSG;
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|r", 
+       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|r",
                        (ZEND_NUM_ARGS()-1) ? &link : &file, &file)) {
                RETURN_FALSE;
        }
-       
+
        PHP_IBASE_LINK_TRANS(link, ib_link, trans);
 
        php_stream_from_zval(stream, &file);
-       
+
        do {
                if (isc_create_blob(IB_STATUS, &ib_link->handle, &trans->handle, &ib_blob.bl_handle,
                                &ib_blob.bl_qd)) {
                        break;
                }
-       
+
                for (size = 0; (b = php_stream_read(stream, bl_data, sizeof(bl_data))); size += b) {
                        if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) {
                                break;
                        }
                }
-               
+
                if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
                        break;
                }
index a5361964a598a8a90d88eb88c6865faadcc5c95c..dc8fab36a454162995c342f6ba106b27db3ca6ad 100644 (file)
 #include "php_interbase.h"
 #include "php_ibase_includes.h"
 
-void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */
+static int le_event;
+
+static void _php_ibase_event_free(char *event_buf, char *result_buf) /* {{{ */
 {
        isc_free(event_buf);
        isc_free(result_buf);
 }
 /* }}} */
 
+PHPAPI void _php_ibase_free_event(ibase_event *event TSRMLS_DC) /* {{{ */
+{
+       unsigned short i;
+
+       event->state = DEAD;
+
+       if (event->link != NULL) {
+               ibase_event **node;
+
+               if (event->link->handle != NULL &&
+                               isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) {
+                       _php_ibase_error(TSRMLS_C);
+               }
+
+               /* delete this event from the link struct */
+               for (node = &event->link->event_head; *node != event; node = &(*node)->event_next);
+               *node = event->event_next;
+       }
+
+       if (event->callback) {
+               zval_dtor(event->callback);
+               FREE_ZVAL(event->callback);
+               event->callback = NULL;
+
+               _php_ibase_event_free(event->event_buffer,event->result_buffer);
+
+               for (i = 0; i < event->event_count; ++i) {
+                       efree(event->events[i]);
+               }
+               efree(event->events);
+       }
+}
+/* }}} */
+
+static void _php_ibase_free_event_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+{
+       ibase_event *e = (ibase_event *) rsrc->ptr;
+
+       _php_ibase_free_event(e TSRMLS_CC);
+
+       efree(e);
+}
+/* }}} */
+
+void php_ibase_events_minit(INIT_FUNC_ARGS) /* {{{ */
+{
+       le_event = zend_register_list_destructors_ex(_php_ibase_free_event_rsrc, NULL, 
+           "interbase event", module_number);
+}
+/* }}} */
+
 static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count, /* {{{ */
        char **events, unsigned short *l, char **event_buf, char **result_buf)
 {
@@ -43,7 +96,7 @@ static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count,
        unsigned long dummy_count[15];
 
        /**
-        * Unfortunately, there's no clean and portable way in C to pass arguments to 
+        * Unfortunately, there's no clean and portable way in C to pass arguments to
         * a variadic function if you don't know the number of arguments at compile time.
         * (And even if there were a way, the Interbase API doesn't provide a version of
         * this function that takes a va_list as an argument)
@@ -52,8 +105,8 @@ static void _php_ibase_event_block(ibase_db_link *ib_link, unsigned short count,
         * so we can work around it.
         */
 
-       *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0], 
-               events[1], events[2], events[3], events[4], events[5], events[6], events[7], 
+       *l = (unsigned short) isc_event_block(event_buf, result_buf, count, events[0],
+               events[1], events[2], events[3], events[4], events[5], events[6], events[7],
                events[8], events[9], events[10], events[11], events[12], events[13], events[14]);
 
        /**
@@ -78,7 +131,7 @@ PHP_FUNCTION(ibase_wait_event)
        char *event_buffer, *result_buffer, *events[15];
        unsigned short i = 0, event_count = 0, buffer_size;
        unsigned long occurred_event[15];
-       
+
        RESET_ERRMSG;
 
        /* no more than 15 events */
@@ -102,8 +155,8 @@ PHP_FUNCTION(ibase_wait_event)
                }
 
                ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", le_link, le_plink);
-       }                               
-               
+       }
+
        for (; i < ZEND_NUM_ARGS(); ++i) {
                convert_to_string_ex(args[i]);
                events[event_count++] = Z_STRVAL_PP(args[i]);
@@ -118,7 +171,7 @@ PHP_FUNCTION(ibase_wait_event)
                _php_ibase_event_free(event_buffer,result_buffer);
                RETURN_FALSE;
        }
-       
+
        /* find out which event occurred */
        isc_event_counts(occurred_event, buffer_size, event_buffer, result_buffer);
        for (i = 0; i < event_count; ++i) {
@@ -128,7 +181,7 @@ PHP_FUNCTION(ibase_wait_event)
                        RETURN_STRING(result,0);
                }
        }
-       
+
        /* If we reach this line, isc_wait_for_event() did return, but we don't know
           which event fired. */
        _php_ibase_event_free(event_buffer,result_buffer);
@@ -141,10 +194,10 @@ static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */
 {
        /* this function is called asynchronously by the Interbase client library. */
        TSRMLS_FETCH_FROM_CTX(event->thread_ctx);
-       
+
        /**
         * The callback function is called when the event is first registered and when the event
-        * is cancelled. I consider this is a bug. By clearing event->callback first and setting 
+        * is cancelled. I consider this is a bug. By clearing event->callback first and setting
         * it to -1 later, we make sure nothing happens if no event was actually posted.
         */
        switch (event->state) {
@@ -155,16 +208,16 @@ static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */
                default: /* == DEAD */
                        break;
                case ACTIVE:
-                       args[0] = &event_name; 
-                       args[1] = &link_id; 
-                       
+                       args[0] = &event_name;
+                       args[1] = &link_id;
+
                        /* copy the updated results into the result buffer */
                        memcpy(event->result_buffer, result_buf, buffer_size);
-                       
+
                        INIT_ZVAL(event_name);
                        INIT_ZVAL(link_id);
                        ZVAL_RESOURCE(&link_id, event->link_res_id);
-       
+
                        /* find out which event occurred */
                        isc_event_counts(occurred_event, buffer_size, event->event_buffer, event->result_buffer);
                        for (i = 0; i < event->event_count; ++i) {
@@ -172,24 +225,24 @@ static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */
                                        ZVAL_STRING(&event_name,event->events[i],0);
                                        break;
                                }
-                       }       
-       
+                       }
+
                        /* call the callback provided by the user */
-                       if (SUCCESS != call_user_function(EG(function_table), NULL, 
+                       if (SUCCESS != call_user_function(EG(function_table), NULL,
                                        event->callback, &return_value, 2, args TSRMLS_CC)) {
                                _php_ibase_module_error("Error calling callback %s" TSRMLS_CC, Z_STRVAL_P(event->callback));
                                break;
                        }
-       
+
                        if (Z_TYPE(return_value) == IS_BOOL && !Z_BVAL(return_value)) {
                                event->state = DEAD;
                                break;
-                       }                       
+                       }
                case NEW:
                        /* re-register the event */
-                       if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size, 
+                       if (isc_que_events(IB_STATUS, &event->link->handle, &event->event_id, buffer_size,
                                event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) {
-               
+
                                _php_ibase_error(TSRMLS_C);
                        }
                        event->state = ACTIVE;
@@ -203,8 +256,8 @@ static isc_callback _php_ibase_callback(ibase_event *event, /* {{{ */
 PHP_FUNCTION(ibase_set_event_handler)
 {
        /**
-        * The callback passed to this function should take an event name (string) and a 
-        * link resource id (int) as arguments. The value returned from the function is 
+        * The callback passed to this function should take an event name (string) and a
+        * link resource id (int) as arguments. The value returned from the function is
         * used to determine if the event handler should remain set.
         */
 
@@ -213,7 +266,7 @@ PHP_FUNCTION(ibase_set_event_handler)
        ibase_event *event;
        unsigned short i = 1, buffer_size;
        int link_res_id;
-               
+
        RESET_ERRMSG;
 
        /* no more than 15 events */
@@ -231,7 +284,7 @@ PHP_FUNCTION(ibase_set_event_handler)
                cb_arg = args[1];
                i = 2;
 
-               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, args[0], -1, 
+               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, args[0], -1,
                        "InterBase link", le_link, le_plink);
 
                convert_to_long_ex(args[0]);
@@ -245,18 +298,18 @@ PHP_FUNCTION(ibase_set_event_handler)
 
                cb_arg = args[0];
 
-               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), 
+               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link),
                        "InterBase link", le_link, le_plink);
                link_res_id = IBG(default_link);
-       }                               
-               
+       }
+
        /* get the callback */
        if (!zend_is_callable(*cb_arg, 0, NULL)) {
                _php_ibase_module_error("Callback argument %s is not a callable function"
                        TSRMLS_CC, Z_STRVAL_PP(cb_arg));
                RETURN_FALSE;
        }
-               
+
        /* allocate the event resource */
        event = (ibase_event *) safe_emalloc(sizeof(ibase_event), 1, 0);
        TSRMLS_SET_CTX(event->thread_ctx);
@@ -270,28 +323,28 @@ PHP_FUNCTION(ibase_set_event_handler)
        *event->callback = **cb_arg;
        INIT_PZVAL(event->callback);
        zval_copy_ctor(event->callback);
-               
+
        for (; i < ZEND_NUM_ARGS(); ++i) {
                convert_to_string_ex(args[i]);
                event->events[event->event_count++] = estrdup(Z_STRVAL_PP(args[i]));
        }
 
        /* fills the required data structure with information about the events */
-       _php_ibase_event_block(ib_link, event->event_count, event->events, 
+       _php_ibase_event_block(ib_link, event->event_count, event->events,
                &buffer_size, &event->event_buffer, &event->result_buffer);
-               
+
        /* now register the events with the Interbase API */
-       if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size, 
+       if (isc_que_events(IB_STATUS, &ib_link->handle, &event->event_id, buffer_size,
                event->event_buffer,(isc_callback)_php_ibase_callback, (void *)event)) {
-                       
+
                _php_ibase_error(TSRMLS_C);
                efree(event);
                RETURN_FALSE;
        }
-       
+
        event->event_next = ib_link->event_head;
        ib_link->event_head = event;
-       
+
        ZEND_REGISTER_RESOURCE(return_value, event, le_event);
        zend_list_addref(Z_LVAL_P(return_value));
 }
@@ -302,14 +355,14 @@ PHP_FUNCTION(ibase_set_event_handler)
 PHP_FUNCTION(ibase_free_event_handler)
 {
        zval *event_arg;
-       
+
        RESET_ERRMSG;
-       
+
        if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &event_arg)) {
                ibase_event *event;
 
                ZEND_FETCH_RESOURCE(event, ibase_event *, &event_arg, -1, "Interbase event", le_event);
-               
+
                event->state = DEAD;
 
                zend_list_delete(Z_LVAL_P(event_arg));
@@ -317,7 +370,7 @@ PHP_FUNCTION(ibase_free_event_handler)
        } else {
                RETURN_FALSE;
        }
-}      
+}
 /* }}} */
 
 #endif /* HAVE_IBASE */
diff --git a/ext/interbase/ibase_query.c b/ext/interbase/ibase_query.c
new file mode 100644 (file)
index 0000000..a29a64c
--- /dev/null
@@ -0,0 +1,2131 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2004 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.0 of the PHP license,       |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_0.txt.                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Ard Biesheuvel <a.k.biesheuvel@its.tudelft.nl>              |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#if HAVE_IBASE
+
+#include "ext/standard/php_standard.h"
+#include "php_interbase.h"
+#include "php_ibase_includes.h"
+
+#define ISC_LONG_MIN   INT_MIN
+#define ISC_LONG_MAX   INT_MAX
+
+#define QUERY_RESULT   1
+#define EXECUTE_RESULT 2
+
+#define FETCH_ROW              1
+#define FETCH_ARRAY            2
+
+typedef struct {
+       unsigned short vary_length;
+       char vary_string[1];
+} IBVARY;
+
+/* sql variables union 
+ * used for convert and binding input variables
+ */
+typedef struct {
+       union {
+               short sval;
+               float fval;
+               ISC_LONG lval;
+               ISC_QUAD qval;
+#ifdef SQL_TIMESTAMP
+               ISC_TIMESTAMP tsval;
+               ISC_DATE dtval;
+               ISC_TIME tmval;
+#endif
+       } val;
+       short sqlind;
+} BIND_BUF;
+
+static int le_result, le_query;
+
+static void _php_ibase_free_xsqlda(XSQLDA *sqlda) /* {{{ */
+{
+       int i;
+       XSQLVAR *var;
+
+       IBDEBUG("Free XSQLDA?");
+       if (sqlda) {
+               IBDEBUG("Freeing XSQLDA...");
+               var = sqlda->sqlvar;
+               for (i = 0; i < sqlda->sqld; i++, var++) {
+                       efree(var->sqldata);
+                       if (var->sqlind) {
+                               efree(var->sqlind);
+                       }
+               }
+               efree(sqlda);
+       }
+}
+/* }}} */
+
+static void _php_ibase_free_result(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+{
+       ibase_result *ib_result = (ibase_result *) rsrc->ptr;
+
+       IBDEBUG("Freeing result by dtor...");
+       if (ib_result) {
+               _php_ibase_free_xsqlda(ib_result->out_sqlda);
+               if (ib_result->stmt && ib_result->type != EXECUTE_RESULT) {
+                       IBDEBUG("Dropping statement handle (free_result dtor)...");
+                       isc_dsql_free_statement(IB_STATUS, &ib_result->stmt, DSQL_drop);
+               }
+               efree(ib_result);
+       }
+}
+/* }}} */
+
+static void _php_ibase_free_query(ibase_query *ib_query TSRMLS_DC) /* {{{ */
+{
+       IBDEBUG("Freeing query...");
+
+       if (ib_query->in_sqlda) {
+               efree(ib_query->in_sqlda);
+       }
+       if (ib_query->out_sqlda) {
+               efree(ib_query->out_sqlda);
+       }
+       if (ib_query->stmt) {
+               IBDEBUG("Dropping statement handle (free_query)...");
+               if (isc_dsql_free_statement(IB_STATUS, &ib_query->stmt, DSQL_drop)) {
+                       _php_ibase_error(TSRMLS_C);
+               }
+       }
+       if (ib_query->in_array) {
+               efree(ib_query->in_array);
+       }
+       if (ib_query->out_array) {
+               efree(ib_query->out_array);
+       }
+       if (ib_query->query) {
+               efree(ib_query->query);
+       }
+}
+/* }}} */
+
+static void php_ibase_free_query_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+{
+       ibase_query *ib_query = (ibase_query *)rsrc->ptr;
+
+       if (ib_query != NULL) {
+               IBDEBUG("Preparing to free query by dtor...");
+
+               _php_ibase_free_query(ib_query TSRMLS_CC);
+               if (ib_query->statement_type != isc_info_sql_stmt_exec_procedure) {
+                       zend_list_delete(ib_query->result_res_id);
+               }
+               efree(ib_query);
+       }
+}
+/* }}} */
+
+void php_ibase_query_minit(INIT_FUNC_ARGS) /* {{{ */
+{
+       le_result = zend_register_list_destructors_ex(_php_ibase_free_result, NULL, 
+           "interbase result", module_number);
+       le_query = zend_register_list_destructors_ex(php_ibase_free_query_rsrc, NULL, 
+           "interbase query", module_number);
+}
+/* }}} */
+
+static int _php_ibase_alloc_array(ibase_array **ib_arrayp, XSQLDA *sqlda, /* {{{ */
+       isc_db_handle link, isc_tr_handle trans, unsigned short *array_cnt TSRMLS_DC)
+{
+       unsigned short i, n;
+       ibase_array *ar;
+
+       /* first check if we have any arrays at all */
+       for (i = *array_cnt = 0; i < sqlda->sqld; ++i) {
+               if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_ARRAY) {
+                       ++*array_cnt;
+               }
+       }
+       if (! *array_cnt) return SUCCESS;
+
+       ar = safe_emalloc(sizeof(ibase_array), *array_cnt, 0);
+
+       for (i = n = 0; i < sqlda->sqld; ++i) {
+               unsigned short dim;
+               unsigned long ar_size = 1;
+               XSQLVAR *var = &sqlda->sqlvar[i];
+
+               if ((var->sqltype & ~1) == SQL_ARRAY) {
+                       ibase_array *a = &ar[n++];
+                       ISC_ARRAY_DESC *ar_desc = &a->ar_desc;
+
+                       if (isc_array_lookup_bounds(IB_STATUS, &link, &trans, var->relname,
+                                       var->sqlname, ar_desc)) {
+                               _php_ibase_error(TSRMLS_C);
+                               efree(ar);
+                               return FAILURE;
+                       }
+
+                       switch (ar_desc->array_desc_dtype) {
+                               case blr_text:
+                               case blr_text2:
+                                       a->el_type = SQL_TEXT;
+                                       a->el_size = ar_desc->array_desc_length;
+                                       break;
+                               case blr_short:
+                                       a->el_type = SQL_SHORT;
+                                       a->el_size = sizeof(short);
+                                       break;
+                               case blr_long:
+                                       a->el_type = SQL_LONG;
+                                       a->el_size = sizeof(ISC_LONG);
+                                       break;
+                               case blr_float:
+                                       a->el_type = SQL_FLOAT;
+                                       a->el_size = sizeof(float);
+                                       break;
+                               case blr_double:
+                                       a->el_type = SQL_DOUBLE;
+                                       a->el_size = sizeof(double);
+                                       break;
+#ifdef blr_int64
+                               case blr_int64:
+                                       a->el_type = SQL_INT64;
+                                       a->el_size = sizeof(ISC_INT64);
+                                       break;
+#endif
+#ifndef blr_timestamp
+                               case blr_date:
+                                       a->el_type = SQL_DATE;
+                                       a->el_size = sizeof(ISC_QUAD);
+                                       break;
+#else
+                               case blr_timestamp:
+                                       a->el_type = SQL_TIMESTAMP;
+                                       a->el_size = sizeof(ISC_TIMESTAMP);
+                                       break;
+                               case blr_sql_date:
+                                       a->el_type = SQL_TYPE_DATE;
+                                       a->el_size = sizeof(ISC_DATE);
+                                       break;
+                               case blr_sql_time:
+                                       a->el_type = SQL_TYPE_TIME;
+                                       a->el_size = sizeof(ISC_TIME);
+                                       break;
+#endif                                         
+                               case blr_varying:
+                               case blr_varying2:
+                                       /**
+                                        * IB has a strange way of handling VARCHAR arrays. It doesn't store
+                                        * the length in the first short, as with VARCHAR fields. It does, 
+                                        * however, expect the extra short to be allocated for each element.
+                                        */
+                                       a->el_type = SQL_TEXT;
+                                       a->el_size = ar_desc->array_desc_length + sizeof(short);
+                                       break;
+                               case blr_quad:
+                               case blr_blob_id:
+                               case blr_cstring:
+                               case blr_cstring2:
+                                       /**
+                                        * These types are mentioned as array types in the manual, but I 
+                                        * wouldn't know how to create an array field with any of these
+                                        * types. I assume these types are not applicable to arrays, and
+                                        * were mentioned erroneously.
+                                        */
+                               default:
+                                       _php_ibase_module_error("Unsupported array type %d in relation '%s' column '%s'"
+                                               TSRMLS_CC, ar_desc->array_desc_dtype, var->relname, var->sqlname);
+                                       efree(ar);
+                                       return FAILURE;
+                       } /* switch array_desc_type */
+
+                       /* calculate elements count */
+                       for (dim = 0; dim < ar_desc->array_desc_dimensions; dim++) {
+                               ar_size *= 1 + ar_desc->array_desc_bounds[dim].array_bound_upper
+                                       -ar_desc->array_desc_bounds[dim].array_bound_lower;
+                       }
+                       a->ar_size = a->el_size * ar_size;
+               } /* if SQL_ARRAY */
+       } /* for column */
+       *ib_arrayp = ar;
+       return SUCCESS;
+}
+/* }}} */
+
+/* allocate and prepare query */
+static int _php_ibase_alloc_query(ibase_query *ib_query, ibase_db_link *link, /* {{{ */
+       ibase_trans *trans, char *query, unsigned short dialect, int trans_res_id TSRMLS_DC)
+{
+       static char info_type[] = {isc_info_sql_stmt_type};
+       char result[8];
+
+       ib_query->link = link;
+       ib_query->trans = trans;
+       ib_query->result_res_id = 0;
+       ib_query->stmt = NULL;
+       ib_query->in_array = NULL;
+       ib_query->out_array = NULL;
+       ib_query->dialect = dialect;
+       ib_query->query = estrdup(query);
+       ib_query->trans_res_id = trans_res_id;
+
+       if (isc_dsql_allocate_statement(IB_STATUS, &link->handle, &ib_query->stmt)) {
+               _php_ibase_error(TSRMLS_C);
+               goto _php_ibase_alloc_query_error;
+       }
+
+       ib_query->out_sqlda = (XSQLDA *) emalloc(XSQLDA_LENGTH(1));
+       ib_query->out_sqlda->sqln = 1;
+       ib_query->out_sqlda->version = SQLDA_CURRENT_VERSION;
+
+       if (isc_dsql_prepare(IB_STATUS, &ib_query->trans->handle, &ib_query->stmt,
+                       0, query, dialect, ib_query->out_sqlda)) {
+               _php_ibase_error(TSRMLS_C);
+               goto _php_ibase_alloc_query_error;
+       }
+
+       /* find out what kind of statement was prepared */
+       if (isc_dsql_sql_info(IB_STATUS, &ib_query->stmt, sizeof(info_type), 
+                       info_type, sizeof(result), result)) {
+               _php_ibase_error(TSRMLS_C);
+               goto _php_ibase_alloc_query_error;
+       }
+       ib_query->statement_type = result[3];   
+
+       /* not enough output variables ? */
+       if (ib_query->out_sqlda->sqld > ib_query->out_sqlda->sqln) {
+               ib_query->out_sqlda = erealloc(ib_query->out_sqlda, XSQLDA_LENGTH(ib_query->out_sqlda->sqld));
+               ib_query->out_sqlda->sqln = ib_query->out_sqlda->sqld;
+               ib_query->out_sqlda->version = SQLDA_CURRENT_VERSION;
+               if (isc_dsql_describe(IB_STATUS, &ib_query->stmt, SQLDA_CURRENT_VERSION, ib_query->out_sqlda)) {
+                       _php_ibase_error(TSRMLS_C);
+                       goto _php_ibase_alloc_query_error;
+               }
+       }
+
+       /* maybe have input placeholders? */
+       ib_query->in_sqlda = emalloc(XSQLDA_LENGTH(1));
+       ib_query->in_sqlda->sqln = 1;
+       ib_query->in_sqlda->version = SQLDA_CURRENT_VERSION;
+       if (isc_dsql_describe_bind(IB_STATUS, &ib_query->stmt, SQLDA_CURRENT_VERSION, ib_query->in_sqlda)) {
+               _php_ibase_error(TSRMLS_C);
+               goto _php_ibase_alloc_query_error;
+       }
+
+       /* not enough input variables ? */
+       if (ib_query->in_sqlda->sqln < ib_query->in_sqlda->sqld) {
+               ib_query->in_sqlda = erealloc(ib_query->in_sqlda, XSQLDA_LENGTH(ib_query->in_sqlda->sqld));
+               ib_query->in_sqlda->sqln = ib_query->in_sqlda->sqld;
+               ib_query->in_sqlda->version = SQLDA_CURRENT_VERSION;
+
+               if (isc_dsql_describe_bind(IB_STATUS, &ib_query->stmt,
+                               SQLDA_CURRENT_VERSION, ib_query->in_sqlda)) {
+                       _php_ibase_error(TSRMLS_C);
+                       goto _php_ibase_alloc_query_error;
+               }
+       }
+
+       /* no, haven't placeholders at all */
+       if (ib_query->in_sqlda->sqld == 0) {
+               efree(ib_query->in_sqlda);
+               ib_query->in_sqlda = NULL;
+       } else if (FAILURE == _php_ibase_alloc_array(&ib_query->in_array, ib_query->in_sqlda,
+                       link->handle, trans->handle, &ib_query->in_array_cnt TSRMLS_CC)) {
+               goto _php_ibase_alloc_query_error;
+       }
+
+       if (ib_query->out_sqlda->sqld == 0) {
+               efree(ib_query->out_sqlda);
+               ib_query->out_sqlda = NULL;
+       } else  if (FAILURE == _php_ibase_alloc_array(&ib_query->out_array, ib_query->out_sqlda,
+                       link->handle, trans->handle, &ib_query->out_array_cnt TSRMLS_CC)) {
+               goto _php_ibase_alloc_query_error;
+       }
+
+       return SUCCESS;
+
+_php_ibase_alloc_query_error:
+
+       if (ib_query->out_sqlda) {
+               efree(ib_query->out_sqlda);
+       }
+       if (ib_query->in_sqlda) {
+               efree(ib_query->in_sqlda);
+       }
+       if (ib_query->out_array) {
+               efree(ib_query->out_array);
+       }
+       if (ib_query->query) {
+               efree(ib_query->query);
+       }
+       return FAILURE;
+}
+/* }}} */
+
+static int _php_ibase_bind_array(zval *val, char *buf, unsigned long buf_size, /* {{{ */
+       ibase_array *array, int dim TSRMLS_DC)
+{
+       zval null_val, *pnull_val = &null_val;
+       int u_bound = array->ar_desc.array_desc_bounds[dim].array_bound_upper,
+               l_bound = array->ar_desc.array_desc_bounds[dim].array_bound_lower,
+               dim_len = 1 + u_bound - l_bound;
+
+       ZVAL_NULL(pnull_val);
+
+       if (dim < array->ar_desc.array_desc_dimensions) {
+               unsigned long slice_size = buf_size / dim_len;
+               unsigned short i;
+               zval **subval = &val;
+
+               if (Z_TYPE_P(val) == IS_ARRAY) {
+                       zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
+               }
+
+               for (i = 0; i < dim_len; ++i) { 
+
+                       if (Z_TYPE_P(val) == IS_ARRAY &&
+                               zend_hash_get_current_data(Z_ARRVAL_P(val), (void *) &subval) == FAILURE)
+                       {
+                               subval = &pnull_val;
+                       }
+
+                       if (_php_ibase_bind_array(*subval, buf, slice_size, array, dim+1 TSRMLS_CC) == FAILURE)
+                       {
+                               return FAILURE;
+                       }
+                       buf += slice_size;
+
+                       if (Z_TYPE_P(val) == IS_ARRAY) {
+                               zend_hash_move_forward(Z_ARRVAL_P(val));
+                       }
+               }
+
+               if (Z_TYPE_P(val) == IS_ARRAY) {
+                       zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
+               }
+
+       } else {
+               /* expect a single value */
+               if (Z_TYPE_P(val) == IS_NULL) {
+                       memset(buf, 0, buf_size);
+               } else if (array->ar_desc.array_desc_scale < 0) {
+
+                       /* no coercion for array types */
+                       double l;
+
+                       convert_to_double(val);
+
+                       if (Z_DVAL_P(val) > 0) {
+                               l = Z_DVAL_P(val) * pow(10, -array->ar_desc.array_desc_scale) + .5;
+                       } else {
+                               l = Z_DVAL_P(val) * pow(10, -array->ar_desc.array_desc_scale) - .5;
+                       }
+
+                       switch (array->el_type) {
+                               case SQL_SHORT:
+                                       if (l > SHRT_MAX || l < SHRT_MIN) {
+                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
+                                               return FAILURE;
+                                       }
+                                       *(short*) buf = (short) l;
+                                       break;
+                               case SQL_LONG:
+                                       if (l > ISC_LONG_MAX || l < ISC_LONG_MIN) {
+                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
+                                               return FAILURE;
+                                       }
+                                       *(ISC_LONG*) buf = (ISC_LONG) l;
+                                       break;
+#ifdef SQL_INT64
+                               case SQL_INT64:
+                                       {
+                                               long double l;
+
+                                               convert_to_string(val);
+
+                                               if (!sscanf(Z_STRVAL_P(val), "%Lf", &l)) {
+                                                       _php_ibase_module_error("Cannot convert '%s' to long double"
+                                                               TSRMLS_CC, Z_STRVAL_P(val));
+                                                       return FAILURE;
+                                               }
+
+                                               if (l > 0) {
+                                                       *(ISC_INT64 *) buf = (ISC_INT64) (l * pow(10, 
+                                                               -array->ar_desc.array_desc_scale) + .5);
+                                               } else {
+                                                       *(ISC_INT64 *) buf = (ISC_INT64) (l * pow(10, 
+                                                               -array->ar_desc.array_desc_scale) - .5);
+                                               }
+                                       }
+                                       break;
+#endif
+                       }                       
+               } else {
+                       struct tm t = { 0, 0, 0, 0, 0, 0 };
+
+                       switch (array->el_type) {
+                               unsigned short n;
+
+                               case SQL_SHORT:
+                                       convert_to_long(val);
+                                       if (Z_LVAL_P(val) > SHRT_MAX || Z_LVAL_P(val) < SHRT_MIN) {
+                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
+                                               return FAILURE;
+                                       }
+                                       *(short *) buf = (short) Z_LVAL_P(val);
+                                       break;
+                               case SQL_LONG:
+                                       convert_to_long(val);
+#if (SIZEOF_LONG > 4)
+                                       if (Z_LVAL_P(val) > ISC_LONG_MAX || Z_LVAL_P(val) < ISC_LONG_MIN) {
+                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
+                                               return FAILURE;
+                                       }
+#endif
+                                       *(ISC_LONG *) buf = (ISC_LONG) Z_LVAL_P(val);
+                                       break;
+#ifdef SQL_INT64
+                               case SQL_INT64:
+#if (SIZEOF_LONG >= 8)
+                                       convert_to_long(val);
+                                       *(long *) buf = Z_LVAL_P(val);
+#else
+                                       {
+                                               ISC_INT64 l;
+
+                                               convert_to_string(val);
+                                               if (!sscanf(Z_STRVAL_P(val), "%" LL_MASK "d", &l)) {
+                                                       _php_ibase_module_error("Cannot convert '%s' to long integer"
+                                                               TSRMLS_CC, Z_STRVAL_P(val));
+                                                       return FAILURE;
+                                               } else {
+                                                       *(ISC_INT64 *) buf = l;
+                                               }
+                                       }
+#endif
+                                       break;
+#endif
+                               case SQL_FLOAT:
+                                       convert_to_double(val);
+                                       *(float*) buf = (float) Z_DVAL_P(val);
+                                       break;
+                               case SQL_DOUBLE:
+                                       convert_to_double(val);
+                                       *(double*) buf = Z_DVAL_P(val);
+                                       break;
+#ifndef SQL_TIMESTAMP
+                               case SQL_DATE:
+#else
+                               case SQL_TIMESTAMP:
+#endif
+                                       convert_to_string(val);
+#ifdef HAVE_STRPTIME
+                                       strptime(Z_STRVAL_P(val), IBG(timestampformat), &t);
+#else
+                                       n = sscanf(Z_STRVAL_P(val), "%d%*[/]%d%*[/]%d %d%*[:]%d%*[:]%d", 
+                                               &t.tm_mon, &t.tm_mday, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec);
+
+                                       if (n != 3 && n != 6) {
+                                               _php_ibase_module_error("Invalid date/time format (expected 3 or 6 fields, got %d."
+                                                       " Use format 'm/d/Y H:i:s'. You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val));
+                                               return FAILURE;
+                                       }
+                                       t.tm_year -= 1900;
+                                       t.tm_mon--;
+#endif
+#ifndef SQL_TIMESTAMP
+                                       isc_encode_date(&t, (ISC_QUAD *) buf);
+                                       break;
+#else
+                                       isc_encode_timestamp(&t, (ISC_TIMESTAMP * ) buf);
+                                       break;
+                               case SQL_TYPE_DATE:
+                                       convert_to_string(val);
+#ifdef HAVE_STRPTIME
+                                       strptime(Z_STRVAL_P(val), IBG(dateformat), &t);
+#else
+                                       n = sscanf(Z_STRVAL_P(val), "%d%*[/]%d%*[/]%d", &t.tm_mon, &t.tm_mday, &t.tm_year);
+
+                                       if (n != 3) {
+                                               _php_ibase_module_error("Invalid date format (expected 3 fields, got %d. "
+                                                       "Use format 'm/d/Y' You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val));
+                                               return FAILURE;
+                                       }
+                                       t.tm_year -= 1900;
+                                       t.tm_mon--;
+#endif
+                                       isc_encode_sql_date(&t, (ISC_DATE *) buf);
+                                       break;
+                               case SQL_TYPE_TIME:
+                                       convert_to_string(val);
+#ifdef HAVE_STRPTIME
+                                       strptime(Z_STRVAL_P(val), IBG(timeformat), &t);
+#else
+                                       n = sscanf(Z_STRVAL_P(val), "%d%*[:]%d%*[:]%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
+
+                                       if (n != 3) {
+                                               _php_ibase_module_error("Invalid time format (expected 3 fields, got %d. "
+                                                       "Use format 'H:i:s'. You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val));
+                                               return FAILURE;
+                                       }
+#endif
+                                       isc_encode_sql_time(&t, (ISC_TIME *) buf);
+                                       break;
+#endif
+                               default:
+                                       convert_to_string(val);
+                                       strncpy(buf, Z_STRVAL_P(val), array->el_size);
+                                       buf[array->el_size-1] = '\0';
+                       }       
+               }
+       }
+       return SUCCESS;
+}              
+/* }}} */
+
+static int _php_ibase_bind(XSQLDA *sqlda, zval **b_vars, BIND_BUF *buf, /* {{{ */
+       ibase_query *ib_query TSRMLS_DC)
+{
+       int i, array_cnt = 0, rv = SUCCESS;
+
+       for (i = 0; i < sqlda->sqld; ++i) { /* bound vars */
+
+               zval *b_var = b_vars[i];
+               XSQLVAR *var = &sqlda->sqlvar[i];
+
+               var->sqlind = &buf[i].sqlind;
+
+               if (Z_TYPE_P(b_var) == IS_NULL) {
+                       if ((var->sqltype & 1) != 1) {
+                               _php_ibase_module_error("Parameter %d must have a value" TSRMLS_CC, i+1);
+                               rv = FAILURE;
+                       }
+                       buf[i].sqlind = -1;
+                       if ((var->sqltype & ~1) == SQL_ARRAY) ++array_cnt;
+               } else {
+                       buf[i].sqlind = 0;
+
+                       if (var->sqlscale < 0) {
+                               /*
+                                 DECIMAL or NUMERIC field are stored internally as scaled integers.
+                                 Coerce it to string and let InterBase's internal routines handle it.
+                               */
+                               var->sqltype = SQL_TEXT;
+                       }
+
+                       var->sqldata = (void*)&buf[i];
+
+                       switch (var->sqltype & ~1) {
+                               struct tm t;
+
+                               case SQL_SHORT:
+                                       convert_to_long(b_var);
+                                       if (Z_LVAL_P(b_var) > SHRT_MAX || Z_LVAL_P(b_var) < SHRT_MIN) {
+                                               _php_ibase_module_error("Parameter %d exceeds field width" TSRMLS_CC, i+1);
+                                               rv = FAILURE;
+                                               break;
+                                       }
+                                       buf[i].val.sval = (short) Z_LVAL_P(b_var);
+                                       break;
+                               case SQL_LONG:
+                                       convert_to_long(b_var);
+#if (SIZEOF_LONG > 4)
+                                       /* ISC_LONG is always 32-bit */
+                                       if (Z_LVAL_P(b_var) > ISC_LONG_MAX || Z_LVAL_P(b_var) < ISC_LONG_MIN) {
+                                               _php_ibase_module_error("Parameter %d exceeds field width" TSRMLS_CC, i+1);
+                                               rv = FAILURE;
+                                               break;
+                                       }
+#endif
+                                       buf[i].val.lval = (ISC_LONG) Z_LVAL_P(b_var);
+                                       break;
+#if defined(SQL_INT64) && (SIZEOF_LONG == 8)
+                               case SQL_INT64:
+                                       convert_to_long(b_var);
+                                       var->sqldata = (void *) &Z_LVAL_P(b_var);
+                                       break;
+#endif
+                               case SQL_FLOAT:
+                                        convert_to_double(b_var);
+                                       buf[i].val.fval = (float) Z_DVAL_P(b_var);
+                                       break;
+                               case SQL_DOUBLE:
+                                       convert_to_double(b_var);
+                                       var->sqldata = (void *) &Z_DVAL_P(b_var);
+                                       break;
+
+                               case SQL_DATE: /* == SQL_TIMESTAMP: */
+#ifdef SQL_TIMESTAMP
+                               case SQL_TYPE_DATE:
+                               case SQL_TYPE_TIME:
+#endif
+                                       if (Z_TYPE_P(b_var) == IS_LONG) {
+                                               /* insert timestamp directly */
+                                               t = *gmtime(&Z_LVAL_P(b_var));
+                                       } else {
+#ifndef HAVE_STRPTIME
+#ifndef SQL_TIMESTAMP
+                                               int n;
+
+                                               t.tm_year = t.tm_mon = t.tm_mday = t.tm_hour = t.tm_min = t.tm_sec = 0;
+
+                                               n = sscanf(Z_STRVAL_P(b_var), "%d%*[/]%d%*[/]%d %d%*[:]%d%*[:]%d",
+                                                       &t.tm_mon, &t.tm_mday, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec);
+
+                                               if (n != 3 && n != 6) {
+                                                       _php_ibase_module_error("Parameter %d: invalid date/time format "
+                                                               "(expected 3 or 6 fields, got %d. Use format m/d/Y H:i:s. You gave '%s')"
+                                                               TSRMLS_CC, i+1, n, Z_STRVAL_P(b_var));
+                                                       rv = FAILURE;
+                                                       break;
+                                               }
+                                               t.tm_year -= 1900;
+                                               t.tm_mon--;
+#else
+                                               goto php_ibase_bind_default; /* let IB string handling take over */
+#endif
+#else
+                                               convert_to_string(b_var);
+
+                                               switch (var->sqltype & ~1) {
+                                                       default: /* == case SQL_TIMESTAMP/SQL_DATE: */
+                                                               strptime(Z_STRVAL_P(b_var), IBG(timestampformat), &t);
+                                                               break;
+                                                       case SQL_TYPE_DATE:
+                                                               strptime(Z_STRVAL_P(b_var), IBG(dateformat), &t);
+                                                               break;
+                                                       case SQL_TYPE_TIME:
+                                                               strptime(Z_STRVAL_P(b_var), IBG(timeformat), &t);
+                                                               break;
+                                               }
+#endif
+                                       }
+
+#ifndef SQL_TIMESTAMP
+                                       isc_encode_date(&t, &buf[i].val.qval);
+#else
+                                       switch (var->sqltype & ~1) {
+                                               default: /* == case SQL_TIMESTAMP */
+                                                       isc_encode_timestamp(&t, &buf[i].val.tsval);
+                                                       break;
+                                               case SQL_TYPE_DATE:
+                                                       isc_encode_sql_date(&t, &buf[i].val.dtval);
+                                                       break;
+                                               case SQL_TYPE_TIME:
+                                                       isc_encode_sql_time(&t, &buf[i].val.tmval);
+                                                       break;
+#endif
+                                       }
+                                       break;
+                               case SQL_BLOB:
+
+                                       convert_to_string(b_var);
+
+                                       if (Z_STRLEN_P(b_var) != BLOB_ID_LEN ||
+                                               !_php_ibase_string_to_quad(Z_STRVAL_P(b_var), &buf[i].val.qval)) {
+
+                                               ibase_blob ib_blob = { NULL, BLOB_INPUT };
+
+                                               if (isc_create_blob(IB_STATUS, &ib_query->link->handle,
+                                                               &ib_query->trans->handle, &ib_blob.bl_handle, &ib_blob.bl_qd)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       return FAILURE;
+                                               }
+
+                                               if (_php_ibase_blob_add(&b_var, &ib_blob TSRMLS_CC) != SUCCESS) {
+                                                       return FAILURE;
+                                               }
+
+                                               if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       return FAILURE;
+                                               }
+                                               buf[i].val.qval = ib_blob.bl_qd;
+                                       }
+                                       break;
+                               case SQL_ARRAY:
+                                       if (Z_TYPE_P(b_var) != IS_ARRAY) {
+                                               convert_to_string(b_var);
+
+                                               if (Z_STRLEN_P(b_var) != BLOB_ID_LEN ||
+                                                       !_php_ibase_string_to_quad(Z_STRVAL_P(b_var), &buf[i].val.qval)) {
+
+                                                       _php_ibase_module_error("Parameter %d: invalid array ID" TSRMLS_CC,i+1);
+                                                       rv = FAILURE;
+                                               }
+                                       } else {
+                                               /* convert the array data into something IB can understand */
+                                               ibase_array *ar = &ib_query->in_array[array_cnt++];
+                                               void *array_data = emalloc(ar->ar_size);
+                                               ISC_QUAD array_id = { 0, 0 };
+
+                                               if (FAILURE == _php_ibase_bind_array(b_var, array_data, ar->ar_size, 
+                                                               ar, 0 TSRMLS_CC)) {
+                                                       _php_ibase_module_error("Parameter %d: failed to bind array argument"
+                                                               TSRMLS_CC,i+1);
+                                                       efree(array_data);
+                                                       rv = FAILURE;
+                                                       break;
+                                               }
+
+                                               if (isc_array_put_slice(IB_STATUS, &ib_query->link->handle, &ib_query->trans->handle, 
+                                                               &array_id, &ar->ar_desc, array_data, &ar->ar_size)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       efree(array_data);
+                                                       return FAILURE;
+                                               }
+                                               buf[i].val.qval = array_id;
+                                               efree(array_data);
+                                       }                               
+                                       break;
+                               default:
+php_ibase_bind_default:
+                                       convert_to_string(b_var);
+                                       var->sqldata = Z_STRVAL_P(b_var);
+                                       var->sqllen      = Z_STRLEN_P(b_var);
+                                       var->sqltype = SQL_TEXT;
+                       } /* switch */
+               } /* if */
+       } /* for */
+       return rv;
+}
+/* }}} */
+
+static void _php_ibase_alloc_xsqlda(XSQLDA *sqlda) /* {{{ */
+{
+       int i;
+
+       for (i = 0; i < sqlda->sqld; i++) {
+               XSQLVAR *var = &sqlda->sqlvar[i];
+
+               switch (var->sqltype & ~1) {
+                       case SQL_TEXT:
+                               var->sqldata = safe_emalloc(sizeof(char), var->sqllen, 0);
+                               break;
+                       case SQL_VARYING:
+                               var->sqldata = safe_emalloc(sizeof(char), var->sqllen + sizeof(short), 0);
+                               break;
+                       case SQL_SHORT:
+                               var->sqldata = emalloc(sizeof(short));
+                               break;
+                       case SQL_LONG:
+                               var->sqldata = emalloc(sizeof(ISC_LONG));
+                               break;
+                       case SQL_FLOAT:
+                               var->sqldata = emalloc(sizeof(float));
+                                       break;
+                       case SQL_DOUBLE:
+                               var->sqldata = emalloc(sizeof(double));
+                               break;
+#ifdef SQL_INT64
+                       case SQL_INT64:
+                               var->sqldata = emalloc(sizeof(ISC_INT64));
+                               break;
+#endif
+#ifdef SQL_TIMESTAMP
+                       case SQL_TIMESTAMP:
+                               var->sqldata = emalloc(sizeof(ISC_TIMESTAMP));
+                               break;
+                       case SQL_TYPE_DATE:
+                               var->sqldata = emalloc(sizeof(ISC_DATE));
+                               break;
+                       case SQL_TYPE_TIME:
+                               var->sqldata = emalloc(sizeof(ISC_TIME));
+                               break;
+#else
+                       case SQL_DATE:
+#endif
+                       case SQL_BLOB:
+                       case SQL_ARRAY:
+                               var->sqldata = emalloc(sizeof(ISC_QUAD));
+                               break;
+               } /* switch */
+
+               if (var->sqltype & 1) { /* sql NULL flag */
+                       var->sqlind = emalloc(sizeof(short));
+               } else {
+                       var->sqlind = NULL;
+               }
+       } /* for */
+}
+/* }}} */
+
+static int _php_ibase_exec(INTERNAL_FUNCTION_PARAMETERS, ibase_result **ib_resultp, /* {{{ */
+       ibase_query *ib_query, int argc, zval **args)
+{
+       XSQLDA *in_sqlda = NULL, *out_sqlda = NULL;
+       BIND_BUF *bind_buf = NULL;
+       int rv = FAILURE;
+       static char info_count[] = { isc_info_sql_records };
+       char result[64];
+       ISC_STATUS isc_result;
+
+       RESET_ERRMSG;
+
+       if (argc > 0 && args != NULL) {
+               SEPARATE_ZVAL(args);
+       }
+
+       switch (ib_query->statement_type) {
+               isc_tr_handle tr;
+               ibase_tr_list **l;
+               ibase_trans *trans;
+
+               case isc_info_sql_stmt_start_trans:
+
+                       /* a SET TRANSACTION statement should be executed with a NULL trans handle */
+                       tr = NULL;
+
+                       if (isc_dsql_execute_immediate(IB_STATUS, &ib_query->link->handle, &tr, 0, 
+                                       ib_query->query, ib_query->dialect, NULL)) {
+                               _php_ibase_error(TSRMLS_C);
+                               goto _php_ibase_exec_error;
+                       }
+
+                       trans = (ibase_trans *) emalloc(sizeof(ibase_trans));
+                       trans->handle = tr;
+                       trans->link_cnt = 1;
+                       trans->affected_rows = 0;
+                       trans->db_link[0] = ib_query->link;
+
+                       if (ib_query->link->tr_list == NULL) {
+                               ib_query->link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
+                               ib_query->link->tr_list->trans = NULL;
+                               ib_query->link->tr_list->next = NULL;
+                       }
+
+                       /* link the transaction into the connection-transaction list */
+                       for (l = &ib_query->link->tr_list; *l != NULL; l = &(*l)->next);
+                       *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
+                       (*l)->trans = trans;
+                       (*l)->next = NULL;
+
+                       ZEND_REGISTER_RESOURCE(return_value, trans, le_trans);
+
+                       return SUCCESS;
+
+               case isc_info_sql_stmt_commit:
+               case isc_info_sql_stmt_rollback:
+
+                       if (isc_dsql_execute_immediate(IB_STATUS, &ib_query->link->handle, 
+                                       &ib_query->trans->handle, 0, ib_query->query, ib_query->dialect, NULL)) {
+                               _php_ibase_error(TSRMLS_C);
+                               goto _php_ibase_exec_error;
+                       }
+
+                       if (ib_query->trans->handle == NULL && ib_query->trans_res_id != 0) {
+                               /* transaction was released by the query and was a registered resource, 
+                                  so we have to release it */
+                               zend_list_delete(ib_query->trans_res_id);
+                       }
+                       return SUCCESS;
+
+               default:
+                       RETVAL_TRUE;
+       }
+
+       /* allocate sqlda and output buffers */
+       if (ib_query->out_sqlda) { /* output variables in select, select for update */
+               ibase_result *res;
+
+               IBDEBUG("Query wants XSQLDA for output");
+               res = emalloc(sizeof(ibase_result)+sizeof(ibase_array)*max(0,ib_query->out_array_cnt-1));
+               res->link = ib_query->link;
+               res->trans = ib_query->trans;
+               res->stmt = ib_query->stmt; 
+               res->statement_type = ib_query->statement_type;
+               res->has_more_rows = 1;
+
+               out_sqlda = res->out_sqlda = emalloc(XSQLDA_LENGTH(ib_query->out_sqlda->sqld));
+               memcpy(out_sqlda, ib_query->out_sqlda, XSQLDA_LENGTH(ib_query->out_sqlda->sqld));
+               _php_ibase_alloc_xsqlda(out_sqlda);
+
+               if (ib_query->out_array) {
+                       memcpy(&res->out_array, ib_query->out_array, sizeof(ibase_array)*ib_query->out_array_cnt);
+               }
+               *ib_resultp = res;
+       }
+
+       if (ib_query->in_sqlda) { /* has placeholders */
+               IBDEBUG("Query wants XSQLDA for input");
+               if (ib_query->in_sqlda->sqld != argc) {
+                       _php_ibase_module_error("Placeholders (%d) and variables (%d) mismatch"
+                               TSRMLS_CC, ib_query->in_sqlda->sqld, argc);
+                       goto _php_ibase_exec_error;
+               }
+               in_sqlda = emalloc(XSQLDA_LENGTH(ib_query->in_sqlda->sqld));
+               memcpy(in_sqlda, ib_query->in_sqlda, XSQLDA_LENGTH(ib_query->in_sqlda->sqld));
+               bind_buf = safe_emalloc(sizeof(BIND_BUF), ib_query->in_sqlda->sqld, 0);
+               if (_php_ibase_bind(in_sqlda, args, bind_buf, ib_query TSRMLS_CC) == FAILURE) {
+                       IBDEBUG("Could not bind input XSQLDA");
+                       goto _php_ibase_exec_error;
+               }
+       }
+
+       if (ib_query->statement_type == isc_info_sql_stmt_exec_procedure) {
+               isc_result = isc_dsql_execute2(IB_STATUS, &ib_query->trans->handle,
+                       &ib_query->stmt, SQLDA_CURRENT_VERSION, in_sqlda, out_sqlda);
+       } else {
+               isc_result = isc_dsql_execute(IB_STATUS, &ib_query->trans->handle,
+                       &ib_query->stmt, SQLDA_CURRENT_VERSION, in_sqlda);
+       }
+       if (isc_result) {
+               IBDEBUG("Could not execute query");
+               _php_ibase_error(TSRMLS_C);
+               goto _php_ibase_exec_error;
+       }
+       ib_query->trans->affected_rows = 0;
+
+       switch (ib_query->statement_type) {
+
+               unsigned long affected_rows;
+
+               case isc_info_sql_stmt_insert:
+               case isc_info_sql_stmt_update:
+               case isc_info_sql_stmt_delete:
+               case isc_info_sql_stmt_exec_procedure:
+
+                       if (isc_dsql_sql_info(IB_STATUS, &ib_query->stmt, sizeof(info_count),
+                                       info_count, sizeof(result), result)) {
+                               _php_ibase_error(TSRMLS_C);
+                               goto _php_ibase_exec_error;
+                       }
+
+                       affected_rows = 0;
+
+                       if (result[0] == isc_info_sql_records) {
+                               unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
+
+                               while (result[i] != isc_info_end && i < result_size) {
+                                       short len = (short)isc_vax_integer(&result[i+1],2);
+                                       if (result[i] != isc_info_req_select_count) {
+                                               affected_rows += isc_vax_integer(&result[i+3],len);
+                                       }
+                                       i += len+3;
+                               }
+                       }
+                       if (affected_rows > 0) {
+                               ib_query->trans->affected_rows = affected_rows;
+                               RETVAL_LONG(affected_rows);
+                       }
+       }
+
+       rv = SUCCESS;
+
+_php_ibase_exec_error:
+
+       if (in_sqlda) {
+               efree(in_sqlda);
+       }
+       if (bind_buf)
+               efree(bind_buf);
+
+       if (rv == FAILURE) {
+               if (*ib_resultp) {
+                       efree(*ib_resultp);
+                       *ib_resultp = NULL;
+               }
+               if (out_sqlda) {
+                       _php_ibase_free_xsqlda(out_sqlda);
+               }
+       }
+
+       return rv;
+}
+/* }}} */
+
+/* {{{ proto mixed ibase_query([resource link_identifier, [ resource link_identifier, ]] string query [, mixed bind_arg [, mixed bind_arg [, ...]]])
+   Execute a query */
+PHP_FUNCTION(ibase_query)
+{
+       zval ***args, **bind_args = NULL;
+       int i, bind_n = 0, trans_res_id = 0;
+       ibase_db_link *ib_link = NULL;
+       ibase_trans *trans = NULL;
+       ibase_query ib_query = { NULL, NULL, 0, 0 };
+       ibase_result *result = NULL;
+       char *query;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() < 1) {
+               WRONG_PARAM_COUNT;
+       }
+
+       /* use stack to avoid leaks */
+       args = (zval ***) do_alloca(sizeof(zval **) * ZEND_NUM_ARGS());
+
+       RETVAL_FALSE;
+
+       if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
+               goto ibase_query_end;
+
+       }
+
+       i = 0;
+       while (Z_TYPE_PP(args[i++]) != IS_STRING) {
+               if (i >= ZEND_NUM_ARGS()) {
+                       _php_ibase_module_error("Query argument missing" TSRMLS_CC);
+                       goto ibase_query_end;
+
+               }
+       }
+
+       convert_to_string_ex(args[i-1]);
+       query = Z_STRVAL_PP(args[i-1]);
+
+       /* find out if the first one or two arguments refer to either a link id, 
+          a trans id or both */
+       switch (i) {
+               case 1:
+
+                       /* no link ids were passed: if there's no default link, use exec_immediate() with
+                          a NULL handle; this will enable the use of CREATE DATABASE statements. */
+                       if (IBG(default_link) == -1) {
+                               isc_db_handle db = NULL;
+                               isc_tr_handle trans = NULL;
+
+                               if (isc_dsql_execute_immediate(IB_STATUS, &db, &trans, 0, query, 
+                                               SQL_DIALECT_CURRENT, NULL)) {
+                                       _php_ibase_error(TSRMLS_C);
+                                       goto ibase_query_end;
+                               }
+
+                               /* has a new database been created ? */
+                               if (db != NULL) {
+
+                                       if ((IBG(max_links) != -1) && (IBG(num_links) >= IBG(max_links))) {                                     
+
+                                               /* too many links already ? => close it up immediately */
+                                               if (isc_detach_database(IB_STATUS, &db)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       goto ibase_query_end;
+                                               }
+                                       } else {
+
+                                               /* register the link as a resource; unfortunately, we cannot register 
+                                                  it in the hash table, because we don't know the connection params */
+                                               ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link));
+                                               ib_link->handle = db;
+                                               ib_link->dialect = SQL_DIALECT_CURRENT;
+                                               ib_link->tr_list = NULL;
+                                               ib_link->event_head = NULL;
+
+                                               ZEND_REGISTER_RESOURCE(return_value, ib_link, le_link);
+                                               zend_list_addref(Z_LVAL_P(return_value));
+                                               IBG(default_link) = Z_LVAL_P(return_value);
+                                               IBG(num_links)++;
+                                               goto ibase_query_end;
+                                       }
+                               }
+                               RETURN_TRUE;
+                       }                                       
+
+                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link",
+                               le_link, le_plink);
+                       break;                  
+               case 2:
+                       /* one id was passed, could be db or trans id */
+                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, args[0], &ib_link, &trans);
+
+                       if (trans != NULL) {
+                               /* argument was a trans id */
+                               convert_to_long_ex(args[0]);
+                               trans_res_id = Z_LVAL_PP(args[0]);
+                       }
+                       break;  
+               case 3:
+                       /* two ids were passed, first should be link and second should be trans; */
+                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link*, args[0], -1, "InterBase link",
+                               le_link, le_plink);
+                       ZEND_FETCH_RESOURCE(trans, ibase_trans*, args[1], -1, "InterBase transaction", le_trans);
+
+                       convert_to_long_ex(args[1]);
+                       trans_res_id = Z_LVAL_PP(args[1]);
+
+                       break;
+               default:
+                       /* more than two arguments preceed the SQL string */
+                       _php_ibase_module_error("Invalid arguments" TSRMLS_CC);
+                       goto ibase_query_end;
+       }
+
+       if (ZEND_NUM_ARGS() > i) { /* have variables to bind */
+               bind_n = ZEND_NUM_ARGS() - i;
+               bind_args = args[i];
+       }
+
+       /* open default transaction */
+       if (ib_link == NULL || _php_ibase_def_trans(ib_link, &trans TSRMLS_CC) == FAILURE) {
+               goto ibase_query_end;
+       }
+
+       if (FAILURE == _php_ibase_alloc_query(&ib_query, ib_link, trans, query, ib_link->dialect,
+                       trans_res_id TSRMLS_CC)) {
+               goto ibase_query_end;
+       }
+
+       do {
+               if (FAILURE == _php_ibase_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, &result, &ib_query, 
+                               bind_n, bind_args)) {
+                       break;
+               }
+
+               if (result != NULL) { /* statement returns a result */
+                       result->type = QUERY_RESULT;    
+
+                       /* EXECUTE PROCEDURE returns only one row => statement can be released immediately */
+                       if (ib_query.statement_type != isc_info_sql_stmt_exec_procedure) {
+                               ib_query.stmt = NULL; /* keep stmt when free query */
+                       }
+                       ZEND_REGISTER_RESOURCE(return_value, result, le_result);
+               }
+       } while (0);
+       _php_ibase_free_query(&ib_query TSRMLS_CC);
+
+ibase_query_end:
+       free_alloca(args);
+}
+/* }}} */
+
+/* {{{ proto int ibase_affected_rows( [ resource link_identifier ] )
+   Returns the number of rows affected by the previous INSERT, UPDATE or DELETE statement */
+PHP_FUNCTION(ibase_affected_rows)
+{
+       ibase_trans *trans = NULL;
+
+       RESET_ERRMSG;
+
+       switch (ZEND_NUM_ARGS()) {
+
+               ibase_db_link *ib_link;
+               zval **arg;
+
+               case 0:
+                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link",
+                               le_link, le_plink);
+                       if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
+                               RETURN_FALSE;
+                       }
+                       trans = ib_link->tr_list->trans;
+                       break;
+
+               case 1: 
+                       if (zend_get_parameters_ex(1, &arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       /* one id was passed, could be db or trans id */
+                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, arg, &ib_link, &trans);
+                       if (trans == NULL) {                    
+                               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, arg, -1, "InterBase link",
+                                       le_link, le_plink);
+
+                               if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
+                                       RETURN_FALSE;
+                               }
+                               trans = ib_link->tr_list->trans;
+                       }
+                       break;
+
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+       RETURN_LONG(trans->affected_rows);
+}
+/* }}} */
+
+/* {{{ proto int ibase_num_rows( resource result_identifier ) 
+   Return the number of rows that are available in a result */
+#if abies_0
+PHP_FUNCTION(ibase_num_rows) 
+{
+       /**
+        * As this function relies on the InterBase API function isc_dsql_sql_info()
+        * which has a couple of limitations (which I hope will be fixed in future 
+        * releases of Firebird), this function is fairly useless. I'm leaving it
+        * in place for people who can live with the limitations, which I only 
+        * found out about after I had implemented it anyway.
+        *
+        * Currently, there's no way to determine how many rows can be fetched from
+        * a cursor. The only number that _can_ be determined is the number of rows
+        * that have already been pre-fetched by the client library. 
+        * This implies the following:
+        * - num_rows() always returns zero before the first fetch;
+        * - num_rows() for SELECT ... FOR UPDATE is broken -> never returns a
+        *   higher number than the number of records fetched so far (no pre-fetch);
+        * - the result of num_rows() for other statements is merely a lower bound 
+        *   on the number of records => calling ibase_num_rows() again after a couple
+        *   of fetches will most likely return a new (higher) figure for large result 
+        *   sets.
+        */
+
+       zval **result_arg;
+       ibase_result *ib_result;
+       static char info_count[] = {isc_info_sql_records};
+       char result[64];
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &result_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
+
+       if (isc_dsql_sql_info(IB_STATUS, &ib_result->stmt, sizeof(info_count), info_count, 
+                       sizeof(result), result)) {
+               _php_ibase_error(TSRMLS_C);
+               RETURN_FALSE;
+       }
+
+       if (result[0] == isc_info_sql_records) {
+               unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
+
+               while (result[i] != isc_info_end && i < result_size) {
+                       short len = (short)isc_vax_integer(&result[i+1],2);
+                       if (result[i] == isc_info_req_select_count) {
+                               RETURN_LONG(isc_vax_integer(&result[i+3],len));
+                       }
+                       i += len+3;
+               }
+       }                                       
+}
+#endif
+/* }}} */
+
+static int _php_ibase_var_zval(zval *val, void *data, int type, int len, /* {{{ */
+       int scale, int flag TSRMLS_DC)
+{
+#ifdef SQL_INT64
+       static ISC_INT64 const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000, 
+               1000000000, LL_LIT(10000000000),LL_LIT(100000000000),LL_LIT(10000000000000),LL_LIT(100000000000000),
+               LL_LIT(1000000000000000),LL_LIT(1000000000000000),LL_LIT(1000000000000000000) };
+#else 
+       static long const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000,
+               1000000000 };
+#endif         
+
+       switch (type & ~1) {
+               unsigned short l;
+               long n;
+               char string_data[255];
+               struct tm t;
+               char *format;
+
+               case SQL_VARYING:
+                       len = ((IBVARY *) data)->vary_length;
+                       data = ((IBVARY *) data)->vary_string;
+                       /* no break */
+               case SQL_TEXT:
+                       if (PG(magic_quotes_runtime)) {
+                               Z_STRVAL_P(val) = php_addslashes(data, len, &Z_STRLEN_P(val), 0 TSRMLS_CC);
+                               Z_TYPE_P(val) = IS_STRING;
+                       } else {
+                               ZVAL_STRINGL(val,(char *) data,len,1);
+                       }
+                       break;
+               case SQL_SHORT:
+                       n = *(short *) data;
+                       goto _sql_long;
+#ifdef SQL_INT64
+               case SQL_INT64:
+#if (SIZEOF_LONG >= 8)
+                       n = *(long *) data;
+                       goto _sql_long;
+#else
+                       if (scale == 0) {
+                               l = sprintf(string_data, "%" LL_MASK "d", *(ISC_INT64 *) data);
+                               ZVAL_STRINGL(val,string_data,l,1);
+                       } else {
+                               ISC_INT64 n = *(ISC_INT64 *) data, f = scales[-scale];
+
+                               if (n >= 0) {
+                                       l = sprintf(string_data, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -scale, n % f);
+                               } else if (n < -f) {
+                                       l = sprintf(string_data, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -scale, -n % f);                               
+                                } else {
+                                       l = sprintf(string_data, "-0.%0*" LL_MASK "d", -scale, -n % f);
+                               }
+                               ZVAL_STRINGL(val,string_data,l,1);
+                       }
+                       break;
+#endif
+#endif
+               case SQL_LONG:
+                       n = *(ISC_LONG *) data; 
+               _sql_long:
+                       if (scale == 0) {
+                               ZVAL_LONG(val,n);
+                       } else {
+                               long f = (long) scales[-scale];
+
+                               if (n >= 0) {
+                                       l = sprintf(string_data, "%ld.%0*ld", n / f, -scale,  n % f);
+                               } else if (n < -f) {
+                                       l = sprintf(string_data, "%ld.%0*ld", n / f, -scale,  -n % f);
+                               } else {
+                                       l = sprintf(string_data, "-0.%0*ld", -scale, -n % f);
+                               }
+                               ZVAL_STRINGL(val,string_data,l,1);
+                       }
+                       break;
+               case SQL_FLOAT:
+                       ZVAL_DOUBLE(val, *(float *) data);
+                       break;
+               case SQL_DOUBLE:
+                       ZVAL_DOUBLE(val, *(double *) data);
+                       break;
+               case SQL_DATE: /* == case SQL_TIMESTAMP: */
+                       format = IBG(timestampformat);
+#ifndef SQL_TIMESTAMP
+                       isc_decode_date((ISC_QUAD *) data, &t);
+#else
+                       isc_decode_timestamp((ISC_TIMESTAMP *) data, &t);
+                       goto format_date_time;
+               case SQL_TYPE_DATE:
+                       format = IBG(dateformat);
+                       isc_decode_sql_date((ISC_DATE *) data, &t);
+                       goto format_date_time;
+               case SQL_TYPE_TIME:
+                       format = IBG(timeformat);
+                       isc_decode_sql_time((ISC_TIME *) data, &t);
+
+format_date_time:
+#endif
+                       /*
+                         XXX - Might have to remove this later - seems that isc_decode_date()
+                          always sets tm_isdst to 0, sometimes incorrectly (InterBase 6 bug?)
+                       */
+                       t.tm_isdst = -1;
+#if HAVE_TM_ZONE
+                       t.tm_zone = tzname[0];
+#endif
+                       if (flag & PHP_IBASE_UNIXTIME) {
+                               ZVAL_LONG(val, mktime(&t));
+                       } else {
+#if HAVE_STRFTIME
+                               l = strftime(string_data, sizeof(string_data), format, &t);
+#else
+                               switch (type & ~1) {
+                                       default:
+                                               l = sprintf(string_data, "%02d/%02d/%4d %02d:%02d:%02d", t.tm_mon+1, t.tm_mday, 
+                                                       t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
+                                               break;
+#ifdef SQL_TIMESTAMP
+                                       case SQL_TYPE_DATE:
+                                               l = sprintf(string_data, "%02d/%02d/%4d", t.tm_mon + 1, t.tm_mday, t.tm_year+1900);
+                                               break;
+                                       case SQL_TYPE_TIME:
+                                               l = sprintf(string_data, "%02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec);
+                                               break;
+#endif
+                               }
+#endif
+                               ZVAL_STRINGL(val,string_data,l,1);
+                               break;
+                       }
+       } /* switch (type) */
+       return SUCCESS;
+}
+/* }}} */
+
+static int _php_ibase_arr_zval(zval *ar_zval, char *data, unsigned long data_size, /* {{{ */
+       ibase_array *ib_array, int dim, int flag TSRMLS_DC)
+{
+       /**
+        * Create multidimension array - recursion function
+        */
+       int 
+               u_bound = ib_array->ar_desc.array_desc_bounds[dim].array_bound_upper,
+               l_bound = ib_array->ar_desc.array_desc_bounds[dim].array_bound_lower,
+               dim_len = 1 + u_bound - l_bound;
+       unsigned short i;
+
+       if (dim < ib_array->ar_desc.array_desc_dimensions) { /* array again */
+               unsigned long slice_size = data_size / dim_len;
+
+               array_init(ar_zval);
+
+               for (i = 0; i < dim_len; ++i) {
+                       zval *slice_zval;
+                       ALLOC_INIT_ZVAL(slice_zval);
+
+                       /* recursion here */
+                       if (FAILURE == _php_ibase_arr_zval(slice_zval, data, slice_size, ib_array, dim + 1,
+                                       flag TSRMLS_CC)) {
+                               return FAILURE;
+                       }
+                       data += slice_size;
+
+                       add_index_zval(ar_zval,l_bound+i,slice_zval);
+               }
+       } else { /* data at last */
+
+               if (FAILURE == _php_ibase_var_zval(ar_zval, data, ib_array->el_type,
+                               ib_array->ar_desc.array_desc_length, ib_array->ar_desc.array_desc_scale, flag TSRMLS_CC)) {
+                       return FAILURE;
+               }
+
+               /* fix for peculiar handling of VARCHAR arrays;
+                  truncate the field to the cstring length */
+               if (ib_array->ar_desc.array_desc_dtype == blr_varying ||
+                       ib_array->ar_desc.array_desc_dtype == blr_varying2) {
+
+                       Z_STRLEN_P(ar_zval) = strlen(Z_STRVAL_P(ar_zval));
+               }
+       }
+       return SUCCESS;
+}
+/* }}} */
+
+static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type) /* {{{ */
+{
+       zval **result_arg, **flag_arg;
+       long i, array_cnt = 0, flag = 0;
+       ibase_result *ib_result;
+
+       RESET_ERRMSG;
+
+       switch (ZEND_NUM_ARGS()) {
+               case 1:
+                       if (FAILURE == zend_get_parameters_ex(1, &result_arg)) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               case 2:
+                       if (FAILURE == zend_get_parameters_ex(2, &result_arg, &flag_arg)) {
+                               RETURN_FALSE;
+                       }
+                       convert_to_long_ex(flag_arg);
+                       flag = Z_LVAL_PP(flag_arg);
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
+
+       if (ib_result->out_sqlda == NULL || !ib_result->has_more_rows) {
+               RETURN_FALSE;
+       }
+
+       if (ib_result->statement_type != isc_info_sql_stmt_exec_procedure) {
+
+               if (isc_dsql_fetch(IB_STATUS, &ib_result->stmt, 1, ib_result->out_sqlda)) {
+
+                       ib_result->has_more_rows = 0;
+                       if (IB_STATUS[0] && IB_STATUS[1]) { /* error in fetch */
+                               _php_ibase_error(TSRMLS_C);
+                       }
+                       RETURN_FALSE;
+               }
+       } else {
+               ib_result->has_more_rows = 0;
+       }       
+
+       array_init(return_value);
+
+       for (i = 0; i < ib_result->out_sqlda->sqld; ++i) {
+               XSQLVAR *var = &ib_result->out_sqlda->sqlvar[i];
+
+               if (((var->sqltype & 1) == 0) || *var->sqlind != -1) {
+                       zval *result;
+                       ALLOC_INIT_ZVAL(result);
+
+                       switch (var->sqltype & ~1) {
+
+                               default:
+                                       _php_ibase_var_zval(result, var->sqldata, var->sqltype, var->sqllen,
+                                               var->sqlscale, flag TSRMLS_CC);
+                                       break;
+                               case SQL_BLOB:
+                                       if (flag & PHP_IBASE_FETCH_BLOBS) { /* fetch blob contents into hash */
+
+                                               ibase_blob blob_handle;
+                                               unsigned long max_len = 0;
+                                               static char bl_items[] = {isc_info_blob_total_length};
+                                               char bl_info[20];
+                                               unsigned short i;
+
+                                               blob_handle.bl_handle = NULL;
+                                               blob_handle.bl_qd = *(ISC_QUAD *) var->sqldata;
+
+                                               if (isc_open_blob(IB_STATUS, &ib_result->link->handle, &ib_result->trans->handle,
+                                                               &blob_handle.bl_handle, &blob_handle.bl_qd)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       goto _php_ibase_fetch_error;
+                                               }
+
+                                               if (isc_blob_info(IB_STATUS, &blob_handle.bl_handle, sizeof(bl_items),
+                                                               bl_items, sizeof(bl_info), bl_info)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       goto _php_ibase_fetch_error;
+                                               }
+
+                                               /* find total length of blob's data */
+                                               for (i = 0; i < sizeof(bl_info); ) {
+                                                       unsigned short item_len;
+                                                       char item = bl_info[i++];
+
+                                                       if (item == isc_info_end || item == isc_info_truncated || 
+                                                               item == isc_info_error || i >= sizeof(bl_info)) {
+
+                                                               _php_ibase_module_error("Could not determine BLOB size (internal error)"
+                                                                       TSRMLS_CC);
+                                                               goto _php_ibase_fetch_error;
+                                                       }                                                               
+
+                                                       item_len = (unsigned short) isc_vax_integer(&bl_info[i], 2);
+
+                                                       if (item == isc_info_blob_total_length) {
+                                                               max_len = isc_vax_integer(&bl_info[i+2], item_len);
+                                                               break;
+                                                       }
+                                                       i += item_len+2;
+                                               }
+
+                                               if (max_len == 0) {
+                                                       ZVAL_STRING(result, "", 1);
+                                               } else if (SUCCESS != _php_ibase_blob_get(result, &blob_handle, 
+                                                               max_len TSRMLS_CC)) {
+                                                       goto _php_ibase_fetch_error;
+                                               }
+
+                                               if (isc_close_blob(IB_STATUS, &blob_handle.bl_handle)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       goto _php_ibase_fetch_error;
+                                               }
+
+                                       } else { /* blob id only */
+                                               ISC_QUAD bl_qd = *(ISC_QUAD *) var->sqldata;
+                                               ZVAL_STRINGL(result,_php_ibase_quad_to_string(bl_qd), BLOB_ID_LEN, 0);
+                                       }
+                                       break;
+                               case SQL_ARRAY:
+                                       if (flag & PHP_IBASE_FETCH_ARRAYS) { /* array can be *huge* so only fetch if asked */
+                                               ISC_QUAD ar_qd = *(ISC_QUAD *) var->sqldata;
+                                               ibase_array *ib_array = &ib_result->out_array[array_cnt++];
+                                               void *ar_data = emalloc(ib_array->ar_size);
+
+                                               if (isc_array_get_slice(IB_STATUS, &ib_result->link->handle, 
+                                                               &ib_result->trans->handle, &ar_qd, &ib_array->ar_desc,
+                                                               ar_data, &ib_array->ar_size)) {
+                                                       _php_ibase_error(TSRMLS_C);
+                                                       efree(ar_data);
+                                                       goto _php_ibase_fetch_error;
+                                               }
+
+                                               if (FAILURE == _php_ibase_arr_zval(result, ar_data, ib_array->ar_size, ib_array,
+                                                               0, flag TSRMLS_CC)) {
+                                                       efree(ar_data);
+                                                       goto _php_ibase_fetch_error;
+                                               }
+                                               efree(ar_data);
+
+                                       } else { /* blob id only */
+                                               ISC_QUAD ar_qd = *(ISC_QUAD *) var->sqldata;
+                                               ZVAL_STRINGL(result,_php_ibase_quad_to_string(ar_qd), BLOB_ID_LEN, 0);
+                                       }
+                                       break;
+                               _php_ibase_fetch_error:
+                                       zval_dtor(result);
+                                       FREE_ZVAL(result);
+                                       RETURN_FALSE;
+                       } /* switch */
+
+                       if (fetch_type & FETCH_ROW) {
+                               add_index_zval(return_value, i, result);
+                       } else {
+                               add_assoc_zval(return_value, var->aliasname, result);
+                       }
+               } else {
+                       if (fetch_type & FETCH_ROW) {
+                               add_index_null(return_value, i);
+                       } else {
+                               add_assoc_null(return_value, var->aliasname);
+                       }
+               }
+       } /* for field */
+}
+/* }}} */
+
+/* {{{ proto array ibase_fetch_row(resource result [, int fetch_flags])
+   Fetch a row  from the results of a query */
+PHP_FUNCTION(ibase_fetch_row)
+{
+       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ROW);
+}
+/* }}} */
+
+/* {{{ proto array ibase_fetch_assoc(resource result [, int fetch_flags])
+   Fetch a row  from the results of a query */
+PHP_FUNCTION(ibase_fetch_assoc)
+{
+       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY);
+}
+/* }}} */
+
+/* {{{ proto object ibase_fetch_object(resource result [, int fetch_flags])
+   Fetch a object from the results of a query */
+PHP_FUNCTION(ibase_fetch_object)
+{
+       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY);
+
+       if (Z_TYPE_P(return_value) == IS_ARRAY) {
+               object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value));
+       }
+}
+/* }}} */
+
+
+/* {{{ proto bool ibase_name_result(resource result, string name)
+   Assign a name to a result for use with ... WHERE CURRENT OF <name> statements */
+PHP_FUNCTION(ibase_name_result)
+{
+       zval **result_arg, **name_arg;
+       ibase_result *ib_result;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &result_arg, &name_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
+       convert_to_string_ex(name_arg);
+
+       if (isc_dsql_set_cursor_name(IB_STATUS, &ib_result->stmt, Z_STRVAL_PP(name_arg), 0)) {
+               _php_ibase_error(TSRMLS_C);
+               RETURN_FALSE;
+       }
+       RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto bool ibase_free_result(resource result)
+   Free the memory used by a result */
+PHP_FUNCTION(ibase_free_result)
+{
+       zval **result_arg;
+       ibase_result *ib_result;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &result_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
+       zend_list_delete(Z_LVAL_PP(result_arg));
+       RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto resource ibase_prepare([resource link_identifier, ] string query)
+   Prepare a query for later execution */
+PHP_FUNCTION(ibase_prepare)
+{
+       zval **link_arg, **trans_arg, **query_arg;
+       ibase_db_link *ib_link;
+       ibase_trans *trans = NULL;
+       int trans_res_id = 0;
+       ibase_query *ib_query;
+       char *query;
+
+       RESET_ERRMSG;
+
+       switch (ZEND_NUM_ARGS()) {
+               case 1:
+                       if (zend_get_parameters_ex(1, &query_arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link",
+                               le_link, le_plink);
+                       break;
+               case 2:
+                       if (zend_get_parameters_ex(2, &link_arg, &query_arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, link_arg, &ib_link, &trans);
+
+                       if (trans != NULL) {
+                               convert_to_long_ex(link_arg);
+                               trans_res_id = Z_LVAL_PP(link_arg);
+                       }
+
+                       break;
+               case 3:
+                       if (zend_get_parameters_ex(3, &link_arg, &trans_arg, &query_arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link*, link_arg, -1, "InterBase link",
+                               le_link, le_plink);
+                       ZEND_FETCH_RESOURCE(trans, ibase_trans*, trans_arg, -1, "InterBase transaction", le_trans);
+
+                       convert_to_long_ex(trans_arg);
+                       trans_res_id = Z_LVAL_PP(trans_arg);
+
+                       break;                  
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+
+       convert_to_string_ex(query_arg);
+       query = Z_STRVAL_PP(query_arg);
+
+       if (FAILURE == _php_ibase_def_trans(ib_link, &trans TSRMLS_CC)) {
+               RETURN_FALSE;
+       }
+
+       ib_query = (ibase_query *) emalloc(sizeof(ibase_query));
+
+       if (FAILURE == _php_ibase_alloc_query(ib_query, ib_link, trans, query, ib_link->dialect,
+                       trans_res_id TSRMLS_CC)) {
+               efree(ib_query);
+               RETURN_FALSE;
+       }
+       ZEND_REGISTER_RESOURCE(return_value, ib_query, le_query);
+}
+/* }}} */
+
+/* {{{ proto mixed ibase_execute(resource query [, mixed bind_arg [, mixed bind_arg [, ...]]])
+   Execute a previously prepared query */
+PHP_FUNCTION(ibase_execute)
+{
+       zval ***args, **bind_args = NULL;
+       ibase_query *ib_query;
+       ibase_result *result = NULL;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() < 1) {
+               WRONG_PARAM_COUNT;
+       }
+
+       /* use stack to avoid leaks */
+       args = (zval ***) do_alloca(ZEND_NUM_ARGS() * sizeof(zval **));
+       if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
+               free_alloca(args);
+               RETURN_FALSE;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, args[0], -1, "InterBase query", le_query);
+
+       if (ZEND_NUM_ARGS() > 1) { /* have variables to bind */
+               bind_args = args[1];
+       }
+
+       /* Have we used this cursor before and it's still open (exec proc has no cursor) ? */
+       if (ib_query->result_res_id != 0 && ib_query->statement_type != isc_info_sql_stmt_exec_procedure) {
+               IBDEBUG("Implicitly closing a cursor");
+               if (isc_dsql_free_statement(IB_STATUS, &ib_query->stmt, DSQL_close)) {
+                       _php_ibase_error(TSRMLS_C);
+               }
+               /* invalidate previous results returned by this query (not necessary for exec proc) */
+               zend_list_delete(ib_query->result_res_id);      
+       }
+
+       if (FAILURE == _php_ibase_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, &result, ib_query,
+                       ZEND_NUM_ARGS()-1, bind_args)) {
+               free_alloca(args);
+               RETURN_FALSE;
+       }
+
+       /* free the query if trans handle was released */
+       if (ib_query->trans->handle == NULL) {
+               zend_list_delete(Z_LVAL_PP(args[0]));
+       }
+
+       free_alloca(args);
+
+       if (result != NULL) {
+               result->type = EXECUTE_RESULT;
+               if (ib_query->statement_type == isc_info_sql_stmt_exec_procedure) {
+                       result->stmt = NULL;
+               }
+               ib_query->result_res_id = zend_list_insert(result, le_result);
+               RETURN_RESOURCE(ib_query->result_res_id);
+       }
+}
+/* }}} */
+
+/* {{{ proto bool ibase_free_query(resource query)
+   Free memory used by a query */
+PHP_FUNCTION(ibase_free_query)
+{
+       zval **query_arg;
+       ibase_query *ib_query;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &query_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, query_arg, -1, "InterBase query", le_query);
+       zend_list_delete(Z_LVAL_PP(query_arg));
+       RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int ibase_num_fields(resource query_result)
+   Get the number of fields in result */
+PHP_FUNCTION(ibase_num_fields)
+{
+       zval **result;
+       int type;
+       XSQLDA *sqlda;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &result)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       zend_list_find(Z_LVAL_PP(result), &type);
+
+       if (type == le_query) {
+               ibase_query *ib_query;
+
+               ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result, -1, "InterBase query", le_query);
+               sqlda = ib_query->out_sqlda;
+       } else {
+               ibase_result *ib_result;
+
+               ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result, -1, "InterBase result", le_result);
+               sqlda = ib_result->out_sqlda;
+       }                                       
+
+       if (sqlda == NULL) {
+               RETURN_LONG(0);
+       } else {
+               RETURN_LONG(sqlda->sqld);
+       }
+}
+/* }}} */
+
+static void _php_ibase_field_info(zval *return_value, XSQLVAR *var) /* {{{ */
+{
+       unsigned short len;
+       char buf[16], *s = buf;
+
+       array_init(return_value);
+
+       add_index_stringl(return_value, 0, var->sqlname, var->sqlname_length, 1);
+       add_assoc_stringl(return_value, "name", var->sqlname, var->sqlname_length, 1);
+
+       add_index_stringl(return_value, 1, var->aliasname, var->aliasname_length, 1);
+       add_assoc_stringl(return_value, "alias", var->aliasname, var->aliasname_length, 1);
+
+       add_index_stringl(return_value, 2, var->relname, var->relname_length, 1);
+       add_assoc_stringl(return_value, "relation", var->relname, var->relname_length, 1);
+
+       len = sprintf(buf, "%d", var->sqllen);
+       add_index_stringl(return_value, 3, buf, len, 1);
+       add_assoc_stringl(return_value, "length", buf, len, 1);
+
+       if (var->sqlscale < 0) {
+               unsigned short precision = 0;
+
+               switch (var->sqltype & ~1) {
+
+                       case SQL_SHORT:
+                               precision = 4;
+                               break;
+                       case SQL_LONG:
+                               precision = 9;
+                               break;
+#ifdef SQL_INT64
+                       case SQL_INT64:
+                               precision = 18;
+                               break;
+#endif
+               }
+               len = sprintf(buf, "NUMERIC(%d,%d)", precision, -var->sqlscale);
+               add_index_stringl(return_value, 4, s, len, 1);
+               add_assoc_stringl(return_value, "type", s, len, 1);
+       } else {
+               switch (var->sqltype & ~1) {
+                       case SQL_TEXT:
+                               s = "CHAR"; 
+                               break;
+                       case SQL_VARYING:
+                               s = "VARCHAR"; 
+                               break;
+                       case SQL_SHORT:
+                               s = "SMALLINT"; 
+                               break;
+                       case SQL_LONG:
+                               s = "INTEGER"; 
+                               break;
+                       case SQL_FLOAT:
+                               s = "FLOAT"; break;
+                       case SQL_DOUBLE:
+                       case SQL_D_FLOAT:
+                               s = "DOUBLE PRECISION"; break;
+#ifdef SQL_INT64
+                       case SQL_INT64: 
+                               s = "BIGINT"; 
+                               break;
+#endif
+#ifdef SQL_TIMESTAMP
+                       case SQL_TIMESTAMP:     
+                               s = "TIMESTAMP"; 
+                               break;
+                       case SQL_TYPE_DATE:
+                               s = "DATE";
+                               break;
+                       case SQL_TYPE_TIME:
+                               s = "TIME"; 
+                               break;
+#else
+                       case SQL_DATE:
+                               s = "DATE"; 
+                               break;
+#endif
+                       case SQL_BLOB:
+                               s = "BLOB"; 
+                               break;
+                       case SQL_ARRAY:
+                               s = "ARRAY";
+                               break;
+                               /* FIXME: provide more detailed information about the field type, field size
+                                * and array dimensions */
+                       case SQL_QUAD:
+                               s = "QUAD";
+                               break;
+               }
+               add_index_string(return_value, 4, s, 1);
+               add_assoc_string(return_value, "type", s, 1);
+       }
+}
+/* }}} */
+
+/* {{{ proto array ibase_field_info(resource query_result, int field_number)
+   Get information about a field */
+PHP_FUNCTION(ibase_field_info)
+{
+       zval **result_arg, **field_arg;
+       int type;
+       XSQLDA *sqlda;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS()!=2 || zend_get_parameters_ex(2, &result_arg, &field_arg)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       zend_list_find(Z_LVAL_PP(result_arg), &type);
+
+       if (type == le_query) {
+               ibase_query *ib_query;
+
+               ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result_arg, -1, "InterBase query", le_query);
+               sqlda = ib_query->out_sqlda;
+       } else {
+               ibase_result *ib_result;
+
+               ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
+               sqlda = ib_result->out_sqlda;
+       }                                       
+
+       if (sqlda == NULL) {
+               _php_ibase_module_error("Trying to get field info from a non-select query" TSRMLS_CC);
+               RETURN_FALSE;
+       }
+
+       convert_to_long_ex(field_arg);
+
+       if (Z_LVAL_PP(field_arg) < 0 || Z_LVAL_PP(field_arg) >= sqlda->sqld) {
+               RETURN_FALSE;
+       }
+       _php_ibase_field_info(return_value,sqlda->sqlvar + Z_LVAL_PP(field_arg));
+}
+/* }}} */
+
+/* {{{ proto int ibase_num_params(resource query)
+   Get the number of params in a prepared query */
+PHP_FUNCTION(ibase_num_params)
+{
+       zval **result;
+       ibase_query *ib_query;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &result) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result, -1, "InterBase query", le_query);
+
+       if (ib_query->in_sqlda == NULL) {
+               RETURN_LONG(0);
+       } else {
+               RETURN_LONG(ib_query->in_sqlda->sqld);
+       }
+}
+/* }}} */
+
+/* {{{ proto array ibase_param_info(resource query, int field_number)
+   Get information about a parameter */
+PHP_FUNCTION(ibase_param_info)
+{
+       zval **result_arg, **field_arg;
+       ibase_query *ib_query;
+
+       RESET_ERRMSG;
+
+       if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &result_arg, &field_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result_arg, -1, "InterBase query", le_query);
+
+       if (ib_query->in_sqlda == NULL) {
+               RETURN_FALSE;
+       }
+
+       convert_to_long_ex(field_arg);
+
+       if (Z_LVAL_PP(field_arg) < 0 || Z_LVAL_PP(field_arg) >= ib_query->in_sqlda->sqld) {
+               RETURN_FALSE;
+       }
+
+       _php_ibase_field_info(return_value,ib_query->in_sqlda->sqlvar + Z_LVAL_PP(field_arg));
+}
+/* }}} */
+
+#endif /* HAVE_IBASE */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
index 147ee4688fd013243cca54e11ed5a06e460361e1..6f564b3fb65cfe8a9fc690a77509c01eae3dcf0a 100644 (file)
 #include "php_interbase.h"
 #include "php_ibase_includes.h"
 
+static int le_service;
+
+static void _php_ibase_free_service(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
+{
+       ibase_service *sv = (ibase_service *) rsrc->ptr;
+
+       if (isc_service_detach(IB_STATUS, &sv->handle)) {
+               _php_ibase_error(TSRMLS_C);
+       }
+
+       if (sv->hostname) {
+               efree(sv->hostname);
+       }
+       if (sv->username) {
+               efree(sv->username);
+       }
+
+       efree(sv);
+}
+/* }}} */
+
+void php_ibase_service_minit(INIT_FUNC_ARGS) /* {{{ */
+{
+       le_service = zend_register_list_destructors_ex(_php_ibase_free_service, NULL, 
+           "interbase service manager handle", module_number);
+
+       /* backup options */
+       REGISTER_LONG_CONSTANT("IBASE_BKP_IGNORE_CHECKSUMS", isc_spb_bkp_ignore_checksums, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_BKP_IGNORE_LIMBO", isc_spb_bkp_ignore_limbo, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_BKP_METADATA_ONLY", isc_spb_bkp_metadata_only, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_BKP_NO_GARBAGE_COLLECT", isc_spb_bkp_no_garbage_collect, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_BKP_OLD_DESCRIPTIONS", isc_spb_bkp_old_descriptions, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_BKP_NON_TRANSPORTABLE", isc_spb_bkp_non_transportable, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_BKP_CONVERT", isc_spb_bkp_convert, CONST_PERSISTENT);
+
+       /* restore options */
+       REGISTER_LONG_CONSTANT("IBASE_RES_DEACTIVATE_IDX", isc_spb_res_deactivate_idx, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RES_NO_SHADOW", isc_spb_res_no_shadow, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RES_NO_VALIDITY", isc_spb_res_no_validity, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RES_ONE_AT_A_TIME", isc_spb_res_one_at_a_time, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RES_REPLACE", isc_spb_res_replace, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RES_CREATE", isc_spb_res_create, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RES_USE_ALL_SPACE", isc_spb_res_use_all_space, CONST_PERSISTENT);
+
+       /* manage options */
+       REGISTER_LONG_CONSTANT("IBASE_PRP_PAGE_BUFFERS", isc_spb_prp_page_buffers, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_SWEEP_INTERVAL", isc_spb_prp_sweep_interval, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_SHUTDOWN_DB", isc_spb_prp_shutdown_db, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_DENY_NEW_TRANSACTIONS", isc_spb_prp_deny_new_transactions, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_DENY_NEW_ATTACHMENTS", isc_spb_prp_deny_new_attachments, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_RESERVE_SPACE", isc_spb_prp_reserve_space, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_PRP_RES_USE_FULL", isc_spb_prp_res_use_full, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_PRP_RES", isc_spb_prp_res, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_WRITE_MODE", isc_spb_prp_write_mode, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_PRP_WM_ASYNC", isc_spb_prp_wm_async, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_PRP_WM_SYNC", isc_spb_prp_wm_sync, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_ACCESS_MODE", isc_spb_prp_access_mode, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_PRP_AM_READONLY", isc_spb_prp_am_readonly, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_PRP_AM_READWRITE", isc_spb_prp_am_readwrite, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_SET_SQL_DIALECT", isc_spb_prp_set_sql_dialect, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_ACTIVATE", isc_spb_prp_activate, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_PRP_DB_ONLINE", isc_spb_prp_db_online, CONST_PERSISTENT);
+
+       /* repair options */
+       REGISTER_LONG_CONSTANT("IBASE_RPR_CHECK_DB", isc_spb_rpr_check_db, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RPR_IGNORE_CHECKSUM", isc_spb_rpr_ignore_checksum, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RPR_KILL_SHADOWS", isc_spb_rpr_kill_shadows, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RPR_MEND_DB", isc_spb_rpr_mend_db, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RPR_VALIDATE_DB", isc_spb_rpr_validate_db, CONST_PERSISTENT);
+         REGISTER_LONG_CONSTANT("IBASE_RPR_FULL", isc_spb_rpr_full, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_RPR_SWEEP_DB", isc_spb_rpr_sweep_db, CONST_PERSISTENT);
+
+       /* db info arguments */
+       REGISTER_LONG_CONSTANT("IBASE_STS_DATA_PAGES", isc_spb_sts_data_pages, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_STS_DB_LOG", isc_spb_sts_db_log, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_STS_HDR_PAGES", isc_spb_sts_hdr_pages, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_STS_IDX_PAGES", isc_spb_sts_idx_pages, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_STS_SYS_RELATIONS", isc_spb_sts_sys_relations, CONST_PERSISTENT);
+
+       /* server info arguments */
+       REGISTER_LONG_CONSTANT("IBASE_SVC_SERVER_VERSION", isc_info_svc_server_version, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_IMPLEMENTATION", isc_info_svc_implementation, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV", isc_info_svc_get_env, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV_LOCK", isc_info_svc_get_env_lock, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV_MSG", isc_info_svc_get_env_msg, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_USER_DBPATH", isc_info_svc_user_dbpath, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_SVR_DB_INFO", isc_info_svc_svr_db_info, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_USERS", isc_info_svc_get_users, CONST_PERSISTENT);
+}
+/* }}} */
+
 static void _php_ibase_user(INTERNAL_FUNCTION_PARAMETERS, int operation) /* {{{ */
 {
        zval **args[8];
@@ -38,7 +129,7 @@ static void _php_ibase_user(INTERNAL_FUNCTION_PARAMETERS, int operation) /* {{{
        char spb_buffer[128], *spb = spb_buffer;
        unsigned short spb_length;
        isc_svc_handle service_handle = NULL;
-       
+
        RESET_ERRMSG;
 
        switch (operation) {
@@ -49,7 +140,7 @@ static void _php_ibase_user(INTERNAL_FUNCTION_PARAMETERS, int operation) /* {{{
                                WRONG_PARAM_COUNT;
                        }
                        break;
-       
+
                case isc_action_svc_delete_user:
                        /* 4 parameters for DELETE operation */
                        if (ZEND_NUM_ARGS() != 4) {
@@ -161,7 +252,7 @@ static void _php_ibase_user(INTERNAL_FUNCTION_PARAMETERS, int operation) /* {{{
                        isc_service_detach(IB_STATUS, &service_handle);
                }
        }
-       
+
        RETURN_TRUE;
 }
 /* }}} */
@@ -190,7 +281,7 @@ PHP_FUNCTION(ibase_delete_user)
 }
 /* }}} */
 
-/* {{{ proto resource ibase_service_attach(string host, string dba_username, string dba_password) 
+/* {{{ proto resource ibase_service_attach(string host, string dba_username, string dba_password)
    Connect to the service manager */
 PHP_FUNCTION(ibase_service_attach)
 {
@@ -200,33 +291,33 @@ PHP_FUNCTION(ibase_service_attach)
        isc_svc_handle handle = NULL;
 
        RESET_ERRMSG;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", 
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss",
                        &host, &hlen, &user, &ulen, &pass, &plen)) {
-               
+
                RETURN_FALSE;
        }
 
        /* construct the spb, hack the service name into it as well */
-       spb_len = snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%s" "%s:service_mgr", 
+       spb_len = snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%s" "%s:service_mgr",
                isc_spb_version, isc_spb_current_version, isc_spb_user_name, (char)ulen,
                user, isc_spb_password, (char)plen, pass, host);
 
        if (spb_len > sizeof(buf) || spb_len == -1) {
-               _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%ld)" 
+               _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%ld)"
                        TSRMLS_CC, spb_len);
                RETURN_FALSE;
        }
-       
+
        spb_len -= hlen + 12;
        loc = buf + spb_len; /* points to %s:service_mgr part */
-       
+
        /* attach to the service manager */
        if (isc_service_attach(IB_STATUS, 0, loc, &handle, (unsigned short)spb_len, buf)) {
                _php_ibase_error(TSRMLS_C);
                RETURN_FALSE;
        }
-       
+
        svm = (ibase_service*)emalloc(sizeof(ibase_service));
        svm->handle = handle;
        svm->hostname = estrdup(host);
@@ -237,20 +328,20 @@ PHP_FUNCTION(ibase_service_attach)
 }
 /* }}} */
 
-/* {{{ proto bool ibase_service_detach(resource service_handle) 
+/* {{{ proto bool ibase_service_detach(resource service_handle)
    Disconnect from the service manager */
 PHP_FUNCTION(ibase_service_detach)
 {
        zval *res;
-       
+
        RESET_ERRMSG;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &res)) { 
-               RETURN_FALSE; 
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &res)) {
+               RETURN_FALSE;
        }
-       
+
        zend_list_delete(Z_LVAL_P(res));
-       
+
        RETURN_TRUE;
 }
 /* }}} */
@@ -259,7 +350,7 @@ static void _php_ibase_service_query(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */
        ibase_service *svm, char info_action)
 {
        static char spb[] = { isc_info_svc_timeout, 10, 0, 0, 0 };
-               
+
        char res_buf[400], *result, *heap_buf = NULL, *heap_p;
        long heap_buf_size = 200, line_len;
 
@@ -272,13 +363,13 @@ static void _php_ibase_service_query(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */
                        RETURN_FALSE;
                }
        }
-       
-query_loop:            
+
+query_loop:
        result = res_buf;
 
        if (isc_service_query(IB_STATUS, &svm->handle, NULL, sizeof(spb), spb,
                        1, &info_action, sizeof(res_buf), res_buf)) {
-               
+
                _php_ibase_error(TSRMLS_C);
                RETURN_FALSE;
        }
@@ -286,8 +377,8 @@ query_loop:
                switch (*result++) {
                        default:
                                RETURN_FALSE;
-                               
-                       case isc_info_svc_line: 
+
+                       case isc_info_svc_line:
                                if (! (line_len = isc_vax_integer(result, 2))) {
                                        /* done */
                                        if (heap_buf) {
@@ -298,9 +389,9 @@ query_loop:
                                }
                                if (!heap_buf || (heap_p - heap_buf + line_len +2) > heap_buf_size) {
                                        long res_size = heap_buf ? heap_p - heap_buf : 0;
-                                       
+
                                        while (heap_buf_size < (res_size + line_len +2)) {
-                                               heap_buf_size *= 2;                                     
+                                               heap_buf_size *= 2;
                                        }
                                        heap_buf = (char*) erealloc(heap_buf, heap_buf_size);
                                        heap_p = heap_buf + res_size;
@@ -316,7 +407,7 @@ query_loop:
                        case isc_info_svc_get_env_lock:
                        case isc_info_svc_get_env_msg:
                        case isc_info_svc_user_dbpath:
-                               RETURN_STRINGL(result + 2, isc_vax_integer(result, 2), 1);                              
+                               RETURN_STRINGL(result + 2, isc_vax_integer(result, 2), 1);
 
                        case isc_info_svc_svr_db_info:
                                array_init(return_value);
@@ -329,7 +420,7 @@ query_loop:
                                                        add_assoc_long(return_value, "attachments", isc_vax_integer(result,4));
                                                        result += 4;
                                                        break;
-                                               
+
                                                case isc_spb_num_db:
                                                        add_assoc_long(return_value, "databases", isc_vax_integer(result,4));
                                                        result += 4;
@@ -342,16 +433,16 @@ query_loop:
                                        }
                                } while (*result != isc_info_flag_end);
                                return;
-                               
+
                        case isc_info_svc_get_users: {
                                zval *user;
                                array_init(return_value);
-                               
+
                                while (*result != isc_info_end) {
-                                       
+
                                        switch (*result++) {
                                                int len;
-                                               
+
                                                case isc_spb_sec_username:
                                                        /* it appears that the username is always first */
                                                        ALLOC_INIT_ZVAL(user);
@@ -362,7 +453,7 @@ query_loop:
                                                        add_assoc_stringl(user, "user_name", result +2, len, 1);
                                                        result += len+2;
                                                        break;
-                                               
+
                                                case isc_spb_sec_firstname:
                                                        len = isc_vax_integer(result,2);
                                                        add_assoc_stringl(user, "first_name", result +2, len, 1);
@@ -385,14 +476,14 @@ query_loop:
                                                        add_assoc_long(user, "user_id", isc_vax_integer(result, 4));
                                                        result += 4;
                                                        break;
-                                                       
+
                                                case isc_spb_sec_groupid:
                                                        add_assoc_long(user, "group_id", isc_vax_integer(result, 4));
                                                        result += 4;
                                                        break;
                                        }
                                }
-                               return; 
+                               return;
                        }
                }
        }
@@ -404,47 +495,47 @@ static void _php_ibase_backup_restore(INTERNAL_FUNCTION_PARAMETERS, char operati
        /**
         * It appears that the service API is a little bit confused about which flag
         * to use for the source and destination in the case of a restore operation.
-        * When passing the backup file as isc_spb_dbname and the destination db as 
+        * When passing the backup file as isc_spb_dbname and the destination db as
         * bpk_file, things work well.
         */
        zval *res;
-       char *db, *bk, buf[200]; 
+       char *db, *bk, buf[200];
        long dblen, bklen, spb_len, opts = 0;
        zend_bool verbose = 0;
        ibase_service *svm;
-       
+
        RESET_ERRMSG;
-       
+
        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss|lb",
-                       &res, &db, &dblen, &bk, &bklen, &opts, &verbose)) { 
-               RETURN_FALSE; 
+                       &res, &db, &dblen, &bk, &bklen, &opts, &verbose)) {
+               RETURN_FALSE;
        }
-       
-       ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, 
+
+       ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1,
                "Interbase service manager handle", le_service);
-       
+
        /* fill the param buffer */
-       spb_len = snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%c%s%c%c%c%c%c", 
-               operation, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), db, 
+       spb_len = snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%c%s%c%c%c%c%c",
+               operation, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), db,
                isc_spb_bkp_file, (char)bklen, (char)(bklen >> 8), bk, isc_spb_options,
                (char)opts,(char)(opts >> 8), (char)(opts >> 16), (char)(opts >> 24));
-               
+
        if (verbose) {
                buf[spb_len++] = isc_spb_verbose;
        }
 
        if (spb_len > sizeof(buf) || spb_len <= 0) {
-               _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%ld)" 
+               _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%ld)"
                        TSRMLS_CC, spb_len);
                RETURN_FALSE;
        }
-       
+
        /* now start the backup/restore job */
        if (isc_service_start(IB_STATUS, &svm->handle, NULL, (unsigned short)spb_len, buf)) {
                _php_ibase_error(TSRMLS_C);
                RETURN_FALSE;
        }
-       
+
        if (!verbose) {
                RETURN_TRUE;
        } else {
@@ -475,22 +566,22 @@ static void _php_ibase_service_action(INTERNAL_FUNCTION_PARAMETERS, char svc_act
        char buf[128], *db;
        long action, spb_len, dblen, argument = 0;
        ibase_service *svm;
-       
+
        RESET_ERRMSG;
-       
+
        if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsl|l",
-                       &res, &db, &dblen, &action, &argument)) { 
-               RETURN_FALSE; 
+                       &res, &db, &dblen, &action, &argument)) {
+               RETURN_FALSE;
        }
-       
-       ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, 
+
+       ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1,
                "Interbase service manager handle", le_service);
-       
+
        if (svc_action == isc_action_svc_db_stats) {
                switch (action) {
                        default:
                                goto unknown_option;
-                               
+
                        case isc_spb_sts_data_pages:
                        case isc_spb_sts_db_log:
                        case isc_spb_sts_hdr_pages:
@@ -502,10 +593,10 @@ static void _php_ibase_service_action(INTERNAL_FUNCTION_PARAMETERS, char svc_act
                /* these actions all expect different types of arguments */
                switch (action) {
                        default:
-unknown_option:                        
+unknown_option:
                                _php_ibase_module_error("Unrecognised option (%ld)" TSRMLS_CC, action);
                                RETURN_FALSE;
-                               
+
                        case isc_spb_rpr_check_db:
                        case isc_spb_rpr_ignore_checksum:
                        case isc_spb_rpr_kill_shadows:
@@ -513,13 +604,13 @@ unknown_option:
                        case isc_spb_rpr_validate_db:
                        case isc_spb_rpr_sweep_db:
                                svc_action = isc_action_svc_repair;
-                               
+
                        case isc_spb_prp_activate:
                        case isc_spb_prp_db_online:
-options_argument:      
+options_argument:
                                argument |= action;
                                action = isc_spb_options;
-       
+
                        case isc_spb_prp_page_buffers:
                        case isc_spb_prp_sweep_interval:
                        case isc_spb_prp_shutdown_db:
@@ -527,22 +618,22 @@ options_argument:
                        case isc_spb_prp_deny_new_attachments:
                        case isc_spb_prp_set_sql_dialect:
                                spb_len = snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c%c%c%c",
-                                       svc_action, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), db, 
+                                       svc_action, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), db,
                                        (char)action, (char)argument, (char)(argument >> 8), (char)(argument >> 16),
                                        (char)(argument >> 24));
                                break;
-                               
+
                        case isc_spb_prp_reserve_space:
                        case isc_spb_prp_write_mode:
                        case isc_spb_prp_access_mode:
                                spb_len = snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c",
-                                       isc_action_svc_properties, isc_spb_dbname, (char)dblen, (char)(dblen >> 8), 
+                                       isc_action_svc_properties, isc_spb_dbname, (char)dblen, (char)(dblen >> 8),
                                        db, (char)action, (char)argument);
                }
        }
-       
+
        if (spb_len > sizeof(buf) || spb_len == -1) {
-               _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%ld)" 
+               _php_ibase_module_error("Internal error: insufficient buffer space for SPB (%ld)"
                        TSRMLS_CC, spb_len);
                RETURN_FALSE;
        }
@@ -551,14 +642,14 @@ options_argument:
                _php_ibase_error(TSRMLS_C);
                RETURN_FALSE;
        }
-       
+
        if (svc_action == isc_action_svc_db_stats) {
                _php_ibase_service_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, svm, isc_info_svc_line);
        } else {
                RETURN_TRUE;
        }
-}      
-/* }}} */          
+}
+/* }}} */
 
 /* {{{ proto bool ibase_maintain_db(resource service_handle, string db, int action [, int argument])
    Execute a maintenance command on the database server */
@@ -577,29 +668,33 @@ PHP_FUNCTION(ibase_db_info)
 /* }}} */
 
 /* {{{ proto string ibase_server_info(resource service_handle, int action)
-   Request information about a database */
+   Request information about a database server */
 PHP_FUNCTION(ibase_server_info)
 {
        zval *res;
        long action;
        ibase_service *svm;
-       
+
        RESET_ERRMSG;
-       
-       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &res, &action)) { 
-               RETURN_FALSE; 
+
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &res, &action)) {
+               RETURN_FALSE;
        }
-       
-       ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1, 
+
+       ZEND_FETCH_RESOURCE(svm, ibase_service *, &res, -1,
                "Interbase service manager handle", le_service);
-       
+
        _php_ibase_service_query(INTERNAL_FUNCTION_PARAM_PASSTHRU, svm, (char)action);
 }
 /* }}} */
 
+#else
+
+void php_ibase_register_service_constants(INIT_FUNC_ARGS) { /* nop */ }
+
 #endif /* HAVE_IBASE6_API */
-        
-/*      
+
+/*
  * Local variables:
  * tab-width: 4
  * c-basic-offset: 4
index 305e94c0cbbf01c6ed691e1b73312fe206219c92..d2eadb92c5afb33fc749031d51e486c3d9e60ebc 100644 (file)
 
 #include <time.h>
 
-#ifdef ZEND_DEBUG_
-#define IBDEBUG(a) php_printf("::: %s (%d)\n", a, __LINE__);
-#endif
-
-#ifndef IBDEBUG
-#define IBDEBUG(a)
-#endif
-
-#define ISC_LONG_MIN   INT_MIN
-#define ISC_LONG_MAX   INT_MAX
-
-#define QUERY_RESULT   1
-#define EXECUTE_RESULT 2
-
 #define ROLLBACK               0
 #define COMMIT                 1
 #define RETAIN                 2
 
-#define FETCH_ROW              1
-#define FETCH_ARRAY            2
-
 /* {{{ extension definition structures */
 function_entry ibase_functions[] = {
        PHP_FE(ibase_connect, NULL)
@@ -154,7 +137,7 @@ ZEND_GET_MODULE(ibase)
 #endif
 
 /* True globals, no need for thread safety */
-int le_blob, le_link, le_plink, le_result, le_query, le_trans, le_event, le_service;
+int le_link, le_plink, le_trans;
 
 ZEND_DECLARE_MODULE_GLOBALS(ibase)
 
@@ -238,28 +221,6 @@ typedef struct {
        char *tpb_ptr;
 } ISC_TEB;
 
-typedef struct {
-       unsigned short vary_length;
-       char vary_string[1];
-} IBVARY;
-
-/* sql variables union 
- * used for convert and binding input variables
- */
-typedef struct {
-       union {
-               short sval;
-               float fval;
-               ISC_LONG lval;
-               ISC_QUAD qval;
-#ifdef SQL_TIMESTAMP
-               ISC_TIMESTAMP tsval;
-               ISC_DATE dtval;
-               ISC_TIME tmval;
-#endif
-       } val;
-       short sqlind;
-} BIND_BUF;
 /* }}} */
 
 /* Fill ib_link and trans with the correct database link and transaction. */
@@ -293,71 +254,6 @@ void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, /* {{{ */
 
 /* destructors ---------------------- */
 
-static void _php_ibase_free_xsqlda(XSQLDA *sqlda) /* {{{ */
-{
-       int i;
-       XSQLVAR *var;
-
-       IBDEBUG("Free XSQLDA?");
-       if (sqlda) {
-               IBDEBUG("Freeing XSQLDA...");
-               var = sqlda->sqlvar;
-               for (i = 0; i < sqlda->sqld; i++, var++) {
-                       efree(var->sqldata);
-                       if (var->sqlind) {
-                               efree(var->sqlind);
-                       }
-               }
-               efree(sqlda);
-       }
-}
-/* }}} */
-
-static void _php_ibase_free_event(ibase_event *event TSRMLS_DC) /* {{{ */
-{
-       unsigned short i;
-       
-       event->state = DEAD;
-       
-       if (event->link != NULL) {
-               ibase_event **node;
-
-               if (event->link->handle != NULL &&
-                               isc_cancel_events(IB_STATUS, &event->link->handle, &event->event_id)) {
-                       _php_ibase_error(TSRMLS_C);
-               }
-               
-               /* delete this event from the link struct */
-               for (node = &event->link->event_head; *node != event; node = &(*node)->event_next);
-               *node = event->event_next;
-       }
-
-       if (event->callback) {
-               zval_dtor(event->callback);
-               FREE_ZVAL(event->callback);
-               event->callback = NULL;
-       
-               _php_ibase_event_free(event->event_buffer,event->result_buffer);
-       
-               for (i = 0; i < event->event_count; ++i) {
-                       efree(event->events[i]);
-               }
-               efree(event->events);
-       }
-}
-/* }}} */
-
-static void _php_ibase_free_event_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
-{
-       ibase_event *e = (ibase_event *) rsrc->ptr;
-       IBDEBUG("Cleaning up event resource");
-
-       _php_ibase_free_event(e TSRMLS_CC);
-       
-       efree(e);
-}
-/* }}} */
-
 static void _php_ibase_commit_link(ibase_db_link *link TSRMLS_DC) /* {{{ */
 {
        unsigned short i = 0, j;
@@ -444,80 +340,6 @@ static void _php_ibase_close_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{
 }
 /* }}} */
 
-static void _php_ibase_free_result(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
-{
-       ibase_result *ib_result = (ibase_result *) rsrc->ptr;
-
-       IBDEBUG("Freeing result by dtor...");
-       if (ib_result) {
-               _php_ibase_free_xsqlda(ib_result->out_sqlda);
-               if (ib_result->stmt && ib_result->type != EXECUTE_RESULT) {
-                       IBDEBUG("Dropping statement handle (free_result dtor)...");
-                       isc_dsql_free_statement(IB_STATUS, &ib_result->stmt, DSQL_drop);
-               }
-               efree(ib_result);
-       }
-}
-/* }}} */
-
-static void _php_ibase_free_query(ibase_query *ib_query TSRMLS_DC) /* {{{ */
-{
-       IBDEBUG("Freeing query...");
-
-       if (ib_query->in_sqlda) {
-               efree(ib_query->in_sqlda);
-       }
-       if (ib_query->out_sqlda) {
-               efree(ib_query->out_sqlda);
-       }
-       if (ib_query->stmt) {
-               IBDEBUG("Dropping statement handle (free_query)...");
-               if (isc_dsql_free_statement(IB_STATUS, &ib_query->stmt, DSQL_drop)) {
-                       _php_ibase_error(TSRMLS_C);
-               }
-       }
-       if (ib_query->in_array) {
-               efree(ib_query->in_array);
-       }
-       if (ib_query->out_array) {
-               efree(ib_query->out_array);
-       }
-       if (ib_query->query) {
-               efree(ib_query->query);
-       }
-}
-/* }}} */
-
-static void php_ibase_free_query_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
-{
-       ibase_query *ib_query = (ibase_query *)rsrc->ptr;
-
-       if (ib_query != NULL) {
-               IBDEBUG("Preparing to free query by dtor...");
-
-               _php_ibase_free_query(ib_query TSRMLS_CC);
-               if (ib_query->statement_type != isc_info_sql_stmt_exec_procedure) {
-                       zend_list_delete(ib_query->result_res_id);
-               }
-               efree(ib_query);
-       }
-}
-/* }}} */
-
-static void _php_ibase_free_blob(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
-{
-       ibase_blob *ib_blob = (ibase_blob *)rsrc->ptr;
-
-       if (ib_blob->bl_handle != NULL) { /* blob open*/
-               if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
-                       _php_ibase_module_error("You can lose data. Close any blob after reading from or "
-                               "writing to it. Use ibase_blob_close() before calling ibase_close()" TSRMLS_CC);
-               }
-       }
-       efree(ib_blob);
-}
-/* }}} */
-
 static void _php_ibase_free_trans(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
 {
        ibase_trans *trans = (ibase_trans *)rsrc->ptr;
@@ -549,29 +371,6 @@ static void _php_ibase_free_trans(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ *
 }
 /* }}} */
 
-#if HAVE_IBASE6_API
-static void _php_ibase_free_service(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
-{
-       ibase_service *sv = (ibase_service *) rsrc->ptr;
-       
-       IBDEBUG("Cleaning up service manager resource");
-       
-       if (isc_service_detach(IB_STATUS, &sv->handle)) {
-               _php_ibase_error(TSRMLS_C);
-       }
-       
-       if (sv->hostname) {
-               efree(sv->hostname);
-       }
-       if (sv->username) {
-               efree(sv->username);
-       }
-       
-       efree(sv);
-}
-/* }}} */
-#endif
-
 /* {{{ startup, shutdown and info functions */
 PHP_INI_BEGIN()
        STD_PHP_INI_BOOLEAN("ibase.allow_persistent", "1", PHP_INI_SYSTEM, OnUpdateLong, allow_persistent, zend_ibase_globals, ibase_globals)
@@ -601,16 +400,9 @@ PHP_MINIT_FUNCTION(ibase)
 
        REGISTER_INI_ENTRIES();
 
-       le_result = zend_register_list_destructors_ex(_php_ibase_free_result, NULL, "interbase result", module_number);
-       le_query = zend_register_list_destructors_ex(php_ibase_free_query_rsrc, NULL, "interbase query", module_number);
-       le_blob = zend_register_list_destructors_ex(_php_ibase_free_blob, NULL, "interbase blob", module_number);
        le_link = zend_register_list_destructors_ex(_php_ibase_close_link, NULL, "interbase link", module_number);
        le_plink = zend_register_list_destructors_ex(php_ibase_commit_link_rsrc, _php_ibase_close_plink, "interbase link persistent", module_number);
        le_trans = zend_register_list_destructors_ex(_php_ibase_free_trans, NULL, "interbase transaction", module_number);
-       le_event = zend_register_list_destructors_ex(_php_ibase_free_event_rsrc, NULL, "interbase event", module_number);
-#if HAVE_IBASE6_API
-       le_service = zend_register_list_destructors_ex(_php_ibase_free_service, NULL, "interbase service manager handle", module_number);
-#endif
 
        REGISTER_LONG_CONSTANT("IBASE_DEFAULT", PHP_IBASE_DEFAULT, CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("IBASE_TEXT", PHP_IBASE_FETCH_BLOBS, CONST_PERSISTENT); /* deprecated, for BC only */
@@ -620,6 +412,7 @@ PHP_MINIT_FUNCTION(ibase)
        REGISTER_LONG_CONSTANT("IBASE_TIMESTAMP", PHP_IBASE_TIMESTAMP, CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("IBASE_DATE", PHP_IBASE_DATE, CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("IBASE_TIME", PHP_IBASE_TIME, CONST_PERSISTENT);
+
        /* transactions */
        REGISTER_LONG_CONSTANT("IBASE_WRITE", PHP_IBASE_WRITE, CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("IBASE_READ", PHP_IBASE_READ, CONST_PERSISTENT);
@@ -630,65 +423,12 @@ PHP_MINIT_FUNCTION(ibase)
        REGISTER_LONG_CONSTANT("IBASE_REC_NO_VERSION", PHP_IBASE_REC_NO_VERSION, CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("IBASE_NOWAIT", PHP_IBASE_NOWAIT, CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("IBASE_WAIT", PHP_IBASE_WAIT, CONST_PERSISTENT);
-#if HAVE_IBASE6_API    
-       /* backup options */
-       REGISTER_LONG_CONSTANT("IBASE_BKP_IGNORE_CHECKSUMS", isc_spb_bkp_ignore_checksums, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_BKP_IGNORE_LIMBO", isc_spb_bkp_ignore_limbo, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_BKP_METADATA_ONLY", isc_spb_bkp_metadata_only, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_BKP_NO_GARBAGE_COLLECT", isc_spb_bkp_no_garbage_collect, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_BKP_OLD_DESCRIPTIONS", isc_spb_bkp_old_descriptions, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_BKP_NON_TRANSPORTABLE", isc_spb_bkp_non_transportable, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_BKP_CONVERT", isc_spb_bkp_convert, CONST_PERSISTENT);
-       /* restore options */
-       REGISTER_LONG_CONSTANT("IBASE_RES_DEACTIVATE_IDX", isc_spb_res_deactivate_idx, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RES_NO_SHADOW", isc_spb_res_no_shadow, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RES_NO_VALIDITY", isc_spb_res_no_validity, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RES_ONE_AT_A_TIME", isc_spb_res_one_at_a_time, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RES_REPLACE", isc_spb_res_replace, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RES_CREATE", isc_spb_res_create, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RES_USE_ALL_SPACE", isc_spb_res_use_all_space, CONST_PERSISTENT);
-       /* manage options */
-       REGISTER_LONG_CONSTANT("IBASE_PRP_PAGE_BUFFERS", isc_spb_prp_page_buffers, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_SWEEP_INTERVAL", isc_spb_prp_sweep_interval, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_SHUTDOWN_DB", isc_spb_prp_shutdown_db, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_DENY_NEW_TRANSACTIONS", isc_spb_prp_deny_new_transactions, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_DENY_NEW_ATTACHMENTS", isc_spb_prp_deny_new_attachments, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_RESERVE_SPACE", isc_spb_prp_reserve_space, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_PRP_RES_USE_FULL", isc_spb_prp_res_use_full, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_PRP_RES", isc_spb_prp_res, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_WRITE_MODE", isc_spb_prp_write_mode, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_PRP_WM_ASYNC", isc_spb_prp_wm_async, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_PRP_WM_SYNC", isc_spb_prp_wm_sync, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_ACCESS_MODE", isc_spb_prp_access_mode, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_PRP_AM_READONLY", isc_spb_prp_am_readonly, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_PRP_AM_READWRITE", isc_spb_prp_am_readwrite, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_SET_SQL_DIALECT", isc_spb_prp_set_sql_dialect, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_ACTIVATE", isc_spb_prp_activate, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_PRP_DB_ONLINE", isc_spb_prp_db_online, CONST_PERSISTENT);
-       /* repair options */
-       REGISTER_LONG_CONSTANT("IBASE_RPR_CHECK_DB", isc_spb_rpr_check_db, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RPR_IGNORE_CHECKSUM", isc_spb_rpr_ignore_checksum, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RPR_KILL_SHADOWS", isc_spb_rpr_kill_shadows, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RPR_MEND_DB", isc_spb_rpr_mend_db, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RPR_VALIDATE_DB", isc_spb_rpr_validate_db, CONST_PERSISTENT);
-         REGISTER_LONG_CONSTANT("IBASE_RPR_FULL", isc_spb_rpr_full, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_RPR_SWEEP_DB", isc_spb_rpr_sweep_db, CONST_PERSISTENT);
-       /* db info arguments */
-       REGISTER_LONG_CONSTANT("IBASE_STS_DATA_PAGES", isc_spb_sts_data_pages, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_STS_DB_LOG", isc_spb_sts_db_log, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_STS_HDR_PAGES", isc_spb_sts_hdr_pages, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_STS_IDX_PAGES", isc_spb_sts_idx_pages, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_STS_SYS_RELATIONS", isc_spb_sts_sys_relations, CONST_PERSISTENT);
-       /* server info arguments */
-       REGISTER_LONG_CONSTANT("IBASE_SVC_SERVER_VERSION", isc_info_svc_server_version, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_IMPLEMENTATION", isc_info_svc_implementation, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV", isc_info_svc_get_env, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV_LOCK", isc_info_svc_get_env_lock, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_ENV_MSG", isc_info_svc_get_env_msg, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_USER_DBPATH", isc_info_svc_user_dbpath, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_SVR_DB_INFO", isc_info_svc_svr_db_info, CONST_PERSISTENT);
-       REGISTER_LONG_CONSTANT("IBASE_SVC_GET_USERS", isc_info_svc_get_users, CONST_PERSISTENT);
-#endif                       
+
+       php_ibase_query_minit(INIT_FUNC_ARGS_PASSTHRU);
+       php_ibase_blobs_minit(INIT_FUNC_ARGS_PASSTHRU);
+       php_ibase_events_minit(INIT_FUNC_ARGS_PASSTHRU);
+       php_ibase_service_minit(INIT_FUNC_ARGS_PASSTHRU);
+       
        return SUCCESS;          
 }                            
                              
@@ -1096,2043 +836,304 @@ PHP_FUNCTION(ibase_drop_db)
 }
 /* }}} */
 
-static int _php_ibase_alloc_array(ibase_array **ib_arrayp, XSQLDA *sqlda, /* {{{ */
-       isc_db_handle link, isc_tr_handle trans, unsigned short *array_cnt TSRMLS_DC)
-{
-       unsigned short i, n;
-       ibase_array *ar;
-
-       /* first check if we have any arrays at all */
-       for (i = *array_cnt = 0; i < sqlda->sqld; ++i) {
-               if ((sqlda->sqlvar[i].sqltype & ~1) == SQL_ARRAY) {
-                       ++*array_cnt;
-               }
-       }
-       if (! *array_cnt) return SUCCESS;
-       
-       ar = safe_emalloc(sizeof(ibase_array), *array_cnt, 0);
-       
-       for (i = n = 0; i < sqlda->sqld; ++i) {
-               unsigned short dim;
-               unsigned long ar_size = 1;
-               XSQLVAR *var = &sqlda->sqlvar[i];
-               
-               if ((var->sqltype & ~1) == SQL_ARRAY) {
-                       ibase_array *a = &ar[n++];
-                       ISC_ARRAY_DESC *ar_desc = &a->ar_desc;
-                       
-                       if (isc_array_lookup_bounds(IB_STATUS, &link, &trans, var->relname,
-                                       var->sqlname, ar_desc)) {
-                               _php_ibase_error(TSRMLS_C);
-                               efree(ar);
-                               return FAILURE;
-                       }
+/* {{{ proto resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]])
+   Start a transaction over one or several databases */
 
-                       switch (ar_desc->array_desc_dtype) {
-                               case blr_text:
-                               case blr_text2:
-                                       a->el_type = SQL_TEXT;
-                                       a->el_size = ar_desc->array_desc_length;
-                                       break;
-                               case blr_short:
-                                       a->el_type = SQL_SHORT;
-                                       a->el_size = sizeof(short);
-                                       break;
-                               case blr_long:
-                                       a->el_type = SQL_LONG;
-                                       a->el_size = sizeof(ISC_LONG);
-                                       break;
-                               case blr_float:
-                                       a->el_type = SQL_FLOAT;
-                                       a->el_size = sizeof(float);
-                                       break;
-                               case blr_double:
-                                       a->el_type = SQL_DOUBLE;
-                                       a->el_size = sizeof(double);
-                                       break;
-#ifdef blr_int64
-                               case blr_int64:
-                                       a->el_type = SQL_INT64;
-                                       a->el_size = sizeof(ISC_INT64);
-                                       break;
-#endif
-#ifndef blr_timestamp
-                               case blr_date:
-                                       a->el_type = SQL_DATE;
-                                       a->el_size = sizeof(ISC_QUAD);
-                                       break;
-#else
-                               case blr_timestamp:
-                                       a->el_type = SQL_TIMESTAMP;
-                                       a->el_size = sizeof(ISC_TIMESTAMP);
-                                       break;
-                               case blr_sql_date:
-                                       a->el_type = SQL_TYPE_DATE;
-                                       a->el_size = sizeof(ISC_DATE);
-                                       break;
-                               case blr_sql_time:
-                                       a->el_type = SQL_TYPE_TIME;
-                                       a->el_size = sizeof(ISC_TIME);
-                                       break;
-#endif                                         
-                               case blr_varying:
-                               case blr_varying2:
-                                       /**
-                                        * IB has a strange way of handling VARCHAR arrays. It doesn't store
-                                        * the length in the first short, as with VARCHAR fields. It does, 
-                                        * however, expect the extra short to be allocated for each element.
-                                        */
-                                       a->el_type = SQL_TEXT;
-                                       a->el_size = ar_desc->array_desc_length + sizeof(short);
-                                       break;
-                               case blr_quad:
-                               case blr_blob_id:
-                               case blr_cstring:
-                               case blr_cstring2:
-                                       /**
-                                        * These types are mentioned as array types in the manual, but I 
-                                        * wouldn't know how to create an array field with any of these
-                                        * types. I assume these types are not applicable to arrays, and
-                                        * were mentioned erroneously.
-                                        */
-                               default:
-                                       _php_ibase_module_error("Unsupported array type %d in relation '%s' column '%s'"
-                                               TSRMLS_CC, ar_desc->array_desc_dtype, var->relname, var->sqlname);
-                                       efree(ar);
-                                       return FAILURE;
-                       } /* switch array_desc_type */
-                       
-                       /* calculate elements count */
-                       for (dim = 0; dim < ar_desc->array_desc_dimensions; dim++) {
-                               ar_size *= 1 + ar_desc->array_desc_bounds[dim].array_bound_upper
-                                       -ar_desc->array_desc_bounds[dim].array_bound_lower;
-                       }
-                       a->ar_size = a->el_size * ar_size;
-               } /* if SQL_ARRAY */
-       } /* for column */
-       *ib_arrayp = ar;
-       return SUCCESS;
-}
-/* }}} */
+#define TPB_MAX_SIZE (8*sizeof(char))
 
-/* allocate and prepare query */
-static int _php_ibase_alloc_query(ibase_query *ib_query, ibase_db_link *link, /* {{{ */
-       ibase_trans *trans, char *query, unsigned short dialect, int trans_res_id TSRMLS_DC)
+PHP_FUNCTION(ibase_trans)
 {
-       static char info_type[] = {isc_info_sql_stmt_type};
-       char result[8];
-       
-       ib_query->link = link;
-       ib_query->trans = trans;
-       ib_query->result_res_id = 0;
-       ib_query->stmt = NULL;
-       ib_query->in_array = NULL;
-       ib_query->out_array = NULL;
-       ib_query->dialect = dialect;
-       ib_query->query = estrdup(query);
-       ib_query->trans_res_id = trans_res_id;
+       unsigned short i, argn, link_cnt = 0, tpb_len = 0;
+       char last_tpb[TPB_MAX_SIZE];
+       ibase_db_link **ib_link = NULL;
+       ibase_trans *ib_trans;
+       isc_tr_handle tr_handle = NULL;
+       ISC_STATUS result;
        
-       if (isc_dsql_allocate_statement(IB_STATUS, &link->handle, &ib_query->stmt)) {
-               _php_ibase_error(TSRMLS_C);
-               goto _php_ibase_alloc_query_error;
-       }
-
-       ib_query->out_sqlda = (XSQLDA *) emalloc(XSQLDA_LENGTH(1));
-       ib_query->out_sqlda->sqln = 1;
-       ib_query->out_sqlda->version = SQLDA_CURRENT_VERSION;
+       RESET_ERRMSG;
 
-       if (isc_dsql_prepare(IB_STATUS, &ib_query->trans->handle, &ib_query->stmt,
-                       0, query, dialect, ib_query->out_sqlda)) {
-               _php_ibase_error(TSRMLS_C);
-               goto _php_ibase_alloc_query_error;
-       }
+       argn = ZEND_NUM_ARGS();
 
-       /* find out what kind of statement was prepared */
-       if (isc_dsql_sql_info(IB_STATUS, &ib_query->stmt, sizeof(info_type), 
-                       info_type, sizeof(result), result)) {
-               _php_ibase_error(TSRMLS_C);
-               goto _php_ibase_alloc_query_error;
-       }
-       ib_query->statement_type = result[3];   
+       /* (1+argn) is an upper bound for the number of links this trans connects to */
+       ib_link = (ibase_db_link **) do_alloca(sizeof(ibase_db_link *) * (1+argn));
        
-       /* not enough output variables ? */
-       if (ib_query->out_sqlda->sqld > ib_query->out_sqlda->sqln) {
-               ib_query->out_sqlda = erealloc(ib_query->out_sqlda, XSQLDA_LENGTH(ib_query->out_sqlda->sqld));
-               ib_query->out_sqlda->sqln = ib_query->out_sqlda->sqld;
-               ib_query->out_sqlda->version = SQLDA_CURRENT_VERSION;
-               if (isc_dsql_describe(IB_STATUS, &ib_query->stmt, SQLDA_CURRENT_VERSION, ib_query->out_sqlda)) {
-                       _php_ibase_error(TSRMLS_C);
-                       goto _php_ibase_alloc_query_error;
-               }
-       }
+       if (argn > 0) {
+               long trans_argl = 0;
+               char *tpb;
+               ISC_TEB *teb;
+               zval ***args = (zval ***) do_alloca(sizeof(zval **) * argn);
 
-       /* maybe have input placeholders? */
-       ib_query->in_sqlda = emalloc(XSQLDA_LENGTH(1));
-       ib_query->in_sqlda->sqln = 1;
-       ib_query->in_sqlda->version = SQLDA_CURRENT_VERSION;
-       if (isc_dsql_describe_bind(IB_STATUS, &ib_query->stmt, SQLDA_CURRENT_VERSION, ib_query->in_sqlda)) {
-               _php_ibase_error(TSRMLS_C);
-               goto _php_ibase_alloc_query_error;
-       }
-       
-       /* not enough input variables ? */
-       if (ib_query->in_sqlda->sqln < ib_query->in_sqlda->sqld) {
-               ib_query->in_sqlda = erealloc(ib_query->in_sqlda, XSQLDA_LENGTH(ib_query->in_sqlda->sqld));
-               ib_query->in_sqlda->sqln = ib_query->in_sqlda->sqld;
-               ib_query->in_sqlda->version = SQLDA_CURRENT_VERSION;
-
-               if (isc_dsql_describe_bind(IB_STATUS, &ib_query->stmt,
-                               SQLDA_CURRENT_VERSION, ib_query->in_sqlda)) {
-                       _php_ibase_error(TSRMLS_C);
-                       goto _php_ibase_alloc_query_error;
+               if (zend_get_parameters_array_ex(argn, args) == FAILURE) {
+                       free_alloca(args);
+                       free_alloca(ib_link);
+                       RETURN_FALSE;
                }
-       }
-       
-       /* no, haven't placeholders at all */
-       if (ib_query->in_sqlda->sqld == 0) {
-               efree(ib_query->in_sqlda);
-               ib_query->in_sqlda = NULL;
-       } else if (FAILURE == _php_ibase_alloc_array(&ib_query->in_array, ib_query->in_sqlda,
-                       link->handle, trans->handle, &ib_query->in_array_cnt TSRMLS_CC)) {
-               goto _php_ibase_alloc_query_error;
-       }
 
-       if (ib_query->out_sqlda->sqld == 0) {
-               efree(ib_query->out_sqlda);
-               ib_query->out_sqlda = NULL;
-       } else  if (FAILURE == _php_ibase_alloc_array(&ib_query->out_array, ib_query->out_sqlda,
-                       link->handle, trans->handle, &ib_query->out_array_cnt TSRMLS_CC)) {
-               goto _php_ibase_alloc_query_error;
-       }
+               teb = (ISC_TEB *) do_alloca(sizeof(ISC_TEB) * argn);
+               tpb = (char *) do_alloca(TPB_MAX_SIZE * argn);
 
-       return SUCCESS;
-       
-_php_ibase_alloc_query_error:
+               /* enumerate all the arguments: assume every non-resource argument 
+                  specifies modifiers for the link ids that follow it */
+               for (i = 0; i < argn; ++i) {
+                       
+                       if (Z_TYPE_PP(args[i]) == IS_RESOURCE) {
+                               
+                               ZEND_FETCH_RESOURCE2(ib_link[link_cnt], ibase_db_link *, args[i], -1, 
+                                       "InterBase link", le_link, le_plink);
        
-       if (ib_query->out_sqlda) {
-               efree(ib_query->out_sqlda);
-       }
-       if (ib_query->in_sqlda) {
-               efree(ib_query->in_sqlda);
-       }
-       if (ib_query->out_array) {
-               efree(ib_query->out_array);
-       }
-       if (ib_query->query) {
-               efree(ib_query->query);
-       }
-       return FAILURE;
-}
-/* }}} */
-
-static int _php_ibase_bind_array(zval *val, char *buf, unsigned long buf_size, /* {{{ */
-       ibase_array *array, int dim TSRMLS_DC)
-{
-       zval null_val, *pnull_val = &null_val;
-       int u_bound = array->ar_desc.array_desc_bounds[dim].array_bound_upper,
-               l_bound = array->ar_desc.array_desc_bounds[dim].array_bound_lower,
-               dim_len = 1 + u_bound - l_bound;
-
-       ZVAL_NULL(pnull_val);
+                               /* copy the most recent modifier string into tbp[] */
+                               memcpy(&tpb[TPB_MAX_SIZE * link_cnt], last_tpb, TPB_MAX_SIZE);
 
-       if (dim < array->ar_desc.array_desc_dimensions) {
-               unsigned long slice_size = buf_size / dim_len;
-               unsigned short i;
-               zval **subval = &val;
+                               /* add a database handle to the TEB with the most recently specified set of modifiers */
+                               teb[link_cnt].db_ptr = &ib_link[link_cnt]->handle;
+                               teb[link_cnt].tpb_len = tpb_len;
+                               teb[link_cnt].tpb_ptr = &tpb[TPB_MAX_SIZE * link_cnt];
                                
-               if (Z_TYPE_P(val) == IS_ARRAY) {
-                       zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
-               }
+                               ++link_cnt;
+                               
+                       } else {
+                               
+                               tpb_len = 0;
 
-               for (i = 0; i < dim_len; ++i) { 
+                               convert_to_long_ex(args[i]);
+                               trans_argl = Z_LVAL_PP(args[i]);
 
-                       if (Z_TYPE_P(val) == IS_ARRAY &&
-                               zend_hash_get_current_data(Z_ARRVAL_P(val), (void *) &subval) == FAILURE)
-                       {
-                               subval = &pnull_val;
-                       }
+                               if (trans_argl != PHP_IBASE_DEFAULT) {
+                                       last_tpb[tpb_len++] = isc_tpb_version3;
 
-                       if (_php_ibase_bind_array(*subval, buf, slice_size, array, dim+1 TSRMLS_CC) == FAILURE)
-                       {
-                               return FAILURE;
-                       }
-                       buf += slice_size;
+                                       /* access mode */
+                                       if (PHP_IBASE_READ == (trans_argl & PHP_IBASE_READ)) {
+                                               last_tpb[tpb_len++] = isc_tpb_read;
+                                       } else if (PHP_IBASE_WRITE == (trans_argl & PHP_IBASE_WRITE)) {
+                                               last_tpb[tpb_len++] = isc_tpb_write;
+                                       }
 
-                       if (Z_TYPE_P(val) == IS_ARRAY) {
-                               zend_hash_move_forward(Z_ARRVAL_P(val));
+                                       /* isolation level */
+                                       if (PHP_IBASE_COMMITTED == (trans_argl & PHP_IBASE_COMMITTED)) {
+                                               last_tpb[tpb_len++] = isc_tpb_read_committed;
+                                               if (PHP_IBASE_REC_VERSION == (trans_argl & PHP_IBASE_REC_VERSION)) {
+                                                       last_tpb[tpb_len++] = isc_tpb_rec_version;
+                                               } else if (PHP_IBASE_REC_NO_VERSION == (trans_argl & PHP_IBASE_REC_NO_VERSION)) {
+                                                       last_tpb[tpb_len++] = isc_tpb_no_rec_version; 
+                                               }       
+                                       } else if (PHP_IBASE_CONSISTENCY == (trans_argl & PHP_IBASE_CONSISTENCY)) {
+                                               last_tpb[tpb_len++] = isc_tpb_consistency;
+                                       } else if (PHP_IBASE_CONCURRENCY == (trans_argl & PHP_IBASE_CONCURRENCY)) {
+                                               last_tpb[tpb_len++] = isc_tpb_concurrency;
+                                       }
+                                       
+                                       /* lock resolution */
+                                       if (PHP_IBASE_NOWAIT == (trans_argl & PHP_IBASE_NOWAIT)) {
+                                               last_tpb[tpb_len++] = isc_tpb_nowait;
+                                       } else if (PHP_IBASE_WAIT == (trans_argl & PHP_IBASE_WAIT)) {
+                                               last_tpb[tpb_len++] = isc_tpb_wait;
+                                       }
+                               }
                        }
+               }       
+                                       
+               if (link_cnt > 0) {
+                       result = isc_start_multiple(IB_STATUS, &tr_handle, link_cnt, teb);
                }
 
-               if (Z_TYPE_P(val) == IS_ARRAY) {
-                       zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
-               }
+               free_alloca(args);
+               free_alloca(tpb);
+               free_alloca(teb);
+       }
 
-       } else {
-               /* expect a single value */
-               if (Z_TYPE_P(val) == IS_NULL) {
-                       memset(buf, 0, buf_size);
-               } else if (array->ar_desc.array_desc_scale < 0) {
+       if (link_cnt == 0) {
+               link_cnt = 1;
+               ZEND_FETCH_RESOURCE2(ib_link[0], ibase_db_link *, NULL, IBG(default_link), "InterBase link", 
+                       le_link, le_plink);
+               result = isc_start_transaction(IB_STATUS, &tr_handle, 1, &ib_link[0]->handle, tpb_len, last_tpb);
+       }
+       
+       /* start the transaction */
+       if (result) {
+               _php_ibase_error(TSRMLS_C);
+               free_alloca(ib_link);
+               RETURN_FALSE;
+       }
 
-                       /* no coercion for array types */
-                       double l;
-                       
-                       convert_to_double(val);
-
-                       if (Z_DVAL_P(val) > 0) {
-                               l = Z_DVAL_P(val) * pow(10, -array->ar_desc.array_desc_scale) + .5;
-                       } else {
-                               l = Z_DVAL_P(val) * pow(10, -array->ar_desc.array_desc_scale) - .5;
-                       }
-
-                       switch (array->el_type) {
-                               case SQL_SHORT:
-                                       if (l > SHRT_MAX || l < SHRT_MIN) {
-                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
-                                               return FAILURE;
-                                       }
-                                       *(short*) buf = (short) l;
-                                       break;
-                               case SQL_LONG:
-                                       if (l > ISC_LONG_MAX || l < ISC_LONG_MIN) {
-                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
-                                               return FAILURE;
-                                       }
-                                       *(ISC_LONG*) buf = (ISC_LONG) l;
-                                       break;
-#ifdef SQL_INT64
-                               case SQL_INT64:
-                                       {
-                                               long double l;
-                                               
-                                               convert_to_string(val);
-                                               
-                                               if (!sscanf(Z_STRVAL_P(val), "%Lf", &l)) {
-                                                       _php_ibase_module_error("Cannot convert '%s' to long double"
-                                                               TSRMLS_CC, Z_STRVAL_P(val));
-                                                       return FAILURE;
-                                               }
-                                               
-                                               if (l > 0) {
-                                                       *(ISC_INT64 *) buf = (ISC_INT64) (l * pow(10, 
-                                                               -array->ar_desc.array_desc_scale) + .5);
-                                               } else {
-                                                       *(ISC_INT64 *) buf = (ISC_INT64) (l * pow(10, 
-                                                               -array->ar_desc.array_desc_scale) - .5);
-                                               }
-                                       }
-                                       break;
-#endif
-                       }                       
-               } else {
-                       struct tm t = { 0, 0, 0, 0, 0, 0 };
-
-                       switch (array->el_type) {
-                               unsigned short n;
-                               
-                               case SQL_SHORT:
-                                       convert_to_long(val);
-                                       if (Z_LVAL_P(val) > SHRT_MAX || Z_LVAL_P(val) < SHRT_MIN) {
-                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
-                                               return FAILURE;
-                                       }
-                                       *(short *) buf = (short) Z_LVAL_P(val);
-                                       break;
-                               case SQL_LONG:
-                                       convert_to_long(val);
-#if (SIZEOF_LONG > 4)
-                                       if (Z_LVAL_P(val) > ISC_LONG_MAX || Z_LVAL_P(val) < ISC_LONG_MIN) {
-                                               _php_ibase_module_error("Array parameter exceeds field width" TSRMLS_CC);
-                                               return FAILURE;
-                                       }
-#endif
-                                       *(ISC_LONG *) buf = (ISC_LONG) Z_LVAL_P(val);
-                                       break;
-#ifdef SQL_INT64
-                               case SQL_INT64:
-#if (SIZEOF_LONG >= 8)
-                                       convert_to_long(val);
-                                       *(long *) buf = Z_LVAL_P(val);
-#else
-                                       {
-                                               ISC_INT64 l;
-
-                                               convert_to_string(val);
-                                               if (!sscanf(Z_STRVAL_P(val), "%" LL_MASK "d", &l)) {
-                                                       _php_ibase_module_error("Cannot convert '%s' to long integer"
-                                                               TSRMLS_CC, Z_STRVAL_P(val));
-                                                       return FAILURE;
-                                               } else {
-                                                       *(ISC_INT64 *) buf = l;
-                                               }
-                                       }
-#endif
-                                       break;
-#endif
-                               case SQL_FLOAT:
-                                       convert_to_double(val);
-                                       *(float*) buf = (float) Z_DVAL_P(val);
-                                       break;
-                               case SQL_DOUBLE:
-                                       convert_to_double(val);
-                                       *(double*) buf = Z_DVAL_P(val);
-                                       break;
-#ifndef SQL_TIMESTAMP
-                               case SQL_DATE:
-#else
-                               case SQL_TIMESTAMP:
-#endif
-                                       convert_to_string(val);
-#ifdef HAVE_STRPTIME
-                                       strptime(Z_STRVAL_P(val), IBG(timestampformat), &t);
-#else
-                                       n = sscanf(Z_STRVAL_P(val), "%d%*[/]%d%*[/]%d %d%*[:]%d%*[:]%d", 
-                                               &t.tm_mon, &t.tm_mday, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec);
-       
-                                       if (n != 3 && n != 6) {
-                                               _php_ibase_module_error("Invalid date/time format (expected 3 or 6 fields, got %d."
-                                                       " Use format 'm/d/Y H:i:s'. You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val));
-                                               return FAILURE;
-                                       }
-                                       t.tm_year -= 1900;
-                                       t.tm_mon--;
-#endif
-#ifndef SQL_TIMESTAMP
-                                       isc_encode_date(&t, (ISC_QUAD *) buf);
-                                       break;
-#else
-                                       isc_encode_timestamp(&t, (ISC_TIMESTAMP * ) buf);
-                                       break;
-                               case SQL_TYPE_DATE:
-                                       convert_to_string(val);
-#ifdef HAVE_STRPTIME
-                                       strptime(Z_STRVAL_P(val), IBG(dateformat), &t);
-#else
-                                       n = sscanf(Z_STRVAL_P(val), "%d%*[/]%d%*[/]%d", &t.tm_mon, &t.tm_mday, &t.tm_year);
-       
-                                       if (n != 3) {
-                                               _php_ibase_module_error("Invalid date format (expected 3 fields, got %d. "
-                                                       "Use format 'm/d/Y' You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val));
-                                               return FAILURE;
-                                       }
-                                       t.tm_year -= 1900;
-                                       t.tm_mon--;
-#endif
-                                       isc_encode_sql_date(&t, (ISC_DATE *) buf);
-                                       break;
-                               case SQL_TYPE_TIME:
-                                       convert_to_string(val);
-#ifdef HAVE_STRPTIME
-                                       strptime(Z_STRVAL_P(val), IBG(timeformat), &t);
-#else
-                                       n = sscanf(Z_STRVAL_P(val), "%d%*[:]%d%*[:]%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
-       
-                                       if (n != 3) {
-                                               _php_ibase_module_error("Invalid time format (expected 3 fields, got %d. "
-                                                       "Use format 'H:i:s'. You gave '%s')" TSRMLS_CC, n, Z_STRVAL_P(val));
-                                               return FAILURE;
-                                       }
-#endif
-                                       isc_encode_sql_time(&t, (ISC_TIME *) buf);
-                                       break;
-#endif
-                               default:
-                                       convert_to_string(val);
-                                       strncpy(buf, Z_STRVAL_P(val), array->el_size);
-                                       buf[array->el_size-1] = '\0';
-                       }       
-               }
-       }
-       return SUCCESS;
-}              
-/* }}} */
-
-static int _php_ibase_bind(XSQLDA *sqlda, zval **b_vars, BIND_BUF *buf, /* {{{ */
-       ibase_query *ib_query TSRMLS_DC)
-{
-       int i, array_cnt = 0, rv = SUCCESS;
-
-       for (i = 0; i < sqlda->sqld; ++i) { /* bound vars */
-               
-               zval *b_var = b_vars[i];
-               XSQLVAR *var = &sqlda->sqlvar[i];
-
-               var->sqlind = &buf[i].sqlind;
-
-               if (Z_TYPE_P(b_var) == IS_NULL) {
-                       if ((var->sqltype & 1) != 1) {
-                               _php_ibase_module_error("Parameter %d must have a value" TSRMLS_CC, i+1);
-                               rv = FAILURE;
-                       }
-                       buf[i].sqlind = -1;
-                       if ((var->sqltype & ~1) == SQL_ARRAY) ++array_cnt;
-               } else {
-                       buf[i].sqlind = 0;
-
-                       if (var->sqlscale < 0) {
-                               /*
-                                 DECIMAL or NUMERIC field are stored internally as scaled integers.
-                                 Coerce it to string and let InterBase's internal routines handle it.
-                               */
-                               var->sqltype = SQL_TEXT;
-                       }
-
-                       var->sqldata = (void*)&buf[i];
-                       
-                       switch (var->sqltype & ~1) {
-                               struct tm t;
-
-                               case SQL_SHORT:
-                                       convert_to_long(b_var);
-                                       if (Z_LVAL_P(b_var) > SHRT_MAX || Z_LVAL_P(b_var) < SHRT_MIN) {
-                                               _php_ibase_module_error("Parameter %d exceeds field width" TSRMLS_CC, i+1);
-                                               rv = FAILURE;
-                                               break;
-                                       }
-                                       buf[i].val.sval = (short) Z_LVAL_P(b_var);
-                                       break;
-                               case SQL_LONG:
-                                       convert_to_long(b_var);
-#if (SIZEOF_LONG > 4)
-                                       /* ISC_LONG is always 32-bit */
-                                       if (Z_LVAL_P(b_var) > ISC_LONG_MAX || Z_LVAL_P(b_var) < ISC_LONG_MIN) {
-                                               _php_ibase_module_error("Parameter %d exceeds field width" TSRMLS_CC, i+1);
-                                               rv = FAILURE;
-                                               break;
-                                       }
-#endif
-                                       buf[i].val.lval = (ISC_LONG) Z_LVAL_P(b_var);
-                                       break;
-#if defined(SQL_INT64) && (SIZEOF_LONG == 8)
-                               case SQL_INT64:
-                                       convert_to_long(b_var);
-                                       var->sqldata = (void *) &Z_LVAL_P(b_var);
-                                       break;
-#endif
-                               case SQL_FLOAT:
-                                       convert_to_double(b_var);
-                                       buf[i].val.fval = (float) Z_DVAL_P(b_var);
-                                       break;
-                               case SQL_DOUBLE:
-                                       convert_to_double(b_var);
-                                       var->sqldata = (void *) &Z_DVAL_P(b_var);
-                                       break;
-
-                               case SQL_DATE: /* == SQL_TIMESTAMP: */
-#ifdef SQL_TIMESTAMP
-                               case SQL_TYPE_DATE:
-                               case SQL_TYPE_TIME:
-#endif
-                                       if (Z_TYPE_P(b_var) == IS_LONG) {
-                                               /* insert timestamp directly */
-                                               t = *gmtime(&Z_LVAL_P(b_var));
-                                       } else {
-#ifndef HAVE_STRPTIME
-#ifndef SQL_TIMESTAMP
-                                               int n;
-
-                                               t.tm_year = t.tm_mon = t.tm_mday = t.tm_hour = t.tm_min = t.tm_sec = 0;
-
-                                               n = sscanf(Z_STRVAL_P(b_var), "%d%*[/]%d%*[/]%d %d%*[:]%d%*[:]%d",
-                                                       &t.tm_mon, &t.tm_mday, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec);
-
-                                               if (n != 3 && n != 6) {
-                                                       _php_ibase_module_error("Parameter %d: invalid date/time format "
-                                                               "(expected 3 or 6 fields, got %d. Use format m/d/Y H:i:s. You gave '%s')"
-                                                               TSRMLS_CC, i+1, n, Z_STRVAL_P(b_var));
-                                                       rv = FAILURE;
-                                                       break;
-                                               }
-                                               t.tm_year -= 1900;
-                                               t.tm_mon--;
-#else
-                                               goto php_ibase_bind_default; /* let IB string handling take over */
-#endif
-#else
-                                               convert_to_string(b_var);
-
-                                               switch (var->sqltype & ~1) {
-                                                       default: /* == case SQL_TIMESTAMP/SQL_DATE: */
-                                                               strptime(Z_STRVAL_P(b_var), IBG(timestampformat), &t);
-                                                               break;
-                                                       case SQL_TYPE_DATE:
-                                                               strptime(Z_STRVAL_P(b_var), IBG(dateformat), &t);
-                                                               break;
-                                                       case SQL_TYPE_TIME:
-                                                               strptime(Z_STRVAL_P(b_var), IBG(timeformat), &t);
-                                                               break;
-                                               }
-#endif
-                                       }
-
-#ifndef SQL_TIMESTAMP
-                                       isc_encode_date(&t, &buf[i].val.qval);
-#else
-                                       switch (var->sqltype & ~1) {
-                                               default: /* == case SQL_TIMESTAMP */
-                                                       isc_encode_timestamp(&t, &buf[i].val.tsval);
-                                                       break;
-                                               case SQL_TYPE_DATE:
-                                                       isc_encode_sql_date(&t, &buf[i].val.dtval);
-                                                       break;
-                                               case SQL_TYPE_TIME:
-                                                       isc_encode_sql_time(&t, &buf[i].val.tmval);
-                                                       break;
-#endif
-                                       }
-                                       break;
-                               case SQL_BLOB:
-
-                                       convert_to_string(b_var);
-
-                                       if (Z_STRLEN_P(b_var) != BLOB_ID_LEN ||
-                                               !_php_ibase_string_to_quad(Z_STRVAL_P(b_var), &buf[i].val.qval)) {
-
-                                               ibase_blob ib_blob = { NULL, BLOB_INPUT };
-
-                                               if (isc_create_blob(IB_STATUS, &ib_query->link->handle,
-                                                               &ib_query->trans->handle, &ib_blob.bl_handle, &ib_blob.bl_qd)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       return FAILURE;
-                                               }
-
-                                               if (_php_ibase_blob_add(&b_var, &ib_blob TSRMLS_CC) != SUCCESS) {
-                                                       return FAILURE;
-                                               }
-                                                       
-                                               if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       return FAILURE;
-                                               }
-                                               buf[i].val.qval = ib_blob.bl_qd;
-                                       }
-                                       break;
-                               case SQL_ARRAY:
-                                       if (Z_TYPE_P(b_var) != IS_ARRAY) {
-                                               convert_to_string(b_var);
-               
-                                               if (Z_STRLEN_P(b_var) != BLOB_ID_LEN ||
-                                                       !_php_ibase_string_to_quad(Z_STRVAL_P(b_var), &buf[i].val.qval)) {
-
-                                                       _php_ibase_module_error("Parameter %d: invalid array ID" TSRMLS_CC,i+1);
-                                                       rv = FAILURE;
-                                               }
-                                       } else {
-                                               /* convert the array data into something IB can understand */
-                                               ibase_array *ar = &ib_query->in_array[array_cnt++];
-                                               void *array_data = emalloc(ar->ar_size);
-                                               ISC_QUAD array_id = { 0, 0 };
-
-                                               if (FAILURE == _php_ibase_bind_array(b_var, array_data, ar->ar_size, 
-                                                               ar, 0 TSRMLS_CC)) {
-                                                       _php_ibase_module_error("Parameter %d: failed to bind array argument"
-                                                               TSRMLS_CC,i+1);
-                                                       efree(array_data);
-                                                       rv = FAILURE;
-                                                       break;
-                                               }
-                                                       
-                                               if (isc_array_put_slice(IB_STATUS, &ib_query->link->handle, &ib_query->trans->handle, 
-                                                               &array_id, &ar->ar_desc, array_data, &ar->ar_size)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       efree(array_data);
-                                                       return FAILURE;
-                                               }
-                                               buf[i].val.qval = array_id;
-                                               efree(array_data);
-                                       }                               
-                                       break;
-                               default:
-php_ibase_bind_default:
-                                       convert_to_string(b_var);
-                                       var->sqldata = Z_STRVAL_P(b_var);
-                                       var->sqllen      = Z_STRLEN_P(b_var);
-                                       var->sqltype = SQL_TEXT;
-                       } /* switch */
-               } /* if */
-       } /* for */
-       return rv;
-}
-/* }}} */
-
-static void _php_ibase_alloc_xsqlda(XSQLDA *sqlda) /* {{{ */
-{
-       int i;
-
-       for (i = 0; i < sqlda->sqld; i++) {
-               XSQLVAR *var = &sqlda->sqlvar[i];
-
-               switch (var->sqltype & ~1) {
-                       case SQL_TEXT:
-                               var->sqldata = safe_emalloc(sizeof(char), var->sqllen, 0);
-                               break;
-                       case SQL_VARYING:
-                               var->sqldata = safe_emalloc(sizeof(char), var->sqllen + sizeof(short), 0);
-                               break;
-                       case SQL_SHORT:
-                               var->sqldata = emalloc(sizeof(short));
-                               break;
-                       case SQL_LONG:
-                               var->sqldata = emalloc(sizeof(ISC_LONG));
-                               break;
-                       case SQL_FLOAT:
-                               var->sqldata = emalloc(sizeof(float));
-                                       break;
-                       case SQL_DOUBLE:
-                               var->sqldata = emalloc(sizeof(double));
-                               break;
-#ifdef SQL_INT64
-                       case SQL_INT64:
-                               var->sqldata = emalloc(sizeof(ISC_INT64));
-                               break;
-#endif
-#ifdef SQL_TIMESTAMP
-                       case SQL_TIMESTAMP:
-                               var->sqldata = emalloc(sizeof(ISC_TIMESTAMP));
-                               break;
-                       case SQL_TYPE_DATE:
-                               var->sqldata = emalloc(sizeof(ISC_DATE));
-                               break;
-                       case SQL_TYPE_TIME:
-                               var->sqldata = emalloc(sizeof(ISC_TIME));
-                               break;
-#else
-                       case SQL_DATE:
-#endif
-                       case SQL_BLOB:
-                       case SQL_ARRAY:
-                               var->sqldata = emalloc(sizeof(ISC_QUAD));
-                               break;
-               } /* switch */
-
-               if (var->sqltype & 1) { /* sql NULL flag */
-                       var->sqlind = emalloc(sizeof(short));
-               } else {
-                       var->sqlind = NULL;
-               }
-       } /* for */
-}
-/* }}} */
-
-static int _php_ibase_exec(INTERNAL_FUNCTION_PARAMETERS, ibase_result **ib_resultp, /* {{{ */
-       ibase_query *ib_query, int argc, zval **args)
-{
-       XSQLDA *in_sqlda = NULL, *out_sqlda = NULL;
-       BIND_BUF *bind_buf = NULL;
-       int rv = FAILURE;
-       static char info_count[] = { isc_info_sql_records };
-       char result[64];
-       ISC_STATUS isc_result;
-       
-       RESET_ERRMSG;
-
-       if (argc > 0 && args != NULL) {
-               SEPARATE_ZVAL(args);
-       }
-       
-       switch (ib_query->statement_type) {
-               isc_tr_handle tr;
+       /* register the transaction in our own data structures */
+       ib_trans = (ibase_trans *) safe_emalloc((link_cnt-1), sizeof(ibase_db_link *), sizeof(ibase_trans));
+       ib_trans->handle = tr_handle;
+       ib_trans->link_cnt = link_cnt;
+       ib_trans->affected_rows = 0;
+       for (i = 0; i < link_cnt; ++i) {
                ibase_tr_list **l;
-               ibase_trans *trans;
-               
-               case isc_info_sql_stmt_start_trans:
-               
-                       /* a SET TRANSACTION statement should be executed with a NULL trans handle */
-                       tr = NULL;
-                       
-                       if (isc_dsql_execute_immediate(IB_STATUS, &ib_query->link->handle, &tr, 0, 
-                                       ib_query->query, ib_query->dialect, NULL)) {
-                               _php_ibase_error(TSRMLS_C);
-                               goto _php_ibase_exec_error;
-                       }
-                       
-                       trans = (ibase_trans *) emalloc(sizeof(ibase_trans));
-                       trans->handle = tr;
-                       trans->link_cnt = 1;
-                       trans->affected_rows = 0;
-                       trans->db_link[0] = ib_query->link;
-       
-                       if (ib_query->link->tr_list == NULL) {
-                               ib_query->link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
-                               ib_query->link->tr_list->trans = NULL;
-                               ib_query->link->tr_list->next = NULL;
-                       }
-                       
-                       /* link the transaction into the connection-transaction list */
-                       for (l = &ib_query->link->tr_list; *l != NULL; l = &(*l)->next);
-                       *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
-                       (*l)->trans = trans;
-                       (*l)->next = NULL;
-       
-                       ZEND_REGISTER_RESOURCE(return_value, trans, le_trans);
-
-                       return SUCCESS;
-
-               case isc_info_sql_stmt_commit:
-               case isc_info_sql_stmt_rollback:
-               
-                       if (isc_dsql_execute_immediate(IB_STATUS, &ib_query->link->handle, 
-                                       &ib_query->trans->handle, 0, ib_query->query, ib_query->dialect, NULL)) {
-                               _php_ibase_error(TSRMLS_C);
-                               goto _php_ibase_exec_error;
-                       }
-
-                       if (ib_query->trans->handle == NULL && ib_query->trans_res_id != 0) {
-                               /* transaction was released by the query and was a registered resource, 
-                                  so we have to release it */
-                               zend_list_delete(ib_query->trans_res_id);
-                       }
-                       return SUCCESS;
-               
-               default:
-                       RETVAL_TRUE;
-       }
-
-       /* allocate sqlda and output buffers */
-       if (ib_query->out_sqlda) { /* output variables in select, select for update */
-               ibase_result *res;
+               ib_trans->db_link[i] = ib_link[i];
                
-               IBDEBUG("Query wants XSQLDA for output");
-               res = emalloc(sizeof(ibase_result)+sizeof(ibase_array)*max(0,ib_query->out_array_cnt-1));
-               res->link = ib_query->link;
-               res->trans = ib_query->trans;
-               res->stmt = ib_query->stmt; 
-               res->statement_type = ib_query->statement_type;
-               res->has_more_rows = 1;
-
-               out_sqlda = res->out_sqlda = emalloc(XSQLDA_LENGTH(ib_query->out_sqlda->sqld));
-               memcpy(out_sqlda, ib_query->out_sqlda, XSQLDA_LENGTH(ib_query->out_sqlda->sqld));
-               _php_ibase_alloc_xsqlda(out_sqlda);
-
-               if (ib_query->out_array) {
-                       memcpy(&res->out_array, ib_query->out_array, sizeof(ibase_array)*ib_query->out_array_cnt);
-               }
-               *ib_resultp = res;
-       }
-
-       if (ib_query->in_sqlda) { /* has placeholders */
-               IBDEBUG("Query wants XSQLDA for input");
-               if (ib_query->in_sqlda->sqld != argc) {
-                       _php_ibase_module_error("Placeholders (%d) and variables (%d) mismatch"
-                               TSRMLS_CC, ib_query->in_sqlda->sqld, argc);
-                       goto _php_ibase_exec_error;
-               }
-               in_sqlda = emalloc(XSQLDA_LENGTH(ib_query->in_sqlda->sqld));
-               memcpy(in_sqlda, ib_query->in_sqlda, XSQLDA_LENGTH(ib_query->in_sqlda->sqld));
-               bind_buf = safe_emalloc(sizeof(BIND_BUF), ib_query->in_sqlda->sqld, 0);
-               if (_php_ibase_bind(in_sqlda, args, bind_buf, ib_query TSRMLS_CC) == FAILURE) {
-                       IBDEBUG("Could not bind input XSQLDA");
-                       goto _php_ibase_exec_error;
+               /* the first item in the connection-transaction list is reserved for the default transaction */
+               if (ib_link[i]->tr_list == NULL) {
+                       ib_link[i]->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
+                       ib_link[i]->tr_list->trans = NULL;
+                       ib_link[i]->tr_list->next = NULL;
                }
-       }
-
-       if (ib_query->statement_type == isc_info_sql_stmt_exec_procedure) {
-               isc_result = isc_dsql_execute2(IB_STATUS, &ib_query->trans->handle,
-                       &ib_query->stmt, SQLDA_CURRENT_VERSION, in_sqlda, out_sqlda);
-       } else {
-               isc_result = isc_dsql_execute(IB_STATUS, &ib_query->trans->handle,
-                       &ib_query->stmt, SQLDA_CURRENT_VERSION, in_sqlda);
-       }
-       if (isc_result) {
-               IBDEBUG("Could not execute query");
-               _php_ibase_error(TSRMLS_C);
-               goto _php_ibase_exec_error;
-       }
-       ib_query->trans->affected_rows = 0;
-       
-       switch (ib_query->statement_type) {
-
-               unsigned long affected_rows;
-
-               case isc_info_sql_stmt_insert:
-               case isc_info_sql_stmt_update:
-               case isc_info_sql_stmt_delete:
-               case isc_info_sql_stmt_exec_procedure:
-               
-                       if (isc_dsql_sql_info(IB_STATUS, &ib_query->stmt, sizeof(info_count),
-                                       info_count, sizeof(result), result)) {
-                               _php_ibase_error(TSRMLS_C);
-                               goto _php_ibase_exec_error;
-                       }
 
-                       affected_rows = 0;
-                       
-                       if (result[0] == isc_info_sql_records) {
-                               unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
-       
-                               while (result[i] != isc_info_end && i < result_size) {
-                                       short len = (short)isc_vax_integer(&result[i+1],2);
-                                       if (result[i] != isc_info_req_select_count) {
-                                               affected_rows += isc_vax_integer(&result[i+3],len);
-                                       }
-                                       i += len+3;
-                               }
-                       }
-                       if (affected_rows > 0) {
-                               ib_query->trans->affected_rows = affected_rows;
-                               RETVAL_LONG(affected_rows);
-                       }
-       }
-
-       rv = SUCCESS;
-       
-_php_ibase_exec_error:
-       
-       if (in_sqlda) {
-               efree(in_sqlda);
-       }
-       if (bind_buf)
-               efree(bind_buf);
-
-       if (rv == FAILURE) {
-               if (*ib_resultp) {
-                       efree(*ib_resultp);
-                       *ib_resultp = NULL;
-               }
-               if (out_sqlda) {
-                       _php_ibase_free_xsqlda(out_sqlda);
-               }
-       }
-       
-       return rv;
-}
-/* }}} */
-
-/* {{{ proto resource ibase_trans([int trans_args [, resource link_identifier [, ... ], int trans_args [, resource link_identifier [, ... ]] [, ...]]])
-   Start a transaction over one or several databases */
-
-#define TPB_MAX_SIZE (8*sizeof(char))
-
-PHP_FUNCTION(ibase_trans)
-{
-       unsigned short i, argn, link_cnt = 0, tpb_len = 0;
-       char last_tpb[TPB_MAX_SIZE];
-       ibase_db_link **ib_link = NULL;
-       ibase_trans *ib_trans;
-       isc_tr_handle tr_handle = NULL;
-       ISC_STATUS result;
-       
-       RESET_ERRMSG;
-
-       argn = ZEND_NUM_ARGS();
-
-       /* (1+argn) is an upper bound for the number of links this trans connects to */
-       ib_link = (ibase_db_link **) do_alloca(sizeof(ibase_db_link *) * (1+argn));
-       
-       if (argn > 0) {
-               long trans_argl = 0;
-               char *tpb;
-               ISC_TEB *teb;
-               zval ***args = (zval ***) do_alloca(sizeof(zval **) * argn);
-
-               if (zend_get_parameters_array_ex(argn, args) == FAILURE) {
-                       free_alloca(args);
-                       free_alloca(ib_link);
-                       RETURN_FALSE;
-               }
-
-               teb = (ISC_TEB *) do_alloca(sizeof(ISC_TEB) * argn);
-               tpb = (char *) do_alloca(TPB_MAX_SIZE * argn);
-
-               /* enumerate all the arguments: assume every non-resource argument 
-                  specifies modifiers for the link ids that follow it */
-               for (i = 0; i < argn; ++i) {
-                       
-                       if (Z_TYPE_PP(args[i]) == IS_RESOURCE) {
-                               
-                               ZEND_FETCH_RESOURCE2(ib_link[link_cnt], ibase_db_link *, args[i], -1, 
-                                       "InterBase link", le_link, le_plink);
-       
-                               /* copy the most recent modifier string into tbp[] */
-                               memcpy(&tpb[TPB_MAX_SIZE * link_cnt], last_tpb, TPB_MAX_SIZE);
-
-                               /* add a database handle to the TEB with the most recently specified set of modifiers */
-                               teb[link_cnt].db_ptr = &ib_link[link_cnt]->handle;
-                               teb[link_cnt].tpb_len = tpb_len;
-                               teb[link_cnt].tpb_ptr = &tpb[TPB_MAX_SIZE * link_cnt];
-                               
-                               ++link_cnt;
-                               
-                       } else {
-                               
-                               tpb_len = 0;
-
-                               convert_to_long_ex(args[i]);
-                               trans_argl = Z_LVAL_PP(args[i]);
-
-                               if (trans_argl != PHP_IBASE_DEFAULT) {
-                                       last_tpb[tpb_len++] = isc_tpb_version3;
-
-                                       /* access mode */
-                                       if (PHP_IBASE_READ == (trans_argl & PHP_IBASE_READ)) {
-                                               last_tpb[tpb_len++] = isc_tpb_read;
-                                       } else if (PHP_IBASE_WRITE == (trans_argl & PHP_IBASE_WRITE)) {
-                                               last_tpb[tpb_len++] = isc_tpb_write;
-                                       }
-
-                                       /* isolation level */
-                                       if (PHP_IBASE_COMMITTED == (trans_argl & PHP_IBASE_COMMITTED)) {
-                                               last_tpb[tpb_len++] = isc_tpb_read_committed;
-                                               if (PHP_IBASE_REC_VERSION == (trans_argl & PHP_IBASE_REC_VERSION)) {
-                                                       last_tpb[tpb_len++] = isc_tpb_rec_version;
-                                               } else if (PHP_IBASE_REC_NO_VERSION == (trans_argl & PHP_IBASE_REC_NO_VERSION)) {
-                                                       last_tpb[tpb_len++] = isc_tpb_no_rec_version; 
-                                               }       
-                                       } else if (PHP_IBASE_CONSISTENCY == (trans_argl & PHP_IBASE_CONSISTENCY)) {
-                                               last_tpb[tpb_len++] = isc_tpb_consistency;
-                                       } else if (PHP_IBASE_CONCURRENCY == (trans_argl & PHP_IBASE_CONCURRENCY)) {
-                                               last_tpb[tpb_len++] = isc_tpb_concurrency;
-                                       }
-                                       
-                                       /* lock resolution */
-                                       if (PHP_IBASE_NOWAIT == (trans_argl & PHP_IBASE_NOWAIT)) {
-                                               last_tpb[tpb_len++] = isc_tpb_nowait;
-                                       } else if (PHP_IBASE_WAIT == (trans_argl & PHP_IBASE_WAIT)) {
-                                               last_tpb[tpb_len++] = isc_tpb_wait;
-                                       }
-                               }
-                       }
-               }       
-                                       
-               if (link_cnt > 0) {
-                       result = isc_start_multiple(IB_STATUS, &tr_handle, link_cnt, teb);
-               }
-
-               free_alloca(args);
-               free_alloca(tpb);
-               free_alloca(teb);
-       }
-
-       if (link_cnt == 0) {
-               link_cnt = 1;
-               ZEND_FETCH_RESOURCE2(ib_link[0], ibase_db_link *, NULL, IBG(default_link), "InterBase link", 
-                       le_link, le_plink);
-               result = isc_start_transaction(IB_STATUS, &tr_handle, 1, &ib_link[0]->handle, tpb_len, last_tpb);
-       }
-       
-       /* start the transaction */
-       if (result) {
-               _php_ibase_error(TSRMLS_C);
-               free_alloca(ib_link);
-               RETURN_FALSE;
-       }
-
-       /* register the transaction in our own data structures */
-       ib_trans = (ibase_trans *) safe_emalloc((link_cnt-1), sizeof(ibase_db_link *), sizeof(ibase_trans));
-       ib_trans->handle = tr_handle;
-       ib_trans->link_cnt = link_cnt;
-       ib_trans->affected_rows = 0;
-       for (i = 0; i < link_cnt; ++i) {
-               ibase_tr_list **l;
-               ib_trans->db_link[i] = ib_link[i];
-               
-               /* the first item in the connection-transaction list is reserved for the default transaction */
-               if (ib_link[i]->tr_list == NULL) {
-                       ib_link[i]->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
-                       ib_link[i]->tr_list->trans = NULL;
-                       ib_link[i]->tr_list->next = NULL;
-               }
-
-               /* link the transaction into the connection-transaction list */
-               for (l = &ib_link[i]->tr_list; *l != NULL; l = &(*l)->next);
-               *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
-               (*l)->trans = ib_trans;
-               (*l)->next = NULL;
+               /* link the transaction into the connection-transaction list */
+               for (l = &ib_link[i]->tr_list; *l != NULL; l = &(*l)->next);
+               *l = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
+               (*l)->trans = ib_trans;
+               (*l)->next = NULL;
        }
        free_alloca(ib_link);
        ZEND_REGISTER_RESOURCE(return_value, ib_trans, le_trans);
 }
 /* }}} */
 
-int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC) /* {{{ */
-{
-       if (ib_link == NULL) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid database link");
-               return FAILURE;
-       }
-
-       /* the first item in the connection-transaction list is reserved for the default transaction */
-       if (ib_link->tr_list == NULL) {
-               ib_link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
-               ib_link->tr_list->trans = NULL;
-               ib_link->tr_list->next = NULL;
-       }
-
-       if (*trans == NULL) {
-               ibase_trans *tr = ib_link->tr_list->trans;
-
-               if (tr == NULL) {
-                       tr = (ibase_trans *) emalloc(sizeof(ibase_trans));
-                       tr->handle = NULL;
-                       tr->link_cnt = 1;
-                       tr->affected_rows = 0;
-                       tr->db_link[0] = ib_link;
-                       ib_link->tr_list->trans = tr;
-               }
-               if (tr->handle == NULL) {
-                       if (isc_start_transaction(IB_STATUS, &tr->handle, 1, &ib_link->handle, 0, NULL)) {
-                               _php_ibase_error(TSRMLS_C);
-                               return FAILURE;
-                       }
-               }
-               *trans = tr;
-       }
-       return SUCCESS;
-}
-/* }}} */
-
-static void _php_ibase_trans_end(INTERNAL_FUNCTION_PARAMETERS, int commit) /* {{{ */
-{
-       ibase_trans *trans = NULL;
-       int res_id = 0;
-       ISC_STATUS result;
-
-       RESET_ERRMSG;
-
-       switch (ZEND_NUM_ARGS()) {
-
-               ibase_db_link *ib_link;
-               zval **arg;
-
-               case 0:
-                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", 
-                               le_link, le_plink);
-                       if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
-                               /* this link doesn't have a default transaction */
-                               _php_ibase_module_error("Default link has no default transaction" TSRMLS_CC);
-                               RETURN_FALSE;
-                       }
-                       trans = ib_link->tr_list->trans;
-                       break;
-
-               case 1: 
-                       if (zend_get_parameters_ex(1, &arg) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       /* one id was passed, could be db or trans id */
-                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, arg, &ib_link, &trans);
-                       if (trans != NULL) {                    
-                               convert_to_long_ex(arg);
-                               res_id = Z_LVAL_PP(arg);
-
-                       } else {
-                               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, arg, -1, "InterBase link", 
-                                       le_link, le_plink);
-
-                               if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
-                                       /* this link doesn't have a default transaction */
-                                       _php_ibase_module_error("Link has no default transaction" TSRMLS_CC);
-                                       RETURN_FALSE;
-                               }
-                               trans = ib_link->tr_list->trans;
-                       }
-                       break;
-
-               default:
-                       WRONG_PARAM_COUNT;
-                       break;
-       }
-
-       switch (commit) {
-               
-               default: /* == case ROLLBACK: */
-                       result = isc_rollback_transaction(IB_STATUS, &trans->handle);
-                       break;
-               case COMMIT:
-                       result = isc_commit_transaction(IB_STATUS, &trans->handle);
-                       break;
-#if HAVE_IBASE6_API
-               case (ROLLBACK | RETAIN):
-                       result = isc_rollback_retaining(IB_STATUS, &trans->handle);
-                       break;
-#endif
-               case (COMMIT | RETAIN):
-                       result = isc_commit_retaining(IB_STATUS, &trans->handle);
-                       break;
-       }
-       
-       if (result) {
-               _php_ibase_error(TSRMLS_C);
-               RETURN_FALSE;
-       }
-
-       /* Don't try to destroy implicitly opened transaction from list... */
-       if ( (commit & RETAIN) == 0 && res_id != 0) {
-               zend_list_delete(res_id);
-       }
-       RETURN_TRUE;
-}
-/* }}} */
-
-/* {{{ proto bool ibase_commit( resource link_identifier )
-   Commit transaction */
-PHP_FUNCTION(ibase_commit)
-{
-       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT);
-}
-/* }}} */
-
-/* {{{ proto bool ibase_rollback( resource link_identifier )
-   Rollback transaction */
-PHP_FUNCTION(ibase_rollback)
-{
-       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK);
-}
-/* }}} */
-
-/* {{{ proto bool ibase_commit_ret( resource link_identifier )
-   Commit transaction and retain the transaction context */
-PHP_FUNCTION(ibase_commit_ret)
-{
-       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT | RETAIN);
-}
-/* }}} */
-
-/* {{{ proto bool ibase_rollback_ret( resource link_identifier )
-   Rollback transaction and retain the transaction context */
-#if HAVE_IBASE6_API
-PHP_FUNCTION(ibase_rollback_ret)
-{
-       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK | RETAIN);
-}
-#endif
-/* }}} */
-
-/* {{{ proto mixed ibase_query([resource link_identifier, [ resource link_identifier, ]] string query [, mixed bind_arg [, mixed bind_arg [, ...]]])
-   Execute a query */
-PHP_FUNCTION(ibase_query)
-{
-       zval ***args, **bind_args = NULL;
-       int i, bind_n = 0, trans_res_id = 0;
-       ibase_db_link *ib_link = NULL;
-       ibase_trans *trans = NULL;
-       ibase_query ib_query = { NULL, NULL, 0, 0 };
-       ibase_result *result = NULL;
-       char *query;
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS() < 1) {
-               WRONG_PARAM_COUNT;
-       }
-
-       /* use stack to avoid leaks */
-       args = (zval ***) do_alloca(sizeof(zval **) * ZEND_NUM_ARGS());
-
-       RETVAL_FALSE;
-
-       if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
-               goto ibase_query_end;
-
-       }
-       
-       i = 0;
-       while (Z_TYPE_PP(args[i++]) != IS_STRING) {
-               if (i >= ZEND_NUM_ARGS()) {
-                       _php_ibase_module_error("Query argument missing" TSRMLS_CC);
-                       goto ibase_query_end;
-
-               }
-       }
-
-       convert_to_string_ex(args[i-1]);
-       query = Z_STRVAL_PP(args[i-1]);
-
-       /* find out if the first one or two arguments refer to either a link id, 
-          a trans id or both */
-       switch (i) {
-               case 1:
-
-                       /* no link ids were passed: if there's no default link, use exec_immediate() with
-                          a NULL handle; this will enable the use of CREATE DATABASE statements. */
-                       if (IBG(default_link) == -1) {
-                               isc_db_handle db = NULL;
-                               isc_tr_handle trans = NULL;
-
-                               if (isc_dsql_execute_immediate(IB_STATUS, &db, &trans, 0, query, 
-                                               SQL_DIALECT_CURRENT, NULL)) {
-                                       _php_ibase_error(TSRMLS_C);
-                                       goto ibase_query_end;
-                               }
-                               
-                               /* has a new database been created ? */
-                               if (db != NULL) {
-
-                                       if ((IBG(max_links) != -1) && (IBG(num_links) >= IBG(max_links))) {                                     
-
-                                               /* too many links already ? => close it up immediately */
-                                               if (isc_detach_database(IB_STATUS, &db)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       goto ibase_query_end;
-                                               }
-                                       } else {
-                                                       
-                                               /* register the link as a resource; unfortunately, we cannot register 
-                                                  it in the hash table, because we don't know the connection params */
-                                               ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link));
-                                               ib_link->handle = db;
-                                               ib_link->dialect = SQL_DIALECT_CURRENT;
-                                               ib_link->tr_list = NULL;
-                                               ib_link->event_head = NULL;
-                                                               
-                                               ZEND_REGISTER_RESOURCE(return_value, ib_link, le_link);
-                                               zend_list_addref(Z_LVAL_P(return_value));
-                                               IBG(default_link) = Z_LVAL_P(return_value);
-                                               IBG(num_links)++;
-                                               goto ibase_query_end;
-                                       }
-                               }
-                               RETURN_TRUE;
-                       }                                       
-                               
-                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link",
-                               le_link, le_plink);
-                       break;                  
-               case 2:
-                       /* one id was passed, could be db or trans id */
-                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, args[0], &ib_link, &trans);
-
-                       if (trans != NULL) {
-                               /* argument was a trans id */
-                               convert_to_long_ex(args[0]);
-                               trans_res_id = Z_LVAL_PP(args[0]);
-                       }
-                       break;  
-               case 3:
-                       /* two ids were passed, first should be link and second should be trans; */
-                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link*, args[0], -1, "InterBase link",
-                               le_link, le_plink);
-                       ZEND_FETCH_RESOURCE(trans, ibase_trans*, args[1], -1, "InterBase transaction", le_trans);
-
-                       convert_to_long_ex(args[1]);
-                       trans_res_id = Z_LVAL_PP(args[1]);
-
-                       break;
-               default:
-                       /* more than two arguments preceed the SQL string */
-                       _php_ibase_module_error("Invalid arguments" TSRMLS_CC);
-                       goto ibase_query_end;
-       }
-                       
-       if (ZEND_NUM_ARGS() > i) { /* have variables to bind */
-               bind_n = ZEND_NUM_ARGS() - i;
-               bind_args = args[i];
-       }
-
-       /* open default transaction */
-       if (ib_link == NULL || _php_ibase_def_trans(ib_link, &trans TSRMLS_CC) == FAILURE) {
-               goto ibase_query_end;
-       }
-
-       if (FAILURE == _php_ibase_alloc_query(&ib_query, ib_link, trans, query, ib_link->dialect,
-                       trans_res_id TSRMLS_CC)) {
-               goto ibase_query_end;
-       }
-
-       do {
-               if (FAILURE == _php_ibase_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, &result, &ib_query, 
-                               bind_n, bind_args)) {
-                       break;
-               }
-       
-               if (result != NULL) { /* statement returns a result */
-                       result->type = QUERY_RESULT;    
-       
-                       /* EXECUTE PROCEDURE returns only one row => statement can be released immediately */
-                       if (ib_query.statement_type != isc_info_sql_stmt_exec_procedure) {
-                               ib_query.stmt = NULL; /* keep stmt when free query */
-                       }
-                       ZEND_REGISTER_RESOURCE(return_value, result, le_result);
-               }
-       } while (0);
-       _php_ibase_free_query(&ib_query TSRMLS_CC);
-
-ibase_query_end:
-       free_alloca(args);
-}
-/* }}} */
-
-/* {{{ proto int ibase_affected_rows( [ resource link_identifier ] )
-   Returns the number of rows affected by the previous INSERT, UPDATE or DELETE statement */
-PHP_FUNCTION(ibase_affected_rows)
-{
-       ibase_trans *trans = NULL;
-
-       RESET_ERRMSG;
-
-       switch (ZEND_NUM_ARGS()) {
-
-               ibase_db_link *ib_link;
-               zval **arg;
-
-               case 0:
-                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link",
-                               le_link, le_plink);
-                       if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
-                               RETURN_FALSE;
-                       }
-                       trans = ib_link->tr_list->trans;
-                       break;
-
-               case 1: 
-                       if (zend_get_parameters_ex(1, &arg) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       /* one id was passed, could be db or trans id */
-                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, arg, &ib_link, &trans);
-                       if (trans == NULL) {                    
-                               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, arg, -1, "InterBase link",
-                                       le_link, le_plink);
-
-                               if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
-                                       RETURN_FALSE;
-                               }
-                               trans = ib_link->tr_list->trans;
-                       }
-                       break;
-
-               default:
-                       WRONG_PARAM_COUNT;
-                       break;
-       }
-       RETURN_LONG(trans->affected_rows);
-}
-/* }}} */
-
-/* {{{ proto int ibase_num_rows( resource result_identifier ) 
-   Return the number of rows that are available in a result */
-#if abies_0
-PHP_FUNCTION(ibase_num_rows) 
-{
-       /**
-        * As this function relies on the InterBase API function isc_dsql_sql_info()
-        * which has a couple of limitations (which I hope will be fixed in future 
-        * releases of Firebird), this function is fairly useless. I'm leaving it
-        * in place for people who can live with the limitations, which I only 
-        * found out about after I had implemented it anyway.
-        *
-        * Currently, there's no way to determine how many rows can be fetched from
-        * a cursor. The only number that _can_ be determined is the number of rows
-        * that have already been pre-fetched by the client library. 
-        * This implies the following:
-        * - num_rows() always returns zero before the first fetch;
-        * - num_rows() for SELECT ... FOR UPDATE is broken -> never returns a
-        *   higher number than the number of records fetched so far (no pre-fetch);
-        * - the result of num_rows() for other statements is merely a lower bound 
-        *   on the number of records => calling ibase_num_rows() again after a couple
-        *   of fetches will most likely return a new (higher) figure for large result 
-        *   sets.
-        */
-       
-       zval **result_arg;
-       ibase_result *ib_result;
-       static char info_count[] = {isc_info_sql_records};
-       char result[64];
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &result_arg) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-
-       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
-       
-       if (isc_dsql_sql_info(IB_STATUS, &ib_result->stmt, sizeof(info_count), info_count, 
-                       sizeof(result), result)) {
-               _php_ibase_error(TSRMLS_C);
-               RETURN_FALSE;
-       }
-       
-       if (result[0] == isc_info_sql_records) {
-               unsigned i = 3, result_size = isc_vax_integer(&result[1],2);
-
-               while (result[i] != isc_info_end && i < result_size) {
-                       short len = (short)isc_vax_integer(&result[i+1],2);
-                       if (result[i] == isc_info_req_select_count) {
-                               RETURN_LONG(isc_vax_integer(&result[i+3],len));
-                       }
-                       i += len+3;
-               }
-       }                                       
-}
-#endif
-/* }}} */
-
-static int _php_ibase_var_zval(zval *val, void *data, int type, int len, /* {{{ */
-       int scale, int flag TSRMLS_DC)
-{
-#ifdef SQL_INT64
-       static ISC_INT64 const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000, 
-               1000000000, LL_LIT(10000000000),LL_LIT(100000000000),LL_LIT(10000000000000),LL_LIT(100000000000000),
-               LL_LIT(1000000000000000),LL_LIT(1000000000000000),LL_LIT(1000000000000000000) };
-#else 
-       static long const scales[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 100000000, 1000000000,
-               1000000000 };
-#endif         
-
-       switch (type & ~1) {
-               unsigned short l;
-               long n;
-               char string_data[255];
-               struct tm t;
-               char *format;
-
-               case SQL_VARYING:
-                       len = ((IBVARY *) data)->vary_length;
-                       data = ((IBVARY *) data)->vary_string;
-                       /* no break */
-               case SQL_TEXT:
-                       if (PG(magic_quotes_runtime)) {
-                               Z_STRVAL_P(val) = php_addslashes(data, len, &Z_STRLEN_P(val), 0 TSRMLS_CC);
-                               Z_TYPE_P(val) = IS_STRING;
-                       } else {
-                               ZVAL_STRINGL(val,(char *) data,len,1);
-                       }
-                       break;
-               case SQL_SHORT:
-                       n = *(short *) data;
-                       goto _sql_long;
-#ifdef SQL_INT64
-               case SQL_INT64:
-#if (SIZEOF_LONG >= 8)
-                       n = *(long *) data;
-                       goto _sql_long;
-#else
-                       if (scale == 0) {
-                               l = sprintf(string_data, "%" LL_MASK "d", *(ISC_INT64 *) data);
-                               ZVAL_STRINGL(val,string_data,l,1);
-                       } else {
-                               ISC_INT64 n = *(ISC_INT64 *) data, f = scales[-scale];
-
-                               if (n >= 0) {
-                                       l = sprintf(string_data, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -scale, n % f);
-                               } else if (n < -f) {
-                                       l = sprintf(string_data, "%" LL_MASK "d.%0*" LL_MASK "d", n / f, -scale, -n % f);                               
-                               } else {
-                                       l = sprintf(string_data, "-0.%0*" LL_MASK "d", -scale, -n % f);
-                               }
-                               ZVAL_STRINGL(val,string_data,l,1);
-                       }
-                       break;
-#endif
-#endif
-               case SQL_LONG:
-                       n = *(ISC_LONG *) data; 
-               _sql_long:
-                       if (scale == 0) {
-                               ZVAL_LONG(val,n);
-                       } else {
-                               long f = (long) scales[-scale];
-                               
-                               if (n >= 0) {
-                                       l = sprintf(string_data, "%ld.%0*ld", n / f, -scale,  n % f);
-                               } else if (n < -f) {
-                                       l = sprintf(string_data, "%ld.%0*ld", n / f, -scale,  -n % f);
-                               } else {
-                                       l = sprintf(string_data, "-0.%0*ld", -scale, -n % f);
-                               }
-                               ZVAL_STRINGL(val,string_data,l,1);
-                       }
-                       break;
-               case SQL_FLOAT:
-                       ZVAL_DOUBLE(val, *(float *) data);
-                       break;
-               case SQL_DOUBLE:
-                       ZVAL_DOUBLE(val, *(double *) data);
-                       break;
-               case SQL_DATE: /* == case SQL_TIMESTAMP: */
-                       format = IBG(timestampformat);
-#ifndef SQL_TIMESTAMP
-                       isc_decode_date((ISC_QUAD *) data, &t);
-#else
-                       isc_decode_timestamp((ISC_TIMESTAMP *) data, &t);
-                       goto format_date_time;
-               case SQL_TYPE_DATE:
-                       format = IBG(dateformat);
-                       isc_decode_sql_date((ISC_DATE *) data, &t);
-                       goto format_date_time;
-               case SQL_TYPE_TIME:
-                       format = IBG(timeformat);
-                       isc_decode_sql_time((ISC_TIME *) data, &t);
-
-format_date_time:
-#endif
-                       /*
-                         XXX - Might have to remove this later - seems that isc_decode_date()
-                          always sets tm_isdst to 0, sometimes incorrectly (InterBase 6 bug?)
-                       */
-                       t.tm_isdst = -1;
-#if HAVE_TM_ZONE
-                       t.tm_zone = tzname[0];
-#endif
-                       if (flag & PHP_IBASE_UNIXTIME) {
-                               ZVAL_LONG(val, mktime(&t));
-                       } else {
-#if HAVE_STRFTIME
-                               l = strftime(string_data, sizeof(string_data), format, &t);
-#else
-                               switch (type & ~1) {
-                                       default:
-                                               l = sprintf(string_data, "%02d/%02d/%4d %02d:%02d:%02d", t.tm_mon+1, t.tm_mday, 
-                                                       t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
-                                               break;
-#ifdef SQL_TIMESTAMP
-                                       case SQL_TYPE_DATE:
-                                               l = sprintf(string_data, "%02d/%02d/%4d", t.tm_mon + 1, t.tm_mday, t.tm_year+1900);
-                                               break;
-                                       case SQL_TYPE_TIME:
-                                               l = sprintf(string_data, "%02d:%02d:%02d", t.tm_hour, t.tm_min, t.tm_sec);
-                                               break;
-#endif
-                               }
-#endif
-                               ZVAL_STRINGL(val,string_data,l,1);
-                               break;
-                       }
-       } /* switch (type) */
-       return SUCCESS;
-}
-/* }}} */
-
-static int _php_ibase_arr_zval(zval *ar_zval, char *data, unsigned long data_size, /* {{{ */
-       ibase_array *ib_array, int dim, int flag TSRMLS_DC)
-{
-       /**
-        * Create multidimension array - recursion function
-        */
-       int 
-               u_bound = ib_array->ar_desc.array_desc_bounds[dim].array_bound_upper,
-               l_bound = ib_array->ar_desc.array_desc_bounds[dim].array_bound_lower,
-               dim_len = 1 + u_bound - l_bound;
-       unsigned short i;
-               
-       if (dim < ib_array->ar_desc.array_desc_dimensions) { /* array again */
-               unsigned long slice_size = data_size / dim_len;
-               
-               array_init(ar_zval);
-
-               for (i = 0; i < dim_len; ++i) {
-                       zval *slice_zval;
-                       ALLOC_INIT_ZVAL(slice_zval);
-
-                       /* recursion here */
-                       if (FAILURE == _php_ibase_arr_zval(slice_zval, data, slice_size, ib_array, dim + 1,
-                                       flag TSRMLS_CC)) {
-                               return FAILURE;
-                       }
-                       data += slice_size;
-                       
-                       add_index_zval(ar_zval,l_bound+i,slice_zval);
-               }
-       } else { /* data at last */
-               
-               if (FAILURE == _php_ibase_var_zval(ar_zval, data, ib_array->el_type,
-                               ib_array->ar_desc.array_desc_length, ib_array->ar_desc.array_desc_scale, flag TSRMLS_CC)) {
-                       return FAILURE;
-               }
-               
-               /* fix for peculiar handling of VARCHAR arrays;
-                  truncate the field to the cstring length */
-               if (ib_array->ar_desc.array_desc_dtype == blr_varying ||
-                       ib_array->ar_desc.array_desc_dtype == blr_varying2) {
-                               
-                       Z_STRLEN_P(ar_zval) = strlen(Z_STRVAL_P(ar_zval));
-               }
-       }
-       return SUCCESS;
-}
-/* }}} */
-
-static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type) /* {{{ */
-{
-       zval **result_arg, **flag_arg;
-       long i, array_cnt = 0, flag = 0;
-       ibase_result *ib_result;
-       
-       RESET_ERRMSG;
-       
-       switch (ZEND_NUM_ARGS()) {
-               case 1:
-                       if (FAILURE == zend_get_parameters_ex(1, &result_arg)) {
-                               RETURN_FALSE;
-                       }
-                       break;
-               case 2:
-                       if (FAILURE == zend_get_parameters_ex(2, &result_arg, &flag_arg)) {
-                               RETURN_FALSE;
-                       }
-                       convert_to_long_ex(flag_arg);
-                       flag = Z_LVAL_PP(flag_arg);
-                       break;
-               default:
-                       WRONG_PARAM_COUNT;
-                       break;
-       }
-
-       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
-
-       if (ib_result->out_sqlda == NULL || !ib_result->has_more_rows) {
-               RETURN_FALSE;
-       }
-
-       if (ib_result->statement_type != isc_info_sql_stmt_exec_procedure) {
-
-               if (isc_dsql_fetch(IB_STATUS, &ib_result->stmt, 1, ib_result->out_sqlda)) {
-       
-                       ib_result->has_more_rows = 0;
-                       if (IB_STATUS[0] && IB_STATUS[1]) { /* error in fetch */
-                               _php_ibase_error(TSRMLS_C);
-                       }
-                       RETURN_FALSE;
-               }
-       } else {
-               ib_result->has_more_rows = 0;
-       }       
-       
-       array_init(return_value);
-       
-       for (i = 0; i < ib_result->out_sqlda->sqld; ++i) {
-               XSQLVAR *var = &ib_result->out_sqlda->sqlvar[i];
-               
-               if (((var->sqltype & 1) == 0) || *var->sqlind != -1) {
-                       zval *result;
-                       ALLOC_INIT_ZVAL(result);
-
-                       switch (var->sqltype & ~1) {
-
-                               default:
-                                       _php_ibase_var_zval(result, var->sqldata, var->sqltype, var->sqllen,
-                                               var->sqlscale, flag TSRMLS_CC);
-                                       break;
-                               case SQL_BLOB:
-                                       if (flag & PHP_IBASE_FETCH_BLOBS) { /* fetch blob contents into hash */
-       
-                                               ibase_blob blob_handle;
-                                               unsigned long max_len = 0;
-                                               static char bl_items[] = {isc_info_blob_total_length};
-                                               char bl_info[20];
-                                               unsigned short i;
-       
-                                               blob_handle.bl_handle = NULL;
-                                               blob_handle.bl_qd = *(ISC_QUAD *) var->sqldata;
-                       
-                                               if (isc_open_blob(IB_STATUS, &ib_result->link->handle, &ib_result->trans->handle,
-                                                               &blob_handle.bl_handle, &blob_handle.bl_qd)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       goto _php_ibase_fetch_error;
-                                               }
-                                               
-                                               if (isc_blob_info(IB_STATUS, &blob_handle.bl_handle, sizeof(bl_items),
-                                                               bl_items, sizeof(bl_info), bl_info)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       goto _php_ibase_fetch_error;
-                                               }
-                                               
-                                               /* find total length of blob's data */
-                                               for (i = 0; i < sizeof(bl_info); ) {
-                                                       unsigned short item_len;
-                                                       char item = bl_info[i++];
-       
-                                                       if (item == isc_info_end || item == isc_info_truncated || 
-                                                               item == isc_info_error || i >= sizeof(bl_info)) {
-
-                                                               _php_ibase_module_error("Could not determine BLOB size (internal error)"
-                                                                       TSRMLS_CC);
-                                                               goto _php_ibase_fetch_error;
-                                                       }                                                               
-
-                                                       item_len = (unsigned short) isc_vax_integer(&bl_info[i], 2);
-
-                                                       if (item == isc_info_blob_total_length) {
-                                                               max_len = isc_vax_integer(&bl_info[i+2], item_len);
-                                                               break;
-                                                       }
-                                                       i += item_len+2;
-                                               }
-                                               
-                                               if (max_len == 0) {
-                                                       ZVAL_STRING(result, "", 1);
-                                               } else if (SUCCESS != _php_ibase_blob_get(result, &blob_handle, 
-                                                               max_len TSRMLS_CC)) {
-                                                       goto _php_ibase_fetch_error;
-                                               }
-                                               
-                                               if (isc_close_blob(IB_STATUS, &blob_handle.bl_handle)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       goto _php_ibase_fetch_error;
-                                               }
-       
-                                       } else { /* blob id only */
-                                               ISC_QUAD bl_qd = *(ISC_QUAD *) var->sqldata;
-                                               ZVAL_STRINGL(result,_php_ibase_quad_to_string(bl_qd), BLOB_ID_LEN, 0);
-                                       }
-                                       break;
-                               case SQL_ARRAY:
-                                       if (flag & PHP_IBASE_FETCH_ARRAYS) { /* array can be *huge* so only fetch if asked */
-                                               ISC_QUAD ar_qd = *(ISC_QUAD *) var->sqldata;
-                                               ibase_array *ib_array = &ib_result->out_array[array_cnt++];
-                                               void *ar_data = emalloc(ib_array->ar_size);
-                                               
-                                               if (isc_array_get_slice(IB_STATUS, &ib_result->link->handle, 
-                                                               &ib_result->trans->handle, &ar_qd, &ib_array->ar_desc,
-                                                               ar_data, &ib_array->ar_size)) {
-                                                       _php_ibase_error(TSRMLS_C);
-                                                       efree(ar_data);
-                                                       goto _php_ibase_fetch_error;
-                                               }
-
-                                               if (FAILURE == _php_ibase_arr_zval(result, ar_data, ib_array->ar_size, ib_array,
-                                                               0, flag TSRMLS_CC)) {
-                                                       efree(ar_data);
-                                                       goto _php_ibase_fetch_error;
-                                               }
-                                               efree(ar_data);
-
-                                       } else { /* blob id only */
-                                               ISC_QUAD ar_qd = *(ISC_QUAD *) var->sqldata;
-                                               ZVAL_STRINGL(result,_php_ibase_quad_to_string(ar_qd), BLOB_ID_LEN, 0);
-                                       }
-                                       break;
-                               _php_ibase_fetch_error:
-                                       zval_dtor(result);
-                                       FREE_ZVAL(result);
-                                       RETURN_FALSE;
-                       } /* switch */
-
-                       if (fetch_type & FETCH_ROW) {
-                               add_index_zval(return_value, i, result);
-                       } else {
-                               add_assoc_zval(return_value, var->aliasname, result);
-                       }
-               } else {
-                       if (fetch_type & FETCH_ROW) {
-                               add_index_null(return_value, i);
-                       } else {
-                               add_assoc_null(return_value, var->aliasname);
-                       }
-               }
-       } /* for field */
-}
-/* }}} */
-
-/* {{{ proto array ibase_fetch_row(resource result [, int fetch_flags])
-   Fetch a row  from the results of a query */
-PHP_FUNCTION(ibase_fetch_row)
-{
-       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ROW);
-}
-/* }}} */
-
-/* {{{ proto array ibase_fetch_assoc(resource result [, int fetch_flags])
-   Fetch a row  from the results of a query */
-PHP_FUNCTION(ibase_fetch_assoc)
-{
-       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY);
-}
-/* }}} */
-
-/* {{{ proto object ibase_fetch_object(resource result [, int fetch_flags])
-   Fetch a object from the results of a query */
-PHP_FUNCTION(ibase_fetch_object)
-{
-       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY);
-
-       if (Z_TYPE_P(return_value) == IS_ARRAY) {
-               object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value));
-       }
-}
-/* }}} */
-
-
-/* {{{ proto bool ibase_name_result(resource result, string name)
-   Assign a name to a result for use with ... WHERE CURRENT OF <name> statements */
-PHP_FUNCTION(ibase_name_result)
-{
-       zval **result_arg, **name_arg;
-       ibase_result *ib_result;
-
-       RESET_ERRMSG;
-       
-       if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &result_arg, &name_arg) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-
-       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
-       convert_to_string_ex(name_arg);
-       
-       if (isc_dsql_set_cursor_name(IB_STATUS, &ib_result->stmt, Z_STRVAL_PP(name_arg), 0)) {
-               _php_ibase_error(TSRMLS_C);
-               RETURN_FALSE;
-       }
-       RETURN_TRUE;
-}
-/* }}} */
-
-
-/* {{{ proto bool ibase_free_result(resource result)
-   Free the memory used by a result */
-PHP_FUNCTION(ibase_free_result)
+int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC) /* {{{ */
 {
-       zval **result_arg;
-       ibase_result *ib_result;
-
-       RESET_ERRMSG;
+       if (ib_link == NULL) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid database link");
+               return FAILURE;
+       }
 
-       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &result_arg) == FAILURE) {
-               WRONG_PARAM_COUNT;
+       /* the first item in the connection-transaction list is reserved for the default transaction */
+       if (ib_link->tr_list == NULL) {
+               ib_link->tr_list = (ibase_tr_list *) emalloc(sizeof(ibase_tr_list));
+               ib_link->tr_list->trans = NULL;
+               ib_link->tr_list->next = NULL;
        }
 
-       ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
-       zend_list_delete(Z_LVAL_PP(result_arg));
-       RETURN_TRUE;
+       if (*trans == NULL) {
+               ibase_trans *tr = ib_link->tr_list->trans;
+
+               if (tr == NULL) {
+                       tr = (ibase_trans *) emalloc(sizeof(ibase_trans));
+                       tr->handle = NULL;
+                       tr->link_cnt = 1;
+                       tr->affected_rows = 0;
+                       tr->db_link[0] = ib_link;
+                       ib_link->tr_list->trans = tr;
+               }
+               if (tr->handle == NULL) {
+                       if (isc_start_transaction(IB_STATUS, &tr->handle, 1, &ib_link->handle, 0, NULL)) {
+                               _php_ibase_error(TSRMLS_C);
+                               return FAILURE;
+                       }
+               }
+               *trans = tr;
+       }
+       return SUCCESS;
 }
 /* }}} */
 
-/* {{{ proto resource ibase_prepare([resource link_identifier, ] string query)
-   Prepare a query for later execution */
-PHP_FUNCTION(ibase_prepare)
+static void _php_ibase_trans_end(INTERNAL_FUNCTION_PARAMETERS, int commit) /* {{{ */
 {
-       zval **link_arg, **trans_arg, **query_arg;
-       ibase_db_link *ib_link;
        ibase_trans *trans = NULL;
-       int trans_res_id = 0;
-       ibase_query *ib_query;
-       char *query;
+       int res_id = 0;
+       ISC_STATUS result;
 
        RESET_ERRMSG;
 
        switch (ZEND_NUM_ARGS()) {
-               case 1:
-                       if (zend_get_parameters_ex(1, &query_arg) == FAILURE) {
+
+               ibase_db_link *ib_link;
+               zval **arg;
+
+               case 0:
+                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link", 
+                               le_link, le_plink);
+                       if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
+                               /* this link doesn't have a default transaction */
+                               _php_ibase_module_error("Default link has no default transaction" TSRMLS_CC);
                                RETURN_FALSE;
                        }
-                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, NULL, IBG(default_link), "InterBase link",
-                               le_link, le_plink);
+                       trans = ib_link->tr_list->trans;
                        break;
-               case 2:
-                       if (zend_get_parameters_ex(2, &link_arg, &query_arg) == FAILURE) {
+
+               case 1: 
+                       if (zend_get_parameters_ex(1, &arg) == FAILURE) {
                                RETURN_FALSE;
                        }
-                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, link_arg, &ib_link, &trans);
-                       
-                       if (trans != NULL) {
-                               convert_to_long_ex(link_arg);
-                               trans_res_id = Z_LVAL_PP(link_arg);
+                       /* one id was passed, could be db or trans id */
+                       _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, arg, &ib_link, &trans);
+                       if (trans != NULL) {                    
+                               convert_to_long_ex(arg);
+                               res_id = Z_LVAL_PP(arg);
+
+                       } else {
+                               ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link *, arg, -1, "InterBase link", 
+                                       le_link, le_plink);
+
+                               if (ib_link->tr_list == NULL || ib_link->tr_list->trans == NULL) {
+                                       /* this link doesn't have a default transaction */
+                                       _php_ibase_module_error("Link has no default transaction" TSRMLS_CC);
+                                       RETURN_FALSE;
+                               }
+                               trans = ib_link->tr_list->trans;
                        }
-                               
                        break;
-               case 3:
-                       if (zend_get_parameters_ex(3, &link_arg, &trans_arg, &query_arg) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       ZEND_FETCH_RESOURCE2(ib_link, ibase_db_link*, link_arg, -1, "InterBase link",
-                               le_link, le_plink);
-                       ZEND_FETCH_RESOURCE(trans, ibase_trans*, trans_arg, -1, "InterBase transaction", le_trans);
-                       
-                       convert_to_long_ex(trans_arg);
-                       trans_res_id = Z_LVAL_PP(trans_arg);
-                       
-                       break;                  
+
                default:
                        WRONG_PARAM_COUNT;
                        break;
        }
-       
-       convert_to_string_ex(query_arg);
-       query = Z_STRVAL_PP(query_arg);
 
-       if (FAILURE == _php_ibase_def_trans(ib_link, &trans TSRMLS_CC)) {
-               RETURN_FALSE;
+       switch (commit) {
+               
+               default: /* == case ROLLBACK: */
+                       result = isc_rollback_transaction(IB_STATUS, &trans->handle);
+                       break;
+               case COMMIT:
+                       result = isc_commit_transaction(IB_STATUS, &trans->handle);
+                       break;
+#if HAVE_IBASE6_API
+               case (ROLLBACK | RETAIN):
+                       result = isc_rollback_retaining(IB_STATUS, &trans->handle);
+                       break;
+#endif
+               case (COMMIT | RETAIN):
+                       result = isc_commit_retaining(IB_STATUS, &trans->handle);
+                       break;
        }
        
-       ib_query = (ibase_query *) emalloc(sizeof(ibase_query));
-
-       if (FAILURE == _php_ibase_alloc_query(ib_query, ib_link, trans, query, ib_link->dialect,
-                       trans_res_id TSRMLS_CC)) {
-               efree(ib_query);
+       if (result) {
+               _php_ibase_error(TSRMLS_C);
                RETURN_FALSE;
        }
-       ZEND_REGISTER_RESOURCE(return_value, ib_query, le_query);
+
+       /* Don't try to destroy implicitly opened transaction from list... */
+       if ( (commit & RETAIN) == 0 && res_id != 0) {
+               zend_list_delete(res_id);
+       }
+       RETURN_TRUE;
 }
 /* }}} */
 
-/* {{{ proto mixed ibase_execute(resource query [, mixed bind_arg [, mixed bind_arg [, ...]]])
-   Execute a previously prepared query */
-PHP_FUNCTION(ibase_execute)
+/* {{{ proto bool ibase_commit( resource link_identifier )
+   Commit transaction */
+PHP_FUNCTION(ibase_commit)
 {
-       zval ***args, **bind_args = NULL;
-       ibase_query *ib_query;
-       ibase_result *result = NULL;
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS() < 1) {
-               WRONG_PARAM_COUNT;
-       }
-
-       /* use stack to avoid leaks */
-       args = (zval ***) do_alloca(ZEND_NUM_ARGS() * sizeof(zval **));
-       if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
-               free_alloca(args);
-               RETURN_FALSE;
-       }
-
-       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, args[0], -1, "InterBase query", le_query);
-
-       if (ZEND_NUM_ARGS() > 1) { /* have variables to bind */
-               bind_args = args[1];
-       }
-       
-       /* Have we used this cursor before and it's still open (exec proc has no cursor) ? */
-       if (ib_query->result_res_id != 0 && ib_query->statement_type != isc_info_sql_stmt_exec_procedure) {
-               IBDEBUG("Implicitly closing a cursor");
-               if (isc_dsql_free_statement(IB_STATUS, &ib_query->stmt, DSQL_close)) {
-                       _php_ibase_error(TSRMLS_C);
-               }
-               /* invalidate previous results returned by this query (not necessary for exec proc) */
-               zend_list_delete(ib_query->result_res_id);      
-       }
-               
-       if (FAILURE == _php_ibase_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU, &result, ib_query,
-                       ZEND_NUM_ARGS()-1, bind_args)) {
-               free_alloca(args);
-               RETURN_FALSE;
-       }
-       
-       /* free the query if trans handle was released */
-       if (ib_query->trans->handle == NULL) {
-               zend_list_delete(Z_LVAL_PP(args[0]));
-       }
-
-       free_alloca(args);
-       
-       if (result != NULL) {
-               result->type = EXECUTE_RESULT;
-               if (ib_query->statement_type == isc_info_sql_stmt_exec_procedure) {
-                       result->stmt = NULL;
-               }
-               ib_query->result_res_id = zend_list_insert(result, le_result);
-               RETURN_RESOURCE(ib_query->result_res_id);
-       }
+       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT);
 }
 /* }}} */
 
-/* {{{ proto bool ibase_free_query(resource query)
-   Free memory used by a query */
-PHP_FUNCTION(ibase_free_query)
+/* {{{ proto bool ibase_rollback( resource link_identifier )
+   Rollback transaction */
+PHP_FUNCTION(ibase_rollback)
 {
-       zval **query_arg;
-       ibase_query *ib_query;
-
-       RESET_ERRMSG;
+       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK);
+}
+/* }}} */
 
-       if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &query_arg) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
+/* {{{ proto bool ibase_commit_ret( resource link_identifier )
+   Commit transaction and retain the transaction context */
+PHP_FUNCTION(ibase_commit_ret)
+{
+       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT | RETAIN);
+}
+/* }}} */
 
-       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, query_arg, -1, "InterBase query", le_query);
-       zend_list_delete(Z_LVAL_PP(query_arg));
-       RETURN_TRUE;
+/* {{{ proto bool ibase_rollback_ret( resource link_identifier )
+   Rollback transaction and retain the transaction context */
+#if HAVE_IBASE6_API
+PHP_FUNCTION(ibase_rollback_ret)
+{
+       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK | RETAIN);
 }
+#endif
 /* }}} */
 
 #if HAVE_STRFTIME
@@ -3190,233 +1191,6 @@ PHP_FUNCTION(ibase_timefmt)
 /* }}} */
 #endif
 
-/* {{{ proto int ibase_num_fields(resource query_result)
-   Get the number of fields in result */
-PHP_FUNCTION(ibase_num_fields)
-{
-       zval **result;
-       int type;
-       XSQLDA *sqlda;
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS()!=1 || zend_get_parameters_ex(1, &result)==FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-       
-       zend_list_find(Z_LVAL_PP(result), &type);
-       
-       if (type == le_query) {
-               ibase_query *ib_query;
-
-               ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result, -1, "InterBase query", le_query);
-               sqlda = ib_query->out_sqlda;
-       } else {
-               ibase_result *ib_result;
-               
-               ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result, -1, "InterBase result", le_result);
-               sqlda = ib_result->out_sqlda;
-       }                                       
-
-       if (sqlda == NULL) {
-               RETURN_LONG(0);
-       } else {
-               RETURN_LONG(sqlda->sqld);
-       }
-}
-/* }}} */
-
-static void _php_ibase_field_info(zval *return_value, XSQLVAR *var) /* {{{ */
-{
-       unsigned short len;
-       char buf[16], *s = buf;
-       
-       array_init(return_value);
-
-       add_index_stringl(return_value, 0, var->sqlname, var->sqlname_length, 1);
-       add_assoc_stringl(return_value, "name", var->sqlname, var->sqlname_length, 1);
-
-       add_index_stringl(return_value, 1, var->aliasname, var->aliasname_length, 1);
-       add_assoc_stringl(return_value, "alias", var->aliasname, var->aliasname_length, 1);
-
-       add_index_stringl(return_value, 2, var->relname, var->relname_length, 1);
-       add_assoc_stringl(return_value, "relation", var->relname, var->relname_length, 1);
-
-       len = sprintf(buf, "%d", var->sqllen);
-       add_index_stringl(return_value, 3, buf, len, 1);
-       add_assoc_stringl(return_value, "length", buf, len, 1);
-
-       if (var->sqlscale < 0) {
-               unsigned short precision = 0;
-
-               switch (var->sqltype & ~1) {
-
-                       case SQL_SHORT:
-                               precision = 4;
-                               break;
-                       case SQL_LONG:
-                               precision = 9;
-                               break;
-#ifdef SQL_INT64
-                       case SQL_INT64:
-                               precision = 18;
-                               break;
-#endif
-               }
-               len = sprintf(buf, "NUMERIC(%d,%d)", precision, -var->sqlscale);
-               add_index_stringl(return_value, 4, s, len, 1);
-               add_assoc_stringl(return_value, "type", s, len, 1);
-       } else {
-               switch (var->sqltype & ~1) {
-                       case SQL_TEXT:
-                               s = "CHAR"; 
-                               break;
-                       case SQL_VARYING:
-                               s = "VARCHAR"; 
-                               break;
-                       case SQL_SHORT:
-                               s = "SMALLINT"; 
-                               break;
-                       case SQL_LONG:
-                               s = "INTEGER"; 
-                               break;
-                       case SQL_FLOAT:
-                               s = "FLOAT"; break;
-                       case SQL_DOUBLE:
-                       case SQL_D_FLOAT:
-                               s = "DOUBLE PRECISION"; break;
-#ifdef SQL_INT64
-                       case SQL_INT64: 
-                               s = "BIGINT"; 
-                               break;
-#endif
-#ifdef SQL_TIMESTAMP
-                       case SQL_TIMESTAMP:     
-                               s = "TIMESTAMP"; 
-                               break;
-                       case SQL_TYPE_DATE:
-                               s = "DATE";
-                               break;
-                       case SQL_TYPE_TIME:
-                               s = "TIME"; 
-                               break;
-#else
-                       case SQL_DATE:
-                               s = "DATE"; 
-                               break;
-#endif
-                       case SQL_BLOB:
-                               s = "BLOB"; 
-                               break;
-                       case SQL_ARRAY:
-                               s = "ARRAY";
-                               break;
-                               /* FIXME: provide more detailed information about the field type, field size
-                                * and array dimensions */
-                       case SQL_QUAD:
-                               s = "QUAD";
-                               break;
-               }
-               add_index_string(return_value, 4, s, 1);
-               add_assoc_string(return_value, "type", s, 1);
-       }
-}
-/* }}} */
-
-/* {{{ proto array ibase_field_info(resource query_result, int field_number)
-   Get information about a field */
-PHP_FUNCTION(ibase_field_info)
-{
-       zval **result_arg, **field_arg;
-       int type;
-       XSQLDA *sqlda;
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS()!=2 || zend_get_parameters_ex(2, &result_arg, &field_arg)==FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-       
-       zend_list_find(Z_LVAL_PP(result_arg), &type);
-       
-       if (type == le_query) {
-               ibase_query *ib_query;
-
-               ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result_arg, -1, "InterBase query", le_query);
-               sqlda = ib_query->out_sqlda;
-       } else {
-               ibase_result *ib_result;
-               
-               ZEND_FETCH_RESOURCE(ib_result, ibase_result *, result_arg, -1, "InterBase result", le_result);
-               sqlda = ib_result->out_sqlda;
-       }                                       
-
-       if (sqlda == NULL) {
-               _php_ibase_module_error("Trying to get field info from a non-select query" TSRMLS_CC);
-               RETURN_FALSE;
-       }
-
-       convert_to_long_ex(field_arg);
-
-       if (Z_LVAL_PP(field_arg) < 0 || Z_LVAL_PP(field_arg) >= sqlda->sqld) {
-               RETURN_FALSE;
-       }
-       _php_ibase_field_info(return_value,sqlda->sqlvar + Z_LVAL_PP(field_arg));
-}
-/* }}} */
-
-/* {{{ proto int ibase_num_params(resource query)
-   Get the number of params in a prepared query */
-PHP_FUNCTION(ibase_num_params)
-{
-       zval **result;
-       ibase_query *ib_query;
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &result) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-       
-       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result, -1, "InterBase query", le_query);
-
-       if (ib_query->in_sqlda == NULL) {
-               RETURN_LONG(0);
-       } else {
-               RETURN_LONG(ib_query->in_sqlda->sqld);
-       }
-}
-/* }}} */
-
-/* {{{ proto array ibase_param_info(resource query, int field_number)
-   Get information about a parameter */
-PHP_FUNCTION(ibase_param_info)
-{
-       zval **result_arg, **field_arg;
-       ibase_query *ib_query;
-
-       RESET_ERRMSG;
-
-       if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &result_arg, &field_arg) == FAILURE) {
-               WRONG_PARAM_COUNT;
-       }
-
-       ZEND_FETCH_RESOURCE(ib_query, ibase_query *, result_arg, -1, "InterBase query", le_query);
-
-       if (ib_query->in_sqlda == NULL) {
-               RETURN_FALSE;
-       }
-
-       convert_to_long_ex(field_arg);
-
-       if (Z_LVAL_PP(field_arg) < 0 || Z_LVAL_PP(field_arg) >= ib_query->in_sqlda->sqld) {
-               RETURN_FALSE;
-       }
-       
-       _php_ibase_field_info(return_value,ib_query->in_sqlda->sqlvar + Z_LVAL_PP(field_arg));
-}
-/* }}} */
-
 /* {{{ proto int ibase_gen_id(string generator [, int increment [, resource link_identifier ]])
    Increments the named generator and returns its new value */
 PHP_FUNCTION(ibase_gen_id)
index b0b0ea31115964fb0730b9e1a6031b2c477acd47..acc537aaedf153f4e6b07ba7be4d7f94062e7b34 100755 (executable)
@@ -46,8 +46,15 @@ typedef unsigned long long ISC_UINT64;
 
 #define IB_STATUS (IBG(status))
 
-extern int le_blob, le_link, le_plink, le_result, le_query, le_trans, 
-       le_event, le_service;
+#ifdef ZEND_DEBUG_
+#define IBDEBUG(a) php_printf("::: %s (%d)\n", a, __LINE__);
+#endif
+
+#ifndef IBDEBUG
+#define IBDEBUG(a)
+#endif
+
+extern int le_link, le_plink, le_trans;
 
 #define IBASE_MSGSIZE 256
 #define MAX_ERRMSG (IBASE_MSGSIZE*2)
@@ -154,7 +161,7 @@ enum php_interbase_option {
        PHP_IBASE_TIMESTAMP             = 1,
        PHP_IBASE_DATE                          = 2,
        PHP_IBASE_TIME                          = 4,
-       /* transaction access mode */   
+       /* transaction access mode */
        PHP_IBASE_WRITE                         = 1,
        PHP_IBASE_READ                          = 2,
        /* transaction isolation level */
@@ -190,14 +197,9 @@ typedef void (__stdcall *info_func_t)(char*);
 typedef void (*info_func_t)(char*);
 #endif
 
-int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd);
-char *_php_ibase_quad_to_string(ISC_QUAD const qd);
-int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC);
-int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC);
-
-void _php_ibase_error(TSRMLS_D);
-void _php_ibase_module_error(char * TSRMLS_DC, ...) 
-       PHP_ATTRIBUTE_FORMAT(printf,1,PHP_ATTR_FMT_OFFSET +2);  
+PHPAPI void _php_ibase_error(TSRMLS_D);
+PHPAPI void _php_ibase_module_error(char * TSRMLS_DC, ...)
+       PHP_ATTRIBUTE_FORMAT(printf,1,PHP_ATTR_FMT_OFFSET +2);
 
 /* determine if a resource is a link or transaction handle */
 #define PHP_IBASE_LINK_TRANS(pzval, lh, th)                                                                                                    \
@@ -208,12 +210,27 @@ void _php_ibase_module_error(char * TSRMLS_DC, ...)
                        _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAM_PASSTHRU, &pzval, &lh, &th);  \
                if (SUCCESS != _php_ibase_def_trans(lh, &th TSRMLS_CC)) { RETURN_FALSE; }                       \
        } while (0)
-       
-int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC);
-void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, zval **link_id, 
+
+PHPAPI int _php_ibase_def_trans(ibase_db_link *ib_link, ibase_trans **trans TSRMLS_DC);
+PHPAPI void _php_ibase_get_link_trans(INTERNAL_FUNCTION_PARAMETERS, zval **link_id,
        ibase_db_link **ib_link, ibase_trans **trans);
 
-void _php_ibase_event_free(char *event_buf, char *result_buf);
+/* provided by ibase_query.c */
+void php_ibase_query_minit(INIT_FUNC_ARGS);
+
+/* provided by ibase_blobs.c */
+void php_ibase_blobs_minit(INIT_FUNC_ARGS);
+PHPAPI int _php_ibase_string_to_quad(char const *id, ISC_QUAD *qd);
+PHPAPI char *_php_ibase_quad_to_string(ISC_QUAD const qd);
+PHPAPI int _php_ibase_blob_get(zval *return_value, ibase_blob *ib_blob, unsigned long max_len TSRMLS_DC);
+PHPAPI int _php_ibase_blob_add(zval **string_arg, ibase_blob *ib_blob TSRMLS_DC);
+
+/* provided by ibase_events.c */
+void php_ibase_events_minit(INIT_FUNC_ARGS);
+PHPAPI void _php_ibase_free_event(ibase_event *event TSRMLS_DC);
+
+/* provided by ibase_service.c */
+void php_ibase_service_minit(INIT_FUNC_ARGS);
 
 #ifndef max
 #define max(a,b) ((a)>(b)?(a):(b))