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 #include <limits.h>
53 
54 #ifdef WIN32
55 #include <malloc.h>
56 #endif
57 
58 #define MYSQL_SILENT
59 
60 /* ranges for C-binding */
61 #define UINT_MAX32      0xFFFFFFFFL
62 #define UINT_MAX24      0x00FFFFFF
63 #define UINT_MAX16      0xFFFF
64 #ifndef INT_MIN8
65 #define INT_MIN8        (~0x7F)
66 #define INT_MAX8        0x7F
67 #endif
68 #define UINT_MAX8       0xFF
69 
70  #define MAX_DOUBLE_STRING_REP_LENGTH 300
71 #if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
72 #define LONGLONG_MIN    ((long long) 0x8000000000000000LL)
73 #define LONGLONG_MAX    ((long long) 0x7FFFFFFFFFFFFFFFLL)
74 #endif
75 
76 #define MAX_DBL_STR (3 + DBL_MANT_DIG - DBL_MIN_EXP)
77 
78 #if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)
79 /* First check for ANSI C99 definition: */
80 #ifdef ULLONG_MAX
81 #define ULONGLONG_MAX  ULLONG_MAX
82 #else
83 #define ULONGLONG_MAX ((unsigned long long)(~0ULL))
84 #endif
85 #endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/
86 
87 #define YY_PART_YEAR 70
88 
89 MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1];
90 my_bool mysql_ps_subsystem_initialized= 0;
91 
92 
93 #define NUMERIC_TRUNCATION(val,min_range, max_range)\
94   ((((val) > (max_range)) || ((val) < (min_range)) ? 1 : 0))
95 
96 
97 void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
98 {
99   while (len-- != 0) *--dst = *--src;
100 }
101 
102 /* {{{ ps_fetch_from_1_to_8_bytes */
103 void ps_fetch_from_1_to_8_bytes(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
104                 unsigned char **row, unsigned int byte_count)
105 {
106   my_bool is_unsigned= test(field->flags & UNSIGNED_FLAG);
107   r_param->buffer_length= byte_count;
108   switch (byte_count) {
109     case 1:
110       *(uchar *)r_param->buffer= **row;
111       *r_param->error= is_unsigned != r_param->is_unsigned && *(uchar *)r_param->buffer > INT_MAX8;
112       break;
113     case 2:
114       shortstore(r_param->buffer, ((ushort) sint2korr(*row)));
115       *r_param->error= is_unsigned != r_param->is_unsigned && *(ushort *)r_param->buffer > INT_MAX16;
116       break;
117     case 4:
118     {
119       longstore(r_param->buffer, ((uint32)sint4korr(*row)));
120       *r_param->error= is_unsigned != r_param->is_unsigned && *(uint32 *)r_param->buffer > INT_MAX32;
121     }
122     break;
123     case 8:
124       {
125         ulonglong val= (ulonglong)sint8korr(*row);
126         longlongstore(r_param->buffer, val);
127         *r_param->error= is_unsigned != r_param->is_unsigned && val > LONGLONG_MAX ;
128       }
129       break;
130     default:
131       r_param->buffer_length= 0;
132       break;
133   }
134   (*row)+= byte_count;
135 }
136 /* }}} */
137 
138 static unsigned long long my_strtoull(const char *str, size_t len, const char **end, int *err)
139 {
140   unsigned long long val = 0;
141   const char *p = str;
142   const char *end_str = p + len;
143 
144   for (; p < end_str; p++)
145   {
146     if (*p < '0' || *p > '9')
147       break;
148 
149     if (val > ULONGLONG_MAX /10 || val*10 > ULONGLONG_MAX - (*p - '0'))
150     {
151       *err = ERANGE;
152       break;
153     }
154     val = val * 10 + *p -'0';
155   }
156 
157   if (p == str)
158     /* Did not parse anything.*/
159     *err = ERANGE;
160 
161   *end = p;
162   return val;
163 }
164 
165 static long long my_strtoll(const char *str, size_t len, const char **end, int *err)
166 {
167   unsigned long long uval = 0;
168   const char *p = str;
169   const char *end_str = p + len;
170   int neg;
171 
172   while (p < end_str && isspace(*p))
173     p++;
174 
175   if (p == end_str)
176   {
177     *end = p;
178     *err = ERANGE;
179     return 0;
180   }
181 
182   neg = *p == '-';
183   if (neg)
184     p++;
185 
186   uval = my_strtoull(p, (end_str - p), &p, err);
187   *end = p;
188   if (*err)
189     return uval;
190 
191   if (!neg)
192   {
193     /* Overflow of the long long range. */
194     if (uval > LONGLONG_MAX)
195     {
196       *end = p - 1;
197       uval = LONGLONG_MAX;
198       *err = ERANGE;
199     }
200     return uval;
201   }
202 
203   if (uval == (unsigned long long) LONGLONG_MIN)
204     return LONGLONG_MIN;
205 
206   if (uval > LONGLONG_MAX)
207   {
208     *end = p - 1;
209     uval = LONGLONG_MIN;
210     *err = ERANGE;
211   }
212 
213   return -1LL * uval;
214 }
215 
216 
217 static long long my_atoll(const char *str, const char *end_str, int *error)
218 {
219   const char *p=str;
220   const char *end;
221   long long ret;
222   while (p < end_str && isspace(*p))
223     p++;
224 
225   ret = my_strtoll(p, end_str - p, &end, error);
226 
227   while(end < end_str && isspace(*end))
228    end++;
229 
230   if(end != end_str)
231     *error= 1;
232 
233   return ret;
234 }
235 
236 
237 static unsigned long long my_atoull(const char *str, const char *end_str, int *error)
238 {
239   const char *p = str;
240   const char *end;
241   unsigned long long ret;
242 
243   while (p < end_str && isspace(*p))
244     p++;
245 
246   ret = my_strtoull(p, end_str - p, &end, error);
247 
248   while(end < end_str && isspace(*end))
249    end++;
250 
251   if(end != end_str)
252     *error= 1;
253 
254   return ret;
255 }
256 
257 double my_atod(const char *number, const char *end, int *error)
258 {
259   double val= 0.0;
260   char buffer[MAX_DBL_STR + 1];
261   int len= (int)(end - number);
262 
263   *error= errno= 0;
264 
265   if (len > MAX_DBL_STR)
266   {
267     *error= 1;
268     len= MAX_DBL_STR;
269   }
270 
271   memcpy(buffer, number, len);
272   buffer[len]= '\0';
273 
274   val= strtod(buffer, NULL);
275 
276   if (errno)
277     *error= errno;
278 
279   return val;
280 }
281 
282 
283 /*
284   strtoui() version, that works for non-null terminated strings
285 */
286 static unsigned int my_strtoui(const char *str, size_t len, const char **end, int *err)
287 {
288   unsigned long long ull = my_strtoull(str, len, end, err);
289   if (ull > UINT_MAX)
290     *err = ERANGE;
291   return (unsigned int)ull;
292 }
293 
294 /*
295   Parse time, in MySQL format.
296 
297   the input string needs is in form "hour:minute:second[.fraction]"
298   hour, minute and second can have leading zeroes or not,
299   they are not necessarily 2 chars.
300 
301   Hour must be < 838, minute < 60, second < 60
302   Only 6 places of fraction are considered, the value is truncated after 6 places.
303 */
304 static const unsigned int frac_mul[] = { 1000000,100000,10000,1000,100,10 };
305 
306 static int parse_time(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm)
307 {
308   int err= 0;
309   const char *p = str;
310   const char *end = str + length;
311   size_t frac_len;
312   int ret=1;
313 
314   tm->hour = my_strtoui(p, end-p, &p, &err);
315   if (err || tm->hour > 838 || p == end || *p != ':' )
316     goto end;
317 
318   p++;
319   tm->minute = my_strtoui(p, end-p, &p, &err);
320   if (err || tm->minute > 59 || p == end || *p != ':')
321     goto end;
322 
323   p++;
324   tm->second = my_strtoui(p, end-p, &p, &err);
325   if (err || tm->second > 59)
326     goto end;
327 
328   ret = 0;
329   tm->second_part = 0;
330 
331   if (p == end)
332     goto end;
333 
334   /* Check for fractional part*/
335   if (*p != '.')
336     goto end;
337 
338   p++;
339   frac_len = MIN(6,end-p);
340 
341   tm->second_part = my_strtoui(p, frac_len, &p, &err);
342   if (err)
343     goto end;
344 
345   if (frac_len < 6)
346     tm->second_part *= frac_mul[frac_len];
347 
348   ret = 0;
349 
350   /* Consume whole fractional part, even after 6 digits.*/
351   p += frac_len;
352   while(p < *end_ptr)
353   {
354     if (*p < '0' || *p > '9')
355       break;
356     p++;
357   }
358 end:
359   *end_ptr = p;
360   return ret;
361 }
362 
363 
364 /*
365   Parse date, in MySQL format.
366 
367   The input string needs is in form "year-month-day"
368   year, month and day can have leading zeroes or not,
369   they do not have fixed length.
370 
371   Year must be < 10000, month < 12, day < 32
372 
373   Years with 2 digits, are converted to values 1970-2069 according to
374   usual rules:
375 
376   00-69 is converted to 2000-2069.
377   70-99 is converted to 1970-1999.
378 */
379 static int parse_date(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm)
380 {
381   int err = 0;
382   const char *p = str;
383   const char *end = str + length;
384   int ret = 1;
385 
386   tm->year = my_strtoui(p, end - p, &p, &err);
387   if (err || tm->year > 9999 || p == end || *p != '-')
388     goto end;
389 
390   if (p - str == 2) // 2-digit year
391     tm->year += (tm->year >= 70) ? 1900 : 2000;
392 
393   p++;
394   tm->month = my_strtoui(p,end -p, &p, &err);
395   if (err || tm->month > 12 || p == end || *p != '-')
396     goto end;
397 
398   p++;
399   tm->day = my_strtoui(p, end -p , &p, &err);
400   if (err || tm->day > 31)
401     goto end;
402 
403   ret = 0;
404 
405 end:
406   *end_ptr = p;
407   return ret;
408 }
409 
410 /*
411   Parse (not null terminated) string representing
412   TIME, DATE, or DATETIME into MYSQL_TIME structure
413 
414   The supported formats by this functions are
415   - TIME : [-]hours:minutes:seconds[.fraction]
416   - DATE : year-month-day
417   - DATETIME : year-month-day<space>hours:minutes:seconds[.fraction]
418 
419   cf https://dev.mysql.com/doc/refman/8.0/en/datetime.html
420 
421   Whitespaces are trimmed from the start and end of the string.
422   The function ignores junk at the end of the string.
423 
424   Parts of date of time do not have fixed length, so that parsing is compatible with server.
425   However server supports additional formats, e.g YYYYMMDD, HHMMSS, which this function does
426   not support.
427 
428 */
429 int str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
430 {
431   const char *p = str;
432   const char *end = str + length;
433   int is_time = 0;
434 
435   if (!p)
436     goto error;
437 
438   while (p < end && isspace(*p))
439     p++;
440   while (p < end && isspace(end[-1]))
441     end--;
442 
443   if (end -p < 5)
444     goto error;
445 
446   if (*p == '-')
447   {
448     tm->neg = 1;
449     /* Only TIME can't be negative.*/
450     is_time = 1;
451     p++;
452   }
453   else
454   {
455     int i;
456     tm->neg = 0;
457     /*
458       Date parsing (in server) accepts leading zeroes, thus position of the delimiters
459       is not fixed. Scan the string to find out what we need to parse.
460     */
461     for (i = 1; p + i < end; i++)
462     {
463       if(p[i] == '-' || p [i] == ':')
464       {
465         is_time = p[i] == ':';
466         break;
467       }
468     }
469   }
470 
471   if (is_time)
472   {
473     if (parse_time(p, end - p, &p, tm))
474       goto error;
475 
476     tm->year = tm->month = tm->day = 0;
477     tm->time_type = MYSQL_TIMESTAMP_TIME;
478     return 0;
479   }
480 
481   if (parse_date(p, end - p, &p, tm))
482     goto error;
483 
484   if (p == end || p[0] != ' ')
485   {
486     tm->hour = tm->minute = tm->second = tm->second_part = 0;
487     tm->time_type = MYSQL_TIMESTAMP_DATE;
488     return 0;
489   }
490 
491   /* Skip space. */
492   p++;
493   if (parse_time(p, end - p, &p, tm))
494     goto error;
495 
496   /* In DATETIME, hours must be < 24.*/
497   if (tm->hour > 23)
498    goto error;
499 
500   tm->time_type = MYSQL_TIMESTAMP_DATETIME;
501   return 0;
502 
503 error:
504   memset(tm, 0, sizeof(*tm));
505   tm->time_type = MYSQL_TIMESTAMP_ERROR;
506   return 1;
507 }
508 
509 
510 static void convert_froma_string(MYSQL_BIND *r_param, char *buffer, size_t len)
511 {
512   int error= 0;
513   switch (r_param->buffer_type)
514   {
515     case MYSQL_TYPE_TINY:
516     {
517       longlong val= my_atoll(buffer, buffer + len, &error);
518       *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8) || error > 0;
519       int1store(r_param->buffer, (uchar) val);
520       r_param->buffer_length= sizeof(uchar);
521     }
522     break;
523     case MYSQL_TYPE_YEAR:
524     case MYSQL_TYPE_SHORT:
525     {
526       longlong val= my_atoll(buffer, buffer + len, &error);
527       *r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16) || error > 0;
528       shortstore(r_param->buffer, (short)val);
529       r_param->buffer_length= sizeof(short);
530     }
531     break;
532     case MYSQL_TYPE_LONG:
533     {
534       longlong val= my_atoll(buffer, buffer + len, &error);
535       *r_param->error=error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32) || error > 0;
536       longstore(r_param->buffer, (int32)val);
537       r_param->buffer_length= sizeof(uint32);
538     }
539     break;
540     case MYSQL_TYPE_LONGLONG:
541     {
542       longlong val= r_param->is_unsigned ? (longlong)my_atoull(buffer, buffer + len, &error) : my_atoll(buffer, buffer + len, &error);
543       *r_param->error= error > 0; /* no need to check for truncation */
544       longlongstore(r_param->buffer, val);
545       r_param->buffer_length= sizeof(longlong);
546     }
547     break;
548     case MYSQL_TYPE_DOUBLE:
549     {
550       double val= my_atod(buffer, buffer + len, &error);
551       *r_param->error= error > 0; /* no need to check for truncation */
552       doublestore((uchar *)r_param->buffer, val);
553       r_param->buffer_length= sizeof(double);
554     }
555     break;
556     case MYSQL_TYPE_FLOAT:
557     {
558       float val= (float)my_atod(buffer, buffer + len, &error);
559       *r_param->error= error > 0; /* no need to check for truncation */
560       floatstore((uchar *)r_param->buffer, val);
561       r_param->buffer_length= sizeof(float);
562     }
563     break;
564     case MYSQL_TYPE_TIME:
565     case MYSQL_TYPE_DATE:
566     case MYSQL_TYPE_DATETIME:
567     case MYSQL_TYPE_TIMESTAMP:
568     {
569       MYSQL_TIME *tm= (MYSQL_TIME *)r_param->buffer;
570       str_to_TIME(buffer, len, tm);
571       break;
572     }
573     break;
574     case MYSQL_TYPE_TINY_BLOB:
575     case MYSQL_TYPE_MEDIUM_BLOB:
576     case MYSQL_TYPE_LONG_BLOB:
577     case MYSQL_TYPE_BLOB:
578     case MYSQL_TYPE_DECIMAL:
579     case MYSQL_TYPE_NEWDECIMAL:
580     default:
581     {
582       if (len >= r_param->offset)
583       {
584         char *start= buffer + r_param->offset; /* stmt_fetch_column sets offset */
585         char *end= buffer + len;
586         size_t copylen= 0;
587 
588         if (start < end)
589         {
590           copylen= end - start;
591           if (r_param->buffer_length)
592             memcpy(r_param->buffer, start, MIN(copylen, r_param->buffer_length));
593         }
594         if (copylen < r_param->buffer_length)
595           ((char *)r_param->buffer)[copylen]= 0;
596         *r_param->error= (copylen > r_param->buffer_length);
597 
598       }
599       *r_param->length= (ulong)len;
600     }
601     break;
602   }
603 }
604 
605 static void convert_from_long(MYSQL_BIND *r_param, const MYSQL_FIELD *field, longlong val, my_bool is_unsigned)
606 {
607   switch (r_param->buffer_type) {
608     case MYSQL_TYPE_TINY:
609       *(uchar *)r_param->buffer= (uchar)val;
610       *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8);
611       r_param->buffer_length= 1;
612       break;
613     case MYSQL_TYPE_SHORT:
614     case MYSQL_TYPE_YEAR:
615       shortstore(r_param->buffer, (short)val);
616       *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16);
617       r_param->buffer_length= 2;
618       break;
619     case MYSQL_TYPE_LONG:
620       longstore(r_param->buffer, (int32)val);
621       *r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32);
622       r_param->buffer_length= 4;
623       break;
624     case MYSQL_TYPE_LONGLONG:
625       *r_param->error= (val < 0 && r_param->is_unsigned != is_unsigned);
626       longlongstore(r_param->buffer, val);
627       r_param->buffer_length= 8;
628       break;
629     case MYSQL_TYPE_DOUBLE:
630     {
631       volatile double dbl;
632 
633       dbl= (is_unsigned) ? ulonglong2double((ulonglong)val) : (double)val;
634       doublestore(r_param->buffer, dbl);
635 
636       *r_param->error = (dbl != ceil(dbl)) ||
637                          (is_unsigned ? (ulonglong )dbl != (ulonglong)val :
638                                         (longlong)dbl != (longlong)val);
639 
640       r_param->buffer_length= 8;
641       break;
642     }
643     case MYSQL_TYPE_FLOAT:
644     {
645       volatile float fval;
646       fval= is_unsigned ? (float)(ulonglong)(val) : (float)val;
647       floatstore((uchar *)r_param->buffer, fval);
648       *r_param->error= (fval != ceilf(fval)) ||
649                         (is_unsigned ? (ulonglong)fval != (ulonglong)val :
650                                        (longlong)fval != val);
651       r_param->buffer_length= 4;
652     }
653     break;
654     default:
655     {
656       char *buffer;
657       char *endptr;
658       uint len;
659       my_bool zf_truncated= 0;
660 
661       buffer= alloca(MAX(field->length, 22));
662       endptr= ma_ll2str(val, buffer, is_unsigned ? 10 : -10);
663       len= (uint)(endptr - buffer);
664 
665       /* check if field flag is zerofill */
666       if (field->flags & ZEROFILL_FLAG)
667       {
668         uint display_width= MAX(field->length, len);
669         if (display_width < r_param->buffer_length)
670         {
671           ma_bmove_upp(buffer + display_width, buffer + len, len);
672           /* coverity[bad_memset] */
673           memset((void*) buffer, (int) '0', display_width - len);
674           len= display_width;
675         }
676         else
677           zf_truncated= 1;
678       }
679       convert_froma_string(r_param, buffer, len);
680       *r_param->error+= zf_truncated;
681     }
682     break;
683   }
684 }
685 
686 
687 /* {{{ ps_fetch_null */
688 static
689 void ps_fetch_null(MYSQL_BIND *r_param __attribute__((unused)),
690                    const MYSQL_FIELD * field __attribute__((unused)),
691                    unsigned char **row __attribute__((unused)))
692 {
693   /* do nothing */
694 }
695 /* }}} */
696 
697 #define GET_LVALUE_FROM_ROW(is_unsigned, data, ucast, scast)\
698   (is_unsigned) ? (longlong)(ucast) *(longlong *)(data) : (longlong)(scast) *(longlong *)(data)
699 /* {{{ ps_fetch_int8 */
700 static
701 void ps_fetch_int8(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
702            unsigned char **row)
703 {
704   switch(r_param->buffer_type) {
705     case MYSQL_TYPE_TINY:
706       ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
707       break;
708     default:
709     {
710       uchar val= **row;
711       longlong lval= field->flags & UNSIGNED_FLAG ? (longlong) val : (longlong)(signed char)val;
712       convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
713       (*row) += 1;
714     }
715     break;
716   }
717 }
718 /* }}} */
719 
720 
721 /* {{{ ps_fetch_int16 */
722 static
723 void ps_fetch_int16(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
724            unsigned char **row)
725 {
726   switch (r_param->buffer_type) {
727     case MYSQL_TYPE_YEAR:
728     case MYSQL_TYPE_SHORT:
729       ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
730     break;
731     default:
732     {
733       short sval= sint2korr(*row);
734       longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ushort) sval : (longlong)sval;
735       convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
736       (*row) += 2;
737     }
738     break;
739   }
740 }
741 /* }}} */
742 
743 
744 /* {{{ ps_fetch_int32 */
745 static
746 void ps_fetch_int32(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
747            unsigned char **row)
748 {
749   switch (r_param->buffer_type) {
750 /*    case MYSQL_TYPE_TINY:
751       ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
752       break;
753     case MYSQL_TYPE_YEAR:
754     case MYSQL_TYPE_SHORT:
755       ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
756       break; */
757     case MYSQL_TYPE_INT24:
758     case MYSQL_TYPE_LONG:
759       ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
760     break;
761     default:
762     {
763       int32 sval= sint4korr(*row);
764       longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(uint32) sval : (longlong)sval;
765       convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
766       (*row) += 4;
767     }
768     break;
769   }
770 }
771 /* }}} */
772 
773 
774 /* {{{ ps_fetch_int64 */
775 static
776 void ps_fetch_int64(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
777            unsigned char **row)
778 {
779   switch(r_param->buffer_type)
780   {
781 /*    case MYSQL_TYPE_TINY:
782       ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
783       break;
784     case MYSQL_TYPE_YEAR:
785     case MYSQL_TYPE_SHORT:
786       ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
787       break;
788     case MYSQL_TYPE_INT24:
789     case MYSQL_TYPE_LONG:
790       ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
791       break; */
792     case MYSQL_TYPE_LONGLONG:
793       ps_fetch_from_1_to_8_bytes(r_param, field, row, 8);
794     break;
795     default:
796     {
797       longlong sval= (longlong)sint8korr(*row);
798       longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ulonglong) sval : (longlong)sval;
799       convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
800       (*row) += 8;
801     }
802     break;
803   }
804 }
805 /* }}} */
806 
807 static void convert_from_float(MYSQL_BIND *r_param, const MYSQL_FIELD *field, float val, int size __attribute__((unused)))
808 {
809   double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
810   char *buf= (char *)r_param->buffer;
811   switch (r_param->buffer_type)
812   {
813     case MYSQL_TYPE_TINY:
814       *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
815       *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
816                                           (double)((int8)*buf));
817       r_param->buffer_length= 1;
818     break;
819     case MYSQL_TYPE_SHORT:
820     case MYSQL_TYPE_YEAR:
821     {
822       if (r_param->is_unsigned)
823       {
824         ushort sval= (ushort)val;
825         shortstore(buf, sval);
826         *r_param->error= check_trunc_val != (double)sval;
827       } else {
828         short sval= (short)val;
829         shortstore(buf, sval);
830         *r_param->error= check_trunc_val != (double)sval;
831       }
832       r_param->buffer_length= 2;
833     }
834     break;
835     case MYSQL_TYPE_LONG:
836     {
837       if (r_param->is_unsigned)
838       {
839         uint32 lval= (uint32)val;
840         longstore(buf, lval);
841         *r_param->error= (check_trunc_val != (double)lval);
842       } else {
843         int32 lval= (int32)val;
844         longstore(buf, lval);
845         *r_param->error= (check_trunc_val != (double)lval);
846       }
847       r_param->buffer_length= 4;
848     }
849     break;
850     case MYSQL_TYPE_LONGLONG:
851     {
852       if (r_param->is_unsigned)
853       {
854         ulonglong llval= (ulonglong)val;
855         longlongstore(buf, llval);
856         *r_param->error= (check_trunc_val != (double)llval);
857       } else {
858         longlong llval= (longlong)val;
859         longlongstore(buf, llval);
860         *r_param->error= (check_trunc_val != (double)llval);
861       }
862       r_param->buffer_length= 8;
863     }
864     break;
865     case MYSQL_TYPE_DOUBLE:
866     {
867       double dval= (double)val;
868       memcpy(buf, &dval, sizeof(double));
869       r_param->buffer_length= 8;
870     }
871     break;
872     default:
873     {
874       char buff[MAX_DOUBLE_STRING_REP_LENGTH];
875       size_t length;
876 
877       length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
878 
879       if (field->decimals >= NOT_FIXED_DEC)
880       {
881         length= ma_gcvt(val, MY_GCVT_ARG_FLOAT, (int)length, buff, NULL);
882       }
883       else
884       {
885         length= ma_fcvt(val, field->decimals, buff, NULL);
886       }
887 
888       /* check if ZEROFILL flag is active */
889       if (field->flags & ZEROFILL_FLAG)
890       {
891         /* enough space available ? */
892         if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
893           break;
894         ma_bmove_upp(buff + field->length, buff + length, length);
895         /* coverity[bad_memset] */
896         memset((void*) buff, (int) '0', field->length - length);
897         length= field->length;
898       }
899 
900       convert_froma_string(r_param, buff, length);
901     }
902     break;
903   }
904 }
905 
906 static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size __attribute__((unused)))
907 {
908   double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
909   char *buf= (char *)r_param->buffer;
910   switch (r_param->buffer_type)
911   {
912     case MYSQL_TYPE_TINY:
913       *buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
914       *r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
915                                           (double)((int8)*buf));
916       r_param->buffer_length= 1;
917     break;
918     case MYSQL_TYPE_SHORT:
919     case MYSQL_TYPE_YEAR:
920     {
921       if (r_param->is_unsigned)
922       {
923         ushort sval= (ushort)val;
924         shortstore(buf, sval);
925         *r_param->error= check_trunc_val != (double)sval;
926       } else {
927         short sval= (short)val;
928         shortstore(buf, sval);
929         *r_param->error= check_trunc_val != (double)sval;
930       }
931       r_param->buffer_length= 2;
932     }
933     break;
934     case MYSQL_TYPE_LONG:
935     {
936       if (r_param->is_unsigned)
937       {
938         uint32 lval= (uint32)val;
939         longstore(buf, lval);
940         *r_param->error= (check_trunc_val != (double)lval);
941       } else {
942         int32 lval= (int32)val;
943         longstore(buf, lval);
944         *r_param->error= (check_trunc_val != (double)lval);
945       }
946       r_param->buffer_length= 4;
947     }
948     break;
949     case MYSQL_TYPE_LONGLONG:
950     {
951       if (r_param->is_unsigned)
952       {
953         ulonglong llval= (ulonglong)val;
954         longlongstore(buf, llval);
955         *r_param->error= (check_trunc_val != (double)llval);
956       } else {
957         longlong llval= (longlong)val;
958         longlongstore(buf, llval);
959         *r_param->error= (check_trunc_val != (double)llval);
960       }
961       r_param->buffer_length= 8;
962     }
963     break;
964     case MYSQL_TYPE_FLOAT:
965     {
966       float fval= (float)val;
967       memcpy(buf, &fval, sizeof(float));
968       *r_param->error= (*(float*)buf != fval);
969       r_param->buffer_length= 4;
970     }
971     break;
972     default:
973     {
974      char buff[MAX_DOUBLE_STRING_REP_LENGTH];
975      size_t length;
976 
977      length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
978 
979      if (field->decimals >= NOT_FIXED_DEC)
980      {
981        length= ma_gcvt(val, MY_GCVT_ARG_DOUBLE, (int)length, buff, NULL);
982      }
983      else
984      {
985        length= ma_fcvt(val, field->decimals, buff, NULL);
986      }
987 
988      /* check if ZEROFILL flag is active */
989      if (field->flags & ZEROFILL_FLAG)
990      {
991        /* enough space available ? */
992        if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
993          break;
994        ma_bmove_upp(buff + field->length, buff + length, length);
995        /* coverity [bad_memset] */
996        memset((void*) buff, (int) '0', field->length - length);
997        length= field->length;
998      }
999      convert_froma_string(r_param, buff, length);
1000     }
1001     break;
1002   }
1003 }
1004 
1005 
1006 /* {{{ ps_fetch_double */
1007 static
1008 void ps_fetch_double(MYSQL_BIND *r_param, const MYSQL_FIELD * field , unsigned char **row)
1009 {
1010   switch (r_param->buffer_type)
1011   {
1012     case MYSQL_TYPE_DOUBLE:
1013     {
1014       double *value= (double *)r_param->buffer;
1015       float8get(*value, *row);
1016       r_param->buffer_length= 8;
1017     }
1018     break;
1019     default:
1020     {
1021       double value;
1022       float8get(value, *row);
1023       convert_from_double(r_param, field, value, sizeof(double));
1024     }
1025     break;
1026   }
1027   (*row)+= 8;
1028 }
1029 /* }}} */
1030 
1031 /* {{{ ps_fetch_float */
1032 static
1033 void ps_fetch_float(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row)
1034 {
1035   switch(r_param->buffer_type)
1036   {
1037     case MYSQL_TYPE_FLOAT:
1038     {
1039       float *value= (float *)r_param->buffer;
1040       float4get(*value, *row);
1041       r_param->buffer_length= 4;
1042       *r_param->error= 0;
1043     }
1044     break;
1045     default:
1046     {
1047       float value;
1048       memcpy(&value, *row, sizeof(float));
1049       float4get(value, (char *)*row);
1050       convert_from_float(r_param, field, value, sizeof(float));
1051     }
1052     break;
1053   }
1054   (*row)+= 4;
1055 }
1056 /* }}} */
1057 
1058 static void convert_to_datetime(MYSQL_TIME *t, unsigned char **row, uint len, enum enum_field_types type)
1059 {
1060   memset(t, 0, sizeof(MYSQL_TIME));
1061 
1062   /* binary protocol for datetime:
1063      4-bytes:  DATE
1064      7-bytes:  DATE + TIME
1065      >7 bytes: DATE + TIME with second_part
1066   */
1067   if (len)
1068   {
1069     unsigned char *to= *row;
1070     int has_date= 0;
1071     uint offset= 7;
1072 
1073     if (type == MYSQL_TYPE_TIME)
1074     {
1075       t->neg= to[0];
1076       t->day= (ulong) sint4korr(to + 1);
1077       t->time_type= MYSQL_TIMESTAMP_TIME;
1078       offset= 8;
1079       to++;
1080     } else
1081     {
1082       t->year= (uint) sint2korr(to);
1083       t->month= (uint) to[2];
1084       t->day= (uint) to[3];
1085       t->time_type= MYSQL_TIMESTAMP_DATE;
1086       if (type == MYSQL_TYPE_DATE)
1087         return;
1088       has_date= 1;
1089     }
1090 
1091     if (len > 4)
1092     {
1093       t->hour= (uint) to[4];
1094       if (type == MYSQL_TYPE_TIME)
1095         t->hour+= t->day * 24;
1096       t->minute= (uint) to[5];
1097       t->second= (uint) to[6];
1098       if (has_date)
1099         t->time_type= MYSQL_TIMESTAMP_DATETIME;
1100     }
1101     if (len > offset)
1102     {
1103       t->second_part= (ulong)sint4korr(to+7);
1104     }
1105   }
1106 }
1107 
1108 
1109 /* {{{ ps_fetch_datetime */
1110 static
1111 void ps_fetch_datetime(MYSQL_BIND *r_param, const MYSQL_FIELD * field,
1112                        unsigned char **row)
1113 {
1114   MYSQL_TIME *t= (MYSQL_TIME *)r_param->buffer;
1115   unsigned int len= net_field_length(row);
1116 
1117   switch (r_param->buffer_type) {
1118     case MYSQL_TYPE_DATETIME:
1119     case MYSQL_TYPE_TIMESTAMP:
1120       convert_to_datetime(t, row, len, field->type);
1121       break;
1122     case MYSQL_TYPE_DATE:
1123       convert_to_datetime(t, row, len, field->type);
1124       break;
1125     case MYSQL_TYPE_TIME:
1126       convert_to_datetime(t, row, len, field->type);
1127       t->year= t->day= t->month= 0;
1128       break;
1129     case MYSQL_TYPE_YEAR:
1130     {
1131       MYSQL_TIME tm;
1132       convert_to_datetime(&tm, row, len, field->type);
1133       shortstore(r_param->buffer, tm.year);
1134       break;
1135     }
1136     default:
1137     {
1138       char dtbuffer[60];
1139       MYSQL_TIME tm;
1140       size_t length;
1141       convert_to_datetime(&tm, row, len, field->type);
1142 
1143       switch(field->type) {
1144       case MYSQL_TYPE_DATE:
1145         length= sprintf(dtbuffer, "%04u-%02u-%02u", tm.year, tm.month, tm.day);
1146         break;
1147       case MYSQL_TYPE_TIME:
1148         length= sprintf(dtbuffer, "%s%02u:%02u:%02u", (tm.neg ? "-" : ""), tm.hour, tm.minute, tm.second);
1149         if (field->decimals && field->decimals <= 6)
1150         {
1151           char ms[8];
1152           sprintf(ms, ".%06lu", tm.second_part);
1153           if (field->decimals < 6)
1154             ms[field->decimals + 1]= 0;
1155           length+= strlen(ms);
1156           strcat(dtbuffer, ms);
1157         }
1158         break;
1159       case MYSQL_TYPE_DATETIME:
1160       case MYSQL_TYPE_TIMESTAMP:
1161         length= sprintf(dtbuffer, "%04u-%02u-%02u %02u:%02u:%02u", tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second);
1162         if (field->decimals && field->decimals <= 6)
1163         {
1164           char ms[8];
1165           sprintf(ms, ".%06lu", tm.second_part);
1166           if (field->decimals < 6)
1167             ms[field->decimals + 1]= 0;
1168           length+= strlen(ms);
1169           strcat(dtbuffer, ms);
1170         }
1171         break;
1172       default:
1173         dtbuffer[0]= 0;
1174         length= 0;
1175         break;
1176       }
1177       convert_froma_string(r_param, dtbuffer, length);
1178       break;
1179     }
1180   }
1181   (*row) += len;
1182 }
1183 /* }}} */
1184 
1185 /* {{{ ps_fetch_string */
1186 static
1187 void ps_fetch_string(MYSQL_BIND *r_param,
1188                      const MYSQL_FIELD *field __attribute__((unused)),
1189                      unsigned char **row)
1190 {
1191   /* C-API differs from PHP. While PHP just converts string to string,
1192      C-API needs to convert the string to the defined type with in
1193      the result bind buffer.
1194    */
1195   ulong field_length= net_field_length(row);
1196 
1197   convert_froma_string(r_param, (char *)*row, field_length);
1198   (*row) += field_length;
1199 }
1200 /* }}} */
1201 
1202 /* {{{ ps_fetch_bin */
1203 static
1204 void ps_fetch_bin(MYSQL_BIND *r_param,
1205              const MYSQL_FIELD *field,
1206              unsigned char **row)
1207 {
1208   if (field->charsetnr == 63)
1209   {
1210     ulong field_length= *r_param->length= net_field_length(row);
1211     uchar *current_pos= (*row) + r_param->offset,
1212           *end= (*row) + field_length;
1213     size_t copylen= 0;
1214 
1215     if (current_pos < end)
1216     {
1217       copylen= end - current_pos;
1218       if (r_param->buffer_length)
1219         memcpy(r_param->buffer, current_pos, MIN(copylen, r_param->buffer_length));
1220     }
1221     if (copylen < r_param->buffer_length &&
1222         (r_param->buffer_type == MYSQL_TYPE_STRING ||
1223          r_param->buffer_type == MYSQL_TYPE_JSON))
1224       ((char *)r_param->buffer)[copylen]= 0;
1225     *r_param->error= copylen > r_param->buffer_length;
1226     (*row)+= field_length;
1227   }
1228   else
1229     ps_fetch_string(r_param, field, row);
1230 }
1231 /* }}} */
1232 
1233 /* {{{ _mysqlnd_init_ps_subsystem */
1234 void mysql_init_ps_subsystem(void)
1235 {
1236   memset(mysql_ps_fetch_functions, 0, sizeof(mysql_ps_fetch_functions));
1237   mysql_ps_fetch_functions[MYSQL_TYPE_NULL].func= ps_fetch_null;
1238   mysql_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len  = 0;
1239   mysql_ps_fetch_functions[MYSQL_TYPE_NULL].max_len  = 0;
1240 
1241   mysql_ps_fetch_functions[MYSQL_TYPE_TINY].func    = ps_fetch_int8;
1242   mysql_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len  = 1;
1243   mysql_ps_fetch_functions[MYSQL_TYPE_TINY].max_len  = 4;
1244 
1245   mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].func    = ps_fetch_int16;
1246   mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len  = 2;
1247   mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].max_len  = 6;
1248 
1249   mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].func    = ps_fetch_int16;
1250   mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len  = 2;
1251   mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].max_len  = 4;
1252 
1253   mysql_ps_fetch_functions[MYSQL_TYPE_INT24].func    = ps_fetch_int32;
1254   mysql_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len  = 4;
1255   mysql_ps_fetch_functions[MYSQL_TYPE_INT24].max_len  = 8;
1256 
1257   mysql_ps_fetch_functions[MYSQL_TYPE_LONG].func    = ps_fetch_int32;
1258   mysql_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len  = 4;
1259   mysql_ps_fetch_functions[MYSQL_TYPE_LONG].max_len  = 11;
1260 
1261   mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func  = ps_fetch_int64;
1262   mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
1263   mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].max_len  = 20;
1264 
1265   mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].func    = ps_fetch_float;
1266   mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len  = 4;
1267   mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].max_len  = MAX_DOUBLE_STRING_REP_LENGTH;
1268 
1269   mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func    = ps_fetch_double;
1270   mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len  = 8;
1271   mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].max_len  = MAX_DOUBLE_STRING_REP_LENGTH;
1272 
1273   mysql_ps_fetch_functions[MYSQL_TYPE_TIME].func  = ps_fetch_datetime;
1274   mysql_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len  = MYSQL_PS_SKIP_RESULT_W_LEN;
1275   mysql_ps_fetch_functions[MYSQL_TYPE_TIME].max_len  = 17;
1276 
1277   mysql_ps_fetch_functions[MYSQL_TYPE_DATE].func  = ps_fetch_datetime;
1278   mysql_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len  = MYSQL_PS_SKIP_RESULT_W_LEN;
1279   mysql_ps_fetch_functions[MYSQL_TYPE_DATE].max_len  = 10;
1280 
1281   mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func    = ps_fetch_string;
1282   mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len  = MYSQL_PS_SKIP_RESULT_W_LEN;
1283   mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].max_len  = -1;
1284 
1285   mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].func  = ps_fetch_datetime;
1286   mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
1287   mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].max_len  = 30;
1288 
1289   mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func  = ps_fetch_datetime;
1290   mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
1291   mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].max_len  = 30;
1292 
1293   mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func  = ps_fetch_bin;
1294   mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
1295   mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].max_len  = -1;
1296 
1297   mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].func    = ps_fetch_bin;
1298   mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1299   mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].max_len  = -1;
1300 
1301   mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func  = ps_fetch_bin;
1302   mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
1303   mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].max_len  = -1;
1304 
1305   mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func    = ps_fetch_bin;
1306   mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1307   mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].max_len  = -1;
1308 
1309   mysql_ps_fetch_functions[MYSQL_TYPE_BIT].func  = ps_fetch_bin;
1310   mysql_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1311   mysql_ps_fetch_functions[MYSQL_TYPE_BIT].max_len  = -1;
1312 
1313   mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func    = ps_fetch_string;
1314   mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1315   mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].max_len  = -1;
1316 
1317   mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func    = ps_fetch_string;
1318   mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1319   mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].max_len  = -1;
1320 
1321   mysql_ps_fetch_functions[MYSQL_TYPE_STRING].func      = ps_fetch_string;
1322   mysql_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len    = MYSQL_PS_SKIP_RESULT_STR;
1323   mysql_ps_fetch_functions[MYSQL_TYPE_STRING].max_len  = -1;
1324 
1325   mysql_ps_fetch_functions[MYSQL_TYPE_JSON].func      = ps_fetch_string;
1326   mysql_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len    = MYSQL_PS_SKIP_RESULT_STR;
1327   mysql_ps_fetch_functions[MYSQL_TYPE_JSON].max_len  = -1;
1328 
1329   mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func    = ps_fetch_string;
1330   mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1331   mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].max_len  = -1;
1332 
1333   mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func    = ps_fetch_string;
1334   mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1335   mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].max_len  = -1;
1336 
1337   mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].func    = ps_fetch_string;
1338   mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len  = MYSQL_PS_SKIP_RESULT_STR;
1339   mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].max_len  = -1;
1340 
1341   mysql_ps_fetch_functions[MYSQL_TYPE_SET].func      = ps_fetch_string;
1342   mysql_ps_fetch_functions[MYSQL_TYPE_SET].pack_len    = MYSQL_PS_SKIP_RESULT_STR;
1343   mysql_ps_fetch_functions[MYSQL_TYPE_SET].max_len  = -1;
1344 
1345   mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func  = ps_fetch_string;
1346   mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQL_PS_SKIP_RESULT_STR;
1347   mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].max_len  = -1;
1348 
1349   mysql_ps_subsystem_initialized= 1;
1350 }
1351 /* }}} */
1352 
1353 
1354 /*
1355  * Local variables:
1356  * tab-width: 4
1357  * c-basic-offset: 4
1358  * End:
1359  * vim600: noet sw=4 ts=4 fdm=marker
1360  * vim<600: noet sw=4 ts=4
1361  */
1362 
1363