1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-14 11:47:15 +01:00
SqMod/module/Vendor/MDBC/libmariadb/my_stmt_codec.c

1118 lines
33 KiB
C
Raw Normal View History

2020-03-22 13:54:40 +01:00
/****************************************************************************
Copyright (C) 2012 Monty Program AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not see <http://www.gnu.org/licenses>
or write to the Free Software Foundation, Inc.,
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
Part of this code includes code from the PHP project which
is freely available from http://www.php.net
*****************************************************************************/
/* The implementation for prepared statements was ported from PHP's mysqlnd
extension, written by Andrey Hristov, Georg Richter and Ulf Wendel
Original file header:
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 2006-2011 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Georg Richter <georg@mysql.com> |
| Andrey Hristov <andrey@mysql.com> |
| Ulf Wendel <uwendel@mysql.com> |
+----------------------------------------------------------------------+
*/
#include "my_global.h"
#include <my_sys.h>
#include <mysys_err.h>
#include <m_string.h>
#include <m_ctype.h>
#include "mysql.h"
#define MYSQL_SILENT
/* ranges for C-binding */
#define UINT_MAX32 0xFFFFFFFFL
#define UINT_MAX24 0x00FFFFFF
#define UINT_MAX16 0xFFFF
#ifndef INT_MIN8
#define INT_MIN8 (~0x7F)
#define INT_MAX8 0x7F
#endif
#define UINT_MAX8 0xFF
#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
#define LONGLONG_MIN ((long long) 0x8000000000000000LL)
#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL)
#endif
#if defined(HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)
/* First check for ANSI C99 definition: */
#ifdef ULLONG_MAX
#define ULONGLONG_MAX ULLONG_MAX
#else
#define ULONGLONG_MAX ((unsigned long long)(~0ULL))
#endif
#endif /* defined (HAVE_LONG_LONG) && !defined(ULONGLONG_MAX)*/
#define YY_PART_YEAR 70
#define MAX_DOUBLE_STRING_REP_LENGTH 300
MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 1];
my_bool mysql_ps_subsystem_initialized= 0;
#define NUMERIC_TRUNCATION(val,min_range, max_range)\
((((val) > (max_range)) || ((val) < (min_range)) ? 1 : 0))
/* helper function for zero fill */
void ma_bmove_upp(register char *dst, register const char *src, register size_t len)
{
while (len-- != 0) *--dst = *--src;
}
/* {{{ ps_fetch_from_1_to_8_bytes */
void ps_fetch_from_1_to_8_bytes(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
unsigned char **row, unsigned int byte_count)
{
my_bool is_unsigned= test(field->flags & UNSIGNED_FLAG);
r_param->buffer_length= byte_count;
switch (byte_count) {
case 1:
*(uchar *)r_param->buffer= **row;
*r_param->error= is_unsigned != r_param->is_unsigned && *(uchar *)r_param->buffer > INT_MAX8;
break;
case 2:
shortstore(r_param->buffer, ((ushort) sint2korr(*row)));
*r_param->error= is_unsigned != r_param->is_unsigned && *(ushort *)r_param->buffer > INT_MAX16;
break;
case 4:
{
longstore(r_param->buffer, ((uint32)sint4korr(*row)));
*r_param->error= is_unsigned != r_param->is_unsigned && *(uint32 *)r_param->buffer > INT_MAX32;
}
break;
case 8:
{
ulonglong val= (ulonglong)sint8korr(*row);
longlongstore(r_param->buffer, val);
*r_param->error= is_unsigned != r_param->is_unsigned && val > LONGLONG_MAX ;
}
break;
default:
r_param->buffer_length= 0;
break;
}
(*row)+= byte_count;
}
/* }}} */
static longlong my_atoll(const char *number, const char *end, int *error)
{
char buffer[255];
longlong llval= 0;
size_t i;
/* set error at the following conditions:
- string contains invalid character(s)
- length > 254
- strtoll returns invalid range
*/
memcpy(buffer, number, MIN((uint)(end - number), 254));
buffer[(uint)(end - number)]= 0;
llval= strtoll(buffer, NULL, 10);
/* check size */
if ((uint)(end - number) > 254)
{
*error= 1;
return llval;
}
/* check characters */
for (i=0; i < strlen(buffer); i++)
{
if (buffer[i] < '0' || buffer[i] > '9')
{
*error= 1;
return llval;
}
}
/* check strtoll result */
if (errno == ERANGE)
*error= errno;
return llval;
}
double my_atod(const char *number, const char *end, int *error)
{
double val= 0.0;
char buffer[255];
int len= (int)(end - number);
if (len > 254)
*error= 1;
len= MIN(len, 254);
memcpy(&buffer, number, len);
buffer[len]= '\0';
val= strtod(buffer, NULL);
/* if (!*error)
*error= errno; */
return val;
}
my_bool str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
{
char *start= alloca(length + 1);
my_bool is_date= 0, is_time= 0;
memset(tm, 0, sizeof(MYSQL_TIME));
if (!start)
goto error;
tm->time_type= MYSQL_TIMESTAMP_NONE;
memcpy(start, str, length);
start[length]= '\0';
while (length && isspace(*start)) start++, length--;
if (!length)
goto error;
/* negativ value? */
if (*start == '-')
{
tm->neg= 1;
start++;
length--;
}
if (!length)
return 1;
/* Determine time type:
MYSQL_TIMESTAMP_DATE: [-]YY[YY].MM.DD
MYSQL_TIMESTAMP_DATETIME: [-]YY[YY].MM.DD hh:mm:ss.mmmmmm
MYSQL_TIMESTAMP_TIME: [-]hh:mm:ss.mmmmmm
*/
if (strchr(start, '-'))
{
if (tm->neg)
goto error;
tm->time_type= MYSQL_TIMESTAMP_DATE;
if (sscanf(start, "%d-%d-%d", &tm->year, &tm->month, &tm->day) < 3)
goto error;
is_date= 1;
if (!(start= strchr(start, ' ')))
goto check;
}
if (!strchr(start, ':'))
goto check;
is_time= 1;
if (tm->time_type== MYSQL_TIMESTAMP_DATE)
tm->time_type= MYSQL_TIMESTAMP_DATETIME;
else
tm->time_type= MYSQL_TIMESTAMP_TIME;
if (strchr(start, '.')) /* fractional seconds */
{
if (sscanf(start, "%d:%d:%d.%ld", &tm->hour, &tm->minute,
&tm->second,&tm->second_part) < 4)
goto error;
} else {
if (sscanf(start, "%d:%d:%d", &tm->hour, &tm->minute,
&tm->second) < 3)
goto error;
}
check:
if (tm->time_type == MYSQL_TIMESTAMP_NONE)
goto error;
if (is_date)
{
if (tm->year < 69)
tm->year+= 2000;
else if (tm->year < 100)
tm->year+= 1900;
if (tm->day > 31 || tm->month > 12)
goto error;
}
if (is_time)
{
if (tm->minute > 59 || tm->second > 59)
goto error;
}
return 0;
error:
tm->time_type= MYSQL_TIMESTAMP_ERROR;
return 1;
}
static void convert_from_string(MYSQL_BIND *r_param, char *buffer, size_t len)
{
int error= 0;
switch (r_param->buffer_type)
{
case MYSQL_TYPE_TINY:
{
longlong val= my_atoll(buffer, buffer + len, &error);
*r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8) || error > 0;
int1store(r_param->buffer, (uchar) val);
r_param->buffer_length= sizeof(uchar);
}
break;
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
{
longlong val= my_atoll(buffer, buffer + len, &error);
*r_param->error= error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16) || error > 0;
shortstore(r_param->buffer, (short)val);
r_param->buffer_length= sizeof(short);
}
break;
case MYSQL_TYPE_LONG:
{
longlong val= my_atoll(buffer, buffer + len, &error);
*r_param->error=error ? 1 : r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32) || error > 0;
longstore(r_param->buffer, (int32)val);
r_param->buffer_length= sizeof(uint32);
}
break;
case MYSQL_TYPE_LONGLONG:
{
longlong val= my_atoll(buffer, buffer + len, &error);
*r_param->error= error > 0; /* no need to check for truncation */
longlongstore(r_param->buffer, val);
r_param->buffer_length= sizeof(longlong);
}
break;
case MYSQL_TYPE_DOUBLE:
{
double val= my_atod(buffer, buffer + len, &error);
*r_param->error= error > 0; /* no need to check for truncation */
doublestore((uchar *)r_param->buffer, val);
r_param->buffer_length= sizeof(double);
}
break;
case MYSQL_TYPE_FLOAT:
{
float val= (float)my_atod(buffer, buffer + len, &error);
*r_param->error= error > 0; /* no need to check for truncation */
floatstore((uchar *)r_param->buffer, val);
r_param->buffer_length= sizeof(float);
}
break;
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
{
MYSQL_TIME *tm= (MYSQL_TIME *)r_param->buffer;
str_to_TIME(buffer, len, tm);
break;
}
break;
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
default:
{
char *start= buffer + r_param->offset; /* stmt_fetch_column sets offset */
char *end= buffer + len;
size_t copylen= 0;
if (start < end)
{
copylen= end - start;
if (r_param->buffer_length)
memcpy(r_param->buffer, start, MIN(copylen, r_param->buffer_length));
}
if (copylen < r_param->buffer_length)
((char *)r_param->buffer)[copylen]= '\0';
*r_param->error= (copylen > r_param->buffer_length);
*r_param->length= (ulong)len;
}
break;
}
}
static void convert_from_long(MYSQL_BIND *r_param, const MYSQL_FIELD *field, longlong val, my_bool is_unsigned)
{
switch (r_param->buffer_type) {
case MYSQL_TYPE_TINY:
*(uchar *)r_param->buffer= (uchar)val;
*r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX8) : NUMERIC_TRUNCATION(val, INT_MIN8, INT_MAX8);
r_param->buffer_length= 1;
break;
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_YEAR:
shortstore(r_param->buffer, (short)val);
*r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX16) : NUMERIC_TRUNCATION(val, INT_MIN16, INT_MAX16);
r_param->buffer_length= 2;
break;
case MYSQL_TYPE_LONG:
longstore(r_param->buffer, (int32)val);
*r_param->error= r_param->is_unsigned ? NUMERIC_TRUNCATION(val, 0, UINT_MAX32) : NUMERIC_TRUNCATION(val, INT_MIN32, INT_MAX32);
r_param->buffer_length= 4;
break;
case MYSQL_TYPE_LONGLONG:
*r_param->error= (val < 0 && r_param->is_unsigned != is_unsigned);
longlongstore(r_param->buffer, val);
r_param->buffer_length= 8;
break;
case MYSQL_TYPE_DOUBLE:
{
volatile double dbl;
dbl= (is_unsigned) ? ulonglong2double((ulonglong)val) : (double)val;
doublestore(r_param->buffer, dbl);
*r_param->error= is_unsigned ? (ulonglong )dbl != (ulonglong)val : (longlong)dbl != (longlong)val;
r_param->buffer_length= 8;
break;
}
case MYSQL_TYPE_FLOAT:
{
float fval;
fval= is_unsigned ? (float)(ulonglong)(val) : (float)val;
float4store((float *)r_param->buffer, fval);
*r_param->error= is_unsigned ? (ulonglong)fval != (ulonglong)val : (longlong)fval != val;
r_param->buffer_length= 4;
}
break;
default:
{
char buffer[22];
char *endptr;
uint len;
endptr= longlong10_to_str(val, buffer, is_unsigned ? 10 : -10);
len= (uint)(endptr - buffer);
if (field->flags & ZEROFILL_FLAG &&
len < field->length && len < r_param->buffer_length)
{
ma_bmove_upp(buffer + field->length, buffer + len, len);
memset((char*) buffer, '0', field->length - len);
len= field->length;
}
convert_from_string(r_param, buffer, len);
}
break;
}
}
/* {{{ ps_fetch_null */
static
void ps_fetch_null(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row)
{
/* do nothing */
}
/* }}} */
#define GET_LVALUE_FROM_ROW(is_unsigned, data, ucast, scast)\
(is_unsigned) ? (longlong)(ucast) *(longlong *)(data) : (longlong)(scast) *(longlong *)(data)
/* {{{ ps_fetch_int8 */
static
void ps_fetch_int8(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
unsigned char **row)
{
switch(r_param->buffer_type) {
case MYSQL_TYPE_TINY:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
break;
default:
{
uchar val= **row;
longlong lval= field->flags & UNSIGNED_FLAG ? (longlong) val : (longlong)(signed char)val;
convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
(*row) += 1;
}
break;
}
}
/* }}} */
/* {{{ ps_fetch_int16 */
static
void ps_fetch_int16(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
unsigned char **row)
{
switch (r_param->buffer_type) {
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
break;
default:
{
short sval= sint2korr(*row);
longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(ushort) sval : (longlong)sval;
convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
(*row) += 2;
}
break;
}
}
/* }}} */
/* {{{ ps_fetch_int32 */
static
void ps_fetch_int32(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
unsigned char **row)
{
switch (r_param->buffer_type) {
/* case MYSQL_TYPE_TINY:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
break;
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
break; */
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
break;
default:
{
int32 sval= sint4korr(*row);
longlong lval= field->flags & UNSIGNED_FLAG ? (longlong)(uint32) sval : (longlong)sval;
convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
(*row) += 4;
}
break;
}
}
/* }}} */
/* {{{ ps_fetch_int64 */
static
void ps_fetch_int64(MYSQL_BIND *r_param, const MYSQL_FIELD * const field,
unsigned char **row)
{
switch(r_param->buffer_type)
{
/* case MYSQL_TYPE_TINY:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 1);
break;
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 2);
break;
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 4);
break; */
case MYSQL_TYPE_LONGLONG:
ps_fetch_from_1_to_8_bytes(r_param, field, row, 8);
break;
default:
{
longlong sval= (longlong)sint8korr(*row);
longlong lval= field->flags & UNSIGNED_FLAG ? (ulonglong) sval : (longlong)sval;
convert_from_long(r_param, field, lval, field->flags & UNSIGNED_FLAG);
(*row) += 8;
}
break;
}
}
/* }}} */
static void convert_from_float(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size)
{
double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
char *buf= (char *)r_param->buffer;
switch (r_param->buffer_type)
{
case MYSQL_TYPE_TINY:
*buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
*r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
(double)((int8)*buf));
r_param->buffer_length= 1;
break;
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_YEAR:
{
if (r_param->is_unsigned)
{
ushort sval= (ushort)val;
shortstore(buf, sval);
*r_param->error= check_trunc_val != (double)sval;
} else {
short sval= (short)val;
shortstore(buf, sval);
*r_param->error= check_trunc_val != (double)sval;
}
r_param->buffer_length= 2;
}
break;
case MYSQL_TYPE_LONG:
{
if (r_param->is_unsigned)
{
uint32 lval= (uint32)val;
longstore(buf, lval);
*r_param->error= (check_trunc_val != (double)lval);
} else {
int32 lval= (int32)val;
longstore(buf, lval);
*r_param->error= (check_trunc_val != (double)lval);
}
r_param->buffer_length= 4;
}
break;
case MYSQL_TYPE_LONGLONG:
{
if (r_param->is_unsigned)
{
ulonglong llval= (ulonglong)val;
longlongstore(buf, llval);
*r_param->error= (check_trunc_val != (double)llval);
} else {
longlong llval= (longlong)val;
longlongstore(buf, llval);
*r_param->error= (check_trunc_val != (double)llval);
}
r_param->buffer_length= 8;
}
break;
case MYSQL_TYPE_FLOAT:
{
float fval= (float)val;
memcpy(buf, &fval, sizeof(float));
*r_param->error= (*(float*)buf != fval);
r_param->buffer_length= 4;
}
break;
case MYSQL_TYPE_DOUBLE:
{
memcpy(buf, &val, sizeof(double));
r_param->buffer_length= 8;
}
break;
default:
{
char buff[MAX_DOUBLE_STRING_REP_LENGTH];
size_t length;
length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
if (field->decimals >= NOT_FIXED_DEC)
{
length= ma_gcvt(val, MY_GCVT_ARG_FLOAT, length, buff, NULL);
}
else
{
length= ma_fcvt(val, field->decimals, buff, NULL);
}
/* check if ZEROFILL flag is active */
if (field->flags & ZEROFILL_FLAG)
{
/* enough space available ? */
if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
break;
ma_bmove_upp(buff + field->length, buff + length, length);
memset((char*) buff, '0', field->length - length);
length= field->length;
}
convert_from_string(r_param, buff, length);
}
break;
}
}
static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size)
{
double check_trunc_val= (val > 0) ? floor(val) : -floor(-val);
char *buf= (char *)r_param->buffer;
switch (r_param->buffer_type)
{
case MYSQL_TYPE_TINY:
*buf= (r_param->is_unsigned) ? (uint8)val : (int8)val;
*r_param->error= check_trunc_val != (r_param->is_unsigned ? (double)((uint8)*buf) :
(double)((int8)*buf));
r_param->buffer_length= 1;
break;
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_YEAR:
{
if (r_param->is_unsigned)
{
ushort sval= (ushort)val;
shortstore(buf, sval);
*r_param->error= check_trunc_val != (double)sval;
} else {
short sval= (short)val;
shortstore(buf, sval);
*r_param->error= check_trunc_val != (double)sval;
}
r_param->buffer_length= 2;
}
break;
case MYSQL_TYPE_LONG:
{
if (r_param->is_unsigned)
{
uint32 lval= (uint32)val;
longstore(buf, lval);
*r_param->error= (check_trunc_val != (double)lval);
} else {
int32 lval= (int32)val;
longstore(buf, lval);
*r_param->error= (check_trunc_val != (double)lval);
}
r_param->buffer_length= 4;
}
break;
case MYSQL_TYPE_LONGLONG:
{
if (r_param->is_unsigned)
{
ulonglong llval= (ulonglong)val;
longlongstore(buf, llval);
*r_param->error= (check_trunc_val != (double)llval);
} else {
longlong llval= (longlong)val;
longlongstore(buf, llval);
*r_param->error= (check_trunc_val != (double)llval);
}
r_param->buffer_length= 8;
}
break;
case MYSQL_TYPE_FLOAT:
{
float fval= (float)val;
memcpy(buf, &fval, sizeof(float));
*r_param->error= (*(float*)buf != fval);
r_param->buffer_length= 4;
}
break;
default:
{
char buff[MAX_DOUBLE_STRING_REP_LENGTH];
size_t length;
length= MIN(MAX_DOUBLE_STRING_REP_LENGTH - 1, r_param->buffer_length);
if (field->decimals >= NOT_FIXED_DEC)
{
length= ma_gcvt(val, MY_GCVT_ARG_DOUBLE, length, buff, NULL);
}
else
{
length= ma_fcvt(val, field->decimals, buff, NULL);
}
/* check if ZEROFILL flag is active */
if (field->flags & ZEROFILL_FLAG)
{
/* enough space available ? */
if (field->length < length || field->length > MAX_DOUBLE_STRING_REP_LENGTH - 1)
break;
ma_bmove_upp(buff + field->length, buff + length, length);
memset((char*) buff, '0', field->length - length);
length= field->length;
}
convert_from_string(r_param, buff, length);
}
break;
}
}
/* {{{ ps_fetch_float */
static
void ps_fetch_float(MYSQL_BIND *r_param, const MYSQL_FIELD * field, unsigned char **row)
{
switch(r_param->buffer_type)
{
case MYSQL_TYPE_FLOAT:
{
float *value= (float *)r_param->buffer;
float4get(*value, *row);
r_param->buffer_length= 4;
*r_param->error= 0;
}
break;
default:
{
float value;
memcpy(&value, *row, sizeof(float));
float4get(value, (char *)*row);
convert_from_float(r_param, field, value, sizeof(float));
}
break;
}
(*row)+= 4;
}
/* }}} */
/* {{{ ps_fetch_double */
static
void ps_fetch_double(MYSQL_BIND *r_param, const MYSQL_FIELD * field , unsigned char **row)
{
switch (r_param->buffer_type)
{
case MYSQL_TYPE_DOUBLE:
{
double *value= (double *)r_param->buffer;
float8get(*value, *row);
r_param->buffer_length= 8;
}
break;
default:
{
double value;
float8get(value, *row);
convert_from_double(r_param, field, value, sizeof(double));
}
break;
}
(*row)+= 8;
}
/* }}} */
static void convert_to_datetime(MYSQL_TIME *t, unsigned char **row, uint len, enum enum_field_types type)
{
memset(t, 0, sizeof(MYSQL_TIME));
/* binary protocol for datetime:
4-bytes: DATE
7-bytes: DATE + TIME
>7 bytes: DATE + TIME with second_part
*/
if (len)
{
unsigned char *to= *row;
int has_date= 0;
uint offset= 7;
if (type == MYSQL_TYPE_TIME)
{
t->neg= to[0];
t->day= (ulong) sint4korr(to + 1);
t->time_type= MYSQL_TIMESTAMP_TIME;
offset= 8;
to++;
} else
{
t->year= (uint) sint2korr(to);
t->month= (uint) to[2];
t->day= (uint) to[3];
t->time_type= MYSQL_TIMESTAMP_DATE;
if (type == MYSQL_TYPE_DATE)
return;
has_date= 1;
}
if (len > 4)
{
t->hour= (uint) to[4];
t->minute= (uint) to[5];
t->second= (uint) to[6];
if (has_date)
t->time_type= MYSQL_TIMESTAMP_DATETIME;
}
if (len > offset)
{
t->second_part= (ulong)sint4korr(to+7);
}
}
}
/* {{{ ps_fetch_datetime */
static
void ps_fetch_datetime(MYSQL_BIND *r_param, const MYSQL_FIELD * field,
unsigned char **row)
{
MYSQL_TIME *t= (MYSQL_TIME *)r_param->buffer;
unsigned int len= net_field_length(row);
switch (r_param->buffer_type) {
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
convert_to_datetime(t, row, len, field->type);
break;
case MYSQL_TYPE_DATE:
convert_to_datetime(t, row, len, field->type);
break;
case MYSQL_TYPE_TIME:
convert_to_datetime(t, row, len, field->type);
if (t->day)
{
t->hour+= t->day * 24;
t->day= 0;
}
t->year= t->day= t->month= 0;
break;
case MYSQL_TYPE_YEAR:
{
MYSQL_TIME tm;
convert_to_datetime(&tm, row, len, field->type);
shortstore(r_param->buffer, tm.year);
break;
}
default:
{
char dtbuffer[60];
MYSQL_TIME tm;
unsigned int length;
convert_to_datetime(&tm, row, len, field->type);
if (tm.time_type== MYSQL_TIMESTAMP_TIME && tm.day)
{
tm.hour+= tm.day * 24;
tm.day=0;
}
switch(field->type) {
case MYSQL_TYPE_DATE:
length= sprintf(dtbuffer, "%04u-%02u-%02u", tm.year, tm.month, tm.day);
break;
case MYSQL_TYPE_TIME:
length= sprintf(dtbuffer, "%s%02u:%02u:%02u", (tm.neg ? "-" : ""), tm.hour, tm.minute, tm.second);
if (field->decimals)
{
char ms[8];
sprintf(ms, ".%06lu", tm.second_part);
if (field->decimals < 6)
ms[field->decimals + 1]= 0;
length+= strlen(ms);
strcat(dtbuffer, ms);
}
break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
length= sprintf(dtbuffer, "%04u-%02u-%02u %02u:%02u:%02u", tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second);
if (field->decimals)
{
char ms[8];
sprintf(ms, ".%06lu", tm.second_part);
if (field->decimals < 6)
ms[field->decimals + 1]= 0;
length+= strlen(ms);
strcat(dtbuffer, ms);
}
break;
default:
dtbuffer[0]= 0;
length= 0;
break;
}
convert_from_string(r_param, dtbuffer, length);
break;
}
}
(*row) += len;
}
/* }}} */
/* {{{ ps_fetch_string */
static
void ps_fetch_string(MYSQL_BIND *r_param, const MYSQL_FIELD *field,
unsigned char **row)
{
/* C-API differs from PHP. While PHP just converts string to string,
C-API needs to convert the string to the defined type with in
the result bind buffer.
*/
ulong field_length= net_field_length(row);
convert_from_string(r_param, (char *)*row, field_length);
(*row) += field_length;
}
/* }}} */
/* {{{ ps_fetch_bin */
static
void ps_fetch_bin(MYSQL_BIND *r_param, const MYSQL_FIELD *field,
unsigned char **row)
{
if (field->charsetnr == 63)
{
ulong field_length= *r_param->length= net_field_length(row);
uchar *current_pos= (*row) + r_param->offset,
*end= (*row) + field_length;
size_t copylen= 0;
if (current_pos < end)
{
copylen= end - current_pos;
if (r_param->buffer_length)
memcpy(r_param->buffer, current_pos, MIN(copylen, r_param->buffer_length));
}
if (copylen < r_param->buffer_length &&
r_param->buffer_type == MYSQL_TYPE_STRING)
((char *)r_param->buffer)[copylen]= 0;
*r_param->error= copylen > r_param->buffer_length;
(*row)+= field_length;
}
else
ps_fetch_string(r_param, field, row);
}
/* }}} */
/* {{{ _mysqlnd_init_ps_subsystem */
void mysql_init_ps_subsystem(void)
{
memset(mysql_ps_fetch_functions, 0, sizeof(mysql_ps_fetch_functions));
mysql_ps_fetch_functions[MYSQL_TYPE_NULL].func= ps_fetch_null;
mysql_ps_fetch_functions[MYSQL_TYPE_NULL].pack_len = 0;
mysql_ps_fetch_functions[MYSQL_TYPE_NULL].max_len = 0;
mysql_ps_fetch_functions[MYSQL_TYPE_TINY].func = ps_fetch_int8;
mysql_ps_fetch_functions[MYSQL_TYPE_TINY].pack_len = 1;
mysql_ps_fetch_functions[MYSQL_TYPE_TINY].max_len = 4;
mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].func = ps_fetch_int16;
mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].pack_len = 2;
mysql_ps_fetch_functions[MYSQL_TYPE_SHORT].max_len = 6;
mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].func = ps_fetch_int16;
mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].pack_len = 2;
mysql_ps_fetch_functions[MYSQL_TYPE_YEAR].max_len = 6;
mysql_ps_fetch_functions[MYSQL_TYPE_INT24].func = ps_fetch_int32;
mysql_ps_fetch_functions[MYSQL_TYPE_INT24].pack_len = 4;
mysql_ps_fetch_functions[MYSQL_TYPE_INT24].max_len = 9;
mysql_ps_fetch_functions[MYSQL_TYPE_LONG].func = ps_fetch_int32;
mysql_ps_fetch_functions[MYSQL_TYPE_LONG].pack_len = 4;
mysql_ps_fetch_functions[MYSQL_TYPE_LONG].max_len = 11;
mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].func = ps_fetch_int64;
mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].pack_len= 8;
mysql_ps_fetch_functions[MYSQL_TYPE_LONGLONG].max_len = 21;
mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].func = ps_fetch_float;
mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].pack_len = 4;
mysql_ps_fetch_functions[MYSQL_TYPE_FLOAT].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].func = ps_fetch_double;
mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].pack_len = 8;
mysql_ps_fetch_functions[MYSQL_TYPE_DOUBLE].max_len = MAX_DOUBLE_STRING_REP_LENGTH;
mysql_ps_fetch_functions[MYSQL_TYPE_TIME].func = ps_fetch_datetime;
mysql_ps_fetch_functions[MYSQL_TYPE_TIME].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
mysql_ps_fetch_functions[MYSQL_TYPE_TIME].max_len = 15;
mysql_ps_fetch_functions[MYSQL_TYPE_DATE].func = ps_fetch_datetime;
mysql_ps_fetch_functions[MYSQL_TYPE_DATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
mysql_ps_fetch_functions[MYSQL_TYPE_DATE].max_len = 10;
mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
mysql_ps_fetch_functions[MYSQL_TYPE_NEWDATE].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].func = ps_fetch_datetime;
mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
mysql_ps_fetch_functions[MYSQL_TYPE_DATETIME].max_len = 30;
mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].func = ps_fetch_datetime;
mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].pack_len= MYSQL_PS_SKIP_RESULT_W_LEN;
mysql_ps_fetch_functions[MYSQL_TYPE_TIMESTAMP].max_len = 30;
mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].func = ps_fetch_bin;
mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_TINY_BLOB].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].func = ps_fetch_bin;
mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_BLOB].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].func = ps_fetch_bin;
mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].pack_len= MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_MEDIUM_BLOB].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].func = ps_fetch_bin;
mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_LONG_BLOB].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_BIT].func = ps_fetch_bin;
mysql_ps_fetch_functions[MYSQL_TYPE_BIT].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_BIT].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_VAR_STRING].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_VARCHAR].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_STRING].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_STRING].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_STRING].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_DECIMAL].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_NEWDECIMAL].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_ENUM].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_SET].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_SET].pack_len = MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_SET].max_len = -1;
mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].func = ps_fetch_string;
mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].pack_len= MYSQL_PS_SKIP_RESULT_STR;
mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY].max_len = -1;
mysql_ps_subsystem_initialized= 1;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/