Skip to content

Commit ce09822

Browse files
committed
And here is the real fix for #66124
1 parent 4a1c9be commit ce09822

File tree

1 file changed

+30
-11
lines changed

1 file changed

+30
-11
lines changed

ext/mysqlnd/mysqlnd_ps_codec.c

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,8 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
636636
occur, and force resend for the next execution.
637637
*/
638638
for (i = 0; i < stmt->param_count; i++) {
639-
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL &&
640-
(stmt->param_bind[i].type == MYSQL_TYPE_LONG || stmt->param_bind[i].type == MYSQL_TYPE_LONGLONG))
641-
{
639+
short current_type = stmt->param_bind[i].type;
640+
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
642641
/* always copy the var, because we do many conversions */
643642
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
644643
PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
@@ -652,10 +651,31 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
652651
*/
653652
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
654653
zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
655-
convert_to_double_ex(&tmp_data);
656-
if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
654+
/*
655+
Because converting to double and back to long can lead
656+
to losing precision we need second variable. Conversion to double is to see if
657+
value is too big for a long. As said, precision could be lost.
658+
*/
659+
zval *tmp_data_copy;
660+
MAKE_STD_ZVAL(tmp_data_copy);
661+
*tmp_data_copy = *tmp_data;
662+
Z_SET_REFCOUNT_P(tmp_data_copy, 1);
663+
zval_copy_ctor(tmp_data_copy);
664+
convert_to_double_ex(&tmp_data_copy);
665+
666+
/*
667+
if it doesn't fit in a long send it as a string.
668+
Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
669+
We do transformation here, which will be used later when sending types. The code later relies on this.
670+
*/
671+
if (Z_DVAL_P(tmp_data_copy) > LONG_MAX || Z_DVAL_P(tmp_data_copy) < LONG_MIN) {
657672
stmt->send_types_to_server = resend_types_next_time = 1;
673+
convert_to_string_ex(&tmp_data);
674+
} else {
675+
convert_to_long_ex(&tmp_data);
658676
}
677+
678+
zval_ptr_dtor(&tmp_data_copy);
659679
}
660680
}
661681
}
@@ -698,19 +718,18 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
698718
*/
699719
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
700720
zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
701-
702-
convert_to_double_ex(&tmp_data);
703-
if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
704-
convert_to_string_ex(&tmp_data);
721+
/*
722+
In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
723+
The actual transformation has been performed several dozens line above.
724+
*/
725+
if (Z_TYPE_P(tmp_data) == IS_STRING) {
705726
current_type = MYSQL_TYPE_VAR_STRING;
706727
/*
707728
don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
708729
we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
709730
if the type is however not long, then we will do a goto in the next switch.
710731
We want to preserve the original bind type given by the user. Thus, we do these hacks.
711732
*/
712-
} else {
713-
convert_to_long_ex(&tmp_data);
714733
}
715734
}
716735
}

0 commit comments

Comments
 (0)