--- /dev/null
+--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!
if (hashed_details) {
mnd_efree(hashed_details);
}
+ errcode = CR_CONNECTION_ERROR;
goto err;
}
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*/
if (errstr) {
DBG_ERR_FMT("[%d] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme);
- SET_CLIENT_ERROR(conn->error_info, errcode, UNKNOWN_SQLSTATE, errstr);
-
+ 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;
}
{
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);
stmt->stmt_id = prepare_resp.stmt_id;
stmt->warning_count = stmt->conn->upsert_status.warning_count = prepare_resp.warning_count;
- stmt->field_count = stmt->conn->field_count = prepare_resp.field_count;
+ stmt->upsert_status.affected_rows = 0;
+ stmt->field_count = prepare_resp.field_count;
+ stmt->conn->field_count = 0;
stmt->param_count = prepare_resp.param_count;
PACKET_FREE_ALLOCA(prepare_resp);
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);
stmt->send_types_to_server = 0;
stmt->upsert_status = conn->upsert_status;
stmt->state = MYSQLND_STMT_EXECUTED;
- if (conn->last_query_type == QUERY_UPSERT) {
- stmt->upsert_status = conn->upsert_status;
- DBG_INF("PASS");
- DBG_RETURN(PASS);
- } else if (conn->last_query_type == QUERY_LOAD_LOCAL) {
+ if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) {
DBG_INF("PASS");
DBG_RETURN(PASS);
}
/* }}} */
+/* {{{ 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) {
/* 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)) {
/*
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;
}
/* 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));
memcpy(*p, Z_STRVAL_P(data), len);
(*p) += len;
}
-
break;
default:
/* Won't happen, but set to NULL */
}
}
}
+ if (copies) {
+ for (i = 0; i < stmt->param_count; i++) {
+ if (copies[i]) {
+ zval_ptr_dtor(&copies[i]);
+ }
+ }
+ efree(copies);
+ }
}
/* }}} */
conn->last_query_type = QUERY_SELECT;
CONN_SET_STATE(conn, CONN_FETCHING_DATA);
/* PS has already allocated it */
+ conn->field_count = rset_header.field_count;
if (!stmt) {
- conn->field_count = rset_header.field_count;
result =
conn->current_result=
mysqlnd_result_init(rset_header.field_count,
prepared statements can't send result set metadata for these queries
on prepare stage. Read it now.
*/
- conn->field_count = rset_header.field_count;
result =
stmt->result =
mysqlnd_result_init(rset_header.field_count,