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]