1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2013 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: Georg Richter <georg@mysql.com> |
16 | Andrey Hristov <andrey@mysql.com> |
17 | Ulf Wendel <uwendel@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 value;
199 DBG_ENTER("ps_fetch_float");
200 float4get(value, *row);
201 ZVAL_DOUBLE(zv, value);
202 (*row)+= 4;
203 DBG_INF_FMT("value=%f", value);
204 DBG_VOID_RETURN;
205 }
206 /* }}} */
207
208
209 /* {{{ ps_fetch_double */
210 static
ps_fetch_double(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,zend_bool as_unicode TSRMLS_DC)211 void ps_fetch_double(zval *zv, const MYSQLND_FIELD * const field,
212 unsigned int pack_len, zend_uchar **row,
213 zend_bool as_unicode TSRMLS_DC)
214 {
215 double value;
216 DBG_ENTER("ps_fetch_double");
217 float8get(value, *row);
218 ZVAL_DOUBLE(zv, value);
219 (*row)+= 8;
220 DBG_INF_FMT("value=%f", value);
221 DBG_VOID_RETURN;
222 }
223 /* }}} */
224
225
226 /* {{{ ps_fetch_time */
227 static
ps_fetch_time(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,zend_bool as_unicode TSRMLS_DC)228 void ps_fetch_time(zval *zv, const MYSQLND_FIELD * const field,
229 unsigned int pack_len, zend_uchar **row,
230 zend_bool as_unicode TSRMLS_DC)
231 {
232 struct st_mysqlnd_time t;
233 unsigned int length; /* First byte encodes the length*/
234 char * value;
235 DBG_ENTER("ps_fetch_time");
236
237 if ((length = php_mysqlnd_net_field_length(row))) {
238 zend_uchar *to= *row;
239
240 t.time_type = MYSQLND_TIMESTAMP_TIME;
241 t.neg = (zend_bool) to[0];
242
243 t.day = (unsigned long) sint4korr(to+1);
244 t.hour = (unsigned int) to[5];
245 t.minute = (unsigned int) to[6];
246 t.second = (unsigned int) to[7];
247 t.second_part = (length > 8) ? (unsigned long) sint4korr(to+8) : 0;
248 t.year = t.month= 0;
249 if (t.day) {
250 /* Convert days to hours at once */
251 t.hour += t.day*24;
252 t.day = 0;
253 }
254
255 (*row) += length;
256 } else {
257 memset(&t, 0, sizeof(t));
258 t.time_type = MYSQLND_TIMESTAMP_TIME;
259 }
260
261 /*
262 QQ : How to make this unicode without copying two times the buffer -
263 Unicode equivalent of spprintf?
264 */
265 length = spprintf(&value, 0, "%s%02u:%02u:%02u", (t.neg ? "-" : ""), t.hour, t.minute, t.second);
266
267 DBG_INF_FMT("%s", value);
268 #if MYSQLND_UNICODE
269 if (!as_unicode) {
270 #endif
271 ZVAL_STRINGL(zv, value, length, 1);
272 efree(value); /* allocated by spprintf */
273 #if MYSQLND_UNICODE
274 } else {
275 ZVAL_UTF8_STRINGL(zv, value, length, ZSTR_AUTOFREE);
276 }
277 #endif
278 DBG_VOID_RETURN;
279 }
280 /* }}} */
281
282
283 /* {{{ ps_fetch_date */
284 static
ps_fetch_date(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,zend_bool as_unicode TSRMLS_DC)285 void ps_fetch_date(zval *zv, const MYSQLND_FIELD * const field,
286 unsigned int pack_len, zend_uchar **row,
287 zend_bool as_unicode TSRMLS_DC)
288 {
289 struct st_mysqlnd_time t = {0};
290 unsigned int length; /* First byte encodes the length*/
291 char * value;
292 DBG_ENTER("ps_fetch_date");
293
294 if ((length = php_mysqlnd_net_field_length(row))) {
295 zend_uchar *to= *row;
296
297 t.time_type= MYSQLND_TIMESTAMP_DATE;
298 t.neg= 0;
299
300 t.second_part = t.hour = t.minute = t.second = 0;
301
302 t.year = (unsigned int) sint2korr(to);
303 t.month = (unsigned int) to[2];
304 t.day = (unsigned int) to[3];
305
306 (*row)+= length;
307 } else {
308 memset(&t, 0, sizeof(t));
309 t.time_type = MYSQLND_TIMESTAMP_DATE;
310 }
311
312 /*
313 QQ : How to make this unicode without copying two times the buffer -
314 Unicode equivalent of spprintf?
315 */
316 length = spprintf(&value, 0, "%04u-%02u-%02u", t.year, t.month, t.day);
317
318 DBG_INF_FMT("%s", value);
319 #if MYSQLND_UNICODE
320 if (!as_unicode) {
321 #endif
322 ZVAL_STRINGL(zv, value, length, 1);
323 efree(value); /* allocated by spprintf */
324 #if MYSQLND_UNICODE
325 } else {
326 ZVAL_UTF8_STRINGL(zv, value, length, ZSTR_AUTOFREE);
327 }
328 #endif
329 DBG_VOID_RETURN;
330 }
331 /* }}} */
332
333
334 /* {{{ ps_fetch_datetime */
335 static
ps_fetch_datetime(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,zend_bool as_unicode TSRMLS_DC)336 void ps_fetch_datetime(zval *zv, const MYSQLND_FIELD * const field,
337 unsigned int pack_len, zend_uchar **row,
338 zend_bool as_unicode TSRMLS_DC)
339 {
340 struct st_mysqlnd_time t;
341 unsigned int length; /* First byte encodes the length*/
342 char * value;
343 DBG_ENTER("ps_fetch_datetime");
344
345 if ((length = php_mysqlnd_net_field_length(row))) {
346 zend_uchar *to= *row;
347
348 t.time_type = MYSQLND_TIMESTAMP_DATETIME;
349 t.neg = 0;
350
351 t.year = (unsigned int) sint2korr(to);
352 t.month = (unsigned int) to[2];
353 t.day = (unsigned int) to[3];
354
355 if (length > 4) {
356 t.hour = (unsigned int) to[4];
357 t.minute = (unsigned int) to[5];
358 t.second = (unsigned int) to[6];
359 } else {
360 t.hour = t.minute = t.second= 0;
361 }
362 t.second_part = (length > 7) ? (unsigned long) sint4korr(to+7) : 0;
363
364 (*row)+= length;
365 } else {
366 memset(&t, 0, sizeof(t));
367 t.time_type = MYSQLND_TIMESTAMP_DATETIME;
368 }
369
370 /*
371 QQ : How to make this unicode without copying two times the buffer -
372 Unicode equivalent of spprintf?
373 */
374 length = spprintf(&value, 0, "%04u-%02u-%02u %02u:%02u:%02u",
375 t.year, t.month, t.day, t.hour, t.minute, t.second);
376
377 DBG_INF_FMT("%s", value);
378 #if MYSQLND_UNICODE
379 if (!as_unicode) {
380 #endif
381 ZVAL_STRINGL(zv, value, length, 1);
382 efree(value); /* allocated by spprintf */
383 #if MYSQLND_UNICODE
384 } else {
385 ZVAL_UTF8_STRINGL(zv, to, length, ZSTR_AUTOFREE);
386 }
387 #endif
388 DBG_VOID_RETURN;
389 }
390 /* }}} */
391
392
393 /* {{{ ps_fetch_string */
394 static
ps_fetch_string(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,zend_bool as_unicode TSRMLS_DC)395 void ps_fetch_string(zval *zv, const MYSQLND_FIELD * const field,
396 unsigned int pack_len, zend_uchar **row,
397 zend_bool as_unicode TSRMLS_DC)
398 {
399 /*
400 For now just copy, before we make it possible
401 to write \0 to the row buffer
402 */
403 unsigned long length = php_mysqlnd_net_field_length(row);
404 DBG_ENTER("ps_fetch_string");
405 DBG_INF_FMT("len = %lu", length);
406 #if MYSQLND_UNICODE
407 if (field->charsetnr == MYSQLND_BINARY_CHARSET_NR) {
408 DBG_INF("Binary charset");
409 ZVAL_STRINGL(zv, (char *)*row, length, 1);
410 } else {
411 DBG_INF_FMT("copying from the row buffer");
412 ZVAL_UTF8_STRINGL(zv, (char*)*row, length, ZSTR_DUPLICATE);
413 }
414 #else
415 DBG_INF("copying from the row buffer");
416 ZVAL_STRINGL(zv, (char *)*row, length, 1);
417 #endif
418
419 (*row) += length;
420 DBG_VOID_RETURN;
421 }
422 /* }}} */
423
424
425 /* {{{ ps_fetch_bit */
426 static
ps_fetch_bit(zval * zv,const MYSQLND_FIELD * const field,unsigned int pack_len,zend_uchar ** row,zend_bool as_unicode TSRMLS_DC)427 void ps_fetch_bit(zval *zv, const MYSQLND_FIELD * const field,
428 unsigned int pack_len, zend_uchar **row,
429 zend_bool as_unicode TSRMLS_DC)
430 {
431 unsigned long length= php_mysqlnd_net_field_length(row);
432 ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, as_unicode, length TSRMLS_CC);
433 }
434 /* }}} */
435
436
437 /* {{{ _mysqlnd_init_ps_fetch_subsystem */
_mysqlnd_init_ps_fetch_subsystem()438 void _mysqlnd_init_ps_fetch_subsystem()
439 {
440 memset(mysqlnd_ps_fetch_functions, 0, sizeof(mysqlnd_ps_fetch_functions));
441 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].func = ps_fetch_null;
442 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
443 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].php_type = IS_NULL;
444 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NULL].can_ret_as_str_in_uni = TRUE;
445
446 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
447 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
448 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].php_type = IS_LONG;
449 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY].can_ret_as_str_in_uni = TRUE;
450
451 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
452 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
453 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].php_type = IS_LONG;
454 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SHORT].can_ret_as_str_in_uni = TRUE;
455
456 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
457 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
458 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].php_type = IS_LONG;
459 mysqlnd_ps_fetch_functions[MYSQL_TYPE_YEAR].can_ret_as_str_in_uni = TRUE;
460
461 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
462 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
463 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].php_type = IS_LONG;
464 mysqlnd_ps_fetch_functions[MYSQL_TYPE_INT24].can_ret_as_str_in_uni = TRUE;
465
466 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
467 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
468 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].php_type = IS_LONG;
469 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG].can_ret_as_str_in_uni = TRUE;
470
471 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
472 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
473 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].php_type= IS_LONG;
474 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONGLONG].can_ret_as_str_in_uni = TRUE;
475
476 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
477 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
478 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].php_type = IS_DOUBLE;
479 mysqlnd_ps_fetch_functions[MYSQL_TYPE_FLOAT].can_ret_as_str_in_uni = TRUE;
480
481 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
482 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
483 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].php_type = IS_DOUBLE;
484 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DOUBLE].can_ret_as_str_in_uni = TRUE;
485
486 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_time;
487 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
488 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].php_type = IS_STRING;
489 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIME].can_ret_as_str_in_uni = TRUE;
490
491 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_date;
492 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
493 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].php_type = IS_STRING;
494 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATE].can_ret_as_str_in_uni = TRUE;
495
496 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
497 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQLND_PS_SKIP_RESULT_W_LEN;
498 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].php_type = IS_STRING;
499 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDATE].can_ret_as_str_in_uni = TRUE;
500
501 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
502 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
503 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].php_type= IS_STRING;
504 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DATETIME].can_ret_as_str_in_uni = TRUE;
505
506 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
507 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQLND_PS_SKIP_RESULT_W_LEN;
508 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].php_type= IS_STRING;
509 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].can_ret_as_str_in_uni = TRUE;
510
511 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_string;
512 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
513 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING;
514 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].is_possibly_blob = TRUE;
515 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].can_ret_as_str_in_uni = TRUE;
516
517 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_string;
518 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
519 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type = IS_STRING;
520 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].is_possibly_blob = TRUE;
521 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].can_ret_as_str_in_uni = TRUE;
522
523 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string;
524 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
525 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type = IS_STRING;
526 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].is_possibly_blob = TRUE;
527 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].can_ret_as_str_in_uni = TRUE;
528
529 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_string;
530 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
531 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type = IS_STRING;
532 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].is_possibly_blob = TRUE;
533 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].can_ret_as_str_in_uni = TRUE;
534
535 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bit;
536 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = 8;
537 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type = IS_LONG;
538 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].can_ret_as_str_in_uni = TRUE;
539
540 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
541 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
542 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING;
543 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].is_possibly_blob = TRUE;
544
545 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
546 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
547 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING;
548 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].is_possibly_blob = TRUE;
549
550 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
551 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
552 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING;
553 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].is_possibly_blob = TRUE;
554
555 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
556 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
557 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type = IS_STRING;
558 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].can_ret_as_str_in_uni = TRUE;
559
560 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
561 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
562 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type = IS_STRING;
563 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].can_ret_as_str_in_uni = TRUE;
564
565 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
566 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
567 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type = IS_STRING;
568
569 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
570 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
571 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type = IS_STRING;
572
573 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
574 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
575 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING;
576 }
577 /* }}} */
578
579
580 /* {{{ mysqlnd_stmt_copy_it */
581 static enum_func_status
mysqlnd_stmt_copy_it(zval *** copies,zval * original,unsigned int param_count,unsigned int current TSRMLS_DC)582 mysqlnd_stmt_copy_it(zval *** copies, zval *original, unsigned int param_count, unsigned int current TSRMLS_DC)
583 {
584 if (!*copies) {
585 *copies = mnd_ecalloc(param_count, sizeof(zval *));
586 }
587 if (*copies) {
588 MAKE_STD_ZVAL((*copies)[current]);
589 *(*copies)[current] = *original;
590 Z_SET_REFCOUNT_P((*copies)[current], 1);
591 zval_copy_ctor((*copies)[current]);
592 return PASS;
593 }
594 return FAIL;
595 }
596 /* }}} */
597
598
599 /* {{{ mysqlnd_stmt_execute_store_params */
600 static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s,zend_uchar ** buf,zend_uchar ** p,size_t * buf_len TSRMLS_DC)601 mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len TSRMLS_DC)
602 {
603 MYSQLND_STMT_DATA * stmt = s->data;
604 unsigned int i = 0;
605 zend_uchar * provided_buffer = *buf;
606 size_t left = (*buf_len - (*p - *buf));
607 size_t data_size = 0;
608 zval **copies = NULL;/* if there are different types */
609 enum_func_status ret = FAIL;
610 int resend_types_next_time = 0;
611 size_t null_byte_offset;
612
613 DBG_ENTER("mysqlnd_stmt_execute_store_params");
614
615 {
616 unsigned int null_count = (stmt->param_count + 7) / 8;
617 /* give it some reserved space - 20 bytes */
618 if (left < (null_count + 20)) {
619 unsigned int offset = *p - *buf;
620 zend_uchar *tmp_buf;
621 *buf_len = offset + null_count + 20;
622 tmp_buf = mnd_emalloc(*buf_len);
623 if (!tmp_buf) {
624 SET_OOM_ERROR(stmt->error_info);
625 goto end;
626 }
627 memcpy(tmp_buf, *buf, offset);
628 if (*buf != provided_buffer) {
629 mnd_efree(*buf);
630 }
631 *buf = tmp_buf;
632
633 /* Update our pos pointer */
634 *p = *buf + offset;
635 }
636 /* put `null` bytes */
637 null_byte_offset = *p - *buf;
638 memset(*p, 0, null_count);
639 *p += null_count;
640 }
641
642 /* 1. Store type information */
643 /*
644 check if need to send the types even if stmt->send_types_to_server is 0. This is because
645 if we send "i" (42) then the type will be int and the server will expect int. However, if next
646 time we try to send > LONG_MAX, the conversion to string will send a string and the server
647 won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
648 occur, and force resend for the next execution.
649 */
650 for (i = 0; i < stmt->param_count; i++) {
651 if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL &&
652 (stmt->param_bind[i].type == MYSQL_TYPE_LONG || stmt->param_bind[i].type == MYSQL_TYPE_LONGLONG))
653 {
654 /* always copy the var, because we do many conversions */
655 if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG &&
656 PASS != mysqlnd_stmt_copy_it(&copies, stmt->param_bind[i].zv, stmt->param_count, i TSRMLS_CC))
657 {
658 SET_OOM_ERROR(stmt->error_info);
659 goto end;
660 }
661 /*
662 if it doesn't fit in a long send it as a string.
663 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
664 */
665 if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
666 zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
667 convert_to_double_ex(&tmp_data);
668 if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
669 stmt->send_types_to_server = resend_types_next_time = 1;
670 }
671 }
672 }
673 }
674
675 int1store(*p, stmt->send_types_to_server);
676 (*p)++;
677
678 if (stmt->send_types_to_server) {
679 /* 2 bytes per type, and leave 20 bytes for future use */
680 if (left < ((stmt->param_count * 2) + 20)) {
681 unsigned int offset = *p - *buf;
682 zend_uchar *tmp_buf;
683 *buf_len = offset + stmt->param_count * 2 + 20;
684 tmp_buf = mnd_emalloc(*buf_len);
685 if (!tmp_buf) {
686 SET_OOM_ERROR(stmt->error_info);
687 goto end;
688 }
689 memcpy(tmp_buf, *buf, offset);
690 if (*buf != provided_buffer) {
691 mnd_efree(*buf);
692 }
693 *buf = tmp_buf;
694
695 /* Update our pos pointer */
696 *p = *buf + offset;
697 }
698 for (i = 0; i < stmt->param_count; i++) {
699 short current_type = stmt->param_bind[i].type;
700 /* our types are not unsigned */
701 #if SIZEOF_LONG==8
702 if (current_type == MYSQL_TYPE_LONG) {
703 current_type = MYSQL_TYPE_LONGLONG;
704 }
705 #endif
706 if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_NULL && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
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 */
711 if (Z_TYPE_P(stmt->param_bind[i].zv) != IS_LONG) {
712 zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
713
714 convert_to_double_ex(&tmp_data);
715 if (Z_DVAL_P(tmp_data) > LONG_MAX || Z_DVAL_P(tmp_data) < LONG_MIN) {
716 convert_to_string_ex(&tmp_data);
717 current_type = MYSQL_TYPE_VAR_STRING;
718 /*
719 don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
720 we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
721 if the type is however not long, then we will do a goto in the next switch.
722 We want to preserve the original bind type given by the user. Thus, we do these hacks.
723 */
724 } else {
725 convert_to_long_ex(&tmp_data);
726 }
727 }
728 }
729 int2store(*p, current_type);
730 *p+= 2;
731 }
732 }
733 stmt->send_types_to_server = resend_types_next_time;
734
735 /* 2. Store data */
736 /* 2.1 Calculate how much space we need */
737 for (i = 0; i < stmt->param_count; i++) {
738 unsigned int j;
739 zval *the_var = stmt->param_bind[i].zv;
740
741 if (!the_var || (stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
742 continue;
743 }
744 for (j = i + 1; j < stmt->param_count; j++) {
745 if (stmt->param_bind[j].zv == the_var) {
746 /* Double binding of the same zval, make a copy */
747 if (!copies || !copies[i]) {
748 if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
749 SET_OOM_ERROR(stmt->error_info);
750 goto end;
751 }
752 }
753 break;
754 }
755 }
756
757 switch (stmt->param_bind[i].type) {
758 case MYSQL_TYPE_DOUBLE:
759 data_size += 8;
760 if (Z_TYPE_P(the_var) != IS_DOUBLE) {
761 if (!copies || !copies[i]) {
762 if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
763 SET_OOM_ERROR(stmt->error_info);
764 goto end;
765 }
766 }
767 }
768 break;
769 case MYSQL_TYPE_LONGLONG:
770 {
771 zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
772 if (Z_TYPE_P(tmp_data) == IS_STRING) {
773 goto use_string;
774 }
775 convert_to_long_ex(&tmp_data);
776 }
777 data_size += 8;
778 break;
779 case MYSQL_TYPE_LONG:
780 {
781 zval *tmp_data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
782 if (Z_TYPE_P(tmp_data) == IS_STRING) {
783 goto use_string;
784 }
785 convert_to_long_ex(&tmp_data);
786 }
787 data_size += 4;
788 break;
789 case MYSQL_TYPE_LONG_BLOB:
790 if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
791 /*
792 User hasn't sent anything, we will send empty string.
793 Empty string has length of 0, encoded in 1 byte. No real
794 data will follows after it.
795 */
796 data_size++;
797 }
798 break;
799 case MYSQL_TYPE_VAR_STRING:
800 use_string:
801 data_size += 8; /* max 8 bytes for size */
802 #if MYSQLND_UNICODE
803 if (Z_TYPE_P(the_var) != IS_STRING || Z_TYPE_P(the_var) == IS_UNICODE)
804 #else
805 if (Z_TYPE_P(the_var) != IS_STRING)
806 #endif
807 {
808 if (!copies || !copies[i]) {
809 if (PASS != mysqlnd_stmt_copy_it(&copies, the_var, stmt->param_count, i TSRMLS_CC)) {
810 SET_OOM_ERROR(stmt->error_info);
811 goto end;
812 }
813 }
814 the_var = copies[i];
815 #if MYSQLND_UNICODE
816 if (Z_TYPE_P(the_var) == IS_UNICODE) {
817 zval_unicode_to_string_ex(the_var, UG(utf8_conv) TSRMLS_CC);
818 }
819 #endif
820 }
821 convert_to_string_ex(&the_var);
822 data_size += Z_STRLEN_P(the_var);
823 break;
824 }
825 }
826
827 /* 2.2 Enlarge the buffer, if needed */
828 left = (*buf_len - (*p - *buf));
829 if (left < data_size) {
830 unsigned int offset = *p - *buf;
831 zend_uchar *tmp_buf;
832 *buf_len = offset + data_size + 10; /* Allocate + 10 for safety */
833 tmp_buf = mnd_emalloc(*buf_len);
834 if (!tmp_buf) {
835 SET_OOM_ERROR(stmt->error_info);
836 goto end;
837 }
838 memcpy(tmp_buf, *buf, offset);
839 /*
840 When too many columns the buffer provided to the function might not be sufficient.
841 In this case new buffer has been allocated above. When we allocate a buffer and then
842 allocate a bigger one here, we should free the first one.
843 */
844 if (*buf != provided_buffer) {
845 mnd_efree(*buf);
846 }
847 *buf = tmp_buf;
848 /* Update our pos pointer */
849 *p = *buf + offset;
850 }
851
852 /* 2.3 Store the actual data */
853 for (i = 0; i < stmt->param_count; i++) {
854 zval *data = (copies && copies[i])? copies[i]: stmt->param_bind[i].zv;
855 /* Handle long data */
856 if (stmt->param_bind[i].zv && Z_TYPE_P(data) == IS_NULL) {
857 (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
858 } else {
859 switch (stmt->param_bind[i].type) {
860 case MYSQL_TYPE_DOUBLE:
861 convert_to_double_ex(&data);
862 float8store(*p, Z_DVAL_P(data));
863 (*p) += 8;
864 break;
865 case MYSQL_TYPE_LONGLONG:
866 if (Z_TYPE_P(data) == IS_STRING) {
867 goto send_string;
868 }
869 /* data has alreade been converted to long */
870 int8store(*p, Z_LVAL_P(data));
871 (*p) += 8;
872 break;
873 case MYSQL_TYPE_LONG:
874 if (Z_TYPE_P(data) == IS_STRING) {
875 goto send_string;
876 }
877 /* data has alreade been converted to long */
878 int4store(*p, Z_LVAL_P(data));
879 (*p) += 4;
880 break;
881 case MYSQL_TYPE_LONG_BLOB:
882 if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
883 stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
884 } else {
885 /* send_long_data() not called, send empty string */
886 *p = php_mysqlnd_net_store_length(*p, 0);
887 }
888 break;
889 case MYSQL_TYPE_VAR_STRING:
890 send_string:
891 {
892 unsigned int len = Z_STRLEN_P(data);
893 /* to is after p. The latter hasn't been moved */
894 *p = php_mysqlnd_net_store_length(*p, len);
895 memcpy(*p, Z_STRVAL_P(data), len);
896 (*p) += len;
897 }
898 break;
899 default:
900 /* Won't happen, but set to NULL */
901 (*buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
902 break;
903 }
904 }
905 }
906 ret = PASS;
907 end:
908 if (copies) {
909 for (i = 0; i < stmt->param_count; i++) {
910 if (copies[i]) {
911 zval_ptr_dtor(&copies[i]);
912 }
913 }
914 mnd_efree(copies);
915 }
916
917 DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
918 DBG_RETURN(ret);
919 }
920 /* }}} */
921
922
923 /* {{{ mysqlnd_stmt_execute_generate_request */
924 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)925 mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, zend_bool * free_buffer TSRMLS_DC)
926 {
927 MYSQLND_STMT_DATA * stmt = s->data;
928 zend_uchar *p = stmt->execute_cmd_buffer.buffer,
929 *cmd_buffer = stmt->execute_cmd_buffer.buffer;
930 size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
931 enum_func_status ret;
932
933 DBG_ENTER("mysqlnd_stmt_execute_generate_request");
934
935 int4store(p, stmt->stmt_id);
936 p += 4;
937
938 /* flags is 4 bytes, we store just 1 */
939 int1store(p, (zend_uchar) stmt->flags);
940 p++;
941
942 /* Make it all zero */
943 int4store(p, 0);
944
945 int1store(p, 1); /* and send 1 for iteration count */
946 p+= 4;
947
948 ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length TSRMLS_CC);
949
950 *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
951 *request_len = (p - cmd_buffer);
952 *request = cmd_buffer;
953 DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
954 DBG_RETURN(ret);
955 }
956 /* }}} */
957
958 /*
959 * Local variables:
960 * tab-width: 4
961 * c-basic-offset: 4
962 * End:
963 * vim600: noet sw=4 ts=4 fdm=marker
964 * vim<600: noet sw=4 ts=4
965 */
966