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