xref: /PHP-5.5/ext/mysqlnd/mysqlnd_ps_codec.c (revision 1933b556)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-2015 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 #include "ext/mysqlnd/mysql_float_to_double.h"
28 
29 #define MYSQLND_SILENT
30 
31 
32 enum mysqlnd_timestamp_type
33 {
34   MYSQLND_TIMESTAMP_NONE= -2,
35   MYSQLND_TIMESTAMP_ERROR= -1,
36   MYSQLND_TIMESTAMP_DATE= 0,
37   MYSQLND_TIMESTAMP_DATETIME= 1,
38   MYSQLND_TIMESTAMP_TIME= 2
39 };
40 
41 
42 struct st_mysqlnd_time
43 {
44   unsigned int  year, month, day, hour, minute, second;
45   unsigned long second_part;
46   zend_bool     neg;
47   enum mysqlnd_timestamp_type time_type;
48 };
49 
50 
51 struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1];
52 
53 #define MYSQLND_PS_SKIP_RESULT_W_LEN	-1
54 #define MYSQLND_PS_SKIP_RESULT_STR		-2
55 
56 /* {{{ ps_fetch_from_1_to_8_bytes */
57 void
ps_fetch_from_1_to_8_bytes(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,unsigned int byte_count TSRMLS_DC)58 ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len,
59 						   zend_uchar ** row, unsigned int byte_count TSRMLS_DC)
60 {
61 	char tmp[22];
62 	size_t tmp_len = 0;
63 	zend_bool is_bit = field->type == MYSQL_TYPE_BIT;
64 	DBG_ENTER("ps_fetch_from_1_to_8_bytes");
65 	DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count);
66 	if (field->flags & UNSIGNED_FLAG) {
67 		uint64_t uval = 0;
68 
69 		switch (byte_count) {
70 			case 8:uval = is_bit? (uint64_t) bit_uint8korr(*row):(uint64_t) uint8korr(*row);break;
71 			case 7:uval = bit_uint7korr(*row);break;
72 			case 6:uval = bit_uint6korr(*row);break;
73 			case 5:uval = bit_uint5korr(*row);break;
74 			case 4:uval = is_bit? (uint64_t) bit_uint4korr(*row):(uint64_t) uint4korr(*row);break;
75 			case 3:uval = is_bit? (uint64_t) bit_uint3korr(*row):(uint64_t) uint3korr(*row);break;
76 			case 2:uval = is_bit? (uint64_t) bit_uint2korr(*row):(uint64_t) uint2korr(*row);break;
77 			case 1:uval = (uint64_t) uint1korr(*row);break;
78 		}
79 
80 #if SIZEOF_LONG==4
81 		if (uval > INT_MAX) {
82 			DBG_INF("stringify");
83 			tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
84 		} else
85 #endif /* #if SIZEOF_LONG==4 */
86 		{
87 			if (byte_count < 8 || uval <= L64(9223372036854775807)) {
88 				ZVAL_LONG(zv, (long) uval); /* the cast is safe, we are in the range */
89 			} else {
90 				DBG_INF("stringify");
91 				tmp_len = sprintf((char *)&tmp, MYSQLND_LLU_SPEC, uval);
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_LONG==4
110 		if ((L64(2147483647) < (int64_t) lval) || (L64(-2147483648) > (int64_t) lval)) {
111 			DBG_INF("stringify");
112 			tmp_len = sprintf((char *)&tmp, MYSQLND_LL_SPEC, lval);
113 		} else
114 #endif /* SIZEOF */
115 		{
116 			ZVAL_LONG(zv, (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, 1);
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,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)131 ps_fetch_null(zval *zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
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,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)140 ps_fetch_int8(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
141 {
142 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 1 TSRMLS_CC);
143 }
144 /* }}} */
145 
146 
147 /* {{{ ps_fetch_int16 */
148 static void
ps_fetch_int16(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)149 ps_fetch_int16(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
150 {
151 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 2 TSRMLS_CC);
152 }
153 /* }}} */
154 
155 
156 /* {{{ ps_fetch_int32 */
157 static void
ps_fetch_int32(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)158 ps_fetch_int32(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
159 {
160 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 4 TSRMLS_CC);
161 }
162 /* }}} */
163 
164 
165 /* {{{ ps_fetch_int64 */
166 static void
ps_fetch_int64(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)167 ps_fetch_int64(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
168 {
169 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, 8 TSRMLS_CC);
170 }
171 /* }}} */
172 
173 
174 /* {{{ ps_fetch_float */
175 static void
ps_fetch_float(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)176 ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
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 : 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,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)199 ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
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,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)214 ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
215 {
216 	struct st_mysqlnd_time t;
217 	unsigned long 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 		zend_uchar * to= *row;
223 
224 		t.time_type = MYSQLND_TIMESTAMP_TIME;
225 		t.neg			= (zend_bool) to[0];
226 
227 		t.day			= (unsigned long) 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) ? (unsigned long) 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 	length = mnd_sprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
246 
247 	DBG_INF_FMT("%s", value);
248 	ZVAL_STRINGL(zv, value, length, 1);
249 	mnd_sprintf_free(value);
250 	DBG_VOID_RETURN;
251 }
252 /* }}} */
253 
254 
255 /* {{{ ps_fetch_date */
256 static void
ps_fetch_date(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)257 ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
258 {
259 	struct st_mysqlnd_time t = {0};
260 	unsigned long length; /* First byte encodes the length*/
261 	char * value;
262 	DBG_ENTER("ps_fetch_date");
263 
264 	if ((length = php_mysqlnd_net_field_length(row))) {
265 		zend_uchar *to= *row;
266 
267 		t.time_type= MYSQLND_TIMESTAMP_DATE;
268 		t.neg= 0;
269 
270 		t.second_part = t.hour = t.minute = t.second = 0;
271 
272 		t.year	= (unsigned int) sint2korr(to);
273 		t.month = (unsigned int) to[2];
274 		t.day	= (unsigned int) to[3];
275 
276 		(*row)+= length;
277 	} else {
278 		memset(&t, 0, sizeof(t));
279 		t.time_type = MYSQLND_TIMESTAMP_DATE;
280 	}
281 
282 	length = mnd_sprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
283 
284 	DBG_INF_FMT("%s", value);
285 	ZVAL_STRINGL(zv, value, length, 1);
286 	mnd_sprintf_free(value);
287 	DBG_VOID_RETURN;
288 }
289 /* }}} */
290 
291 
292 /* {{{ ps_fetch_datetime */
293 static void
ps_fetch_datetime(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)294 ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
295 {
296 	struct st_mysqlnd_time t;
297 	unsigned long length; /* First byte encodes the length*/
298 	char * value;
299 	DBG_ENTER("ps_fetch_datetime");
300 
301 	if ((length = php_mysqlnd_net_field_length(row))) {
302 		zend_uchar * to = *row;
303 
304 		t.time_type = MYSQLND_TIMESTAMP_DATETIME;
305 		t.neg	 = 0;
306 
307 		t.year	 = (unsigned int) sint2korr(to);
308 		t.month = (unsigned int) to[2];
309 		t.day	 = (unsigned int) to[3];
310 
311 		if (length > 4) {
312 			t.hour	 = (unsigned int) to[4];
313 			t.minute = (unsigned int) to[5];
314 			t.second = (unsigned int) to[6];
315 		} else {
316 			t.hour = t.minute = t.second= 0;
317 		}
318 		t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0;
319 
320 		(*row)+= length;
321 	} else {
322 		memset(&t, 0, sizeof(t));
323 		t.time_type = MYSQLND_TIMESTAMP_DATETIME;
324 	}
325 
326 	length = mnd_sprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u", t.year, t.month, t.day, t.hour, t.minute, t.second);
327 
328 	DBG_INF_FMT("%s", value);
329 	ZVAL_STRINGL(zv, value, length, 1);
330 	mnd_sprintf_free(value);
331 	DBG_VOID_RETURN;
332 }
333 /* }}} */
334 
335 
336 /* {{{ ps_fetch_string */
337 static void
ps_fetch_string(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)338 ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
339 {
340 	/*
341 	  For now just copy, before we make it possible
342 	  to write \0 to the row buffer
343 	*/
344 	const unsigned long length = php_mysqlnd_net_field_length(row);
345 	DBG_ENTER("ps_fetch_string");
346 	DBG_INF_FMT("len = %lu", length);
347 	DBG_INF("copying from the row buffer");
348 	ZVAL_STRINGL(zv, (char *)*row, length, 1);
349 
350 	(*row) += length;
351 	DBG_VOID_RETURN;
352 }
353 /* }}} */
354 
355 
356 /* {{{ ps_fetch_bit */
357 static void
ps_fetch_bit(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row TSRMLS_DC)358 ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_len, zend_uchar ** row TSRMLS_DC)
359 {
360 	unsigned long length = php_mysqlnd_net_field_length(row);
361 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length TSRMLS_CC);
362 }
363 /* }}} */
364 
365 
366 /* {{{ _mysqlnd_init_ps_fetch_subsystem */
_mysqlnd_init_ps_fetch_subsystem()367 void _mysqlnd_init_ps_fetch_subsystem()
368 {
369 	memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions));
370 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func		= ps_fetch_null;
371 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len	= 0;
372 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type	= IS_NULL;
373 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni	= TRUE;
374 
375 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func		= ps_fetch_int8;
376 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len	= 1;
377 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type	= IS_LONG;
378 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni	= TRUE;
379 
380 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func		= ps_fetch_int16;
381 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len	= 2;
382 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type	= IS_LONG;
383 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni	= TRUE;
384 
385 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func		= ps_fetch_int16;
386 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len	= 2;
387 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type	= IS_LONG;
388 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni	= TRUE;
389 
390 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func		= ps_fetch_int32;
391 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len	= 4;
392 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type	= IS_LONG;
393 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni	= TRUE;
394 
395 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func		= ps_fetch_int32;
396 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len	= 4;
397 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type	= IS_LONG;
398 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni	= TRUE;
399 
400 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func	= ps_fetch_int64;
401 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
402 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type= IS_LONG;
403 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni	= TRUE;
404 
405 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func		= ps_fetch_float;
406 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len	= 4;
407 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type	= IS_DOUBLE;
408 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni	= TRUE;
409 
410 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func		= ps_fetch_double;
411 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len	= 8;
412 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type	= IS_DOUBLE;
413 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni	= TRUE;
414 
415 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func		= ps_fetch_time;
416 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN;
417 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type	= IS_STRING;
418 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni	= TRUE;
419 
420 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func		= ps_fetch_date;
421 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN;
422 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type	= IS_STRING;
423 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni	= TRUE;
424 
425 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func		= ps_fetch_string;
426 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len	= MYSQLND_PS_SKIP_RESULT_W_LEN;
427 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type	= IS_STRING;
428 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni	= TRUE;
429 
430 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func	= ps_fetch_datetime;
431 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
432 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING;
433 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni	= TRUE;
434 
435 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func	= ps_fetch_datetime;
436 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
437 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING;
438 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni	= TRUE;
439 
440 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func	= ps_fetch_string;
441 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
442 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING;
443 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE;
444 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni	= TRUE;
445 
446 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func		= ps_fetch_string;
447 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
448 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type	= IS_STRING;
449 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE;
450 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni	= TRUE;
451 
452 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func		= ps_fetch_string;
453 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
454 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type	= IS_STRING;
455 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE;
456 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni	= TRUE;
457 
458 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func		= ps_fetch_string;
459 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
460 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type	= IS_STRING;
461 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE;
462 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni 	= TRUE;
463 
464 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func		= ps_fetch_bit;
465 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len	= 8;
466 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type	= IS_LONG;
467 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE;
468 
469 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func		= ps_fetch_string;
470 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
471 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING;
472 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE;
473 
474 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func		= ps_fetch_string;
475 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
476 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type	= IS_STRING;
477 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE;
478 
479 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func			= ps_fetch_string;
480 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len		= MYSQLND_PS_SKIP_RESULT_STR;
481 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type	= IS_STRING;
482 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE;
483 
484 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func		= ps_fetch_string;
485 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
486 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type	= IS_STRING;
487 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni	= TRUE;
488 
489 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func		= ps_fetch_string;
490 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
491 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type	= IS_STRING;
492 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni	= TRUE;
493 
494 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func		= ps_fetch_string;
495 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len	= MYSQLND_PS_SKIP_RESULT_STR;
496 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type	= IS_STRING;
497 
498 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func			= ps_fetch_string;
499 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len		= MYSQLND_PS_SKIP_RESULT_STR;
500 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type		= IS_STRING;
501 
502 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func	= ps_fetch_string;
503 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
504 	mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING;
505 }
506 /* }}} */
507 
508 
509 /* {{{ mysqlnd_stmt_copy_it */
510 static enum_func_status
mysqlnd_stmt_copy_it(zval *** copies,zval * original,unsigned int param_count,unsigned int current TSRMLS_DC)511 mysqlnd_stmt_copy_it(zval *** copies, zval * original, unsigned int param_count, unsigned int current TSRMLS_DC)
512 {
513 	if (!*copies) {
514 		*copies = mnd_ecalloc(param_count, sizeof(zval *));
515 	}
516 	if (*copies) {
517 		MAKE_STD_ZVAL((*copies)[current]);
518 		*(*copies)[current] = *original;
519 		Z_SET_REFCOUNT_P((*copies)[current], 1);
520 		zval_copy_ctor((*copies)[current]);
521 		return PASS;
522 	}
523 	return FAIL;
524 }
525 /* }}} */
526 
527 
528 /* {{{ mysqlnd_stmt_execute_store_params */
529 static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s,zend_uchar ** buf,zend_uchar ** p,size_t * buf_len TSRMLS_DC)530 mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len  TSRMLS_DC)
531 {
532 	MYSQLND_STMT_DATA * stmt = s->data;
533 	unsigned int i = 0;
534 	zend_uchar * provided_buffer = *buf;
535 	size_t left = (*buf_len - (*p - *buf));
536 	size_t data_size = 0;
537 	zval **copies = NULL;/* if there are different types */
538 	enum_func_status ret = FAIL;
539 	int resend_types_next_time = 0;
540 	size_t null_byte_offset;
541 
542 	DBG_ENTER("mysqlnd_stmt_execute_store_params");
543 
544 	{
545 		unsigned int null_count = (stmt->param_count + 7) / 8;
546 		/* give it some reserved space - 20 bytes */
547 		if (left < (null_count + 20)) {
548 			unsigned int offset = *p - *buf;
549 			zend_uchar *tmp_buf;
550 			*buf_len = offset + null_count + 20;
551 			tmp_buf = mnd_emalloc(*buf_len);
552 			if (!tmp_buf) {
553 				SET_OOM_ERROR(*stmt->error_info);
554 				goto end;
555 			}
556 			memcpy(tmp_buf, *buf, offset);
557 			if (*buf != provided_buffer) {
558 				mnd_efree(*buf);
559 			}
560 			*buf = tmp_buf;
561 
562 			/* Update our pos pointer */
563 			*p = *buf + offset;
564 		}
565 		/* put `null` bytes */
566 		null_byte_offset = *p - *buf;
567 		memset(*p, 0, null_count);
568 		*p += null_count;
569 	}
570 
571 	left = (*buf_len - (*p - *buf));
572 /* 1. Store type information */
573 	/*
574 	  check if need to send the types even if stmt->send_types_to_server is 0. This is because
575 	  if we send "i" (42) then the type will be int and the server will expect int. However, if next
576 	  time we try to send > LONG_MAX, the conversion to string will send a string and the server
577 	  won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
578 	  occur, and force resend for the next execution.
579 	*/
580 	for (i = 0; i < stmt->param_count; i++) {
581 		short current_type = stmt->param_bind[i].type;
582 		if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
583 			/* always copy the var, because we do many conversions */
584 			if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
585 				PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
586 			{
587 				SET_OOM_ERROR(*stmt->error_info);
588 				goto end;
589 			}
590 			/*
591 			  if it doesn't fit in a long send it as a string.
592 			  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
593 			*/
594 			if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
595 				zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
596 				/*
597 				  Because converting to double and back to long can lead
598 				  to losing precision we need second variable. Conversion to double is to see if
599 				  value is too big for a long. As said, precision could be lost.
600 				*/
601 				zval *tmp_data_copy;
602 				MAKE_STD_ZVAL(tmp_data_copy);
603 				*tmp_data_copy = *tmp_data;
604 				Z_SET_REFCOUNT_P(tmp_data_copy, 1);
605 				zval_copy_ctor(tmp_data_copy);
606 				convert_to_double_ex(&tmp_data_copy);
607 
608 				/*
609 				  if it doesn't fit in a long send it as a string.
610 				  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
611 				  We do transformation here, which will be used later when sending types. The code later relies on this.
612 				*/
613 				if (Z_DVAL_P(tmp_data_copy) > LONG_MAX || Z_DVAL_P(tmp_data_copy) < LONG_MIN) {
614 					stmt->send_types_to_server = resend_types_next_time = 1;
615 					convert_to_string_ex(&tmp_data);
616 				} else {
617 					convert_to_long_ex(&tmp_data);
618 				}
619 
620 				zval_ptr_dtor(&tmp_data_copy);
621 			}
622 		}
623 	}
624 
625 	int1store(*p, stmt->send_types_to_server);
626 	(*p)++;
627 
628 	if (stmt->send_types_to_server) {
629 		/* 2 bytes per type, and leave 20 bytes for future use */
630 		if (left < ((stmt->param_count * 2) + 20)) {
631 			unsigned int offset = *p - *buf;
632 			zend_uchar *tmp_buf;
633 			*buf_len = offset + stmt->param_count * 2 + 20;
634 			tmp_buf = mnd_emalloc(*buf_len);
635 			if (!tmp_buf) {
636 				SET_OOM_ERROR(*stmt->error_info);
637 				goto end;
638 			}
639 			memcpy(tmp_buf, *buf, offset);
640 			if (*buf != provided_buffer) {
641 				mnd_efree(*buf);
642 			}
643 			*buf = tmp_buf;
644 
645 			/* Update our pos pointer */
646 			*p = *buf + offset;
647 		}
648 		for (i = 0; i < stmt->param_count; i++) {
649 			short current_type = stmt->param_bind[i].type;
650 			/* our types are not unsigned */
651 #if SIZEOF_LONG==8
652 			if (current_type == MYSQL_TYPE_LONG) {
653 				current_type = MYSQL_TYPE_LONGLONG;
654 			}
655 #endif
656 			if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
657 				/*
658 				  if it doesn't fit in a long send it as a string.
659 				  Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
660 				*/
661 				if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
662 					zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
663 					/*
664 					  In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
665 					  The actual transformation has been performed several dozens line above.
666 					*/
667 					if (Z_TYPE_P(tmp_data) == IS_STRING) {
668 						current_type = MYSQL_TYPE_VAR_STRING;
669 						/*
670 						  don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
671 						  we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
672 						  if the type is however not long, then we will do a goto in the next switch.
673 						  We want to preserve the original bind type given by the user. Thus, we do these hacks.
674 						*/
675 					}
676 				}
677 			}
678 			int2store(*p, current_type);
679 			*p+= 2;
680 		}
681 	}
682 	stmt->send_types_to_server = resend_types_next_time;
683 
684 /* 2. Store data */
685 	/* 2.1 Calculate how much space we need */
686 	for (i = 0; i < stmt->param_count; i++) {
687 		unsigned int j;
688 		zval *the_var = stmt->param_bind[i].zv;
689 
690 		if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
691 			continue;
692 		}
693 		for (j = i + 1; j < stmt->param_count; j++) {
694 			if (stmt->param_bind[j].zv == the_var) {
695 				/* Double binding of the same zval, make a copy */
696 				if (!copies || !copies[i]) {
697 					if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
698 						SET_OOM_ERROR(*stmt->error_info);
699 						goto end;
700 					}
701 				}
702 				break;
703 			}
704 		}
705 
706 		switch (stmt->param_bind[i].type) {
707 			case MYSQL_TYPE_DOUBLE:
708 				data_size += 8;
709 				if (Z_TYPE_P(the_var) != IS_DOUBLE) {
710 					if (!copies || !copies[i]) {
711 						if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
712 							SET_OOM_ERROR(*stmt->error_info);
713 							goto end;
714 						}
715 					}
716 				}
717 				break;
718 			case MYSQL_TYPE_LONGLONG:
719 				{
720 					zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
721 					if (Z_TYPE_P(tmp_data) == IS_STRING) {
722 						goto use_string;
723 					}
724 					convert_to_long_ex(&tmp_data);
725 				}
726 				data_size += 8;
727 				break;
728 			case MYSQL_TYPE_LONG:
729 				{
730 					zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
731 					if (Z_TYPE_P(tmp_data) == IS_STRING) {
732 						goto use_string;
733 					}
734 					convert_to_long_ex(&tmp_data);
735 				}
736 				data_size += 4;
737 				break;
738 			case MYSQL_TYPE_LONG_BLOB:
739 				if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
740 					/*
741 					  User hasn't sent anything, we will send empty string.
742 					  Empty string has length of 0, encoded in 1 byte. No real
743 					  data will follows after it.
744 					*/
745 					data_size++;
746 				}
747 				break;
748 			case MYSQL_TYPE_VAR_STRING:
749 use_string:
750 				data_size += 8; /* max 8 bytes for size */
751 				if (Z_TYPE_P(the_var) != IS_STRING) {
752 					if (!copies || !copies[i]) {
753 						if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
754 							SET_OOM_ERROR(*stmt->error_info);
755 							goto end;
756 						}
757 					}
758 					the_var = copies[i];
759 				}
760 				convert_to_string_ex(&the_var);
761 				data_size += Z_STRLEN_P(the_var);
762 				break;
763 		}
764 	}
765 
766 	/* 2.2 Enlarge the buffer, if needed */
767 	left = (*buf_len - (*p - *buf));
768 	if (left < data_size) {
769 		unsigned int offset = *p - *buf;
770 		zend_uchar *tmp_buf;
771 		*buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
772 		tmp_buf = mnd_emalloc(*buf_len);
773 		if (!tmp_buf) {
774 			SET_OOM_ERROR(*stmt->error_info);
775 			goto end;
776 		}
777 		memcpy(tmp_buf, *buf, offset);
778 		/*
779 		  When too many columns the buffer provided to the function might not be sufficient.
780 		  In this case new buffer has been allocated above. When we allocate a buffer and then
781 		  allocate a bigger one here, we should free the first one.
782 		*/
783 		if (*buf != provided_buffer) {
784 			mnd_efree(*buf);
785 		}
786 		*buf = tmp_buf;
787 		/* Update our pos pointer */
788 		*p = *buf + offset;
789 	}
790 
791 	/* 2.3 Store the actual data */
792 	for (i = 0; i < stmt->param_count; i++) {
793 		zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
794 		/* Handle long data */
795 		if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
796 			(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
797 		} else {
798 			switch (stmt->param_bind[i].type) {
799 				case MYSQL_TYPE_DOUBLE:
800 					convert_to_double_ex(&data);
801 					float8store(*p, Z_DVAL_P(data));
802 					(*p) += 8;
803 					break;
804 				case MYSQL_TYPE_LONGLONG:
805 					if (Z_TYPE_P(data) == IS_STRING) {
806 						goto send_string;
807 					}
808 					/* data has alreade been converted to long */
809 					int8store(*p, Z_LVAL_P(data));
810 					(*p) += 8;
811 					break;
812 				case MYSQL_TYPE_LONG:
813 					if (Z_TYPE_P(data) == IS_STRING) {
814 						goto send_string;
815 					}
816 					/* data has alreade been converted to long */
817 					int4store(*p, Z_LVAL_P(data));
818 					(*p) += 4;
819 					break;
820 				case MYSQL_TYPE_LONG_BLOB:
821 					if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
822 						stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
823 					} else {
824 						/* send_long_data() not called, send empty string */
825 						*p = php_mysqlnd_net_store_length(*p, 0);
826 					}
827 					break;
828 				case MYSQL_TYPE_VAR_STRING:
829 send_string:
830 					{
831 						unsigned int len = Z_STRLEN_P(data);
832 						/* to is after p. The latter hasn't been moved */
833 						*p = php_mysqlnd_net_store_length(*p, len);
834 						memcpy(*p, Z_STRVAL_P(data), len);
835 						(*p) += len;
836 					}
837 					break;
838 				default:
839 					/* Won't happen, but set to NULL */
840 					(*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
841 					break;
842 			}
843 		}
844 	}
845 	ret = PASS;
846 end:
847 	if (copies) {
848 		for (i = 0; i < stmt->param_count; i++) {
849 			if (copies[i]) {
850 				zval_ptr_dtor(&copies[i]);
851 			}
852 		}
853 		mnd_efree(copies);
854 	}
855 
856 	DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
857 	DBG_RETURN(ret);
858 }
859 /* }}} */
860 
861 
862 /* {{{ mysqlnd_stmt_execute_generate_request */
863 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)864 mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC)
865 {
866 	MYSQLND_STMT_DATA * stmt = s->data;
867 	zend_uchar	*p = stmt->execute_cmd_buffer.buffer,
868 				*cmd_buffer = stmt->execute_cmd_buffer.buffer;
869 	size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
870 	enum_func_status ret;
871 
872 	DBG_ENTER("mysqlnd_stmt_execute_generate_request");
873 
874 	int4store(p, stmt->stmt_id);
875 	p += 4;
876 
877 	/* flags is 4 bytes, we store just 1 */
878 	int1store(p, (zend_uchar) stmt->flags);
879 	p++;
880 
881 	/* Make it all zero */
882 	int4store(p, 0);
883 
884 	int1store(p, 1); /* and send 1 for iteration count */
885 	p+= 4;
886 
887 	ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length TSRMLS_CC);
888 
889 	*free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
890 	*request_len = (p - cmd_buffer);
891 	*request = cmd_buffer;
892 	DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
893 	DBG_RETURN(ret);
894 }
895 /* }}} */
896 
897 /*
898  * Local variables:
899  * tab-width: 4
900  * c-basic-offset: 4
901  * End:
902  * vim600: noet sw=4 ts=4 fdm=marker
903  * vim<600: noet sw=4 ts=4
904  */
905