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