export/import LOB
[m6w6/ext-pq] / php_pq.c
index 6743cf60dda2744e0341d7d5375eb40d25e470ee..46088498357cb0bc8304ea3e07e96b46c216a591 100644 (file)
--- a/php_pq.c
+++ b/php_pq.c
@@ -88,7 +88,6 @@ static zend_class_entry *php_pqres_class_entry;
 static zend_class_entry *php_pqstm_class_entry;
 static zend_class_entry *php_pqtxn_class_entry;
 static zend_class_entry *php_pqcancel_class_entry;
-static zend_class_entry *php_pqevent_class_entry;
 static zend_class_entry *php_pqlob_class_entry;
 static zend_class_entry *php_pqcopy_class_entry;
 
@@ -152,7 +151,6 @@ static zend_object_handlers php_pqres_object_handlers;
 static zend_object_handlers php_pqstm_object_handlers;
 static zend_object_handlers php_pqtxn_object_handlers;
 static zend_object_handlers php_pqcancel_object_handlers;
-static zend_object_handlers php_pqevent_object_handlers;
 static zend_object_handlers php_pqlob_object_handlers;
 static zend_object_handlers php_pqcopy_object_handlers;
 
@@ -297,6 +295,7 @@ typedef struct php_pqevent_object {
 typedef struct php_pqlob {
        int lofd;
        Oid loid;
+       int stream;
        php_pqtxn_object_t *txn;
 } php_pqlob_t;
 
@@ -338,7 +337,6 @@ static HashTable php_pqres_object_prophandlers;
 static HashTable php_pqstm_object_prophandlers;
 static HashTable php_pqtxn_object_prophandlers;
 static HashTable php_pqcancel_object_prophandlers;
-static HashTable php_pqevent_object_prophandlers;
 static HashTable php_pqlob_object_prophandlers;
 static HashTable php_pqcopy_object_prophandlers;
 
@@ -430,37 +428,39 @@ static zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_
                }
        }
 
-       for (c = 0, cols = PQnfields(res); c < cols; ++c) {
-               if (PQgetisnull(res, row, c)) {
-                       switch (fetch_type) {
-                       case PHP_PQRES_FETCH_OBJECT:
-                               add_property_null(data, PQfname(res, c));
-                               break;
+       if (PQntuples(res) > row) {
+               for (c = 0, cols = PQnfields(res); c < cols; ++c) {
+                       if (PQgetisnull(res, row, c)) {
+                               switch (fetch_type) {
+                               case PHP_PQRES_FETCH_OBJECT:
+                                       add_property_null(data, PQfname(res, c));
+                                       break;
 
-                       case PHP_PQRES_FETCH_ASSOC:
-                               add_assoc_null(data, PQfname(res, c));
-                               break;
+                               case PHP_PQRES_FETCH_ASSOC:
+                                       add_assoc_null(data, PQfname(res, c));
+                                       break;
 
-                       case PHP_PQRES_FETCH_ARRAY:
-                               add_index_null(data, c);
-                               break;
-                       }
-               } else {
-                       char *val = PQgetvalue(res, row, c);
-                       int len = PQgetlength(res, row, c);
+                               case PHP_PQRES_FETCH_ARRAY:
+                                       add_index_null(data, c);
+                                       break;
+                               }
+                       } else {
+                               char *val = PQgetvalue(res, row, c);
+                               int len = PQgetlength(res, row, c);
 
-                       switch (fetch_type) {
-                       case PHP_PQRES_FETCH_OBJECT:
-                               add_property_stringl(data, PQfname(res, c), val, len, 1);
-                               break;
+                               switch (fetch_type) {
+                               case PHP_PQRES_FETCH_OBJECT:
+                                       add_property_stringl(data, PQfname(res, c), val, len, 1);
+                                       break;
 
-                       case PHP_PQRES_FETCH_ASSOC:
-                               add_assoc_stringl(data, PQfname(res, c), val, len, 1);
-                               break;
+                               case PHP_PQRES_FETCH_ASSOC:
+                                       add_assoc_stringl(data, PQfname(res, c), val, len, 1);
+                                       break;
 
-                       case PHP_PQRES_FETCH_ARRAY:
-                               add_index_stringl(data, c, val, len ,1);
-                               break;
+                               case PHP_PQRES_FETCH_ARRAY:
+                                       add_index_stringl(data, c, val, len ,1);
+                                       break;
+                               }
                        }
                }
        }
@@ -473,10 +473,9 @@ static void php_pqres_iterator_current(zend_object_iterator *i, zval ***data_ptr
        php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
        php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
 
-       if (iter->current_val) {
-               zval_ptr_dtor(&iter->current_val);
+       if (!iter->current_val) {
+               iter->current_val = php_pqres_row_to_zval(obj->intern->res, iter->index, iter->fetch_type, NULL TSRMLS_CC);
        }
-       iter->current_val = php_pqres_row_to_zval(obj->intern->res, iter->index, iter->fetch_type, NULL TSRMLS_CC);
        *data_ptr = &iter->current_val;
 }
 
@@ -489,10 +488,21 @@ static int php_pqres_iterator_key(zend_object_iterator *i, char **key_str, uint
        return HASH_KEY_IS_LONG;
 }
 
+static void php_pqres_iterator_invalidate(zend_object_iterator *i TSRMLS_DC)
+{
+       php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
+
+       if (iter->current_val) {
+               zval_ptr_dtor(&iter->current_val);
+               iter->current_val = NULL;
+       }
+}
+
 static void php_pqres_iterator_next(zend_object_iterator *i TSRMLS_DC)
 {
        php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
 
+       php_pqres_iterator_invalidate(i TSRMLS_CC);
        ++iter->index;
 }
 
@@ -500,6 +510,7 @@ static void php_pqres_iterator_rewind(zend_object_iterator *i TSRMLS_DC)
 {
        php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
 
+       php_pqres_iterator_invalidate(i TSRMLS_CC);
        iter->index = 0;
 }
 
@@ -516,7 +527,7 @@ static zend_object_iterator_funcs php_pqres_iterator_funcs = {
        /* rewind to start of data (optional, may be NULL) */
        php_pqres_iterator_rewind,
        /* invalidate current value/key (optional, may be NULL) */
-       NULL
+       php_pqres_iterator_invalidate
 };
 
 static int php_pqres_count_elements(zval *object, long *count TSRMLS_DC)
@@ -547,6 +558,25 @@ static STATUS php_pqres_success(PGresult *res TSRMLS_DC)
        }
 }
 
+static void php_pq_callback_dtor(php_pq_callback_t *cb) {
+       if (cb->fci.size > 0) {
+               zend_fcall_info_args_clear(&cb->fci, 1);
+               zval_ptr_dtor(&cb->fci.function_name);
+               if (cb->fci.object_ptr) {
+                       zval_ptr_dtor(&cb->fci.object_ptr);
+               }
+       }
+       cb->fci.size = 0;
+}
+
+static void php_pq_callback_addref(php_pq_callback_t *cb)
+{
+       Z_ADDREF_P(cb->fci.function_name);
+       if (cb->fci.object_ptr) {
+               Z_ADDREF_P(cb->fci.object_ptr);
+       }
+}
+
 /*
 static void php_pqconn_del_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, ulong id TSRMLS_DC)
 {
@@ -558,46 +588,23 @@ static void php_pqconn_del_eventhandler(php_pqconn_object_t *obj, const char *ty
 }
 */
 
-static ulong php_pqconn_add_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, zval *zevent TSRMLS_DC)
+static ulong php_pqconn_add_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, php_pq_callback_t *cb TSRMLS_DC)
 {
-       zval **evhs;
        ulong h;
+       HashTable *evhs;
 
-       if (SUCCESS == zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) {
-               Z_ADDREF_P(zevent);
-               h = zend_hash_next_free_element(Z_ARRVAL_PP(evhs));
-               add_next_index_zval(*evhs, zevent);
-       } else {
-               zval *evh;
+       if (SUCCESS != zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) {
+               HashTable evh;
 
-               MAKE_STD_ZVAL(evh);
-               array_init(evh);
-               Z_ADDREF_P(zevent);
-               h = zend_hash_next_free_element(Z_ARRVAL_P(evh));
-               add_next_index_zval(evh, zevent);
-               zend_hash_add(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(zval *), NULL);
+               zend_hash_init(&evh, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0);
+               zend_hash_add(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(evh), (void *) &evhs);
        }
 
-       return h;
-}
-
-static void php_pq_callback_dtor(php_pq_callback_t *cb) {
-       if (cb->fci.size > 0) {
-               zend_fcall_info_args_clear(&cb->fci, 1);
-               zval_ptr_dtor(&cb->fci.function_name);
-               if (cb->fci.object_ptr) {
-                       zval_ptr_dtor(&cb->fci.object_ptr);
-               }
-       }
-       cb->fci.size = 0;
-}
+       php_pq_callback_addref(cb);
+       h = zend_hash_next_free_element(evhs);
+       zend_hash_index_update(evhs, h, (void *) cb, sizeof(*cb), NULL);
 
-static void php_pq_callback_addref(php_pq_callback_t *cb)
-{
-       Z_ADDREF_P(cb->fci.function_name);
-       if (cb->fci.object_ptr) {
-               Z_ADDREF_P(cb->fci.object_ptr);
-       }
+       return h;
 }
 
 static void php_pq_object_to_zval(void *o, zval **zv TSRMLS_DC)
@@ -776,22 +783,6 @@ static void php_pqcancel_object_free(void *o TSRMLS_DC)
        efree(obj);
 }
 
-static void php_pqevent_object_free(void *o TSRMLS_DC)
-{
-       php_pqevent_object_t *obj = o;
-#if DBG_GC
-       fprintf(stderr, "FREE event(#%d) %p\n", obj->zv.handle, obj);
-#endif
-       if (obj->intern) {
-               php_pq_callback_dtor(&obj->intern->cb);
-               efree(obj->intern->type);
-               efree(obj->intern);
-               obj->intern = NULL;
-       }
-       zend_object_std_dtor((zend_object *) o TSRMLS_CC);
-       efree(obj);
-}
-
 static void php_pqlob_object_free(void *o TSRMLS_DC)
 {
        php_pqlob_object_t *obj = o;
@@ -802,6 +793,11 @@ static void php_pqlob_object_free(void *o TSRMLS_DC)
                if (obj->intern->lofd) {
                        lo_close(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd);
                }
+               /* invalidate the stream */
+               if (obj->intern->stream) {
+                       zend_list_delete(obj->intern->stream);
+                       obj->intern->stream = 0;
+               }
                php_pq_object_delref(obj->intern->txn TSRMLS_CC);
                efree(obj->intern);
                obj->intern = NULL;
@@ -965,29 +961,6 @@ static zend_object_value php_pqcancel_create_object_ex(zend_class_entry *ce, php
        return o->zv;
 }
 
-static zend_object_value php_pqevent_create_object_ex(zend_class_entry *ce, php_pqevent_t *intern, php_pqevent_object_t **ptr TSRMLS_DC)
-{
-       php_pqevent_object_t *o;
-
-       o = ecalloc(1, sizeof(*o));
-       zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
-       object_properties_init((zend_object *) o, ce);
-       o->prophandler = &php_pqevent_object_prophandlers;
-
-       if (ptr) {
-               *ptr = o;
-       }
-
-       if (intern) {
-               o->intern = intern;
-       }
-
-       o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqevent_object_free, NULL TSRMLS_CC);
-       o->zv.handlers = &php_pqevent_object_handlers;
-
-       return o->zv;
-}
-
 static zend_object_value php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern, php_pqlob_object_t **ptr TSRMLS_DC)
 {
        php_pqlob_object_t *o;
@@ -1064,11 +1037,6 @@ static zend_object_value php_pqcancel_create_object(zend_class_entry *class_type
        return php_pqcancel_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
 }
 
-static zend_object_value php_pqevent_create_object(zend_class_entry *class_type TSRMLS_DC)
-{
-       return php_pqevent_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
-}
-
 static zend_object_value php_pqlob_create_object(zend_class_entry *class_type TSRMLS_DC)
 {
        return php_pqlob_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
@@ -1637,13 +1605,6 @@ static void php_pqcancel_object_read_connection(zval *object, void *o, zval *ret
        php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
 }
 
-static void php_pqevent_object_read_type(zval *object, void *o, zval *return_value TSRMLS_DC)
-{
-       php_pqevent_object_t *obj = o;
-
-       RETVAL_STRING(obj->intern->type, 1);
-}
-
 static void php_pqlob_object_read_transaction(zval *object, void *o, zval *return_value TSRMLS_DC)
 {
        php_pqlob_object_t *obj = o;
@@ -1658,6 +1619,23 @@ static void php_pqlob_object_read_oid(zval *object, void *o, zval *return_value
        RETVAL_LONG(obj->intern->loid);
 }
 
+static void php_pqlob_object_update_stream(zval *this_ptr, php_pqlob_object_t *obj, zval **zstream TSRMLS_DC);
+
+static void php_pqlob_object_read_stream(zval *object, void *o, zval *return_value TSRMLS_DC)
+{
+       php_pqlob_object_t *obj = o;
+
+       if (!obj->intern->stream) {
+               zval *zstream;
+
+               php_pqlob_object_update_stream(object, obj, &zstream TSRMLS_CC);
+               RETVAL_ZVAL(zstream, 1, 1);
+       } else {
+               RETVAL_RESOURCE(obj->intern->stream);
+               zend_list_addref(obj->intern->stream);
+       }
+}
+
 static void php_pqcopy_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
 {
        php_pqcopy_object_t *obj = o;
@@ -1774,11 +1752,12 @@ static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj
 
 static int apply_event(void *p, void *a TSRMLS_DC)
 {
-       zval **evh = p;
+       php_pq_callback_t *cb = p;
        zval *args = a;
        zval *retval = NULL;
 
-       zend_call_method_with_1_params(evh, Z_OBJCE_PP(evh), NULL, "trigger", &retval, args);
+       zend_fcall_info_args(&cb->fci, args TSRMLS_CC);
+       zend_fcall_info_call(&cb->fci, &cb->fcc, &retval, NULL TSRMLS_CC);
        if (retval) {
                zval_ptr_dtor(&retval);
        }
@@ -1791,7 +1770,7 @@ static void php_pqconn_event_connreset(PGEventConnReset *event)
        php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event);
 
        if (data) {
-               zval **evhs;
+               HashTable *evhs;
                TSRMLS_DF(data);
 
                if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("reset"), (void *) &evhs)) {
@@ -1801,7 +1780,7 @@ static void php_pqconn_event_connreset(PGEventConnReset *event)
                        array_init(args);
                        php_pq_object_to_zval(data->obj, &connection TSRMLS_CC);
                        add_next_index_zval(args, connection);
-                       zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC);
+                       zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC);
                        zval_ptr_dtor(&args);
                }
        }
@@ -1829,7 +1808,7 @@ static void php_pqconn_event_resultcreate(PGEventResultCreate *event)
 
        if (data) {
                php_pqres_object_t *obj;
-               zval **evhs;
+               HashTable *evhs;
                TSRMLS_DF(data);
 
                php_pqres_init_instance_data(event->result, &obj TSRMLS_CC);
@@ -1844,7 +1823,7 @@ static void php_pqconn_event_resultcreate(PGEventResultCreate *event)
                        add_next_index_zval(args, connection);
                        php_pq_object_to_zval(obj, &res TSRMLS_CC);
                        add_next_index_zval(args, res);
-                       zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC);
+                       zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC);
                        zval_ptr_dtor(&args);
                }
 
@@ -1904,7 +1883,7 @@ static void php_pqconn_notice_recv(void *p, const PGresult *res)
        php_pqconn_event_data_t *data = p;
 
        if (data) {
-               zval **evhs;
+               HashTable *evhs;
                TSRMLS_DF(data);
 
                if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("notice"), (void *) &evhs)) {
@@ -1915,7 +1894,7 @@ static void php_pqconn_notice_recv(void *p, const PGresult *res)
                        php_pq_object_to_zval(data->obj, &connection TSRMLS_CC);
                        add_next_index_zval(args, connection);
                        add_next_index_string(args, PHP_PQresultErrorMessage(res), 1);
-                       zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC);
+                       zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC);
                        zval_ptr_dtor(&args);
        }
        }
@@ -2069,7 +2048,7 @@ static PHP_METHOD(pqconn, __construct) {
                        obj->intern = ecalloc(1, sizeof(*obj->intern));
 
                        zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
-                       zend_hash_init(&obj->intern->eventhandlers, 0, NULL, ZVAL_PTR_DTOR, 0);
+                       zend_hash_init(&obj->intern->eventhandlers, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
 
                        if (flags & PHP_PQCONN_PERSISTENT) {
                                php_persistent_handle_factory_t *phf = php_persistent_handle_concede(NULL, ZEND_STRL("pq\\Connection"), dsn_str, dsn_len, php_pqconn_wakeup, php_pqconn_retire TSRMLS_CC);
@@ -2410,7 +2389,7 @@ static PHP_METHOD(pqconn, getResult) {
                php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 
                if (!obj->intern) {
-                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connectio not initialized");
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
                        PGresult *res = PQgetResult(obj->intern->conn);
 
@@ -3119,6 +3098,34 @@ static PHP_METHOD(pqconn, trace) {
        }
 }
 
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_on, 0, 0, 2)
+       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqconn, on) {
+       zend_error_handling zeh;
+       char *type_str;
+       int type_len;
+       php_pq_callback_t cb;
+       STATUS rv;
+
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &type_str, &type_len, &cb.fci, &cb.fcc);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (SUCCESS == rv) {
+               php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (!obj->intern) {
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
+               } else {
+                       php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+                       RETVAL_LONG(php_pqconn_add_eventhandler(obj, type_str, type_len, &cb TSRMLS_CC));
+               }
+       }
+}
+
 static zend_function_entry php_pqconn_methods[] = {
        PHP_ME(pqconn, __construct, ai_pqconn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
        PHP_ME(pqconn, reset, ai_pqconn_reset, ZEND_ACC_PUBLIC)
@@ -3142,6 +3149,7 @@ static zend_function_entry php_pqconn_methods[] = {
        PHP_ME(pqconn, startTransaction, ai_pqconn_start_transaction, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, startTransactionAsync, ai_pqconn_start_transaction_async, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, trace, ai_pqconn_trace, ZEND_ACC_PUBLIC)
+       PHP_ME(pqconn, on, ai_pqconn_on, ZEND_ACC_PUBLIC)
        {0}
 };
 
@@ -3284,7 +3292,9 @@ static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_p
         obj = zend_object_store_get_object(getThis() TSRMLS_CC);
        }
 
-       if (!obj->intern->iter) {
+       if (obj->intern->iter) {
+               obj->intern->iter->zi.funcs->move_forward((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
+       } else {
                obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(getThis()), getThis(), 0 TSRMLS_CC);
                obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
        }
@@ -3292,7 +3302,6 @@ static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_p
        obj->intern->iter->fetch_type = fetch_type;
        if (SUCCESS == (rv = obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC))) {
                obj->intern->iter->zi.funcs->get_current_data((zend_object_iterator *) obj->intern->iter, row TSRMLS_CC);
-               obj->intern->iter->zi.funcs->move_forward((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
        }
        obj->intern->iter->fetch_type = orig_fetch;
 
@@ -4481,7 +4490,7 @@ static PHP_METHOD(pqtxn, openLOB) {
                        int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode);
 
                        if (lofd < 0) {
-                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%ld with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
                        } else {
                                php_pqlob_t *lob = ecalloc(1, sizeof(*lob));
 
@@ -4525,7 +4534,7 @@ static PHP_METHOD(pqtxn, createLOB) {
                                int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode);
 
                                if (lofd < 0) {
-                                       throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%ld with mode '%s': %s", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                                       throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s': %s", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
                                } else {
                                        php_pqlob_t *lob = ecalloc(1, sizeof(*lob));
 
@@ -4565,7 +4574,77 @@ static PHP_METHOD(pqtxn, unlinkLOB) {
                        int rc = lo_unlink(obj->intern->conn->intern->conn, loid);
 
                        if (rc != 1) {
-                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to unlink LOB (oid=%ld): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to unlink LOB (oid=%u): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                       }
+
+                       php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_lob, 0, 0, 1)
+       ZEND_ARG_INFO(0, local_path)
+       ZEND_ARG_INFO(0, oid)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, importLOB) {
+       zend_error_handling zeh;
+       char *path_str;
+       int path_len;
+       long oid = InvalidOid;
+       STATUS rv;
+
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &path_str, &path_len, &oid);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (rv == SUCCESS) {
+               php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (!obj->intern) {
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
+               } else {
+                       if (oid == InvalidOid) {
+                               oid = lo_import(obj->intern->conn->intern->conn, path_str);
+                       } else {
+                               oid = lo_import_with_oid(obj->intern->conn->intern->conn, path_str, oid);
+                       }
+
+                       if (oid == InvalidOid) {
+                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import LOB from '%s' (%s)", path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                       } else {
+                               RETVAL_LONG(oid);
+                       }
+
+                       php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
+               }
+       }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_lob, 0, 0, 2)
+       ZEND_ARG_INFO(0, oid)
+       ZEND_ARG_INFO(0, local_path)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, exportLOB) {
+       zend_error_handling zeh;
+       char *path_str;
+       int path_len;
+       long oid;
+       STATUS rv;
+
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lp", &oid, &path_str, &path_len);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (rv == SUCCESS) {
+               php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (!obj->intern) {
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
+               } else {
+                       int rc = lo_export(obj->intern->conn->intern->conn, oid, path_str);
+
+                       if (rc == -1) {
+                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export LOB (oid=%u) to '%s' (%s)", oid, path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
                        }
 
                        php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
@@ -4588,6 +4667,8 @@ static zend_function_entry php_pqtxn_methods[] = {
        PHP_ME(pqtxn, openLOB, ai_pqtxn_open_lob, ZEND_ACC_PUBLIC)
        PHP_ME(pqtxn, createLOB, ai_pqtxn_create_lob, ZEND_ACC_PUBLIC)
        PHP_ME(pqtxn, unlinkLOB, ai_pqtxn_unlink_lob, ZEND_ACC_PUBLIC)
+       PHP_ME(pqtxn, importLOB, ai_pqtxn_import_lob, ZEND_ACC_PUBLIC)
+       PHP_ME(pqtxn, exportLOB, ai_pqtxn_export_lob, ZEND_ACC_PUBLIC)
        {0}
 };
 
@@ -4656,79 +4737,123 @@ static zend_function_entry php_pqcancel_methods[] = {
        {0}
 };
 
-ZEND_BEGIN_ARG_INFO_EX(ai_pqevent_construct, 0, 0, 3)
-       ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
-       ZEND_ARG_INFO(0, type)
-       ZEND_ARG_INFO(0, callable)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(pqevent, __construct) {
-       zend_error_handling zeh;
-       zval *zconn;
-       char *type_str;
-       int type_len;
-       php_pq_callback_t cb;
-       STATUS rv;
+static size_t php_pqlob_stream_write(php_stream *stream, const char *buffer, size_t length TSRMLS_DC)
+{
+       php_pqlob_object_t *obj = stream->abstract;
+       int written = 0;
 
-       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
-       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osf", &zconn, php_pqconn_class_entry, &type_str, &type_len, &cb.fci, &cb.fcc);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
+       if (obj) {
+               written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length);
 
-       if (SUCCESS == rv) {
-               php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
+               if (written < 0) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write to LOB with oid=%u (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
+               }
 
-               if (!conn_obj->intern) {
-                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
+               php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC);
+       }
+
+       return written;
+}
+
+static size_t php_pqlob_stream_read(php_stream *stream, char *buffer, size_t length TSRMLS_DC)
+{
+       php_pqlob_object_t *obj = stream->abstract;
+       int read = 0;
+
+       if (obj) {
+
+               if (!buffer && !length) {
+                       if (lo_tell(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd) == lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, 0, SEEK_CUR)) {
+                               return EOF;
+                       }
                } else {
-                       php_pqevent_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+                       read = lo_read(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length);
 
-                       obj->intern = ecalloc(1, sizeof(*obj->intern));
-                       php_pq_callback_addref(&cb);
-                       obj->intern->cb = cb;
-                       obj->intern->type = estrdup(type_str);
-                       obj->intern->h = php_pqconn_add_eventhandler(conn_obj, type_str, type_len, getThis() TSRMLS_CC);
+                       if (read < 0) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to read from LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
+                       }
                }
+
+               php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC);
        }
+
+       return read;
 }
 
-ZEND_BEGIN_ARG_INFO_EX(ai_pqevent_trigger, 0, 0, 1)
-       ZEND_ARG_ARRAY_INFO(0, args, 1)
-ZEND_END_ARG_INFO();
-static PHP_METHOD(pqevent, trigger) {
-       zend_error_handling zeh;
-       zval *args;
-       STATUS rv;
+static STATUS php_pqlob_stream_close(php_stream *stream, int close_handle TSRMLS_DC)
+{
+       return SUCCESS;
+}
 
-       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
-       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &args);
-       zend_restore_error_handling(&zeh TSRMLS_CC);
+static int php_pqlob_stream_flush(php_stream *stream TSRMLS_DC)
+{
+       return SUCCESS;
+}
 
-       if (SUCCESS == rv) {
-               php_pqevent_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+static STATUS php_pqlob_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC)
+{
+       STATUS rv = FAILURE;
+       php_pqlob_object_t *obj = stream->abstract;
 
-               if (!obj->intern) {
-                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Event not initialized");
-               } else {
-                       zval *rv = NULL;
+       if (obj) {
+               int position = lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, offset, whence);
 
-                       if (SUCCESS != zend_fcall_info_call(&obj->intern->cb.fci, &obj->intern->cb.fcc, &rv, args TSRMLS_CC)) {
-                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to trigger event");
-                       } else {
-                               if (rv) {
-                                       RETVAL_ZVAL(rv, 0, 1);
-                               } else {
-                                       RETVAL_TRUE;
-                               }
-                       }
+               if (position < 0) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
+                       rv = FAILURE;
+               } else {
+                       *newoffset = position;
+                       rv = SUCCESS;
                }
+
+               php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC);
        }
+
+       return rv;
 }
 
-static zend_function_entry php_pqevent_methods[] = {
-       PHP_ME(pqevent, __construct, ai_pqevent_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
-       PHP_ME(pqevent, trigger, ai_pqevent_trigger, ZEND_ACC_PUBLIC)
-       {0}
+static php_stream_ops php_pqlob_stream_ops = {
+       /* stdio like functions - these are mandatory! */
+       php_pqlob_stream_write,
+       php_pqlob_stream_read,
+       php_pqlob_stream_close,
+       php_pqlob_stream_flush,
+
+       "pq\\LOB stream",
+
+       /* these are optional */
+       php_pqlob_stream_seek,
+       NULL, /* cast */
+       NULL, /* stat */
+       NULL, /* set_option */
 };
 
+static void php_pqlob_object_update_stream(zval *this_ptr, php_pqlob_object_t *obj, zval **zstream_ptr TSRMLS_DC)
+{
+       zval *zstream, zmember;
+       php_stream *stream;
+
+       INIT_PZVAL(&zmember);
+       ZVAL_STRINGL(&zmember, "stream", sizeof("stream")-1, 0);
+
+       MAKE_STD_ZVAL(zstream);
+       if (!obj) {
+               obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       }
+       stream = php_stream_alloc(&php_pqlob_stream_ops, obj, NULL, "r+b");
+       stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
+       zend_list_addref(obj->intern->stream = stream->rsrc_id);
+       php_stream_to_zval(stream, zstream);
+
+       zend_get_std_object_handlers()->write_property(getThis(), &zmember, zstream, NULL TSRMLS_CC);
+
+       if (zstream_ptr) {
+               *zstream_ptr = zstream;
+       } else {
+               zval_ptr_dtor(&zstream);
+       }
+}
+
 ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_construct, 0, 0, 1)
        ZEND_ARG_OBJ_INFO(0, transaction, pq\\Transaction, 0)
        ZEND_ARG_INFO(0, oid)
@@ -4762,7 +4887,7 @@ static PHP_METHOD(pqlob, __construct) {
                                int lofd = lo_open(txn_obj->intern->conn->intern->conn, loid, mode);
 
                                if (lofd < 0) {
-                                       throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%ld with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn));
+                                       throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn));
                                } else {
                                        php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 
@@ -4801,7 +4926,7 @@ static PHP_METHOD(pqlob, write) {
                        int written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, data_str, data_len);
 
                        if (written < 0) {
-                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to write to LOB with oid=%ld (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
+                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to write to LOB with oid=%u (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn));
                        } else {
                                RETVAL_LONG(written);
                        }
@@ -5294,8 +5419,13 @@ static PHP_MINIT_FUNCTION(pq)
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_WRITING"), PGRES_POLLING_WRITING TSRMLS_CC);
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_OK"), PGRES_POLLING_OK TSRMLS_CC);
 
+       zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_NOTICE"), ZEND_STRL("notice") TSRMLS_CC);
+       zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_RESULT"), ZEND_STRL("result") TSRMLS_CC);
+       zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_RESET"), ZEND_STRL("reset") TSRMLS_CC);
+
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("ASYNC"), 0x1 TSRMLS_CC);
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("PERSISTENT"), 0x2 TSRMLS_CC);
+
        memset(&ce, 0, sizeof(ce));
        INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods);
        php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
@@ -5459,28 +5589,6 @@ static PHP_MINIT_FUNCTION(pq)
        ph.read = php_pqcancel_object_read_connection;
        zend_hash_add(&php_pqcancel_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
 
-       memset(&ce, 0, sizeof(ce));
-       INIT_NS_CLASS_ENTRY(ce, "pq", "Event", php_pqevent_methods);
-       php_pqevent_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
-       php_pqevent_class_entry->create_object = php_pqevent_create_object;
-
-       memcpy(&php_pqevent_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
-       php_pqevent_object_handlers.read_property = php_pq_object_read_prop;
-       php_pqevent_object_handlers.write_property = php_pq_object_write_prop;
-       php_pqevent_object_handlers.clone_obj = NULL;
-       php_pqevent_object_handlers.get_property_ptr_ptr = NULL;
-       php_pqevent_object_handlers.get_debug_info = php_pq_object_debug_info;
-
-       zend_hash_init(&php_pqevent_object_prophandlers, 1, NULL, NULL, 1);
-
-       zend_declare_property_null(php_pqevent_class_entry, ZEND_STRL("type"), ZEND_ACC_PUBLIC TSRMLS_CC);
-       ph.read = php_pqevent_object_read_type;
-       zend_hash_add(&php_pqevent_object_prophandlers, "type", sizeof("type"), (void *) &ph, sizeof(ph), NULL);
-
-       zend_declare_class_constant_stringl(php_pqevent_class_entry, ZEND_STRL("NOTICE"), ZEND_STRL("notice") TSRMLS_CC);
-       zend_declare_class_constant_stringl(php_pqevent_class_entry, ZEND_STRL("RESULT"), ZEND_STRL("result") TSRMLS_CC);
-       zend_declare_class_constant_stringl(php_pqevent_class_entry, ZEND_STRL("RESET"), ZEND_STRL("reset") TSRMLS_CC);
-
        memset(&ce, 0, sizeof(ce));
        INIT_NS_CLASS_ENTRY(ce, "pq", "LOB", php_pqlob_methods);
        php_pqlob_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
@@ -5493,7 +5601,7 @@ static PHP_MINIT_FUNCTION(pq)
        php_pqlob_object_handlers.get_property_ptr_ptr = NULL;
        php_pqlob_object_handlers.get_debug_info = php_pq_object_debug_info;
 
-       zend_hash_init(&php_pqlob_object_prophandlers, 2, NULL, NULL, 1);
+       zend_hash_init(&php_pqlob_object_prophandlers, 3, NULL, NULL, 1);
 
        zend_declare_property_null(php_pqlob_class_entry, ZEND_STRL("transaction"), ZEND_ACC_PUBLIC TSRMLS_CC);
        ph.read = php_pqlob_object_read_transaction;
@@ -5503,6 +5611,10 @@ static PHP_MINIT_FUNCTION(pq)
        ph.read = php_pqlob_object_read_oid;
        zend_hash_add(&php_pqlob_object_prophandlers, "oid", sizeof("oid"), (void *) &ph, sizeof(ph), NULL);
 
+       zend_declare_property_null(php_pqlob_class_entry, ZEND_STRL("stream"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       ph.read = php_pqlob_object_read_stream;
+       zend_hash_add(&php_pqlob_object_prophandlers, "stream", sizeof("stream"), (void *) &ph, sizeof(ph), NULL);
+
        zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("INVALID_OID"), InvalidOid TSRMLS_CC);
        zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("R"), INV_READ TSRMLS_CC);
        zend_declare_class_constant_long(php_pqlob_class_entry, ZEND_STRL("W"), INV_WRITE TSRMLS_CC);