xref: /PHP-8.0/ext/mysqlnd/mysqlnd_ps_codec.c (revision 682823b7)
1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | http://www.php.net/license/3_01.txt                                  |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Andrey Hristov <andrey@php.net>                             |
14   |          Ulf Wendel <uw@php.net>                                     |
15   +----------------------------------------------------------------------+
16 */
17 
18 #include <math.h>
19 #include "php.h"
20 #include "mysqlnd.h"
21 #include "mysqlnd_wireprotocol.h"
22 #include "mysqlnd_connection.h"
23 #include "mysqlnd_ps.h"
24 #include "mysqlnd_priv.h"
25 #include "mysqlnd_debug.h"
26 #include "mysql_float_to_double.h"
27 
28 
29 enum mysqlnd_timestamp_type
30 {
31   MYSQLND_TIMESTAMP_NONE= -2,
32   MYSQLND_TIMESTAMP_ERROR= -1,
33   MYSQLND_TIMESTAMP_DATE= 0,
34   MYSQLND_TIMESTAMP_DATETIME= 1,
35   MYSQLND_TIMESTAMP_TIME= 2
36 };
37 
38 
39 struct st_mysqlnd_time
40 {
41   unsigned int  year, month, day, hour, minute, second;
42   zend_ulong second_part;
43   zend_bool     neg;
44   enum mysqlnd_timestamp_type time_type;
45 };
46 
47 
48 struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
49 
50 #define MYSQLND_PS_SKIP_RESULT_W_LEN	-1
51 #define MYSQLND_PS_SKIP_RESULT_STR		-2
52 
53 /* {{{ ps_fetch_from_1_to_8_bytes */
54 void
ps_fetch_from_1_to_8_bytes(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row,unsigned int byte_count)55 ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
56 						   const zend_uchar ** row, unsigned int byte_count)
57 {
58 	char tmp[22];
59 	size_t tmp_len = 0;
60 	zend_bool is_bit = field->type == MYSQL_TYPE_BIT;
61 	DBG_ENTER("ps_fetch_from_1_to_8_bytes");
62 	DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count);
63 	if (field->flags & UNSIGNED_FLAG) {
64 		uint64_t uval = 0;
65 
66 		switch (byte_count) {
67 			case 8:uval = is_bit? (uint64_t) bit_uint8korr(*row):(uint64_t) uint8korr(*row);break;
68 			case 7:uval = bit_uint7korr(*row);break;
69 			case 6:uval = bit_uint6korr(*row);break;
70 			case 5:uval = bit_uint5korr(*row);break;
71 			case 4:uval = is_bit? (uint64_t) bit_uint4korr(*row):(uint64_t) uint4korr(*row);break;
72 			case 3:uval = is_bit? (uint64_t) bit_uint3korr(*row):(uint64_t) uint3korr(*row);break;
73 			case 2:uval = is_bit? (uint64_t) bit_uint2korr(*row):(uint64_t) uint2korr(*row);break;
74 			case 1:uval = (uint64_t) uint1korr(*row);break;
75 		}
76 
77 #if SIZEOF_ZEND_LONG==4
78 		if (uval > INT_MAX) {
79 			DBG_INF("stringify");
80 			tmp_len = sprintf((char *)&tmp, "%" PRIu64, uval);
81 		} else
82 #endif /* #if SIZEOF_LONG==4 */
83 		{
84 			if (byte_count < 8 || uval <= L64(9223372036854775807)) {
85 				ZVAL_LONG(zv, (zend_long) uval); /* the cast is safe, we are in the range */
86 			} else {
87 				DBG_INF("stringify");
88 				tmp_len = sprintf((char *)&tmp, "%" PRIu64, uval);
89 				DBG_INF_FMT("value=%s", tmp);
90 			}
91 		}
92 	} else {
93 		/* SIGNED */
94 		int64_t lval = 0;
95 		switch (byte_count) {
96 			case 8:lval = (int64_t) sint8korr(*row);break;
97 			/*
98 			  7, 6 and 5 are not possible.
99 			  BIT is only unsigned, thus only uint5|6|7 macros exist
100 			*/
101 			case 4:lval = (int64_t) sint4korr(*row);break;
102 			case 3:lval = (int64_t) sint3korr(*row);break;
103 			case 2:lval = (int64_t) sint2korr(*row);break;
104 			case 1:lval = (int64_t) *(int8_t*)*row;break;
105 		}
106 
107 #if SIZEOF_ZEND_LONG==4
108 		if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) {
109 			DBG_INF("stringify");
110 			tmp_len = sprintf((char *)&tmp, "%" PRIi64, lval);
111 		} else
112 #endif /* SIZEOF */
113 		{
114 			ZVAL_LONG(zv, (zend_long) lval); /* the cast is safe, we are in the range */
115 		}
116 	}
117 
118 	if (tmp_len) {
119 		ZVAL_STRINGL(zv, tmp, tmp_len);
120 	}
121 	(*row)+= byte_count;
122 	DBG_VOID_RETURN;
123 }
124 /* }}} */
125 
126 
127 /* {{{ ps_fetch_null */
128 static void
ps_fetch_null(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)129 ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
130 {
131 	ZVAL_NULL(zv);
132 }
133 /* }}} */
134 
135 
136 /* {{{ ps_fetch_int8 */
137 static void
ps_fetch_int8(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)138 ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
139 {
140 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 1);
141 }
142 /* }}} */
143 
144 
145 /* {{{ ps_fetch_int16 */
146 static void
ps_fetch_int16(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)147 ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
148 {
149 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 2);
150 }
151 /* }}} */
152 
153 
154 /* {{{ ps_fetch_int32 */
155 static void
ps_fetch_int32(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)156 ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
157 {
158 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 4);
159 }
160 /* }}} */
161 
162 
163 /* {{{ ps_fetch_int64 */
164 static void
ps_fetch_int64(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)165 ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
166 {
167 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 8);
168 }
169 /* }}} */
170 
171 
172 /* {{{ ps_fetch_float */
173 static void
ps_fetch_float(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)174 ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
175 {
176 	float fval;
177 	double dval;
178 	DBG_ENTER("ps_fetch_float");
179 	float4get(fval, *row);
180 	(*row)+= 4;
181 	DBG_INF_FMT("value=%f", fval);
182 
183 #ifndef NOT_FIXED_DEC
184 # define NOT_FIXED_DEC 31
185 #endif
186 
187 	dval = mysql_float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : (int)field->decimals);
188 
189 	ZVAL_DOUBLE(zv, dval);
190 	DBG_VOID_RETURN;
191 }
192 /* }}} */
193 
194 
195 /* {{{ ps_fetch_double */
196 static void
ps_fetch_double(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)197 ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
198 {
199 	double value;
200 	DBG_ENTER("ps_fetch_double");
201 	float8get(value, *row);
202 	ZVAL_DOUBLE(zv, value);
203 	(*row)+= 8;
204 	DBG_INF_FMT("value=%f", value);
205 	DBG_VOID_RETURN;
206 }
207 /* }}} */
208 
209 
210 /* {{{ ps_fetch_time */
211 static void
ps_fetch_time(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)212 ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
213 {
214 	struct st_mysqlnd_time t;
215 	zend_ulong length; /* First byte encodes the length*/
216 	char * value;
217 	DBG_ENTER("ps_fetch_time");
218 
219 	if ((length = php_mysqlnd_net_field_length(row))) {
220 		const zend_uchar * to = *row;
221 
222 		t.time_type = MYSQLND_TIMESTAMP_TIME;
223 		t.neg			= (zend_bool) to[0];
224 
225 		t.day			= (zend_ulong) sint4korr(to+1);
226 		t.hour			= (unsigned int) to[5];
227 		t.minute		= (unsigned int) to[6];
228 		t.second		= (unsigned int) to[7];
229 		t.second_part	= (length > 8) ? (zend_ulong) sint4korr(to+8) : 0;
230 		t.year			= t.month= 0;
231 		if (t.day) {
232 			/* Convert days to hours at once */
233 			t.hour += t.day*24;
234 			t.day	= 0;
235 		}
236 
237 		(*row) += length;
238 	} else {
239 		memset(&t, 0, sizeof(t));
240 		t.time_type = MYSQLND_TIMESTAMP_TIME;
241 	}
242 
243     if (field->decimals > 0 && field->decimals < 7) {
244         length = mnd_sprintf(
245             &value,
246             0,
247             "%s%02u:%02u:%02u.%0*u",
248             (t.neg ? "-" : ""),
249             t.hour,
250             t.minute,
251             t.second,
252             field->decimals,
253            (uint32_t) (t.second_part / pow(10, 6 - field->decimals))
254         );
255     } else {
256         length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
257     }
258 
259 	DBG_INF_FMT("%s", value);
260 	ZVAL_STRINGL(zv, value, length);
261 	mnd_sprintf_free(value);
262 	DBG_VOID_RETURN;
263 }
264 /* }}} */
265 
266 
267 /* {{{ ps_fetch_date */
268 static void
ps_fetch_date(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)269 ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
270 {
271 	struct st_mysqlnd_time t = {0};
272 	zend_ulong length; /* First byte encodes the length*/
273 	char * value;
274 	DBG_ENTER("ps_fetch_date");
275 
276 	if ((length = php_mysqlnd_net_field_length(row))) {
277 		const zend_uchar * to = *row;
278 
279 		t.time_type = MYSQLND_TIMESTAMP_DATE;
280 		t.neg = 0;
281 
282 		t.second_part = t.hour = t.minute = t.second = 0;
283 
284 		t.year	= (unsigned int) sint2korr(to);
285 		t.month = (unsigned int) to[2];
286 		t.day	= (unsigned int) to[3];
287 
288 		(*row)+= length;
289 	} else {
290 		memset(&t, 0, sizeof(t));
291 		t.time_type = MYSQLND_TIMESTAMP_DATE;
292 	}
293 
294 	length = mnd_sprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
295 
296 	DBG_INF_FMT("%s", value);
297 	ZVAL_STRINGL(zv, value, length);
298 	mnd_sprintf_free(value);
299 	DBG_VOID_RETURN;
300 }
301 /* }}} */
302 
303 
304 /* {{{ ps_fetch_datetime */
305 static void
ps_fetch_datetime(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)306 ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
307 {
308 	struct st_mysqlnd_time t;
309 	zend_ulong length; /* First byte encodes the length*/
310 	char * value;
311 	DBG_ENTER("ps_fetch_datetime");
312 
313 	if ((length = php_mysqlnd_net_field_length(row))) {
314 		const zend_uchar * to = *row;
315 
316 		t.time_type = MYSQLND_TIMESTAMP_DATETIME;
317 		t.neg = 0;
318 
319 		t.year	 = (unsigned int) sint2korr(to);
320 		t.month = (unsigned int) to[2];
321 		t.day	 = (unsigned int) to[3];
322 
323 		if (length > 4) {
324 			t.hour	 = (unsigned int) to[4];
325 			t.minute = (unsigned int) to[5];
326 			t.second = (unsigned int) to[6];
327 		} else {
328 			t.hour = t.minute = t.second= 0;
329 		}
330 		t.second_part = (length > 7) ? (zend_ulong) sint4korr(to+7) : 0;
331 
332 		(*row)+= length;
333 	} else {
334 		memset(&t, 0, sizeof(t));
335 		t.time_type = MYSQLND_TIMESTAMP_DATETIME;
336 	}
337 
338     if (field->decimals > 0 && field->decimals < 7) {
339     	length = mnd_sprintf(
340             &value,
341             0,
342             "%04u-%02u-%02u %02u:%02u:%02u.%0*u",
343             t.year,
344             t.month,
345             t.day,
346             t.hour,
347             t.minute,
348             t.second,
349             field->decimals,
350             (uint32_t) (t.second_part / pow(10, 6 - field->decimals))
351         );
352     } else {
353     	length = mnd_sprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second);
354     }
355 
356 	DBG_INF_FMT("%s", value);
357 	ZVAL_STRINGL(zv, value, length);
358 	mnd_sprintf_free(value);
359 	DBG_VOID_RETURN;
360 }
361 /* }}} */
362 
363 
364 /* {{{ ps_fetch_string */
365 static void
ps_fetch_string(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)366 ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
367 {
368 	/*
369 	  For now just copy, before we make it possible
370 	  to write \0 to the row buffer
371 	*/
372 	const zend_ulong length = php_mysqlnd_net_field_length(row);
373 	DBG_ENTER("ps_fetch_string");
374 	DBG_INF_FMT("len = %lu", length);
375 	DBG_INF("copying from the row buffer");
376 	ZVAL_STRINGL(zv, (char *)*row, length);
377 
378 	(*row) += length;
379 	DBG_VOID_RETURN;
380 }
381 /* }}} */
382 
383 
384 /* {{{ ps_fetch_bit */
385 static void
ps_fetch_bit(zval * zv,const MYSQLND_FIELD * const field,const unsigned int pack_len,const zend_uchar ** row)386 ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
387 {
388 	const zend_ulong length = php_mysqlnd_net_field_length(row);
389 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length);
390 }
391 /* }}} */
392 
393 
394 /* {{{ _mysqlnd_init_ps_fetch_subsystem */
_mysqlnd_init_ps_fetch_subsystem()395 void _mysqlnd_init_ps_fetch_subsystem()
396 {
397 	memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions));
398 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func		= ps_fetch_null;
399 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len	= 0;
400 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type	= IS_NULL;
401 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni	= TRUE;
402 
403 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func		= ps_fetch_int8;
404 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len	= 1;
405 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type	= IS_LONG;
406 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni	= TRUE;
407 
408 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func		= ps_fetch_int16;
409 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len	= 2;
410 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type	= IS_LONG;
411 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni	= TRUE;
412 
413 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func		= ps_fetch_int16;
414 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len	= 2;
415 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type	= IS_LONG;
416 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni	= TRUE;
417 
418 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func		= ps_fetch_int32;
419 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len	= 4;
420 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type	= IS_LONG;
421 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni	= TRUE;
422 
423 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func		= ps_fetch_int32;
424 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len	= 4;
425 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type	= IS_LONG;
426 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni	= TRUE;
427 
428 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func	= ps_fetch_int64;
429 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
430 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type= IS_LONG;
431 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni	= TRUE;
432 
433 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func		= ps_fetch_float;
434 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len	= 4;
435 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type	= IS_DOUBLE;
436 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni	= TRUE;
437 
438 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func		= ps_fetch_double;
439 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len	= 8;
440 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type	= IS_DOUBLE;
441 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni	= TRUE;
442 
443 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func		= ps_fetch_time;
444 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN;
445 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type	= IS_STRING;
446 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni	= TRUE;
447 
448 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func		= ps_fetch_date;
449 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN;
450 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type	= IS_STRING;
451 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni	= TRUE;
452 
453 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func		= ps_fetch_string;
454 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN;
455 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type	= IS_STRING;
456 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni	= TRUE;
457 
458 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func	= ps_fetch_datetime;
459 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
460 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING;
461 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni	= TRUE;
462 
463 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func	= ps_fetch_datetime;
464 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
465 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING;
466 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni	= TRUE;
467 
468 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].func	= ps_fetch_string;
469 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
470 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].php_type = IS_STRING;
471 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].is_possibly_blob = TRUE;
472 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].can_ret_as_str_in_uni	= TRUE;
473 
474 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func	= ps_fetch_string;
475 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
476 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING;
477 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE;
478 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni	= TRUE;
479 
480 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func		= ps_fetch_string;
481 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
482 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type	= IS_STRING;
483 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE;
484 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni	= TRUE;
485 
486 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func		= ps_fetch_string;
487 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
488 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type	= IS_STRING;
489 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE;
490 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni	= TRUE;
491 
492 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func		= ps_fetch_string;
493 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
494 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type	= IS_STRING;
495 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE;
496 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni 	= TRUE;
497 
498 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func		= ps_fetch_bit;
499 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len	= 8;
500 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type	= IS_LONG;
501 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE;
502 
503 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func		= ps_fetch_string;
504 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
505 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING;
506 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE;
507 
508 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func		= ps_fetch_string;
509 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
510 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type	= IS_STRING;
511 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE;
512 
513 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func			= ps_fetch_string;
514 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len		= MYSQLND_PS_SKIP_RESULT_STR;
515 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type	= IS_STRING;
516 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE;
517 
518 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func		= ps_fetch_string;
519 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
520 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type	= IS_STRING;
521 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni	= TRUE;
522 
523 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func		= ps_fetch_string;
524 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
525 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type	= IS_STRING;
526 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni	= TRUE;
527 
528 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func		= ps_fetch_string;
529 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
530 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type	= IS_STRING;
531 
532 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func			= ps_fetch_string;
533 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len		= MYSQLND_PS_SKIP_RESULT_STR;
534 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type		= IS_STRING;
535 
536 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func	= ps_fetch_string;
537 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
538 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING;
539 }
540 /* }}} */
541 
542 
543 /* {{{ mysqlnd_stmt_copy_it */
544 static enum_func_status
mysqlnd_stmt_copy_it(zval ** copies,zval * original,unsigned int param_count,unsigned int current)545 mysqlnd_stmt_copy_it(zval ** copies, zval * original, unsigned int param_count, unsigned int current)
546 {
547 	if (!*copies) {
548 		*copies = mnd_ecalloc(param_count, sizeof(zval));
549 	}
550 	if (*copies) {
551 		ZVAL_COPY(&(*copies)[current], original);
552 		return PASS;
553 	}
554 	return FAIL;
555 }
556 /* }}} */
557 
558 
559 /* {{{ mysqlnd_stmt_free_copies */
560 static void
mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt,zval * copies)561 mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval *copies)
562 {
563 	if (copies) {
564 		unsigned int i;
565 		for (i = 0; i < stmt->param_count; i++) {
566 			zval_ptr_dtor(&copies[i]);
567 		}
568 		mnd_efree(copies);
569 	}
570 }
571 /* }}} */
572 
573 
574 /* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
575 static enum_func_status
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)576 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)
577 {
578 	const size_t overalloc = 5;
579 	size_t left = (*buf_len - (*p - *buf));
580 
581 	if (left < (needed_bytes + overalloc)) {
582 		const size_t offset = *p - *buf;
583 		zend_uchar *tmp_buf;
584 		*buf_len = offset + needed_bytes + overalloc;
585 		tmp_buf = mnd_emalloc(*buf_len);
586 		if (!tmp_buf) {
587 			return FAIL;
588 		}
589 		memcpy(tmp_buf, *buf, offset);
590 		if (*buf != provided_buffer) {
591 			mnd_efree(*buf);
592 		}
593 		*buf = tmp_buf;
594 		/* Update our pos pointer */
595 		*p = *buf + offset;
596 	}
597 	return PASS;
598 }
599 /* }}} */
600 
601 
602 /* {{{ mysqlnd_stmt_execute_prepare_param_types */
603 static enum_func_status
mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt,zval ** copies_param,int * resend_types_next_time)604 mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copies_param, int * resend_types_next_time)
605 {
606 	unsigned int i;
607 	DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
608 	for (i = 0; i < stmt->param_count; i++) {
609 		const short current_type = stmt->param_bind[i].type;
610 		zval *parameter = &stmt->param_bind[i].zv;
611 
612 		ZVAL_DEREF(parameter);
613 		if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG || current_type == MYSQL_TYPE_TINY)) {
614 			/* always copy the var, because we do many conversions */
615 			if (Z_TYPE_P(parameter) != IS_LONG &&
616 				PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
617 			{
618 				SET_OOM_ERROR(stmt->error_info);
619 				goto end;
620 			}
621 			/*
622 			  if it doesn't fit in a long send it as a string.
623 			  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
624 			*/
625 			if (Z_TYPE_P(parameter) != IS_LONG) {
626 				zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: parameter;
627 				/*
628 				  Because converting to double and back to long can lead
629 				  to losing precision we need second variable. Conversion to double is to see if
630 				  value is too big for a long. As said, precision could be lost.
631 				*/
632 				double d = zval_get_double(tmp_data);
633 
634 				/*
635 				  if it doesn't fit in a long send it as a string.
636 				  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
637 				  We do transformation here, which will be used later when sending types. The code later relies on this.
638 				*/
639 				if (d >= (double) ZEND_LONG_MAX || d < (double) ZEND_LONG_MIN) {
640 					stmt->send_types_to_server = *resend_types_next_time = 1;
641 					convert_to_string_ex(tmp_data);
642 				} else {
643 					convert_to_long(tmp_data);
644 				}
645 			}
646 		}
647 	}
648 	DBG_RETURN(PASS);
649 end:
650 	DBG_RETURN(FAIL);
651 }
652 /* }}} */
653 
654 
655 /* {{{ mysqlnd_stmt_execute_store_types */
656 static void
mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt,zval * copies,zend_uchar ** p)657 mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt, zval * copies, zend_uchar ** p)
658 {
659 	unsigned int i;
660 	for (i = 0; i < stmt->param_count; i++) {
661 		short current_type = stmt->param_bind[i].type;
662 		zval *parameter = &stmt->param_bind[i].zv;
663 		/* our types are not unsigned */
664 #if SIZEOF_ZEND_LONG==8
665 		if (current_type == MYSQL_TYPE_LONG) {
666 			current_type = MYSQL_TYPE_LONGLONG;
667 		}
668 #endif
669 		ZVAL_DEREF(parameter);
670 		if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
671 			/*
672 			  if it doesn't fit in a long send it as a string.
673 			  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
674 			*/
675 			if (Z_TYPE_P(parameter) != IS_LONG) {
676 				const zval *tmp_data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i] : parameter;
677 				/*
678 				  In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
679 				  The actual transformation has been performed several dozens line above.
680 				*/
681 				if (Z_TYPE_P(tmp_data) == IS_STRING) {
682 					current_type = MYSQL_TYPE_VAR_STRING;
683 					/*
684 					  don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
685 					  we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
686 					  if the type is however not long, then we will do a goto in the next switch.
687 					  We want to preserve the original bind type given by the user. Thus, we do these hacks.
688 					*/
689 				}
690 			}
691 		}
692 		int2store(*p, current_type);
693 		*p+= 2;
694 	}
695 }
696 /* }}} */
697 
698 
699 /* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
700 static enum_func_status
mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt,zval ** copies_param,size_t * data_size)701 mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval ** copies_param, size_t * data_size)
702 {
703 	unsigned int i;
704 	DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
705 	for (i = 0; i < stmt->param_count; i++) {
706 		unsigned short is_longlong = 0;
707 		unsigned int j;
708 		zval *bind_var, *the_var = &stmt->param_bind[i].zv;
709 
710 		bind_var = the_var;
711 		ZVAL_DEREF(the_var);
712 		if ((stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
713 			continue;
714 		}
715 
716 		if (Z_ISREF_P(bind_var)) {
717 			for (j = i + 1; j < stmt->param_count; j++) {
718 				if (Z_ISREF(stmt->param_bind[j].zv) && Z_REFVAL(stmt->param_bind[j].zv) == the_var) {
719 					/* Double binding of the same zval, make a copy */
720 					if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
721 						if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
722 							SET_OOM_ERROR(stmt->error_info);
723 							goto end;
724 						}
725 					}
726 					break;
727 				}
728 			}
729 		}
730 
731 		switch (stmt->param_bind[i].type) {
732 			case MYSQL_TYPE_DOUBLE:
733 				*data_size += 8;
734 				if (Z_TYPE_P(the_var) != IS_DOUBLE) {
735 					if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
736 						if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
737 							SET_OOM_ERROR(stmt->error_info);
738 							goto end;
739 						}
740 					}
741 				}
742 				break;
743 			case MYSQL_TYPE_LONGLONG:
744 				is_longlong = 4;
745 				/* fall-through */
746 			case MYSQL_TYPE_LONG:
747 				{
748 					zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: the_var;
749 					if (Z_TYPE_P(tmp_data) == IS_STRING) {
750 						goto use_string;
751 					}
752 					convert_to_long_ex(tmp_data);
753 				}
754 				*data_size += 4 + is_longlong;
755 				break;
756 			case MYSQL_TYPE_LONG_BLOB:
757 				if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
758 					/*
759 					  User hasn't sent anything, we will send empty string.
760 					  Empty string has length of 0, encoded in 1 byte. No real
761 					  data will follows after it.
762 					*/
763 					(*data_size)++;
764 				}
765 				break;
766 			case MYSQL_TYPE_VAR_STRING:
767 use_string:
768 				*data_size += 8; /* max 8 bytes for size */
769 				if (Z_TYPE_P(the_var) != IS_STRING) {
770 					if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
771 						if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
772 							SET_OOM_ERROR(stmt->error_info);
773 							goto end;
774 						}
775 					}
776 					the_var = &((*copies_param)[i]);
777 				}
778 
779 				if (!try_convert_to_string(the_var)) {
780 					goto end;
781 				}
782 				*data_size += Z_STRLEN_P(the_var);
783 				break;
784 		}
785 	}
786 	DBG_RETURN(PASS);
787 end:
788 	DBG_RETURN(FAIL);
789 }
790 /* }}} */
791 
792 
793 /* {{{ mysqlnd_stmt_execute_store_param_values */
794 static void
mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt,zval * copies,zend_uchar * buf,zend_uchar ** p,size_t null_byte_offset)795 mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval * copies, zend_uchar * buf, zend_uchar ** p, size_t null_byte_offset)
796 {
797 	unsigned int i;
798 	for (i = 0; i < stmt->param_count; i++) {
799 		zval *data, *parameter = &stmt->param_bind[i].zv;
800 
801 		ZVAL_DEREF(parameter);
802 		data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i]: parameter;
803 		/* Handle long data */
804 		if (!Z_ISUNDEF_P(parameter) && Z_TYPE_P(data) == IS_NULL) {
805 			(buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
806 		} else {
807 			switch (stmt->param_bind[i].type) {
808 				case MYSQL_TYPE_DOUBLE:
809 					convert_to_double_ex(data);
810 					float8store(*p, Z_DVAL_P(data));
811 					(*p) += 8;
812 					break;
813 				case MYSQL_TYPE_LONGLONG:
814 					if (Z_TYPE_P(data) == IS_STRING) {
815 						goto send_string;
816 					}
817 					/* data has alreade been converted to long */
818 					int8store(*p, Z_LVAL_P(data));
819 					(*p) += 8;
820 					break;
821 				case MYSQL_TYPE_LONG:
822 					if (Z_TYPE_P(data) == IS_STRING) {
823 						goto send_string;
824 					}
825 					/* data has alreade been converted to long */
826 					int4store(*p, Z_LVAL_P(data));
827 					(*p) += 4;
828 					break;
829 				case MYSQL_TYPE_TINY:
830 					if (Z_TYPE_P(data) == IS_STRING) {
831 						goto send_string;
832 					}
833 					int1store(*p, Z_LVAL_P(data));
834 					(*p)++;
835 					break;
836 				case MYSQL_TYPE_LONG_BLOB:
837 					if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
838 						stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
839 					} else {
840 						/* send_long_data() not called, send empty string */
841 						*p = php_mysqlnd_net_store_length(*p, 0);
842 					}
843 					break;
844 				case MYSQL_TYPE_VAR_STRING:
845 send_string:
846 					{
847 						const size_t len = Z_STRLEN_P(data);
848 						/* to is after p. The latter hasn't been moved */
849 						*p = php_mysqlnd_net_store_length(*p, len);
850 						memcpy(*p, Z_STRVAL_P(data), len);
851 						(*p) += len;
852 					}
853 					break;
854 				default:
855 					/* Won't happen, but set to NULL */
856 					(buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
857 					break;
858 			}
859 		}
860 	}
861 }
862 /* }}} */
863 
864 
865 /* {{{ mysqlnd_stmt_execute_store_params */
866 static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s,zend_uchar ** buf,zend_uchar ** p,size_t * buf_len)867 mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len )
868 {
869 	MYSQLND_STMT_DATA * stmt = s->data;
870 	zend_uchar * provided_buffer = *buf;
871 	size_t data_size = 0;
872 	zval *copies = NULL;/* if there are different types */
873 	enum_func_status ret = FAIL;
874 	int resend_types_next_time = 0;
875 	size_t null_byte_offset;
876 
877 	DBG_ENTER("mysqlnd_stmt_execute_store_params");
878 
879 	{
880 		unsigned int null_count = (stmt->param_count + 7) / 8;
881 		if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count)) {
882 			SET_OOM_ERROR(stmt->error_info);
883 			goto end;
884 		}
885 		/* put `null` bytes */
886 		null_byte_offset = *p - *buf;
887 		memset(*p, 0, null_count);
888 		*p += null_count;
889 	}
890 
891 /* 1. Store type information */
892 	/*
893 	  check if need to send the types even if stmt->send_types_to_server is 0. This is because
894 	  if we send "i" (42) then the type will be int and the server will expect int. However, if next
895 	  time we try to send > LONG_MAX, the conversion to string will send a string and the server
896 	  won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
897 	  occur, and force resend for the next execution.
898 	*/
899 	if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time)) {
900 		goto end;
901 	}
902 
903 	int1store(*p, stmt->send_types_to_server);
904 	(*p)++;
905 
906 	if (stmt->send_types_to_server) {
907 		if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2)) {
908 			SET_OOM_ERROR(stmt->error_info);
909 			goto end;
910 		}
911 		mysqlnd_stmt_execute_store_types(stmt, copies, p);
912 	}
913 
914 	stmt->send_types_to_server = resend_types_next_time;
915 
916 /* 2. Store data */
917 	/* 2.1 Calculate how much space we need */
918 	if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size)) {
919 		goto end;
920 	}
921 
922 	/* 2.2 Enlarge the buffer, if needed */
923 	if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size)) {
924 		SET_OOM_ERROR(stmt->error_info);
925 		goto end;
926 	}
927 
928 	/* 2.3 Store the actual data */
929 	mysqlnd_stmt_execute_store_param_values(stmt, copies, *buf, p, null_byte_offset);
930 
931 	ret = PASS;
932 end:
933 	mysqlnd_stmt_free_copies(stmt, copies);
934 
935 	DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
936 	DBG_RETURN(ret);
937 }
938 /* }}} */
939 
940 
941 /* {{{ mysqlnd_stmt_execute_generate_request */
942 enum_func_status
mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s,zend_uchar ** request,size_t * request_len,zend_bool * free_buffer)943 mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer)
944 {
945 	MYSQLND_STMT_DATA * stmt = s->data;
946 	zend_uchar	*p = stmt->execute_cmd_buffer.buffer,
947 				*cmd_buffer = stmt->execute_cmd_buffer.buffer;
948 	size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
949 	enum_func_status ret = PASS;
950 
951 	DBG_ENTER("mysqlnd_stmt_execute_generate_request");
952 
953 	int4store(p, stmt->stmt_id);
954 	p += 4;
955 
956 	/* flags is 4 bytes, we store just 1 */
957 	int1store(p, (zend_uchar) stmt->flags);
958 	p++;
959 
960 	/* Make it all zero */
961 	int4store(p, 0);
962 
963 	int1store(p, 1); /* and send 1 for iteration count */
964 	p+= 4;
965 
966 	if (stmt->param_count != 0) {
967 	    ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length);
968 	}
969 
970 	*free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
971 	*request_len = (p - cmd_buffer);
972 	*request = cmd_buffer;
973 	DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
974 	DBG_RETURN(ret);
975 }
976 /* }}} */
977