Skip to content

Commit 63791d0

Browse files
committed
New result fetching mode for mysqlnd, which should use less memory but
implies more memory copy. The old method is still available and can be used. It stays as default. Choosing the method is through a flag to mysqli_query()/mysqli_real_query() New mode can be forced with an INI setting, for all extensions that support this mode (ext/mysql and mysqli, because PDO due to it's architecture can't support it) The setting is mysqlnd.fetch_data_copy=[0|1]
1 parent 973f379 commit 63791d0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1810
-380
lines changed

ext/mysql/php_mysql.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
877877
#ifndef MYSQL_USE_MYSQLND
878878
mysql->conn = mysql_init(NULL);
879879
#else
880-
mysql->conn = mysql_init(persistent);
880+
mysql->conn = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent);
881881
#endif
882882

883883
if (connect_timeout != -1) {
@@ -886,7 +886,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
886886
#ifndef MYSQL_USE_MYSQLND
887887
if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
888888
#else
889-
if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags TSRMLS_CC) == NULL)
889+
if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL)
890890
#endif
891891
{
892892
/* Populate connect error globals so that the error functions can read them */
@@ -934,7 +934,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
934934
#ifndef MYSQL_USE_MYSQLND
935935
if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
936936
#else
937-
if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags TSRMLS_CC) == NULL)
937+
if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL)
938938
#endif
939939
{
940940
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect");
@@ -996,7 +996,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
996996
#ifndef MYSQL_USE_MYSQLND
997997
mysql->conn = mysql_init(NULL);
998998
#else
999-
mysql->conn = mysql_init(persistent);
999+
mysql->conn = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent);
10001000
#endif
10011001
if (!mysql->conn) {
10021002
MySG(connect_error) = estrdup("OOM");
@@ -1013,7 +1013,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
10131013
#ifndef MYSQL_USE_MYSQLND
10141014
if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL)
10151015
#else
1016-
if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags TSRMLS_CC) == NULL)
1016+
if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL)
10171017
#endif
10181018
{
10191019
/* Populate connect error globals so that the error functions can read them */

ext/mysqli/mysqli.c

+1
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ PHP_MINIT_FUNCTION(mysqli)
721721
REGISTER_LONG_CONSTANT("MYSQLI_USE_RESULT", MYSQLI_USE_RESULT, CONST_CS | CONST_PERSISTENT);
722722
#if defined (MYSQLI_USE_MYSQLND)
723723
REGISTER_LONG_CONSTANT("MYSQLI_ASYNC", MYSQLI_ASYNC, CONST_CS | CONST_PERSISTENT);
724+
REGISTER_LONG_CONSTANT("MYSQLI_STORE_RESULT_COPY_DATA", MYSQLI_STORE_RESULT_COPY_DATA, CONST_CS | CONST_PERSISTENT);
724725
#endif
725726

726727
/* for mysqli_fetch_assoc */

ext/mysqli/mysqli_api.c

+11-5
Original file line numberDiff line numberDiff line change
@@ -1477,7 +1477,7 @@ void php_mysqli_init(INTERNAL_FUNCTION_PARAMETERS)
14771477
We create always persistent, as if the user want to connecto
14781478
to p:somehost, we can't convert the handle then
14791479
*/
1480-
if (!(mysql->mysql = mysql_init(TRUE)))
1480+
if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, TRUE)))
14811481
#endif
14821482
{
14831483
efree(mysql);
@@ -2557,21 +2557,27 @@ PHP_FUNCTION(mysqli_stmt_sqlstate)
25572557
}
25582558
/* }}} */
25592559

2560-
/* {{{ proto object mysqli_store_result(object link)
2560+
/* {{{ proto object mysqli_store_result(object link [, flags])
25612561
Buffer result set on client */
25622562
PHP_FUNCTION(mysqli_store_result)
25632563
{
25642564
MY_MYSQL *mysql;
25652565
MYSQL_RES *result;
25662566
zval *mysql_link;
25672567
MYSQLI_RESOURCE *mysqli_resource;
2568+
long flags = 0;
25682569

2569-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
2570+
2571+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_link, mysqli_link_class_entry, &flags) == FAILURE) {
25702572
return;
25712573
}
25722574
MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID);
2573-
2574-
if (!(result = mysql_store_result(mysql->mysql))) {
2575+
#if MYSQLI_USE_MYSQLND
2576+
result = flags & MYSQLI_STORE_RESULT_COPY_DATA? mysqlnd_store_result_ofs(mysql->mysql) : mysqlnd_store_result(mysql->mysql);
2577+
#else
2578+
result = mysql_store_result(mysql->mysql);
2579+
#endif
2580+
if (!result) {
25752581
MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
25762582
RETURN_FALSE;
25772583
}

ext/mysqli/mysqli_fe.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_rollback, 0, 0, 0)
142142
ZEND_ARG_INFO(0, name)
143143
ZEND_END_ARG_INFO()
144144

145+
146+
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_store_result, 0, 0, 1)
147+
MYSQLI_ZEND_ARG_OBJ_INFO_LINK()
148+
ZEND_ARG_INFO(0, flags)
149+
ZEND_END_ARG_INFO()
150+
151+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_store_result, 0, 0, 0)
152+
ZEND_ARG_INFO(0, flags)
153+
ZEND_END_ARG_INFO()
154+
155+
145156
ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_change_user, 0, 0, 4)
146157
MYSQLI_ZEND_ARG_OBJ_INFO_LINK()
147158
ZEND_ARG_INFO(0, user)
@@ -498,7 +509,7 @@ const zend_function_entry mysqli_functions[] = {
498509
PHP_FE(mysqli_sqlstate, arginfo_mysqli_only_link)
499510
PHP_FE(mysqli_ssl_set, arginfo_mysqli_ssl_set)
500511
PHP_FE(mysqli_stat, arginfo_mysqli_only_link)
501-
PHP_FE(mysqli_store_result, arginfo_mysqli_only_link)
512+
PHP_FE(mysqli_store_result, arginfo_mysqli_store_result)
502513
PHP_FE(mysqli_thread_id, arginfo_mysqli_only_link)
503514
PHP_FE(mysqli_thread_safe, arginfo_mysqli_no_params)
504515
PHP_FE(mysqli_use_result, arginfo_mysqli_only_link)
@@ -568,7 +579,7 @@ const zend_function_entry mysqli_link_methods[] = {
568579
PHP_FALIAS(ssl_set, mysqli_ssl_set, arginfo_class_mysqli_ssl_set)
569580
PHP_FALIAS(stat, mysqli_stat, arginfo_mysqli_no_params)
570581
PHP_FALIAS(stmt_init, mysqli_stmt_init, arginfo_mysqli_no_params)
571-
PHP_FALIAS(store_result, mysqli_store_result, arginfo_mysqli_no_params)
582+
PHP_FALIAS(store_result, mysqli_store_result, arginfo_class_store_result)
572583
PHP_FALIAS(thread_safe, mysqli_thread_safe, arginfo_mysqli_no_params)
573584
PHP_FALIAS(use_result, mysqli_use_result, arginfo_mysqli_no_params)
574585
PHP_FALIAS(refresh,mysqli_refresh, arginfo_class_mysqli_refresh)

ext/mysqli/mysqli_nonapi.c

+10-5
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne
217217
#if !defined(MYSQLI_USE_MYSQLND)
218218
if (!(mysql->mysql = mysql_init(NULL))) {
219219
#else
220-
if (!(mysql->mysql = mysqlnd_init(persistent))) {
220+
if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent))) {
221221
#endif
222222
goto err;
223223
}
@@ -240,7 +240,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne
240240
if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname, port, socket, flags) == NULL)
241241
#else
242242
if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len,
243-
port, socket, flags TSRMLS_CC) == NULL)
243+
port, socket, flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL)
244244
#endif
245245
{
246246
/* Save error messages - for mysqli_connect_error() & mysqli_connect_errno() */
@@ -575,7 +575,7 @@ PHP_FUNCTION(mysqli_query)
575575
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query");
576576
RETURN_FALSE;
577577
}
578-
if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT) {
578+
if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) != MYSQLI_STORE_RESULT) {
579579
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode");
580580
RETURN_FALSE;
581581
}
@@ -609,9 +609,14 @@ PHP_FUNCTION(mysqli_query)
609609
RETURN_TRUE;
610610
}
611611

612-
switch (resultmode) {
612+
switch (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) {
613613
case MYSQLI_STORE_RESULT:
614-
result = mysql_store_result(mysql->mysql);
614+
#ifdef MYSQLI_USE_MYSQLND
615+
if (resultmode & MYSQLI_STORE_RESULT_COPY_DATA) {
616+
result = mysqlnd_store_result_ofs(mysql->mysql);
617+
} else
618+
#endif
619+
result = mysql_store_result(mysql->mysql);
615620
break;
616621
case MYSQLI_USE_RESULT:
617622
result = mysql_use_result(mysql->mysql);

ext/mysqli/mysqli_priv.h

+2
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,11 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRML
114114
#define MYSQLI_USE_RESULT 1
115115
#ifdef MYSQLI_USE_MYSQLND
116116
#define MYSQLI_ASYNC 8
117+
#define MYSQLI_STORE_RESULT_COPY_DATA 16
117118
#else
118119
/* libmysql */
119120
#define MYSQLI_ASYNC 0
121+
#define MYSQLI_STORE_RESULT_OFS 0
120122
#endif
121123

122124
/* for mysqli_fetch_assoc */

ext/mysqli/tests/bug66043.phpt

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ require_once('skipifconnectfailure.inc');
1212
--FILE--
1313
<?php
1414
require 'connect.inc';
15-
$db = new mysqli($host, $user, $passwd, 'mysql');
15+
if (!$db = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
16+
printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
17+
}
1618

1719
$stmt = $db->stmt_init();
1820
$stmt->prepare("SELECT User FROM user WHERE password=\"\"");

ext/mysqli/tests/bug66762.phpt

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ require_once('skipifconnectfailure.inc');
99
<?php
1010
require_once("connect.inc");
1111

12-
$mysqli = new mysqli($host, $user, $passwd, $db);
12+
if (!$mysqli = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
13+
printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
14+
}
1315

1416
$read_stmt = $mysqli->prepare("SELECT 1");
1517

1618
var_dump($read_stmt->bind_result($data));
1719

1820
unset($mysqli);
1921
var_dump($read_stmt->bind_result($data));
20-
2122
?>
2223
done!
2324
--EXPECT--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
--TEST--
2+
mysqli_begin_transaction()
3+
--SKIPIF--
4+
<?php
5+
require_once('skipif.inc');
6+
require_once('skipifemb.inc');
7+
require_once('skipifconnectfailure.inc');
8+
9+
require_once('connect.inc');
10+
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
11+
die(sprintf("Cannot connect, [%d] %s", mysqli_connect_errno(), mysqli_connect_error()));
12+
13+
if (!have_innodb($link))
14+
die(sprintf("Needs InnoDB support, [%d] %s", $link->errno, $link->error));
15+
?>
16+
--FILE--
17+
<?php
18+
require_once("connect.inc");
19+
/* {{{ proto bool mysqli_begin_transaction(object link, [int flags [, string name]]) */
20+
$tmp = NULL;
21+
$link = NULL;
22+
23+
if (!is_null($tmp = @mysqli_begin_transaction()))
24+
printf("[001] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp);
25+
26+
if (!is_null($tmp = @mysqli_begin_transaction($link)))
27+
printf("[002] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp);
28+
29+
if (!is_null($tmp = @mysqli_begin_transaction($link, $link)))
30+
printf("[003] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp);
31+
32+
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
33+
printf("[004] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
34+
$host, $user, $db, $port, $socket);
35+
36+
if (!is_null($tmp = @mysqli_begin_transaction($link, $link)))
37+
printf("[005] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp);
38+
39+
if (!is_null($tmp = @mysqli_begin_transaction($link, 0, $link)))
40+
printf("[006] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp);
41+
42+
if (!is_null($tmp = @mysqli_begin_transaction($link, 0, "mytrx", $link)))
43+
printf("[007] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp);
44+
45+
if (!mysqli_query($link, 'DROP TABLE IF EXISTS test'))
46+
printf("[008] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
47+
48+
if (!mysqli_query($link, 'CREATE TABLE test(id INT) ENGINE = InnoDB'))
49+
printf("[009] Cannot create test table, [%d] %s\n", mysqli_errno($link), mysqli_error($link));
50+
51+
if (true !== ($tmp = mysqli_autocommit($link, true)))
52+
printf("[010] Cannot turn on autocommit, expecting true, got %s/%s\n", gettype($tmp), $tmp);
53+
54+
/* overrule autocommit */
55+
if (true !== ($tmp = mysqli_begin_transaction($link)))
56+
printf("[011] Got %s - [%d] %s\n", var_dump($tmp, true), mysqli_errno($link), mysqli_error($link));
57+
58+
if (!mysqli_query($link, 'INSERT INTO test(id) VALUES (1)'))
59+
printf("[012] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
60+
61+
$tmp = mysqli_rollback($link);
62+
if ($tmp !== true)
63+
printf("[013] Expecting boolean/true, got %s/%s\n", gettype($tmp), $tmp);
64+
65+
/* empty */
66+
$res = mysqli_query($link, "SELECT * FROM test");
67+
var_dump($res->fetch_assoc());
68+
69+
/* valid flags */
70+
$flags = array(
71+
MYSQLI_TRANS_START_WITH_CONSISTENT_SNAPSHOT,
72+
MYSQLI_TRANS_START_READ_WRITE,
73+
MYSQLI_TRANS_START_READ_ONLY,
74+
MYSQLI_TRANS_COR_AND_CHAIN,
75+
MYSQLI_TRANS_COR_AND_NO_CHAIN,
76+
MYSQLI_TRANS_COR_RELEASE,
77+
MYSQLI_TRANS_COR_NO_RELEASE);
78+
79+
/* just coverage */
80+
foreach ($flags as $flag) {
81+
if (!mysqli_begin_transaction($link, $flag, sprintf("flag %d", $flag))) {
82+
printf("[014] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
83+
}
84+
if (!mysqli_query($link, 'SELECT * FROM test') ||
85+
!mysqli_rollback($link)) {
86+
printf("[015] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
87+
}
88+
}
89+
90+
/* does it really set a flag? */
91+
if (mysqli_get_server_version($link) >= 50600) {
92+
if (!mysqli_begin_transaction($link, MYSQLI_TRANS_START_READ_ONLY, sprintf("flag %d", $flag))) {
93+
printf("[016] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
94+
}
95+
if (!mysqli_query($link, "INSERT INTO test(id) VALUES (2)") ||
96+
!mysqli_commit($link)) {
97+
printf("[017] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
98+
} else {
99+
$res = mysqli_query($link, "SELECT id FROM test WHERE id = 2");
100+
var_dump($res->fetch_assoc());
101+
}
102+
}
103+
104+
/* invalid flag */
105+
do {
106+
$invalid_flag = mt_rand(0, 10000);
107+
} while (isset(array_flip($flags)[$invalid_flag]));
108+
/* we may or may not hit an invalid combination provoking a SQL error */
109+
if (!mysqli_begin_transaction($link, $invalid_flag, sprintf("flag %d", $invalid_flag))) {
110+
printf("[018] invalid_flag = %d [%d] %s\n", $invalid_flag, mysqli_errno($link), mysqli_error($link));
111+
} else {
112+
printf("[018] invalid_flag = %d [%d] %s\n", $invalid_flag, mysqli_errno($link), mysqli_error($link));
113+
}
114+
if (!mysqli_begin_transaction($link, -1)) {
115+
printf("[019] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
116+
}
117+
118+
/* does it like stupid names? */
119+
if (!$link->begin_transaction(MYSQLI_TRANS_START_READ_WRITE, "*/trick me?\n\0"))
120+
printf("[020] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
121+
122+
/* does it like stupid names? */
123+
if (!$link->begin_transaction(MYSQLI_TRANS_START_READ_WRITE, "az09"))
124+
printf("[021] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
125+
126+
print "done!";
127+
?>
128+
--CLEAN--
129+
<?php
130+
require_once("clean_table.inc");
131+
?>
132+
--EXPECTF--
133+
NULL
134+
[017] [1792] %s
135+
[018] invalid_flag = %d [%d]%A
136+
137+
Warning: mysqli_begin_transaction(): Invalid value for parameter flags (-1) in %s on line %d
138+
[019] [%d]%A
139+
[020] [%d]%A
140+
done!

0 commit comments

Comments
 (0)