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