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