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