Skip to content

Commit 73008bb

Browse files
committed
More refactoring of the big function generating execute requests
1 parent cf99666 commit 73008bb

File tree

1 file changed

+164
-94
lines changed

1 file changed

+164
-94
lines changed

ext/mysqlnd/mysqlnd_ps_codec.c

+164-94
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,23 @@ mysqlnd_stmt_copy_it(zval *** copies, zval * original, unsigned int param_count,
516516
/* }}} */
517517

518518

519+
/* {{{ mysqlnd_stmt_free_copies */
520+
static void
521+
mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval ** copies TSRMLS_DC)
522+
{
523+
if (copies) {
524+
unsigned int i;
525+
for (i = 0; i < stmt->param_count; i++) {
526+
if (copies[i]) {
527+
zval_ptr_dtor(&copies[i]);
528+
}
529+
}
530+
mnd_efree(copies);
531+
}
532+
}
533+
/* }}} */
534+
535+
519536
/* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
520537
static enum_func_status
521538
mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, size_t * buf_len, zend_uchar * const provided_buffer, size_t needed_bytes TSRMLS_DC)
@@ -544,51 +561,24 @@ mysqlnd_stmt_execute_check_n_enlarge_buffer(zend_uchar **buf, zend_uchar **p, si
544561
/* }}} */
545562

546563

547-
/* {{{ mysqlnd_stmt_execute_store_params */
564+
/* {{{ mysqlnd_stmt_execute_prepare_param_types */
548565
static enum_func_status
549-
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t * buf_len TSRMLS_DC)
566+
mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval *** copies_param, int * resend_types_next_time TSRMLS_DC)
550567
{
551-
MYSQLND_STMT_DATA * stmt = s->data;
552-
unsigned int i = 0;
553-
zend_uchar * provided_buffer = *buf;
554-
size_t data_size = 0;
555-
zval **copies = NULL;/* if there are different types */
556-
enum_func_status ret = FAIL;
557-
int resend_types_next_time = 0;
558-
size_t null_byte_offset;
559-
560-
DBG_ENTER("mysqlnd_stmt_execute_store_params");
561-
562-
{
563-
unsigned int null_count = (stmt->param_count + 7) / 8;
564-
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count TSRMLS_CC)) {
565-
SET_OOM_ERROR(*stmt->error_info);
566-
goto end;
567-
}
568-
/* put `null` bytes */
569-
null_byte_offset = *p - *buf;
570-
memset(*p, 0, null_count);
571-
*p += null_count;
572-
}
573-
574-
/* 1. Store type information */
575-
/*
576-
check if need to send the types even if stmt->send_types_to_server is 0. This is because
577-
if we send "i" (42) then the type will be int and the server will expect int. However, if next
578-
time we try to send > LONG_MAX, the conversion to string will send a string and the server
579-
won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
580-
occur, and force resend for the next execution.
581-
*/
568+
unsigned int i;
569+
DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
582570
for (i = 0; i < stmt->param_count; i++) {
583571
short current_type = stmt->param_bind[i].type;
584572
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
573+
zval ** copies;
585574
/* always copy the var, because we do many conversions */
586575
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
587-
PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
576+
PASS != mysqlnd_stmt_copy_it(copies_param, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
588577
{
589578
SET_OOM_ERROR(*stmt->error_info);
590579
goto end;
591580
}
581+
copies = *copies_param;
592582
/*
593583
if it doesn't fit in a long send it as a string.
594584
Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
@@ -613,7 +603,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
613603
We do transformation here, which will be used later when sending types. The code later relies on this.
614604
*/
615605
if (Z_DVAL_P(tmp_data_copy) > LONG_MAX || Z_DVAL_P(tmp_data_copy) < LONG_MIN) {
616-
stmt->send_types_to_server = resend_types_next_time = 1;
606+
stmt->send_types_to_server = *resend_types_next_time = 1;
617607
convert_to_string_ex(&tmp_data);
618608
} else {
619609
convert_to_long_ex(&tmp_data);
@@ -623,54 +613,64 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
623613
}
624614
}
625615
}
616+
DBG_RETURN(PASS);
617+
end:
618+
DBG_RETURN(FAIL);
619+
}
620+
/* }}} */
626621

627-
int1store(*p, stmt->send_types_to_server);
628-
(*p)++;
629622

630-
if (stmt->send_types_to_server) {
631-
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2 TSRMLS_CC)) {
632-
SET_OOM_ERROR(*stmt->error_info);
633-
goto end;
634-
}
635-
for (i = 0; i < stmt->param_count; i++) {
636-
short current_type = stmt->param_bind[i].type;
637-
/* our types are not unsigned */
623+
/* {{{ mysqlnd_stmt_execute_store_types */
624+
static void
625+
mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt, zval ** copies, zend_uchar ** p)
626+
{
627+
unsigned int i;
628+
for (i = 0; i < stmt->param_count; i++) {
629+
short current_type = stmt->param_bind[i].type;
630+
/* our types are not unsigned */
638631
#if SIZEOF_LONG==8
639-
if (current_type == MYSQL_TYPE_LONG) {
640-
current_type = MYSQL_TYPE_LONGLONG;
641-
}
632+
if (current_type == MYSQL_TYPE_LONG) {
633+
current_type = MYSQL_TYPE_LONGLONG;
634+
}
642635
#endif
643-
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
636+
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
637+
/*
638+
if it doesn't fit in a long send it as a string.
639+
Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
640+
*/
641+
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
642+
const zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
644643
/*
645-
if it doesn't fit in a long send it as a string.
646-
Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
644+
In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
645+
The actual transformation has been performed several dozens line above.
647646
*/
648-
if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
649-
zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
647+
if (Z_TYPE_P(tmp_data) == IS_STRING) {
648+
current_type = MYSQL_TYPE_VAR_STRING;
650649
/*
651-
In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
652-
The actual transformation has been performed several dozens line above.
650+
don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
651+
we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
652+
if the type is however not long, then we will do a goto in the next switch.
653+
We want to preserve the original bind type given by the user. Thus, we do these hacks.
653654
*/
654-
if (Z_TYPE_P(tmp_data) == IS_STRING) {
655-
current_type = MYSQL_TYPE_VAR_STRING;
656-
/*
657-
don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
658-
we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
659-
if the type is however not long, then we will do a goto in the next switch.
660-
We want to preserve the original bind type given by the user. Thus, we do these hacks.
661-
*/
662-
}
663655
}
664656
}
665-
int2store(*p, current_type);
666-
*p+= 2;
667657
}
658+
int2store(*p, current_type);
659+
*p+= 2;
668660
}
669-
stmt->send_types_to_server = resend_types_next_time;
661+
}
662+
/* }}} */
663+
664+
665+
/* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
666+
static enum_func_status
667+
mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval *** copies_param, size_t * data_size TSRMLS_DC)
668+
{
669+
unsigned int i;
670+
DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
670671

671-
/* 2. Store data */
672-
/* 2.1 Calculate how much space we need */
673672
for (i = 0; i < stmt->param_count; i++) {
673+
zval ** copies;
674674
unsigned int j;
675675
zval *the_var = stmt->param_bind[i].zv;
676676

@@ -681,7 +681,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
681681
if (stmt->param_bind[j].zv == the_var) {
682682
/* Double binding of the same zval, make a copy */
683683
if (!copies || !copies[i]) {
684-
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
684+
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
685685
SET_OOM_ERROR(*stmt->error_info);
686686
goto end;
687687
}
@@ -690,12 +690,14 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
690690
}
691691
}
692692

693+
copies = *copies_param;
694+
693695
switch (stmt->param_bind[i].type) {
694696
case MYSQL_TYPE_DOUBLE:
695-
data_size += 8;
697+
*data_size += 8;
696698
if (Z_TYPE_P(the_var) != IS_DOUBLE) {
697699
if (!copies || !copies[i]) {
698-
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
700+
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
699701
SET_OOM_ERROR(*stmt->error_info);
700702
goto end;
701703
}
@@ -710,7 +712,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
710712
}
711713
convert_to_long_ex(&tmp_data);
712714
}
713-
data_size += 8;
715+
*data_size += 8;
714716
break;
715717
case MYSQL_TYPE_LONG:
716718
{
@@ -720,7 +722,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
720722
}
721723
convert_to_long_ex(&tmp_data);
722724
}
723-
data_size += 4;
725+
*data_size += 4;
724726
break;
725727
case MYSQL_TYPE_LONG_BLOB:
726728
if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
@@ -729,35 +731,41 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
729731
Empty string has length of 0, encoded in 1 byte. No real
730732
data will follows after it.
731733
*/
732-
data_size++;
734+
(*data_size)++;
733735
}
734736
break;
735737
case MYSQL_TYPE_VAR_STRING:
736738
use_string:
737-
data_size += 8; /* max 8 bytes for size */
739+
*data_size += 8; /* max 8 bytes for size */
738740
if (Z_TYPE_P(the_var) != IS_STRING) {
739741
if (!copies || !copies[i]) {
740-
if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
742+
if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i TSRMLS_CC)) {
741743
SET_OOM_ERROR(*stmt->error_info);
742744
goto end;
743745
}
744746
}
747+
copies = *copies_param;
745748
the_var = copies[i];
746749
}
747750
convert_to_string_ex(&the_var);
748-
data_size += Z_STRLEN_P(the_var);
751+
*data_size += Z_STRLEN_P(the_var);
749752
break;
750753
}
751754
}
752-
/* 2.2 Enlarge the buffer, if needed */
753-
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size TSRMLS_CC)) {
754-
SET_OOM_ERROR(*stmt->error_info);
755-
goto end;
756-
}
755+
DBG_RETURN(PASS);
756+
end:
757+
DBG_RETURN(FAIL);
758+
}
759+
/* }}} */
757760

758-
/* 2.3 Store the actual data */
761+
762+
/* {{{ mysqlnd_stmt_execute_store_param_values */
763+
static void
764+
mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval ** copies, zend_uchar ** buf, zend_uchar ** p, size_t null_byte_offset)
765+
{
766+
unsigned int i;
759767
for (i = 0; i < stmt->param_count; i++) {
760-
zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
768+
zval * data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
761769
/* Handle long data */
762770
if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
763771
(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
@@ -809,17 +817,79 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
809817
}
810818
}
811819
}
812-
ret = PASS;
813-
end:
814-
if (copies) {
815-
for (i = 0; i < stmt->param_count; i++) {
816-
if (copies[i]) {
817-
zval_ptr_dtor(&copies[i]);
818-
}
820+
}
821+
/* }}} */
822+
823+
824+
/* {{{ mysqlnd_stmt_execute_store_params */
825+
static enum_func_status
826+
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar ** buf, zend_uchar ** p, size_t * buf_len TSRMLS_DC)
827+
{
828+
MYSQLND_STMT_DATA * stmt = s->data;
829+
zend_uchar * provided_buffer = *buf;
830+
size_t data_size = 0;
831+
zval ** copies = NULL;/* if there are different types */
832+
enum_func_status ret = FAIL;
833+
int resend_types_next_time = 0;
834+
size_t null_byte_offset;
835+
836+
DBG_ENTER("mysqlnd_stmt_execute_store_params");
837+
838+
{
839+
unsigned int null_count = (stmt->param_count + 7) / 8;
840+
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count TSRMLS_CC)) {
841+
SET_OOM_ERROR(*stmt->error_info);
842+
goto end;
819843
}
820-
mnd_efree(copies);
844+
/* put `null` bytes */
845+
null_byte_offset = *p - *buf;
846+
memset(*p, 0, null_count);
847+
*p += null_count;
848+
}
849+
850+
/* 1. Store type information */
851+
/*
852+
check if need to send the types even if stmt->send_types_to_server is 0. This is because
853+
if we send "i" (42) then the type will be int and the server will expect int. However, if next
854+
time we try to send > LONG_MAX, the conversion to string will send a string and the server
855+
won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
856+
occur, and force resend for the next execution.
857+
*/
858+
if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time TSRMLS_CC)) {
859+
goto end;
860+
}
861+
862+
int1store(*p, stmt->send_types_to_server);
863+
(*p)++;
864+
865+
if (stmt->send_types_to_server) {
866+
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2 TSRMLS_CC)) {
867+
SET_OOM_ERROR(*stmt->error_info);
868+
goto end;
869+
}
870+
mysqlnd_stmt_execute_store_types(stmt, copies, p);
871+
}
872+
stmt->send_types_to_server = resend_types_next_time;
873+
874+
/* 2. Store actual data */
875+
/* 2.1 Calculate how much space we need */
876+
if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size TSRMLS_CC)) {
877+
goto end;
878+
}
879+
880+
/* 2.2 Enlarge the buffer, if needed */
881+
if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size TSRMLS_CC)) {
882+
SET_OOM_ERROR(*stmt->error_info);
883+
goto end;
821884
}
822885

886+
/* 2.3 Store the actual data */
887+
mysqlnd_stmt_execute_store_param_values(stmt, copies, buf, p, null_byte_offset);
888+
889+
ret = PASS;
890+
end:
891+
mysqlnd_stmt_free_copies(stmt, copies TSRMLS_CC);
892+
823893
DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
824894
DBG_RETURN(ret);
825895
}
@@ -828,7 +898,7 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar
828898

829899
/* {{{ mysqlnd_stmt_execute_generate_request */
830900
enum_func_status
831-
mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC)
901+
mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t * request_len, zend_bool * free_buffer TSRMLS_DC)
832902
{
833903
MYSQLND_STMT_DATA * stmt = s->data;
834904
zend_uchar *p = stmt->execute_cmd_buffer.buffer,

0 commit comments

Comments
 (0)