2680 lines
78 KiB
C
2680 lines
78 KiB
C
/****************************************************************************
|
|
Copyright (C) 2012 Monty Program AB
|
|
Copyright (c) 2021 OceanBase.
|
|
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
|
|
|
|
*****************************************************************************/
|
|
|
|
/* 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: |
|
|
| 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 |
|
|
| so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Georg Richter < > |
|
|
| Andrey Hristov < > |
|
|
| Ulf Wendel < > |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "ma_global.h"
|
|
#include <ma_sys.h>
|
|
#include <ma_string.h>
|
|
#include <mariadb_ctype.h>
|
|
#include "mysql.h"
|
|
#include <math.h> /* ceil() */
|
|
#include <limits.h>
|
|
#include "ob_complex.h"
|
|
#ifdef WIN32
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#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
|
|
|
|
#define MAX_DOUBLE_STRING_REP_LENGTH 300
|
|
#if defined(HAVE_LONG_LONG) && !defined(LONGLONG_MIN)
|
|
#define LONGLONG_MIN ((long long) 0x8000000000000000LL)
|
|
#define LONGLONG_MAX ((long long) 0x7FFFFFFFFFFFFFFFLL)
|
|
#endif
|
|
|
|
#define MAX_DBL_STR (3 + DBL_MANT_DIG - DBL_MIN_EXP)
|
|
|
|
#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
|
|
|
|
MYSQL_PS_CONVERSION mysql_ps_fetch_functions[MYSQL_TYPE_GEOMETRY + 2];
|
|
my_bool mysql_ps_subsystem_initialized= 0;
|
|
|
|
|
|
#define NUMERIC_TRUNCATION(val,min_range, max_range)\
|
|
((((val) > (max_range)) || ((val) < (min_range)) ? 1 : 0))
|
|
|
|
|
|
extern ulong calculate_interval_length(uchar *cp, enum_field_types type);
|
|
extern int rewrite_interval(uchar *cp, char *to, const uint to_size, ulong *convert_len, enum_field_types type);
|
|
extern ulong calculate_new_time_length_with_nls(MYSQL *mysql, uchar *cp, ulong len, enum_field_types type);
|
|
extern ulong rewrite_new_time_with_nls(MYSQL *mysql, uchar *cp, ulong len, char *to, int64_t buf_len, enum_field_types type);
|
|
|
|
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 unsigned long long my_strtoull(const char *str, size_t len, const char **end, int *err)
|
|
{
|
|
unsigned long long val = 0;
|
|
const char *p = str;
|
|
const char *end_str = p + len;
|
|
|
|
for (; p < end_str; p++)
|
|
{
|
|
if (*p < '0' || *p > '9')
|
|
break;
|
|
|
|
if (val > ULONGLONG_MAX /10 || val*10 > ULONGLONG_MAX - (*p - '0'))
|
|
{
|
|
*err = ERANGE;
|
|
break;
|
|
}
|
|
val = val * 10 + *p -'0';
|
|
}
|
|
|
|
if (p == str)
|
|
/* Did not parse anything.*/
|
|
*err = ERANGE;
|
|
|
|
*end = p;
|
|
return val;
|
|
}
|
|
|
|
static long long my_strtoll(const char *str, size_t len, const char **end, int *err)
|
|
{
|
|
unsigned long long uval = 0;
|
|
const char *p = str;
|
|
const char *end_str = p + len;
|
|
int neg;
|
|
|
|
while (p < end_str && isspace(*p))
|
|
p++;
|
|
|
|
if (p == end_str)
|
|
{
|
|
*end = p;
|
|
*err = ERANGE;
|
|
return 0;
|
|
}
|
|
|
|
neg = *p == '-';
|
|
if (neg)
|
|
p++;
|
|
|
|
uval = my_strtoull(p, (end_str - p), &p, err);
|
|
*end = p;
|
|
if (*err)
|
|
return uval;
|
|
|
|
if (!neg)
|
|
{
|
|
/* Overflow of the long long range. */
|
|
if (uval > LONGLONG_MAX)
|
|
{
|
|
*end = p - 1;
|
|
uval = LONGLONG_MAX;
|
|
*err = ERANGE;
|
|
}
|
|
return uval;
|
|
}
|
|
|
|
if (uval == (unsigned long long) LONGLONG_MIN)
|
|
return LONGLONG_MIN;
|
|
|
|
if (uval > LONGLONG_MAX)
|
|
{
|
|
*end = p - 1;
|
|
uval = LONGLONG_MIN;
|
|
*err = ERANGE;
|
|
}
|
|
|
|
return -1LL * uval;
|
|
}
|
|
|
|
|
|
static long long my_atoll(const char *str, const char *end_str, int *error)
|
|
{
|
|
const char *p=str;
|
|
const char *end;
|
|
long long ret;
|
|
while (p < end_str && isspace(*p))
|
|
p++;
|
|
|
|
ret = my_strtoll(p, end_str - p, &end, error);
|
|
|
|
while(end < end_str && isspace(*end))
|
|
end++;
|
|
|
|
if(end != end_str)
|
|
*error= 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static unsigned long long my_atoull(const char *str, const char *end_str, int *error)
|
|
{
|
|
const char *p = str;
|
|
const char *end;
|
|
unsigned long long ret;
|
|
|
|
while (p < end_str && isspace(*p))
|
|
p++;
|
|
|
|
ret = my_strtoull(p, end_str - p, &end, error);
|
|
|
|
while(end < end_str && isspace(*end))
|
|
end++;
|
|
|
|
if(end != end_str)
|
|
*error= 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
double my_atod(const char *number, const char *end, int *error)
|
|
{
|
|
double val= 0.0;
|
|
char buffer[MAX_DBL_STR + 1];
|
|
int len= (int)(end - number);
|
|
|
|
*error= errno= 0;
|
|
|
|
if (len > MAX_DBL_STR)
|
|
{
|
|
*error= 1;
|
|
len= MAX_DBL_STR;
|
|
}
|
|
|
|
memcpy(buffer, number, len);
|
|
buffer[len]= '\0';
|
|
|
|
val= strtod(buffer, NULL);
|
|
|
|
if (errno)
|
|
*error= errno;
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
/*
|
|
strtoui() version, that works for non-null terminated strings
|
|
*/
|
|
static unsigned int my_strtoui(const char *str, size_t len, const char **end, int *err)
|
|
{
|
|
unsigned long long ull = my_strtoull(str, len, end, err);
|
|
if (ull > UINT_MAX)
|
|
*err = ERANGE;
|
|
return (unsigned int)ull;
|
|
}
|
|
|
|
/*
|
|
Parse time, in MySQL format.
|
|
|
|
the input string needs is in form "hour:minute:second[.fraction]"
|
|
hour, minute and second can have leading zeroes or not,
|
|
they are not necessarily 2 chars.
|
|
|
|
Hour must be < 838, minute < 60, second < 60
|
|
Only 6 places of fraction are considered, the value is truncated after 6 places.
|
|
*/
|
|
static const unsigned int frac_mul[] = { 1000000,100000,10000,1000,100,10 };
|
|
|
|
static int parse_time(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm)
|
|
{
|
|
int err= 0;
|
|
const char *p = str;
|
|
const char *end = str + length;
|
|
size_t frac_len;
|
|
int ret=1;
|
|
|
|
tm->hour = my_strtoui(p, end-p, &p, &err);
|
|
if (err || tm->hour > 838 || p == end || *p != ':' )
|
|
goto end;
|
|
|
|
p++;
|
|
tm->minute = my_strtoui(p, end-p, &p, &err);
|
|
if (err || tm->minute > 59 || p == end || *p != ':')
|
|
goto end;
|
|
|
|
p++;
|
|
tm->second = my_strtoui(p, end-p, &p, &err);
|
|
if (err || tm->second > 59)
|
|
goto end;
|
|
|
|
ret = 0;
|
|
tm->second_part = 0;
|
|
|
|
if (p == end)
|
|
goto end;
|
|
|
|
/* Check for fractional part*/
|
|
if (*p != '.')
|
|
goto end;
|
|
|
|
p++;
|
|
frac_len = MIN(6,end-p);
|
|
|
|
tm->second_part = my_strtoui(p, frac_len, &p, &err);
|
|
if (err)
|
|
goto end;
|
|
|
|
if (frac_len < 6)
|
|
tm->second_part *= frac_mul[frac_len];
|
|
|
|
ret = 0;
|
|
|
|
/* Consume whole fractional part, even after 6 digits.*/
|
|
p += frac_len;
|
|
while(p < *end_ptr)
|
|
{
|
|
if (*p < '0' || *p > '9')
|
|
break;
|
|
p++;
|
|
}
|
|
end:
|
|
*end_ptr = p;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
Parse date, in MySQL format.
|
|
|
|
The input string needs is in form "year-month-day"
|
|
year, month and day can have leading zeroes or not,
|
|
they do not have fixed length.
|
|
|
|
Year must be < 10000, month < 12, day < 32
|
|
|
|
Years with 2 digits, are converted to values 1970-2069 according to
|
|
usual rules:
|
|
|
|
00-69 is converted to 2000-2069.
|
|
70-99 is converted to 1970-1999.
|
|
*/
|
|
static int parse_date(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm)
|
|
{
|
|
int err = 0;
|
|
const char *p = str;
|
|
const char *end = str + length;
|
|
int ret = 1;
|
|
|
|
tm->year = my_strtoui(p, end - p, &p, &err);
|
|
if (err || tm->year > 9999 || p == end || *p != '-')
|
|
goto end;
|
|
|
|
if (p - str == 2) // 2-digit year
|
|
tm->year += (tm->year >= 70) ? 1900 : 2000;
|
|
|
|
p++;
|
|
tm->month = my_strtoui(p,end -p, &p, &err);
|
|
if (err || tm->month > 12 || p == end || *p != '-')
|
|
goto end;
|
|
|
|
p++;
|
|
tm->day = my_strtoui(p, end -p , &p, &err);
|
|
if (err || tm->day > 31)
|
|
goto end;
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
*end_ptr = p;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
Parse (not null terminated) string representing
|
|
TIME, DATE, or DATETIME into MYSQL_TIME structure
|
|
|
|
The supported formats by this functions are
|
|
- TIME : [-]hours:minutes:seconds[.fraction]
|
|
- DATE : year-month-day
|
|
- DATETIME : year-month-day<space>hours:minutes:seconds[.fraction]
|
|
|
|
cf https://dev.mysql.com/doc/refman/8.0/en/datetime.html
|
|
|
|
Whitespaces are trimmed from the start and end of the string.
|
|
The function ignores junk at the end of the string.
|
|
|
|
Parts of date of time do not have fixed length, so that parsing is compatible with server.
|
|
However server supports additional formats, e.g YYYYMMDD, HHMMSS, which this function does
|
|
not support.
|
|
|
|
*/
|
|
int str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
|
|
{
|
|
const char *p = str;
|
|
const char *end = str + length;
|
|
int is_time = 0;
|
|
|
|
if (!p)
|
|
goto error;
|
|
|
|
while (p < end && isspace(*p))
|
|
p++;
|
|
while (p < end && isspace(end[-1]))
|
|
end--;
|
|
|
|
if (end -p < 5)
|
|
goto error;
|
|
|
|
if (*p == '-')
|
|
{
|
|
tm->neg = 1;
|
|
/* Only TIME can't be negative.*/
|
|
is_time = 1;
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
tm->neg = 0;
|
|
/*
|
|
Date parsing (in server) accepts leading zeroes, thus position of the delimiters
|
|
is not fixed. Scan the string to find out what we need to parse.
|
|
*/
|
|
for (i = 1; p + i < end; i++)
|
|
{
|
|
if(p[i] == '-' || p [i] == ':')
|
|
{
|
|
is_time = p[i] == ':';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_time)
|
|
{
|
|
if (parse_time(p, end - p, &p, tm))
|
|
goto error;
|
|
|
|
tm->year = tm->month = tm->day = 0;
|
|
tm->time_type = MYSQL_TIMESTAMP_TIME;
|
|
return 0;
|
|
}
|
|
|
|
if (parse_date(p, end - p, &p, tm))
|
|
goto error;
|
|
|
|
if (p == end || p[0] != ' ')
|
|
{
|
|
tm->hour = tm->minute = tm->second = tm->second_part = 0;
|
|
tm->time_type = MYSQL_TIMESTAMP_DATE;
|
|
return 0;
|
|
}
|
|
|
|
/* Skip space. */
|
|
p++;
|
|
if (parse_time(p, end - p, &p, tm))
|
|
goto error;
|
|
|
|
/* In DATETIME, hours must be < 24.*/
|
|
if (tm->hour > 23)
|
|
goto error;
|
|
|
|
tm->time_type = MYSQL_TIMESTAMP_DATETIME;
|
|
return 0;
|
|
|
|
error:
|
|
memset(tm, 0, sizeof(*tm));
|
|
tm->time_type = MYSQL_TIMESTAMP_ERROR;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void convert_froma_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:
|
|
case MYSQL_TYPE_CURSOR:
|
|
{
|
|
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= r_param->is_unsigned ? (longlong)my_atoull(buffer, buffer + len, &error) : 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:
|
|
case MYSQL_TYPE_OB_UROWID:
|
|
default:
|
|
{
|
|
if (len >= r_param->offset)
|
|
{
|
|
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:
|
|
case MYSQL_TYPE_CURSOR:
|
|
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 = (dbl != ceil(dbl)) ||
|
|
(is_unsigned ? (ulonglong )dbl != (ulonglong)val :
|
|
(longlong)dbl != (longlong)val);
|
|
|
|
r_param->buffer_length= 8;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_FLOAT:
|
|
{
|
|
volatile float fval;
|
|
fval= is_unsigned ? (float)(ulonglong)(val) : (float)val;
|
|
floatstore((uchar *)r_param->buffer, fval);
|
|
*r_param->error= (fval != ceilf(fval)) ||
|
|
(is_unsigned ? (ulonglong)fval != (ulonglong)val :
|
|
(longlong)fval != val);
|
|
r_param->buffer_length= 4;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
char *buffer;
|
|
char *endptr;
|
|
uint len;
|
|
my_bool zf_truncated= 0;
|
|
|
|
buffer= alloca(MAX(field->length, 22));
|
|
endptr= ma_ll2str(val, buffer, is_unsigned ? 10 : -10);
|
|
len= (uint)(endptr - buffer);
|
|
|
|
/* check if field flag is zerofill */
|
|
if (field->flags & ZEROFILL_FLAG)
|
|
{
|
|
uint display_width= MAX(field->length, len);
|
|
if (display_width < r_param->buffer_length)
|
|
{
|
|
ma_bmove_upp(buffer + display_width, buffer + len, len);
|
|
/* coverity[bad_memset] */
|
|
memset((void*) buffer, (int) '0', display_width - len);
|
|
len= display_width;
|
|
}
|
|
else
|
|
zf_truncated= 1;
|
|
}
|
|
convert_froma_string(r_param, buffer, len);
|
|
*r_param->error+= zf_truncated;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* {{{ ps_fetch_null */
|
|
static
|
|
void ps_fetch_null(MYSQL_BIND *r_param __attribute__((unused)),
|
|
const MYSQL_FIELD * field __attribute__((unused)),
|
|
unsigned char **row __attribute__((unused)))
|
|
{
|
|
/* 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:
|
|
case MYSQL_TYPE_CURSOR:
|
|
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 ? (longlong)(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, float val, int size __attribute__((unused)))
|
|
{
|
|
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:
|
|
case MYSQL_TYPE_CURSOR:
|
|
{
|
|
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_DOUBLE:
|
|
{
|
|
double dval= (double)val;
|
|
memcpy(buf, &dval, 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 (isnan(val)) {
|
|
snprintf(buff, length, "%s", "Nan");
|
|
length = 3;
|
|
} else if (isinf(val)) {
|
|
if (val > 0) {
|
|
snprintf(buff, length, "%s", "Inf");
|
|
length = 3;
|
|
} else {
|
|
snprintf(buff, length, "%s", "-Inf");
|
|
length = 4;
|
|
}
|
|
} else {
|
|
if (field->decimals >= NOT_FIXED_DEC)
|
|
{
|
|
length = ma_gcvt(val, MY_GCVT_ARG_FLOAT, (int)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);
|
|
/* coverity[bad_memset] */
|
|
memset((void*) buff, (int) '0', field->length - length);
|
|
length= field->length;
|
|
}
|
|
|
|
convert_froma_string(r_param, buff, length);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void convert_from_double(MYSQL_BIND *r_param, const MYSQL_FIELD *field, double val, int size __attribute__((unused)))
|
|
{
|
|
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:
|
|
case MYSQL_TYPE_CURSOR:
|
|
{
|
|
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 (isnan(val)) {
|
|
snprintf(buff, length, "%s", "Nan");
|
|
length = 3;
|
|
} else if (isinf(val)) {
|
|
if (val > 0) {
|
|
snprintf(buff, length, "%s", "Inf");
|
|
length = 3;
|
|
} else {
|
|
snprintf(buff, length, "%s", "-Inf");
|
|
length = 4;
|
|
}
|
|
} else {
|
|
if (field->decimals >= NOT_FIXED_DEC)
|
|
{
|
|
length = ma_gcvt(val, MY_GCVT_ARG_DOUBLE, (int)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);
|
|
/* coverity [bad_memset] */
|
|
memset((void*) buff, (int) '0', field->length - length);
|
|
length= field->length;
|
|
}
|
|
convert_froma_string(r_param, buff, length);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* {{{ 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;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ 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;
|
|
}
|
|
/* }}} */
|
|
|
|
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];
|
|
if (type == MYSQL_TYPE_TIME)
|
|
t->hour+= t->day * 24;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ob oracle extened type
|
|
*/
|
|
static
|
|
void ps_fetch_oracle_timestamp(MYSQL_BIND *param,
|
|
const MYSQL_FIELD *field,
|
|
uchar **row)
|
|
{
|
|
uint buffer_length = 0;
|
|
uint length = net_field_length(row);
|
|
|
|
switch (param->buffer_type) {
|
|
case MYSQL_TYPE_OB_TIMESTAMP_NANO:
|
|
case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE:
|
|
case MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE: {
|
|
if (length > 16) {
|
|
buffer_length = length - 16 + sizeof(ORACLE_TIME) + 2;
|
|
} else {
|
|
buffer_length = sizeof(ORACLE_TIME);
|
|
}
|
|
|
|
if (param->buffer_length < buffer_length) {
|
|
*param->length = buffer_length;
|
|
*param->error = 1;
|
|
*row += length;
|
|
} else {
|
|
ORACLE_TIME *tm = (ORACLE_TIME *)param->buffer;
|
|
uint tz_length = 0;
|
|
uint buffer_offset = 0;
|
|
uchar *to = *row;
|
|
|
|
memset(tm, 0, sizeof(ORACLE_TIME));
|
|
tm->century = (int)(*(char*)to++);
|
|
tm->year = (int)(*(char*)to++);
|
|
tm->month = (uint)(*to++);
|
|
tm->day = (uint)(*to++);
|
|
tm->hour = (uint)(*to++);
|
|
tm->minute = (uint)(*to++);
|
|
tm->second = (uint)(*to++);
|
|
|
|
tm->second_part = (ulong)sint4korr(to);
|
|
to += 4;
|
|
tm->scale = (uint)(*to++);
|
|
|
|
buffer_length = buffer_offset = sizeof(ORACLE_TIME);
|
|
if (length > 12) {
|
|
tm->offset_hour = (int)(*(char*)to++);
|
|
tm->offset_minute = (int)(*(char*)to++);
|
|
|
|
tz_length = (uint)(*to++);
|
|
buffer_length += (tz_length + 1);
|
|
if (tz_length > 0 && buffer_offset + tz_length + 1 < param->buffer_length) {
|
|
memcpy((char*)param->buffer + buffer_offset, to, tz_length);
|
|
tm->tz_name = (char*)param->buffer + buffer_offset;
|
|
buffer_offset += tz_length;
|
|
*((char*)param->buffer + buffer_offset) = '\0';
|
|
buffer_offset++;
|
|
}
|
|
to += tz_length;
|
|
|
|
tz_length = (uint)(*to++);
|
|
buffer_length += (tz_length + 1);
|
|
if (tz_length > 0 && buffer_offset + tz_length + 1 < param->buffer_length) {
|
|
memcpy((char*)param->buffer + buffer_offset, to, tz_length);
|
|
tm->tz_abbr = (char*)param->buffer + buffer_offset;
|
|
buffer_offset += tz_length;
|
|
*((char*)param->buffer + buffer_offset) = '\0';
|
|
buffer_offset++;
|
|
}
|
|
to += tz_length;
|
|
}
|
|
*param->length = buffer_length;
|
|
*param->error = param->buffer_length < buffer_length;
|
|
*row = to;
|
|
}
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:{
|
|
uchar *buffer = *row;
|
|
ulong convert_len = calculate_new_time_length_with_nls(param->mysql, buffer, length, field->type);
|
|
*param->length = convert_len;
|
|
if (param->buffer_length < convert_len) {
|
|
*param->error = 1;
|
|
} else {
|
|
convert_len = rewrite_new_time_with_nls(param->mysql, buffer, length, param->buffer, (uint)(param->buffer_length), field->type);
|
|
*param->length = convert_len;
|
|
}
|
|
*row += length;
|
|
break;
|
|
}
|
|
default: {
|
|
convert_froma_string(param, (char *)*row, length);
|
|
*row += length;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void ps_fetch_oracle_interval(MYSQL_BIND *param, const MYSQL_FIELD *field, uchar **row)
|
|
{
|
|
uint length = net_field_length(row);
|
|
uchar * buffer = *row;
|
|
|
|
switch (param->buffer_type)
|
|
{
|
|
case MYSQL_TYPE_OB_INTERVAL_DS: {
|
|
*param->length = sizeof(ORACLE_INTERVAL);
|
|
if (param->buffer_length < sizeof(ORACLE_INTERVAL) || length != 14) {
|
|
*param->error = 1;
|
|
} else {
|
|
ORACLE_INTERVAL * interval = (ORACLE_INTERVAL*)param->buffer;
|
|
interval->mysql_type = MYSQL_TYPE_OB_INTERVAL_DS;
|
|
interval->data_symbol = (buffer[0] > 0 ? -1 : 1);
|
|
interval->data_object.ds_object.ds_day = sint4korr(buffer + 1);
|
|
interval->data_object.ds_object.ds_hour = buffer[5];
|
|
interval->data_object.ds_object.ds_minute = buffer[6];
|
|
interval->data_object.ds_object.ds_second = buffer[7];
|
|
interval->data_object.ds_object.ds_frac_second = sint4korr(buffer + 8);
|
|
interval->data_object.ds_object.ds_day_scale = buffer[12];
|
|
interval->data_object.ds_object.ds_frac_second_scale = buffer[13];
|
|
}
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_INTERVAL_YM: {
|
|
*param->length = sizeof(ORACLE_INTERVAL);
|
|
if (param->buffer_length < sizeof(ORACLE_INTERVAL) || length != 7) {
|
|
*param->error = 1;
|
|
} else {
|
|
ORACLE_INTERVAL * interval = (ORACLE_INTERVAL*)param->buffer;
|
|
interval->mysql_type = MYSQL_TYPE_OB_INTERVAL_YM;
|
|
interval->data_symbol = (buffer[0] > 0 ? -1 : 1);
|
|
interval->data_object.ym_object.ym_year = sint4korr(buffer + 1);
|
|
interval->data_object.ym_object.ym_month = buffer[5];
|
|
interval->data_object.ym_object.ym_scale = buffer[6];
|
|
}
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING: {
|
|
ulong convert_len = calculate_interval_length(buffer, field->type);
|
|
*param->length = convert_len;
|
|
if ((length != 7 && length != 14) || param->buffer_length < convert_len) {
|
|
*param->error = 1;
|
|
} else {
|
|
if (!rewrite_interval(buffer, param->buffer, (uint)(param->buffer_length), &convert_len, field->type)) {
|
|
*param->error = 1;
|
|
}
|
|
*param->length = convert_len;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
convert_froma_string(param, (char *)*row, length);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*row += length;
|
|
}
|
|
|
|
static
|
|
void ps_fetch_oracle_raw(MYSQL_BIND *param, const MYSQL_FIELD *field, uchar **row)
|
|
{
|
|
ulong length = net_field_length(row);
|
|
uchar * buffer = *row;
|
|
UNUSED(field);
|
|
|
|
switch (param->buffer_type)
|
|
{
|
|
case MYSQL_TYPE_OB_RAW: {
|
|
convert_froma_string(param, (char *)*row, length);
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:{
|
|
uchar *to = param->buffer;
|
|
uchar *cp = buffer;
|
|
uchar *end = buffer + length;
|
|
if (param->buffer_length < length * 2) {
|
|
*param->error = 1;
|
|
} else {
|
|
for (; cp < end; cp++, to += 2) {
|
|
sprintf((char *)to, "%02X", *((uchar*)cp));
|
|
}
|
|
(*to++) = 0;
|
|
}
|
|
*param->length = length * 2;
|
|
break;
|
|
}
|
|
default: {
|
|
convert_froma_string(param, (char *)*row, length);
|
|
break;
|
|
}
|
|
}
|
|
*row += length;
|
|
}
|
|
|
|
|
|
/* {{{ 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);
|
|
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;
|
|
size_t length;
|
|
convert_to_datetime(&tm, row, len, field->type);
|
|
|
|
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 && field->decimals <= 6)
|
|
{
|
|
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 && field->decimals <= 6)
|
|
{
|
|
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_froma_string(r_param, dtbuffer, length);
|
|
break;
|
|
}
|
|
}
|
|
(*row) += len;
|
|
}
|
|
/* }}} */
|
|
|
|
/*fetch ob_lob*/
|
|
static void fill_ob_lob_locator(OB_LOB_LOCATOR *ob_lob_locator, uchar *row)
|
|
{
|
|
ob_lob_locator->magic_code_ = uint4korr(row);
|
|
ob_lob_locator->version_ = uint4korr(row + 4);
|
|
ob_lob_locator->snapshot_version_ = sint8korr(row + 8);
|
|
ob_lob_locator->table_id_ = uint8korr(row + 16);
|
|
ob_lob_locator->column_id_ = uint4korr(row + 24);
|
|
ob_lob_locator->mode_ = uint2korr(row + 28);
|
|
ob_lob_locator->option_ = uint2korr(row + 30);
|
|
ob_lob_locator->payload_offset_ = uint4korr(row + 32);
|
|
ob_lob_locator->payload_size_ = uint4korr(row + 36);
|
|
}
|
|
|
|
|
|
static void fill_ob_client_mem_lob_common(ObClientMemLobCommon* common, uchar *row)
|
|
{
|
|
#define BIT1 0x01
|
|
#define BIT4 0x0F
|
|
#define BIT8 0xFF
|
|
#define BIT15 0x7FFF
|
|
uint32_t tmps = 0;
|
|
common->magic_ = uint4korr(row);
|
|
tmps = uint4korr(row + 4);
|
|
common->version_ = tmps & BIT8;
|
|
tmps >>= 8;
|
|
common->type_ = tmps & BIT4;
|
|
tmps >>= 4;
|
|
common->read_only_ = tmps & BIT1;
|
|
tmps >>= 1;
|
|
common->is_inrow_ = tmps & BIT1;
|
|
tmps >>= 1;
|
|
common->is_open_ = tmps & BIT1;
|
|
tmps >>= 1;
|
|
common->is_simple = tmps & BIT1;
|
|
tmps >>= 1;
|
|
common->has_extern = tmps & BIT1;
|
|
tmps >>= 1;
|
|
common->reserved_ = tmps & BIT15;
|
|
}
|
|
static void fill_ob_client_mem_lob_extern_header(ObClientMemLobExternHeader* header, uchar *row)
|
|
{
|
|
#define BIT1 0x01
|
|
#define BIT13 0x1FFF
|
|
uint16_t tmps = 0;
|
|
header->snapshot_ver_ = sint8korr(row);
|
|
header->table_id_ = uint8korr(row+8);
|
|
header->column_idx_ = uint4korr(row+16);
|
|
|
|
tmps = uint2korr(row + 20);
|
|
header->has_tx_info = tmps & BIT1;
|
|
tmps >>= 1;
|
|
header->has_cid_hash = tmps & BIT1;
|
|
tmps >>= 1;
|
|
header->has_view_info = tmps & BIT1;
|
|
tmps >>= 1;
|
|
header->extern_flags_ = tmps & BIT13;
|
|
|
|
header->rowkey_size_ = uint2korr(row + 22);
|
|
header->payload_offset_ = uint4korr(row + 24);
|
|
header->payload_size_ = uint4korr(row + 28);
|
|
}
|
|
static void fill_ob_lob_locator_v2(OB_LOB_LOCATOR_V2 *ob_lob_locator, uchar *row)
|
|
{
|
|
fill_ob_client_mem_lob_common(&ob_lob_locator->common, row);
|
|
if (ob_lob_locator->common.has_extern){
|
|
fill_ob_client_mem_lob_extern_header(&ob_lob_locator->extern_header, row+sizeof(ObClientMemLobCommon));
|
|
}
|
|
}
|
|
|
|
static void fetch_result_ob_lob(MYSQL_BIND *param,
|
|
const MYSQL_FIELD *field __attribute__((unused)),
|
|
uchar **row)
|
|
{
|
|
ulong length= net_field_length(row);
|
|
ulong copy_length = 0;
|
|
|
|
if (param->buffer_length <= 0
|
|
|| param->buffer_length < MAX_OB_LOB_LOCATOR_HEADER_LENGTH
|
|
|| length < MAX_OB_LOB_LOCATOR_HEADER_LENGTH) {
|
|
*param->error= 1;
|
|
} else {
|
|
ObClientMemLobCommon common;
|
|
fill_ob_client_mem_lob_common(&common, *row);
|
|
if (common.version_ == OBCLIENT_LOB_LOCATORV1) {
|
|
OB_LOB_LOCATOR *ob_lob_locator = (OB_LOB_LOCATOR *)param->buffer;
|
|
fill_ob_lob_locator(ob_lob_locator, *row);
|
|
|
|
copy_length = MIN(param->buffer_length - MAX_OB_LOB_LOCATOR_HEADER_LENGTH, length - MAX_OB_LOB_LOCATOR_HEADER_LENGTH);
|
|
memcpy(ob_lob_locator->data_, (*row) + MAX_OB_LOB_LOCATOR_HEADER_LENGTH, copy_length);
|
|
*param->error= copy_length + MAX_OB_LOB_LOCATOR_HEADER_LENGTH < length;
|
|
} else if (common.version_ == OBCLIENT_LOB_LOCATORV2) {
|
|
OB_LOB_LOCATOR_V2 *ob_lob_locatorv2 = (OB_LOB_LOCATOR_V2 *)param->buffer;
|
|
//for oracle mode, common.has_extern = 1
|
|
fill_ob_lob_locator_v2(ob_lob_locatorv2, *row);
|
|
|
|
copy_length = MIN(param->buffer_length - MAX_OB_LOB_LOCATOR_HEADER_LENGTH, length - MAX_OB_LOB_LOCATOR_HEADER_LENGTH);
|
|
memcpy(ob_lob_locatorv2->data_, (*row) + MAX_OB_LOB_LOCATOR_HEADER_LENGTH, copy_length);
|
|
*param->error = copy_length + MAX_OB_LOB_LOCATOR_HEADER_LENGTH < length;
|
|
}
|
|
}
|
|
|
|
*param->length= length;
|
|
*row += length;
|
|
}
|
|
|
|
/*fetch_complex_type*/
|
|
static void fetch_result_complex(MYSQL_BIND *param, void *buffer,
|
|
CHILD_TYPE *child, uchar **row);
|
|
static void *fetch_result_complex_alloc_space(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
ulong length);
|
|
static void fill_complex_type(MYSQL_BIND *param, void *buffer,
|
|
CHILD_TYPE *child);
|
|
static ulong get_complex_header_length(enum_types type) {
|
|
switch (type) {
|
|
case TYPE_OBJECT:
|
|
return sizeof(MYSQL_COMPLEX_BIND_OBJECT);
|
|
case TYPE_COLLECTION:
|
|
return sizeof(MYSQL_COMPLEX_BIND_ARRAY);
|
|
case TYPE_NVARCHAR2:
|
|
case TYPE_VARCHAR2:
|
|
case TYPE_CHAR:
|
|
case TYPE_RAW:
|
|
return sizeof(MYSQL_COMPLEX_BIND_STRING);
|
|
case TYPE_NUMBER:
|
|
return sizeof(MYSQL_COMPLEX_BIND_DECIMAL);
|
|
case TYPE_LONG:
|
|
case TYPE_LONGLONG:
|
|
case TYPE_TINY:
|
|
case TYPE_SHORT:
|
|
case TYPE_FLOAT:
|
|
case TYPE_DOUBLE:
|
|
return sizeof(MYSQL_COMPLEX_BIND_BASIC);
|
|
case TYPE_OB_NUMBER_FLOAT:
|
|
return sizeof(MYSQL_COMPLEX_BIND_STRING);
|
|
case TYPE_OB_INTERVAL_YM:
|
|
case TYPE_OB_INTERVAL_DS:
|
|
case TYPE_OB_TIMESTAMP_NANO:
|
|
case TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE:
|
|
case TYPE_OB_TIMESTAMP_WITH_TIME_ZONE:
|
|
return sizeof(MYSQL_COMPLEX_BIND_BASIC);
|
|
default:
|
|
return sizeof(MYSQL_COMPLEX_BIND_BASIC);
|
|
}
|
|
}
|
|
static void fetch_result_str_complex(MYSQL_COMPLEX_BIND_STRING *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
ulong length;
|
|
|
|
length = net_field_length(row);
|
|
|
|
//include end '\0'
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, length + 1);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
memcpy(buffer, (char *)*row, length);
|
|
((uchar *)buffer)[length]= '\0';
|
|
header->length = length;
|
|
*row+= length;
|
|
return;
|
|
}
|
|
static void fetch_result_long_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 4);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
longstore(buffer, ((uint32)sint4korr(*row)));
|
|
*row += 4;
|
|
|
|
return;
|
|
}
|
|
static void fetch_result_longlong_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
ulonglong val;
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 8);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
val= (ulonglong)sint8korr(*row);
|
|
longlongstore(buffer, val);
|
|
*row += 8;
|
|
|
|
return;
|
|
}
|
|
static void fetch_result_short_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 2);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
shortstore(buffer, ((ushort)sint2korr(*row)));
|
|
*row += 2;
|
|
|
|
return;
|
|
}
|
|
static void fetch_result_tiny_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 1);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
*(uchar *)buffer= **row;
|
|
*row += 1;
|
|
|
|
return;
|
|
}
|
|
static void fetch_result_float_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
float *value;
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 4);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
value= (float *)buffer;
|
|
float4get(*value, *row);
|
|
|
|
*row += 4;
|
|
return;
|
|
}
|
|
static void fetch_result_double_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
double *value;
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, 8);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
value= (double *)buffer;
|
|
float8get(*value, *row);
|
|
|
|
*row += 8;
|
|
return;
|
|
}
|
|
|
|
static void read_binary_interval(int type, ORACLE_INTERVAL *tm, uchar **pos)
|
|
{
|
|
uint length = net_field_length(pos);
|
|
uchar *to = *pos;
|
|
ORACLE_INTERVAL *interval = tm;
|
|
|
|
switch (type)
|
|
{
|
|
case MYSQL_TYPE_OB_INTERVAL_DS: {
|
|
if (length == 14) {
|
|
interval->mysql_type = MYSQL_TYPE_OB_INTERVAL_DS;
|
|
interval->data_symbol = (to[0] > 0 ? -1 : 1);
|
|
interval->data_object.ds_object.ds_day = sint4korr(to + 1);
|
|
interval->data_object.ds_object.ds_hour = to[5];
|
|
interval->data_object.ds_object.ds_minute = to[6];
|
|
interval->data_object.ds_object.ds_second = to[7];
|
|
interval->data_object.ds_object.ds_frac_second = sint4korr(to + 8);
|
|
interval->data_object.ds_object.ds_day_scale = to[12];
|
|
interval->data_object.ds_object.ds_frac_second_scale = to[13];
|
|
}
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_INTERVAL_YM: {
|
|
if (length == 7) {
|
|
interval->mysql_type = MYSQL_TYPE_OB_INTERVAL_YM;
|
|
interval->data_symbol = (to[0] > 0 ? -1 : 1);
|
|
interval->data_object.ym_object.ym_year = sint4korr(to + 1);
|
|
interval->data_object.ym_object.ym_month = to[5];
|
|
interval->data_object.ym_object.ym_scale = to[6];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
*pos += length;
|
|
}
|
|
static void fetch_result_ob_interval_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
ORACLE_INTERVAL *tm = NULL;
|
|
|
|
tm = fetch_result_complex_alloc_space(header, param, sizeof(ORACLE_INTERVAL));
|
|
if (NULL == tm) {
|
|
return;
|
|
}
|
|
|
|
read_binary_interval(header->buffer_type, tm, row);
|
|
return;
|
|
}
|
|
|
|
static void fetch_result_ob_timestamp_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer = NULL;
|
|
ulong bufferlen = 0;
|
|
ulong length = 0;
|
|
ulong tz_length = 0;
|
|
ulong buffer_offset = 0;
|
|
ulong buffer_length = 0;
|
|
ORACLE_TIME *tm = NULL;
|
|
uchar *to = NULL;
|
|
|
|
length = net_field_length(row);
|
|
bufferlen = sizeof(ORACLE_TIME) + length; //+length 包括TZ信息
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, bufferlen);
|
|
if (NULL == buffer || length < 12) {
|
|
return;
|
|
}
|
|
|
|
to = *row;
|
|
|
|
tm = (ORACLE_TIME*)buffer;
|
|
memset(tm, 0, sizeof(ORACLE_TIME));
|
|
tm->century = (int)(*(char*)to++);
|
|
tm->year = (int)(*(char*)to++);
|
|
tm->month = (uint)(*to++);
|
|
tm->day = (uint)(*to++);
|
|
tm->hour = (uint)(*to++);
|
|
tm->minute = (uint)(*to++);
|
|
tm->second = (uint)(*to++);
|
|
|
|
tm->second_part = (ulong)sint4korr(to);
|
|
to += 4;
|
|
tm->scale = (uint)(*to++);
|
|
|
|
buffer_length = buffer_offset = sizeof(ORACLE_TIME);
|
|
if (length > 12) {
|
|
tm->offset_hour = (int)(*(char*)to++);
|
|
tm->offset_minute = (int)(*(char*)to++);
|
|
|
|
tz_length = (uint)(*to++);
|
|
buffer_length += (tz_length + 1);
|
|
if (tz_length > 0 && buffer_offset + tz_length + 1 < bufferlen) {
|
|
memcpy((char*)buffer + buffer_offset, to, tz_length);
|
|
tm->tz_name = (char*)buffer + buffer_offset;
|
|
buffer_offset += tz_length;
|
|
*((char*)buffer + buffer_offset) = '\0';
|
|
buffer_offset++;
|
|
}
|
|
to += tz_length;
|
|
|
|
tz_length = (uint)(*to++);
|
|
buffer_length += (tz_length + 1);
|
|
if (tz_length > 0 && buffer_offset + tz_length + 1 < bufferlen) {
|
|
memcpy((char*)buffer + buffer_offset, to, tz_length);
|
|
tm->tz_abbr = (char*)buffer + buffer_offset;
|
|
buffer_offset += tz_length;
|
|
*((char*)buffer + buffer_offset) = '\0';
|
|
buffer_offset++;
|
|
}
|
|
to += tz_length;
|
|
}
|
|
*row = to;
|
|
return;
|
|
}
|
|
|
|
static void fetch_result_object_complex(MYSQL_COMPLEX_BIND_OBJECT *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer = NULL;
|
|
ulong length = 0;
|
|
uint i = 0;
|
|
COMPLEX_TYPE_OBJECT *object = NULL;
|
|
struct st_complex_type *complex_type = NULL;
|
|
uchar *null_ptr, bit;
|
|
|
|
complex_type = get_complex_type(param->mysql, header->owner_name, header->type_name);
|
|
|
|
if (complex_type == NULL) {
|
|
*param->error= 1;
|
|
return;
|
|
}
|
|
|
|
object = (COMPLEX_TYPE_OBJECT *)complex_type;
|
|
|
|
for (i = 0; i < object->attr_no; i++) {
|
|
length += get_complex_header_length(object->child[i].type);
|
|
}
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header, param, length);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
null_ptr = *row;
|
|
*row += (object->attr_no + 9)/8; /* skip null bits */
|
|
bit = 4; /* first 2 bits are reserved */
|
|
|
|
for (i = 0; i < object->attr_no; i++) {
|
|
fill_complex_type(param, buffer, &(object->child[i]));
|
|
|
|
if (*null_ptr & bit) {
|
|
((MYSQL_COMPLEX_BIND_BASIC *)buffer)->is_null = 1;
|
|
} else {
|
|
fetch_result_complex(param, buffer, &(object->child[i]), row);
|
|
}
|
|
|
|
buffer = (char*)buffer + get_complex_header_length(object->child[i].type);
|
|
|
|
if (!((bit<<=1) & 255)) {
|
|
bit= 1; /* To next uchar */
|
|
null_ptr++;
|
|
}
|
|
}
|
|
|
|
header->length = (char*)buffer - (char*)header->buffer;
|
|
return;
|
|
}
|
|
|
|
static void read_binary_datetime(MYSQL_TIME *tm, uchar **pos)
|
|
{
|
|
uint length= net_field_length(pos);
|
|
|
|
if (length)
|
|
{
|
|
uchar *to= *pos;
|
|
|
|
tm->neg= 0;
|
|
tm->year= (uint) sint2korr(to);
|
|
tm->month= (uint) to[2];
|
|
tm->day= (uint) to[3];
|
|
|
|
if (length > 4)
|
|
{
|
|
tm->hour= (uint) to[4];
|
|
tm->minute= (uint) to[5];
|
|
tm->second= (uint) to[6];
|
|
}
|
|
else
|
|
tm->hour= tm->minute= tm->second= 0;
|
|
tm->second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
|
|
tm->time_type= MYSQL_TIMESTAMP_DATETIME;
|
|
|
|
*pos+= length;
|
|
}
|
|
else
|
|
{
|
|
memset(tm, 0, sizeof(*tm));
|
|
tm->time_type= MYSQL_TIMESTAMP_DATETIME;
|
|
}
|
|
}
|
|
static void fetch_result_datetime_complex(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
MYSQL_TIME *tm = NULL;
|
|
|
|
tm = fetch_result_complex_alloc_space(header, param, sizeof(MYSQL_TIME));
|
|
if (NULL == tm) {
|
|
return;
|
|
}
|
|
|
|
read_binary_datetime(tm, row);
|
|
return;
|
|
}
|
|
|
|
static void fetch_result_bin_complex(MYSQL_COMPLEX_BIND_STRING *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row)
|
|
{
|
|
void *buffer;
|
|
ulong length;
|
|
|
|
length = net_field_length(row);
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_HEADER *)header, param, length);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
memcpy(buffer, (char *)*row, length);
|
|
header->length = length;
|
|
*row+= length;
|
|
return;
|
|
}
|
|
static void fetch_result_array_complex(MYSQL_COMPLEX_BIND_ARRAY *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row,
|
|
COMPLEX_TYPE *complex_type)
|
|
{
|
|
void *buffer = NULL;
|
|
ulong length = 0;
|
|
uint num = 0;
|
|
uint i = 0;
|
|
COMPLEX_TYPE_COLLECTION *object = NULL;
|
|
uchar *null_ptr, bit;
|
|
|
|
if (NULL == complex_type) {
|
|
complex_type = get_complex_type(param->mysql, header->owner_name, header->type_name);
|
|
|
|
if (complex_type == NULL) {
|
|
*param->error= 1;
|
|
return;
|
|
}
|
|
} else {/* 匿名数组本地存储信息 */}
|
|
|
|
object = (COMPLEX_TYPE_COLLECTION *)complex_type;
|
|
|
|
num = net_field_length(row);
|
|
length = num * get_complex_header_length(object->child.type);
|
|
|
|
buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header, param, length);
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
null_ptr = *row;
|
|
*row += (num + 9)/8; /* skip null bits */
|
|
bit = 4; /* first 2 bits are reserved */
|
|
|
|
for (i = 0; i < num; i++) {
|
|
fill_complex_type(param, buffer, &(object->child));
|
|
|
|
if (*null_ptr & bit) {
|
|
((MYSQL_COMPLEX_BIND_BASIC *)buffer)->is_null = 1;
|
|
} else {
|
|
fetch_result_complex(param, buffer, &(object->child), row);
|
|
}
|
|
|
|
buffer = (char*)buffer + get_complex_header_length(object->child.type);
|
|
|
|
if (!((bit<<=1) & 255)) {
|
|
bit= 1; /* To next uchar */
|
|
null_ptr++;
|
|
}
|
|
}
|
|
|
|
header->length = num;
|
|
return;
|
|
}
|
|
static void fetch_result_complex(MYSQL_BIND *param, void *buffer,
|
|
CHILD_TYPE *child, uchar **row)
|
|
{
|
|
switch (child->type) {
|
|
case TYPE_OB_NUMBER_FLOAT:
|
|
case TYPE_NUMBER:
|
|
{
|
|
fetch_result_str_complex((MYSQL_COMPLEX_BIND_STRING *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_NVARCHAR2:
|
|
case TYPE_VARCHAR2:
|
|
case TYPE_CHAR:
|
|
{
|
|
fetch_result_str_complex((MYSQL_COMPLEX_BIND_STRING *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_RAW:
|
|
{
|
|
fetch_result_bin_complex((MYSQL_COMPLEX_BIND_STRING *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_DATE:
|
|
{
|
|
fetch_result_datetime_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_OBJECT:
|
|
{
|
|
fetch_result_object_complex((MYSQL_COMPLEX_BIND_OBJECT *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_COLLECTION:
|
|
{
|
|
fetch_result_array_complex((MYSQL_COMPLEX_BIND_ARRAY *)buffer, param, row, NULL);
|
|
break;
|
|
}
|
|
case TYPE_LONG:
|
|
{
|
|
fetch_result_long_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_LONGLONG:
|
|
{
|
|
fetch_result_longlong_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_TINY:
|
|
{
|
|
fetch_result_tiny_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_SHORT:
|
|
{
|
|
fetch_result_short_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_FLOAT:
|
|
{
|
|
fetch_result_float_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_DOUBLE:
|
|
{
|
|
fetch_result_double_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_OB_INTERVAL_DS:
|
|
case TYPE_OB_INTERVAL_YM:
|
|
{
|
|
fetch_result_ob_interval_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
case TYPE_OB_TIMESTAMP_NANO:
|
|
case TYPE_OB_TIMESTAMP_WITH_TIME_ZONE:
|
|
case TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE:
|
|
{
|
|
fetch_result_ob_timestamp_complex((MYSQL_COMPLEX_BIND_BASIC *)buffer, param, row);
|
|
break;
|
|
}
|
|
default:
|
|
*param->error= 1;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
static void *fetch_result_complex_alloc_space(MYSQL_COMPLEX_BIND_BASIC *header,
|
|
MYSQL_BIND *param,
|
|
ulong length)
|
|
{
|
|
void *buffer = NULL;
|
|
(*param->length) += length;
|
|
if (1 == *param->error) {
|
|
buffer = (void *)ma_alloc_root(¶m->bind_alloc, length);
|
|
} else if (param->offset + length > param->buffer_length) {
|
|
*param->error= 1;
|
|
buffer = (void *)ma_alloc_root(¶m->bind_alloc, length);
|
|
} else {
|
|
buffer = (char*)param->buffer + param->offset;
|
|
}
|
|
|
|
header->buffer = buffer;
|
|
if (header->buffer) {
|
|
memset(header->buffer, 0, length);
|
|
param->offset += length;
|
|
}
|
|
|
|
return (header->buffer);
|
|
}
|
|
static uint mysql_type_to_object_type(uint mysql_type)
|
|
{
|
|
enum_types object_type;
|
|
switch ((enum_field_types)mysql_type) {
|
|
case MYSQL_TYPE_LONG:
|
|
case MYSQL_TYPE_CURSOR:
|
|
{
|
|
object_type = TYPE_LONG;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_LONGLONG:
|
|
{
|
|
object_type = TYPE_LONGLONG;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_TINY:
|
|
{
|
|
object_type = TYPE_TINY;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_SHORT:
|
|
{
|
|
object_type = TYPE_SHORT;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_FLOAT:
|
|
{
|
|
object_type = TYPE_FLOAT;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_DOUBLE:
|
|
{
|
|
object_type = TYPE_DOUBLE;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_NEWDECIMAL:
|
|
{
|
|
object_type = TYPE_NUMBER;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_VARCHAR:
|
|
case MYSQL_TYPE_VAR_STRING:
|
|
case MYSQL_TYPE_STRING:
|
|
{
|
|
object_type = TYPE_VARCHAR2;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_NVARCHAR2:
|
|
{
|
|
object_type = TYPE_NVARCHAR2;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_RAW:
|
|
{
|
|
object_type = TYPE_RAW;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_DATETIME:
|
|
{
|
|
object_type = TYPE_DATE;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OBJECT:
|
|
{
|
|
object_type = TYPE_OBJECT;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_ARRAY:
|
|
{
|
|
object_type = TYPE_COLLECTION;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_NUMBER_FLOAT:
|
|
{
|
|
object_type = TYPE_OB_NUMBER_FLOAT;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_INTERVAL_DS:
|
|
{
|
|
object_type = TYPE_OB_INTERVAL_DS;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_INTERVAL_YM:
|
|
{
|
|
object_type = TYPE_OB_INTERVAL_YM;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_TIMESTAMP_NANO:
|
|
{
|
|
object_type = TYPE_OB_TIMESTAMP_NANO;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE:
|
|
{
|
|
object_type = TYPE_OB_TIMESTAMP_WITH_TIME_ZONE;
|
|
break;
|
|
}
|
|
case MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE:
|
|
{
|
|
object_type = TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE;
|
|
break;
|
|
}
|
|
default:
|
|
object_type = TYPE_UNKNOW;
|
|
break;
|
|
}
|
|
return object_type;
|
|
}
|
|
static void fill_complex_type(MYSQL_BIND *param, void *buffer,
|
|
CHILD_TYPE *child)
|
|
{
|
|
|
|
switch (child->type) {
|
|
case TYPE_NUMBER:
|
|
{
|
|
MYSQL_COMPLEX_BIND_DECIMAL *header = (MYSQL_COMPLEX_BIND_DECIMAL *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
|
break;
|
|
}
|
|
case TYPE_VARCHAR2:
|
|
case TYPE_CHAR:
|
|
{
|
|
MYSQL_COMPLEX_BIND_STRING *header = (MYSQL_COMPLEX_BIND_STRING *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_VARCHAR;
|
|
break;
|
|
}
|
|
case TYPE_NVARCHAR2:
|
|
{
|
|
MYSQL_COMPLEX_BIND_STRING *header = (MYSQL_COMPLEX_BIND_STRING *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_NVARCHAR2;
|
|
break;
|
|
}
|
|
case TYPE_RAW:
|
|
{
|
|
MYSQL_COMPLEX_BIND_STRING *header = (MYSQL_COMPLEX_BIND_STRING *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_RAW;
|
|
break;
|
|
}
|
|
case TYPE_DATE:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_DATETIME;
|
|
break;
|
|
}
|
|
case TYPE_LONG:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_LONG;
|
|
break;
|
|
}
|
|
case TYPE_LONGLONG:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_LONGLONG;
|
|
break;
|
|
}
|
|
case TYPE_TINY:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_TINY;
|
|
break;
|
|
}
|
|
case TYPE_SHORT:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_SHORT;
|
|
break;
|
|
}
|
|
case TYPE_FLOAT:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_FLOAT;
|
|
break;
|
|
}
|
|
case TYPE_DOUBLE:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_DOUBLE;
|
|
break;
|
|
}
|
|
case TYPE_OB_NUMBER_FLOAT:
|
|
{
|
|
MYSQL_COMPLEX_BIND_STRING *header = (MYSQL_COMPLEX_BIND_STRING *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_NUMBER_FLOAT;
|
|
break;
|
|
}
|
|
case TYPE_OB_INTERVAL_DS:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_INTERVAL_DS;
|
|
break;
|
|
}
|
|
case TYPE_OB_INTERVAL_YM:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_INTERVAL_YM;
|
|
break;
|
|
}
|
|
case TYPE_OB_TIMESTAMP_NANO:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_TIMESTAMP_NANO;
|
|
break;
|
|
}
|
|
case TYPE_OB_TIMESTAMP_WITH_TIME_ZONE:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE;
|
|
break;
|
|
}
|
|
case TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE:
|
|
{
|
|
MYSQL_COMPLEX_BIND_BASIC *header = (MYSQL_COMPLEX_BIND_BASIC *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE;
|
|
break;
|
|
}
|
|
case TYPE_OBJECT:
|
|
{
|
|
MYSQL_COMPLEX_BIND_OBJECT *header = (MYSQL_COMPLEX_BIND_OBJECT *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_OBJECT;
|
|
header->type_name = child->object->type_name;
|
|
header->owner_name = child->object->owner_name;
|
|
break;
|
|
}
|
|
case TYPE_COLLECTION:
|
|
{
|
|
MYSQL_COMPLEX_BIND_ARRAY *header = (MYSQL_COMPLEX_BIND_ARRAY *)buffer;
|
|
header->buffer_type = MYSQL_TYPE_ARRAY;
|
|
header->type_name = child->object->type_name;
|
|
header->owner_name = child->object->owner_name;
|
|
break;
|
|
}
|
|
default:
|
|
*param->error= 1;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
static void fetch_result_type_complex(MYSQL_COMPLEX_BIND_OBJECT *header,
|
|
MYSQL_BIND *param,
|
|
uchar **row,
|
|
COMPLEX_TYPE *complex_type)
|
|
{
|
|
if (NULL == complex_type) {
|
|
complex_type = get_complex_type(param->mysql, header->owner_name, header->type_name);
|
|
if (NULL == complex_type) {
|
|
*param->error= 1;
|
|
return;
|
|
}
|
|
} else { /*匿名数组,meta信息不能通过server查询,本地传入 */ }
|
|
|
|
if (TYPE_OBJECT == complex_type->type) {
|
|
MYSQL_COMPLEX_BIND_OBJECT *buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header,
|
|
param, sizeof(MYSQL_COMPLEX_BIND_OBJECT));
|
|
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
buffer->owner_name = header->owner_name;
|
|
buffer->type_name = header->type_name;
|
|
buffer->buffer_type = MYSQL_TYPE_OBJECT;
|
|
|
|
fetch_result_object_complex(buffer, param, row);
|
|
} else if (TYPE_COLLECTION == complex_type->type) {
|
|
MYSQL_COMPLEX_BIND_ARRAY *buffer = fetch_result_complex_alloc_space((MYSQL_COMPLEX_BIND_BASIC *)header,
|
|
param, sizeof(MYSQL_COMPLEX_BIND_ARRAY));
|
|
|
|
if (NULL == buffer) {
|
|
return;
|
|
}
|
|
|
|
buffer->owner_name = header->owner_name;
|
|
buffer->type_name = header->type_name;
|
|
buffer->buffer_type = MYSQL_TYPE_ARRAY;
|
|
|
|
fetch_result_array_complex(buffer, param, row, complex_type);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void ps_fetch_result_type(MYSQL_BIND *param,
|
|
const MYSQL_FIELD *field,
|
|
uchar **row)
|
|
{
|
|
MYSQL_COMPLEX_BIND_OBJECT header;
|
|
|
|
(*param->length) = 0;
|
|
param->offset = 0;
|
|
ma_init_alloc_root(¶m->bind_alloc, 2048, 0);
|
|
|
|
header.owner_name = field->owner_name;
|
|
header.type_name = field->type_name;
|
|
|
|
if (NULL == header.owner_name && NULL == header.type_name) {
|
|
COMPLEX_TYPE_COLLECTION complex_type;
|
|
complex_type.header.type = TYPE_COLLECTION;
|
|
complex_type.header.owner_name[0] = '\0';
|
|
complex_type.header.type_name[0] = '\0';
|
|
complex_type.header.version = field->version;
|
|
complex_type.header.is_valid = TRUE;
|
|
complex_type.child.type = (enum_types)mysql_type_to_object_type(field->elem_type);
|
|
|
|
if (TYPE_UNKNOW == complex_type.child.type) {
|
|
*param->error = 1;
|
|
} else {
|
|
fetch_result_type_complex(&header, param, row, (COMPLEX_TYPE *)&complex_type);
|
|
}
|
|
} else {
|
|
fetch_result_type_complex(&header, param, row, NULL);
|
|
}
|
|
|
|
ma_free_root(¶m->bind_alloc, MYF(0));
|
|
return;
|
|
}
|
|
|
|
/* {{{ ps_fetch_string */
|
|
static
|
|
void ps_fetch_string(MYSQL_BIND *r_param,
|
|
const MYSQL_FIELD *field __attribute__((unused)),
|
|
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_froma_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 ||
|
|
r_param->buffer_type == MYSQL_TYPE_JSON))
|
|
((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);
|
|
}
|
|
/* }}} */
|
|
|
|
static
|
|
void ps_fetch_mysql_lob(MYSQL_BIND *r_param, const MYSQL_FIELD *field, unsigned char **row)
|
|
{
|
|
ulong length = net_field_length(row);
|
|
if (field->charsetnr == 63)
|
|
{
|
|
ulong field_length = *r_param->length = length;
|
|
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 ||
|
|
r_param->buffer_type == MYSQL_TYPE_JSON))
|
|
((char *)r_param->buffer)[copylen] = 0;
|
|
*r_param->error = copylen > r_param->buffer_length;
|
|
} else {
|
|
convert_froma_string(r_param, (char *)*row, length);
|
|
}
|
|
(*row) += length;
|
|
}
|
|
|
|
/* {{{ ps_fetch_result_skip_direct */
|
|
static
|
|
void ps_fetch_result_skip_direct(MYSQL_BIND *r_param,
|
|
const MYSQL_FIELD *field,
|
|
unsigned char **row)
|
|
{
|
|
if (NULL != r_param) {
|
|
(*r_param->skip_result)(r_param, (MYSQL_FIELD *)field, row);
|
|
*r_param->error= 0;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ _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_CURSOR].func = ps_fetch_int32; /* parse cursor it with type of INT32 */
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_CURSOR].pack_len = 4;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_CURSOR].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 = 17;
|
|
|
|
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_OB_TIMESTAMP_NANO].func = ps_fetch_oracle_timestamp;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_NANO].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_NANO].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE].func = ps_fetch_oracle_timestamp;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_WITH_LOCAL_TIME_ZONE].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE].func = ps_fetch_oracle_timestamp;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_TIMESTAMP_WITH_TIME_ZONE].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_INTERVAL_YM].func = ps_fetch_oracle_interval;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_INTERVAL_YM].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_INTERVAL_YM].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_INTERVAL_DS].func = ps_fetch_oracle_interval;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_INTERVAL_DS].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_INTERVAL_DS].max_len = -1;
|
|
|
|
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_mysql_lob;
|
|
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_mysql_lob;
|
|
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_mysql_lob;
|
|
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_mysql_lob;
|
|
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_JSON].func = ps_fetch_string;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_JSON].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_JSON].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_OB_NUMBER_FLOAT].func = ps_fetch_string;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NUMBER_FLOAT].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NUMBER_FLOAT].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NVARCHAR2].func = ps_fetch_string;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NVARCHAR2].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NVARCHAR2].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NCHAR].func = ps_fetch_string;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NCHAR].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_NCHAR].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_OB_UROWID].func = ps_fetch_string;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_UROWID].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_UROWID].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_ORA_BLOB].func = fetch_result_ob_lob;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_ORA_BLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_ORA_BLOB].max_len = -1;
|
|
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_ORA_CLOB].func = fetch_result_ob_lob;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_ORA_CLOB].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_ORA_CLOB].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_RAW].func = ps_fetch_oracle_raw;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_RAW].pack_len = MYSQL_PS_SKIP_RESULT_W_LEN;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OB_RAW].max_len = -1;
|
|
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OBJECT].func = ps_fetch_result_type;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OBJECT].pack_len = MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MYSQL_TYPE_OBJECT].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;
|
|
|
|
//It will be used in returning into
|
|
mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].func = ps_fetch_result_skip_direct;
|
|
mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].pack_len= MYSQL_PS_SKIP_RESULT_STR;
|
|
mysql_ps_fetch_functions[MAX_NO_FIELD_TYPES].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
|
|
*/
|
|
|