]> granicus.if.org Git - php/commitdiff
# Compiles. Doesn't work yet. Unless leaking memory counts as 'working'.
authorJouni Ahto <jah@php.net>
Sun, 12 Mar 2000 19:44:23 +0000 (19:44 +0000)
committerJouni Ahto <jah@php.net>
Sun, 12 Mar 2000 19:44:23 +0000 (19:44 +0000)
# Trying to finish before RC1...

ext/interbase/interbase.c
ext/interbase/php_interbase.h

index 5be769cceaaa42a0c3fe0f811db490c13f03bc5e..40d416162fb602694612b9f12acb9bc576a856a1 100644 (file)
    | obtain it through the world-wide-web, please send a note to          |
    | license@php.net so we can mail you a copy immediately.               |
    +----------------------------------------------------------------------+
-   | Authors: Jouni Ahto <jah@cultnet.fi>                                 |
+   | Authors: Jouni Ahto <jah@mork.net>                                   |
+   |             Andrew Avdeev <andy@rsc.mv.ru>                              |
    +----------------------------------------------------------------------+
  */
 
 /* $Id$ */
 
-/* TODO: A lot... */
+
+/* TODO: Arrays, roles?
+A lot... */
+
+/*
+       Changes:
+               1999-09-21:     Ivo Panacek <ivop@regionet.cz>
+                       - added COMPILE_DL section
+                       - more verbose php_info_ibase function
+                         mostly stolen from pgsql.c for now
+               1999-10-05:     Ivo Panacek <ivop@regionet.cz>
+                       - safe rinit/rfinish: check for NULL so
+                         rfinish could be called repeatedly
+                         emalloc & co. replaced with malloc & co.
+*/
 
 #include "php.h"
-#include "php_config.h"
+#include "php_ini.h"
+#include "ext/standard/php_standard.h"
+#include "php_globals.h"
 #include "php_interbase.h"
 
 #if HAVE_IBASE
 #include <ibase.h>
 #include <time.h>
+#include "ext/standard/fsock.h"
+/*
+#include "php_list.h"
 #include "php_string.h"
-#include "php_globals.h"
+#include "fsock.h"
+#include "head.h"
+*/
 
 /* {{{ extension definition structures */
 function_entry ibase_functions[] = {
-       PHP_FE(ibase_connect,                                                   NULL)
-       PHP_FE(ibase_pconnect,                                                  NULL)
-       PHP_FE(ibase_close,                                                             NULL)
-       PHP_FE(ibase_query,                                                             NULL)
-       PHP_FE(ibase_fetch_row,                                                 NULL)
-       PHP_FE(ibase_free_result,                                               NULL)
-       PHP_FE(ibase_prepare,                                                   NULL)
-       PHP_FE(ibase_bind,                                                              NULL)
-       PHP_FE(ibase_execute,                                                   NULL)
-       PHP_FE(ibase_free_query,                                                NULL)
-       PHP_FE(ibase_timefmt,                                                   NULL)
+       PHP_FE(ibase_connect, NULL)
+       PHP_FE(ibase_pconnect, NULL)
+       PHP_FE(ibase_close, NULL)
+       PHP_FE(ibase_query, NULL)
+       PHP_FE(ibase_fetch_row, NULL)
+       PHP_FE(ibase_fetch_object, NULL)
+       PHP_FE(ibase_free_result, NULL)
+       PHP_FE(ibase_prepare, NULL)
+       PHP_FE(ibase_execute, NULL)
+       PHP_FE(ibase_free_query, NULL)
+       PHP_FE(ibase_timefmt, NULL)
+
+       PHP_FE(ibase_num_fields, NULL)
+       PHP_FE(ibase_field_info, NULL)
+
+       PHP_FE(ibase_trans, NULL)
+       PHP_FE(ibase_commit, NULL)
+       PHP_FE(ibase_rollback, NULL)
+
+       PHP_FE(ibase_blob_info, NULL)
+       PHP_FE(ibase_blob_create, NULL)
+       PHP_FE(ibase_blob_add, NULL)
+       PHP_FE(ibase_blob_cancel, NULL)
+       PHP_FE(ibase_blob_close, NULL)
+       PHP_FE(ibase_blob_open, NULL)
+       PHP_FE(ibase_blob_get, NULL)
+       PHP_FE(ibase_blob_echo, NULL)
+#ifndef PHP_WIN32
+#if 0
+       PHP_FE(ibase_blob_import, NULL)
+#endif
+#endif
+       PHP_FE(ibase_errmsg, NULL)
        {NULL, NULL, NULL}
 };
 
@@ -51,1061 +95,2559 @@ zend_module_entry ibase_module_entry =
        "interbase",
        ibase_functions,
        PHP_MINIT(ibase),
-       NULL,
+       PHP_MSHUTDOWN(ibase),
        PHP_RINIT(ibase),
-       NULL,
+       PHP_MSHUTDOWN(ibase),
        PHP_MINFO(ibase),
        STANDARD_MODULE_PROPERTIES
 };
-/* }}} */
 
-/* {{{ thread safety stuff */
-#if defined(THREAD_SAFE)
-typedef ibase_global_struct{
-        ibase_module php_ibase_module;
-} ibase_global_struct;
+#if COMPILE_DL
+#include "dl/phpdl.h"
+DLEXPORT php_module_entry *get_module() { return &ibase_module_entry; }
+
+#define DL_MALLOC(size) malloc(size)
+#define DL_STRDUP(str) strdup(str)
+#define DL_FREE(ptr) free(ptr)
 
-#define IBASE_GLOBAL(a) ibase_globals->a
+#else
+
+#define DL_MALLOC(size) emalloc(size)
+#define DL_STRDUP(str) estrdup(str)
+#define DL_FREE(ptr) efree(ptr)
 
-#define IBASE_TLS_VARS \
-        ibase_global_struct *ibase_globals; \
-        ibase_globals=TlsGetValue(IBASETls); 
+#endif
 
+#ifdef ZTS
+int ibase_globals_id;
 #else
+PHP_IBASE_API php_ibase_globals ibase_globals;
+#endif
+
+
+/* }}} */
+
+
+/* {{{ internal macros and structures */
+
+#define IB_STATUS (IBG(status))
+
+/* db_handle and transaction handle keep in one variable
+  link = db_handle * IBASE_TRANS_ON_LINK + trans_handle
+*/
+
+/* get link and transaction from long argument
+ */
+#define GET_LINK_TRANS(link_id, ib_link, trans_n) { \
+       int type; \
+       trans_n = link_id % IBASE_TRANS_ON_LINK; \
+       link_id /= IBASE_TRANS_ON_LINK; \
+       ib_link = (ibase_db_link *) zend_list_find(link_id, &type); \
+       if (type!=IBG(le_link) && type!=IBG(le_plink)) { \
+               _php_ibase_module_error("%d is not link or transaction index",link_id); \
+               RETURN_FALSE; \
+       }}
+
+
+/* get query  */
+#define GET_QUERY(query_id, ib_query) { \
+       int type; \
+       ib_query = (ibase_query *) zend_list_find(query_id, &type); \
+       if (type!=IBG(le_query)) { \
+               _php_ibase_module_error("%d is not query index",query_id); \
+               RETURN_FALSE; \
+       }}
+
+/* get result  */
+#define GET_RESULT(result_id, ib_result) { \
+       int type; \
+       ib_result = (ibase_result *) zend_list_find(result_id, &type); \
+       if (type!=IBG(le_result)) { \
+               _php_ibase_module_error("%d is not result index",result_id); \
+               RETURN_FALSE; \
+       }}
+
+
+#define RESET_ERRMSG { IBG(errmsg)[0] = '\0';}
+
+#define TEST_ERRMSG ( IBG(errmsg)[0] != '\0')
+
+/* sql variables union
+used for convert and binding input variables
+*/
+typedef struct {
+       union {
+               short sval;
+               float fval;
+               ISC_QUAD qval;
+       } val;
+       short sqlind;
+} BIND_BUF;
+
+
+/* get blob identifier from argument
+ on empty unset argument ib_blob set to NULL
+ */
+#define GET_BLOB_ID_ARG(blob_arg, ib_blob)\
+{\
+       if (blob_arg->type == IS_STRING && blob_arg->value.str.len == 0) {\
+               ib_blob = NULL;\
+       } else if (blob_arg->type != IS_STRING\
+                        || blob_arg->value.str.len != sizeof(ibase_blob_handle)\
+                        || ((ibase_blob_handle *)(blob_arg->value.str.val))->bl_handle != 0){\
+               _php_ibase_module_error("invalid blob id");\
+               RETURN_FALSE;\
+       } else {\
+               ib_blob = (ibase_blob_handle *)blob_arg->value.str.val;\
+       }\
+}
+
+
+/* get blob handle from argument
+ note: blob already open when handle active
+ */
+#define GET_BLOB_HANDLE_ARG(blob_arg, blob_ptr) \
+{ \
+       int type; \
+       convert_to_long(blob_arg); \
+       blob_ptr = (ibase_blob_handle *) zend_list_find(blob_arg->value.lval, &type); \
+       if (type!=IBG(le_blob)) { \
+               _php_ibase_module_error("%d is not blob handle",blob_arg->value.lval); \
+               RETURN_FALSE; \
+       } \
+}
+
+/* blob information struct */
+typedef struct {
+       ISC_LONG  max_segment;          /* Length of longest segment */
+       ISC_LONG  num_segments;         /* Total number of segments */
+       ISC_LONG  total_length;         /* Total length of blob */
+       int               bl_stream;            /* blob is stream ? */
+} IBASE_BLOBINFO;
+
+/* }}} */
+
+/*
 #define IBASE_GLOBAL(a) a
 #define IBASE_TLS_VARS
 ibase_module php_ibase_module;
-#endif
+*/
+
+/* error handling ---------------------------- */
+
+/* {{{ proto string ibase_errmsg(void) 
+   Return error message */
+PHP_FUNCTION(ibase_errmsg)
+{
+       char *errmsg = IBG(errmsg);
+       IBLS_FETCH();
+       
+       if(errmsg[0]){
+               RETURN_STRING(errmsg,1);
+       }
+       RETURN_FALSE;
+}
 /* }}} */
 
-/* {{{ php_ibase_close_link() */
-static void php_ibase_close_link(isc_db_handle db)
+
+/* {{{ _php_ibase_error() 
+    print interbase error and save it for ibase_errmsg() */
+static void _php_ibase_error(void)
 {
-       ISC_STATUS status[20];
-       IBASE_TLS_VARS;
+       char *s, *errmsg = IBG(errmsg);
+       ISC_STATUS *statusp = IB_STATUS;
 
-       isc_detach_database(status, &db);
-       IBASE_GLOBAL(php_ibase_module).num_links--;
+       s = errmsg;
+       while((s - errmsg) < MAX_ERRMSG - (IBASE_MSGSIZE + 2) && isc_interprete(s, &statusp)){
+               strcat(errmsg, " ");
+               s = errmsg + strlen(errmsg);
+       }
+       php_error(E_WARNING, "InterBase: %s",errmsg);
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_module_error()
+       print php interbase module error         and save it for ibase_errmsg() */
+static void _php_ibase_module_error(char *msg, ...)
+{
+       char *errmsg = IBG(errmsg);
+       va_list ap;
+       int len;
+
+       va_start(ap, msg);
+       len = vsnprintf(errmsg, MAX_ERRMSG - 1, msg, ap);
+       va_end(ap);
+       errmsg[len] = '\0';
+       
+       php_error(E_WARNING, "InterBase module: %s",errmsg);
 }
 /* }}} */
 
-/* {{{ php_ibase_close_plink() */
-static void php_ibase_close_plink(isc_db_handle db)
+
+/* destructors ---------------------- */
+
+
+/* {{{ _php_ibase_free_xsqlda() */
+/* not actual destructor ... */
+static void _php_ibase_free_xsqlda(XSQLDA *sqlda)
 {
-       ISC_STATUS status[20];
-       IBASE_TLS_VARS;
+       int i;
+       XSQLVAR *var;
 
-       isc_detach_database(status, &db);
-       php_ibase_module.num_persistent--;
-       php_ibase_module.num_links--;
+       if(sqlda){
+               var = sqlda->sqlvar;
+               for (i = 0; i < sqlda->sqld; i++, var++) {
+                       efree(var->sqldata);
+                       if(var->sqlind)
+                               efree(var->sqlind);
+               }
+               efree(sqlda);
+       }
 }
 /* }}} */
 
-/* {{{ php_ibase_free_result() */
-static void php_ibase_free_result(ibase_result_handle *result)
+
+/* {{{ _php_ibase_commit_link() */
+static void _php_ibase_commit_link(ibase_db_link *link)
 {
        int i;
-       ISC_STATUS status[20];
-       IBASE_TLS_VARS;
 
-       if (!IBASE_GLOBAL(php_ibase_module).manualtransactions && result->commitok) {
-               isc_commit_transaction(status, &result->trans);
+       if (link->trans[0] != NULL) { /* commit default */
+               if (isc_commit_transaction(IB_STATUS, &link->trans[0])) {
+                       _php_ibase_error();
+               }
+               link->trans[0] = NULL;
        }
-       if (result->sqlda != NULL) {
-               for (i = 0; i < result->sqlda->sqld; i++) {
-                       efree(result->sqlda->sqlvar[i].sqldata);
-                       efree(result->sqlda->sqlvar[i].sqlind);
+       for (i = 1; i < IBASE_TRANS_ON_LINK; i++) {
+               if (link->trans[i] != NULL) {
+                       if (isc_rollback_transaction(IB_STATUS, &link->trans[i])) {
+                               _php_ibase_error();
+                       }
+                       link->trans[i] = NULL;
                }
-               efree(result->sqlda);
        }
-       isc_dsql_free_statement(status, &result->result, DSQL_drop);
-       efree(result);
 }
 /* }}} */
 
-/* {{{ php_ibase_free_query() */
-static void php_ibase_free_query(ibase_query_handle *query)
+
+/* {{{ _php_ibase_close_link() */
+static void _php_ibase_close_link(ibase_db_link *link)
 {
-       int i;
-       ISC_STATUS status[20];
+       _php_ibase_commit_link(link);
+       isc_detach_database(IB_STATUS, &link->link);
+       IBG(num_links)--;
+       efree(link);
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_close_plink() */
+static void _php_ibase_close_plink(ibase_db_link *link)
+{
+       _php_ibase_commit_link(link);
+       isc_detach_database(IB_STATUS, &link->link);
+       IBG(num_persistent)--;
+       IBG(num_links)--;
+       free(link);
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_free_result() */
+static void _php_ibase_free_result(ibase_result *ib_result)
+{
+       if (ib_result){
+               _php_ibase_free_xsqlda(ib_result->out_sqlda);
+               if (ib_result->drop_stmt && ib_result->stmt) {
+                       if(isc_dsql_free_statement(IB_STATUS, &ib_result->stmt, DSQL_drop)){
+                               _php_ibase_error();
+                       }
+               } else {
+                       if(isc_dsql_free_statement(IB_STATUS, &ib_result->stmt, DSQL_close)){
+                               _php_ibase_error();
+                       }
+               }
+               if (ib_result->out_array) {
+                       efree(ib_result->out_array);
+               }
+               efree(ib_result);
+       }
+}
+/* }}} */
+
 
-       if (query->sqlda != NULL) {
-               if (query->alloced) {
-                       for (i = 0; i < query->sqlda->sqld; i++) {
-                               efree(query->sqlda->sqlvar[i].sqldata);
-                               efree(query->sqlda->sqlvar[i].sqlind);
+/* {{{ _php_ibase_free_query() */
+static void _php_ibase_free_query(ibase_query *ib_query)
+{
+       
+       if(ib_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) {
+                       if (isc_dsql_free_statement(IB_STATUS, &ib_query->stmt, DSQL_drop)){
+                               _php_ibase_error();
                        }
                }
-               efree(query->sqlda);
+               if (ib_query->in_array) {
+                       efree(ib_query->in_array);
+               }
+               if (ib_query->out_array) {
+                       efree(ib_query->out_array);
+               }
+               efree(ib_query);
+       }
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_free_blob()  */
+static void _php_ibase_free_blob(ibase_blob_handle *ib_blob)
+{
+       
+       if (ib_blob->bl_handle != NULL) { /* blob open*/
+               if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)) {
+                       _php_ibase_error();
+               }
        }
-       isc_dsql_free_statement(status, &query->query, DSQL_drop);
-       efree(query);
+       efree(ib_blob);
 }
 /* }}} */
 
+
 /* {{{ startup, shutdown and info functions */
 PHP_MINIT_FUNCTION(ibase)
 {
-       IBASE_TLS_VARS;
+       IBLS_FETCH();
 
-       if (cfg_get_long("ibase.allow_persistent", &IBASE_GLOBAL(php_ibase_module).allow_persistent) == FAILURE) {
-               IBASE_GLOBAL(php_ibase_module).allow_persistent = 1;
+       if (cfg_get_long("ibase.allow_persistent", &IBG(allow_persistent)) == FAILURE) {
+               IBG(allow_persistent) = 1;
        }
-       if (cfg_get_long("ibase.max_persistent", &IBASE_GLOBAL(php_ibase_module).max_persistent) == FAILURE) {
-               IBASE_GLOBAL(php_ibase_module).max_persistent = -1;
+       if (cfg_get_long("ibase.max_persistent", &IBG(max_persistent)) == FAILURE) {
+               IBG(max_persistent) = -1;
        }
-       if (cfg_get_long("ibase.max_links", &IBASE_GLOBAL(php_ibase_module).max_links) == FAILURE) {
-               IBASE_GLOBAL(php_ibase_module).max_links = -1;
+       if (cfg_get_long("ibase.max_links", &IBG(max_links)) == FAILURE) {
+               IBG(max_links) = -1;
        }
-       /*
-       if (cfg_get_long("ibase.manualtransactions", &IBASE_GLOBAL(php_ibase_module).manualtransactions) == FAILURE) {
-               IBASE_GLOBAL(php_ibase_module).manualtransactions = 0;
+       if (cfg_get_string("ibase.default_user", &IBG(default_user)) == FAILURE
+               || IBG(default_user)[0] == 0) {
+               IBG(default_user) = "";
        }
-       */
-       IBASE_GLOBAL(php_ibase_module).manualtransactions = 0;
-       if (cfg_get_string("ibase.default_user", &IBASE_GLOBAL(php_ibase_module).default_user) == FAILURE
-               || IBASE_GLOBAL(php_ibase_module).default_user[0] == 0) {
-               IBASE_GLOBAL(php_ibase_module).default_user = "";
-       }
-       if (cfg_get_string("ibase.default_password", &IBASE_GLOBAL(php_ibase_module).default_password) == FAILURE
-               || IBASE_GLOBAL(php_ibase_module).default_password[0] == 0) {   
-               IBASE_GLOBAL(php_ibase_module).default_password = "";
-       }
-       if (cfg_get_string("ibase.timeformat", &IBASE_GLOBAL(php_ibase_module).timeformat) == FAILURE) {
-               IBASE_GLOBAL(php_ibase_module).timeformat = "%Y-%m-%d %H:%M:%S";
-       }
-       IBASE_GLOBAL(php_ibase_module).num_persistent=0;
-       IBASE_GLOBAL(php_ibase_module).le_result = register_list_destructors(php_ibase_free_result, NULL);
-       IBASE_GLOBAL(php_ibase_module).le_query = register_list_destructors(php_ibase_free_query, NULL);
-       IBASE_GLOBAL(php_ibase_module).le_link = register_list_destructors(php_ibase_close_link, NULL);
-       IBASE_GLOBAL(php_ibase_module).le_plink = register_list_destructors(NULL, php_ibase_close_plink);
+       if (cfg_get_string("ibase.default_password", &IBG(default_password)) == FAILURE
+               || IBG(default_password)[0] == 0) {     
+               IBG(default_password) = "";
+       }
+       if (cfg_get_string("ibase.timeformat", &IBG(timeformat)) == FAILURE) {
+               IBG(cfg_timeformat) = "%m/%d/%Y %H:%M:%S";
+       }
+
+       IBG(timeformat) = NULL;
+       IBG(errmsg) = NULL;
+
+       IBG(num_persistent) = 0;
+       IBG(le_result) = register_list_destructors(_php_ibase_free_result, NULL);
+       IBG(le_query) = register_list_destructors(_php_ibase_free_query, NULL);
+       IBG(le_blob) = register_list_destructors(_php_ibase_free_blob, NULL);
+       IBG(le_link) = register_list_destructors(_php_ibase_close_link, NULL);
+       IBG(le_plink) = register_list_destructors(_php_ibase_commit_link, _php_ibase_close_plink);
+
+       REGISTER_LONG_CONSTANT("IBASE_DEFAULT", PHP_IBASE_DEFAULT, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_TEXT", PHP_IBASE_TEXT, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_TIMESTAMP", PHP_IBASE_TIMESTAMP, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_READ", PHP_IBASE_READ, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_COMMITED", PHP_IBASE_COMMITTED, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_CONSISTENCY", PHP_IBASE_CONSISTENCY, CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("IBASE_NOWAIT", PHP_IBASE_NOWAIT, CONST_PERSISTENT);
+       
        return SUCCESS;
 }
 
+
 PHP_RINIT_FUNCTION(ibase)
 {
-       IBASE_TLS_VARS;
+       IBLS_FETCH();
+
+    IBG(default_link)= -1;
+       IBG(num_links) = IBG(num_persistent);
+
+       if(IBG(timeformat))
+        DL_FREE(IBG(timeformat));
+       IBG(timeformat) = DL_STRDUP(IBG(cfg_timeformat));
+
+       if(IBG(errmsg))
+           DL_FREE(IBG(errmsg));
+       IBG(errmsg) = DL_MALLOC(sizeof(char)*MAX_ERRMSG+1);
 
-       IBASE_GLOBAL(php_ibase_module).default_link=-1;
-       IBASE_GLOBAL(php_ibase_module).num_links = php_ibase_module.num_persistent;
        return SUCCESS;
 }
 
-/* TODO IF NEEDED
 
-int php_mfinish_ibase(void)
+PHP_MSHUTDOWN_FUNCTION(ibase)
 {
+       return SUCCESS;
 }
 
-int php_rfinish_ibase(void)
+
+PHP_RSHUTDOWN_FUNCTION(ibase)
 {
-}
-*/
+    IBLS_FETCH();
+
+       if(IBG(timeformat))
+           DL_FREE(IBG(timeformat));
+    IBG(timeformat) = NULL;
+    
+       if(IBG(errmsg))
+           DL_FREE(IBG(errmsg));
+       IBG(errmsg) = NULL;
 
+    return SUCCESS;
+} 
+
 PHP_MINFO_FUNCTION(ibase)
 {
-       /* TODO */
+       IBLS_FETCH();
+
+    php_printf(
+                "<table>"
+                "<tr><td>Revision:</td><td>$Revision$</td></tr>\n"
+#ifdef COMPILE_DL
+                "<tr><td>Dynamic module:</td><td>Yes</td></tr>\n"
+#endif
+                "<tr><td>Allow persistent links:</td><td>%s</td></tr>\n"
+                "<tr><td>Persistent links:</td><td>%d/",
+                (IBG(allow_persistent)?"Yes":"No"),
+                IBG(num_persistent));
+       if (IBG(max_persistent) == -1) {
+        php_printf("Unlimited");
+       } else {
+        php_printf("%ld",IBG(max_persistent));
+       }
+    php_printf("</td></tr>\n"
+                "<tr><td>Total links:</td><td>%d/",
+                IBG(num_links));
+       if (IBG(max_links) == -1) {
+        php_printf("Unlimited");
+       } else {
+        php_printf("%ld",IBG(max_links));
+       }
+    php_printf("</td></tr>\n"
+                "<tr><td>Time format:</td><td>\"%s\"</td></tr>\n"
+                "</table>\n",
+                IBG(timeformat));
 }
 /* }}} */
 
+
 /* {{{ _php_ibase_attach_db() */
-static int _php_ibase_attach_db(char *server, char *uname, int uname_len, char *passwd, int passwd_len, isc_db_handle *db)
+static int _php_ibase_attach_db(char *server, char *uname, char *passwd, char *charset, int buffers, char *role, isc_db_handle *db)
 {
-       ISC_STATUS status[20];
-       int db_parbuf_len = 4;
-       short foo = 4;
-       char dpb[255], *db_parbuf;
+       char dpb_buffer[256], *dpb, *p;
+       int dpb_length, len;
+
+       dpb = dpb_buffer;
+
+       *dpb++ = isc_dpb_version1;
+
+       if (uname != NULL && (len = strlen(uname))) {
+               *dpb++ = isc_dpb_user_name;
+               *dpb++ = len;
+               for (p = uname; *p;) {
+                       *dpb++ = *p++;
+               }
+       }
 
-       if (uname_len) {
-               db_parbuf_len += uname_len + 2;
+       if (passwd != NULL && (len = strlen(passwd))) {
+               *dpb++ = isc_dpb_password;
+               *dpb++ = strlen(passwd);
+               for (p = passwd; *p;) {
+                       *dpb++ = *p++;
+               }
        }
 
-       if (passwd_len) {
-               db_parbuf_len += passwd_len + 2;
+       if (charset != NULL && (len = strlen(charset))) {
+               *dpb++ = isc_dpb_lc_ctype;
+               *dpb++ = strlen(charset);
+               for (p = charset; *p;) {
+                       *dpb++ = *p++;
+               }
        }
 
-       db_parbuf = dpb;
-       *db_parbuf++ = isc_dpb_version1;
-       *db_parbuf++ = isc_dpb_num_buffers;
-       *db_parbuf++ = 1;
-       *db_parbuf++ = 90;
+       if (buffers) {
+               *dpb++ = isc_dpb_num_buffers;
+               *dpb++ = 1;
+               *dpb++ = buffers;
+       }
 
-       if (uname_len) {
-               if (passwd_len) {
-                       isc_expand_dpb(&db_parbuf, &foo, isc_dpb_user_name, uname, isc_dpb_password, passwd, NULL);
-               } else {
-                       isc_expand_dpb(&db_parbuf, &foo, isc_dpb_user_name, uname, NULL);
+#ifdef isc_dpb_sql_role_name
+       if (role != NULL && (len = strlen(role))) {
+               *dpb++ = isc_dpb_sql_role_name;
+               *dpb++ = strlen(role);
+               for (p = role; *p;) {
+                       *dpb++ = *p++;
                }
        }
+#endif
 
-       isc_attach_database(status, strlen(server), server, db, db_parbuf_len, dpb);
+       dpb_length = dpb - dpb_buffer;
 
-       if (status[0] == 1 && status[1]) {
-               php_error(E_WARNING,"Unable to connect to InterBase server:  %s", "XXX");
-               return 1;
+       if (isc_attach_database(IB_STATUS, strlen(server), server, db, dpb_length, dpb_buffer)) {
+               _php_ibase_error();
+               return FAILURE;
        }
-       return 0;
+       return SUCCESS;
 }
 /* }}} */
 
-/* {{{ php_ibase_connect() */
-static void php_ibase_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
-{
-       pval *server, *uname, *passwd;
 
-       char *ib_server, *ib_uname, *ib_passwd;
-       int ib_server_len, ib_uname_len, ib_passwd_len;
+/* {{{ _php_ibase_connect() */
+static void _php_ibase_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
+{
+       pval **args;
+       char *ib_server = NULL, *ib_uname, *ib_passwd, *ib_charset = NULL;
+       int i, ib_uname_len, ib_passwd_len;
        isc_db_handle db_handle = NULL;
        char *hashed_details;
-       int hashed_details_length;
-       IBASE_TLS_VARS;
-
-       ib_uname = IBASE_GLOBAL(php_ibase_module).default_user;
-       ib_passwd = IBASE_GLOBAL(php_ibase_module).default_password;
+       int hashed_details_length = 0;
+       ibase_db_link *ib_link;
+       IBLS_FETCH();
+       
+       RESET_ERRMSG;
+               
+       ib_uname = IBG(default_user);
+       ib_passwd = IBG(default_password);
        ib_uname_len = ib_uname ? strlen(ib_uname) : 0;
        ib_passwd_len = ib_passwd ? strlen(ib_passwd) : 0;
-
-       switch(ARG_COUNT(ht)) {
-       case 1:
-               {
-                       if (getParameters(ht, 1, &server) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       convert_to_string(server);
-                       ib_server = server->value.str.val;
-                       ib_server_len = server->value.str.len;
-                       hashed_details_length = server->value.str.len+ib_uname_len+ib_passwd_len+5+3;
-                       hashed_details = (char *) emalloc(hashed_details_length+1);
-                       sprintf(hashed_details, "ibase_%s_%s_%s", ib_server, ib_uname, ib_passwd);
-               }
-               break;
-       case 2:
-               {
-                       if (getParameters(ht, 2, &server, &uname) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       convert_to_string(server);
-                       convert_to_string(uname);
-                       ib_server = server->value.str.val;
-                       ib_uname = uname->value.str.val;
-                       ib_server_len = server->value.str.len;
-                       ib_uname_len = uname->value.str.len;
-                       hashed_details_length = server->value.str.len+uname->value.str.len+ib_passwd_len+5+3;
-                       hashed_details = (char *) emalloc(hashed_details_length+1);
-                       sprintf(hashed_details, "ibase_%s_%s_%s", ib_server, ib_uname, ib_passwd);
-               }
-               break;
-       case 3:
-               {
-                       if (getParameters(ht, 3, &server, &uname, &passwd) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       convert_to_string(server);
-                       convert_to_string(uname);
-                       convert_to_string(passwd);
-                       ib_server = server->value.str.val;
-                       ib_uname = uname->value.str.val;
-                       ib_passwd = passwd->value.str.val;
-                       ib_server_len = server->value.str.len;
-                       ib_uname_len = uname->value.str.len;
-                       ib_passwd_len = passwd->value.str.len;
-                       hashed_details_length = server->value.str.len+uname->value.str.len+passwd->value.str.len+5+3;
-                       hashed_details = (char *) emalloc(hashed_details_length+1);
-                       sprintf(hashed_details, "ibase_%s_%s_%s", ib_server, ib_uname, ib_passwd);
-               }
-               break;
-       default:
+       
+       if(ARG_COUNT(ht) < 1 || ARG_COUNT(ht) > 4){
                WRONG_PARAM_COUNT;
-               break;
+       }
+       
+       args = emalloc(sizeof(pval*) * ARG_COUNT(ht));
+       if (getParametersArray(ht, ARG_COUNT(ht), args) == FAILURE) {
+               efree(args);
+               RETURN_FALSE;
        }
 
+       switch(ARG_COUNT(ht)) {
+               case 4:
+                       convert_to_string(args[3]);
+                       ib_charset = args[3]->value.str.val;
+                       hashed_details_length += args[3]->value.str.len;
+                       /* fallout */
+               case 3:
+                       convert_to_string(args[2]);
+                       ib_passwd = args[2]->value.str.val;
+                       hashed_details_length += args[2]->value.str.len;
+                       /* fallout */
+               case 2:
+                       convert_to_string(args[1]);
+                       ib_uname = args[1]->value.str.val;
+                       hashed_details_length += args[1]->value.str.len;
+                       /* fallout */
+               case 1:
+                       convert_to_string(args[0]);
+                       ib_server = args[0]->value.str.val;
+                       hashed_details_length += args[0]->value.str.len;
+       }/* case */
+       
+       efree(args);
+       
+       hashed_details = (char *) emalloc(hashed_details_length+strlen("ibase_%s_%s_%s_%s")+1);
+       sprintf(hashed_details, "ibase_%s_%s_%s_%s", ib_server, ib_uname, ib_passwd, ib_charset);
+
        if (persistent) {
                list_entry *le;
                
-               if (zend_hash_find(plist, hashed_details, hashed_details_length+1, (void **) &le)==FAILURE) {
+               if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length+1, (void **) &le)==FAILURE) {
                        list_entry new_le;
                        
-                       if (IBASE_GLOBAL(php_ibase_module).max_links!=-1 && IBASE_GLOBAL(php_ibase_module).num_links>=IBASE_GLOBAL(php_ibase_module).max_links) {
-                               php_error(E_WARNING,"InterBase: Too many open links (%d)", IBASE_GLOBAL(php_ibase_module).num_links);
+                       if (IBG(max_links)!=-1 && IBG(num_links)>=IBG(max_links)) {
+                               _php_ibase_module_error("Too many open links (%d)", IBG(num_links));
                                efree(hashed_details);
                                RETURN_FALSE;
                        }
-                       if (IBASE_GLOBAL(php_ibase_module).max_persistent!=-1 && IBASE_GLOBAL(php_ibase_module).num_persistent>=IBASE_GLOBAL(php_ibase_module).max_persistent) {
-                               php_error(E_WARNING,"InterBase: Too many open persistent links (%d)", IBASE_GLOBAL(php_ibase_module).num_persistent);
+                       if (IBG(max_persistent)!=-1 && IBG(num_persistent)>=IBG(max_persistent)) {
+                               _php_ibase_module_error("Too many open persistent links (%d)", IBG(num_persistent));
                                efree(hashed_details);
                                RETURN_FALSE;
                        }
 
-                       /* create the link */
+                       /* create the ib_link */
 
-                       if (_php_ibase_attach_db(ib_server, ib_uname, ib_uname_len, ib_passwd, ib_passwd_len, &db_handle)) {
+                       if (_php_ibase_attach_db(ib_server, ib_uname, ib_passwd, ib_charset, 0, NULL, &db_handle) == FAILURE) {
                                efree(hashed_details);
                                RETURN_FALSE;
                        }
 
+                       ib_link = (ibase_db_link *) malloc(sizeof(ibase_db_link));
+                       ib_link->link = db_handle;
+                       for(i = 0; i < IBASE_TRANS_ON_LINK; i++)
+                               ib_link->trans[i] = NULL;
+                       
                        /* hash it up */
-                       new_le.type = php_ibase_module.le_plink;
-                       new_le.ptr = db_handle;
-                       if (zend_hash_update(plist, hashed_details, hashed_details_length+1, (void *) &new_le, sizeof(list_entry), NULL)==FAILURE) {
+                       new_le.type = IBG(le_plink);
+                       new_le.ptr = ib_link;
+                       if (zend_hash_update(&EG(persistent_list), hashed_details, hashed_details_length+1, (void *) &new_le, sizeof(list_entry), NULL)==FAILURE) {
                                efree(hashed_details);
+                               free(ib_link);
                                RETURN_FALSE;
                        }
-                       IBASE_GLOBAL(php_ibase_module).num_links++;
-                       IBASE_GLOBAL(php_ibase_module).num_persistent++;
+                       IBG(num_links)++;
+                       IBG(num_persistent)++;
                } else {
-                       if (le->type != IBASE_GLOBAL(php_ibase_module).le_plink) {
+                       if (le->type != IBG(le_plink)) {
                                RETURN_FALSE;
                        }
-                       /* TODO: ensure that the link did not die */
-
-                       db_handle = (isc_db_handle) le->ptr;
+                       /* TODO: ensure that the ib_link did not die */
+                       ib_link = (ibase_db_link *) le->ptr;
                }
-               return_value->value.lval = zend_list_insert(db_handle, IBASE_GLOBAL(php_ibase_module).le_plink);
+               return_value->value.lval = IBASE_TRANS_ON_LINK *
+                       zend_list_insert(ib_link, IBG(le_plink));
                return_value->type = IS_LONG;
        } else {
                list_entry *index_ptr, new_index_ptr;
                
-               /* first we check the hash for the hashed_details key.  if it exists,
-                * it should point us to the right offset where the actual pgsql link sits.
-                * if it doesn't, open a new pgsql link, add it to the resource list,
+               /* first we check the hash for the hashed_details key.  if it exists,
+                * it should point us to the right offset where the actual ib_link sits.
+                * if it doesn't, open a new ib_link, add it to the resource list,
                 * and add a pointer to it with hashed_details as the key.
                 */
-               if (zend_hash_find(list,hashed_details,hashed_details_length+1,(void **) &index_ptr)==SUCCESS) {
-                       int type,link;
+               if (zend_hash_find(&EG(regular_list), hashed_details, hashed_details_length+1, (void **) &index_ptr)==SUCCESS) {
+                       int type,xlink;
                        void *ptr;
-
                        if (index_ptr->type != le_index_ptr) {
                                RETURN_FALSE;
                        }
-                       link = (int) index_ptr->ptr;
-                       ptr = zend_list_find(link,&type);   /* check if the link is still there */
-                       if (ptr && (type==IBASE_GLOBAL(php_ibase_module).le_link || type==IBASE_GLOBAL(php_ibase_module).le_plink)) {
-                               return_value->value.lval = IBASE_GLOBAL(php_ibase_module).default_link = link;
+                       xlink = (int) index_ptr->ptr;
+                       ptr = zend_list_find(xlink, &type);      /* check if the xlink is still there */
+                       if (ptr && (type==IBG(le_link) || type==IBG(le_plink))) {
+                               return_value->value.lval = IBG(default_link) = xlink;
                                return_value->type = IS_LONG;
                                efree(hashed_details);
                                return;
                        } else {
-                               zend_hash_del(list,hashed_details,hashed_details_length+1);
+                               zend_hash_del(&EG(regular_list), hashed_details, hashed_details_length+1);
                        }
                }
-               if (IBASE_GLOBAL(php_ibase_module).max_links!=-1 && IBASE_GLOBAL(php_ibase_module).num_links>=IBASE_GLOBAL(php_ibase_module).max_links) {
-                       php_error(E_WARNING,"InterBase:  Too many open links (%d)", IBASE_GLOBAL(php_ibase_module).num_links);
+               if (IBG(max_links)!=-1 && IBG(num_links)>=IBG(max_links)) {
+                       _php_ibase_module_error("Too many open links (%d)", IBG(num_links));
                        efree(hashed_details);
                        RETURN_FALSE;
                }
-               /* create the link */
+               /* create the ib_link */
 
-               if (_php_ibase_attach_db(ib_server, ib_uname, ib_uname_len, ib_passwd, ib_passwd_len, &db_handle)) {
+               if (_php_ibase_attach_db(ib_server, ib_uname, ib_passwd, ib_charset, 0, NULL, &db_handle) == FAILURE) {
                        efree(hashed_details);
                        RETURN_FALSE;
                }
 
+               ib_link = (ibase_db_link *) emalloc(sizeof(ibase_db_link));
+               ib_link->link = db_handle;
+               for(i = 0; i < IBASE_TRANS_ON_LINK; i++)
+                       ib_link->trans[i] = NULL;
+
                /* add it to the list */
-               return_value->value.lval = zend_list_insert(db_handle, IBASE_GLOBAL(php_ibase_module).le_link);
+               return_value->value.lval = IBASE_TRANS_ON_LINK *
+                       zend_list_insert(ib_link, IBG(le_link));
                return_value->type = IS_LONG;
 
                /* add it to the hash */
                new_index_ptr.ptr = (void *) return_value->value.lval;
                new_index_ptr.type = le_index_ptr;
-               if (zend_hash_update(list,hashed_details,hashed_details_length+1,(void *) &new_index_ptr, sizeof(list_entry), NULL)==FAILURE) {
+               if (zend_hash_update(&EG(regular_list), hashed_details, hashed_details_length+1, (void *) &new_index_ptr, sizeof(list_entry), NULL)==FAILURE) {
                        efree(hashed_details);
                        RETURN_FALSE;
                }
-               IBASE_GLOBAL(php_ibase_module).num_links++;
+               IBG(num_links)++;
        }
        efree(hashed_details);
-       IBASE_GLOBAL(php_ibase_module).default_link=return_value->value.lval;
+       IBG(default_link) = return_value->value.lval;
 }
 /* }}} */
 
-/* {{{ proto int ibase_connect(string database [, string username] [, string password])
+
+/* {{{ proto int ibase_connect(string database [, string username] [, string password] [, string charset])
    Open a connection to an InterBase database */
 PHP_FUNCTION(ibase_connect)
 {
-       php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+       _php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
 }
 /* }}} */
 
-/* {{{ proto int ibase_pconnect(string database [, string username] [, string password])
+
+/* {{{ proto int ibase_pconnect(string database [, string username] [, string password] [, string charset])
    Open a persistent connection to an InterBase database */
 PHP_FUNCTION(ibase_pconnect)
 {
-       php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+       _php_ibase_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
 }
 /* }}} */
 
+
 /* {{{ proto int ibase_close([int link_identifier])
    Close an InterBase connection */
 PHP_FUNCTION(ibase_close)
 {
-       pval *ibase_link;
-       int id, type;
-       isc_db_handle db_handle;
-       IBASE_TLS_VARS;
+       pval *link_arg;
+       ibase_db_link *ib_link;
+       int link_id, trans_n;
+       IBLS_FETCH();
+       
+       
+       RESET_ERRMSG;
        
        switch (ARG_COUNT(ht)) {
                case 0:
-                       id = IBASE_GLOBAL(php_ibase_module).default_link;
+                       link_id = IBG(default_link);
                        break;
                case 1:
-                       if (getParameters(ht, 1, &ibase_link) == FAILURE) {
+                       if (getParameters(ht, 1, &link_arg) == FAILURE) {
                                RETURN_FALSE;
                        }
-                       convert_to_long(ibase_link);
-                       id = ibase_link->value.lval;
+                       convert_to_long(link_arg);
+                       link_id = link_arg->value.lval;
                        break;
                default:
                        WRONG_PARAM_COUNT;
                        break;
        }
        
-       db_handle = (isc_db_handle) zend_list_find(id, &type);
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_link && type!=IBASE_GLOBAL(php_ibase_module).le_plink) {
-               php_error(E_WARNING, "%d is not an InterBase link index",id);
-               RETURN_FALSE;
-       }
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
        
-       zend_list_delete(ibase_link->value.lval);
+       zend_list_delete(link_id);
        RETURN_TRUE;
 }
 /* }}} */
 
-/* {{{ php_ibase_prepare() */
-static XSQLDA *php_ibase_prepare(isc_db_handle db, isc_tr_handle tr, isc_stmt_handle *query_handle, char *query)
+/* {{{ _php_ibase_alloc_array() */
+static int _php_ibase_alloc_array(ibase_array **ib_arrayp, int *array_cntp,
+                                                                 XSQLDA *sqlda, isc_db_handle link, isc_tr_handle trans)
 {
-       ISC_STATUS status[20];
-       XSQLDA *isqlda;
-
-       isqlda = (XSQLDA *) emalloc(XSQLDA_LENGTH(0));
-    isqlda->sqln = 0;
-    isqlda->version = SQLDA_VERSION1;
-
-       if (isc_dsql_allocate_statement(status, &db, query_handle)) {
-               php_error(E_WARNING, "InterBase: couldn't allocate space for query");
-               return NULL;
-       }
+#define IB_ARRAY (*ib_arrayp)
+       
+       int i, dim, ar_cnt, ar_length;
+       XSQLVAR *var;
+       
 
-       if (isc_dsql_prepare(status, &tr, query_handle, 0, query, 1, isqlda)) {
-               php_error(E_WARNING, "InterBase: couldn't prepare query");
-               return NULL;
-       }
+       IB_ARRAY = NULL;
        
-       /*
-        * Check if query has placeholders and needs binding. If it has, allocate
-        * input sqlda big enough and return it.
-        */
+       ar_cnt = 0; /* find arrays */
+       var = sqlda->sqlvar;
+       for(i = 0; i < sqlda->sqld; i++, var++){
+               if((var->sqltype & ~1) == SQL_ARRAY)
+                       ar_cnt++;
+       }
 
+       if(ar_cnt){       /* have  arrays ? */
+
+               *array_cntp = ar_cnt;
+               IB_ARRAY = emalloc(sizeof(ibase_array)*ar_cnt);
+               ar_cnt = 0;
+               var = sqlda->sqlvar;
+               for(i = 0; i < sqlda->sqld; i++, var++){
+                       if((var->sqltype & ~1) == SQL_ARRAY){
+                               
+                               ISC_ARRAY_DESC *ar_desc = &IB_ARRAY[ar_cnt].ar_desc;
+                               
+                               if (isc_array_lookup_bounds(IB_STATUS, &link, &trans,
+                                                                                       var->relname, var->sqlname, ar_desc)) {
+                                       _php_ibase_error();
+                                       efree(IB_ARRAY);
+                                       IB_ARRAY = NULL;
+                                       return FAILURE;
+                               }
 
-       if (isc_dsql_describe_bind(status, query_handle, 1, isqlda)) {
-               php_error(E_WARNING, "InterBase: couldn't describe placeholders in query");
-       }
+                               switch (ar_desc->array_desc_dtype) {
+                                       case blr_text:
+                                       case blr_text2:
+                                               IB_ARRAY[ar_cnt].el_type = SQL_TEXT;
+                                               IB_ARRAY[ar_cnt].el_size = ar_desc->array_desc_length+1;
+                                               break;
+                                       case blr_short:
+                                               IB_ARRAY[ar_cnt].el_type = SQL_SHORT;
+                                               IB_ARRAY[ar_cnt].el_size = sizeof(short);
+                                               break;
+                                       case blr_long:
+                                               IB_ARRAY[ar_cnt].el_type = SQL_LONG;
+                                               IB_ARRAY[ar_cnt].el_size = sizeof(long);
+                                               break;
+                                       case blr_float:
+                                               IB_ARRAY[ar_cnt].el_type = SQL_FLOAT;
+                                               IB_ARRAY[ar_cnt].el_size = sizeof(float);
+                                               break;
+                                       case blr_double:
+                                               IB_ARRAY[ar_cnt].el_type = SQL_DOUBLE;
+                                               IB_ARRAY[ar_cnt].el_size = sizeof(double);
+                                               break;
+                                       case blr_date:
+                                               IB_ARRAY[ar_cnt].el_type = SQL_DATE;
+                                               IB_ARRAY[ar_cnt].el_size = sizeof(ISC_QUAD);
+                                               break;
+                                       case blr_varying:
+                                       case blr_varying2:      /* changed to SQL_TEXT ? */
+                                               /* sql_type = SQL_VARYING; Why? FIXME: ??? */
+                                               IB_ARRAY[ar_cnt].el_type = SQL_TEXT;
+                                               IB_ARRAY[ar_cnt].el_size = ar_desc->array_desc_length+sizeof(short);
+                                               break;
+                                       default:
+                                               _php_ibase_module_error("unexpected array type %d in relation '%s' column '%s')",
+                                                                                  ar_desc->array_desc_dtype,var->relname, var->sqlname);
+                                               efree(IB_ARRAY);
+                                               IB_ARRAY = NULL;
+                                               return FAILURE;
+                               }/* switch array_desc_type */
+                               
+                               ar_length = 0; /* calculate elements count */
+                               for (dim = 0; dim < ar_desc->array_desc_dimensions; dim++) {
+                                       ar_length += 1 + ar_desc->array_desc_bounds[dim].array_bound_upper
+                                               - ar_desc->array_desc_bounds[dim].array_bound_lower;
+                               }
+                               IB_ARRAY[ar_cnt].ar_size = IB_ARRAY[ar_cnt].el_size * ar_length;
+                               
+                               ar_cnt++;
+                               
+                       }/* if SQL_ARRAY */
+               }/* for column */
+       }/* if array_cnt */
        
-       if (isqlda->sqld > 1) {
-               isqlda = (XSQLDA *) erealloc(isqlda, XSQLDA_LENGTH(isqlda->sqld));
-               isqlda->sqln = isqlda->sqld;
-               isqlda->version = SQLDA_VERSION1;
-               if (isc_dsql_describe(status, query_handle, 1, isqlda)) {
-                       php_error(E_WARNING, "InterBase: couldn't describe query");
-               }
-               return isqlda;
-       } else if (isqlda->sqld == 1) {
-               return isqlda;
-       } else {
-               efree(isqlda);
-               return NULL;
-       }
+       return SUCCESS;
+#undef IB_ARRAY
 }
 /* }}} */
 
-/* {{{ php_ibase_execute() */
-static XSQLDA *php_ibase_execute(isc_tr_handle tr_handle, isc_stmt_handle query_handle, XSQLDA *isqlda, ISC_STATUS *status)
-{
-       int i, coltype;
-       static char query_info[] = { isc_info_sql_stmt_type };
-       char info_buffer[18];
-    short l;
-    long query_type;
-       XSQLDA *osqlda;
 
-       /*
-        * Find out what kind of query is to be executed.
-        */
+/* {{{ _php_ibase_alloc_query() */
+/* allocate and prepare query */
+static int _php_ibase_alloc_query(ibase_query **ib_queryp, isc_db_handle link, isc_tr_handle trans, char *query)
+{
+#define IB_QUERY (*ib_queryp)
        
-       if (!isc_dsql_sql_info(status, &query_handle, sizeof(query_info), query_info, sizeof(info_buffer), info_buffer)) {
-               l = (short) isc_vax_integer((char ISC_FAR *) info_buffer + 1, 2);
-               query_type = isc_vax_integer((char ISC_FAR *) info_buffer + 3, l);
+       IB_QUERY = emalloc(sizeof(ibase_query));
+       IB_QUERY->link = link;
+       IB_QUERY->trans = trans;
+       IB_QUERY->stmt = NULL;
+       IB_QUERY->out_sqlda = NULL;
+       IB_QUERY->in_sqlda = NULL;
+       IB_QUERY->in_array = NULL;
+       IB_QUERY->in_array_cnt = 0;
+       IB_QUERY->out_array = NULL;
+       IB_QUERY->out_array_cnt = 0;
+       
+       if (isc_dsql_allocate_statement(IB_STATUS, &link, &IB_QUERY->stmt)) {
+               _php_ibase_error();
+               goto _php_ibase_alloc_query_error;
        }
 
-       if (query_type == isc_info_sql_stmt_select || query_type == isc_info_sql_stmt_select_for_upd) {
-               /*
-                * Select, need to allocate output sqlda and and prepare it for use.
-                */
-
-               osqlda = (XSQLDA *) emalloc(XSQLDA_LENGTH(0));
-               osqlda->sqln = 0;
-               osqlda->version = SQLDA_VERSION1;
+       IB_QUERY->out_sqlda = emalloc(XSQLDA_LENGTH(0));
+       IB_QUERY->out_sqlda->sqln = 0;
+       IB_QUERY->out_sqlda->version = SQLDA_VERSION1;
 
-               if (isc_dsql_describe(status, &query_handle, 1, osqlda)) {
-                       php_error(E_WARNING, "InterBase: couldn't describe query");
+       if (isc_dsql_prepare(IB_STATUS, &IB_QUERY->trans, &IB_QUERY->stmt, 0, query, SQLDA_VERSION1, IB_QUERY->out_sqlda)) {
+               _php_ibase_error();
+               goto _php_ibase_alloc_query_error;
+       }
+       
+       /* 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_VERSION1;
+               if (isc_dsql_describe(IB_STATUS, &IB_QUERY->stmt, SQLDA_VERSION1, IB_QUERY->out_sqlda)) {
+                       _php_ibase_error();
+                       goto _php_ibase_alloc_query_error;
                }
+       }
 
-               if (osqlda->sqld) {
-                       osqlda = (XSQLDA *) erealloc(osqlda, XSQLDA_LENGTH(osqlda->sqld));
-                       osqlda->sqln = osqlda->sqld;
-                       osqlda->version = SQLDA_VERSION1;
-                       if (isc_dsql_describe(status, &query_handle, 1, osqlda)) {
-                               php_error(E_WARNING, "InterBase: couldn't describe query");
-                       }
-               }
-               for (i = 0; i < osqlda->sqld; i++) {
-                       osqlda->sqlvar[i].sqlind = (short *) emalloc(sizeof(short));
-                       coltype = osqlda->sqlvar[i].sqltype & ~1;
-                       switch(coltype)
-                               {
-                               case SQL_TEXT:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(char)*(osqlda->sqlvar[i].sqllen));
-                                       break;
-                               case SQL_VARYING:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(char)*(osqlda->sqlvar[i].sqllen+2));
-                                       break;
-                               case SQL_SHORT:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(short));
-                                       break;
-                               case SQL_LONG:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(long));
-                                       break;
-                               case SQL_FLOAT:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(float));
-                                       break;
-                               case SQL_DOUBLE:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(double));
-                                       break;
-                               case SQL_DATE:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(ISC_QUAD));
-                                       break;
-                               case SQL_BLOB:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(ISC_QUAD));
-                                       break;
-                               case SQL_ARRAY:
-                                       osqlda->sqlvar[i].sqldata = (char *) emalloc(sizeof(ISC_QUAD));
-                                       break;
-                               }
+       /* maybe have input placeholders? */
+       IB_QUERY->in_sqlda = emalloc(XSQLDA_LENGTH(0));
+       IB_QUERY->in_sqlda->sqln = 0;
+       IB_QUERY->in_sqlda->version = SQLDA_VERSION1;
+       if (isc_dsql_describe_bind(IB_STATUS, &IB_QUERY->stmt, SQLDA_VERSION1, IB_QUERY->in_sqlda)) {
+               _php_ibase_error();
+               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_VERSION1;
+               if (isc_dsql_describe_bind(IB_STATUS, &IB_QUERY->stmt, SQLDA_VERSION1, IB_QUERY->in_sqlda)) {
+                       _php_ibase_error();
+                       goto _php_ibase_alloc_query_error;
                }
-               if (isqlda == NULL) {
-                       if (isc_dsql_execute(status, &tr_handle, &query_handle, 1, NULL)) {
-                               php_error(E_WARNING, "InterBase: couldn't execute query");
-                               return NULL;
-                       } else {
-                               return osqlda;
-                       }
+       }
        
-               } else {
-                       if (isc_dsql_execute2(status, &tr_handle, &query_handle, 1, isqlda, osqlda)) {
-                               php_error(E_WARNING, "InterBase: couldn't execute query");
-                               return NULL;
-                       } else {
-                               return osqlda;
-                       }
+       /* allocate arrays... */
+       if (_php_ibase_alloc_array(&IB_QUERY->in_array, &IB_QUERY->in_array_cnt,
+                                                               IB_QUERY->in_sqlda, link, trans) == FAILURE){
+               goto _php_ibase_alloc_query_error; /* error report already done */
+       }
        
-               }
-       } else {
-               /* Not select */
-               if (isc_dsql_execute(status, &tr_handle, &query_handle, 1, isqlda)) {
-                       php_error(E_WARNING, "InterBase: couldn't execute query");
-               }
-               /*
-               if (!php_ibase_module.manualtransactions) {
-                       if(0) {
-                               isc_commit_transaction(status, tr_handle);
-                       } else {
-                               isc_rollback_transaction(status, tr_handle);
-                       }
-               }
-               */
+       if (_php_ibase_alloc_array(&IB_QUERY->out_array, &IB_QUERY->out_array_cnt,
+                                                               IB_QUERY->out_sqlda, link, trans) == FAILURE){
+               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;
        }
 
-       return NULL;
+       if(IB_QUERY->out_sqlda->sqld == 0){
+               efree(IB_QUERY->out_sqlda);
+               IB_QUERY->out_sqlda = NULL;
+       }
+
+       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);
+       efree(IB_QUERY);
+       IB_QUERY = NULL;
+       
+       return FAILURE;
+#undef IB_QUERY
 }
 /* }}} */
 
-/* {{{ proto int ibase_query([int link_identifier], string query)
-   Execute a query (without parameter placeholders) */
-PHP_FUNCTION(ibase_query)
+
+/* {{{ proto int ibase_bind(int query)
+   Bind parameter placeholders in a previously prepared query */
+static int _php_ibase_bind(XSQLDA *sqlda, pval **b_vars, BIND_BUF *buf)
 {
-       pval *query, *ibase_link;
-       int id, type;
-       isc_db_handle db_handle;
-       isc_tr_handle tr_handle = NULL;
-       isc_stmt_handle query_handle = NULL;
-       ISC_STATUS status[20];
-       XSQLDA *isqlda, *osqlda;
-       ibase_result_handle *ibase_result;
-       IBASE_TLS_VARS;
+       XSQLVAR *var;
+       pval *b_var;
+       int i;
+       
 
-       switch (ARG_COUNT(ht)) {
-               case 1:
-                       if (getParameters(ht, 1, &query) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       id = IBASE_GLOBAL(php_ibase_module).default_link;
+       var = sqlda->sqlvar;
+       for(i = 0; i < sqlda->sqld; var++, i++) { /* binded vars */
+               
+               buf[i].sqlind = 0;
+               var->sqlind      = &buf[i].sqlind;
+               b_var = b_vars[i];
+               
+               switch(var->sqltype & ~1) {
+                       case SQL_TEXT:                     /* direct to variable */
+                       case SQL_VARYING:
+                               convert_to_string(b_var);
+                               var->sqldata = (void ISC_FAR *)b_var->value.str.val;
+                               var->sqllen      = b_var->value.str.len;
+                               var->sqltype = SQL_TEXT + (var->sqltype & 1);
+                               break;
+                       case SQL_SHORT:
+                               convert_to_long(b_var);
+                               if(b_var->value.lval > SHRT_MAX || b_var->value.lval < SHRT_MIN){
+                                       _php_ibase_module_error("field %*s overflow", var->aliasname_length, var->aliasname);
+                                       return FAILURE;
+                               }
+                               buf[i].val.sval = (short)b_var->value.lval;
+                               var->sqldata = (void ISC_FAR *)(&buf[i].val.sval);
+                               break;
+                       case SQL_LONG:  /* direct to variable */
+                               convert_to_long(b_var);
+                               var->sqldata = (void ISC_FAR *)(&b_var->value.lval);
+                               break;
+                       case SQL_FLOAT:
+                               convert_to_double(b_var);
+                               buf[i].val.fval = (float)b_var->value.dval;
+                               var->sqldata = (void ISC_FAR *)(&buf[i].val.fval);
+                               break;
+                       case SQL_DOUBLE:  /* direct to variable */
+                               convert_to_double(b_var);
+                               var->sqldata = (void ISC_FAR *)(&b_var->value.dval);
+                               break;
+                       case SQL_DATE:
+                               {
+                                       struct tm t;
+                                       
+                                       t.tm_year = t.tm_mon = t.tm_mday = t.tm_hour =
+                                               t.tm_min = t.tm_sec = 0;
+                                       
+                                       convert_to_string(b_var);
+                                       
+#if HAVE_STRPTIME /*FIXME: HAVE_STRPTIME ?*/
+                                       
+                                       strptime(b_var->value.str.val, IBG(timeformat), &t);
+#else
+                                       {
+                                               int n = sscanf(b_var->value.str.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");
+                                                       return FAILURE;
+                                               }
+                                       
+                                               t.tm_year -= 1900;
+                                               t.tm_mon--;
+                                       }
+#endif
+                                       isc_encode_date(&t, &buf[i].val.qval);
+                                       var->sqldata = (void ISC_FAR *)(&buf[i].val.qval);
+                               }
+                               break;
+                       case SQL_BLOB:
+                               {
+                                       ibase_blob_handle *ib_blob_id;
+                                       if(b_var->type != IS_STRING
+                                          || b_var->value.str.len != sizeof(ibase_blob_handle)
+                                          || ((ibase_blob_handle *)(b_var->value.str.val))->bl_handle != 0){
+                                               _php_ibase_module_error("invalid blob id string");
+                                               return FAILURE;
+                                       }
+                                       ib_blob_id = (ibase_blob_handle *)b_var->value.str.val;
+                                       
+                                       var->sqldata = (void ISC_FAR *)&ib_blob_id->bl_qd;
+                               }
                        break;
-               case 2:
-                       if (getParameters(ht, 2, &ibase_link, &query) == FAILURE) {
+                       case SQL_ARRAY:
+                               _php_ibase_module_error("binding arrays not supported yet");
+                               return FAILURE;
+                       break;
+               }/*switch*/
+       }/*for*/
+       return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_alloc_xsqlda() */
+static void _php_ibase_alloc_xsqlda(XSQLDA *sqlda)
+{
+       int i;
+       XSQLVAR *var = sqlda->sqlvar;
+       
+       
+       for (i = 0; i < sqlda->sqld; i++, var++) {
+               switch(var->sqltype & ~1) {
+                       case SQL_TEXT:
+                               var->sqldata = emalloc(sizeof(char)*(var->sqllen));
+                               break;
+                       case SQL_VARYING:
+                               var->sqldata = emalloc(sizeof(char)*(var->sqllen+sizeof(short)));
+                               break;
+                       case SQL_SHORT:
+                               var->sqldata = emalloc(sizeof(short));
+                               break;
+                       case SQL_LONG:
+                               var->sqldata = emalloc(sizeof(long));
+                               break;
+                       case SQL_FLOAT:
+                               var->sqldata = emalloc(sizeof(float));
+                                       break;
+                       case SQL_DOUBLE:
+                               var->sqldata = emalloc(sizeof(double));
+                               break;
+                       case SQL_DATE:
+                       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*/
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_exec() */
+static int _php_ibase_exec(ibase_result **ib_resultp, ibase_query *ib_query, int argc, pval **args)
+{
+#define IB_RESULT (*ib_resultp)
+       XSQLDA *in_sqlda = NULL, *out_sqlda = NULL;
+       BIND_BUF *bind_buf = NULL;
+       int rv = FAILURE;
+       
+
+       IB_RESULT = NULL;
+       
+       /* allocate sqlda and output buffers */
+       if (ib_query->out_sqlda) { /* output variables in select, select for update */
+               IB_RESULT = emalloc(sizeof(ibase_result));
+               IB_RESULT->link = ib_query->link;
+               IB_RESULT->trans = ib_query->trans;
+               IB_RESULT->stmt = ib_query->stmt; 
+               IB_RESULT->drop_stmt = 0; /* when free result close but not drop!*/
+               out_sqlda = IB_RESULT->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) {
+                       IB_RESULT->out_array = emalloc(sizeof(ibase_array)*ib_query->out_array_cnt);
+                       memcpy(IB_RESULT->out_array, ib_query->out_array, sizeof(ibase_array)*ib_query->out_array_cnt);
+               } else {
+                       IB_RESULT->out_array = NULL;
+               }
+       }
+
+       if (ib_query->in_sqlda) { /* has placeholders */
+               if (ib_query->in_sqlda->sqld != argc) {
+                       _php_ibase_module_error("placeholders (%d) and variables (%d) mismatch",ib_query->in_sqlda->sqld, argc);
+                       goto _php_ibase_exec_error;  /* yes mommy, goto! */
+               }
+               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 = emalloc(sizeof(BIND_BUF) * ib_query->in_sqlda->sqld);
+               if(_php_ibase_bind(in_sqlda, args, bind_buf) == FAILURE){
+                       goto _php_ibase_exec_error;
+               }
+       }
+
+       if (isc_dsql_execute(IB_STATUS, &ib_query->trans, &ib_query->stmt, 1, in_sqlda)) {
+               _php_ibase_error();
+               goto _php_ibase_exec_error;
+       }
+
+       rv = SUCCESS;
+       
+_php_ibase_exec_error:          /* I'm a bad boy... */
+       
+       if (in_sqlda)
+               efree(in_sqlda);
+       if (bind_buf)
+               efree(bind_buf);
+
+       if (rv == FAILURE) {
+               if (IB_RESULT) {
+                       efree(IB_RESULT);
+                       IB_RESULT = NULL;
+               }
+               if (out_sqlda)
+                       efree(out_sqlda);
+       }
+       
+       return rv;
+#undef IB_RESULT
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_trans([int trans_args [, int link_identifier]])
+   Start transaction */
+PHP_FUNCTION(ibase_trans)
+{
+       pval **args;
+       char tpb[20], *tpbp = NULL;
+       long trans_argl = 0;
+       int tpb_len = 0, argn, link_id, trans_n;
+       ibase_db_link *ib_link;
+       IBLS_FETCH();
+       
+       
+       RESET_ERRMSG;
+
+       link_id = IBG(default_link);
+
+       /* TODO: multi-databases trans */
+       argn = ARG_COUNT(ht);
+       if (argn < 0 || argn > 2) {
+               WRONG_PARAM_COUNT;
+       }
+
+       if (argn) {
+               args = emalloc(sizeof(pval*) * argn);
+               if (getParametersArray(ht, argn, args) == FAILURE) {
+                       efree(args);
+                       RETURN_FALSE;
+               }
+
+               argn--;
+               convert_to_long(args[argn]); /* last arg - trans_args */
+               trans_argl = args[argn]->value.lval;
+
+               /* TODO: multi
+                do {
+                        link_id[argn] = args[argn];
+                        convert_to_long(args[argn]);
+                        GET_LINK_TRANS(args[argn].value.lval, ib_link[argn], trans_n);
+                } while(argn--); */
+
+               if (argn) {
+                       argn--;
+                       convert_to_long(args[argn]);
+                       link_id = args[argn]->value.lval;
+               }
+       
+               efree(args);
+       }
+
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
+       
+       if (trans_argl ){
+               tpb[tpb_len++] = isc_tpb_version3;
+               tpbp = tpb;
+               /* access mode */
+               if(trans_argl & PHP_IBASE_READ) /* READ ONLY TRANSACTION */
+                       tpb[tpb_len++] = isc_tpb_read;
+               else
+                       tpb[tpb_len++] = isc_tpb_write;
+               /* isolation level */
+               if(trans_argl & PHP_IBASE_COMMITTED){
+                       tpb[tpb_len++] = isc_tpb_read_committed;
+               }else if (trans_argl & PHP_IBASE_CONSISTENCY)
+                       tpb[tpb_len++] = isc_tpb_consistency;
+               else 
+                       tpb[tpb_len++] = isc_tpb_concurrency;
+               /* lock resolution */
+               if(trans_argl & PHP_IBASE_NOWAIT)
+                       tpb[tpb_len++] = isc_tpb_nowait;
+               else 
+                       tpb[tpb_len++] = isc_tpb_wait;
+
+       }
+
+       /* find empty transaction slot */
+       for (trans_n = 0; trans_n < IBASE_TRANS_ON_LINK
+               && ib_link->trans[trans_n]; trans_n++)
+               ;
+       if (trans_n == IBASE_TRANS_ON_LINK) {
+               _php_ibase_module_error("too many transactions on link");
+               RETURN_FALSE;
+       }
+       
+       if (isc_start_transaction(IB_STATUS, &ib_link->trans[trans_n], 1, &ib_link->link, tpb_len, tpbp)) {
+               _php_ibase_error();
+               RETURN_FALSE;
+       }
+
+       RETURN_LONG(link_id * IBASE_TRANS_ON_LINK + trans_n);
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_def_trans() */
+/* open default transaction */
+static int _php_ibase_def_trans(ibase_db_link * ib_link, int trans_n)
+{
+       
+       if (trans_n == 0 && ib_link->trans[0] == NULL) { 
+               if (isc_start_transaction(IB_STATUS, &ib_link->trans[0], 1, &ib_link->link, 0, NULL)) {
+                       _php_ibase_error();
+                       return FAILURE;
+               }
+       }
+       return SUCCESS;
+}
+/*}}}*/
+
+
+/* {{{ _php_ibase_trans_end() */
+#define COMMIT 1
+#define ROLLBACK 0
+static void _php_ibase_trans_end(INTERNAL_FUNCTION_PARAMETERS, int commit)
+{
+       pval *link_trans_arg;
+       int link_id = 0, trans_n = 0;
+       ibase_db_link *ib_link;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       switch (ARG_COUNT(ht)) {
+               case 0:
+                       link_id = IBG(default_link);
+                       break;
+               case 1: 
+                       if (getParameters(ht, 1, &link_trans_arg) == FAILURE) {
                                RETURN_FALSE;
                        }
-                       convert_to_long(ibase_link);
-                       id = ibase_link->value.lval;
+                       convert_to_long(link_trans_arg);
+                       link_id = link_trans_arg->value.lval;
                        break;
                default:
                        WRONG_PARAM_COUNT;
                        break;
        }
+
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
+
+       if (commit) {
+               if (isc_commit_transaction(IB_STATUS, &ib_link->trans[trans_n])) {
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+       } else {
+               if (isc_rollback_transaction(IB_STATUS, &ib_link->trans[trans_n])) {
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+       }
+       ib_link->trans[trans_n] = NULL;
        
-       db_handle = (isc_db_handle) zend_list_find(id, &type);
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_link && type!=IBASE_GLOBAL(php_ibase_module).le_plink) {
-               php_error(E_WARNING, "%d is not an InterBase link index", id);
+       RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_commit([int link_identifier,] int trans_number)
+   Commit transaction */
+PHP_FUNCTION(ibase_commit)
+{
+       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, COMMIT);
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_rollback([int link_identifier,] int trans_number)
+   Roolback transaction */
+PHP_FUNCTION(ibase_rollback)
+{
+       _php_ibase_trans_end(INTERNAL_FUNCTION_PARAM_PASSTHRU, ROLLBACK);
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_query([int link_identifier,] string query [, int bind_args])
+   Execute a query */
+PHP_FUNCTION(ibase_query)
+{
+       pval **args, **bind_args = NULL;
+       int i, link_id = 0, trans_n = 0, bind_n = 0;
+       char *query;
+       ibase_db_link *ib_link;
+       ibase_query *ib_query;
+       ibase_result *ib_result;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       if(ARG_COUNT(ht) < 1){
+               WRONG_PARAM_COUNT;
+       }
+
+       args = emalloc(sizeof(pval*) * ARG_COUNT(ht));
+       if (getParametersArray(ht, ARG_COUNT(ht), args) == FAILURE) {
+               efree(args);
                RETURN_FALSE;
        }
+
+       i = 0;
+       if (args[i]->type == IS_LONG) { /* link or transaction argument */
+               link_id = args[i]->value.lval;
+               i++; /* next arg */
+       } else {
+               link_id = IBG(default_link);
+       }
+
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
+
+       if (args[i]->type == IS_STRING) { /* query argument */
+               query = args[i]->value.str.val;
+               i++; /* next arg */
+       } else {
+               _php_ibase_module_error("query argument missed");
+               efree(args);
+               RETURN_FALSE;
+       }
+
+       if (ARG_COUNT(ht) > i) { /* have variables to bind */
+               bind_n = ARG_COUNT(ht) - i;
+               bind_args = &args[i];
+       }
+       
+       /* open default transaction */
+       if (_php_ibase_def_trans(ib_link, trans_n) == FAILURE) {
+               efree(args);
+               RETURN_FALSE;
+       }
+
+       if (_php_ibase_alloc_query(&ib_query, ib_link->link, ib_link->trans[trans_n],  query) == FAILURE) {
+               efree(args);
+               RETURN_FALSE;
+       }
+
+       if (_php_ibase_exec(&ib_result, ib_query, bind_n, bind_args) == FAILURE) {
+               _php_ibase_free_query(ib_query);
+               efree(args);
+               RETURN_FALSE;
+       }
+       
+       efree(args);
+       
+       if (ib_result) { /* select statement */
+               ib_result->drop_stmt = 1; /* drop stmt when free result */
+               ib_query->stmt = NULL; /* keep stmt when free query */
+               _php_ibase_free_query(ib_query);
+               RETURN_LONG(zend_list_insert(ib_result, IBG(le_result)));
+       } else {
+               _php_ibase_free_query(ib_query);
+               RETURN_TRUE;
+       }
+}
+/* }}} */
+
+
+/* {{{ _php_ibase_var_pval() */
+static int _php_ibase_var_pval(pval *val, void *data, int type, int len, int scale, int flag)
+{
+       char string_data[255];
+       
+
+       switch(type & ~1) {
+               case SQL_VARYING:
+                       len = ((IBASE_VCHAR *) data)->var_len;
+                       data = ((IBASE_VCHAR *) data)->var_str;
+                       /* fallout */
+               case SQL_TEXT:
+                       val->value.str.val = (char *)emalloc(sizeof(char)*(len+1));
+            strncpy(val->value.str.val, data, len);
+                       if (PG(magic_quotes_runtime)) {
+                               char *tmp = val->value.str.val;
+                               val->value.str.val = php_addslashes(val->value.str.val, len, &len, 0);
+                               efree(tmp);
+            }
+                       val->type = IS_STRING;
+                       val->value.str.len = len;
+                       break;
+               case SQL_LONG:
+                       if (scale) {
+                               int j, f = 1;
+                               float n = *(long *)(data);
+                               
+                               for (j = 0; j < -scale; j++)
+                                       f *= 10;
+                               val->type = IS_STRING;
+                               val->value.str.len = sprintf(string_data, "%.*f", -scale, n / f);
+                               val->value.str.val = estrdup(string_data);
+                       }else{
+                               val->type = IS_LONG;
+                               val->value.lval = *(long *)(data);
+                       }
+                       break;
+               case SQL_SHORT:
+                       val->type = IS_LONG;
+                       val->value.lval = *(short *)(data);
+                       break;
+               case SQL_FLOAT:
+                       val->type = IS_DOUBLE;
+                       val->value.dval = *(float *)(data);
+                       break;
+               case SQL_DOUBLE:
+                       if (scale) {
+                               val->type = IS_STRING;
+                               val->value.str.len = sprintf(string_data, "%.*f", -scale, *(double *)data);
+                               val->value.str.val = estrdup(string_data);
+                       }else{
+                               val->type = IS_DOUBLE;
+                               val->value.dval = *(double *)data;
+                       }
+                       break;
+               case SQL_DATE:{
+                       struct tm t;
+                       long timestamp = -1;
+                                        
+                       isc_decode_date((ISC_QUAD *) data, &t);
+                       timestamp = mktime(&t);
+#if HAVE_TM_ZONE
+                       t.tm_zone = tzname[0];
+#endif
+                       if (flag & PHP_IBASE_TIMESTAMP) {
+                               val->type = IS_LONG;
+                               val->value.lval = timestamp;
+                       } else {
+                               val->type = IS_STRING;
+#if HAVE_STRFTIME
+                               val->value.str.len = strftime(string_data, sizeof(string_data), IBG(timeformat), &t);
+#else
+                               if (!t.tm_hour && !t.tm_min && !t.tm_sec)
+                                       val->value.str.len = sprintf(string_data, "%02d/%02d/%4d", t.tm_mon+1, t.tm_mday, t.tm_year+1900);
+                               else
+                                       val->value.str.len = 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);
+#endif
+                               val->value.str.val = estrdup(string_data);
+                               break;
+                       }
+               }
+               default:
+                       return FAILURE;
+       }/* switch (type) */
+       return SUCCESS;
+}
+/* }}} */
+
+/* {{{ _php_ibase_arr_pval() */
+/* create multidimension array - resursion function
+ (*datap) argument changed */
+static int _php_ibase_arr_pval(pval *ar_pval, char **datap, ibase_array *ib_array, int dim, int flag)
+{
+       pval tmp;
+       int i, dim_len, l_bound, u_bound;
        
-       convert_to_string(query);
 
-       if (!IBASE_GLOBAL(php_ibase_module).manualtransactions) {
-               if (isc_start_transaction(status, &tr_handle, 1, &db_handle, 0, NULL)) {
-                       php_error(E_WARNING, "InterBase: couldn't start transaction");
+       if (dim > 16){ /* InterBase limit */
+               _php_ibase_module_error("php module internal error in %s %d",__FILE__,__LINE__);
+               return FAILURE;
+       }
+
+       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;
+               
+       if (dim < ib_array->ar_desc.array_desc_dimensions - 1) { /* array again */
+               for (i = 0; i < dim_len; i++) {
+                       /* recursion here */
+                       if (_php_ibase_arr_pval(ar_pval, datap, ib_array, dim+1, flag) == FAILURE) {
+                               return FAILURE;
+                       }
+               }
+       }else{ /* data at last */
+               
+               array_init(ar_pval);
+
+               for (i = 0; i < dim_len; i++) {
+                       if (_php_ibase_var_pval(&tmp, *datap, ib_array->el_type,
+                                                                       ib_array->ar_desc.array_desc_length,
+                                                                       ib_array->ar_desc.array_desc_scale,
+                                                                       flag) == FAILURE){
+                               return FAILURE;
+                       }
+                       /* FIXME */
+                       zend_hash_index_update(ar_pval->value.ht,
+                                                                       l_bound + i,
+                                                                       (void *) &tmp, sizeof(pval),NULL);
+                       *datap += ib_array->el_size;
+               }
+       }
+       return SUCCESS;
+}
+
+
+/* {{{ _php_ibase_fetch_hash() */
+
+#define FETCH_ARRAY 1
+#define FETCH_OBJECT 2
+
+static void _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int fetch_type)
+{
+       pval *result_arg, *flag_arg;
+       long flag = 0;
+       int i, arr_cnt;
+       ibase_result *ib_result;
+       XSQLVAR *var;
+       IBLS_FETCH();
+       
+       RESET_ERRMSG;
+       
+       switch(ARG_COUNT(ht)) {
+               case 1:
+                       if (ARG_COUNT(ht)==1 && getParameters(ht, 1, &result_arg)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               case 2:
+                       if (ARG_COUNT(ht)==2 && getParameters(ht, 2, &result_arg, &flag_arg)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       convert_to_long(flag_arg);
+                       flag = flag_arg->value.lval;
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+       
+       convert_to_long(result_arg);
+       
+       GET_RESULT(result_arg->value.lval, ib_result);
+       
+       if (ib_result->out_sqlda == NULL) {
+               _php_ibase_module_error("trying to fetch results from a non-select query");
+               RETURN_FALSE;
+       }
+       
+       if (isc_dsql_fetch(IB_STATUS, &ib_result->stmt, 1, ib_result->out_sqlda) == 100L) {
+               RETURN_FALSE;  /* end of cursor */
+       }
+       
+       if (IB_STATUS[0] && IB_STATUS[1]) { /* error in fetch */
+               _php_ibase_error();
+               RETURN_FALSE;
+       }
+       
+       if (fetch_type == FETCH_ARRAY) {
+               if (array_init(return_value)==FAILURE) {
+                       RETURN_FALSE;
+               }
+       } else if(fetch_type == FETCH_OBJECT) {
+               if (object_init(return_value)==FAILURE) {
                        RETURN_FALSE;
                }
        }
+       
+       arr_cnt = 0;
+       var = ib_result->out_sqlda->sqlvar;
+       for (i = 0; i < ib_result->out_sqlda->sqld; i++, var++) {
+               pval tmp;
+               if (((var->sqltype & 1) == 0) || *var->sqlind != -1){
+                       switch(var->sqltype & ~1) {
+                               case SQL_VARYING:
+                               case SQL_TEXT:
+                               case SQL_SHORT:
+                               case SQL_LONG:
+                               case SQL_FLOAT:
+                               case SQL_DOUBLE:
+                               case SQL_DATE:
+                                       _php_ibase_var_pval(&tmp, var->sqldata, var->sqltype, var->sqllen, var->sqlscale, flag);
+                                       break;
+                               case SQL_BLOB:
+                                       if (flag & PHP_IBASE_TEXT) { /* text ? */
+                                               int stat;
+                                               isc_blob_handle bl_handle = NULL;
+                                               ISC_LONG max_len = 0, cur_len = 0;
+                                               char bl_items[1], *bl_data, bl_info[20], *p;
+                                               
+                                               if (isc_open_blob(IB_STATUS, &ib_result->link, &ib_result->trans, &bl_handle, (ISC_QUAD ISC_FAR *) var->sqldata)) {
+                                                       _php_ibase_error();
+                                                       RETURN_FALSE;
+                                               }
+                                               
+                                               bl_items[0] = isc_info_blob_total_length;
+                                               if (isc_blob_info(IB_STATUS,&bl_handle,sizeof(bl_items),bl_items,sizeof(bl_info),bl_info)) {
+                                                       _php_ibase_error();
+                                                       RETURN_FALSE;
+                                               }
+                                               
+                                               /* find total length of blob's data */
+                                               for (p = bl_info; *p != isc_info_end && p < bl_info+sizeof(bl_info);) {
+                                                       int item_len, item = *p++;
+                                                       item_len = (short)isc_vax_integer(p, 2);
+                                                       p += 2;
+                                                       if (item == isc_info_blob_total_length)
+                                                               max_len = isc_vax_integer(p, item_len);
+                                                       p += item_len;
+                                               }
+                                               
+                                               bl_data = emalloc(max_len+1);
+                                               
+                                               for (cur_len = stat = 0; stat == 0 && cur_len < max_len; ) {
+                                                       unsigned short seg_len, max_seg = max_len-cur_len > USHRT_MAX ? USHRT_MAX : max_len-cur_len;
+                                                       stat = isc_get_segment(IB_STATUS, &bl_handle, &seg_len, max_seg, &bl_data[cur_len]);
+                                                       cur_len += seg_len;
+                                                       if(cur_len > max_len){ /* never!*/
+                                                               efree(bl_data);
+                                                               _php_ibase_module_error("php module internal error in %s %d",__FILE__,__LINE__);
+                                                               RETURN_FALSE;
+                                                       }
+                                               }
+                                               
+                                               if (IB_STATUS[0] && IB_STATUS[1] && (IB_STATUS[1] != isc_segstr_eof)) {
+                                                       efree(bl_data);
+                                                       _php_ibase_error();
+                                                       RETURN_FALSE;
+                                               }
+                                               bl_data[cur_len] = '\0';
+                                               if (isc_close_blob(IB_STATUS, &bl_handle)) {
+                                                       efree(bl_data);
+                                                       _php_ibase_error();
+                                                       RETURN_FALSE;
+                                               }
+                                               tmp.type = IS_STRING;
+                                               tmp.value.str.len = cur_len;
+                                               tmp.value.str.val = bl_data;
+                                       } else { /* blob id only */
+                                               ISC_QUAD *bl_qd = (ISC_QUAD ISC_FAR *) var->sqldata;
+                                               ibase_blob_handle *ib_blob_id;
+                                               
+                                               ib_blob_id = (ibase_blob_handle *) emalloc(sizeof(ibase_blob_handle));
+                                               
+                                               ib_blob_id->link = ib_result->link;
+                                               ib_blob_id->trans_handle = ib_result->trans;
+                                               ib_blob_id->bl_qd.gds_quad_high = bl_qd->gds_quad_high;
+                                               ib_blob_id->bl_qd.gds_quad_low = bl_qd->gds_quad_low;
+                                               ib_blob_id->bl_handle = NULL;
+                                               
+                                               tmp.type = IS_STRING;
+                                               tmp.value.str.len = sizeof(ibase_blob_handle);
+                                               tmp.value.str.val = (char *)ib_blob_id;
+                                       }
+                                       break;
+                               case SQL_ARRAY:{
+                                       ISC_QUAD ar_qd = *(ISC_QUAD ISC_FAR *) var->sqldata;
+                                       ibase_array *ib_array = &ib_result->out_array[arr_cnt];
+                                       void *ar_data;
+                                       char *tmp_ptr;
+                                       
+                                       ar_data = emalloc(ib_array->ar_size);
+                                       
+                                       if (isc_array_get_slice(IB_STATUS, &ib_result->link, &ib_result->trans,
+                                                                                       &ar_qd, &ib_array->ar_desc, ar_data, &ib_array->ar_size)){
+                                               _php_ibase_error();
+                                               RETURN_FALSE;
+                                       }
+                                       
+                                       tmp_ptr = ar_data; /* avoid changes in _arr_pval */
+                                       if(_php_ibase_arr_pval(&tmp, &tmp_ptr, ib_array, 0, flag) == FAILURE){
+                                               RETURN_FALSE;
+                                       }
+                                       efree(ar_data);
+                               }
+                               break;
+                               default:
+                                       break;
+                       }/*switch*/
+                       if (fetch_type & FETCH_ARRAY) {
+                               /* FIXME */
+                               zend_hash_index_update(return_value->value.ht, i, (void *) &tmp, sizeof(pval),NULL);
+                       } else {
+                               zend_hash_update(return_value->value.ht, var->aliasname, var->aliasname_length+1, (void *) &tmp, sizeof(pval), NULL);
+                       }
+               }/* if not null */
+               if ((var->sqltype & ~1) == SQL_ARRAY) {
+                       arr_cnt++;
+               }
+       }/*for field*/
+}
+/* }}} */
+
+
+/* {{{ proto array ibase_fetch_row(int result [, int blob_flag])
+   Fetch a row  from the results of a query */
+PHP_FUNCTION(ibase_fetch_row)
+{
+       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_ARRAY);
+}
+/* }}} */
+
+
+/* {{{ proto object ibase_fetch_object(int result [, int blob_flag])
+   Fetch a object from the results of a query */
+PHP_FUNCTION(ibase_fetch_object)
+{
+       _php_ibase_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, FETCH_OBJECT);
+}
+/* }}} */
 
-       isqlda = php_ibase_prepare(db_handle, tr_handle, &query_handle, query->value.str.val);
-       if (isqlda != NULL) {
-               isc_rollback_transaction(status, &tr_handle);
-               isc_dsql_free_statement(status, &query_handle, DSQL_drop);
-               php_error(E_WARNING, "InterBase: ibase_query doesn't support parameter placeholders in query");
+
+/* {{{ proto int ibase_free_result(int result)
+   Free the memory used by a result */
+PHP_FUNCTION(ibase_free_result)
+{
+       pval *result_arg;
+       ibase_result *ib_result;
+       IBLS_FETCH();
+
+       RESET_ERRMSG;
+
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &result_arg)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+       
+       convert_to_long(result_arg);
+       if (result_arg->value.lval==0) {
                RETURN_FALSE;
        }
+       
+       GET_RESULT(result_arg->value.lval, ib_result);
+       
+       zend_list_delete(result_arg->value.lval);
+       RETURN_TRUE;
+}
+/* }}} */
 
-       osqlda = php_ibase_execute(tr_handle, query_handle, isqlda, status);
-       if (osqlda != NULL) {
-               ibase_result = (ibase_result_handle *) emalloc(sizeof(ibase_result_handle));
-               ibase_result->result = query_handle;
-               ibase_result->sqlda = osqlda;
-               ibase_result->trans = tr_handle;
-               ibase_result->commitok = 1;
-               return_value->value.lval = zend_list_insert(ibase_result, php_ibase_module.le_result);
-               return_value->type = IS_LONG;
-       } else {
-               if (status[0] == 1 && status[1]) {
-                       if (!IBASE_GLOBAL(php_ibase_module).manualtransactions) {
-                               isc_rollback_transaction(status, &tr_handle);
-                               isc_dsql_free_statement(status, &query_handle, DSQL_drop);
+
+/* {{{ proto int ibase_prepare([int link_identifier,] string query)
+   Prepare a query for later execution */
+PHP_FUNCTION(ibase_prepare)
+{
+       pval *link_arg, *query_arg;
+       int link_id, trans_n = 0;
+       ibase_db_link *ib_link;
+       ibase_query *ib_query;
+       char *query;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       switch (ARG_COUNT(ht)) {
+               case 1:
+                       if (getParameters(ht, 1, &query_arg) == FAILURE) {
                                RETURN_FALSE;
                        }
-               } else {
-                       if (!IBASE_GLOBAL(php_ibase_module).manualtransactions) {
-                               isc_commit_transaction(status, &tr_handle);
-                               isc_dsql_free_statement(status, &query_handle, DSQL_drop);
+                       link_id = IBG(default_link);
+                       break;
+               case 2:
+                       if (getParameters(ht, 2, &link_arg, &query_arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       convert_to_long(link_arg);
+                       link_id = link_arg->value.lval;
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+       
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
+       
+       convert_to_string(query_arg);
+       query = query_arg->value.str.val;
+
+       /* open default transaction */
+       if(_php_ibase_def_trans(ib_link, trans_n) == FAILURE){
+               RETURN_FALSE;
+       }
+       
+       if(_php_ibase_alloc_query(&ib_query, ib_link->link, ib_link->trans[trans_n],  query) == FAILURE){
+               RETURN_FALSE;
+       }
+
+       RETURN_LONG(zend_list_insert(ib_query, IBG(le_query)));
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_execute(int query [, int bind_args [, int ...])
+   Execute a previously prepared query */
+PHP_FUNCTION(ibase_execute)
+{
+       pval **args, **bind_args = NULL;
+       ibase_query *ib_query;
+       ibase_result *ib_result;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       if(ARG_COUNT(ht) < 1){
+               WRONG_PARAM_COUNT;
+       }
+
+       args = emalloc(sizeof(pval*) * ARG_COUNT(ht));
+       if (getParametersArray(ht, ARG_COUNT(ht), args) == FAILURE) {
+               efree(args);
+               RETURN_FALSE;
+       }
+
+       GET_QUERY(args[0]->value.lval, ib_query);
+       
+       if(ARG_COUNT(ht) > 1){ /* have variables to bind */
+               bind_args = &args[1];
+       }
+       
+       if( _php_ibase_exec(&ib_result, ib_query, ARG_COUNT(ht)-1, bind_args) == FAILURE){;
+               efree(args);
+               RETURN_FALSE;
+       }
+       
+       efree(args);
+       
+       if (ib_result) { /* select statement */
+               RETURN_LONG(zend_list_insert(ib_result, IBG(le_result)));
+       }
+       
+       RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_free_query(int query)
+   Free memory used by a query */
+PHP_FUNCTION(ibase_free_query)
+{
+       pval *query_arg;
+       ibase_query *ib_query;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &query_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+       
+       convert_to_long(query_arg);
+
+       GET_QUERY(query_arg->value.lval, ib_query);
+       
+       zend_list_delete(query_arg->value.lval);
+       RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_timefmt(string format)
+   Sets the format of datetime columns returned from queries. Still nonfunctional */
+PHP_FUNCTION(ibase_timefmt)
+{
+       pval *fmt;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+#if HAVE_STRFTIME
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &fmt)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+    convert_to_string(fmt);
+    
+       if(IBG(timeformat))
+         DL_FREE(IBG(timeformat));
+    IBG(timeformat) = DL_STRDUP(fmt->value.str.val);
+    
+       RETURN_TRUE;
+#else
+       _php_ibase_module_error("ibase_timefmt not supported on this platform");
+       RETURN_FALSE;
+#endif
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_num_fields(int result)
+   Get the number of fields in result */
+PHP_FUNCTION(ibase_num_fields)
+{
+       pval *result;
+       int type;
+       ibase_result *ib_result;
+       
+
+       RESET_ERRMSG;
+
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &result)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+       
+       convert_to_long(result);
+       ib_result = (ibase_result *) zend_list_find(result->value.lval, &type);
+       
+       if (type!=IBG(le_result)) {
+               _php_ibase_module_error("%d is not result index", result->value.lval);
+               RETURN_FALSE;
+       }
+
+       if (ib_result->out_sqlda == NULL) {
+               _php_ibase_module_error("trying to get num fields from a non-select query");
+               RETURN_FALSE;
+       }
+
+       RETURN_LONG(ib_result->out_sqlda->sqld);
+}
+/* }}} */
+
+
+/* {{{ proto array ibase_field_info(int result, int field_number)
+   Get information about a field */
+PHP_FUNCTION(ibase_field_info)
+{
+       pval *ret_val, *result_arg, *field_arg;
+       ibase_result *ib_result;
+       char buf[30], *s;
+       int len;
+       XSQLVAR *var;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       if (ARG_COUNT(ht)!=2 || getParameters(ht, 2, &result_arg, &field_arg)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+       
+       convert_to_long(result_arg);
+
+       GET_RESULT(result_arg->value.lval, ib_result);
+       
+       if (ib_result->out_sqlda == NULL) {
+               _php_ibase_module_error("trying to get field info from a non-select query");
+               RETURN_FALSE;
+       }
+
+       convert_to_long(field_arg);
+
+       if (field_arg->value.lval<0 || field_arg->value.lval>=ib_result->out_sqlda->sqld)
+               RETURN_FALSE;
+
+       if (array_init(return_value)==FAILURE)
+               RETURN_FALSE;
+
+       var = ib_result->out_sqlda->sqlvar + field_arg->value.lval;
+
+       /* FIXME */
+       add_get_index_stringl(return_value, 0, var->sqlname, var->sqlname_length, (void **) &ret_val, 1);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "name", sizeof(char)*5, ret_val);
+       */
+
+       add_get_index_stringl(return_value, 1, var->aliasname, var->aliasname_length, (void **) &ret_val, 1);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "alias", sizeof(char)*6, ret_val);
+       */
+
+       add_get_index_stringl(return_value, 2, var->relname, var->relname_length, (void **) &ret_val, 1);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "relation", sizeof(char)*9, ret_val);
+       */
+
+       len = sprintf(buf, "%d", var->sqllen);
+       add_get_index_stringl(return_value, 3, buf, len, (void **) &ret_val, 1);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "length", sizeof(char)*7, ret_val);
+       */
+
+       switch (var->sqltype & ~1){
+               case SQL_TEXT:     s = "TEXT"; break;
+               case SQL_VARYING:  s = "VARYING"; break;
+               case SQL_SHORT:    s = "SHORT"; break;
+               case SQL_LONG:     s = "LONG"; break;
+               case SQL_FLOAT:    s = "FLOAT"; break;
+               case SQL_DOUBLE:   s = "DOUBLE"; break;
+               case SQL_D_FLOAT:  s = "D_FLOAT"; break;
+               case SQL_DATE:     s = "DATE"; break;
+               case SQL_BLOB:     s = "BLOB"; break;
+               case SQL_ARRAY:    s = "ARRAY"; break;
+               case SQL_QUAD:     s = "QUAD"; break;
+       default:
+               sprintf(buf,"unknown (%d)", var->sqltype & ~1);
+               s = buf;
+               break;
+       }
+       add_get_index_stringl(return_value, 4, s, strlen(s), (void **) &ret_val, 1);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "type", sizeof(char)*5, ret_val);
+       */
+
+}
+/* }}} */
+
+
+/* blobs ----------------------------------- */
+
+
+/* {{{ _php_ibase_blob_info(isc_blob_handle bl_handle,IBASE_BLOBINFO *bl_info) */
+static int _php_ibase_blob_info(isc_blob_handle bl_handle,IBASE_BLOBINFO *bl_info)
+{
+       
+       static char bl_items[] = {
+               isc_info_blob_num_segments,
+               isc_info_blob_max_segment,
+               isc_info_blob_total_length,
+               isc_info_blob_type
+       };
+       
+       char bl_inf[sizeof(long)*8], *p;
+       
+
+       bl_info->max_segment = 0;
+       bl_info->num_segments = 0;
+       bl_info->total_length = 0;
+       bl_info->bl_stream = 0;
+
+       if (isc_blob_info(IB_STATUS,&bl_handle,sizeof(bl_items),bl_items,sizeof(bl_inf),bl_inf)) {
+               _php_ibase_error();
+               return FAILURE;
+       }
+
+       for (p = bl_inf; *p != isc_info_end && p < bl_inf+sizeof(bl_inf);) {
+               int item_len, item = *p++;
+               item_len = (short)isc_vax_integer(p, 2);
+               p += 2;
+               switch(item){
+                       case isc_info_blob_num_segments:
+                               bl_info->num_segments = isc_vax_integer(p, item_len);
+                               break;
+                       case isc_info_blob_max_segment:
+                               bl_info->max_segment = isc_vax_integer(p, item_len);
+                               break;
+                       case isc_info_blob_total_length:
+                               bl_info->total_length = isc_vax_integer(p, item_len);
+                               break;
+                       case isc_info_blob_type:
+                               bl_info->bl_stream = isc_vax_integer(p, item_len);
+                               break;
+                       case isc_info_end:
+                               break;
+                       case isc_info_truncated:
+                       case isc_info_error:  /* hmm. don't think so...*/
+                               _php_ibase_module_error("php module internal error in %s %d",__FILE__,__LINE__);
+                               return FAILURE;
+               }/*switch*/
+               p += item_len;
+       }/*for*/
+       return SUCCESS;
+}
+/* }}} */
+
+
+/* {{{ proto int ibase_blob_create([int link_identifier])
+   Create blob for adding data */
+PHP_FUNCTION(ibase_blob_create)
+{
+       pval *link_arg;
+       int trans_n, link_id = 0;
+       ibase_db_link *ib_link;
+       ibase_blob_handle *ib_blob;
+       IBLS_FETCH();
+       
+
+       RESET_ERRMSG;
+
+       switch (ARG_COUNT(ht)) {
+               case 0:
+                       link_id = IBG(default_link);
+                       break;
+               case 1:
+                       if (getParameters(ht, 1, &link_arg) == FAILURE) {
                                RETURN_FALSE;
                        }
-               }
+                       convert_to_long(link_arg);
+                       link_id = link_arg->value.lval;
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
        }
-}
-/* }}} */
-
-/* {{{ proto int ibase_fetch_row(int result)
-   Fetch a row from the results of a query */
-PHP_FUNCTION(ibase_fetch_row)
-{
-       pval *result;
-       pval *pval_ptr;
-       int type;
-       int i, coltype, collen;
-       char string_data[255];
-       char *fieldname;
-       char *char_data;
-       /*
-       struct tm t;
-       */
-       ibase_result_handle *ibase_result;
-       ISC_STATUS status[20];
-       XSQLVAR *var;
-       IBASE_VCHAR *vchar;
-       IBASE_TLS_VARS;
-       PLS_FETCH();
+       
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
 
-       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &result)==FAILURE) {
-               WRONG_PARAM_COUNT;
+       /* open default transaction */
+       if(_php_ibase_def_trans(ib_link, trans_n) == FAILURE){
+               RETURN_FALSE;
        }
        
-       convert_to_long(result);
-       ibase_result = (ibase_result_handle *) zend_list_find(result->value.lval, &type);
+       ib_blob = (ibase_blob_handle *) emalloc(sizeof(ibase_blob_handle));
+       ib_blob->trans_handle = ib_link->trans[trans_n];
+       ib_blob->link = ib_link->link;
+       ib_blob->bl_handle = NULL;
        
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_result) {
-               php_error(E_WARNING,"%d is not an InterBase result index", result->value.lval);
+       if (isc_create_blob(IB_STATUS, &ib_blob->link, &ib_blob->trans_handle, &ib_blob->bl_handle,&ib_blob->bl_qd)){
+               efree(ib_blob);
+               _php_ibase_error();
                RETURN_FALSE;
        }
+       
+       RETURN_LONG(zend_list_insert(ib_blob, IBG(le_blob)));
+}
+/* }}} */
 
-       if (ibase_result->sqlda == NULL) {
-               php_error(E_WARNING,"InterBase: trying to fetch results from a non-select query");
-               RETURN_FALSE;
-       }
 
-       var = ibase_result->sqlda->sqlvar;
+/* {{{ proto int ibase_blob_open(string blob_id)
+   Open blob for retriving data parts */
+PHP_FUNCTION(ibase_blob_open)
+{
+       pval *blob_arg;
+       ibase_blob_handle *ib_blob, *ib_blob_id;
+       IBLS_FETCH();
+       
 
-       if (isc_dsql_fetch(status, &ibase_result->result, 1, ibase_result->sqlda) != 100L) {
-               if (array_init(return_value)==FAILURE) {
-                       RETURN_FALSE;
-               }
+       RESET_ERRMSG;
 
-               for (i = 0; i < ibase_result->sqlda->sqld; i++) {
-                       var[i].sqlname[var[i].sqlname_length] = '\0';
-                       fieldname = var[i].sqlname;
-                       if (*var[i].sqlind < 0) {
-                               /* XXX. Column is NULL. This is not the best idea to do, think something... */
-                               add_get_index_stringl(return_value, i, NULL, 0, (void **) &pval_ptr, 1);
-                               zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                               continue;
-                       }
-                       coltype = var[i].sqltype & ~1;
-                       switch(coltype) {
-                               case SQL_TEXT:
-                                       {
-                                               int len = var[i].sqllen;
-                                               char_data = (char *)emalloc(sizeof(char)*(var[i].sqllen));
-                                               collen = sprintf(char_data, "%*.*s", len, len, ibase_result->sqlda->sqlvar[i].sqldata);
-                                               if (PG(magic_quotes_runtime)) {
-                                                       int newlen;
-                                                       char *tmp = php_addslashes(char_data, collen, &newlen, 0);
-                                                       add_get_index_stringl(return_value, i, tmp, newlen, (void **) &pval_ptr, 0);
-                                               } else {
-                                                       add_get_index_stringl(return_value, i, char_data, collen, (void **) &pval_ptr, 1);
-                                               }
-                                               zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                               efree(char_data);
-                                       }
-                                       break;
-                               case SQL_VARYING:
-                                       {
-                                               vchar = (IBASE_VCHAR *) var[i].sqldata;
-                                               char_data = (char *)emalloc(sizeof(char)*(vchar->var_len));
-                                               collen = sprintf(char_data, "%*.*s", vchar->var_len, vchar->var_len, vchar->var_str);
-                                               if (PG(magic_quotes_runtime)) {
-                                                       int newlen;
-                                                       char *tmp = php_addslashes(char_data, collen, &newlen, 0);
-                                                       add_get_index_stringl(return_value, i, tmp, newlen, (void **) &pval_ptr, 0);
-                                               } else {
-                                                       add_get_index_stringl(return_value, i, char_data, collen, (void **) &pval_ptr, 1);
-                                               }
-                                               zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                               efree(char_data);
-                                       }
-                                       break;
-                               case SQL_SHORT:
-                                       collen = sprintf(string_data, "%d", *(short *)(var[i].sqldata));
-                                       add_get_index_stringl(return_value, i, string_data, collen, (void **) &pval_ptr, 1);
-                                       zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                       break;
-                               case SQL_LONG:
-                                       if (var[i].sqlscale) {
-                                               int j, f = 1;
-                                               float n;
-                                               n = *(long *)(var[i].sqldata);
-                                               for (j = 0; j < -var[i].sqlscale; j++)
-                                                       f *= 10;
-                                               n /= f;
-                                               collen = sprintf(string_data, "%.*f", -var[i].sqlscale, n);
-
-                                       } else {
-                                               collen = sprintf(string_data, "%ld", *(long *)(var[i].sqldata));
-                                       }
-                                       add_get_index_stringl(return_value, i, string_data, collen, (void **) &pval_ptr, 1);
-                                       zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                       break;
-                               case SQL_FLOAT:
-                                       collen = sprintf(string_data, "%f", *(float *)(var[i].sqldata));
-                                       add_get_index_stringl(return_value, i, string_data, collen, (void **) &pval_ptr, 1);
-                                       zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                       break;
-                               case SQL_DOUBLE:
-                                       if (ibase_result->sqlda->sqlvar[i].sqlscale) {
-                                               collen = sprintf(string_data, "%.*f", -var[i].sqlscale, *(double *)(var[i].sqldata));
-                                       } else {
-                                               collen = sprintf(string_data, "%f", *(double *)(var[i].sqldata));
-                                       }
-                                       add_get_index_stringl(return_value, i, string_data, collen, (void **) &pval_ptr, 1);
-                                       zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                       break;
-                               case SQL_DATE: {
-                                       struct tm *t;
-                                       t = emalloc(sizeof(struct tm));
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &blob_arg)==FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
 
-                                       isc_decode_date((ISC_QUAD *) var[i].sqldata, t);
-                                       /*
-#if HAVE_STRFTIME
-                                       collen = (int) strftime(string_data, 255, IBASE_GLOBAL(php_ibase_module).timeformat, t);
-#else
-                                       */
-                                       collen = sprintf(string_data, "%4d-%02d-%02d %02d:%02d:%02d", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
-                                       /* #endif */
-                                       add_get_index_stringl(return_value, i, string_data, collen, (void **) &pval_ptr, 1);
-                                       zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                       efree(t);
-                                       }
-                                       break;
-                               case SQL_BLOB:
-                               case SQL_ARRAY:
-                                       {
-                                               /* TODO, currently just show the id */
-                                               ISC_QUAD bid;
-                                               bid = *(ISC_QUAD ISC_FAR *) var[i].sqldata;
-                                               sprintf(string_data, "%lx:%lx", bid.isc_quad_high, bid.isc_quad_low);
-                                               add_get_index_stringl(return_value, i, string_data, collen, (void **) &pval_ptr, 1);
-                                               zend_hash_pointer_update(return_value->value.ht, fieldname, var[i].sqlname_length+1, pval_ptr);
-                                       }
-                                       break;
-                               default:
-                                       break;
-                       }
-               }
-       } else {
+       ib_blob = (ibase_blob_handle *) emalloc(sizeof(ibase_blob_handle));
+
+       GET_BLOB_ID_ARG(blob_arg,ib_blob_id);
+
+       if(ib_blob_id == NULL){ /* blob IS NULL or argument unset */
+               RETURN_FALSE;
+       }
+       
+       ib_blob->link = ib_blob_id->link;
+       ib_blob->trans_handle = ib_blob_id->trans_handle;
+       ib_blob->bl_qd.gds_quad_high = ib_blob_id->bl_qd.gds_quad_high;
+       ib_blob->bl_qd.gds_quad_low = ib_blob_id->bl_qd.gds_quad_low;
+       ib_blob->bl_handle = NULL;
+       if (isc_open_blob(IB_STATUS, &ib_blob->link, &ib_blob->trans_handle, &ib_blob->bl_handle, &ib_blob->bl_qd)){
+               efree(ib_blob);
+               _php_ibase_error();
                RETURN_FALSE;
        }
+       
+       RETURN_LONG(zend_list_insert(ib_blob, IBG(le_blob)));
 }
 /* }}} */
 
-/* {{{ proto int ibase_free_result(int result)
-   Free the memory used by a result */
-PHP_FUNCTION(ibase_free_result)
+
+/* {{{ proto int ibase_blob_add(int blob_id, string data)
+   Add data into created blob */
+PHP_FUNCTION(ibase_blob_add)
 {
-       pval *result;
-       ibase_result_handle *ibase_result;
-       int type;
-       IBASE_TLS_VARS;
+       pval *blob_arg,*string_arg;
+       ibase_blob_handle *ib_blob;
+       IBLS_FETCH();
 
-       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &result)==FAILURE) {
+
+       RESET_ERRMSG;
+
+       if (ARG_COUNT(ht)!=2 || getParameters(ht, 2, &blob_arg, &string_arg)==FAILURE) {
                WRONG_PARAM_COUNT;
        }
+
+       GET_BLOB_HANDLE_ARG(blob_arg, ib_blob);
        
-       convert_to_long(result);
-       if (result->value.lval==0) {
-               RETURN_FALSE;
-       }
-       
-       ibase_result = (ibase_result_handle *) zend_list_find(result->value.lval,&type);
-       
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_result) {
-               php_error(E_WARNING,"%d is not an InterBase result index",result->value.lval);
+       convert_to_string(string_arg);
+
+       if (isc_put_segment(IB_STATUS, &ib_blob->bl_handle, string_arg->value.str.len, string_arg->value.str.val)){
+               _php_ibase_error();
                RETURN_FALSE;
        }
-       zend_list_delete(result->value.lval);
        RETURN_TRUE;
 }
 /* }}} */
 
-/* {{{ proto int ibase_prepare([int link_identifier], string query)
-   Prepare a query for later binding of parameter placeholders and execution */
-PHP_FUNCTION(ibase_prepare)
+
+/* {{{ proto string ibase_blob_get(int blob_id, int len)
+   Get len bytes data from open blob */
+PHP_FUNCTION(ibase_blob_get)
 {
-       pval *query, *ibase_link;
-       int id, type;
-       isc_db_handle db_handle;
-       isc_tr_handle tr_handle = NULL;
-       isc_stmt_handle query_handle = NULL;
-       ISC_STATUS status[20];
-       XSQLDA *isqlda;
-       ibase_query_handle *ibase_query;
-       IBASE_TLS_VARS;
+       pval *blob_arg, *len_arg;
+       int stat;
+       char *bl_data;
+       unsigned short max_len = 0, cur_len, seg_len;
+       ibase_blob_handle *ib_blob;
+       IBLS_FETCH();
 
-       switch (ARG_COUNT(ht)) {
-               case 1:
-                       if (getParameters(ht, 1, &query) == FAILURE) {
-                               RETURN_FALSE;
-                       }
-                       id = IBASE_GLOBAL(php_ibase_module).default_link;
-                       break;
-               case 2:
-                       if (getParameters(ht, 2, &ibase_link, &query) == FAILURE) {
+
+       RESET_ERRMSG;
+
+       if(ARG_COUNT(ht) != 2 || getParameters(ht, 2, &blob_arg, &len_arg) == FAILURE) {
+               WRONG_PARAM_COUNT;
+       }
+
+       convert_to_long(len_arg);
+       max_len = len_arg->value.lval;
+
+       GET_BLOB_HANDLE_ARG(blob_arg, ib_blob);
+
+       if(ib_blob->bl_qd.gds_quad_high || ib_blob->bl_qd.gds_quad_low){ /*not null ?*/
+               
+               bl_data = emalloc(max_len+1);
+
+               for(cur_len = stat = 0; stat == 0; ){
+                       stat = isc_get_segment(IB_STATUS, &ib_blob->bl_handle, &seg_len, max_len-cur_len, &bl_data[cur_len]);
+                       cur_len += seg_len;
+                       if(cur_len > max_len){ /* never!*/
+                               efree(bl_data);
+                               _php_ibase_module_error("php module internal error in %s %d",__FILE__,__LINE__);
                                RETURN_FALSE;
                        }
-                       convert_to_long(ibase_link);
-                       id = ibase_link->value.lval;
-                       break;
-               default:
-                       WRONG_PARAM_COUNT;
-                       break;
+               }
+
+               bl_data[cur_len] = '\0';
+               if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof && IB_STATUS[1] != isc_segment)){
+                       efree(bl_data);
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+               RETURN_STRINGL(bl_data,cur_len,0);
+       }else{ /* null blob */
+               RETURN_STRING("",1); /* empty string */
        }
+}
+/* }}} */
+
+
+#define BLOB_CLOSE 1
+#define BLOB_CANCEL 2
+
+/* {{{ _php_ibase_blob_end() */
+/* Close or Cancel created or Close open blob */
+static void _php_ibase_blob_end(INTERNAL_FUNCTION_PARAMETERS, int bl_end)
+{
+       pval *blob_arg;
+       ibase_blob_handle *ib_blob;
+       IBLS_FETCH();
+
+
+       RESET_ERRMSG;
        
-       db_handle = (isc_db_handle) zend_list_find(id, &type);
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_link && type!=IBASE_GLOBAL(php_ibase_module).le_plink) {
-               php_error(E_WARNING, "%d is not an InterBase link index", id);
-               RETURN_FALSE;
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &blob_arg)==FAILURE) {
+               WRONG_PARAM_COUNT;
        }
-       
-       convert_to_string(query);
 
-       if (!IBASE_GLOBAL(php_ibase_module).manualtransactions) {
-               if (isc_start_transaction(status, &tr_handle, 1, &db_handle, 0, NULL)) {
-                       php_error(E_WARNING, "InterBase: couldn't start transaction");
+       GET_BLOB_HANDLE_ARG(blob_arg, ib_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 ?*/
+                       if (isc_close_blob(IB_STATUS, &ib_blob->bl_handle)){
+                               _php_ibase_error();
+                               RETURN_FALSE;
+                       }
+               }
+               ib_blob->bl_handle = NULL;
+               RETVAL_STRINGL((char *)ib_blob, sizeof(ibase_blob_handle), 1);
+               zend_list_delete(blob_arg->value.lval);
+       } else { /* discard created blob */
+               if (isc_cancel_blob(IB_STATUS, &ib_blob->bl_handle)){
+                       _php_ibase_error();
                        RETURN_FALSE;
                }
+               ib_blob->bl_handle = NULL;
+               zend_list_delete(blob_arg->value.lval);
+               RETURN_TRUE;
        }
+}
+/* }}} */
 
-       isqlda = php_ibase_prepare(db_handle, tr_handle, &query_handle, query->value.str.val);
-       ibase_query = (ibase_query_handle *) emalloc(sizeof(ibase_query_handle));
-       ibase_query->query = query_handle;
-       ibase_query->sqlda = isqlda;
-       ibase_query->trans = tr_handle;
-       ibase_query->alloced = 0;
-       return_value->value.lval = zend_list_insert(ibase_query, php_ibase_module.le_query);
-       return_value->type = IS_LONG;
+
+/* {{{ proto int ibase_blob_close(int blob_id)
+   Close blob */
+PHP_FUNCTION(ibase_blob_close)
+{
+       _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU,BLOB_CLOSE);
 }
 /* }}} */
 
-/* {{{ proto int ibase_bind(int query)
-   Bind parameter placeholders in a previously prepared query. Still nonfunctional. */
-PHP_FUNCTION(ibase_bind)
+
+/* {{{ proto int ibase_blob_cancel(int blob_id)
+   Cancel creating blob */
+PHP_FUNCTION(ibase_blob_cancel)
 {
-       pval *query;
-       ibase_query_handle *ibase_query;
-       int type;
-       IBASE_TLS_VARS;
+       _php_ibase_blob_end(INTERNAL_FUNCTION_PARAM_PASSTHRU,BLOB_CANCEL);
+}
+/* }}} */
+
+
+/* {{{ proto object ibase_blob_info(string blob_id_str)
+   Return blob length and other useful info */
+PHP_FUNCTION(ibase_blob_info)
+{
+       pval *blob_arg, *result_var;
+       ibase_blob_handle *ib_blob_id;
+       IBASE_BLOBINFO bl_info;
+       IBLS_FETCH();
 
-       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &query)==FAILURE) {
+
+       RESET_ERRMSG;
+
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &blob_arg)==FAILURE) {
                WRONG_PARAM_COUNT;
        }
        
-       convert_to_long(query);
-       if (query->value.lval==0) {
+       GET_BLOB_ID_ARG(blob_arg, ib_blob_id);
+
+       if (array_init(return_value)==FAILURE){
                RETURN_FALSE;
        }
        
-       ibase_query = (ibase_query_handle *) zend_list_find(query->value.lval,&type);
-       
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_query) {
-               php_error(E_WARNING,"%d is not an InterBase query index",query->value.lval);
-               RETURN_FALSE;
-       }
+       if(ib_blob_id->bl_qd.gds_quad_high || ib_blob_id->bl_qd.gds_quad_low){ /*not null ?*/
+               if (isc_open_blob(IB_STATUS, &ib_blob_id->link, &ib_blob_id->trans_handle,
+                                                 &ib_blob_id->bl_handle, &ib_blob_id->bl_qd)){
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
 
-       if (ibase_query->sqlda == NULL) {
-               php_error(E_WARNING,"InterBase: trying to bind a query having no parameter placeholders");
-               RETURN_FALSE;
+               if(_php_ibase_blob_info(ib_blob_id->bl_handle,&bl_info)){
+                       RETURN_FALSE;
+               }
+               if(isc_close_blob(IB_STATUS, &ib_blob_id->bl_handle)){
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+               ib_blob_id->bl_handle = NULL;
+       }else{ /* null blob, all values to zero  */
+               bl_info.max_segment = 0;
+               bl_info.num_segments = 0;
+               bl_info.total_length = 0;
+               bl_info.bl_stream = 0;
        }
 
-       /* TODO 
-       for (i = 0; i < ibase_query->sqlda->sqld; i++) {
-               ibase_query->sqlda->sqlvar[i].sqlind = (short *) emalloc(sizeof(short));
-               coltype = ibase_query->sqlda->sqlvar[i].sqltype & ~1;
-               switch(coltype)
-                       {
-                       case SQL_TEXT:
-                               break;
-                       case SQL_VARYING:
-                               break;
-                       case SQL_SHORT:
-                               break;
-                       case SQL_LONG:
-                               break;
-                       case SQL_FLOAT:
-                               break;
-                       case SQL_DOUBLE:
-                               break;
-                       case SQL_DATE:
-                               break;
-                       case SQL_BLOB:
-                               break;
-                       case SQL_ARRAY:
-                               break;
-                       }
-       }
+       /* FIXME */
+       add_get_index_long(return_value, 0, bl_info.total_length, (void **)&result_var);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "length", sizeof("length"), result_var);
+       */
+
+       add_get_index_long(return_value, 1, bl_info.num_segments, (void **)&result_var);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "numseg", sizeof("numseg"), result_var);
+       */
+
+       add_get_index_long(return_value, 2, bl_info.max_segment, (void **)&result_var);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "maxseg", sizeof("maxseg"), result_var);
+       */
+
+       add_get_index_long(return_value, 3, bl_info.bl_stream, (void **)&result_var);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "stream", sizeof("stream"), result_var);
+       */
+       
+       add_get_index_long(return_value, 4,
+                                          (!ib_blob_id->bl_qd.gds_quad_high
+                                               && !ib_blob_id->bl_qd.gds_quad_low),
+                                          (void **)&result_var);
+       /*
+       zend_hash_pointer_update(return_value->value.ht, "isnull", sizeof("isnull"), result_var);
        */
 }
 /* }}} */
 
-/* {{{ proto int ibase_execute(int query)
-   Execute a previously prepared (and possibly binded) query */
-PHP_FUNCTION(ibase_execute)
+
+/* {{{ proto int ibase_blob_echo(string blob_id_str)
+   Output blob contents to browser */
+PHP_FUNCTION(ibase_blob_echo)
 {
-       pval *query;
-       ibase_query_handle *ibase_query;
-       ibase_result_handle *ibase_result;
-       int type;
-       ISC_STATUS status[20];
-       XSQLDA *osqlda;
-       IBASE_TLS_VARS;
+       pval *blob_arg;
+       char bl_data[IBASE_BLOB_SEG];
+       unsigned short seg_len;
+       ibase_blob_handle *ib_blob_id;
+       
+
+       IBLS_FETCH();
+
+       RESET_ERRMSG;
 
-       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &query)==FAILURE) {
+       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &blob_arg)==FAILURE) {
                WRONG_PARAM_COUNT;
        }
+
+       GET_BLOB_ID_ARG(blob_arg, ib_blob_id);
        
-       convert_to_long(query);
-       if (query->value.lval==0) {
+       if (!php_header()) {
                RETURN_FALSE;
        }
+
+       if(ib_blob_id){ /*not null ?*/
+               
+               if (isc_open_blob(IB_STATUS, &ib_blob_id->link, &ib_blob_id->trans_handle,
+                                                 &ib_blob_id->bl_handle,&ib_blob_id->bl_qd)){
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+
+               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);
+               }
        
-       ibase_query = (ibase_query_handle *) zend_list_find(query->value.lval,&type);
-       
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_query) {
-               php_error(E_WARNING,"%d is not an InterBase query index", query->value.lval);
-               RETURN_FALSE;
-       }
+               if (IB_STATUS[0] && (IB_STATUS[1] != isc_segstr_eof)){
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
 
-       osqlda = php_ibase_execute(ibase_query->trans, ibase_query->query, ibase_query->sqlda, status);
-       ibase_result = (ibase_result_handle *) emalloc(sizeof(ibase_result_handle));
-       ibase_result->result = ibase_query->query;
-       ibase_result->sqlda = osqlda;
-       return_value->value.lval = zend_list_insert(ibase_result, IBASE_GLOBAL(php_ibase_module).le_result);
-       return_value->type = IS_LONG;
+               if (isc_close_blob(IB_STATUS, &ib_blob_id->bl_handle)){
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+               ib_blob_id->bl_handle = NULL;
+       }/* not null ? */
+
+       RETURN_TRUE;
 }
 /* }}} */
+#ifndef PHP_WIN32
+#if 0
 
-/* {{{ proto int ibase_free_query(int query)
-   Free memory used by a query */
-PHP_FUNCTION(ibase_free_query)
+extern int le_fp, le_pp;
+extern int wsa_fp; /*to handle reading and writing to windows sockets*/
+
+/* {{{ proto string ibase_blob_import([link_identifier,] file_id)
+   Create blob, copy file in it, and close it */
+
+
+PHP_FUNCTION(ibase_blob_import)
 {
-       pval *query;
-       ibase_query_handle *ibase_query;
-       int type;
-       IBASE_TLS_VARS;
+       pval *link_arg, *file_arg;
+       int trans_n, type,      link_id = 0, file_id, size, b;
+       int issock=0, socketd=0, *sock;
+       ibase_blob_handle ib_blob;
+       ibase_db_link *ib_link;
+       char bl_data[IBASE_BLOB_SEG]; /* FIXME? blob_seg_size parameter?         */
+       FILE *fp;
+       IBLS_FETCH();
 
-       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &query)==FAILURE) {
-               WRONG_PARAM_COUNT;
+
+       RESET_ERRMSG;
+
+       switch (ARG_COUNT(ht)) {
+               case 1:
+                       if (getParameters(ht, 1, &file_arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       link_id = IBG(default_link);
+                       break;
+               case 2:
+                       if (getParameters(ht, 2, &link_arg, &file_arg) == FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       convert_to_long(link_arg);
+                       link_id = link_arg->value.lval;
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
        }
        
-       convert_to_long(query);
-       if (query->value.lval==0) {
+       GET_LINK_TRANS(link_id, ib_link, trans_n);
+       
+       /* open default transaction */
+       if(_php_ibase_def_trans(ib_link, trans_n) == FAILURE){
                RETURN_FALSE;
        }
+
+       convert_to_long(file_arg);
+       file_id = file_arg->value.lval;
+    fp = zend_list_find(file_id, &type);
+       if (type == wsa_fp){
+               issock = 1;
+               sock = zend_list_find(file_id, &type);
+               socketd =* sock;
+    }
+    
+    if ((!fp || (type!= le_fp) && type!=le_pp
+         && (!socketd || type != wsa_fp))) {
+        _php_ibase_module_error("Unable to find file identifier %d",file_id);
+        RETURN_FALSE;
+    }
+
+       ib_blob.link = ib_link->link;
+       ib_blob.trans_handle = ib_link->trans[trans_n];
+       ib_blob.bl_handle = NULL;
+       ib_blob.bl_qd.gds_quad_high = 0;
+       ib_blob.bl_qd.gds_quad_low = 0;
        
-       ibase_query = (ibase_query_handle *) zend_list_find(query->value.lval, &type);
-       
-       if (type!=IBASE_GLOBAL(php_ibase_module).le_query) {
-               php_error(E_WARNING,"%d is not an InterBase query index", query->value.lval);
+       if (isc_create_blob(IB_STATUS, &ib_blob.link, &ib_blob.trans_handle, &ib_blob.bl_handle,&ib_blob.bl_qd)){
+               _php_ibase_error();
                RETURN_FALSE;
        }
-       zend_list_delete(query->value.lval);
-       RETURN_TRUE;
-}
-/* }}} */
-
-/* {{{ proto int ibase_timefmt(string format)
-   Sets the format of datetime columns returned from queries. Still nonfunctional. */
-PHP_FUNCTION(ibase_timefmt)
-{
-       pval *pmode;
-       IBASE_TLS_VARS;
 
-#if HAVE_STRFTIME
-       if (ARG_COUNT(ht)!=1 || getParameters(ht, 1, &pmode)==FAILURE) {
-               WRONG_PARAM_COUNT;
+       size = 0;
+       while (issock?(b=SOCK_FREAD(bl_data,sizeof(bl_data),socketd)):(b = fread(bl_data, 1, sizeof(bl_data), fp)) > 0) {
+               if (isc_put_segment(IB_STATUS, &ib_blob.bl_handle, b, bl_data)) {
+                       _php_ibase_error();
+                       RETURN_FALSE;
+               }
+               size += b;
+       }
+       
+       if (isc_close_blob(IB_STATUS, &ib_blob.bl_handle)){
+               _php_ibase_error();
+               RETURN_FALSE;
        }
-       convert_to_string(pmode);
 
-       RETURN_TRUE;
-#else
-       php_error(E_WARNING,"InterBase: ibase_timefmt not supported on this platform");
-       RETURN_FALSE;
-#endif
+       zend_list_delete(file_id);
+
+       ib_blob.bl_handle = NULL;
+       RETVAL_STRINGL((char *)&ib_blob, sizeof(ibase_blob_handle), 1);
 }
 /* }}} */
+#endif
+#endif
+
 
 #endif
 
@@ -1115,3 +2657,4 @@ PHP_FUNCTION(ibase_timefmt)
  * c-basic-offset: 4
  * End:
  */
+
index d5ae548ce42a8ae585b7835ebc77a2ddf34e0630..812419feb8c7ece3a9159108c99436c3f5c85785 100644 (file)
@@ -1,30 +1,19 @@
 /*
    +----------------------------------------------------------------------+
-   | PHP HTML Embedded Scripting Language Version 3.0                     |
+   | PHP version 4.0                                                      |
    +----------------------------------------------------------------------+
-   | Copyright (c) 1997,1998 PHP Development Team (See Credits file)      |
+   | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group                   |
    +----------------------------------------------------------------------+
-   | This program is free software; you can redistribute it and/or modify |
-   | it under the terms of one of the following licenses:                 |
-   |                                                                      |
-   |  A) the GNU General Public License as published by the Free Software |
-   |     Foundation; either version 2 of the License, or (at your option) |
-   |     any later version.                                               |
-   |                                                                      |
-   |  B) the PHP License as published by the PHP Development Team and     |
-   |     included in the distribution in the file: LICENSE                |
-   |                                                                      |
-   | This program is distributed in the hope that it will be useful,      |
-   | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
-   | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
-   | GNU General Public License for more details.                         |
-   |                                                                      |
-   | You should have received a copy of both licenses referred to here.   |
-   | If you did not, or have any questions about PHP licensing, please    |
-   | contact core@php.net.                                                |
+   | This source file is subject to version 2.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_01.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: Jouni Ahto <jah@cultnet.fi>                                 |
-   |                                                                      |
+   | Authors: Jouni Ahto <jah@mork.net>                                                          |
+   |             Andrew Avdeev <andy@simgts.mv.ru>                                                       |
    +----------------------------------------------------------------------+
  */
 
 #include <ibase.h>
 
 extern zend_module_entry ibase_module_entry;
-#define php_ibase_module_ptr &ibase_module_entry
+#define phpext_interbase_ptr &ibase_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_IBASE_API __declspec(dllexport)
+#else
+#define PHP_IBASE_API
+#endif
 
 extern PHP_MINIT_FUNCTION(ibase);
 extern PHP_RINIT_FUNCTION(ibase);
 extern PHP_MSHUTDOWN_FUNCTION(ibase);
+extern PHP_MSHUTDOWN_FUNCTION(ibase);
 PHP_MINFO_FUNCTION(ibase);
 
 PHP_FUNCTION(ibase_connect);
@@ -54,53 +50,123 @@ PHP_FUNCTION(ibase_pconnect);
 PHP_FUNCTION(ibase_close);
 PHP_FUNCTION(ibase_query);
 PHP_FUNCTION(ibase_fetch_row);
+PHP_FUNCTION(ibase_fetch_object);
 PHP_FUNCTION(ibase_free_result);
 PHP_FUNCTION(ibase_prepare);
-PHP_FUNCTION(ibase_bind);
 PHP_FUNCTION(ibase_execute);
 PHP_FUNCTION(ibase_free_query);
 PHP_FUNCTION(ibase_timefmt);
 
+PHP_FUNCTION(ibase_num_fields);
+PHP_FUNCTION(ibase_field_info);
+
+PHP_FUNCTION(ibase_trans);
+PHP_FUNCTION(ibase_commit);
+PHP_FUNCTION(ibase_rollback);
+
+PHP_FUNCTION(ibase_blob_create);
+PHP_FUNCTION(ibase_blob_add);
+PHP_FUNCTION(ibase_blob_cancel);
+PHP_FUNCTION(ibase_blob_open);
+PHP_FUNCTION(ibase_blob_get);
+PHP_FUNCTION(ibase_blob_close);
+PHP_FUNCTION(ibase_blob_echo);
+PHP_FUNCTION(ibase_blob_info);
+PHP_FUNCTION(ibase_blob_import);
+
+PHP_FUNCTION(ibase_errmsg);
+
+#define IBASE_MSGSIZE 256
+#define MAX_ERRMSG (IBASE_MSGSIZE*2)
+#define IBASE_TRANS_ON_LINK 10
+#define IBASE_BLOB_SEG 4096
+
 typedef struct {
+       ISC_STATUS status[20];
        long default_link;
        long num_links, num_persistent;
        long max_links, max_persistent;
        long allow_persistent;
-       int le_link, le_plink, le_result, le_query;
+       int le_blob, le_link, le_plink, le_result, le_query;
        char *default_user, *default_password;
-       long manualtransactions;
        char *timeformat;
-} ibase_module;
+       char *cfg_timeformat;
+       char *errmsg;
+} php_ibase_globals;
+
+typedef struct {
+       isc_tr_handle trans[IBASE_TRANS_ON_LINK];
+       isc_db_handle link;
+} ibase_db_link;
 
-typedef struct _php_ibase_result {
-       isc_stmt_handle result;
+typedef struct {
+       ISC_ARRAY_DESC ar_desc;
+       int el_type, /* sqltype kinda SQL_TEXT, ...*/
+               el_size; /* element size in bytes */
+       ISC_LONG ISC_FAR ar_size; /* all array size in bytes */
+} ibase_array;
+
+typedef struct {
+       isc_tr_handle trans_handle; 
+       isc_db_handle link;
+       ISC_QUAD bl_qd;
+       isc_blob_handle bl_handle;
+} ibase_blob_handle;
+
+typedef struct {
+       isc_db_handle link; /* db link for this result */
        isc_tr_handle trans;
-       XSQLDA *sqlda;
-       int commitok;
-} ibase_result_handle;
+       isc_stmt_handle stmt;
+       XSQLDA *in_sqlda, *out_sqlda;
+       ibase_array *in_array, *out_array;
+       int in_array_cnt, out_array_cnt;
+} ibase_query;
 
-typedef struct _php_ibase_query {
-       isc_stmt_handle query;
+typedef struct {
+       isc_db_handle link; /* db link for this result */
        isc_tr_handle trans;
-       XSQLDA *sqlda;
-       int alloced;
-} ibase_query_handle;
+       isc_stmt_handle stmt;
+       int drop_stmt;
+       XSQLDA *out_sqlda;
+       ibase_array *out_array;
+} ibase_result;
 
 typedef struct _php_ibase_varchar {
-    short var_len;
-    char var_str[1];
+       short var_len;
+       char var_str[1];
 } IBASE_VCHAR;
 
+/*
 extern ibase_module php_ibase_module;
+*/
+
+enum php_interbase_option {
+       PHP_IBASE_DEFAULT = 0,
+       PHP_IBASE_TEXT = 1,
+       PHP_IBASE_TIMESTAMP = 2,
+       PHP_IBASE_READ = 4,
+       PHP_IBASE_COMMITTED = 8,
+       PHP_IBASE_CONSISTENCY = 16,
+       PHP_IBASE_NOWAIT = 32
+};
+
+#ifdef ZTS
+#define IBLS_D php_ibase_globals *ibase_globals
+#define IBG(v) (ibase_globals->v)
+#define IBLS_FETCH() php_ibase_globals *ibase_globals = ts_resource(ibase_globals_id)
+#else
+#define IBLS_D
+#define IBG(v) (ibase_globals.v)
+#define IBLS_FETCH()
+extern PHP_IBASE_API php_ibase_globals ibase_globals;
+#endif
 
 #else
 
-#define php_ibase_module_ptr NULL
+#define phpext_interbase_ptr NULL
 
 #endif /* HAVE_IBASE */
 
-#define phpext_interbase_ptr php_ibase_module_ptr
-
 #endif /* _PHP_IBASE_H */
 
 /*