andrey Thu Mar 20 14:03:30 2008 UTC Added files: (Branch: PHP_5_3) /php-src/ext/mysqli/tests mysqli_stmt_datatype_change.phpt
Modified files: /php-src/ext/mysqli mysqli_api.c /php-src/ext/mysqlnd mysqlnd.c mysqlnd_ps.c mysqlnd_ps_codec.c Log: - 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.
http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/mysqli_api.c?r1=1.118.2.22.2.16.2.14&r2=1.118.2.22.2.16.2.15&diff_format=u Index: php-src/ext/mysqli/mysqli_api.c diff -u php-src/ext/mysqli/mysqli_api.c:1.118.2.22.2.16.2.14 php-src/ext/mysqli/mysqli_api.c:1.118.2.22.2.16.2.15 --- php-src/ext/mysqli/mysqli_api.c:1.118.2.22.2.16.2.14 Mon Mar 10 20:15:38 2008 +++ php-src/ext/mysqli/mysqli_api.c Thu Mar 20 14:03:29 2008 @@ -17,7 +17,7 @@ | Ulf Wendel <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ - $Id: mysqli_api.c,v 1.118.2.22.2.16.2.14 2008/03/10 20:15:38 andrey Exp $ + $Id: mysqli_api.c,v 1.118.2.22.2.16.2.15 2008/03/20 14:03:29 andrey Exp $ */ #ifdef HAVE_CONFIG_H @@ -689,6 +689,22 @@ } /* }}} */ +#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 @@ 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 @@ 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 @@ 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); } http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd.c?r1=1.5.2.16&r2=1.5.2.17&diff_format=u Index: php-src/ext/mysqlnd/mysqlnd.c diff -u php-src/ext/mysqlnd/mysqlnd.c:1.5.2.16 php-src/ext/mysqlnd/mysqlnd.c:1.5.2.17 --- php-src/ext/mysqlnd/mysqlnd.c:1.5.2.16 Tue Mar 18 16:57:31 2008 +++ php-src/ext/mysqlnd/mysqlnd.c Thu Mar 20 14:03:30 2008 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd.c,v 1.5.2.16 2008/03/18 16:57:31 andrey Exp $ */ +/* $Id: mysqlnd.c,v 1.5.2.17 2008/03/20 14:03:30 andrey Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -591,6 +591,7 @@ if (hashed_details) { mnd_efree(hashed_details); } + errcode = CR_CONNECTION_ERROR; goto err; } @@ -748,7 +749,7 @@ 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 @@ 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 @@ { 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); http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_ps.c?r1=1.3.2.10&r2=1.3.2.11&diff_format=u Index: php-src/ext/mysqlnd/mysqlnd_ps.c diff -u php-src/ext/mysqlnd/mysqlnd_ps.c:1.3.2.10 php-src/ext/mysqlnd/mysqlnd_ps.c:1.3.2.11 --- php-src/ext/mysqlnd/mysqlnd_ps.c:1.3.2.10 Thu Feb 14 14:50:21 2008 +++ php-src/ext/mysqlnd/mysqlnd_ps.c Thu Mar 20 14:03:30 2008 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps.c,v 1.3.2.10 2008/02/14 14:50:21 andrey Exp $ */ +/* $Id: mysqlnd_ps.c,v 1.3.2.11 2008/03/20 14:03:30 andrey Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -522,6 +522,7 @@ 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); http://cvs.php.net/viewvc.cgi/php-src/ext/mysqlnd/mysqlnd_ps_codec.c?r1=1.3.2.5&r2=1.3.2.6&diff_format=u Index: php-src/ext/mysqlnd/mysqlnd_ps_codec.c diff -u php-src/ext/mysqlnd/mysqlnd_ps_codec.c:1.3.2.5 php-src/ext/mysqlnd/mysqlnd_ps_codec.c:1.3.2.6 --- php-src/ext/mysqlnd/mysqlnd_ps_codec.c:1.3.2.5 Wed Jan 23 19:11:28 2008 +++ php-src/ext/mysqlnd/mysqlnd_ps_codec.c Thu Mar 20 14:03:30 2008 @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps_codec.c,v 1.3.2.5 2008/01/23 19:11:28 andrey Exp $ */ +/* $Id: mysqlnd_ps_codec.c,v 1.3.2.6 2008/03/20 14:03:30 andrey Exp $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -650,14 +650,30 @@ /* }}} */ +/* {{{ 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 @@ /* 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 @@ 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 @@ /* 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 @@ memcpy(*p, Z_STRVAL_P(data), len); (*p) += len; } - break; default: /* Won't happen, but set to NULL */ @@ -800,6 +850,14 @@ } } } + if (copies) { + for (i = 0; i < stmt->param_count; i++) { + if (copies[i]) { + zval_ptr_dtor(&copies[i]); + } + } + efree(copies); + } } /* }}} */ http://cvs.php.net/viewvc.cgi/php-src/ext/mysqli/tests/mysqli_stmt_datatype_change.phpt?view=markup&rev=1.1 Index: php-src/ext/mysqli/tests/mysqli_stmt_datatype_change.phpt +++ php-src/ext/mysqli/tests/mysqli_stmt_datatype_change.phpt --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!
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php