mirror of
https://github.com/php/php-src.git
synced 2024-09-22 02:17:32 +00:00
- Don't modify the variables which are passed for parameter binding.
We need to clone them, if there will be a transformation (convert_to_xxx) which will change the origin (bug#44390 bind_param / bind_result and Object member variables) - Make mysqlnd more compatible to libmysql, in this case if the execute of a statement fails set the state of the statement back to PREPARED - A test case to check the case of a failing statement.
This commit is contained in:
parent
b2af8cac94
commit
6867385d75
@ -689,6 +689,22 @@ PHP_FUNCTION(mysqli_error)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#ifndef MYSQLI_USE_MYSQLND
|
||||
/* {{{ php_mysqli_stmt_copy_it */
|
||||
static void
|
||||
php_mysqli_stmt_copy_it(zval *** copies, zval *original, uint param_count, uint current)
|
||||
{
|
||||
if (!*copies) {
|
||||
*copies = ecalloc(param_count, sizeof(zval *));
|
||||
}
|
||||
MAKE_STD_ZVAL((*copies)[current]);
|
||||
*(*copies)[current] = *original;
|
||||
Z_SET_REFCOUNT_P((*copies)[current], 1);
|
||||
zval_copy_ctor((*copies)[current]);
|
||||
}
|
||||
/* }}} */
|
||||
#endif
|
||||
|
||||
/* {{{ proto bool mysqli_stmt_execute(object stmt)
|
||||
Execute a prepared statement */
|
||||
PHP_FUNCTION(mysqli_stmt_execute)
|
||||
@ -697,6 +713,7 @@ PHP_FUNCTION(mysqli_stmt_execute)
|
||||
zval *mysql_stmt;
|
||||
#ifndef MYSQLI_USE_MYSQLND
|
||||
unsigned int i;
|
||||
zval **copies = NULL;
|
||||
#endif
|
||||
|
||||
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
|
||||
@ -705,23 +722,48 @@ PHP_FUNCTION(mysqli_stmt_execute)
|
||||
MYSQLI_FETCH_RESOURCE(stmt, MY_STMT *, &mysql_stmt, "mysqli_stmt", MYSQLI_STATUS_VALID);
|
||||
|
||||
#ifndef MYSQLI_USE_MYSQLND
|
||||
if (stmt->param.var_cnt) {
|
||||
int j;
|
||||
for (i = 0; i < stmt->param.var_cnt; i++) {
|
||||
for (j = i + 1; j < stmt->param.var_cnt; j++) {
|
||||
/* Oops, someone binding the same variable - clone */
|
||||
if (stmt->param.vars[j] == stmt->param.vars[i]) {
|
||||
php_mysqli_stmt_copy_it(&copies, stmt->param.vars[i], stmt->param.var_cnt, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < stmt->param.var_cnt; i++) {
|
||||
if (stmt->param.vars[i]) {
|
||||
if ( !(stmt->param.is_null[i] = (stmt->param.vars[i]->type == IS_NULL)) ) {
|
||||
zval *the_var = copies && copies[i]? copies[i]:stmt->param.vars[i];
|
||||
switch (stmt->stmt->params[i].buffer_type) {
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
convert_to_string_ex(&stmt->param.vars[i]);
|
||||
stmt->stmt->params[i].buffer = Z_STRVAL_PP(&stmt->param.vars[i]);
|
||||
stmt->stmt->params[i].buffer_length = Z_STRLEN_PP(&stmt->param.vars[i]);
|
||||
if (the_var == stmt->param.vars[i] && Z_TYPE_P(stmt->param.vars[i]) != IS_STRING) {
|
||||
php_mysqli_stmt_copy_it(&copies, stmt->param.vars[i], stmt->param.var_cnt, i);
|
||||
the_var = copies[i];
|
||||
}
|
||||
convert_to_string_ex(&the_var);
|
||||
stmt->stmt->params[i].buffer = Z_STRVAL_P(the_var);
|
||||
stmt->stmt->params[i].buffer_length = Z_STRLEN_P(the_var);
|
||||
break;
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
convert_to_double_ex(&stmt->param.vars[i]);
|
||||
stmt->stmt->params[i].buffer = &Z_LVAL_PP(&stmt->param.vars[i]);
|
||||
if (the_var == stmt->param.vars[i] && Z_TYPE_P(stmt->param.vars[i]) != IS_DOUBLE) {
|
||||
php_mysqli_stmt_copy_it(&copies, stmt->param.vars[i], stmt->param.var_cnt, i);
|
||||
the_var = copies[i];
|
||||
}
|
||||
convert_to_double_ex(&the_var);
|
||||
stmt->stmt->params[i].buffer = &Z_DVAL_P(the_var);
|
||||
break;
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
case MYSQL_TYPE_LONG:
|
||||
convert_to_long_ex(&stmt->param.vars[i]);
|
||||
stmt->stmt->params[i].buffer = &Z_LVAL_PP(&stmt->param.vars[i]);
|
||||
if (the_var == stmt->param.vars[i] && Z_TYPE_P(stmt->param.vars[i]) != IS_LONG) {
|
||||
php_mysqli_stmt_copy_it(&copies, stmt->param.vars[i], stmt->param.var_cnt, i);
|
||||
the_var = copies[i];
|
||||
}
|
||||
convert_to_long_ex(&the_var);
|
||||
stmt->stmt->params[i].buffer = &Z_LVAL_P(the_var);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -738,6 +780,17 @@ PHP_FUNCTION(mysqli_stmt_execute)
|
||||
RETVAL_TRUE;
|
||||
}
|
||||
|
||||
#ifndef MYSQLI_USE_MYSQLND
|
||||
if (copies) {
|
||||
for (i = 0; i < stmt->param.var_cnt; i++) {
|
||||
if (copies[i]) {
|
||||
zval_ptr_dtor(&copies[i]);
|
||||
}
|
||||
}
|
||||
efree(copies);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
|
||||
php_mysqli_report_index(stmt->query, mysqli_stmt_server_status(stmt->stmt) TSRMLS_CC);
|
||||
}
|
||||
|
151
ext/mysqli/tests/mysqli_stmt_datatype_change.phpt
Normal file
151
ext/mysqli/tests/mysqli_stmt_datatype_change.phpt
Normal file
@ -0,0 +1,151 @@
|
||||
--TEST--
|
||||
mysqli_stmt_bind_param() - playing with references
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once('skipif.inc');
|
||||
require_once('skipifemb.inc');
|
||||
require_once('skipifconnectfailure.inc');
|
||||
?>
|
||||
--FILE--
|
||||
<?php
|
||||
include "connect.inc";
|
||||
require('table.inc');
|
||||
|
||||
if (!$c1 = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
|
||||
$host, $user, $db, $port, $socket);
|
||||
exit(1);
|
||||
}
|
||||
if (!$c2 = mysqli_connect($host, $user, $passwd, $db, $port, $socket)) {
|
||||
printf("Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n",
|
||||
$host, $user, $db, $port, $socket);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$c1->query("use $db");
|
||||
$c2->query("use $db");
|
||||
$c1->query("drop table if exists type_change");
|
||||
$c1->query("create table type_change(a int, b char(10))");
|
||||
$c1->query("insert into type_change values (1, 'one'), (2, 'two')");
|
||||
$s1 = $c1->prepare("select a from type_change order by a");
|
||||
var_dump($s1);
|
||||
var_dump($s1->execute(), $s1->bind_result($col1));
|
||||
echo "---- Row 1\n";
|
||||
var_dump($s1->fetch());
|
||||
var_dump($col1);
|
||||
echo "---- Row 2\n";
|
||||
var_dump($s1->fetch());
|
||||
var_dump($col1);
|
||||
echo "---- Row 3\n";
|
||||
var_dump($s1->fetch());
|
||||
echo "----\n";
|
||||
|
||||
echo "ALTER\n";
|
||||
var_dump($c2->query("alter table type_change drop a"));
|
||||
var_dump($s1->execute());
|
||||
var_dump($c1->error);
|
||||
|
||||
echo "---- Row 1\n";
|
||||
var_dump($s1->fetch());
|
||||
var_dump($col1);
|
||||
echo "---- Row 2\n";
|
||||
var_dump($s1->fetch());
|
||||
var_dump($col1);
|
||||
echo "---- Row 3\n";
|
||||
var_dump($s1->fetch());
|
||||
echo "----\n";
|
||||
|
||||
echo "done!";
|
||||
?>
|
||||
--EXPECTF--
|
||||
object(mysqli_stmt)#%d (%d) {
|
||||
["affected_rows"]=>
|
||||
int(0)
|
||||
["insert_id"]=>
|
||||
int(0)
|
||||
["num_rows"]=>
|
||||
int(0)
|
||||
["param_count"]=>
|
||||
int(0)
|
||||
["field_count"]=>
|
||||
int(1)
|
||||
["errno"]=>
|
||||
int(0)
|
||||
["error"]=>
|
||||
string(0) ""
|
||||
["sqlstate"]=>
|
||||
string(5) "00000"
|
||||
["id"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(true)
|
||||
bool(true)
|
||||
---- Row 1
|
||||
bool(true)
|
||||
int(1)
|
||||
---- Row 2
|
||||
bool(true)
|
||||
int(2)
|
||||
---- Row 3
|
||||
NULL
|
||||
----
|
||||
ALTER
|
||||
bool(true)
|
||||
bool(false)
|
||||
string(34) "Unknown column 'a' in 'field list'"
|
||||
---- Row 1
|
||||
bool(false)
|
||||
int(2)
|
||||
---- Row 2
|
||||
bool(false)
|
||||
int(2)
|
||||
---- Row 3
|
||||
bool(false)
|
||||
----
|
||||
done!
|
||||
--UEXPECTF--
|
||||
object(mysqli_stmt)#%d (%d) {
|
||||
[u"affected_rows"]=>
|
||||
int(0)
|
||||
[u"insert_id"]=>
|
||||
int(0)
|
||||
[u"num_rows"]=>
|
||||
int(0)
|
||||
[u"param_count"]=>
|
||||
int(0)
|
||||
[u"field_count"]=>
|
||||
int(1)
|
||||
[u"errno"]=>
|
||||
int(0)
|
||||
[u"error"]=>
|
||||
unicode(0) ""
|
||||
[u"sqlstate"]=>
|
||||
unicode(5) "00000"
|
||||
[u"id"]=>
|
||||
int(1)
|
||||
}
|
||||
bool(true)
|
||||
bool(true)
|
||||
---- Row 1
|
||||
bool(true)
|
||||
int(1)
|
||||
---- Row 2
|
||||
bool(true)
|
||||
int(2)
|
||||
---- Row 3
|
||||
NULL
|
||||
----
|
||||
ALTER
|
||||
bool(true)
|
||||
bool(false)
|
||||
unicode(34) "Unknown column 'a' in 'field list'"
|
||||
---- Row 1
|
||||
bool(false)
|
||||
int(2)
|
||||
---- Row 2
|
||||
bool(false)
|
||||
int(2)
|
||||
---- Row 3
|
||||
bool(false)
|
||||
----
|
||||
done!
|
@ -591,6 +591,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
|
||||
if (hashed_details) {
|
||||
mnd_efree(hashed_details);
|
||||
}
|
||||
errcode = CR_CONNECTION_ERROR;
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -748,7 +749,7 @@ PHPAPI MYSQLND *mysqlnd_connect(MYSQLND *conn,
|
||||
conn->net.cmd_buffer.length = 128L*1024L;
|
||||
conn->net.cmd_buffer.buffer = mnd_pemalloc(conn->net.cmd_buffer.length, conn->persistent);
|
||||
|
||||
mysqlnd_local_infile_default(conn);
|
||||
mysqlnd_local_infile_default(conn);
|
||||
{
|
||||
uint buf_size;
|
||||
buf_size = MYSQLND_G(net_read_buffer_size); /* this is long, cast to uint*/
|
||||
@ -807,11 +808,12 @@ err:
|
||||
DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
|
||||
SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr);
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
|
||||
|
||||
mnd_efree(errstr);
|
||||
/* no mnd_ since we don't allocate it */
|
||||
efree(errstr);
|
||||
}
|
||||
if (conn->scheme) {
|
||||
mnd_pefree(conn->scheme, conn->persistent);
|
||||
/* no mnd_ since we don't allocate it */
|
||||
pefree(conn->scheme, conn->persistent);
|
||||
conn->scheme = NULL;
|
||||
}
|
||||
|
||||
@ -1315,7 +1317,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_conn, get_state)(MYSQLND * const conn TSRMLS_DC)
|
||||
{
|
||||
enum mysqlnd_connection_state state;
|
||||
DBG_ENTER("mysqlnd_conn::get_state");
|
||||
tsrm_mutex_lock(conn->LOCK_state);
|
||||
tsrm_mutex_lock(conn->LOCK_state);
|
||||
state = conn->state;
|
||||
tsrm_mutex_unlock(conn->LOCK_state);
|
||||
DBG_RETURN(state);
|
||||
|
@ -522,6 +522,7 @@ MYSQLND_METHOD(mysqlnd_stmt, execute)(MYSQLND_STMT * const stmt TSRMLS_DC)
|
||||
if (CONN_GET_STATE(conn) == CONN_QUIT_SENT) {
|
||||
/* close the statement here, the connection has been closed */
|
||||
}
|
||||
stmt->state = MYSQLND_STMT_PREPARED;
|
||||
} else {
|
||||
SET_EMPTY_ERROR(stmt->error_info);
|
||||
SET_EMPTY_ERROR(stmt->conn->error_info);
|
||||
|
@ -650,14 +650,30 @@ void _mysqlnd_init_ps_subsystem()
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysqlnd_stmt_copy_it */
|
||||
static void
|
||||
mysqlnd_stmt_copy_it(zval *** copies, zval *original, uint param_count, uint current)
|
||||
{
|
||||
if (!*copies) {
|
||||
*copies = ecalloc(param_count, sizeof(zval *));
|
||||
}
|
||||
MAKE_STD_ZVAL((*copies)[current]);
|
||||
*(*copies)[current] = *original;
|
||||
Z_SET_REFCOUNT_P((*copies)[current], 1);
|
||||
zval_copy_ctor((*copies)[current]);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ mysqlnd_stmt_execute_store_params */
|
||||
void
|
||||
static void
|
||||
mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uchar **p,
|
||||
size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
unsigned left = (*buf_len - (*p - *buf));
|
||||
unsigned int data_size = 0;
|
||||
zval **copies = NULL;/* if there are different types */
|
||||
|
||||
/* 1. Store type information */
|
||||
if (stmt->send_types_to_server) {
|
||||
@ -689,26 +705,44 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch
|
||||
/* 2. Store data */
|
||||
/* 2.1 Calculate how much space we need */
|
||||
for (i = 0; i < stmt->param_count; i++) {
|
||||
unsigned int j;
|
||||
zval *the_var = stmt->param_bind[i].zv;
|
||||
if (stmt->param_bind[i].zv &&
|
||||
Z_TYPE_P(stmt->param_bind[i].zv) == IS_NULL) {
|
||||
continue;
|
||||
}
|
||||
for (j = i + 1; j < stmt->param_count; j++) {
|
||||
if (stmt->param_bind[j].zv == stmt->param_bind[i].zv) {
|
||||
/* Double binding of the same zval, make a copy */
|
||||
mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (stmt->param_bind[i].type) {
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
data_size += 8;
|
||||
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_DOUBLE) {
|
||||
if (!copies || !copies[i]) {
|
||||
mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if SIZEOF_LONG==8
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
data_size += 8;
|
||||
break;
|
||||
#elif SIZEOF_LONG==4
|
||||
case MYSQL_TYPE_LONG:
|
||||
data_size += 4;
|
||||
break;
|
||||
#else
|
||||
#error "Should not happen"
|
||||
#endif
|
||||
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
|
||||
if (!copies || !copies[i]) {
|
||||
mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
|
||||
/*
|
||||
@ -721,8 +755,25 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch
|
||||
break;
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
data_size += 8; /* max 8 bytes for size */
|
||||
convert_to_string_ex(&stmt->param_bind[i].zv);
|
||||
data_size += Z_STRLEN_P(stmt->param_bind[i].zv);
|
||||
#if PHP_MAJOR_VERSION < 6
|
||||
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_STRING)
|
||||
#elif PHP_MAJOR_VERSION >= 6
|
||||
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_STRING ||
|
||||
(UG(unicode) && Z_TYPE_P(stmt->param_bind[i].zv) == IS_UNICODE))
|
||||
#endif
|
||||
{
|
||||
if (!copies || !copies[i]) {
|
||||
mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i);
|
||||
}
|
||||
the_var = copies[i];
|
||||
#if PHP_MAJOR_VERSION >= 6
|
||||
if (UG(unicode) && Z_TYPE_P(the_var) == IS_UNICODE) {
|
||||
zval_unicode_to_string_ex(the_var, UG(utf8_conv) TSRMLS_CC);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
convert_to_string_ex(&the_var);
|
||||
data_size += Z_STRLEN_P(the_var);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -743,7 +794,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch
|
||||
|
||||
/* 2.3 Store the actual data */
|
||||
for (i = 0; i < stmt->param_count; i++) {
|
||||
zval *data = stmt->param_bind[i].zv;
|
||||
zval *data = copies && copies[i]? copies[i]: stmt->param_bind[i].zv;
|
||||
/* Handle long data */
|
||||
if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
|
||||
(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
|
||||
@ -791,7 +842,6 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch
|
||||
memcpy(*p, Z_STRVAL_P(data), len);
|
||||
(*p) += len;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
/* Won't happen, but set to NULL */
|
||||
@ -800,6 +850,14 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT *stmt, zend_uchar **buf, zend_uch
|
||||
}
|
||||
}
|
||||
}
|
||||
if (copies) {
|
||||
for (i = 0; i < stmt->param_count; i++) {
|
||||
if (copies[i]) {
|
||||
zval_ptr_dtor(&copies[i]);
|
||||
}
|
||||
}
|
||||
efree(copies);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user