prepareAsync++, Statement::execAsync++
authorMichael Wallner <mike@php.net>
Tue, 22 Jan 2013 17:11:32 +0000 (18:11 +0100)
committerMichael Wallner <mike@php.net>
Tue, 22 Jan 2013 17:11:32 +0000 (18:11 +0100)
src/php_pq.c
tests/async003.phpt
tests/async004.phpt
tests/async005.phpt [new file with mode: 0644]

index d1041e68a13515e2288632fb96b2700c5c18c3d9..80c654728ad5978737afe4f3305c053ddb573c51 100644 (file)
@@ -1188,7 +1188,7 @@ static PHP_METHOD(pqconn, exec) {
        zend_restore_error_handling(&zeh TSRMLS_CC);
 }
 
-ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_getResult, 0, 0, 0)
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_get_result, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(pqconn, getResult) {
        if (SUCCESS == zend_parse_parameters_none()) {
@@ -1292,28 +1292,37 @@ static int apply_to_param(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_k
        return ZEND_HASH_APPLY_KEEP;
 }
 
-static void php_pq_types_to_array(HashTable *ht, Oid **types TSRMLS_DC)
+static int php_pq_types_to_array(HashTable *ht, Oid **types TSRMLS_DC)
 {
+       int count = zend_hash_num_elements(ht);
+       
        *types = NULL;
 
-       if (zend_hash_num_elements(ht)) {
+       if (count) {
                Oid *tmp;
 
-               *types = tmp = ecalloc(zend_hash_num_elements(ht) + 1, sizeof(Oid));
+               /* +1 for when less types than params are specified */
+               *types = tmp = ecalloc(count + 1, sizeof(Oid));
                zend_hash_apply_with_argument(ht, apply_to_oid, &tmp TSRMLS_CC);
        }
+       
+       return count;
 }
 
-static void php_pq_params_to_array(HashTable *ht, char ***params, int *count, HashTable *zdtor TSRMLS_DC)
+static int php_pq_params_to_array(HashTable *ht, char ***params, HashTable *zdtor TSRMLS_DC)
 {
+       int count = zend_hash_num_elements(ht);
+       
        *params = NULL;
 
-       if ((*count = zend_hash_num_elements(ht))) {
+       if (count) {
                char **tmp;
 
-               *params = tmp = ecalloc(*count, sizeof(char *));
+               *params = tmp = ecalloc(count, sizeof(char *));
                zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_param, 2, &tmp, zdtor);
        }
+       
+       return count;
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params, 0, 0, 2)
@@ -1334,13 +1343,13 @@ static PHP_METHOD(pqconn, execParams) {
 
                if (obj->conn) {
                        PGresult *res;
-                       int count = 0;
+                       int count;
                        Oid *types = NULL;
                        char **params = NULL;
                        HashTable zdtor;
 
                        ZEND_INIT_SYMTABLE(&zdtor);
-                       php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &count, &zdtor TSRMLS_CC);
+                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
 
                        if (ztypes) {
                                php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC);
@@ -1394,13 +1403,13 @@ static PHP_METHOD(pqconn, execParamsAsync) {
                php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
 
                if (obj->conn) {
-                       int count = 0;
+                       int count;
                        Oid *types = NULL;
                        char **params = NULL;
                        HashTable zdtor;
 
                        ZEND_INIT_SYMTABLE(&zdtor);
-                       php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &count, &zdtor TSRMLS_CC);
+                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
 
                        if (ztypes) {
                                php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC);
@@ -1446,13 +1455,11 @@ static STATUS php_pqconn_prepare(PGconn *conn, const char *name, const char *que
        PGresult *res;
        STATUS rv;
 
-       if (typest && (count = zend_hash_num_elements(typest))) {
-               Oid *tmp;
-
-               tmp = types = ecalloc(count, sizeof(Oid));
-               zend_hash_apply_with_argument(typest, apply_to_oid, &tmp TSRMLS_CC);
+       if (typest) {
+               count = zend_hash_num_elements(typest);
+               php_pq_types_to_array(typest, &types TSRMLS_CC);
        }
-
+       
        res = PQprepare(conn, name, query, count, types);
 
        if (types) {
@@ -1466,7 +1473,7 @@ static STATUS php_pqconn_prepare(PGconn *conn, const char *name, const char *que
                rv = FAILURE;
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not prepare statement: %s", PQerrorMessage(conn));
        }
-
+       
        return rv;
 }
 
@@ -1495,6 +1502,60 @@ static PHP_METHOD(pqconn, prepare) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
                }
        }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
+static STATUS php_pqconn_prepare_async(PGconn *conn, const char *name, const char *query, HashTable *typest TSRMLS_DC)
+{
+       STATUS rv;
+       int count;
+       Oid *types = NULL;
+       
+       if (typest) {
+               count = php_pq_types_to_array(typest, &types TSRMLS_CC);
+       }
+       
+       if (PQsendPrepare(conn, name, query, count, types)) {
+               rv = SUCCESS;
+       } else {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not prepare statement: %s", PQerrorMessage(conn));
+               rv = FAILURE;
+       }
+       
+       if (types) {
+               efree(types);
+       }
+       
+       return rv;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2)
+ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, query)
+       ZEND_ARG_ARRAY_INFO(0, types, 1)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqconn, prepareAsync) {
+       zend_error_handling zeh;
+       zval *ztypes = NULL;
+       char *name_str, *query_str;
+       int name_len, *query_len;
+
+       zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes)) {
+               php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->conn) {
+                       obj->poller = PQconsumeInput;
+                       if (SUCCESS == php_pqconn_prepare_async(obj->conn, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
+                               return_value->type = IS_OBJECT;
+                               return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, getThis(), name_str, NULL TSRMLS_CC);
+                       }
+                       php_pqconn_notify_listeners(getThis(), obj TSRMLS_CC);
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
 }
 
 static zend_function_entry php_pqconn_methods[] = {
@@ -1506,9 +1567,10 @@ static zend_function_entry php_pqconn_methods[] = {
        PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)
-       PHP_ME(pqconn, getResult, ai_pqconn_getResult, ZEND_ACC_PUBLIC)
+       PHP_ME(pqconn, getResult, ai_pqconn_get_result, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, execAsync, ai_pqconn_exec_async, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC)
+       PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC)
        {0}
 };
 
@@ -1614,29 +1676,41 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_construct, 0, 0, 3)
        ZEND_ARG_INFO(0, name)
        ZEND_ARG_INFO(0, query)
        ZEND_ARG_ARRAY_INFO(0, types, 1)
+       ZEND_ARG_INFO(0, async)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(pqstm, __construct) {
        zend_error_handling zeh;
        zval *zconn, *ztypes = NULL;
        char *name_str, *query_str;
        int name_len, *query_len;
+       zend_bool async = 0;
 
        zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
-       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oss|a/!", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes)) {
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oss|a/!b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes, &async)) {
                php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
                php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
 
                if (conn_obj->conn) {
-                       if (SUCCESS == php_pqconn_prepare(conn_obj->conn, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
-                               Z_ADDREF_P(zconn);
-                               obj->conn = zconn;
-                               obj->name = estrdup(name_str);
+                       if (async) {
+                               conn_obj->poller = PQconsumeInput;
+                               if (SUCCESS == php_pqconn_prepare_async(conn_obj->conn, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
+                                       Z_ADDREF_P(zconn);
+                                       obj->conn = zconn;
+                                       obj->name = estrdup(name_str);
+                               }
+                       } else {
+                               if (SUCCESS == php_pqconn_prepare(conn_obj->conn, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
+                                       Z_ADDREF_P(zconn);
+                                       obj->conn = zconn;
+                                       obj->name = estrdup(name_str);
+                               }
+                               php_pqconn_notify_listeners(obj->conn, conn_obj TSRMLS_CC);
                        }
-                       php_pqconn_notify_listeners(obj->conn, conn_obj TSRMLS_CC);
                } else {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
                }
        }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec, 0, 0, 0)
@@ -1659,13 +1733,9 @@ static PHP_METHOD(pqstm, exec) {
                                HashTable zdtor;
                                PGresult *res;
 
-                               ZEND_INIT_SYMTABLE(&zdtor);
-
-                               if (zparams && (count = zend_hash_num_elements(Z_ARRVAL_P(zparams)))) {
-                                       char **tmp;
-
-                                       tmp = params = ecalloc(count, sizeof(char *));
-                                       zend_hash_apply_with_arguments(Z_ARRVAL_P(zparams) TSRMLS_CC, apply_to_param, 2, &tmp, &zdtor);
+                               if (zparams) {
+                                       ZEND_INIT_SYMTABLE(&zdtor);
+                                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
                                }
 
                                res = PQexecPrepared(conn_obj->conn, obj->name, count, (const char *const*) params, NULL, NULL, 0);
@@ -1673,7 +1743,9 @@ static PHP_METHOD(pqstm, exec) {
                                if (params) {
                                        efree(params);
                                }
-                               zend_hash_destroy(&zdtor);
+                               if (zparams) {
+                                       zend_hash_destroy(&zdtor);
+                               }
 
                                php_pqconn_notify_listeners(obj->conn, conn_obj TSRMLS_CC);
 
@@ -1695,6 +1767,68 @@ static PHP_METHOD(pqstm, exec) {
        zend_restore_error_handling(&zeh TSRMLS_CC);
 }
 
+ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec_async, 0, 0, 0)
+       ZEND_ARG_ARRAY_INFO(0, params, 1)
+       ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqstm, execAsync) {
+       zend_error_handling zeh;
+       zval *zparams = NULL;
+       php_pq_callback_t resolver;
+
+       zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!f", &zparams, &resolver.fci, &resolver.fcc)) {
+               php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (obj->conn && obj->name) {
+                       php_pqconn_object_t *conn_obj = zend_object_store_get_object(obj->conn TSRMLS_CC);
+
+                       if (conn_obj->conn) {
+                               int count;
+                               char **params = NULL;
+                               HashTable zdtor;
+
+                               if (zparams) {
+                                       ZEND_INIT_SYMTABLE(&zdtor);
+                                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
+                               }
+
+                               php_pq_callback_dtor(&conn_obj->onevent);
+                               if (resolver.fci.size > 0) {
+                                       conn_obj->onevent = resolver;
+                                       php_pq_callback_addref(&conn_obj->onevent);
+                               }
+
+                               conn_obj->poller = PQconsumeInput;
+
+                               if (PQsendQueryPrepared(conn_obj->conn, obj->name, count, (const char *const*) params, NULL, NULL, 0)) {
+                                       RETVAL_TRUE;
+                               } else {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute statement: %s", PQerrorMessage(conn_obj->conn));
+                                       RETVAL_FALSE;
+                               }
+
+                               if (params) {
+                                       efree(params);
+                               }
+                               if (zparams) {
+                                       zend_hash_destroy(&zdtor);
+                               }
+
+                               php_pqconn_notify_listeners(obj->conn, conn_obj TSRMLS_CC);
+
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not initialized");
+                               RETVAL_FALSE;
+                       }
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Statement not initialized");
+                       RETVAL_FALSE;
+               }
+       }
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+}
+
 ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(pqstm, desc) {
@@ -1738,6 +1872,7 @@ static zend_function_entry php_pqstm_methods[] = {
        PHP_ME(pqstm, __construct, ai_pqstm_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
        PHP_ME(pqstm, exec, ai_pqstm_exec, ZEND_ACC_PUBLIC)
        PHP_ME(pqstm, desc, ai_pqstm_desc, ZEND_ACC_PUBLIC)
+       PHP_ME(pqstm, execAsync, ai_pqstm_exec_async, ZEND_ACC_PUBLIC)
        {0}
 };
 
index bb242fe41849fad0c8e6217d1c8c159231655649..a1af7c95db762bdbc7e236977b79199febadd7fc 100644 (file)
@@ -1,5 +1,5 @@
 --TEST--
-async query
+async exec
 --SKIPIF--
 <?php include "_skipif.inc"; ?>
 --FILE--
index 65512866094e23709ece975b1bc664124f13588c..aaaf91691e50aa9e4997a6de9a96221fd53ec4ae 100644 (file)
@@ -1,5 +1,5 @@
 --TEST--
-async query
+async exec params
 --SKIPIF--
 <?php include "_skipif.inc"; ?>
 --FILE--
diff --git a/tests/async005.phpt b/tests/async005.phpt
new file mode 100644 (file)
index 0000000..0382ade
--- /dev/null
@@ -0,0 +1,52 @@
+--TEST--
+async prepared statement
+--SKIPIF--
+<?php include "_skipif.inc"; ?>
+--FILE--
+<?php
+echo "Test\n";
+
+include "_setup.inc";
+
+function complete($s) {
+       do {
+               while ($s->connection->busy) {
+                       $r = array($s->connection->socket);
+                       $w = $e = null;
+                       if (stream_select($r, $w, $e, null)) {
+                               $s->connection->poll();
+                       }
+               }
+       } while ($s->connection->getResult());
+}
+
+$c = new pq\Connection(PQ_DSN);
+$s = $c->prepareAsync("test", "SELECT \$1,\$2::int4", array($c->types->byName->int4->oid));
+
+complete($s);
+
+$s->execAsync(array(1,2), function ($res) {
+       var_dump($res);
+});
+
+complete($s);
+
+?>
+DONE
+--EXPECTF--
+Test
+object(pq\Result)#%d (6) {
+  ["status"]=>
+  int(2)
+  ["errorMessage"]=>
+  string(0) ""
+  ["numRows"]=>
+  int(1)
+  ["numCols"]=>
+  int(2)
+  ["affectedRows"]=>
+  int(1)
+  ["fetchType"]=>
+  int(0)
+}
+DONE
\ No newline at end of file