Asynchronous queries for mysqli, when mysqlnd is enabled.
Includes 4 tests for mysqli_poll
#if defined(MYSQLI_USE_MYSQLND) && defined(MYSQLND_THREADED)
REGISTER_LONG_CONSTANT("MYSQLI_BG_STORE_RESULT", MYSQLI_BG_STORE_RESULT, CONST_CS | CONST_PERSISTENT);
#endif
+#if defined (MYSQLI_USE_MYSQLND)
+ REGISTER_LONG_CONSTANT("MYSQLI_ASYNC", MYSQLI_ASYNC, CONST_CS | CONST_PERSISTENT);
+#endif
/* for mysqli_fetch_assoc */
REGISTER_LONG_CONSTANT("MYSQLI_ASSOC", MYSQLI_ASSOC, CONST_CS | CONST_PERSISTENT);
PHP_FE(mysqli_num_rows, NULL)
PHP_FE(mysqli_options, NULL)
PHP_FE(mysqli_ping, NULL)
+#if defined(MYSQLI_USE_MYSQLND)
+ PHP_FE(mysqli_poll, NULL)
+#endif
PHP_FE(mysqli_prepare, NULL)
PHP_FE(mysqli_report, NULL)
PHP_FE(mysqli_query, NULL)
PHP_FE(mysqli_real_connect, NULL)
PHP_FE(mysqli_real_escape_string, NULL)
PHP_FE(mysqli_real_query, NULL)
+#if defined(MYSQLI_USE_MYSQLND)
+ PHP_FE(mysqli_reap_async_query, NULL)
+#endif
PHP_FE(mysqli_rollback, NULL)
PHP_FE(mysqli_select_db, NULL)
#ifdef HAVE_MYSQLI_SET_CHARSET
PHP_FALIAS(query,mysqli_query,NULL)
PHP_FALIAS(real_connect,mysqli_real_connect,NULL)
PHP_FALIAS(real_escape_string,mysqli_real_escape_string,NULL)
+#if defined(MYSQLI_USE_MYSQLND)
+ PHP_FALIAS(poll,mysqli_poll,NULL)
+ PHP_FALIAS(reap_async_query,mysqli_reap_async_query,NULL)
+#endif
PHP_FALIAS(escape_string, mysqli_real_escape_string,NULL)
PHP_FALIAS(real_query,mysqli_real_query,NULL)
PHP_FALIAS(rollback,mysqli_rollback,NULL)
PHP_FALIAS(fetch,mysqli_stmt_fetch,NULL)
PHP_FALIAS(get_warnings, mysqli_stmt_get_warnings, NULL)
PHP_FALIAS(result_metadata, mysqli_stmt_result_metadata,NULL)
- PHP_FALIAS(num_rows, mysqli_stmt_num_rows,NULL)
#if defined(MYSQLI_USE_MYSQLND)
- PHP_FALIAS(more_results, mysqli_stmt_more_results, NULL)
- PHP_FALIAS(next_result, mysqli_stmt_next_result, NULL)
+ PHP_FALIAS(more_results, mysqli_stmt_more_results,NULL)
+ PHP_FALIAS(next_result, mysqli_stmt_next_result,NULL)
#endif
+ PHP_FALIAS(num_rows, mysqli_stmt_num_rows,NULL)
PHP_FALIAS(send_long_data,mysqli_stmt_send_long_data,NULL)
PHP_FALIAS(stmt,mysqli_prepare,NULL)
PHP_FALIAS(free_result,mysqli_stmt_free_result,NULL)
#define mysqli_stmt_close(c, implicit) mysqlnd_stmt_close((c), (implicit))
#define mysqli_free_result(r, implicit) mysqlnd_free_result((r), (implicit))
#define mysqli_bg_store_result(r) mysqlnd_bg_store_result((r))
+#define mysqli_async_query(c, q, l) mysqlnd_async_query((c), (q), (l))
#endif
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query");
RETURN_FALSE;
}
- if (resultmode != MYSQLI_USE_RESULT && resultmode != MYSQLI_STORE_RESULT
+ if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT
#if defined(MYSQLI_USE_MYSQLND) && defined(MYSQLND_THREADED)
- && resultmode != MYSQLI_BG_STORE_RESULT
+ && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_BG_STORE_RESULT
#endif
) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
MYSQLI_DISABLE_MQ;
+#ifdef MYSQLI_USE_MYSQLND
+ if (resultmode & MYSQLI_ASYNC) {
+ if (mysqli_async_query(mysql->mysql, query, query_len)) {
+ MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
+ RETURN_FALSE;
+ }
+ mysql->async_result_fetch_type = resultmode & ~MYSQLI_ASYNC;
+ RETURN_TRUE;
+ }
+#endif
+
if (mysql_real_query(mysql->mysql, query, query_len)) {
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
RETURN_FALSE;
#if defined(MYSQLI_USE_MYSQLND)
+#include "php_network.h"
+/* {{{ mysqlnd_zval_array_to_mysqlnd_array functions */
+static int mysqlnd_zval_array_to_mysqlnd_array(zval *in_array, MYSQLND ***out_array TSRMLS_DC)
+{
+ zval **elem;
+ int i = 0, current = 0;
+
+ if (Z_TYPE_P(in_array) != IS_ARRAY) {
+ return 0;
+ }
+ *out_array = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(in_array)) + 1, sizeof(MYSQLND *));
+ for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(in_array));
+ zend_hash_get_current_data(Z_ARRVAL_P(in_array), (void **) &elem) == SUCCESS;
+ zend_hash_move_forward(Z_ARRVAL_P(in_array))) {
+ i++;
+ if (Z_TYPE_PP(elem) != IS_OBJECT ||
+ !instanceof_function(Z_OBJCE_PP(elem), mysqli_link_class_entry TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter %d not a mysqli object", i);
+ } else {
+ MY_MYSQL *mysql;
+ MYSQLI_RESOURCE *my_res;
+ mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*elem TSRMLS_CC);
+ if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] Couldn't fetch %s", i, intern->zo.ce->name);
+ continue;
+ }
+ mysql = (MY_MYSQL*) my_res->ptr;
+ if (MYSQLI_STATUS_VALID && my_res->status < MYSQLI_STATUS_VALID) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid object %d or resource %s", i, intern->zo.ce->name);
+ continue;
+ }
+ (*out_array)[current++] = mysql->mysql;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_zval_array_from_mysqlnd_array */
+static int mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *out_array TSRMLS_DC)
+{
+ MYSQLND **p = in_array;
+ HashTable *new_hash;
+ zval **elem, **dest_elem;
+ int ret = 0;
+
+ ALLOC_HASHTABLE(new_hash);
+ zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(out_array)), NULL, ZVAL_PTR_DTOR, 0);
+
+ for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(out_array));
+ zend_hash_get_current_data(Z_ARRVAL_P(out_array), (void **) &elem) == SUCCESS;
+ zend_hash_move_forward(Z_ARRVAL_P(out_array)))
+ {
+ if (Z_TYPE_PP(elem) != IS_OBJECT || !instanceof_function(Z_OBJCE_PP(elem), mysqli_link_class_entry TSRMLS_CC)) {
+ continue;
+ }
+ {
+ MY_MYSQL *mysql;
+ mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*elem TSRMLS_CC);
+ mysql = (MY_MYSQL *) ((MYSQLI_RESOURCE *)intern->ptr)->ptr;
+ if (mysql->mysql == *p) {
+ zend_hash_next_index_insert(new_hash, (void *)elem, sizeof(zval *), (void **)&dest_elem);
+ if (dest_elem) {
+ zval_add_ref(dest_elem);
+ }
+ ret++;
+ p++;
+ }
+ }
+ }
+
+ /* destroy old array and add new one */
+ zend_hash_destroy(Z_ARRVAL_P(out_array));
+ efree(Z_ARRVAL_P(out_array));
+
+ zend_hash_internal_pointer_reset(new_hash);
+ Z_ARRVAL_P(out_array) = new_hash;
+
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ mysqlnd_dont_poll_zval_array_from_mysqlnd_array */
+static int mysqlnd_dont_poll_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *in_zval_array, zval *out_array TSRMLS_DC)
+{
+ MYSQLND **p = in_array;
+ HashTable *new_hash;
+ zval **elem, **dest_elem;
+ int ret = 0;
+
+ ALLOC_HASHTABLE(new_hash);
+ zend_hash_init(new_hash, zend_hash_num_elements(Z_ARRVAL_P(in_zval_array)), NULL, ZVAL_PTR_DTOR, 0);
+ if (in_array) {
+ for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(in_zval_array));
+ zend_hash_get_current_data(Z_ARRVAL_P(in_zval_array), (void **) &elem) == SUCCESS;
+ zend_hash_move_forward(Z_ARRVAL_P(in_zval_array)))
+ {
+ MY_MYSQL *mysql;
+ mysqli_object *intern = (mysqli_object *)zend_object_store_get_object(*elem TSRMLS_CC);
+ mysql = (MY_MYSQL *) ((MYSQLI_RESOURCE *)intern->ptr)->ptr;
+ if (mysql->mysql == *p) {
+ zend_hash_next_index_insert(new_hash, (void *)elem, sizeof(zval *), (void **)&dest_elem);
+ if (dest_elem) {
+ zval_add_ref(dest_elem);
+ }
+ ret++;
+ p++;
+ }
+ }
+ }
+
+ /* destroy old array and add new one */
+ zend_hash_destroy(Z_ARRVAL_P(out_array));
+ efree(Z_ARRVAL_P(out_array));
+
+ zend_hash_internal_pointer_reset(new_hash);
+ Z_ARRVAL_P(out_array) = new_hash;
+
+ return 0;
+}
+/* }}} */
+
+
+/* {{{ proto int mysqli_poll(array read, array write, array error, long sec [, long usec]) U
+ Poll connections */
+PHP_FUNCTION(mysqli_poll)
+{
+ zval *r_array, *e_array, *dont_poll_array;
+ MYSQLND **new_r_array = NULL, **new_e_array = NULL, **new_dont_poll_array = NULL;
+ long sec = 0, usec = 0;
+ enum_func_status ret;
+ uint desc_num;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!a!al|l", &r_array, &e_array, &dont_poll_array, &sec, &usec) == FAILURE) {
+ return;
+ }
+ if (sec < 0 || usec < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
+ RETURN_FALSE;
+ }
+
+ if (r_array != NULL) {
+ mysqlnd_zval_array_to_mysqlnd_array(r_array, &new_r_array TSRMLS_CC);
+ }
+ if (e_array != NULL) {
+ mysqlnd_zval_array_to_mysqlnd_array(e_array, &new_e_array TSRMLS_CC);
+ }
+
+ ret = mysqlnd_poll(new_r_array, new_e_array, &new_dont_poll_array, sec, usec, &desc_num);
+
+ mysqlnd_dont_poll_zval_array_from_mysqlnd_array(r_array != NULL ? new_dont_poll_array:NULL, r_array, dont_poll_array TSRMLS_CC);
+
+ if (r_array != NULL) {
+ mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array TSRMLS_CC);
+ }
+ if (e_array != NULL) {
+ mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array TSRMLS_CC);
+ }
+
+ if (new_dont_poll_array) {
+ efree(new_dont_poll_array);
+ }
+ if (new_r_array) {
+ efree(new_r_array);
+ }
+ if (new_e_array) {
+ efree(new_e_array);
+ }
+ if (ret == PASS) {
+ RETURN_LONG(desc_num);
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+
+/* {{{ proto int mysqli_reap_async_query(object link) U
+ Poll connections */
+PHP_FUNCTION(mysqli_reap_async_query)
+{
+ MY_MYSQL *mysql;
+ zval *mysql_link;
+ MYSQLI_RESOURCE *mysqli_resource;
+ MYSQL_RES *result;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
+ return;
+ }
+
+ MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
+
+ if (FAIL == mysqlnd_reap_async_query(mysql->mysql)) {
+ RETURN_FALSE;
+ }
+
+ if (!mysql_field_count(mysql->mysql)) {
+ /* no result set - not a SELECT */
+ if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
+/* php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql) TSRMLS_CC); */
+ }
+ RETURN_TRUE;
+ }
+
+ switch (mysql->async_result_fetch_type) {
+ case MYSQLI_STORE_RESULT:
+ result = mysql_store_result(mysql->mysql);
+ break;
+ case MYSQLI_USE_RESULT:
+ result = mysql_use_result(mysql->mysql);
+ break;
+#if defined(MYSQLI_USE_MYSQLND) && defined(MYSQLND_THREADED)
+ case MYSQLI_BG_STORE_RESULT:
+ result = mysqli_bg_store_result(mysql->mysql);
+ break;
+#endif
+ }
+
+ if (!result) {
+ php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql) TSRMLS_CC,
+ "%s", mysql_error(mysql->mysql));
+ RETURN_FALSE;
+ }
+
+ if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
+/* php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql) TSRMLS_CC); */
+ }
+
+ mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
+ mysqli_resource->ptr = (void *)result;
+ mysqli_resource->status = MYSQLI_STATUS_VALID;
+ MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
+}
+/* }}} */
+
+
/* {{{ proto object mysqli_stmt_get_result(object link) U
Buffer result set on client */
PHP_FUNCTION(mysqli_stmt_get_result)
ZVAL_LONG(*retval, rc);
} else {
char *ret;
- int l = spprintf(&ret, 0, MYSQLI_LLU_SPEC, (my_ulonglong) rc);
+ int l = spprintf(&ret, 0, MYSQLI_LLU_SPEC, rc);
ZVAL_STRINGL(*retval, ret, l, 0);
}
}
ZVAL_LONG(*retval, rc);
} else {
char *ret;
- int l = spprintf(&ret, 0, MYSQLI_LLU_SPEC, (my_ulonglong) rc);
+ int l = spprintf(&ret, 0, MYSQLI_LLU_SPEC, rc);
ZVAL_STRINGL(*retval, ret, l, 0);
}
}
char *hash_key;
zval *li_read;
php_stream *li_stream;
- zend_bool persistent;
unsigned int multi_query;
+ zend_bool persistent;
+#if defined(MYSQLI_USE_MYSQLND)
+ int async_result_fetch_type;
+#endif
} MY_MYSQL;
typedef struct {
#define MYSQLI_STORE_RESULT 0
#define MYSQLI_USE_RESULT 1
#ifdef MYSQLI_USE_MYSQLND
-#define MYSQLI_BG_STORE_RESULT 101
+#ifdef MYSQLND_THREADED
+#define MYSQLI_BG_STORE_RESULT 4
+#endif
+#define MYSQLI_ASYNC 8
+#else
+/* libmysql */
+#define MYSQLI_ASYNC 0
#endif
/* for mysqli_fetch_assoc */
PHP_FUNCTION(mysqli_num_rows);
PHP_FUNCTION(mysqli_options);
PHP_FUNCTION(mysqli_ping);
+PHP_FUNCTION(mysqli_poll);
PHP_FUNCTION(mysqli_prepare);
PHP_FUNCTION(mysqli_query);
PHP_FUNCTION(mysqli_stmt_result_metadata);
PHP_FUNCTION(mysqli_real_connect);
PHP_FUNCTION(mysqli_real_query);
PHP_FUNCTION(mysqli_real_escape_string);
+PHP_FUNCTION(mysqli_reap_async_query);
PHP_FUNCTION(mysqli_rollback);
PHP_FUNCTION(mysqli_row_seek);
PHP_FUNCTION(mysqli_select_db);
--TEST--
mysqli_get_metadata
--SKIPIF--
-<?php
-require_once('skipif.inc');
+<?php
+require_once('skipif.inc');
require_once('skipifconnectfailure.inc');
?>
--FILE--
$mysql = new mysqli($host, $user, $passwd, $db, $port, $socket);
$cs = array();
- $cs[] = $mysql->set_charset("latin5");
+ $cs[] = $mysql->set_charset("latin1");
$cs[] = $mysql->character_set_name();
$cs[] = $mysql->set_charset("utf8");
[0]=>
bool(true)
[1]=>
- string(6) "latin5"
+ string(6) "latin1"
[2]=>
bool(true)
[3]=>
[5]=>
string(4) "utf8"
}
-done!
\ No newline at end of file
+done!
/* $expected_methods['get_cache_stats'] = true; */
/* $expected_methods['get_client_stats'] = true; */
$expected_methods['get_connection_stats'] = true;
+ $expected_methods['poll'] = true;
+ $expected_methods['reap_async_query'] = true;
} else {
// libmysql only
if (function_exists('mysqli_ssl_set'))
$expected_constants['MYSQLI_OPT_NET_CMD_BUFFER_SIZE'] = true;
$expected_constants['MYSQLI_OPT_NET_READ_BUFFER_SIZE'] = true;
$expected_constants['MYSQLI_DEBUG_TRACE_ENABLED'] = true;
+ $expected_constants['MYSQLI_ASYNC'] = true;
} else {
$version = mysqli_get_client_version();
--- /dev/null
+--TEST--
+int mysqli_poll() simple
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('connect.inc');
+require_once('skipifconnectfailure.inc');
+
+if (!$IS_MYSQLND)
+ die("skip mysqlnd only feature, compile PHP using --with-mysqli=mysqlnd");
+?>
+--FILE--
+<?php
+ require_once('connect.inc');
+
+ function get_connection() {
+ global $host, $user, $passwd, $db, $port, $socket;
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket))
+ printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ return $link;
+ }
+
+ if (!$link = get_connection())
+ printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+
+ if (NULL !== ($tmp = @mysqli_poll()))
+ printf("[002] Expecting NULL got %s\n", var_export($tmp, true));
+
+ if (NULL !== ($tmp = @mysqli_poll(array($link))))
+ printf("[003] Expecting NULL got %s\n", var_export($tmp, true));
+
+ if (NULL !== ($tmp = @mysqli_poll(array($link), NULL)))
+ printf("[004] Expecting NULL got %s\n", var_export($tmp, true));
+
+ if (NULL !== ($tmp = @mysqli_poll(array($link), NULL, NULL)))
+ printf("[005] Expecting NULL got %s\n", var_export($tmp, true));
+
+ $int_val = 43;
+ $myerrors = &$int_val;
+
+ if (NULL !== ($tmp = @mysqli_poll(array($link), $myerrors, NULL, 1)))
+ printf("[006] Expecting NULL got %s\n", var_export($tmp, true));
+
+ if (NULL !== ($tmp = @mysqli_poll(array($link), NULL, NULL, -1)))
+ printf("[007] Expecting boolean/false got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ if (NULL !== ($tmp = @mysqli_poll(array($link), NULL, NULL, 0, -1)))
+ printf("[008] Expecting boolean/false got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ if (0 !== ($tmp = (mysqli_poll(array($link), array($link), array($link), 0, 1))))
+ printf("[009] Expecting int/0 got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+
+ function poll_async($offset, $links, $errors, $reject, $exp_ready) {
+
+ if ($exp_ready !== ($tmp = mysqli_poll($links, $errors, $reject, 0, 1000)))
+ printf("[%03d + 1] There should be %d links ready to read from, %d ready\n",
+ $exp_ready, $tmp);
+
+ foreach ($links as $mysqli) {
+ if (is_object($res = mysqli_reap_async_query($mysqli))) {
+ printf("[%03d + 2] Can fetch resultset although no query has been run!\n", $offset);
+ } else if (mysqli_errno($mysqli) > 0) {
+ printf("[%03d + 3] Error indicated through links array: %d/%s",
+ $offset, mysqli_errno($mysqli), mysqli_error($mysqli));
+ } else {
+ printf("[%03d + 4] Cannot fetch and no error set - non resultset query (no SELECT)!\n", $offset);
+ }
+ }
+
+ foreach ($errors as $mysqli)
+ printf("[%03d + 5] Error on %d: %d/%s\n",
+ $offset, mysqli_thread_id($mysqli), mysqli_errno($mysqli), mysqli_error($mysqli));
+
+ foreach ($reject as $mysqli)
+ printf("[%03d + 6] Rejecting thread %d: %d/%s\n",
+ $offset, mysqli_thread_id($mysqli), mysqli_errno($mysqli), mysqli_error($mysqli));
+
+ }
+
+ // Connections on which no query has been send - 1
+ $link = get_connection();
+ $links = array($link);
+ $errors = array($link);
+ $reject = array($link);
+ poll_async(10, $links, $errors, $reject, 0);
+ mysqli_close($link);
+
+ // Connections on which no query has been send - 2
+ // Difference: pass $links twice
+ $link = get_connection();
+ $links = array($link, $link);
+ $errors = array($link, $link);
+ $reject = array();
+ poll_async(11, $links, $errors, $reject, 0);
+
+ // Connections on which no query has been send - 3
+ // Difference: pass two connections
+ $link = get_connection();
+ $links = array($link, get_connection());
+ $errors = array($link, $link);
+ $reject = array();
+ poll_async(12, $links, $errors, $reject, 0);
+
+ // Reference mess...
+ $link = get_connection();
+ $links = array($link);
+ $errors = array($link);
+ $ref_errors =& $errors;
+ $reject = array();
+ poll_async(13, $links, $ref_errors, $reject, 0);
+
+ print "done!";
+?>
+--EXPECTF--
+[010 + 6] Rejecting thread %d: 0/
+[011 + 6] Rejecting thread %d: 0/
+[011 + 6] Rejecting thread %d: 0/
+[012 + 6] Rejecting thread %d: 0/
+[012 + 6] Rejecting thread %d: 0/
+[013 + 6] Rejecting thread %d: 0/
+done!
--- /dev/null
+--TEST--
+int mysqli_poll() and kill
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('connect.inc');
+require_once('skipifconnectfailure.inc');
+
+if (!$IS_MYSQLND)
+ die("skip mysqlnd only feature, compile PHP using --with-mysqli=mysqlnd");
+?>
+--FILE--
+<?php
+ require_once('connect.inc');
+
+ function get_connection() {
+ global $host, $user, $passwd, $db, $port, $socket;
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket))
+ printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ return $link;
+ }
+
+ // Killing connection - 1
+
+ $link = get_connection();
+ if (true !== ($tmp = mysqli_query($link, "SELECT 1 AS 'processed beofre killed'", MYSQLI_ASYNC | MYSQLI_USE_RESULT)))
+ printf("[002] Expecting boolean/true got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ // Sleep 0.1s - the asynchronous query should have been processed after the wait period
+ usleep(100000);
+ $thread_id = mysqli_thread_id($link);
+ mysqli_kill(get_connection(), $thread_id);
+
+ $links = array($link);
+ $errors = array($link);
+ $reject = array($link);
+
+ // Yes, 1 - the asynchronous query should have been processed
+ if (1 !== ($tmp = (mysqli_poll($links, $errors, $reject, 0, 10000))))
+ printf("[003] Expecting int/1 got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ if (!is_array($links) || empty($links))
+ printf("[004] Expecting non-empty array got %s/%s\n", gettype($links), var_export($links, true));
+ else
+ foreach ($links as $link) {
+ if (is_object($res = mysqli_reap_async_query($link))) {
+ // Yes, you can fetch a result - the query has been processed
+ var_dump(mysqli_fetch_assoc($res));
+ mysqli_free_result($res);
+ } else if ($link->errno > 0) {
+ printf("[005] Error: %d\n", $link->errno);
+ }
+ }
+
+ // No error!
+ if (!is_array($errors) || !empty($errors))
+ printf("[006] Expecting non-empty array got %s/%s\n", gettype($errors), var_export($errors, true));
+
+ if (!is_array($reject) || !empty($reject))
+ printf("[007] Expecting empty array got %s/%s\n", gettype($reject), var_export($reject, true));
+
+ // Lets pass a dead connection
+ $links = array($link);
+ $errors = array($link);
+ $reject = array($link);
+ if (0 !== ($tmp = mysqli_poll($links, $errors, $reject, 1)))
+ printf("[008] There should be no connection ready! Returned %s/%s, expecting int/0.\n",
+ gettype($tmp), var_export($tmp, true));
+
+ if (!empty($errors))
+ printf("[009] There should be no errors but one rejected connection\n");
+
+ foreach ($reject as $mysqli)
+ if (mysqli_thread_id($mysqli) != $thread_id) {
+ printf("[010] Rejected thread %d should have rejected thread %d\n",
+ mysqli_thread_id($mysqli), $thread_id);
+ }
+
+ // Killing connection - 2
+
+ $link = get_connection();
+ if (true !== ($tmp = mysqli_query($link, "SELECT 1", MYSQLI_ASYNC | MYSQLI_USE_RESULT)))
+ printf("[011] Expecting boolean/true got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ usleep(100000);
+ $thread_id = mysqli_thread_id($link);
+ mysqli_kill(get_connection(), $thread_id);
+
+ // Yes, 1 - fetch OK packet of kill!
+ $processed = 0;
+ do {
+ $links = array($link, $link);
+ $errors = array($link, $link);
+ $reject = array($link, $link);
+ $ready = mysqli_poll($links, $errors, $reject, 1);
+
+ if (!empty($errors)) {
+ foreach ($errors as $mysqli) {
+ printf("[012] Error on thread %d: %s/%s\n",
+ mysqli_thread_id($mysqli),
+ mysqli_errno($mysqli),
+ mysqli_error($mysqli));
+ }
+ break;
+ }
+
+ if (!empty($reject)) {
+ foreach ($reject as $mysqli) {
+ printf("[013] Rejecting thread %d: %s/%s\n",
+ mysqli_thread_id($mysqli),
+ mysqli_errno($mysqli),
+ mysqli_error($mysqli));
+ }
+ $processed += count($reject);
+ }
+
+ foreach ($links as $mysqli) {
+ if (is_object($res = mysqli_reap_async_query($mysqli))) {
+ printf("Fetching from thread %d...\n", mysqli_thread_id($mysqli));
+ var_dump(mysqli_fetch_assoc($res));
+ } else if (mysqli_errno($mysqli) > 0) {
+ printf("[014] %d/%s\n", mysqli_errno($mysqli), mysqli_error($mysqli));
+ }
+ $processed++;
+ }
+
+ } while ($processed < 2);
+
+
+ // Killing connection - 3
+
+ $link = get_connection();
+ $thread_id = mysqli_thread_id($link);
+ mysqli_kill(get_connection(), $thread_id);
+ // Sleep 0.1s to ensure the KILL gets recognized
+ usleep(100000);
+ if (false !== ($tmp = mysqli_query($link, "SELECT 1 AS 'processed beofre killed'", MYSQLI_ASYNC | MYSQLI_USE_RESULT)))
+ printf("[015] Expecting boolean/false got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ $links = array($link);
+ $errors = array($link);
+ $reject = array($link);
+
+ // Yes, that is weird, right? Its the OK package we have to fetch
+ if (1 !== ($tmp = (mysqli_poll($links, $errors, $reject, 0, 10000))))
+ printf("[016] Expecting int/0 got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ if (!is_array($links) || empty($links))
+ printf("[017] Expecting non-empty array got %s/%s\n", gettype($links), var_export($links, true));
+ else
+ foreach ($links as $link) {
+ if (is_object($res = mysqli_reap_async_query($link))) {
+ // No, you cannot fetch the result
+ var_dump(mysqli_fetch_assoc($res));
+ mysqli_free_result($res);
+ } else if ($link->errno > 0) {
+ // But you are supposed to handle the error the way its shown here!
+ printf("[018] Error: %d/%s\n", $link->errno, $link->error);
+ }
+ }
+
+ // None of these will indicate an error, check errno on the list of returned connections!
+ if (!is_array($errors) || !empty($errors))
+ printf("[019] Expecting non-empty array got %s/%s\n", gettype($errors), var_export($errors, true));
+
+ if (!is_array($reject) || !empty($reject))
+ printf("[020] Expecting empty array got %s/%s\n", gettype($reject), var_export($reject, true));
+
+
+ mysqli_close($link);
+ print "done!";
+?>
+--EXPECTF--
+array(1) {
+ ["processed beofre killed"]=>
+ string(1) "1"
+}
+Fetching from thread %d...
+array(1) {
+ [1]=>
+ string(1) "1"
+}
+
+Warning: mysqli_reap_async_query(): GREET %s
+
+Warning: mysqli_reap_async_query(): MySQL server has gone away in %s on line %d
+[014] 2014/%s
+
+Warning: Error while sending QUERY packet. PID=%d in %s on line %d
+
+Warning: mysqli_reap_async_query(): MySQL server has gone away in %s on line %d
+
+Warning: mysqli_reap_async_query(): Error reading result set's header in %s on line %d
+[018] Error: 2006/%s
+done!
--- /dev/null
+--TEST--
+mysqli_poll() & INSERT SELECT
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('connect.inc');
+require_once('skipifconnectfailure.inc');
+
+if (!$IS_MYSQLND)
+ die("skip mysqlnd only feature, compile PHP using --with-mysqli=mysqlnd");
+?>
+--FILE--
+<?php
+ require_once('table.inc');
+
+ function get_connection() {
+ global $host, $user, $passwd, $db, $port, $socket;
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket))
+ printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ return $link;
+ }
+
+
+ // Note: some queries will fail! They are supposed to fail.
+ $queries = array(
+ 'CREATE TABLE IF NOT EXISTS bogus(id INT)',
+ 'SET @a = 1',
+ 'SELECT * FROM test ORDER BY id ASC LIMIT 2',
+ 'INSERT INTO test(id, label) VALUES (100, "z")',
+ 'SELECT * FROM test ORDER BY id ASC LIMIT 2',
+ 'SELECT',
+ 'UPDATE test SET id = 101 WHERE id > 3',
+ 'UPDATE_FIX test SET id = 101 WHERE id > 3',
+ 'DROP TABLE IF EXISTS bogus',
+ 'DELETE FROM test WHERE id = @a',
+ 'DELETE FROM test WHERE id = 1',
+ );
+
+ $link = get_connection();
+ $have_proc = false;
+ mysqli_real_query($link, "DROP PROCEDURE IF EXISTS p");
+ if (mysqli_real_query($link, 'CREATE PROCEDURE p(IN ver_in VARCHAR(25), OUT ver_out VARCHAR(25)) BEGIN SELECT ver_in INTO ver_out; END;')) {
+ $have_proc = true;
+ $queries[] = 'CALL p("myversion", @version)';
+ }
+ mysqli_close($link);
+
+ $links = array();
+ for ($i = 0; $i < count($queries); $i++) {
+
+ $link = get_connection();
+
+ if (true !== ($tmp = mysqli_query($link, $queries[$i], MYSQLI_ASYNC | MYSQLI_USE_RESULT)))
+ printf("[002] Expecting true got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ // WARNING KLUDGE NOTE
+ // Add a tiny delay to ensure that queries get executed in a certain order
+ // If your MySQL server is very slow the test may randomly fail!
+ usleep(20000);
+
+ $links[mysqli_thread_id($link)] = array(
+ 'query' => $queries[$i],
+ 'link' => $link,
+ 'processed' => false,
+ );
+ }
+
+ $saved_errors = array();
+ do {
+ $poll_links = $poll_errors = $poll_reject = array();
+ foreach ($links as $thread_id => $link) {
+ if (!$link['processed']) {
+ $poll_links[] = $link['link'];
+ $poll_errors[] = $link['link'];
+ $poll_reject[] = $link['link'];
+ }
+ }
+ if (0 == count($poll_links))
+ break;
+
+ if (0 == ($num_ready = mysqli_poll($poll_links, $poll_errors, $poll_reject, 0, 200000)))
+ continue;
+
+ if (!empty($poll_errors)) {
+ die(var_dump($poll_errors));
+ }
+
+ foreach ($poll_links as $link) {
+ $thread_id = mysqli_thread_id($link);
+ $links[$thread_id]['processed'] = true;
+
+ if (is_object($res = mysqli_reap_async_query($link))) {
+ // result set object
+ while ($row = mysqli_fetch_assoc($res)) {
+ // eat up all results
+ ;
+ }
+ mysqli_free_result($res);
+ } else {
+ // either there is no result (no SELECT) or there is an error
+ if (mysqli_errno($link) > 0) {
+ $saved_errors[$thread_id] = mysqli_errno($link);
+ printf("[003] '%s' caused %d\n", $links[$thread_id]['query'], mysqli_errno($link));
+ }
+ }
+ }
+
+ } while (true);
+
+ // Checking if all lines are still usable
+ foreach ($links as $thread_id => $link) {
+ if (isset($saved_errors[$thread_id]) &&
+ $saved_errors[$thread_id] != mysqli_errno($link['link'])) {
+ printf("[004] Error state not saved for query '%s', %d != %d\n", $link['query'],
+ $saved_errors[$thread_id], mysqli_errno($link['link']));
+ }
+
+ if (!$res = mysqli_query($link['link'], 'SELECT * FROM test WHERE id = 100'))
+ printf("[005] Expecting true got %s/%s\n", gettype($tmp), var_export($tmp, true));
+ if (!$row = mysqli_fetch_row($res))
+ printf("[006] Expecting true got %s/%s\n", gettype($tmp), var_export($tmp, true));
+
+ mysqli_free_result($res);
+ }
+
+ if ($res = mysqli_query($link['link'], "SELECT * FROM test WHERE id = 100")) {
+ $row = mysqli_fetch_assoc($res);
+ var_dump($row);
+ mysqli_free_result($res);
+ }
+
+ if ($have_proc && ($res = mysqli_query($link['link'], "SELECT @version as _version"))) {
+ $row = mysqli_fetch_assoc($res);
+ if ($row['_version'] != 'myversion') {
+ printf("[007] Check procedures\n");
+ }
+ mysqli_free_result($res);
+ }
+
+ foreach ($links as $link)
+ mysqli_close($link['link']);
+
+ $link = get_connection();
+ if (!mysqli_query($link, 'SELECT 1', MYSQLI_ASYNC))
+ printf("[008] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+
+ if (!mysqli_query($link, 'SELECT 1', MYSQLI_ASYNC))
+ printf("[009] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
+
+ mysqli_close($link);
+
+ print "done!";
+?>
+--EXPECTF--
+[003] 'SELECT' caused 1064
+[003] 'UPDATE test SET id = 101 WHERE id > 3' caused 1062
+[003] 'UPDATE_FIX test SET id = 101 WHERE id > 3' caused 1064
+array(2) {
+ ["id"]=>
+ string(3) "100"
+ ["label"]=>
+ string(1) "z"
+}
+[009] [2014] %s
+done!
--- /dev/null
+--TEST--
+mysqli_poll() & references
+--SKIPIF--
+<?php
+require_once('skipif.inc');
+require_once('skipifemb.inc');
+require_once('connect.inc');
+require_once('skipifconnectfailure.inc');
+
+if (!$IS_MYSQLND)
+ die("skip mysqlnd only feature, compile PHP using --with-mysqli=mysqlnd");
+
+if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket))
+ die("skip cannot connect");
+
+if (mysqli_server_version($link) < 50012))
+ die("skip Test needs SQL function SLEEP() available as of MySQL 5.0.12");
+
+mysqli_close($link);
+?>
+--FILE--
+<?php
+ require_once('connect.inc');
+
+ function get_connection() {
+ global $host, $user, $passwd, $db, $port, $socket;
+
+ if (!$link = mysqli_connect($host, $user, $passwd, $db, $port, $socket))
+ printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
+ return $link;
+ }
+
+
+ $mysqli1 = get_connection();
+ $mysqli2 = get_connection();
+
+ var_dump(mysqli_query($mysqli1, "SELECT SLEEP(0.10)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+ var_dump(mysqli_query($mysqli2, "SELECT SLEEP(0.20)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+
+ $processed = $loops = 0;
+ do {
+ $loops++;
+ if ($loops > 10) {
+ printf("[002] The queries should have finished already\n");
+ break;
+ }
+ // WARNING: All arrays point to the same object - this will give bogus results!
+ // The behaviour is in line with stream_select(). Be warned, be careful.
+ $links = $errors = $reject = array($mysqli1, $mysqli2);
+ if (0 == ($ready = mysqli_poll($links, $errors, $reject, 0, 50000))) {
+ continue;
+ }
+
+ foreach ($links as $link) {
+ if ($res = mysqli_reap_async_query($link)) {
+ mysqli_free_result($res);
+ }
+ $processed++;
+ }
+ } while ($processed < 2);
+
+ mysqli_close($mysqli1);
+ mysqli_close($mysqli2);
+
+ $mysqli1 = get_connection();
+ $mysqli2 = get_connection();
+
+ var_dump(mysqli_query($mysqli1, "SELECT SLEEP(0.10)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+ var_dump(mysqli_query($mysqli2, "SELECT SLEEP(0.20)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+
+ $processed = $loops = 0;
+ do {
+ $loops++;
+ if ($loops > 10) {
+ printf("[003] The queries should have finished already\n");
+ break;
+ }
+ // WARNING: All arrays point to the same object - this will give bogus results!
+ $links = $errors = array($mysqli1, $mysqli2);
+ $reject = array($mysqli1, $mysqli2);
+ if (0 == ($ready = mysqli_poll($links, $errors, $reject, 0, 50000))) {
+ continue;
+ }
+ foreach ($links as $link) {
+ if ($res = mysqli_reap_async_query($link)) {
+ mysqli_free_result($res);
+ }
+ $processed++;
+ }
+ } while ($processed < 2);
+
+ mysqli_close($mysqli1);
+ mysqli_close($mysqli2);
+
+ $mysqli1 = get_connection();
+ $mysqli2 = get_connection();
+
+ var_dump(mysqli_query($mysqli1, "SELECT SLEEP(0.10)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+ var_dump(mysqli_query($mysqli2, "SELECT SLEEP(0.20)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+
+ $processed = $loops = 0;
+ do {
+ $loops++;
+ if ($loops > 10) {
+ printf("[004] The queries should have finished already\n");
+ break;
+ }
+ // WARNING: All arrays point to the same object - this will give bogus results!
+ $links = array($mysqli1, $mysqli2);
+ $errors = $reject = array($mysqli1, $mysqli2);
+ if (0 == ($ready = mysqli_poll($links, $errors, $reject, 0, 50000))) {
+ continue;
+ }
+ foreach ($links as $link) {
+ if ($res = mysqli_reap_async_query($link)) {
+ mysqli_free_result($res);
+ }
+ $processed++;
+ }
+ } while ($processed < 2);
+
+ mysqli_close($mysqli1);
+ mysqli_close($mysqli2);
+
+ // This is bogus code and bogus usage - OK to throw no errors!
+ $mysqli1 = get_connection();
+ $mysqli2 = get_connection();
+
+ var_dump(mysqli_query($mysqli1, "SELECT SLEEP(0.10)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+ $thread_id = mysqli_thread_id($mysqli2);
+ printf("Connection %d should be rejected...\n", $thread_id);
+
+ $processed = $loops = 0;
+ do {
+ $loops++;
+ if ($loops > 10) {
+ printf("[005] The queries should have finished already\n");
+ break;
+ }
+ $links = $errors = $reject = array($mysqli1, $mysqli2);
+ if (0 == ($ready = mysqli_poll($links, $errors, $reject, 0, 50000))) {
+ continue;
+ }
+ // WARNING: Due to the reference issue none of these should ever fire!
+ foreach ($reject as $links) {
+ printf("Connection %d was rejected...\n", mysqli_thread_id($link));
+ $processed++;
+ }
+ foreach ($errors as $links) {
+ printf("Connection %d has an error...\n", mysqli_thread_id($link));
+ $processed++;
+ }
+ foreach ($links as $link) {
+ if ($res = mysqli_reap_async_query($link)) {
+ mysqli_free_result($res);
+ $processed++;
+ }
+ }
+ } while ($processed < 2);
+
+ mysqli_close($mysqli1);
+ mysqli_close($mysqli2);
+
+ $mysqli1 = get_connection();
+ $mysqli2 = get_connection();
+
+ var_dump(mysqli_query($mysqli1, "SELECT SLEEP(0.10)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+ var_dump(mysqli_query($mysqli2, "SELECT SLEEP(0.20)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
+
+ $processed = $loops = 0;
+ $all = array($mysqli1, $mysqli2);
+ do {
+ $loops++;
+ if ($loops > 10) {
+ printf("[006] The queries should have finished already\n");
+ break;
+ }
+ $links = $errors = $reject = $all;
+ ob_start();
+ if (0 == ($ready = mysqli_poll($links, $errors, $reject, 0, 50000))) {
+ $tmp = ob_get_contents();
+ ob_end_clean();
+ if ($tmp != '') {
+ printf("Expected error:\n%s\n", $tmp);
+ break;
+ }
+ continue;
+ }
+ foreach ($links as $link) {
+ if ($res = mysqli_reap_async_query($link)) {
+ mysqli_free_result($res);
+ }
+ $processed++;
+ }
+ } while ($processed < 2);
+
+ mysqli_close($mysqli1);
+ mysqli_close($mysqli2);
+
+ print "done!";
+?>
+--EXPECTF--
+bool(true)
+bool(true)
+[002] The queries should have finished already
+bool(true)
+bool(true)
+[003] The queries should have finished already
+bool(true)
+bool(true)
+bool(true)
+Connection %d should be rejected...
+[005] The queries should have finished already
+bool(true)
+bool(true)
+Expected error:
+
+Warning: mysqli_poll(): No stream arrays were passed in %s on line %d
+
+done!
/* }}} */
+/* {{{ mysqlnd_conn::send_query */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC)
+{
+ enum_func_status ret;
+ DBG_ENTER("mysqlnd_conn::send_query");
+ DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query);
+
+ ret = mysqlnd_simple_command(conn, COM_QUERY, query, query_len,
+ PROT_LAST /* we will handle the OK packet*/,
+ FALSE, FALSE TSRMLS_CC);
+ CONN_SET_STATE(conn, CONN_QUERY_SENT);
+ DBG_RETURN(ret);
+}
+/* }}} */
+
+/* {{{ mysqlnd_conn::send_query */
+static enum_func_status
+MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC)
+{
+ DBG_ENTER("mysqlnd_conn::reap_query");
+ DBG_INF_FMT("conn=%llu", conn->thread_id);
+ enum_mysqlnd_connection_state state = CONN_GET_STATE(conn);
+ if (state <= CONN_READY || state == CONN_QUIT_SENT) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection not opened, clear or has been closed");
+ DBG_RETURN(FAIL);
+ }
+ DBG_RETURN(mysqlnd_query_read_result_set_header(conn, NULL TSRMLS_CC));
+}
+/* }}} */
+
+
+#include "php_network.h"
+
+MYSQLND ** mysqlnd_stream_array_check_for_readiness(MYSQLND ** conn_array TSRMLS_DC)
+{
+ int cnt = 0;
+ MYSQLND **p = conn_array, **p_p;
+ MYSQLND **ret = NULL;
+
+ while (*p) {
+ if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
+ cnt++;
+ }
+ p++;
+ }
+ if (cnt) {
+ MYSQLND **ret_p = ret = ecalloc(cnt + 1, sizeof(MYSQLND *));
+ p_p = p = conn_array;
+ while (*p) {
+ if (CONN_GET_STATE(*p) <= CONN_READY || CONN_GET_STATE(*p) == CONN_QUIT_SENT) {
+ *ret_p = *p;
+ *p = NULL;
+ ret_p++;
+ } else {
+ *p_p = *p;
+ p_p++;
+ }
+ p++;
+ }
+ *ret_p = NULL;
+ }
+ return ret;
+}
+
+
+/* {{{ stream_select mysqlnd_stream_array_to_fd_set functions */
+static int mysqlnd_stream_array_to_fd_set(MYSQLND **conn_array, fd_set *fds, php_socket_t *max_fd TSRMLS_DC)
+{
+ php_socket_t this_fd;
+ int cnt = 0;
+ MYSQLND **p = conn_array;
+
+ while (*p) {
+ /* get the fd.
+ * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag
+ * when casting. It is only used here so that the buffered data warning
+ * is not displayed.
+ * */
+ if (SUCCESS == php_stream_cast((*p)->net.stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
+ (void*)&this_fd, 1) && this_fd >= 0) {
+
+ PHP_SAFE_FD_SET(this_fd, fds);
+
+ if (this_fd > *max_fd) {
+ *max_fd = this_fd;
+ }
+ cnt++;
+ }
+ p++;
+ }
+ return cnt ? 1 : 0;
+}
+
+static int mysqlnd_stream_array_from_fd_set(MYSQLND **conn_array, fd_set *fds TSRMLS_DC)
+{
+ php_socket_t this_fd;
+ int ret = 0;
+ zend_bool disproportion = FALSE;
+
+
+ MYSQLND **fwd = conn_array, **bckwd = conn_array;
+
+ while (*fwd) {
+ if (SUCCESS == php_stream_cast((*fwd)->net.stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL,
+ (void*)&this_fd, 1) && this_fd >= 0) {
+ if (PHP_SAFE_FD_ISSET(this_fd, fds)) {
+ if (disproportion) {
+ *bckwd = *fwd;
+ }
+ bckwd++;
+ fwd++;
+ ret++;
+ continue;
+ }
+ }
+ disproportion = TRUE;
+ fwd++;
+ }
+ *bckwd = NULL;/* NULL-terminate the list */
+
+ return ret;
+}
+
+
+#ifndef PHP_WIN32
+#define php_select(m, r, w, e, t) select(m, r, w, e, t)
+#else
+#include "win32/select.h"
+#endif
+
+/* {{{ _mysqlnd_poll */
+enum_func_status
+_mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC)
+{
+
+ struct timeval tv;
+ struct timeval *tv_p = NULL;
+ fd_set rfds, wfds, efds;
+ php_socket_t max_fd = 0;
+ int retval, sets = 0;
+ int set_count, max_set_count = 0;
+ DBG_ENTER("mysqlnd_poll");
+
+ if (sec < 0 || usec < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Negative values passed for sec and/or usec");
+ DBG_RETURN(FAIL);
+ }
+
+ *dont_poll = mysqlnd_stream_array_check_for_readiness(r_array TSRMLS_CC);
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ if (r_array != NULL) {
+ set_count = mysqlnd_stream_array_to_fd_set(r_array, &rfds, &max_fd TSRMLS_CC);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (e_array != NULL) {
+ set_count = mysqlnd_stream_array_to_fd_set(e_array, &efds, &max_fd TSRMLS_CC);
+ if (set_count > max_set_count) {
+ max_set_count = set_count;
+ }
+ sets += set_count;
+ }
+
+ if (!sets) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, *dont_poll ? "All arrays passed are clear":"No stream arrays were passed");
+ DBG_RETURN(FAIL);
+ }
+
+ PHP_SAFE_MAX_FD(max_fd, max_set_count);
+
+ /* Solaris + BSD do not like microsecond values which are >= 1 sec */
+ if (usec > 999999) {
+ tv.tv_sec = sec + (usec / 1000000);
+ tv.tv_usec = usec % 1000000;
+ } else {
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ }
+
+ tv_p = &tv;
+
+ retval = php_select(max_fd + 1, &rfds, &wfds, &efds, tv_p);
+
+ if (retval == -1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to select [%d]: %s (max_fd=%d)",
+ errno, strerror(errno), max_fd);
+ DBG_RETURN(FAIL);
+ }
+
+ if (r_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(r_array, &rfds TSRMLS_CC);
+ }
+ if (e_array != NULL) {
+ mysqlnd_stream_array_from_fd_set(e_array, &efds TSRMLS_CC);
+ }
+
+ *desc_num = retval;
+
+ DBG_RETURN(PASS);
+}
+/* }}} */
+
+
/*
COM_FIELD_LIST is special, different from a SHOW FIELDS FROM :
- There is no result set header - status from the command, which
MYSQLND_METHOD(mysqlnd_conn, escape_string),
MYSQLND_METHOD(mysqlnd_conn, set_charset),
MYSQLND_METHOD(mysqlnd_conn, query),
+ MYSQLND_METHOD(mysqlnd_conn, send_query),
+ MYSQLND_METHOD(mysqlnd_conn, reap_query),
MYSQLND_METHOD(mysqlnd_conn, use_result),
MYSQLND_METHOD(mysqlnd_conn, store_result),
MYSQLND_METHOD(mysqlnd_conn, background_store_result),
#define mysqlnd_close(conn,is_forced) (conn)->m->close((conn), (is_forced) TSRMLS_CC)
#define mysqlnd_query(conn, query_str, query_len) (conn)->m->query((conn), (query_str), (query_len) TSRMLS_CC)
+#define mysqlnd_async_query(conn, query_str, query_len) (conn)->m->send_query((conn), (query_str), (query_len) TSRMLS_CC)
+#define mysqlnd_poll(r, err, d_pull,sec,usec,desc_num) _mysqlnd_poll((r), (err), (d_pull), (sec), (usec), (desc_num) TSRMLS_CC)
+#define mysqlnd_reap_async_query(conn) (conn)->m->reap_query((conn) TSRMLS_CC)
#define mysqlnd_unbuffered_skip_result(result) (result)->m.skip_result((result) TSRMLS_CC)
+enum_func_status _mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long sec, long usec, uint * desc_num TSRMLS_DC);
#define mysqlnd_use_result(conn) (conn)->m->use_result((conn) TSRMLS_CC)
#define mysqlnd_store_result(conn) (conn)->m->store_result((conn) TSRMLS_CC)
#define L64(x) x##i64
#endif
#else
+
+#if __i386__
+#define MYSQLND_LL_SPEC "%lli"
#define MYSQLND_LLU_SPEC "%llu"
-#define MYSQLND_LL_SPEC "%lld"
+#endif
+
+#if __ia64__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __powerpc64__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __x86_64__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __s390x__
+#define MYSQLND_LL_SPEC "%li"
+#define MYSQLND_LLU_SPEC "%lu"
+#endif
+
+#if __powerpc__ && !__powerpc64__
+#define MYSQLND_LL_SPEC "%lli"
+#define MYSQLND_LLU_SPEC "%llu"
+#endif
+
+#if __s390__ && !__s390x__
+#define MYSQLND_LL_SPEC "%lli"
+#define MYSQLND_LLU_SPEC "%llu"
+#endif
+
#define MYSQLND_SZ_T_SPEC "%zd"
#ifndef L64
#define L64(x) x##LL
ulong (*escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC);
enum_func_status (*set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC);
enum_func_status (*query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC);
+ enum_func_status (*send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC);
+ enum_func_status (*reap_query)(MYSQLND *conn TSRMLS_DC);
MYSQLND_RES * (*use_result)(MYSQLND * const conn TSRMLS_DC);
MYSQLND_RES * (*store_result)(MYSQLND * const conn TSRMLS_DC);
MYSQLND_RES * (*background_store_result)(MYSQLND * const conn TSRMLS_DC);