@@ -636,9 +636,8 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
636
636
occur, and force resend for the next execution.
637
637
*/
638
638
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 )) {
642
641
/* always copy the var, because we do many conversions */
643
642
if (Z_TYPE_P (stmt -> param_bind [i ].zv ) != IS_LONG &&
644
643
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
652
651
*/
653
652
if (Z_TYPE_P (stmt -> param_bind [i ].zv ) != IS_LONG ) {
654
653
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 ) {
657
672
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 );
658
676
}
677
+
678
+ zval_ptr_dtor (& tmp_data_copy );
659
679
}
660
680
}
661
681
}
@@ -698,19 +718,18 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
698
718
*/
699
719
if (Z_TYPE_P (stmt -> param_bind [i ].zv ) != IS_LONG ) {
700
720
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 ) {
705
726
current_type = MYSQL_TYPE_VAR_STRING ;
706
727
/*
707
728
don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
708
729
we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
709
730
if the type is however not long, then we will do a goto in the next switch.
710
731
We want to preserve the original bind type given by the user. Thus, we do these hacks.
711
732
*/
712
- } else {
713
- convert_to_long_ex (& tmp_data );
714
733
}
715
734
}
716
735
}
0 commit comments