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_JSON].func = ps_fetch_string;
418 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
419 mysqlnd_ps_fetch_functions[MYSQL_TYPE_JSON].php_type = IS_STRING;
420
421 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_string;
422 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
423 mysqlnd_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].php_type = IS_STRING;
424
425 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_string;
426 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
427 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BLOB].php_type = IS_STRING;
428
429 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_string;
430 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
431 mysqlnd_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].php_type = IS_STRING;
432
433 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_string;
434 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
435 mysqlnd_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].php_type = IS_STRING;
436
437 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bit;
438 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = 8;
439 mysqlnd_ps_fetch_functions[MYSQL_TYPE_BIT].php_type = IS_LONG;
440
441 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
442 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
443 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].php_type = IS_STRING;
444
445 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
446 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
447 mysqlnd_ps_fetch_functions[MYSQL_TYPE_VARCHAR].php_type = IS_STRING;
448
449 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
450 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
451 mysqlnd_ps_fetch_functions[MYSQL_TYPE_STRING].php_type = IS_STRING;
452
453 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
454 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
455 mysqlnd_ps_fetch_functions[MYSQL_TYPE_DECIMAL].php_type = IS_STRING;
456
457 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
458 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
459 mysqlnd_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].php_type = IS_STRING;
460
461 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
462 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
463 mysqlnd_ps_fetch_functions[MYSQL_TYPE_ENUM].php_type = IS_STRING;
464
465 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
466 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQLND_PS_SKIP_RESULT_STR;
467 mysqlnd_ps_fetch_functions[MYSQL_TYPE_SET].php_type = IS_STRING;
468
469 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
470 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQLND_PS_SKIP_RESULT_STR;
471 mysqlnd_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].php_type= IS_STRING;
472 }
473 /* }}} */
474
475
476 /* {{{ mysqlnd_stmt_copy_it */
477 static enum_func_status
mysqlnd_stmt_copy_it(zval ** copies,zval * original,unsigned int param_count,unsigned int current)478 mysqlnd_stmt_copy_it(zval ** copies, zval * original, unsigned int param_count, unsigned int current)
479 {
480 if (!*copies) {
481 *copies = mnd_ecalloc(param_count, sizeof(zval));
482 }
483 if (*copies) {
484 ZVAL_COPY(&(*copies)[current], original);
485 return PASS;
486 }
487 return FAIL;
488 }
489 /* }}} */
490
491
492 /* {{{ mysqlnd_stmt_free_copies */
493 static void
mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt,zval * copies)494 mysqlnd_stmt_free_copies(MYSQLND_STMT_DATA * stmt, zval *copies)
495 {
496 if (copies) {
497 unsigned int i;
498 for (i = 0; i < stmt->param_count; i++) {
499 zval_ptr_dtor(&copies[i]);
500 }
501 mnd_efree(copies);
502 }
503 }
504 /* }}} */
505
506
507 /* {{{ mysqlnd_stmt_execute_check_n_enlarge_buffer */
508 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)509 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)
510 {
511 const size_t overalloc = 5;
512 size_t left = (*buf_len - (*p - *buf));
513
514 if (left < (needed_bytes + overalloc)) {
515 const size_t offset = *p - *buf;
516 zend_uchar *tmp_buf;
517 *buf_len = offset + needed_bytes + overalloc;
518 tmp_buf = mnd_emalloc(*buf_len);
519 if (!tmp_buf) {
520 return FAIL;
521 }
522 memcpy(tmp_buf, *buf, offset);
523 if (*buf != provided_buffer) {
524 mnd_efree(*buf);
525 }
526 *buf = tmp_buf;
527 /* Update our pos pointer */
528 *p = *buf + offset;
529 }
530 return PASS;
531 }
532 /* }}} */
533
534
535 /* {{{ mysqlnd_stmt_execute_prepare_param_types */
536 static enum_func_status
mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt,zval ** copies_param,int * resend_types_next_time)537 mysqlnd_stmt_execute_prepare_param_types(MYSQLND_STMT_DATA * stmt, zval ** copies_param, int * resend_types_next_time)
538 {
539 unsigned int i;
540 DBG_ENTER("mysqlnd_stmt_execute_prepare_param_types");
541 for (i = 0; i < stmt->param_count; i++) {
542 const short current_type = stmt->param_bind[i].type;
543 zval *parameter = &stmt->param_bind[i].zv;
544
545 ZVAL_DEREF(parameter);
546 if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG || current_type == MYSQL_TYPE_TINY)) {
547 /* always copy the var, because we do many conversions */
548 if (Z_TYPE_P(parameter) != IS_LONG &&
549 PASS != mysqlnd_stmt_copy_it(copies_param, parameter, stmt->param_count, i))
550 {
551 SET_OOM_ERROR(stmt->error_info);
552 goto end;
553 }
554 /*
555 if it doesn't fit in a long send it as a string.
556 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
557 */
558 if (Z_TYPE_P(parameter) != IS_LONG) {
559 zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: parameter;
560 /*
561 Because converting to double and back to long can lead
562 to losing precision we need second variable. Conversion to double is to see if
563 value is too big for a long. As said, precision could be lost.
564 */
565 double d = zval_get_double(tmp_data);
566
567 /*
568 if it doesn't fit in a long send it as a string.
569 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
570 We do transformation here, which will be used later when sending types. The code later relies on this.
571 */
572 if (d >= (double) ZEND_LONG_MAX || d < (double) ZEND_LONG_MIN) {
573 stmt->send_types_to_server = *resend_types_next_time = 1;
574 convert_to_string(tmp_data);
575 } else {
576 convert_to_long(tmp_data);
577 }
578 }
579 }
580 }
581 DBG_RETURN(PASS);
582 end:
583 DBG_RETURN(FAIL);
584 }
585 /* }}} */
586
587
588 /* {{{ mysqlnd_stmt_execute_store_types */
589 static void
mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt,zval * copies,zend_uchar ** p)590 mysqlnd_stmt_execute_store_types(MYSQLND_STMT_DATA * stmt, zval * copies, zend_uchar ** p)
591 {
592 unsigned int i;
593 for (i = 0; i < stmt->param_count; i++) {
594 short current_type = stmt->param_bind[i].type;
595 zval *parameter = &stmt->param_bind[i].zv;
596 /* our types are not unsigned */
597 #if SIZEOF_ZEND_LONG==8
598 if (current_type == MYSQL_TYPE_LONG) {
599 current_type = MYSQL_TYPE_LONGLONG;
600 }
601 #endif
602 ZVAL_DEREF(parameter);
603 if (!Z_ISNULL_P(parameter) && (current_type == MYSQL_TYPE_LONG || current_type == MYSQL_TYPE_LONGLONG)) {
604 /*
605 if it doesn't fit in a long send it as a string.
606 Check bug #52891 : Wrong data inserted with mysqli/mysqlnd when using bind_param, value > LONG_MAX
607 */
608 if (Z_TYPE_P(parameter) != IS_LONG) {
609 const zval *tmp_data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i] : parameter;
610 /*
611 In case of IS_LONG we do nothing, it is ok, in case of string, we just need to set current_type.
612 The actual transformation has been performed several dozens line above.
613 */
614 if (Z_TYPE_P(tmp_data) == IS_STRING) {
615 current_type = MYSQL_TYPE_VAR_STRING;
616 /*
617 don't change stmt->param_bind[i].type to MYSQL_TYPE_VAR_STRING
618 we force convert_to_long in all cases, thus the type will be right in the next switch.
619 if the type is however not long, then we will do a goto in the next switch.
620 We want to preserve the original bind type given by the user. Thus, we do these hacks.
621 */
622 }
623 }
624 }
625 int2store(*p, current_type);
626 *p+= 2;
627 }
628 }
629 /* }}} */
630
631
632 /* {{{ mysqlnd_stmt_execute_calculate_param_values_size */
633 static enum_func_status
mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt,zval ** copies_param,size_t * data_size)634 mysqlnd_stmt_execute_calculate_param_values_size(MYSQLND_STMT_DATA * stmt, zval ** copies_param, size_t * data_size)
635 {
636 unsigned int i;
637 DBG_ENTER("mysqlnd_stmt_execute_calculate_param_values_size");
638 for (i = 0; i < stmt->param_count; i++) {
639 unsigned short is_longlong = 0;
640 unsigned int j;
641 zval *bind_var, *the_var = &stmt->param_bind[i].zv;
642
643 bind_var = the_var;
644 ZVAL_DEREF(the_var);
645 if ((stmt->param_bind[i].type != MYSQL_TYPE_LONG_BLOB && Z_TYPE_P(the_var) == IS_NULL)) {
646 continue;
647 }
648
649 if (Z_ISREF_P(bind_var)) {
650 for (j = i + 1; j < stmt->param_count; j++) {
651 if (Z_ISREF(stmt->param_bind[j].zv) && Z_REFVAL(stmt->param_bind[j].zv) == the_var) {
652 /* Double binding of the same zval, make a copy */
653 if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
654 if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
655 SET_OOM_ERROR(stmt->error_info);
656 goto end;
657 }
658 }
659 break;
660 }
661 }
662 }
663
664 switch (stmt->param_bind[i].type) {
665 case MYSQL_TYPE_DOUBLE:
666 *data_size += 8;
667 if (Z_TYPE_P(the_var) != IS_DOUBLE) {
668 if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
669 if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
670 SET_OOM_ERROR(stmt->error_info);
671 goto end;
672 }
673 }
674 }
675 break;
676 case MYSQL_TYPE_LONGLONG:
677 is_longlong = 4;
678 ZEND_FALLTHROUGH;
679 case MYSQL_TYPE_LONG:
680 {
681 zval *tmp_data = (*copies_param && !Z_ISUNDEF((*copies_param)[i]))? &(*copies_param)[i]: the_var;
682 if (Z_TYPE_P(tmp_data) == IS_STRING) {
683 goto use_string;
684 }
685 convert_to_long(tmp_data);
686 }
687 *data_size += 4 + is_longlong;
688 break;
689 case MYSQL_TYPE_LONG_BLOB:
690 if (!(stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED)) {
691 /*
692 User hasn't sent anything, we will send empty string.
693 Empty string has length of 0, encoded in 1 byte. No real
694 data will follow after it.
695 */
696 (*data_size)++;
697 }
698 break;
699 case MYSQL_TYPE_VAR_STRING:
700 use_string:
701 *data_size += 8; /* max 8 bytes for size */
702 if (Z_TYPE_P(the_var) != IS_STRING) {
703 if (!*copies_param || Z_ISUNDEF((*copies_param)[i])) {
704 if (PASS != mysqlnd_stmt_copy_it(copies_param, the_var, stmt->param_count, i)) {
705 SET_OOM_ERROR(stmt->error_info);
706 goto end;
707 }
708 }
709 the_var = &((*copies_param)[i]);
710 }
711
712 if (!try_convert_to_string(the_var)) {
713 goto end;
714 }
715 *data_size += Z_STRLEN_P(the_var);
716 break;
717 }
718 }
719 DBG_RETURN(PASS);
720 end:
721 DBG_RETURN(FAIL);
722 }
723 /* }}} */
724
725
726 /* {{{ mysqlnd_stmt_execute_store_param_values */
727 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)728 mysqlnd_stmt_execute_store_param_values(MYSQLND_STMT_DATA * stmt, zval * copies, zend_uchar * buf, zend_uchar ** p, size_t null_byte_offset)
729 {
730 unsigned int i;
731 for (i = 0; i < stmt->param_count; i++) {
732 zval *data, *parameter = &stmt->param_bind[i].zv;
733
734 ZVAL_DEREF(parameter);
735 data = (copies && !Z_ISUNDEF(copies[i]))? &copies[i]: parameter;
736 /* Handle long data */
737 if (!Z_ISUNDEF_P(parameter) && Z_TYPE_P(data) == IS_NULL) {
738 (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
739 } else {
740 switch (stmt->param_bind[i].type) {
741 case MYSQL_TYPE_DOUBLE:
742 convert_to_double(data);
743 float8store(*p, Z_DVAL_P(data));
744 (*p) += 8;
745 break;
746 case MYSQL_TYPE_LONGLONG:
747 if (Z_TYPE_P(data) == IS_STRING) {
748 goto send_string;
749 }
750 /* data has alreade been converted to long */
751 int8store(*p, Z_LVAL_P(data));
752 (*p) += 8;
753 break;
754 case MYSQL_TYPE_LONG:
755 if (Z_TYPE_P(data) == IS_STRING) {
756 goto send_string;
757 }
758 /* data has alreade been converted to long */
759 int4store(*p, Z_LVAL_P(data));
760 (*p) += 4;
761 break;
762 case MYSQL_TYPE_TINY:
763 if (Z_TYPE_P(data) == IS_STRING) {
764 goto send_string;
765 }
766 int1store(*p, Z_LVAL_P(data));
767 (*p)++;
768 break;
769 case MYSQL_TYPE_LONG_BLOB:
770 if (stmt->param_bind[i].flags & MYSQLND_PARAM_BIND_BLOB_USED) {
771 stmt->param_bind[i].flags &= ~MYSQLND_PARAM_BIND_BLOB_USED;
772 } else {
773 /* send_long_data() not called, send empty string */
774 *p = php_mysqlnd_net_store_length(*p, 0);
775 }
776 break;
777 case MYSQL_TYPE_VAR_STRING:
778 send_string:
779 {
780 const size_t len = Z_STRLEN_P(data);
781 /* to is after p. The latter hasn't been moved */
782 *p = php_mysqlnd_net_store_length(*p, len);
783 memcpy(*p, Z_STRVAL_P(data), len);
784 (*p) += len;
785 }
786 break;
787 default:
788 /* Won't happen, but set to NULL */
789 (buf + null_byte_offset)[i/8] |= (zend_uchar) (1 << (i & 7));
790 break;
791 }
792 }
793 }
794 }
795 /* }}} */
796
797
798 /* {{{ mysqlnd_stmt_execute_store_params */
799 static enum_func_status
mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s,zend_uchar ** buf,zend_uchar ** p,size_t * buf_len)800 mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len )
801 {
802 MYSQLND_STMT_DATA * stmt = s->data;
803 zend_uchar * provided_buffer = *buf;
804 size_t data_size = 0;
805 zval *copies = NULL;/* if there are different types */
806 enum_func_status ret = FAIL;
807 int resend_types_next_time = 0;
808 size_t null_byte_offset;
809
810 DBG_ENTER("mysqlnd_stmt_execute_store_params");
811
812 {
813 unsigned int null_count = (stmt->param_count + 7) / 8;
814 if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, null_count)) {
815 SET_OOM_ERROR(stmt->error_info);
816 goto end;
817 }
818 /* put `null` bytes */
819 null_byte_offset = *p - *buf;
820 memset(*p, 0, null_count);
821 *p += null_count;
822 }
823
824 /* 1. Store type information */
825 /*
826 check if need to send the types even if stmt->send_types_to_server is 0. This is because
827 if we send "i" (42) then the type will be int and the server will expect int. However, if next
828 time we try to send > LONG_MAX, the conversion to string will send a string and the server
829 won't expect it and interpret the value as 0. Thus we need to resend the types, if any such values
830 occur, and force resend for the next execution.
831 */
832 if (FAIL == mysqlnd_stmt_execute_prepare_param_types(stmt, &copies, &resend_types_next_time)) {
833 goto end;
834 }
835
836 int1store(*p, stmt->send_types_to_server);
837 (*p)++;
838
839 if (stmt->send_types_to_server) {
840 if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, stmt->param_count * 2)) {
841 SET_OOM_ERROR(stmt->error_info);
842 goto end;
843 }
844 mysqlnd_stmt_execute_store_types(stmt, copies, p);
845 }
846
847 stmt->send_types_to_server = resend_types_next_time;
848
849 /* 2. Store data */
850 /* 2.1 Calculate how much space we need */
851 if (FAIL == mysqlnd_stmt_execute_calculate_param_values_size(stmt, &copies, &data_size)) {
852 goto end;
853 }
854
855 /* 2.2 Enlarge the buffer, if needed */
856 if (FAIL == mysqlnd_stmt_execute_check_n_enlarge_buffer(buf, p, buf_len, provided_buffer, data_size)) {
857 SET_OOM_ERROR(stmt->error_info);
858 goto end;
859 }
860
861 /* 2.3 Store the actual data */
862 mysqlnd_stmt_execute_store_param_values(stmt, copies, *buf, p, null_byte_offset);
863
864 ret = PASS;
865 end:
866 mysqlnd_stmt_free_copies(stmt, copies);
867
868 DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
869 DBG_RETURN(ret);
870 }
871 /* }}} */
872
873
874 /* {{{ mysqlnd_stmt_execute_generate_request */
875 enum_func_status
mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s,zend_uchar ** request,size_t * request_len,bool * free_buffer)876 mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** request, size_t *request_len, bool * free_buffer)
877 {
878 MYSQLND_STMT_DATA * stmt = s->data;
879 zend_uchar *p = stmt->execute_cmd_buffer.buffer,
880 *cmd_buffer = stmt->execute_cmd_buffer.buffer;
881 size_t cmd_buffer_length = stmt->execute_cmd_buffer.length;
882 enum_func_status ret = PASS;
883
884 DBG_ENTER("mysqlnd_stmt_execute_generate_request");
885
886 int4store(p, stmt->stmt_id);
887 p += 4;
888
889 /* flags is 4 bytes, we store just 1 */
890 int1store(p, (zend_uchar) stmt->flags);
891 p++;
892
893 /* Make it all zero */
894 int4store(p, 0);
895
896 int1store(p, 1); /* and send 1 for iteration count */
897 p+= 4;
898
899 if (stmt->param_count != 0) {
900 ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length);
901 }
902
903 *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer);
904 *request_len = (p - cmd_buffer);
905 *request = cmd_buffer;
906 DBG_INF_FMT("ret=%s", ret == PASS? "PASS":"FAIL");
907 DBG_RETURN(ret);
908 }
909 /* }}} */
910