Since I didn't get much comment/request, so I changed it the way I
like. (I'll use zend_parse_parameter() later, since existing
functions are not using it yet)

 - get rid of some functions and made easier to use async query
 - garbage collection at request shutdown
 - pg_send_query() changes mode to nonblocking internally
 - pg_is_busy() calls PQconsumeInput() internally

It's much easier to use now.

Aync Query Functions:
 - pg_send_query() - send query
 - pg_get_result() - get async query result
 - pg_is_busy() - executing async query or not
 - pg_request_cancel() - candel currently executing query

Misc Functions:
 - pg_reset() - reconnect to server. Useful when backend is died.
 - pg_status() - current connection status.

README, patch and test script is attached. (Make sure you use CGI
bin, set output_buffering=Off and edit database connection
parameter. There are some limitations in libpq. You may not be
able to execute query asyncronously)

Comments are welcome.

--
Yasuo Ohgaki
Index: pgsql.c
===================================================================
RCS file: /repository/php4/ext/pgsql/pgsql.c,v
retrieving revision 1.130
diff -u -r1.130 pgsql.c
--- pgsql.c     11 Oct 2001 23:33:40 -0000      1.130
+++ pgsql.c     25 Nov 2001 06:28:36 -0000
@@ -94,6 +94,12 @@
        PHP_FALIAS(pg_clientencoding,           pg_client_encoding,                    
 NULL)
        PHP_FALIAS(pg_setclientencoding,        pg_set_client_encoding, NULL)
 #endif
+       PHP_FE(pg_reset,                                 NULL)
+       PHP_FE(pg_status,                                NULL)
+       PHP_FE(pg_send_query,                    NULL)
+       PHP_FE(pg_request_cancel,                NULL)
+       PHP_FE(pg_get_result,                    NULL)
+       PHP_FE(pg_is_busy,                               NULL)
        {NULL, NULL, NULL}
 };
 /* }}} */
@@ -147,7 +153,17 @@
 static void _close_pgsql_link(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 {
        PGconn *link = (PGconn *)rsrc->ptr;
+       PGresult *res;
 
+       PQsetnonblocking(link,1);
+       if (PQisBusy(link)) {
+               if (!PQrequestCancel(link)) {
+                       php_error(E_WARNING,"PostgreSQL: failed to cancel qeury. %s", 
+PQerrorMessage(link));
+               }
+       }
+       while ((res = PQgetResult(link))) {
+               PQclear(res);
+       }
        PQfinish(link);
        PGG(num_links)--;
 }
@@ -158,7 +174,17 @@
 static void _close_pgsql_plink(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 {
        PGconn *link = (PGconn *)rsrc->ptr;
+       PGresult *res;
 
+       PQsetnonblocking(link,1);
+       if (PQisBusy(link)) {
+               if (!PQrequestCancel(link)) {
+                       php_error(E_WARNING,"PostgreSQL: failed to cancel qeury. %s", 
+PQerrorMessage(link));
+               }
+       }
+       while ((res = PQgetResult(link))) {
+               PQclear(res);
+       }
        PQfinish(link);
        PGG(num_persistent)--;
        PGG(num_links)--;
@@ -187,12 +213,22 @@
 static int _rollback_transactions(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 {
        PGconn *link;
+       PGresult *res;
 
        if (Z_TYPE_P(rsrc) != le_plink) 
                return 0;
 
        link = (PGconn *) rsrc->ptr;
        
+       PQsetnonblocking(link,1);
+       if (PQisBusy(link)) {
+               if (!PQrequestCancel(link)) {
+                       php_error(E_WARNING,"PostgreSQL: failed to cancel qeury. %s", 
+PQerrorMessage(link));
+               }
+       }
+       while ((res = PQgetResult(link))) {
+               PQclear(res);
+       }
        PGG(ignore_notices) = 1;
        PQexec(link,"BEGIN;ROLLBACK;");
        PGG(ignore_notices) = 0;
@@ -226,7 +262,7 @@
 PHP_INI_BEGIN()
        STD_PHP_INI_BOOLEAN("pgsql.allow_persistent",   "1",    PHP_INI_SYSTEM,        
 OnUpdateInt,            allow_persistent,       php_pgsql_globals,              
pgsql_globals)
        STD_PHP_INI_ENTRY_EX("pgsql.max_persistent",    "-1",   PHP_INI_SYSTEM,        
 OnUpdateInt,            max_persistent,         php_pgsql_globals,              
pgsql_globals,  display_link_numbers)
-       STD_PHP_INI_ENTRY_EX("pgsql.max_links",         "-1",   PHP_INI_SYSTEM,        
         OnUpdateInt,            max_links,                      php_pgsql_globals,    
          pgsql_globals,  display_link_numbers)
+       STD_PHP_INI_ENTRY_EX("pgsql.max_links",         "-1",   PHP_INI_SYSTEM,        
+ OnUpdateInt,            max_links,                      php_pgsql_globals,           
+   pgsql_globals,  display_link_numbers)
 PHP_INI_END()
 /* }}} */
 
@@ -262,6 +298,9 @@
        REGISTER_LONG_CONSTANT("PGSQL_NUM", PGSQL_NUM, CONST_CS | CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PGSQL_BOTH", PGSQL_BOTH, CONST_CS | CONST_PERSISTENT);
 
+       REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_BAD", CONNECTION_BAD, CONST_CS | 
+CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("PGSQL_CONNECTION_OK", CONNECTION_OK, CONST_CS | 
+CONST_PERSISTENT);
+
        return SUCCESS;
 }
 /* }}} */
@@ -721,7 +760,7 @@
        PGresult *pgsql_result;
        ExecStatusType status;
        pgsql_result_handle *pg_result;
-
+       
        switch(ZEND_NUM_ARGS()) {
                case 1:
                        if (zend_get_parameters_ex(1, &query)==FAILURE) {
@@ -743,6 +782,10 @@
        ZEND_FETCH_RESOURCE2(pgsql, PGconn *, pgsql_link, id, "PostgreSQL link", 
le_link, le_plink);
 
        convert_to_string_ex(query);
+       if (PQisBusy(pgsql)) {
+               php_error(E_NOTICE,"PostgreSQL: Cannot execute query while executing 
+async query.");
+               RETURN_FALSE;
+       }
        pgsql_result = PQexec(pgsql, Z_STRVAL_PP(query));
        
        if (pgsql_result) {
@@ -751,7 +794,6 @@
                status = (ExecStatusType) PQstatus(pgsql);
        }
        
-       
        switch (status) {
                case PGRES_EMPTY_QUERY:
                case PGRES_BAD_RESPONSE:
@@ -979,7 +1021,7 @@
        }
        return ret;
 }
-/* }}} */                      
+/* }}} */
 
 #define PHP_PG_FIELD_NAME 1
 #define PHP_PG_FIELD_SIZE 2
@@ -1962,7 +2004,232 @@
 }
 /* }}} */
 #endif
+
+
+/* {{{ proto long pg_status(resource conn)
+   Get connection status */
+PHP_FUNCTION(pg_status)
+{
+       zval **pgsql_link = NULL;
+       int id = -1;
+       PGconn *pgsql;
+
+       switch(ZEND_NUM_ARGS()) {
+               case 0:
+                       id = PGG(default_link);
+                       CHECK_DEFAULT_LINK(id);
+                       break;
+               case 1:
+                       if (zend_get_parameters_ex(1, &pgsql_link)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+
+       ZEND_FETCH_RESOURCE2(pgsql, PGconn *, pgsql_link, id, "PostgreSQL link", 
+le_link, le_plink);
+       Z_LVAL_P(return_value) = PQstatus(pgsql);
+       Z_TYPE_P(return_value) = IS_LONG;
+}
+
+/* }}} */
+
+/* {{{ proto void pg_reset(resource conn)
+   Reset connection */
+PHP_FUNCTION(pg_reset)
+{
+       zval **pgsql_link = NULL;
+       int id = -1;
+       PGconn *pgsql;
+
+       switch(ZEND_NUM_ARGS()) {
+               case 0:
+                       id = PGG(default_link);
+                       CHECK_DEFAULT_LINK(id);
+                       break;
+               case 1:
+                       if (zend_get_parameters_ex(1, &pgsql_link)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+       ZEND_FETCH_RESOURCE2(pgsql, PGconn *, pgsql_link, id, "PostgreSQL link", 
+le_link, le_plink);
+       PQreset(pgsql); /* status may be checked with pg_status() */
+}
+
+/* }}} */
+
+/* Following functions are for asyncronous query
+ * Report bugs to [EMAIL PROTECTED]
+ */ 
+#define PHP_PG_ASYNC_IS_BUSY           1
+#define PHP_PG_ASYNC_REQUEST_CANCEL 2
+
+/* {{{ php_pgsql_do_async
+ */
+void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) 
+{
+       zval **pgsql_link = NULL;
+       int id = -1;
+       PGconn *pgsql;
+
+       switch(ZEND_NUM_ARGS()) {
+               case 0:
+                       id = PGG(default_link);
+                       CHECK_DEFAULT_LINK(id);
+                       break;
+               case 1:
+                       if (zend_get_parameters_ex(1, &pgsql_link)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+
+       ZEND_FETCH_RESOURCE2(pgsql, PGconn *, pgsql_link, id, "PostgreSQL link", 
+le_link, le_plink);
+
+       switch(entry_type) {
+               case PHP_PG_ASYNC_IS_BUSY:
+                       PQconsumeInput(pgsql);
+                       Z_LVAL_P(return_value) = PQisBusy(pgsql);
+                       Z_TYPE_P(return_value) = IS_LONG;
+                       break;
+               case PHP_PG_ASYNC_REQUEST_CANCEL:
+                       Z_LVAL_P(return_value) = PQrequestCancel(pgsql);
+                       Z_TYPE_P(return_value) = IS_LONG;
+                       break;
+               default:
+                       php_error(E_ERROR,"Pgsql module error. Report this error");
+                       break;
+       }
+       convert_to_boolean_ex(&return_value);
+}
+/* }}} */
+
+/* {{{ proto bool pg_async_request_cancel([resource connection])
+   Cancel request */
+PHP_FUNCTION(pg_request_cancel)
+{
+       php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, 
+PHP_PG_ASYNC_REQUEST_CANCEL);
+}
+/* }}} */
+
+/* {{{ proto query bool pg_isbusy([resource connection])
+   Get connection is busy or not */
+PHP_FUNCTION(pg_is_busy)
+{
+       php_pgsql_do_async(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_PG_ASYNC_IS_BUSY);
+}
+/* }}} */
+
+/* {{{ proto bool pg_async_exec([resource connection], string qeury)
+   Send asynchronous query */
+PHP_FUNCTION(pg_send_query)
+{
+       zval **query, **pgsql_link = NULL;
+       int id = -1;
+       PGconn *pgsql;
+       PGresult *res;
+       int leftover = 0;
+
+       switch(ZEND_NUM_ARGS()) {
+               case 1:
+                       if (zend_get_parameters_ex(1, &query)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       id = PGG(default_link);
+                       CHECK_DEFAULT_LINK(id);
+                       break;
+               case 2:
+                       if (zend_get_parameters_ex(2, &pgsql_link, &query)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
        
+       ZEND_FETCH_RESOURCE2(pgsql, PGconn *, pgsql_link, id, "PostgreSQL link", 
+le_link, le_plink);
+
+       convert_to_string_ex(query);
+       if (PQsetnonblocking(pgsql, 1)) {
+               php_error(E_NOTICE,"PostgreSQL: Cannot set connection to nonblocking 
+mode in pg_send_query()");
+       }
+       if (PQisBusy(pgsql)) {
+               php_error(E_WARNING,"PostgreSQL: Cannot send multiple query using 
+pg_send_query()");
+               RETURN_FALSE;
+       }
+       while ((res = PQgetResult(pgsql))) {
+               PQclear(res);
+               leftover = 1;
+       }
+       if (leftover) {
+               php_error(E_NOTICE,"PostgreSQL: There are results on this 
+connection.");
+       }
+       if (!PQsendQuery(pgsql, Z_STRVAL_PP(query))) {
+               RETURN_FALSE;
+       }
+       if (PQsetnonblocking(pgsql, 0)) {
+               php_error(E_NOTICE,"PostgreSQL: Cannot set connection to blocking mode 
+in pg_send_query()");
+       }
+       RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto query resouce pg_arync_result([resource connection])
+   Get asynchronous query result */
+PHP_FUNCTION(pg_get_result)
+{
+       zval **pgsql_link = NULL;
+       int id = -1;
+       PGconn *pgsql;
+       PGresult *pgsql_result;
+       pgsql_result_handle *pg_result;
+
+       switch(ZEND_NUM_ARGS()) {
+               case 0:
+                       id = PGG(default_link);
+                       CHECK_DEFAULT_LINK(id);
+                       break;
+               case 1:
+                       if (zend_get_parameters_ex(1, &pgsql_link)==FAILURE) {
+                               RETURN_FALSE;
+                       }
+                       break;
+               default:
+                       WRONG_PARAM_COUNT;
+                       break;
+       }
+
+       ZEND_FETCH_RESOURCE2(pgsql, PGconn *, pgsql_link, id, "PostgreSQL link", 
+le_link, le_plink);
+       if (PQsetnonblocking(pgsql, 1)) {
+               php_error(E_NOTICE,"PostgreSQL: Cannot set connection to nonblocking 
+mode in pg_get_result()");
+       }
+       pgsql_result = PQgetResult(pgsql);
+       if (!pgsql_result) {
+               /* no result */
+               RETURN_FALSE;
+       }
+       if (PQsetnonblocking(pgsql, 0)) {
+               php_error(E_NOTICE,"PostgreSQL: Cannot set connection to blocking mode 
+in pg_get_result()");
+       }
+       pg_result = (pgsql_result_handle *) emalloc(sizeof(pgsql_result_handle));
+       pg_result->conn = pgsql;
+       pg_result->result = pgsql_result;
+       pg_result->row = -1;
+       ZEND_REGISTER_RESOURCE(return_value, pg_result, le_result);
+}
+/* }}} */
+
 #endif
 
 /*
Index: php_pgsql.h
===================================================================
RCS file: /repository/php4/ext/pgsql/php_pgsql.h,v
retrieving revision 1.33
diff -u -r1.33 php_pgsql.h
--- php_pgsql.h 26 Sep 2001 21:44:48 -0000      1.33
+++ php_pgsql.h 25 Nov 2001 06:28:37 -0000
@@ -94,6 +94,12 @@
 PHP_FUNCTION(pg_client_encoding);
 PHP_FUNCTION(pg_set_client_encoding);
 #endif
+PHP_FUNCTION(pg_reset);
+PHP_FUNCTION(pg_status);
+PHP_FUNCTION(pg_send_query);
+PHP_FUNCTION(pg_request_cancel);
+PHP_FUNCTION(pg_get_result);
+PHP_FUNCTION(pg_is_busy);
 
 void php_pgsql_do_connect(INTERNAL_FUNCTION_PARAMETERS,int persistent);
 int php_pgsql_get_default_link(INTERNAL_FUNCTION_PARAMETERS);
@@ -102,7 +108,7 @@
 char *get_field_name(PGconn *pgsql, Oid oid, HashTable *list);
 void php_pgsql_get_field_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type);
 void php_pgsql_data_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type);
-
+void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS,int entry_type);
 
 typedef struct pgLofp {
         PGconn *conn;
<?php
/**
 * PostgreSQL async query function test script.
 *
 * There are number of restrictions to make async query work.
 * Refer to libpq manual for details.
 *
 * pg_send_query does not Multiple query, however user
 * may retrive result separately.
 *
 * This script is for php binary. Disable output buffering
 * to see the difference.
 *
 * [EMAIL PROTECTED]
 */

/***** CONFIG ******/
$table = 'pgsql_async_func_test';  // Test table name to create
$numrec = 50000;                   // Number of records to create
$connstr = 'host=dev dbname=yohgaki user=yohgaki'; // Database to connect


/******** test funcitons ********/
function create_test_table($db) 
{
        global $table;  
        $sql="
SELECT
        a.attname,
        a.attnum,
        t.typname,
        a.attlen,
        a.atttypmod,
        a.attnotNULL,
        a.atthasdef
FROM
        pg_class as c,
        pg_attribute a,
        pg_type t
WHERE
        a.attnum > 0 AND
        a.attrelid = c.oid AND
        c.relname = '$table' AND
        a.atttypid = t.oid
ORDER BY
        a.attnum;

";
        $res = pg_exec($db,$sql);
        if (!pg_numrows($res)) {
                echo "Creating test table..\n";
                $sql = "CREATE TABLE $table (str text);";
                if (pg_exec($db,$sql) !== FALSE)
                {
                        $val = 'Async query can improve application performance 
significantly. Please test and report any failure to [EMAIL PROTECTED] There are 
cases that async query does not work as expected. Refer to libpq manual for details. I 
also implemented functions that I need pg_status() - returns connection stauts and 
pg_reset() - reset connection. (Useful when connection is broken for some reason - 
most likely backend is died)';
                        for ($i=0; $i < 50000; $i++) {
                                pg_exec($db,"INSERT INTO $table (str) VALUES 
('$val');");
                                if (!($i % 100)) {
                                        echo '.';
                                }
                        }
                        echo "\n";
                        $val = '     XYZ         '; // To make select actually return 
a row. 'XYZ' is search pattern.
                        pg_exec($db,"INSERT INTO $table (str) VALUES ('$val');");
                }
                else {
                        die("ERR: Failed creating test 
table.".pg_errormessage()."\n\n\n\n\n\n");
                }
        }
        else {
                echo "Test table exists\n";
        }
}

function check_status($db) 
{
        $status = pg_status($db);

        switch ($status) {
                case PGSQL_CONNECTION_OK:
                        echo "Status: Connection is ready\n";
                        break;
                case PGSQL_CONNECTION_BAD:
                        echo "Status: Connectin is BAD\n";
                        break;
                default:
                        echo "Status: Unkonwn ($status)\n";
                        break;
        }
        return $status;
}

function execute_query($db,$async = TRUE) 
{
        global $table;
        echo "Executing query..";
        $ret = null;
        $sql = "
SELECT * FROM $table WHERE str like '%XYZ%';
";
        if ($async) {
                return pg_send_query($db,$sql);
        }
        if (!($ret = pg_exec($db,$sql))) {
                echo "Query failed..\n";
        }
        return $ret;
}

function check_result($result) 
{
        if (!$result){
                echo "Check result: NG\n";
                return false;
        }
        echo "Check result: Ok\n";
        return true;
}


function dump_result($result) 
{
        if (check_result($result)) {
                $cnt = pg_numrows($result);
                for($i=0; $i < $cnt; $i++) {
                        $rec = pg_fetch_array($result,$i,PGSQL_ASSOC);
                        print_r($rec);
                }
        }
}

/********  MAIN **********/
$db = pg_connect($connstr);
create_test_table($db);

////////////// nonblocked query /////////////////////
echo "\n *** non-blocking qeury *** \n\n";

check_status($db);
execute_query($db);
while (pg_is_busy($db)) 
{
        echo ".";
        sleep(1);
}
echo "\n";

$result = pg_get_result($db);
dump_result($result);

/////////////// blocked query /////////////////////////
echo "\n *** blocking qeury *** \n\n";

check_status($db);
$result = execute_query($db, false);
echo "\n";

dump_result($result);

?>












-- 
PHP Development Mailing List <http://www.php.net/>
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
To contact the list administrators, e-mail: [EMAIL PROTECTED]

Reply via email to