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