Merge branch 'feature/cursor-async' of https://github.com/DaveRandom/pecl-database-pq
authorMichael Wallner <mike@php.net>
Fri, 29 May 2015 07:44:10 +0000 (09:44 +0200)
committerMichael Wallner <mike@php.net>
Fri, 29 May 2015 07:44:10 +0000 (09:44 +0200)
1  2 
src/php_pqconn.c
src/php_pqcur.c

diff --combined src/php_pqconn.c
index 47d06701dbb66e620d51fa539a1d34661ee21e8c,a455f92ba87e084b5fe79bcac99162347532ba69..51e126dbd5adaa5cc1f373f92c1a2815926c8344
@@@ -486,11 -486,11 +486,11 @@@ static void php_pqconn_object_write_def
        }
  }
  
 -static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
 +static ZEND_RESULT_CODE php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
  {
        zval *zsocket, zmember;
        php_stream *stream;
 -      STATUS retval;
 +      ZEND_RESULT_CODE retval;
        int socket;
  
        if (!obj) {
@@@ -652,14 -652,14 +652,14 @@@ static void php_pqconn_retire(php_persi
  
  ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1)
        ZEND_ARG_INFO(0, dsn)
 -      ZEND_ARG_INFO(0, async)
 +      ZEND_ARG_INFO(0, flags)
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, __construct) {
        zend_error_handling zeh;
        char *dsn_str = "";
        int dsn_len = 0;
        long flags = 0;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &dsn_str, &dsn_len, &flags);
@@@ -709,7 -709,7 +709,7 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, reset) {
        zend_error_handling zeh;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters_none();
@@@ -736,7 -736,7 +736,7 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset_
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, resetAsync) {
        zend_error_handling zeh;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters_none();
@@@ -767,7 -767,7 +767,7 @@@ static PHP_METHOD(pqconn, unlisten
        zend_error_handling zeh;
        char *channel_str;
        int channel_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len);
@@@ -796,7 -796,7 +796,7 @@@ static PHP_METHOD(pqconn, unlistenAsync
        zend_error_handling zeh;
        char *channel_str;
        int channel_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len);
@@@ -858,7 -858,7 +858,7 @@@ static PHP_METHOD(pqconn, listen) 
        char *channel_str = NULL;
        int channel_len = 0;
        php_pq_callback_t listener = {{0}};
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc);
@@@ -912,7 -912,7 +912,7 @@@ static PHP_METHOD(pqconn, listenAsync) 
        char *channel_str = NULL;
        int channel_len = 0;
        php_pq_callback_t listener = {{0}};
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc);
@@@ -958,7 -958,7 +958,7 @@@ static PHP_METHOD(pqconn, notify) 
        zend_error_handling zeh;
        char *channel_str, *message_str;
        int channel_len, message_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len);
@@@ -995,7 -995,7 +995,7 @@@ static PHP_METHOD(pqconn, notifyAsync) 
        zend_error_handling zeh;
        char *channel_str, *message_str;
        int channel_len, message_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len);
@@@ -1024,7 -1024,7 +1024,7 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_poll, 
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, poll) {
        zend_error_handling zeh;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters_none();
@@@ -1055,7 -1055,7 +1055,7 @@@ static PHP_METHOD(pqconn, exec) 
        zend_error_handling zeh;
        char *query_str;
        int query_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query_str, &query_len);
@@@ -1086,7 -1086,7 +1086,7 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_get_re
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, getResult) {
        zend_error_handling zeh;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters_none();
@@@ -1120,7 -1120,7 +1120,7 @@@ static PHP_METHOD(pqconn, execAsync) 
        php_pq_callback_t resolver = {{0}};
        char *query_str;
        int query_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f", &query_str, &query_len, &resolver.fci, &resolver.fcc);
@@@ -1156,7 -1156,7 +1156,7 @@@ static PHP_METHOD(pqconn, execParams) 
        int query_len;
        zval *zparams;
        zval *ztypes = NULL;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes);
@@@ -1203,7 -1203,7 +1203,7 @@@ static PHP_METHOD(pqconn, execParamsAsy
        int query_len;
        zval *zparams;
        zval *ztypes = NULL;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!f", &query_str, &query_len, &zparams, &ztypes, &resolver.fci, &resolver.fcc);
        zend_restore_error_handling(&zeh TSRMLS_CC);
  }
  
 -STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
 +ZEND_RESULT_CODE php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
  {
        PGresult *res;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        if (!obj) {
                obj = zend_object_store_get_object(object TSRMLS_CC);
@@@ -1271,7 -1271,7 +1271,7 @@@ static PHP_METHOD(pqconn, prepare) 
        zval *ztypes = NULL;
        char *name_str, *query_str;
        int name_len, *query_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes);
        }
  }
  
 -STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
 +ZEND_RESULT_CODE php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
  {
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        if (!obj) {
                obj = zend_object_store_get_object(object TSRMLS_CC);
@@@ -1327,7 -1327,7 +1327,7 @@@ static PHP_METHOD(pqconn, prepareAsync
        zval *ztypes = NULL;
        char *name_str, *query_str;
        int name_len, *query_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes);
        }
  }
  
 -STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
 +ZEND_RESULT_CODE php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
  {
        PGresult *res;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        if (!obj) {
                obj = zend_object_store_get_object(object TSRMLS_CC);
@@@ -1386,7 -1386,7 +1386,7 @@@ static PHP_METHOD(pqconn, declare) 
        char *name_str, *query_str;
        int name_len, query_len;
        long flags;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
-                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len);
+                       int query_offset;
+                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len, &query_offset);
  
                        if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) {
                                efree(decl);
                        } else {
-                               php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
-                               php_pq_object_addref(obj TSRMLS_CC);
-                               cur->conn = obj;
-                               cur->open = 1;
-                               cur->name = estrdup(name_str);
-                               cur->decl = decl;
+                               php_pqcur_t *cur = php_pqcur_init(obj, name_str, decl, query_offset, flags TSRMLS_CC);
  
                                return_value->type = IS_OBJECT;
                                return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
        }
  }
  
 -STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
 +ZEND_RESULT_CODE php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
  {
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        if (!obj) {
                obj = zend_object_store_get_object(object TSRMLS_CC);
@@@ -1448,7 -1443,7 +1443,7 @@@ static PHP_METHOD(pqconn, declareAsync
        char *name_str, *query_str;
        int name_len, query_len;
        long flags;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
-                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len);
+                       int query_offset;
+                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len, &query_offset);
  
                        if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) {
                                efree(decl);
                        } else {
-                               php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
-                               php_pq_object_addref(obj TSRMLS_CC);
-                               cur->conn = obj;
-                               cur->open = 1;
-                               cur->name = estrdup(name_str);
-                               cur->decl = decl;
+                               php_pqcur_t *cur = php_pqcur_init(obj, name_str, decl, query_offset, flags TSRMLS_CC);
  
                                return_value->type = IS_OBJECT;
                                return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
@@@ -1586,9 -1576,9 +1576,9 @@@ static PHP_METHOD(pqconn, unescapeBytea
        }
  }
  
 -STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
 +ZEND_RESULT_CODE php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
  {
 -      STATUS rv = FAILURE;
 +      ZEND_RESULT_CODE rv = FAILURE;
  
        if (!conn_obj) {
                conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
        return rv;
  }
  
 -STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
 +ZEND_RESULT_CODE php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
  {
 -      STATUS rv = FAILURE;
 +      ZEND_RESULT_CODE rv = FAILURE;
  
        if (!conn_obj) {
                conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
@@@ -1674,7 -1664,7 +1664,7 @@@ static PHP_METHOD(pqconn, startTransact
        long isolation = obj->intern ? obj->intern->default_txn_isolation : PHP_PQTXN_READ_COMMITTED;
        zend_bool readonly = obj->intern ? obj->intern->default_txn_readonly : 0;
        zend_bool deferrable = obj->intern ? obj->intern->default_txn_deferrable : 0;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable);
@@@ -1710,7 -1700,7 +1700,7 @@@ static PHP_METHOD(pqconn, startTransact
        long isolation = obj->intern ? obj->intern->default_txn_isolation : PHP_PQTXN_READ_COMMITTED;
        zend_bool readonly = obj->intern ? obj->intern->default_txn_readonly : 0;
        zend_bool deferrable = obj->intern ? obj->intern->default_txn_deferrable : 0;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable);
@@@ -1775,7 -1765,7 +1765,7 @@@ static PHP_METHOD(pqconn, off) 
        zend_error_handling zeh;
        char *type_str;
        int type_len;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &type_str, &type_len);
@@@ -1801,7 -1791,7 +1791,7 @@@ static PHP_METHOD(pqconn, on) 
        char *type_str;
        int type_len;
        php_pq_callback_t cb = {{0}};
 -      STATUS rv;
 +      ZEND_RESULT_CODE 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);
@@@ -1849,7 -1839,7 +1839,7 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_set_co
        ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0)
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, setConverter) {
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
        zend_error_handling zeh;
        zval *zcnv;
  
@@@ -1887,7 -1877,7 +1877,7 @@@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unset_
        ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0)
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqconn, unsetConverter) {
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
        zend_error_handling zeh;
        zval *zcnv;
  
diff --combined src/php_pqcur.c
index fc5be1d69694ebfdd3245155e67343672fa45be6,7680880550daec5450f586f37530411adb8bfcfe..a84041ea68739784dd3eda3e11747fd797476efc
@@@ -29,7 -29,7 +29,7 @@@ zend_class_entry *php_pqcur_class_entry
  static zend_object_handlers php_pqcur_object_handlers;
  static HashTable php_pqcur_object_prophandlers;
  
- static void cur_close(php_pqcur_object_t *obj TSRMLS_DC)
+ static void cur_close(php_pqcur_object_t *obj, zend_bool async, zend_bool silent TSRMLS_DC)
  {
        if (obj->intern->open && obj->intern->conn->intern) {
                PGresult *res;
                smart_str_appends(&cmd, obj->intern->name);
                smart_str_0(&cmd);
  
-               if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) {
-                       PHP_PQclear(res);
+               if (async) {
+                       if (PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) {
+                               obj->intern->conn->intern->poller = PQconsumeInput;
+                               php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
+                       } else if (!silent) {
+                               throw_exce(EX_IO TSRMLS_CC, "Failed to close cursor (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                       }
+               } else {
+                       if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) {
+                               PHP_PQclear(res);
+                       } else if (!silent) {
+                               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to close cursor (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+                       }
                }
-               smart_str_free(&cmd);
  
+               smart_str_free(&cmd);
                obj->intern->open = 0;
        }
  }
  
+ static void cur_open(INTERNAL_FUNCTION_PARAMETERS, zend_bool async)
+ {
+       zend_error_handling zeh;
+       STATUS rv;
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters_none();
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+       if (rv == FAILURE) {
+               return;
+       }
+       php_pqcur_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+       if (!obj->intern) {
+               throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized");
+               return;
+       } else if (obj->intern->open) {
+               return;
+       }
+       if (async) {
+               rv = php_pqconn_declare_async(NULL, obj->intern->conn, obj->intern->decl TSRMLS_CC);
+       } else {
+               rv = php_pqconn_declare(NULL, obj->intern->conn, obj->intern->decl TSRMLS_CC);
+       }
+       if (rv == SUCCESS) {
+               obj->intern->open = 1;
+       }
+ }
  static void cur_fetch_or_move(INTERNAL_FUNCTION_PARAMETERS, const char *action, zend_bool async)
  {
        char *spec_str = "1";
        int spec_len = 1;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
        php_pq_callback_t resolver = {{0}};
        zend_error_handling zeh;
  
@@@ -110,7 -154,7 +154,7 @@@ static void php_pqcur_object_free(void 
        fprintf(stderr, "FREE cur(#%d) %p (conn: %p)\n", obj->zv.handle, obj, obj->intern->conn);
  #endif
        if (obj->intern) {
-               cur_close(obj TSRMLS_CC);
+               cur_close(obj, 0, 1 TSRMLS_CC);
                php_pq_object_delref(obj->intern->conn TSRMLS_CC);
                efree(obj->intern->decl);
                efree(obj->intern->name);
@@@ -163,9 -207,23 +207,23 @@@ static void php_pqcur_object_read_conne
        php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
  }
  
char *php_pqcur_declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len)
static void php_pqcur_object_read_query(zval *object, void *o, zval *return_value TSRMLS_DC)
  {
-       size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL  CURSOR WITHOUT HOLD FOR ");
+       php_pqcur_object_t *obj = o;
+       RETVAL_STRING(obj->intern->decl + obj->intern->query_offset, 1);
+ }
+ static void php_pqcur_object_read_flags(zval *object, void *o, zval *return_value TSRMLS_DC)
+ {
+       php_pqcur_object_t *obj = o;
+       RETVAL_LONG(obj->intern->flags);
+ }
+ char *php_pqcur_declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len, int *query_offset)
+ {
+       size_t decl_len = name_len + query_len + sizeof("DECLARE  BINARY INSENSITIVE NO SCROLL CURSOR WITH HOLD FOR ");
        char *decl_str;
  
        decl_str = emalloc(decl_len);
                        (flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "",
                        query_str
        );
+       if (query_offset) {
+               /* sizeof() includes the terminating null byte, so no need for spaces in the string literals */
+               *query_offset = sizeof("DECLARE")
+                       + (name_len + 1)
+                       + ((flags & PHP_PQ_DECLARE_BINARY) ? sizeof("BINARY") : 1)
+                       + ((flags & PHP_PQ_DECLARE_INSENSITIVE) ? sizeof("INSENSITIVE") : 1)
+                       + ((flags & PHP_PQ_DECLARE_NO_SCROLL) ? sizeof("NO SCROLL") :
+                                       (flags & PHP_PQ_DECLARE_SCROLL) ? sizeof("SCROLL") : 1)
+                       + sizeof("CURSOR")
+                       + ((flags & PHP_PQ_DECLARE_WITH_HOLD) ? sizeof("WITH HOLD") : 1)
+                       + sizeof("FOR");
+       }
        return decl_str;
  }
  
+ php_pqcur_t *php_pqcur_init(php_pqconn_object_t *conn, const char *name, char *decl, int query_offset, long flags TSRMLS_DC)
+ {
+       php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
+       php_pq_object_addref(conn TSRMLS_CC);
+       cur->conn = conn;
+       cur->name = estrdup(name);
+       cur->decl = decl;
+       cur->query_offset = query_offset;
+       cur->flags = flags;
+       cur->open = 1;
+       return cur;
+ }
  ZEND_BEGIN_ARG_INFO_EX(ai_pqcur___construct, 0, 0, 4)
        ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
        ZEND_ARG_INFO(0, name)
@@@ -194,7 -281,7 +281,7 @@@ static PHP_METHOD(pqcur, __construct) 
        int name_len, query_len;
        long flags;
        zval *zconn;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
        zend_bool async = 0;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
                } if (!conn_obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
-                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len);
+                       int query_offset;
+                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len, &query_offset);
  
                        if (async) {
                                rv = php_pqconn_declare_async(zconn, conn_obj, decl TSRMLS_CC);
                        if (SUCCESS != rv) {
                                efree(decl);
                        } else {
-                               php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
-                               php_pq_object_addref(conn_obj TSRMLS_CC);
-                               cur->conn = conn_obj;
-                               cur->open = 1;
-                               cur->name = estrdup(name_str);
-                               cur->decl = decl;
-                               obj->intern = cur;
+                               obj->intern = php_pqcur_init(conn_obj, name_str, decl, query_offset, flags TSRMLS_CC);
                        }
                }
        }
  ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_open, 0, 0, 0)
  ZEND_END_ARG_INFO();
  static PHP_METHOD(pqcur, open)
+ {
+       cur_open(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+ }
+ ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_openAsync, 0, 0, 0)
+ ZEND_END_ARG_INFO();
+ static PHP_METHOD(pqcur, openAsync)
+ {
+       cur_open(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+ }
+ ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_close, 0, 0, 0)
+ ZEND_END_ARG_INFO();
+ static PHP_METHOD(pqcur, close)
  {
        zend_error_handling zeh;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters_none();
  
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized");
-               } else if (!obj->intern->open) {
-                       if (SUCCESS == php_pqconn_declare(NULL, obj->intern->conn, obj->intern->decl TSRMLS_CC)) {
-                               obj->intern->open = 1;
-                       }
+               } else {
+                       cur_close(obj, 0, 0 TSRMLS_CC);
                }
        }
  }
  
- ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_close, 0, 0, 0)
+ ZEND_BEGIN_ARG_INFO_EX(ai_pqcur_closeAsync, 0, 0, 0)
  ZEND_END_ARG_INFO();
- static PHP_METHOD(pqcur, close)
+ static PHP_METHOD(pqcur, closeAsync)
  {
        zend_error_handling zeh;
 -      STATUS rv;
 +      ZEND_RESULT_CODE rv;
  
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
        rv = zend_parse_parameters_none();
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cursor not initialized");
                } else {
-                       cur_close(obj TSRMLS_CC);
+                       cur_close(obj, 1, 0 TSRMLS_CC);
                }
        }
  }
@@@ -317,7 -410,9 +410,9 @@@ static PHP_METHOD(pqcur, moveAsync
  static zend_function_entry php_pqcur_methods[] = {
        PHP_ME(pqcur, __construct, ai_pqcur___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
        PHP_ME(pqcur, open, ai_pqcur_open, ZEND_ACC_PUBLIC)
+       PHP_ME(pqcur, openAsync, ai_pqcur_open, ZEND_ACC_PUBLIC)
        PHP_ME(pqcur, close, ai_pqcur_close, ZEND_ACC_PUBLIC)
+       PHP_ME(pqcur, closeAsync, ai_pqcur_closeAsync, ZEND_ACC_PUBLIC)
        PHP_ME(pqcur, fetch, ai_pqcur_fetch, ZEND_ACC_PUBLIC)
        PHP_ME(pqcur, move, ai_pqcur_move, ZEND_ACC_PUBLIC)
        PHP_ME(pqcur, fetchAsync, ai_pqcur_fetchAsync, ZEND_ACC_PUBLIC)
@@@ -365,6 -460,14 +460,14 @@@ PHP_MINIT_FUNCTION(pqcur
        ph.read = php_pqcur_object_read_connection;
        zend_hash_add(&php_pqcur_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
  
+       zend_declare_property_null(php_pqcur_class_entry, ZEND_STRL("query"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       ph.read = php_pqcur_object_read_query;
+       zend_hash_add(&php_pqcur_object_prophandlers, "query", sizeof("query"), (void *) &ph, sizeof(ph), NULL);
+       zend_declare_property_null(php_pqcur_class_entry, ZEND_STRL("flags"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       ph.read = php_pqcur_object_read_flags;
+       zend_hash_add(&php_pqcur_object_prophandlers, "flags", sizeof("flags"), (void *) &ph, sizeof(ph), NULL);
        return SUCCESS;
  }