1/****************************************************************************
2 Copyright (C) 2012 Monty Program AB
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with this library; if not see <http://www.gnu.org/licenses>
16 or write to the Free Software Foundation, Inc.,
17 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
18
19 Part of this code includes code from the PHP project which
20 is freely available from http://www.php.net
21*****************************************************************************/
22
23/* The implementation for prepared statements was ported from PHP's mysqlnd
24 extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
25
26 Original file header:
27 +----------------------------------------------------------------------+
28 | PHP Version 5 |
29 +----------------------------------------------------------------------+
30 | Copyright (c) 2006-2011 The PHP Group |
31 +----------------------------------------------------------------------+
32 | This source file is subject to version 3.01 of the PHP license, |
33 | that is bundled with this package in the file LICENSE, and is |
34 | available through the world-wide-web at the following url: |
35 | http://www.php.net/license/3_01.txt |
36 | If you did not receive a copy of the PHP license and are unable to |
37 | obtain it through the world-wide-web, please send a note to |
38 | license@php.net so we can mail you a copy immediately. |
39 +----------------------------------------------------------------------+
40 | Authors: Georg Richter <georg@mysql.com> |
41 | Andrey Hristov <andrey@mysql.com> |
42 | Ulf Wendel <uwendel@mysql.com> |
43 +----------------------------------------------------------------------+
44*/
45
46#include "ma_global.h"
47#include <ma_sys.h>
48#include <ma_string.h>
49#include <mariadb_ctype.h>
50#include "mysql.h"
51#include <math.h> /* ceil() */
52
53#ifdef WIN32
54#include <malloc.h>
55#endif
56
57#define MYSQL_SILENT
58
59/* ranges for C-binding */
60#define UINT_MAX32 0xFFFFFFFFL
61#define UINT_MAX24 0x00FFFFFF
62#define UINT_MAX16 0xFFFF
63#ifndef INT_MIN8
64#define INT_MIN8 (~0x7F)
65#define INT_MAX8 0x7F
66#endif
67#define UINT_MAX8 0xFF
68
69 #define MAX_DOUBLE_STRING_REP_LENGTH 300
70#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
71#define LONGLONG_MIN ((long long) 0x8000000000000000LL)
72#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL)
73#endif
74
75#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)
76/* First check for ANSI C99 definition: */
77#ifdef ULLONG_MAX
78#define ULONGLONG_MAX ULLONG_MAX
79#else
80#define ULONGLONG_MAX ((unsigned long long)(~0ULL))
81#endif
82#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/
83
84#define YY_PART_YEAR 70
85
86MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1];
87my_bool mysql_ps_subsystem_initialized= 0;
88
89
90#define NUMERIC_TRUNCATION(val,min_range, max_range)\
91 ((((val) > (max_range)) || ((val) < (min_range)) ? 1 : 0))
92
93
94void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
95{
96 while (len-- != 0) *--dst = *--src;
97}
98
99/* {{{ ps_fetch_from_1_to_8_bytes */
100void ps_fetch_from_1_to_8_bytes(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
101 unsigned char **row, unsigned int byte_count)
102{
103 my_bool is_unsigned= test(field->flags & UNSIGNED_FLAG);
104 r_param->buffer_length= byte_count;
105 switch (byte_count) {
106 case 1:
107 *(uchar *)r_param->buffer= **row;
108 *r_param->error= is_unsigned != r_param->is_unsigned && *(uchar *)r_param->buffer > INT_MAX8;
109 break;
110 case 2:
111 shortstore(r_param->buffer, ((ushort) sint2korr(*row)));
112 *r_param->error= is_unsigned != r_param->is_unsigned && *(ushort *)r_param->buffer > INT_MAX16;
113 break;
114 case 4:
115 {
116 longstore(r_param->buffer, ((uint32)sint4korr(*row)));
117 *r_param->error= is_unsigned != r_param->is_unsigned && *(uint32 *)r_param->buffer > INT_MAX32;
118 }
119 break;
120 case 8:
121 {
122 ulonglong val= (ulonglong)sint8korr(*row);
123 longlongstore(r_param->buffer, val);
124 *r_param->error= is_unsigned != r_param->is_unsigned && val > LONGLONG_MAX ;
125 }
126 break;
127 default:
128 r_param->buffer_length= 0;
129 break;
130 }
131 (*row)+= byte_count;
132}
133/* }}} */
134
135static longlong my_atoll(const char *number, const char *end, int *error)
136{
137 char buffer[255];
138 longlong llval= 0;
139 size_t i;
140 *error= 0;
141 /* set error at the following conditions:
142 - string contains invalid character(s)
143 - length > 254
144 - strtoll returns invalid range
145 */
146
147 memcpy(buffer, number, MIN((uint)(end - number), 254));
148 buffer[(uint)(end - number)]= 0;
149
150 errno= 0;
151#ifdef _MSC_VER
152 llval = _strtoi64(buffer, NULL, 10);
153#else
154 llval= strtoll(buffer, NULL, 10);
155#endif
156
157 /* check size */
158 if ((uint)(end - number) > 254)
159 {
160 *error= 1;
161 return llval;
162 }
163
164 /* check characters */
165 for (i=0; i < strlen(buffer); i++)
166 {
167 if ((buffer[i] < '0' || buffer[i] > '9') && !isspace(buffer[i]))
168 {
169 *error= 1;
170 return llval;
171 }
172 }
173
174 /* check strtoll result */
175 if (errno == ERANGE)
176 *error= errno;
177 return llval;
178}
179
180double my_atod(const char *number, const char *end, int *error)
181{
182 double val= 0.0;
183 char buffer[255];
184 int len= (int)(end - number);
185
186 if (len > 254)
187 *error= 1;
188
189 len= MIN(len, 254);
190 memcpy(&buffer, number, len);
191 buffer[len]= '\0';
192
193 val= strtod(buffer, NULL);
194/* if (!*error)
195 *error= errno; */
196 return val;
197}
198
199my_bool str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
200{
201 my_bool is_time=0, is_date=0, has_time_frac=0;
202 char *p= (char *)str;
203
204 if ((p= strchr(str, '-')) && p <= str + length)
205 is_date= 1;
206 if ((p= strchr(str, ':')) && p <= str + length)
207 is_time= 1;
208 if ((p= strchr(str, '.')) && p <= str + length)
209 has_time_frac= 1;
210
211 p= (char *)str;
212
213 memset(tm, 0, sizeof(MYSQL_TIME));
214
215 if (is_date)
216 {
217 sscanf(str, "%d-%d-%d", &tm->year, &tm->month, &tm->day);
218 p= strchr(str, ' ');
219 if (!p)
220 {
221 tm->time_type= MYSQL_TIMESTAMP_DATE;
222 return 0;
223 }
224 }
225 if (has_time_frac)
226 {
227 sscanf(p, "%d:%d:%d.%ld", &tm->hour, &tm->minute, &tm->second, &tm->second_part);
228 tm->time_type= (is_date) ? MYSQL_TIMESTAMP_DATETIME : MYSQL_TIMESTAMP_TIME;
229 return 0;
230 }
231 if (is_time)
232 {
233 sscanf(p, "%d:%d:%d", &tm->hour, &tm->minute, &tm->second);
234 tm->time_type= (is_date) ? MYSQL_TIMESTAMP_DATETIME : MYSQL_TIMESTAMP_TIME;
235 return 0;
236 }
237 return 1;
238}
239
240
241static void convert_froma_string(MYSQL_BIND *r_param, char *buffer, size_t len)
242{
243 int error= 0;
244 switch (r_param->buffer_type)
245 {
246 case MYSQL_TYPE_TINY:
247 {
248 longlong val= my_atoll(buffer, buffer + len, &error);
249 *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8) || error > 0;
250 int1store(r_param->buffer, (uchar) val);
251 r_param->buffer_length= sizeof(uchar);
252 }
253 break;
254 case MYSQL_TYPE_YEAR:
255 case MYSQL_TYPE_SHORT:
256 {
257 longlong val= my_atoll(buffer, buffer + len, &error);
258 *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16) || error > 0;
259 shortstore(r_param->buffer, (short)val);
260 r_param->buffer_length= sizeof(short);
261 }
262 break;
263 case MYSQL_TYPE_LONG:
264 {
265 longlong val= my_atoll(buffer, buffer + len, &error);
266 *r_param->error=error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32) || error > 0;
267 longstore(r_param->buffer, (int32)val);
268 r_param->buffer_length= sizeof(uint32);
269 }
270 break;
271 case MYSQL_TYPE_LONGLONG:
272 {
273 longlong val= my_atoll(buffer, buffer + len, &error);
274 *r_param->error= error > 0; /* no need to check for truncation */
275 longlongstore(r_param->buffer, val);
276 r_param->buffer_length= sizeof(longlong);
277 }
278 break;
279 case MYSQL_TYPE_DOUBLE:
280 {
281 double val= my_atod(buffer, buffer + len, &error);
282 *r_param->error= error > 0; /* no need to check for truncation */
283 doublestore((uchar *)r_param->buffer, val);
284 r_param->buffer_length= sizeof(double);
285 }
286 break;
287 case MYSQL_TYPE_FLOAT:
288 {
289 float val= (float)my_atod(buffer, buffer + len, &error);
290 *r_param->error= error > 0; /* no need to check for truncation */
291 floatstore((uchar *)r_param->buffer, val);
292 r_param->buffer_length= sizeof(float);
293 }
294 break;
295 case MYSQL_TYPE_TIME:
296 case MYSQL_TYPE_DATE:
297 case MYSQL_TYPE_DATETIME:
298 case MYSQL_TYPE_TIMESTAMP:
299 {
300 MYSQL_TIME *tm= (MYSQL_TIME *)r_param->buffer;
301 str_to_TIME(buffer, len, tm);
302 break;
303 }
304 break;
305 case MYSQL_TYPE_TINY_BLOB:
306 case MYSQL_TYPE_MEDIUM_BLOB:
307 case MYSQL_TYPE_LONG_BLOB:
308 case MYSQL_TYPE_BLOB:
309 case MYSQL_TYPE_DECIMAL:
310 case MYSQL_TYPE_NEWDECIMAL:
311 default:
312 {
313 char *start= buffer + r_param->offset; /* stmt_fetch_column sets offset */
314 char *end= buffer + len;
315 size_t copylen= 0;
316
317 if (start < end)
318 {
319 copylen= end - start;
320 if (r_param->buffer_length)
321 memcpy(r_param->buffer, start, MIN(copylen, r_param->buffer_length));
322 }
323 if (copylen < r_param->buffer_length)
324 ((char *)r_param->buffer)[copylen]= 0;
325 *r_param->error= (copylen > r_param->buffer_length);
326
327 *r_param->length= (ulong)len;
328 }
329 break;
330 }
331}
332
333static void convert_from_long(MYSQL_BIND *r_param, const MYSQL_FIELD *field, longlong val, my_bool is_unsigned)
334{
335 switch (r_param->buffer_type) {
336 case MYSQL_TYPE_TINY:
337 *(uchar *)r_param->buffer= (uchar)val;
338 *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8);
339 r_param->buffer_length= 1;
340 break;
341 case MYSQL_TYPE_SHORT:
342 case MYSQL_TYPE_YEAR:
343 shortstore(r_param->buffer, (short)val);
344 *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16);
345 r_param->buffer_length= 2;
346 break;
347 case MYSQL_TYPE_LONG:
348 longstore(r_param->buffer, (int32)val);
349 *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32);
350 r_param->buffer_length= 4;
351 break;
352 case MYSQL_TYPE_LONGLONG:
353 *r_param->error= (val < 0 && r_param->is_unsigned != is_unsigned);
354 longlongstore(r_param->buffer, val);
355 r_param->buffer_length= 8;
356 break;
357 case MYSQL_TYPE_DOUBLE:
358 {
359 volatile double dbl;
360
361 dbl= (is_unsigned) ? ulonglong2double((ulonglong)val) : (double)val;
362 doublestore(r_param->buffer, dbl);
363
364 *r_param->error = (dbl != ceil(dbl)) ||
365 (is_unsigned ? (ulonglong )dbl != (ulonglong)val :
366 (longlong)dbl != (longlong)val);
367
368 r_param->buffer_length= 8;
369 break;
370 }
371 case MYSQL_TYPE_FLOAT:
372 {
373 float fval;
374 fval= is_unsigned ? (float)(ulonglong)(val) : (float)val;
375 floatstore((uchar *)r_param->buffer, fval);
376 *r_param->error= (fval != ceilf(fval)) ||
377 (is_unsigned ? (ulonglong)fval != (ulonglong)val :
378 (longlong)fval != val);
379 r_param->buffer_length= 4;
380 }
381 break;
382 default:
383 {
384 char *buffer;
385 char *endptr;
386 uint len;
387
388 buffer= alloca(MAX(field->length, 22));
389 endptr= ma_ll2str(val, buffer, is_unsigned ? 10 : -10);
390 len= (uint)(endptr - buffer);
391
392 /* check if field flag is zerofill */
393 if (field->flags & ZEROFILL_FLAG &&
394 len < field->length && len < r_param->buffer_length)
395 {
396 ma_bmove_upp(buffer + field->length, buffer + len, len);
397 memset((char*) buffer, '0', field->length - len);
398 len= field->length;
399 }
400 convert_froma_string(r_param, buffer, len);
401 }
402 break;
403 }
404}
405
406
407/* {{{ ps_fetch_null */
408static
409void ps_fetch_null(MYSQL_BIND *r_param __attribute__((unused)),
410 const MYSQL_FIELD * field __attribute__((unused)),
411 unsigned char **row __attribute__((unused)))
412{
413 /* do nothing */
414}
415/* }}} */
416
417#define GET_LVALUE_FROM_ROW(is_unsigned, data, ucast, scast)\
418 (is_unsigned) ? (longlong)(ucast) *(longlong *)(data) : (longlong)(scast) *(longlong *)(data)
419/* {{{ ps_fetch_int8 */
420static
421void ps_fetch_int8(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
422 unsigned char **row)
423{
424 switch(r_param->buffer_type) {
425 case MYSQL_TYPE_TINY:
426 ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
427 break;
428 default:
429 {
430 uchar val= **row;
431 longlong lval= field->flags & UNSIGNED_FLAG ? (longlong) val : (longlong)(signed char)val;
432 convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
433 (*row) += 1;
434 }
435 break;
436 }
437}
438/* }}} */
439
440
441/* {{{ ps_fetch_int16 */
442static
443void ps_fetch_int16(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
444 unsigned char **row)
445{
446 switch (r_param->buffer_type) {
447 case MYSQL_TYPE_YEAR:
448 case MYSQL_TYPE_SHORT:
449 ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
450 break;
451 default:
452 {
453 short sval= sint2korr(*row);
454 longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ushort) sval : (longlong)sval;
455 convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
456 (*row) += 2;
457 }
458 break;
459 }
460}
461/* }}} */
462
463
464/* {{{ ps_fetch_int32 */
465static
466void ps_fetch_int32(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
467 unsigned char **row)
468{
469 switch (r_param->buffer_type) {
470/* case MYSQL_TYPE_TINY:
471 ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
472 break;
473 case MYSQL_TYPE_YEAR:
474 case MYSQL_TYPE_SHORT:
475 ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
476 break; */
477 case MYSQL_TYPE_INT24:
478 case MYSQL_TYPE_LONG:
479 ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
480 break;
481 default:
482 {
483 int32 sval= sint4korr(*row);
484 longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(uint32) sval : (longlong)sval;
485 convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
486 (*row) += 4;
487 }
488 break;
489 }
490}
491/* }}} */
492
493
494/* {{{ ps_fetch_int64 */
495static
496void ps_fetch_int64(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
497 unsigned char **row)
498{
499 switch(r_param->buffer_type)
500 {
501/* case MYSQL_TYPE_TINY:
502 ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
503 break;
504 case MYSQL_TYPE_YEAR:
505 case MYSQL_TYPE_SHORT:
506 ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
507 break;
508 case MYSQL_TYPE_INT24:
509 case MYSQL_TYPE_LONG:
510 ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
511 break; */
512 case MYSQL_TYPE_LONGLONG:
513 ps_fetch_from_1_to_8_bytes(r_param, field, row, 8);
514 break;
515 default:
516 {
517 longlong sval= (longlong)sint8korr(*row);
518 longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ulonglong) sval : (longlong)sval;
519 convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
520 (*row) += 8;
521 }
522 break;
523 }
524}
525/* }}} */
526
527static void convert_from_float(MYSQL_BIND *r_param, const MYSQL_FIELD *field, float val, int size __attribute__((unused)))
528{
529 double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
530 char *buf= (char *)r_param->buffer;
531 switch (r_param->buffer_type)
532 {
533 case MYSQL_TYPE_TINY:
534 *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
535 *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
536 (double)((int8)*buf));
537 r_param->buffer_length= 1;
538 break;
539 case MYSQL_TYPE_SHORT:
540 case MYSQL_TYPE_YEAR:
541 {
542 if (r_param->is_unsigned)
543 {
544 ushort sval= (ushort)val;
545 shortstore(buf, sval);
546 *r_param->error= check_trunc_val != (double)sval;
547 } else {
548 short sval= (short)val;
549 shortstore(buf, sval);
550 *r_param->error= check_trunc_val != (double)sval;
551 }
552 r_param->buffer_length= 2;
553 }
554 break;
555 case MYSQL_TYPE_LONG:
556 {
557 if (r_param->is_unsigned)
558 {
559 uint32 lval= (uint32)val;
560 longstore(buf, lval);
561 *r_param->error= (check_trunc_val != (double)lval);
562 } else {
563 int32 lval= (int32)val;
564 longstore(buf, lval);
565 *r_param->error= (check_trunc_val != (double)lval);
566 }
567 r_param->buffer_length= 4;
568 }
569 break;
570 case MYSQL_TYPE_LONGLONG:
571 {
572 if (r_param->is_unsigned)
573 {
574 ulonglong llval= (ulonglong)val;
575 longlongstore(buf, llval);
576 *r_param->error= (check_trunc_val != (double)llval);
577 } else {
578 longlong llval= (longlong)val;
579 longlongstore(buf, llval);
580 *r_param->error= (check_trunc_val != (double)llval);
581 }
582 r_param->buffer_length= 8;
583 }
584 break;
585 case MYSQL_TYPE_DOUBLE:
586 {
587 double dval= (double)val;
588 memcpy(buf, &dval, sizeof(double));
589 r_param->buffer_length= 8;
590 }
591 break;
592 default:
593 {
594 char buff[MAX_DOUBLE_STRING_REP_LENGTH];
595 size_t length;
596
597 length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
598
599 if (field->decimals >= NOT_FIXED_DEC)
600 {
601 length= ma_gcvt(val, MY_GCVT_ARG_FLOAT, (int)length, buff, NULL);
602 }
603 else
604 {
605 length= ma_fcvt(val, field->decimals, buff, NULL);
606 }
607
608 /* check if ZEROFILL flag is active */
609 if (field->flags & ZEROFILL_FLAG)
610 {
611 /* enough space available ? */
612 if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
613 break;
614 ma_bmove_upp(buff + field->length, buff + length, length);
615 memset((char*) buff, '0', field->length - length);
616 length= field->length;
617 }
618
619 convert_froma_string(r_param, buff, length);
620 }
621 break;
622 }
623}
624
625static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size __attribute__((unused)))
626{
627 double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
628 char *buf= (char *)r_param->buffer;
629 switch (r_param->buffer_type)
630 {
631 case MYSQL_TYPE_TINY:
632 *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
633 *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
634 (double)((int8)*buf));
635 r_param->buffer_length= 1;
636 break;
637 case MYSQL_TYPE_SHORT:
638 case MYSQL_TYPE_YEAR:
639 {
640 if (r_param->is_unsigned)
641 {
642 ushort sval= (ushort)val;
643 shortstore(buf, sval);
644 *r_param->error= check_trunc_val != (double)sval;
645 } else {
646 short sval= (short)val;
647 shortstore(buf, sval);
648 *r_param->error= check_trunc_val != (double)sval;
649 }
650 r_param->buffer_length= 2;
651 }
652 break;
653 case MYSQL_TYPE_LONG:
654 {
655 if (r_param->is_unsigned)
656 {
657 uint32 lval= (uint32)val;
658 longstore(buf, lval);
659 *r_param->error= (check_trunc_val != (double)lval);
660 } else {
661 int32 lval= (int32)val;
662 longstore(buf, lval);
663 *r_param->error= (check_trunc_val != (double)lval);
664 }
665 r_param->buffer_length= 4;
666 }
667 break;
668 case MYSQL_TYPE_LONGLONG:
669 {
670 if (r_param->is_unsigned)
671 {
672 ulonglong llval= (ulonglong)val;
673 longlongstore(buf, llval);
674 *r_param->error= (check_trunc_val != (double)llval);
675 } else {
676 longlong llval= (longlong)val;
677 longlongstore(buf, llval);
678 *r_param->error= (check_trunc_val != (double)llval);
679 }
680 r_param->buffer_length= 8;
681 }
682 break;
683 case MYSQL_TYPE_FLOAT:
684 {
685 float fval= (float)val;
686 memcpy(buf, &fval, sizeof(float));
687 *r_param->error= (*(float*)buf != fval);
688 r_param->buffer_length= 4;
689 }
690 break;
691 default:
692 {
693 char buff[MAX_DOUBLE_STRING_REP_LENGTH];
694 size_t length;
695
696 length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
697
698 if (field->decimals >= NOT_FIXED_DEC)
699 {
700 length= ma_gcvt(val, MY_GCVT_ARG_DOUBLE, (int)length, buff, NULL);
701 }
702 else
703 {
704 length= ma_fcvt(val, field->decimals, buff, NULL);
705 }
706
707 /* check if ZEROFILL flag is active */
708 if (field->flags & ZEROFILL_FLAG)
709 {
710 /* enough space available ? */
711 if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
712 break;
713 ma_bmove_upp(buff + field->length, buff + length, length);
714 memset((char*) buff, '0', field->length - length);
715 length= field->length;
716 }
717 convert_froma_string(r_param, buff, length);
718 }
719 break;
720 }
721}
722
723
724/* {{{ ps_fetch_double */
725static
726void ps_fetch_double(MYSQL_BIND *r_param, const MYSQL_FIELD * field , unsigned char **row)
727{
728 switch (r_param->buffer_type)
729 {
730 case MYSQL_TYPE_DOUBLE:
731 {
732 double *value= (double *)r_param->buffer;
733 float8get(*value, *row);
734 r_param->buffer_length= 8;
735 }
736 break;
737 default:
738 {
739 double value;
740 float8get(value, *row);
741 convert_from_double(r_param, field, value, sizeof(double));
742 }
743 break;
744 }
745 (*row)+= 8;
746}
747/* }}} */
748
749/* {{{ ps_fetch_float */
750static
751void ps_fetch_float(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row)
752{
753 switch(r_param->buffer_type)
754 {
755 case MYSQL_TYPE_FLOAT:
756 {
757 float *value= (float *)r_param->buffer;
758 float4get(*value, *row);
759 r_param->buffer_length= 4;
760 *r_param->error= 0;
761 }
762 break;
763 default:
764 {
765 float value;
766 memcpy(&value, *row, sizeof(float));
767 float4get(value, (char *)*row);
768 convert_from_float(r_param, field, value, sizeof(float));
769 }
770 break;
771 }
772 (*row)+= 4;
773}
774/* }}} */
775
776static void convert_to_datetime(MYSQL_TIME *t, unsigned char **row, uint len, enum enum_field_types type)
777{
778 memset(t, 0, sizeof(MYSQL_TIME));
779
780 /* binary protocol for datetime:
781 4-bytes: DATE
782 7-bytes: DATE + TIME
783 >7 bytes: DATE + TIME with second_part
784 */
785 if (len)
786 {
787 unsigned char *to= *row;
788 int has_date= 0;
789 uint offset= 7;
790
791 if (type == MYSQL_TYPE_TIME)
792 {
793 t->neg= to[0];
794 t->day= (ulong) sint4korr(to + 1);
795 t->time_type= MYSQL_TIMESTAMP_TIME;
796 offset= 8;
797 to++;
798 } else
799 {
800 t->year= (uint) sint2korr(to);
801 t->month= (uint) to[2];
802 t->day= (uint) to[3];
803 t->time_type= MYSQL_TIMESTAMP_DATE;
804 if (type == MYSQL_TYPE_DATE)
805 return;
806 has_date= 1;
807 }
808
809 if (len > 4)
810 {
811 t->hour= (uint) to[4];
812 if (type == MYSQL_TYPE_TIME)
813 t->hour+= t->day * 24;
814 t->minute= (uint) to[5];
815 t->second= (uint) to[6];
816 if (has_date)
817 t->time_type= MYSQL_TIMESTAMP_DATETIME;
818 }
819 if (len > offset)
820 {
821 t->second_part= (ulong)sint4korr(to+7);
822 }
823 }
824}
825
826
827/* {{{ ps_fetch_datetime */
828static
829void ps_fetch_datetime(MYSQL_BIND *r_param, const MYSQL_FIELD * field,
830 unsigned char **row)
831{
832 MYSQL_TIME *t= (MYSQL_TIME *)r_param->buffer;
833 unsigned int len= net_field_length(row);
834
835 switch (r_param->buffer_type) {
836 case MYSQL_TYPE_DATETIME:
837 case MYSQL_TYPE_TIMESTAMP:
838 convert_to_datetime(t, row, len, field->type);
839 break;
840 case MYSQL_TYPE_DATE:
841 convert_to_datetime(t, row, len, field->type);
842 break;
843 case MYSQL_TYPE_TIME:
844 convert_to_datetime(t, row, len, field->type);
845 t->year= t->day= t->month= 0;
846 break;
847 case MYSQL_TYPE_YEAR:
848 {
849 MYSQL_TIME tm;
850 convert_to_datetime(&tm, row, len, field->type);
851 shortstore(r_param->buffer, tm.year);
852 break;
853 }
854 default:
855 {
856 char dtbuffer[60];
857 MYSQL_TIME tm;
858 size_t length;
859 convert_to_datetime(&tm, row, len, field->type);
860 /*
861 if (tm.time_type== MYSQL_TIMESTAMP_TIME && tm.day)
862 {
863 tm.hour+= tm.day * 24;
864 tm.day=0;
865 }
866*/
867 switch(field->type) {
868 case MYSQL_TYPE_DATE:
869 length= sprintf(dtbuffer, "%04u-%02u-%02u", tm.year, tm.month, tm.day);
870 break;
871 case MYSQL_TYPE_TIME:
872 length= sprintf(dtbuffer, "%s%02u:%02u:%02u", (tm.neg ? "-" : ""), tm.hour, tm.minute, tm.second);
873 if (field->decimals && field->decimals <= 6)
874 {
875 char ms[8];
876 sprintf(ms, ".%06lu", tm.second_part);
877 if (field->decimals < 6)
878 ms[field->decimals + 1]= 0;
879 length+= strlen(ms);
880 strcat(dtbuffer, ms);
881 }
882 break;
883 case MYSQL_TYPE_DATETIME:
884 case MYSQL_TYPE_TIMESTAMP:
885 length= sprintf(dtbuffer, "%04u-%02u-%02u %02u:%02u:%02u", tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second);
886 if (field->decimals && field->decimals <= 6)
887 {
888 char ms[8];
889 sprintf(ms, ".%06lu", tm.second_part);
890 if (field->decimals < 6)
891 ms[field->decimals + 1]= 0;
892 length+= strlen(ms);
893 strcat(dtbuffer, ms);
894 }
895 break;
896 default:
897 dtbuffer[0]= 0;
898 length= 0;
899 break;
900 }
901 convert_froma_string(r_param, dtbuffer, length);
902 break;
903 }
904 }
905 (*row) += len;
906}
907/* }}} */
908
909/* {{{ ps_fetch_string */
910static
911void ps_fetch_string(MYSQL_BIND *r_param,
912 const MYSQL_FIELD *field __attribute__((unused)),
913 unsigned char **row)
914{
915 /* C-API differs from PHP. While PHP just converts string to string,
916 C-API needs to convert the string to the defined type with in
917 the result bind buffer.
918 */
919 ulong field_length= net_field_length(row);
920
921 convert_froma_string(r_param, (char *)*row, field_length);
922 (*row) += field_length;
923}
924/* }}} */
925
926/* {{{ ps_fetch_bin */
927static
928void ps_fetch_bin(MYSQL_BIND *r_param,
929 const MYSQL_FIELD *field,
930 unsigned char **row)
931{
932 if (field->charsetnr == 63)
933 {
934 ulong field_length= *r_param->length= net_field_length(row);
935 uchar *current_pos= (*row) + r_param->offset,
936 *end= (*row) + field_length;
937 size_t copylen= 0;
938
939 if (current_pos < end)
940 {
941 copylen= end - current_pos;
942 if (r_param->buffer_length)
943 memcpy(r_param->buffer, current_pos, MIN(copylen, r_param->buffer_length));
944 }
945 if (copylen < r_param->buffer_length &&
946 (r_param->buffer_type == MYSQL_TYPE_STRING ||
947 r_param->buffer_type == MYSQL_TYPE_JSON))
948 ((char *)r_param->buffer)[copylen]= 0;
949 *r_param->error= copylen > r_param->buffer_length;
950 (*row)+= field_length;
951 }
952 else
953 ps_fetch_string(r_param, field, row);
954}
955/* }}} */
956
957/* {{{ _mysqlnd_init_ps_subsystem */
958void mysql_init_ps_subsystem(void)
959{
960 memset(mysql_ps_fetch_functions, 0, sizeof(mysql_ps_fetch_functions));
961 mysql_ps_fetch_functions[MYSQL_TYPE_NULL].func= ps_fetch_null;
962 mysql_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
963 mysql_ps_fetch_functions[MYSQL_TYPE_NULL].max_len = 0;
964
965 mysql_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
966 mysql_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
967 mysql_ps_fetch_functions[MYSQL_TYPE_TINY].max_len = 4;
968
969 mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
970 mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
971 mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].max_len = 6;
972
973 mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
974 mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
975 mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].max_len = 6;
976
977 mysql_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
978 mysql_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
979 mysql_ps_fetch_functions[MYSQL_TYPE_INT24].max_len = 9;
980
981 mysql_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
982 mysql_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
983 mysql_ps_fetch_functions[MYSQL_TYPE_LONG].max_len = 11;
984
985 mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
986 mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
987 mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].max_len = 21;
988
989 mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
990 mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
991 mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
992
993 mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
994 mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
995 mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
996
997 mysql_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_datetime;
998 mysql_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
999 mysql_ps_fetch_functions[MYSQL_TYPE_TIME].max_len = 17;
1000
1001 mysql_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_datetime;
1002 mysql_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
1003 mysql_ps_fetch_functions[MYSQL_TYPE_DATE].max_len = 10;
1004
1005 mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
1006 mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
1007 mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].max_len = -1;
1008
1009 mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
1010 mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
1011 mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].max_len = 30;
1012
1013 mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
1014 mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
1015 mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].max_len = 30;
1016
1017 mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_bin;
1018 mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
1019 mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].max_len = -1;
1020
1021 mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_bin;
1022 mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1023 mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].max_len = -1;
1024
1025 mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_bin;
1026 mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
1027 mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].max_len = -1;
1028
1029 mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_bin;
1030 mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1031 mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].max_len = -1;
1032
1033 mysql_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bin;
1034 mysql_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1035 mysql_ps_fetch_functions[MYSQL_TYPE_BIT].max_len = -1;
1036
1037 mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
1038 mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1039 mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].max_len = -1;
1040
1041 mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
1042 mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1043 mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].max_len = -1;
1044
1045 mysql_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
1046 mysql_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1047 mysql_ps_fetch_functions[MYSQL_TYPE_STRING].max_len = -1;
1048
1049 mysql_ps_fetch_functions[MYSQL_TYPE_JSON].func = ps_fetch_string;
1050 mysql_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1051 mysql_ps_fetch_functions[MYSQL_TYPE_JSON].max_len = -1;
1052
1053 mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
1054 mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1055 mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].max_len = -1;
1056
1057 mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
1058 mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1059 mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].max_len = -1;
1060
1061 mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
1062 mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1063 mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].max_len = -1;
1064
1065 mysql_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
1066 mysql_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQL_PS_SKIP_RESULT_STR;
1067 mysql_ps_fetch_functions[MYSQL_TYPE_SET].max_len = -1;
1068
1069 mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
1070 mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQL_PS_SKIP_RESULT_STR;
1071 mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].max_len = -1;
1072
1073 mysql_ps_subsystem_initialized= 1;
1074}
1075/* }}} */
1076
1077
1078/*
1079 * Local variables:
1080 * tab-width: 4
1081 * c-basic-offset: 4
1082 * End:
1083 * vim600: noet sw=4 ts=4 fdm=marker
1084 * vim<600: noet sw=4 ts=4
1085 */
1086