2 +----------------------------------------------------------------------+
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: Andrey Hristov <andrey@mysql.com> |
16 | Ulf Wendel <uwendel@mysql.com> |
17 | Georg Richter <georg@mysql.com> |
18 +----------------------------------------------------------------------+
24 #include "mysqlnd_wireprotocol.h"
25 #include "mysqlnd_priv.h"
26 #include "mysqlnd_debug.h"
28 #define MYSQLND_SILENT
31 enum mysqlnd_timestamp_type
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
41 struct st_mysqlnd_time
43 unsigned int year
, month
, day
, hour
, minute
, second
;
44 unsigned long second_part
;
46 enum mysqlnd_timestamp_type time_type
;
50 struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LAST
+ 1];
52 #define MYSQLND_PS_SKIP_RESULT_W_LEN -1
53 #define MYSQLND_PS_SKIP_RESULT_STR -2
55 /* {{{ ps_fetch_from_1_to_8_bytes */
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
)
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
) {
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;
82 tmp_len
= sprintf((char *)&tmp
, MYSQLND_LLU_SPEC
, uval
);
84 #endif /* #if SIZEOF_LONG==4 */
86 if (byte_count
< 8 || uval
<= L64(9223372036854775807)) {
87 ZVAL_LONG(zv
, (long) uval
); /* the cast is safe, we are in the range */
90 tmp_len
= sprintf((char *)&tmp
, MYSQLND_LLU_SPEC
, uval
);
97 case 8:lval
= (int64_t) sint8korr(*row
);break;
99 7, 6 and 5 are not possible.
100 BIT is only unsigned, thus only uint5|6|7 macroses exist
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;
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
);
115 ZVAL_LONG(zv
, (long) lval
); /* the cast is safe, we are in the range */
122 DBG_INF("stringify");
123 ZVAL_UTF8_STRINGL(zv
, tmp
, tmp_len
, ZSTR_DUPLICATE
);
127 DBG_INF("stringify");
128 ZVAL_STRINGL(zv
, tmp
, tmp_len
, 1);
137 /* {{{ ps_fetch_null */
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
)
148 /* {{{ ps_fetch_int8 */
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
)
154 ps_fetch_from_1_to_8_bytes(zv
, field
, pack_len
, row
, as_unicode
, 1 TSRMLS_CC
);
159 /* {{{ ps_fetch_int16 */
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
)
165 ps_fetch_from_1_to_8_bytes(zv
, field
, pack_len
, row
, as_unicode
, 2 TSRMLS_CC
);
170 /* {{{ ps_fetch_int32 */
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
)
176 ps_fetch_from_1_to_8_bytes(zv
, field
, pack_len
, row
, as_unicode
, 4 TSRMLS_CC
);
181 /* {{{ ps_fetch_int64 */
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
)
187 ps_fetch_from_1_to_8_bytes(zv
, field
, pack_len
, row
, as_unicode
, 8 TSRMLS_CC
);
192 /* {{{ ps_fetch_float */
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
)
199 DBG_ENTER("ps_fetch_float");
200 float4get(value
, *row
);
201 ZVAL_DOUBLE(zv
, value
);
203 DBG_INF_FMT("value=%f", value
);
209 /* {{{ ps_fetch_double */
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
)
216 DBG_ENTER("ps_fetch_double");
217 float8get(value
, *row
);
218 ZVAL_DOUBLE(zv
, value
);
220 DBG_INF_FMT("value=%f", value
);
226 /* {{{ ps_fetch_time */
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
)
232 struct st_mysqlnd_time t
;
233 unsigned int length
; /* First byte encodes the length*/
235 DBG_ENTER("ps_fetch_time");
237 if ((length
= php_mysqlnd_net_field_length(row
))) {
238 zend_uchar
*to
= *row
;
240 t
.time_type
= MYSQLND_TIMESTAMP_TIME
;
241 t
.neg
= (zend_bool
) to
[0];
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;
250 /* Convert days to hours at once */
257 memset(&t
, 0, sizeof(t
));
258 t
.time_type
= MYSQLND_TIMESTAMP_TIME
;
261 length
= mnd_sprintf(&value
, 0, "%s%02u:%02u:%02u", (t
.neg
? "-" : ""), t
.hour
, t
.minute
, t
.second
);
263 DBG_INF_FMT("%s", value
);
267 ZVAL_STRINGL(zv
, value
, length
, 1);
268 mnd_sprintf_free(value
);
271 ZVAL_UTF8_STRINGL(zv
, value
, length
, ZSTR_AUTOFREE
);
279 /* {{{ ps_fetch_date */
281 void ps_fetch_date(zval
*zv
, const MYSQLND_FIELD
* const field
,
282 unsigned int pack_len
, zend_uchar
**row
,
283 zend_bool as_unicode TSRMLS_DC
)
285 struct st_mysqlnd_time t
= {0};
286 unsigned int length
; /* First byte encodes the length*/
288 DBG_ENTER("ps_fetch_date");
290 if ((length
= php_mysqlnd_net_field_length(row
))) {
291 zend_uchar
*to
= *row
;
293 t
.time_type
= MYSQLND_TIMESTAMP_DATE
;
296 t
.second_part
= t
.hour
= t
.minute
= t
.second
= 0;
298 t
.year
= (unsigned int) sint2korr(to
);
299 t
.month
= (unsigned int) to
[2];
300 t
.day
= (unsigned int) to
[3];
304 memset(&t
, 0, sizeof(t
));
305 t
.time_type
= MYSQLND_TIMESTAMP_DATE
;
308 length
= mnd_sprintf(&value
, 0, "%04u-%02u-%02u", t
.year
, t
.month
, t
.day
);
310 DBG_INF_FMT("%s", value
);
314 ZVAL_STRINGL(zv
, value
, length
, 1);
315 mnd_sprintf_free(value
);
318 ZVAL_UTF8_STRINGL(zv
, value
, length
, ZSTR_AUTOFREE
);
326 /* {{{ ps_fetch_datetime */
328 void ps_fetch_datetime(zval
*zv
, const MYSQLND_FIELD
* const field
,
329 unsigned int pack_len
, zend_uchar
**row
,
330 zend_bool as_unicode TSRMLS_DC
)
332 struct st_mysqlnd_time t
;
333 unsigned int length
; /* First byte encodes the length*/
335 DBG_ENTER("ps_fetch_datetime");
337 if ((length
= php_mysqlnd_net_field_length(row
))) {
338 zend_uchar
*to
= *row
;
340 t
.time_type
= MYSQLND_TIMESTAMP_DATETIME
;
343 t
.year
= (unsigned int) sint2korr(to
);
344 t
.month
= (unsigned int) to
[2];
345 t
.day
= (unsigned int) to
[3];
348 t
.hour
= (unsigned int) to
[4];
349 t
.minute
= (unsigned int) to
[5];
350 t
.second
= (unsigned int) to
[6];
352 t
.hour
= t
.minute
= t
.second
= 0;
354 t
.second_part
= (length
> 7) ? (unsigned long) sint4korr(to
+7) : 0;
358 memset(&t
, 0, sizeof(t
));
359 t
.time_type
= MYSQLND_TIMESTAMP_DATETIME
;
362 length
= mnd_sprintf(&value
, 0, "%04u-%02u-%02u %02u:%02u:%02u", t
.year
, t
.month
, t
.day
, t
.hour
, t
.minute
, t
.second
);
364 DBG_INF_FMT("%s", value
);
368 ZVAL_STRINGL(zv
, value
, length
, 1);
369 mnd_sprintf_free(value
);
372 ZVAL_UTF8_STRINGL(zv
, to
, length
, ZSTR_AUTOFREE
);
380 /* {{{ ps_fetch_string */
382 void ps_fetch_string(zval
*zv
, const MYSQLND_FIELD
* const field
,
383 unsigned int pack_len
, zend_uchar
**row
,
384 zend_bool as_unicode TSRMLS_DC
)
387 For now just copy, before we make it possible
388 to write \0 to the row buffer
390 unsigned long length
= php_mysqlnd_net_field_length(row
);
391 DBG_ENTER("ps_fetch_string");
392 DBG_INF_FMT("len = %lu", length
);
394 if (field
->charsetnr
== MYSQLND_BINARY_CHARSET_NR
) {
395 DBG_INF("Binary charset");
396 ZVAL_STRINGL(zv
, (char *)*row
, length
, 1);
398 DBG_INF_FMT("copying from the row buffer");
399 ZVAL_UTF8_STRINGL(zv
, (char*)*row
, length
, ZSTR_DUPLICATE
);
402 DBG_INF("copying from the row buffer");
403 ZVAL_STRINGL(zv
, (char *)*row
, length
, 1);
412 /* {{{ ps_fetch_bit */
414 void ps_fetch_bit(zval
*zv
, const MYSQLND_FIELD
* const field
,
415 unsigned int pack_len
, zend_uchar
**row
,
416 zend_bool as_unicode TSRMLS_DC
)
418 unsigned long length
= php_mysqlnd_net_field_length(row
);
419 ps_fetch_from_1_to_8_bytes(zv
, field
, pack_len
, row
, as_unicode
, length TSRMLS_CC
);
424 /* {{{ _mysqlnd_init_ps_fetch_subsystem */
425 void _mysqlnd_init_ps_fetch_subsystem()
427 memset(mysqlnd_ps_fetch_functions
, 0, sizeof(mysqlnd_ps_fetch_functions
));
428 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NULL
].func
= ps_fetch_null
;
429 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NULL
].pack_len
= 0;
430 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NULL
].php_type
= IS_NULL
;
431 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NULL
].can_ret_as_str_in_uni
= TRUE
;
433 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY
].func
= ps_fetch_int8
;
434 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY
].pack_len
= 1;
435 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY
].php_type
= IS_LONG
;
436 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY
].can_ret_as_str_in_uni
= TRUE
;
438 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SHORT
].func
= ps_fetch_int16
;
439 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SHORT
].pack_len
= 2;
440 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SHORT
].php_type
= IS_LONG
;
441 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SHORT
].can_ret_as_str_in_uni
= TRUE
;
443 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_YEAR
].func
= ps_fetch_int16
;
444 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_YEAR
].pack_len
= 2;
445 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_YEAR
].php_type
= IS_LONG
;
446 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_YEAR
].can_ret_as_str_in_uni
= TRUE
;
448 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_INT24
].func
= ps_fetch_int32
;
449 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_INT24
].pack_len
= 4;
450 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_INT24
].php_type
= IS_LONG
;
451 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_INT24
].can_ret_as_str_in_uni
= TRUE
;
453 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG
].func
= ps_fetch_int32
;
454 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG
].pack_len
= 4;
455 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG
].php_type
= IS_LONG
;
456 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG
].can_ret_as_str_in_uni
= TRUE
;
458 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONGLONG
].func
= ps_fetch_int64
;
459 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONGLONG
].pack_len
= 8;
460 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONGLONG
].php_type
= IS_LONG
;
461 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONGLONG
].can_ret_as_str_in_uni
= TRUE
;
463 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_FLOAT
].func
= ps_fetch_float
;
464 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_FLOAT
].pack_len
= 4;
465 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_FLOAT
].php_type
= IS_DOUBLE
;
466 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_FLOAT
].can_ret_as_str_in_uni
= TRUE
;
468 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DOUBLE
].func
= ps_fetch_double
;
469 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DOUBLE
].pack_len
= 8;
470 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DOUBLE
].php_type
= IS_DOUBLE
;
471 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DOUBLE
].can_ret_as_str_in_uni
= TRUE
;
473 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIME
].func
= ps_fetch_time
;
474 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIME
].pack_len
= MYSQLND_PS_SKIP_RESULT_W_LEN
;
475 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIME
].php_type
= IS_STRING
;
476 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIME
].can_ret_as_str_in_uni
= TRUE
;
478 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATE
].func
= ps_fetch_date
;
479 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATE
].pack_len
= MYSQLND_PS_SKIP_RESULT_W_LEN
;
480 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATE
].php_type
= IS_STRING
;
481 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATE
].can_ret_as_str_in_uni
= TRUE
;
483 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDATE
].func
= ps_fetch_string
;
484 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDATE
].pack_len
= MYSQLND_PS_SKIP_RESULT_W_LEN
;
485 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDATE
].php_type
= IS_STRING
;
486 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDATE
].can_ret_as_str_in_uni
= TRUE
;
488 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATETIME
].func
= ps_fetch_datetime
;
489 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATETIME
].pack_len
= MYSQLND_PS_SKIP_RESULT_W_LEN
;
490 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATETIME
].php_type
= IS_STRING
;
491 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DATETIME
].can_ret_as_str_in_uni
= TRUE
;
493 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIMESTAMP
].func
= ps_fetch_datetime
;
494 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIMESTAMP
].pack_len
= MYSQLND_PS_SKIP_RESULT_W_LEN
;
495 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIMESTAMP
].php_type
= IS_STRING
;
496 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TIMESTAMP
].can_ret_as_str_in_uni
= TRUE
;
498 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY_BLOB
].func
= ps_fetch_string
;
499 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY_BLOB
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
500 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY_BLOB
].php_type
= IS_STRING
;
501 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY_BLOB
].is_possibly_blob
= TRUE
;
502 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_TINY_BLOB
].can_ret_as_str_in_uni
= TRUE
;
504 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BLOB
].func
= ps_fetch_string
;
505 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BLOB
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
506 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BLOB
].php_type
= IS_STRING
;
507 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BLOB
].is_possibly_blob
= TRUE
;
508 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BLOB
].can_ret_as_str_in_uni
= TRUE
;
510 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_MEDIUM_BLOB
].func
= ps_fetch_string
;
511 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_MEDIUM_BLOB
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
512 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_MEDIUM_BLOB
].php_type
= IS_STRING
;
513 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_MEDIUM_BLOB
].is_possibly_blob
= TRUE
;
514 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_MEDIUM_BLOB
].can_ret_as_str_in_uni
= TRUE
;
516 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG_BLOB
].func
= ps_fetch_string
;
517 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG_BLOB
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
518 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG_BLOB
].php_type
= IS_STRING
;
519 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG_BLOB
].is_possibly_blob
= TRUE
;
520 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_LONG_BLOB
].can_ret_as_str_in_uni
= TRUE
;
522 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BIT
].func
= ps_fetch_bit
;
523 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BIT
].pack_len
= 8;
524 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BIT
].php_type
= IS_LONG
;
525 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_BIT
].can_ret_as_str_in_uni
= TRUE
;
527 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VAR_STRING
].func
= ps_fetch_string
;
528 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VAR_STRING
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
529 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VAR_STRING
].php_type
= IS_STRING
;
530 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VAR_STRING
].is_possibly_blob
= TRUE
;
532 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VARCHAR
].func
= ps_fetch_string
;
533 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VARCHAR
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
534 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VARCHAR
].php_type
= IS_STRING
;
535 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_VARCHAR
].is_possibly_blob
= TRUE
;
537 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_STRING
].func
= ps_fetch_string
;
538 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_STRING
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
539 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_STRING
].php_type
= IS_STRING
;
540 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_STRING
].is_possibly_blob
= TRUE
;
542 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DECIMAL
].func
= ps_fetch_string
;
543 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DECIMAL
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
544 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DECIMAL
].php_type
= IS_STRING
;
545 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_DECIMAL
].can_ret_as_str_in_uni
= TRUE
;
547 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDECIMAL
].func
= ps_fetch_string
;
548 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDECIMAL
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
549 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDECIMAL
].php_type
= IS_STRING
;
550 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_NEWDECIMAL
].can_ret_as_str_in_uni
= TRUE
;
552 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_ENUM
].func
= ps_fetch_string
;
553 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_ENUM
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
554 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_ENUM
].php_type
= IS_STRING
;
556 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SET
].func
= ps_fetch_string
;
557 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SET
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
558 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_SET
].php_type
= IS_STRING
;
560 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_GEOMETRY
].func
= ps_fetch_string
;
561 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_GEOMETRY
].pack_len
= MYSQLND_PS_SKIP_RESULT_STR
;
562 mysqlnd_ps_fetch_functions
[MYSQL_TYPE_GEOMETRY
].php_type
= IS_STRING
;
567 /* {{{ mysqlnd_stmt_copy_it */
568 static enum_func_status
569 mysqlnd_stmt_copy_it(zval
*** copies
, zval
*original
, unsigned int param_count
, unsigned int current TSRMLS_DC
)
572 *copies
= mnd_ecalloc(param_count
, sizeof(zval
*));
575 MAKE_STD_ZVAL((*copies
)[current
]);
576 *(*copies
)[current
] = *original
;
577 Z_SET_REFCOUNT_P((*copies
)[current
], 1);
578 zval_copy_ctor((*copies
)[current
]);
586 /* {{{ mysqlnd_stmt_execute_store_params */
587 static enum_func_status
588 mysqlnd_stmt_execute_store_params(MYSQLND_STMT
* s
, zend_uchar
**buf
, zend_uchar
**p
, size_t *buf_len TSRMLS_DC
)
590 MYSQLND_STMT_DATA
* stmt
= s
->data
;
592 zend_uchar
* provided_buffer
= *buf
;
593 size_t left
= (*buf_len
- (*p
- *buf
));
594 size_t data_size
= 0;
595 zval
**copies
= NULL
;/* if there are different types */
596 enum_func_status ret
= FAIL
;
597 int resend_types_next_time
= 0;
598 size_t null_byte_offset
;
600 DBG_ENTER("mysqlnd_stmt_execute_store_params");
603 unsigned int null_count
= (stmt
->param_count
+ 7) / 8;
604 /* give it some reserved space - 20 bytes */
605 if (left
< (null_count
+ 20)) {
606 unsigned int offset
= *p
- *buf
;
608 *buf_len
= offset
+ null_count
+ 20;
609 tmp_buf
= mnd_emalloc(*buf_len
);
611 SET_OOM_ERROR(*stmt
->error_info
);
614 memcpy(tmp_buf
, *buf
, offset
);
615 if (*buf
!= provided_buffer
) {
620 /* Update our pos pointer */
623 /* put `null` bytes */
624 null_byte_offset
= *p
- *buf
;
625 memset(*p
, 0, null_count
);
629 /* 1. Store type information */
631 check if need to send the types even if stmt->send_types_to_server is 0. This is because
632 if we send "i" (42) then the type will be int and the server will expect int. However, if next
633 time we try to send > LONG_MAX, the conversion to string will send a string and the server
634 won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
635 occur, and force resend for the next execution.
637 for (i
= 0; i
< stmt
->param_count
; i
++) {
638 if (Z_TYPE_P(stmt
->param_bind
[i
].zv
) != IS_NULL
&&
639 (stmt
->param_bind
[i
].type
== MYSQL_TYPE_LONG
|| stmt
->param_bind
[i
].type
== MYSQL_TYPE_LONGLONG
))
641 /* always copy the var, because we do many conversions */
642 if (Z_TYPE_P(stmt
->param_bind
[i
].zv
) != IS_LONG
&&
643 PASS
!= mysqlnd_stmt_copy_it(&copies
, stmt
->param_bind
[i
].zv
, stmt
->param_count
, i TSRMLS_CC
))
645 SET_OOM_ERROR(*stmt
->error_info
);
649 if it doesn't fit in a long send it as a string.
650 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
652 if (Z_TYPE_P(stmt
->param_bind
[i
].zv
) != IS_LONG
) {
653 zval
*tmp_data
= (copies
&& copies
[i
])? copies
[i
]: stmt
->param_bind
[i
].zv
;
654 convert_to_double_ex(&tmp_data
);
655 if (Z_DVAL_P(tmp_data
) > LONG_MAX
|| Z_DVAL_P(tmp_data
) < LONG_MIN
) {
656 stmt
->send_types_to_server
= resend_types_next_time
= 1;
662 int1store(*p
, stmt
->send_types_to_server
);
665 if (stmt
->send_types_to_server
) {
666 /* 2 bytes per type, and leave 20 bytes for future use */
667 if (left
< ((stmt
->param_count
* 2) + 20)) {
668 unsigned int offset
= *p
- *buf
;
670 *buf_len
= offset
+ stmt
->param_count
* 2 + 20;
671 tmp_buf
= mnd_emalloc(*buf_len
);
673 SET_OOM_ERROR(*stmt
->error_info
);
676 memcpy(tmp_buf
, *buf
, offset
);
677 if (*buf
!= provided_buffer
) {
682 /* Update our pos pointer */
685 for (i
= 0; i
< stmt
->param_count
; i
++) {
686 short current_type
= stmt
->param_bind
[i
].type
;
687 /* our types are not unsigned */
689 if (current_type
== MYSQL_TYPE_LONG
) {
690 current_type
= MYSQL_TYPE_LONGLONG
;
693 if (Z_TYPE_P(stmt
->param_bind
[i
].zv
) != IS_NULL
&& (current_type
== MYSQL_TYPE_LONG
|| current_type
== MYSQL_TYPE_LONGLONG
)) {
695 if it doesn't fit in a long send it as a string.
696 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
698 if (Z_TYPE_P(stmt
->param_bind
[i
].zv
) != IS_LONG
) {
699 zval
*tmp_data
= (copies
&& copies
[i
])? copies
[i
]: stmt
->param_bind
[i
].zv
;
701 convert_to_double_ex(&tmp_data
);
702 if (Z_DVAL_P(tmp_data
) > LONG_MAX
|| Z_DVAL_P(tmp_data
) < LONG_MIN
) {
703 convert_to_string_ex(&tmp_data
);
704 current_type
= MYSQL_TYPE_VAR_STRING
;
706 don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
707 we force convert_to_long_ex in all cases, thus the type will be right in the next switch.
708 if the type is however not long, then we will do a goto in the next switch.
709 We want to preserve the original bind type given by the user. Thus, we do these hacks.
712 convert_to_long_ex(&tmp_data
);
716 int2store(*p
, current_type
);
720 stmt
->send_types_to_server
= resend_types_next_time
;
723 /* 2.1 Calculate how much space we need */
724 for (i
= 0; i
< stmt
->param_count
; i
++) {
726 zval
*the_var
= stmt
->param_bind
[i
].zv
;
728 if (!the_var
|| (stmt
->param_bind
[i
].type
!= MYSQL_TYPE_LONG_BLOB
&& Z_TYPE_P(the_var
) == IS_NULL
)) {
731 for (j
= i
+ 1; j
< stmt
->param_count
; j
++) {
732 if (stmt
->param_bind
[j
].zv
== the_var
) {
733 /* Double binding of the same zval, make a copy */
734 if (!copies
|| !copies
[i
]) {
735 if (PASS
!= mysqlnd_stmt_copy_it(&copies
, the_var
, stmt
->param_count
, i TSRMLS_CC
)) {
736 SET_OOM_ERROR(*stmt
->error_info
);
744 switch (stmt
->param_bind
[i
].type
) {
745 case MYSQL_TYPE_DOUBLE
:
747 if (Z_TYPE_P(the_var
) != IS_DOUBLE
) {
748 if (!copies
|| !copies
[i
]) {
749 if (PASS
!= mysqlnd_stmt_copy_it(&copies
, the_var
, stmt
->param_count
, i TSRMLS_CC
)) {
750 SET_OOM_ERROR(*stmt
->error_info
);
756 case MYSQL_TYPE_LONGLONG
:
758 zval
*tmp_data
= (copies
&& copies
[i
])? copies
[i
]: stmt
->param_bind
[i
].zv
;
759 if (Z_TYPE_P(tmp_data
) == IS_STRING
) {
762 convert_to_long_ex(&tmp_data
);
766 case MYSQL_TYPE_LONG
:
768 zval
*tmp_data
= (copies
&& copies
[i
])? copies
[i
]: stmt
->param_bind
[i
].zv
;
769 if (Z_TYPE_P(tmp_data
) == IS_STRING
) {
772 convert_to_long_ex(&tmp_data
);
776 case MYSQL_TYPE_LONG_BLOB
:
777 if (!(stmt
->param_bind
[i
].flags
& MYSQLND_PARAM_BIND_BLOB_USED
)) {
779 User hasn't sent anything, we will send empty string.
780 Empty string has length of 0, encoded in 1 byte. No real
781 data will follows after it.
786 case MYSQL_TYPE_VAR_STRING
:
788 data_size
+= 8; /* max 8 bytes for size */
790 if (Z_TYPE_P(the_var
) != IS_STRING
|| Z_TYPE_P(the_var
) == IS_UNICODE
)
792 if (Z_TYPE_P(the_var
) != IS_STRING
)
795 if (!copies
|| !copies
[i
]) {
796 if (PASS
!= mysqlnd_stmt_copy_it(&copies
, the_var
, stmt
->param_count
, i TSRMLS_CC
)) {
797 SET_OOM_ERROR(*stmt
->error_info
);
803 if (Z_TYPE_P(the_var
) == IS_UNICODE
) {
804 zval_unicode_to_string_ex(the_var
, UG(utf8_conv
) TSRMLS_CC
);
808 convert_to_string_ex(&the_var
);
809 data_size
+= Z_STRLEN_P(the_var
);
814 /* 2.2 Enlarge the buffer, if needed */
815 left
= (*buf_len
- (*p
- *buf
));
816 if (left
< data_size
) {
817 unsigned int offset
= *p
- *buf
;
819 *buf_len
= offset
+ data_size
+ 10; /* Allocate + 10 for safety */
820 tmp_buf
= mnd_emalloc(*buf_len
);
822 SET_OOM_ERROR(*stmt
->error_info
);
825 memcpy(tmp_buf
, *buf
, offset
);
827 When too many columns the buffer provided to the function might not be sufficient.
828 In this case new buffer has been allocated above. When we allocate a buffer and then
829 allocate a bigger one here, we should free the first one.
831 if (*buf
!= provided_buffer
) {
835 /* Update our pos pointer */
839 /* 2.3 Store the actual data */
840 for (i
= 0; i
< stmt
->param_count
; i
++) {
841 zval
*data
= (copies
&& copies
[i
])? copies
[i
]: stmt
->param_bind
[i
].zv
;
842 /* Handle long data */
843 if (stmt
->param_bind
[i
].zv
&& Z_TYPE_P(data
) == IS_NULL
) {
844 (*buf
+ null_byte_offset
)[i
/8] |= (zend_uchar
) (1 << (i
& 7));
846 switch (stmt
->param_bind
[i
].type
) {
847 case MYSQL_TYPE_DOUBLE
:
848 convert_to_double_ex(&data
);
849 float8store(*p
, Z_DVAL_P(data
));
852 case MYSQL_TYPE_LONGLONG
:
853 if (Z_TYPE_P(data
) == IS_STRING
) {
856 /* data has alreade been converted to long */
857 int8store(*p
, Z_LVAL_P(data
));
860 case MYSQL_TYPE_LONG
:
861 if (Z_TYPE_P(data
) == IS_STRING
) {
864 /* data has alreade been converted to long */
865 int4store(*p
, Z_LVAL_P(data
));
868 case MYSQL_TYPE_LONG_BLOB
:
869 if (stmt
->param_bind
[i
].flags
& MYSQLND_PARAM_BIND_BLOB_USED
) {
870 stmt
->param_bind
[i
].flags
&= ~MYSQLND_PARAM_BIND_BLOB_USED
;
872 /* send_long_data() not called, send empty string */
873 *p
= php_mysqlnd_net_store_length(*p
, 0);
876 case MYSQL_TYPE_VAR_STRING
:
879 unsigned int len
= Z_STRLEN_P(data
);
880 /* to is after p. The latter hasn't been moved */
881 *p
= php_mysqlnd_net_store_length(*p
, len
);
882 memcpy(*p
, Z_STRVAL_P(data
), len
);
887 /* Won't happen, but set to NULL */
888 (*buf
+ null_byte_offset
)[i
/8] |= (zend_uchar
) (1 << (i
& 7));
896 for (i
= 0; i
< stmt
->param_count
; i
++) {
898 zval_ptr_dtor(&copies
[i
]);
904 DBG_INF_FMT("ret=%s", ret
== PASS
? "PASS":"FAIL");
910 /* {{{ mysqlnd_stmt_execute_generate_request */
912 mysqlnd_stmt_execute_generate_request(MYSQLND_STMT
* const s
, zend_uchar
** request
, size_t *request_len
, zend_bool
* free_buffer TSRMLS_DC
)
914 MYSQLND_STMT_DATA
* stmt
= s
->data
;
915 zend_uchar
*p
= stmt
->execute_cmd_buffer
.buffer
,
916 *cmd_buffer
= stmt
->execute_cmd_buffer
.buffer
;
917 size_t cmd_buffer_length
= stmt
->execute_cmd_buffer
.length
;
918 enum_func_status ret
;
920 DBG_ENTER("mysqlnd_stmt_execute_generate_request");
922 int4store(p
, stmt
->stmt_id
);
925 /* flags is 4 bytes, we store just 1 */
926 int1store(p
, (zend_uchar
) stmt
->flags
);
929 /* Make it all zero */
932 int1store(p
, 1); /* and send 1 for iteration count */
935 ret
= mysqlnd_stmt_execute_store_params(s
, &cmd_buffer
, &p
, &cmd_buffer_length TSRMLS_CC
);
937 *free_buffer
= (cmd_buffer
!= stmt
->execute_cmd_buffer
.buffer
);
938 *request_len
= (p
- cmd_buffer
);
939 *request
= cmd_buffer
;
940 DBG_INF_FMT("ret=%s", ret
== PASS
? "PASS":"FAIL");
950 * vim600: noet sw=4 ts=4 fdm=marker
951 * vim<600: noet sw=4 ts=4