Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 5245 lines (4680 sloc) 144 KB
/*
* DBD::mysql - DBI driver for the mysql database
*
* Copyright (c) 2005-2009 Patrick Galbraith
* Copyright (c) 2003-2005 Rudolf Lippan
* Copyright (c) 1997-2003 Jochen Wiedmann
*
* You may distribute this under the terms of either the GNU General Public
* License or the Artistic License, as specified in the Perl README file.
*
* $Id: dbdimp.c 12860 2009-06-19 01:52:29Z capttofu $
*/
#ifdef WIN32
#include "windows.h"
#include "winsock.h"
#endif
#include "dbdimp.h"
#if defined(WIN32) && defined(WORD)
#undef WORD
typedef short WORD;
#endif
#if MYSQL_ASYNC
# include <poll.h>
# define ASYNC_CHECK_RETURN(h, value)\
if(imp_dbh->async_query_in_flight) {\
do_error(h, 2000, "Calling a synchronous function on an asynchronous handle", "HY000");\
return (value);\
}
#else
# define ASYNC_CHECK_RETURN(h, value)
#endif
static int parse_number(char *string, STRLEN len, char **end);
DBISTATE_DECLARE;
typedef struct sql_type_info_s
{
const char *type_name;
int data_type;
int column_size;
const char *literal_prefix;
const char *literal_suffix;
const char *create_params;
int nullable;
int case_sensitive;
int searchable;
int unsigned_attribute;
int fixed_prec_scale;
int auto_unique_value;
const char *local_type_name;
int minimum_scale;
int maximum_scale;
int num_prec_radix;
int sql_datatype;
int sql_datetime_sub;
int interval_precision;
int native_type;
int is_num;
} sql_type_info_t;
/*
This function manually counts the number of placeholders in an SQL statement,
used for emulated prepare statements < 4.1.3
*/
static int
count_params(imp_xxh_t *imp_xxh, pTHX_ char *statement, bool bind_comment_placeholders)
{
bool comment_end= false;
char* ptr= statement;
int num_params= 0;
int comment_length= 0;
char c;
if (DBIc_DBISTATE(imp_xxh)->debug >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), ">count_params statement %s\n", statement);
while ( (c = *ptr++) )
{
switch (c) {
/* so, this is a -- comment, so let's burn up characters */
case '-':
{
if (bind_comment_placeholders)
{
c = *ptr++;
break;
}
else
{
comment_length= 1;
/* let's see if the next one is a dash */
c = *ptr++;
if (c == '-') {
/* if two dashes, ignore everything until newline */
while ((c = *ptr))
{
if (DBIc_DBISTATE(imp_xxh)->debug >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "%c\n", c);
ptr++;
comment_length++;
if (c == '\n')
{
comment_end= true;
break;
}
}
/*
if not comment_end, the comment never ended and we need to iterate
back to the beginning of where we started and let the database
handle whatever is in the statement
*/
if (! comment_end)
ptr-= comment_length;
}
/* otherwise, only one dash/hyphen, backtrack by one */
else
ptr--;
break;
}
}
/* c-type comments */
case '/':
{
if (bind_comment_placeholders)
{
c = *ptr++;
break;
}
else
{
c = *ptr++;
/* let's check if the next one is an asterisk */
if (c == '*')
{
comment_length= 0;
comment_end= false;
/* ignore everything until closing comment */
while ((c= *ptr))
{
ptr++;
comment_length++;
if (c == '*')
{
c = *ptr++;
/* alas, end of comment */
if (c == '/')
{
comment_end= true;
break;
}
/*
nope, just an asterisk, not so fast, not
end of comment, go back one
*/
else
ptr--;
}
}
/*
if the end of the comment was never found, we have
to backtrack to whereever we first started skipping
over the possible comment.
This means we will pass the statement to the database
to see its own fate and issue the error
*/
if (!comment_end)
ptr -= comment_length;
}
else
ptr--;
break;
}
}
case '`':
case '"':
case '\'':
/* Skip string */
{
char end_token = c;
while ((c = *ptr) && c != end_token)
{
if (c == '\\')
if (! *(++ptr))
continue;
++ptr;
}
if (c)
++ptr;
break;
}
case '?':
++num_params;
break;
default:
break;
}
}
return num_params;
}
/*
allocate memory in statement handle per number of placeholders
*/
static imp_sth_ph_t *alloc_param(int num_params)
{
imp_sth_ph_t *params;
if (num_params)
Newz(908, params, (unsigned int) num_params, imp_sth_ph_t);
else
params= NULL;
return params;
}
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
/*
allocate memory in MYSQL_BIND bind structure per
number of placeholders
*/
static MYSQL_BIND *alloc_bind(int num_params)
{
MYSQL_BIND *bind;
if (num_params)
Newz(908, bind, (unsigned int) num_params, MYSQL_BIND);
else
bind= NULL;
return bind;
}
/*
allocate memory in fbind imp_sth_phb_t structure per
number of placeholders
*/
static imp_sth_phb_t *alloc_fbind(int num_params)
{
imp_sth_phb_t *fbind;
if (num_params)
Newz(908, fbind, (unsigned int) num_params, imp_sth_phb_t);
else
fbind= NULL;
return fbind;
}
/*
alloc memory for imp_sth_fbh_t fbuffer per number of fields
*/
static imp_sth_fbh_t *alloc_fbuffer(int num_fields)
{
imp_sth_fbh_t *fbh;
if (num_fields)
Newz(908, fbh, (unsigned int) num_fields, imp_sth_fbh_t);
else
fbh= NULL;
return fbh;
}
/*
free MYSQL_BIND bind struct
*/
static void free_bind(MYSQL_BIND *bind)
{
if (bind)
Safefree(bind);
}
/*
free imp_sth_phb_t fbind structure
*/
static void free_fbind(imp_sth_phb_t *fbind)
{
if (fbind)
Safefree(fbind);
}
/*
free imp_sth_fbh_t fbh structure
*/
static void free_fbuffer(imp_sth_fbh_t *fbh)
{
if (fbh)
Safefree(fbh);
}
#endif
/*
free statement param structure per num_params
*/
static void
free_param(pTHX_ imp_sth_ph_t *params, int num_params)
{
if (params)
{
int i;
for (i= 0; i < num_params; i++)
{
imp_sth_ph_t *ph= params+i;
if (ph->value)
{
(void) SvREFCNT_dec(ph->value);
ph->value= NULL;
}
}
Safefree(params);
}
}
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
/*
Convert a MySQL type to a type that perl can handle
NOTE: In the future we may want to return a struct with a lot of
information for each type
*/
static enum enum_field_types mysql_to_perl_type(enum enum_field_types type)
{
static enum enum_field_types enum_type;
switch (type) {
case MYSQL_TYPE_DOUBLE:
case MYSQL_TYPE_FLOAT:
enum_type= MYSQL_TYPE_DOUBLE;
break;
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_YEAR:
#if MYSQL_VERSION_ID > NEW_DATATYPE_VERSION
case MYSQL_TYPE_BIT:
#endif
enum_type= MYSQL_TYPE_LONG;
break;
#if MYSQL_VERSION_ID > NEW_DATATYPE_VERSION
case MYSQL_TYPE_NEWDECIMAL:
#endif
case MYSQL_TYPE_DECIMAL:
enum_type= MYSQL_TYPE_DECIMAL;
break;
case MYSQL_TYPE_LONGLONG: /* No longlong in perl */
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_VAR_STRING:
#if MYSQL_VERSION_ID > NEW_DATATYPE_VERSION
case MYSQL_TYPE_VARCHAR:
#endif
case MYSQL_TYPE_STRING:
enum_type= MYSQL_TYPE_STRING;
break;
#if MYSQL_VERSION_ID > GEO_DATATYPE_VERSION
case MYSQL_TYPE_GEOMETRY:
#endif
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_TINY_BLOB:
enum_type= MYSQL_TYPE_BLOB;
break;
default:
enum_type= MYSQL_TYPE_STRING; /* MySQL can handle all types as strings */
}
return(enum_type);
}
#endif
#if defined(DBD_MYSQL_EMBEDDED)
/*
count embedded options
*/
int count_embedded_options(char *st)
{
int rc;
char c;
char *ptr;
ptr= st;
rc= 0;
if (st)
{
while ((c= *ptr++))
{
if (c == ',')
rc++;
}
rc++;
}
return rc;
}
/*
Free embbedded options
*/
int free_embedded_options(char ** options_list, int options_count)
{
int i;
for (i= 0; i < options_count; i++)
{
if (options_list[i])
free(options_list[i]);
}
free(options_list);
return 1;
}
/*
Print out embbedded option settings
*/
int print_embedded_options(char ** options_list, int options_count)
{
int i;
for (i=0; i<options_count; i++)
{
if (options_list[i])
PerlIO_printf(DBILOGFP,
"Embedded server, parameter[%d]=%s\n",
i, options_list[i]);
}
return 1;
}
/*
*/
char **fill_out_embedded_options(char *options,
int options_type,
int slen, int cnt)
{
int ind, len;
char c;
char *ptr;
char **options_list= NULL;
if (!(options_list= (char **) calloc(cnt, sizeof(char *))))
{
PerlIO_printf(DBILOGFP,
"Initialize embedded server. Out of memory \n");
return NULL;
}
ptr= options;
ind= 0;
if (options_type == 0)
{
/* server_groups list NULL terminated */
options_list[cnt]= (char *) NULL;
}
if (options_type == 1)
{
/* first item in server_options list is ignored. fill it with \0 */
if (!(options_list[0]= calloc(1,sizeof(char))))
return NULL;
ind++;
}
while ((c= *ptr++))
{
slen--;
if (c == ',' || !slen)
{
len= ptr - options;
if (c == ',')
len--;
if (!(options_list[ind]=calloc(len+1,sizeof(char))))
return NULL;
strncpy(options_list[ind], options, len);
ind++;
options= ptr;
}
}
return options_list;
}
#endif
/*
constructs an SQL statement previously prepared with
actual values replacing placeholders
*/
static char *parse_params(
imp_xxh_t *imp_xxh,
pTHX_ MYSQL *sock,
char *statement,
STRLEN *slen_ptr,
imp_sth_ph_t* params,
int num_params,
bool bind_type_guessing,
bool bind_comment_placeholders)
{
bool comment_end= false;
char *salloc, *statement_ptr;
char *statement_ptr_end, *ptr, *valbuf;
char *cp, *end;
int alen, i;
int slen= *slen_ptr;
int limit_flag= 0;
int comment_length=0;
STRLEN vallen;
imp_sth_ph_t *ph;
if (DBIc_DBISTATE(imp_xxh)->debug >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), ">parse_params statement %s\n", statement);
if (num_params == 0)
return NULL;
while (isspace(*statement))
{
++statement;
--slen;
}
/* Calculate the number of bytes being allocated for the statement */
alen= slen;
for (i= 0, ph= params; i < num_params; i++, ph++)
{
int defined= 0;
if (ph->value)
{
if (SvMAGICAL(ph->value))
mg_get(ph->value);
if (SvOK(ph->value))
defined=1;
}
if (!defined)
alen+= 3; /* Erase '?', insert 'NULL' */
else
{
valbuf= SvPV(ph->value, vallen);
alen+= 2+vallen+1;
/* this will most likely not happen since line 214 */
/* of mysql.xs hardcodes all types to SQL_VARCHAR */
if (!ph->type)
{
if (bind_type_guessing)
{
valbuf= SvPV(ph->value, vallen);
ph->type= SQL_INTEGER;
if (parse_number(valbuf, vallen, &end) != 0)
{
ph->type= SQL_VARCHAR;
}
}
else
ph->type= SQL_VARCHAR;
}
}
}
/* Allocate memory, why *2, well, because we have ptr and statement_ptr */
New(908, salloc, alen*2, char);
ptr= salloc;
i= 0;
/* Now create the statement string; compare count_params above */
statement_ptr_end= (statement_ptr= statement)+ slen;
while (statement_ptr < statement_ptr_end)
{
/* LIMIT should be the last part of the query, in most cases */
if (! limit_flag)
{
/*
it would be good to be able to handle any number of cases and orders
*/
if ((*statement_ptr == 'l' || *statement_ptr == 'L') &&
(!strncmp(statement_ptr+1, "imit ?", 6) ||
!strncmp(statement_ptr+1, "IMIT ?", 6)))
{
limit_flag = 1;
}
}
switch (*statement_ptr)
{
/* comment detection. Anything goes in a comment */
case '-':
{
if (bind_comment_placeholders)
{
*ptr++= *statement_ptr++;
break;
}
else
{
comment_length= 1;
comment_end= false;
*ptr++ = *statement_ptr++;
if (*statement_ptr == '-')
{
/* ignore everything until newline or end of string */
while (*statement_ptr)
{
comment_length++;
*ptr++ = *statement_ptr++;
if (!*statement_ptr || *statement_ptr == '\n')
{
comment_end= true;
break;
}
}
/* if not end of comment, go back to where we started, no end found */
if (! comment_end)
{
statement_ptr -= comment_length;
ptr -= comment_length;
}
}
break;
}
}
/* c-type comments */
case '/':
{
if (bind_comment_placeholders)
{
*ptr++= *statement_ptr++;
break;
}
else
{
comment_length= 1;
comment_end= false;
*ptr++ = *statement_ptr++;
if (*statement_ptr == '*')
{
/* use up characters everything until newline */
while (*statement_ptr)
{
*ptr++ = *statement_ptr++;
comment_length++;
if (!strncmp(statement_ptr, "*/", 2))
{
comment_length += 2;
comment_end= true;
break;
}
}
/* Go back to where started if comment end not found */
if (! comment_end)
{
statement_ptr -= comment_length;
ptr -= comment_length;
}
}
break;
}
}
case '`':
case '\'':
case '"':
/* Skip string */
{
char endToken = *statement_ptr++;
*ptr++ = endToken;
while (statement_ptr != statement_ptr_end &&
*statement_ptr != endToken)
{
if (*statement_ptr == '\\')
{
*ptr++ = *statement_ptr++;
if (statement_ptr == statement_ptr_end)
break;
}
*ptr++= *statement_ptr++;
}
if (statement_ptr != statement_ptr_end)
*ptr++= *statement_ptr++;
}
break;
case '?':
/* Insert parameter */
statement_ptr++;
if (i >= num_params)
{
break;
}
ph = params+ (i++);
if (!ph->value || !SvOK(ph->value))
{
*ptr++ = 'N';
*ptr++ = 'U';
*ptr++ = 'L';
*ptr++ = 'L';
}
else
{
int is_num = FALSE;
valbuf= SvPV(ph->value, vallen);
if (valbuf)
{
switch (ph->type)
{
case SQL_NUMERIC:
case SQL_DECIMAL:
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
case SQL_BIGINT:
case SQL_TINYINT:
is_num = TRUE;
break;
}
/* (note this sets *end, which we use if is_num) */
if ( parse_number(valbuf, vallen, &end) != 0 && is_num)
{
if (bind_type_guessing) {
/* .. not a number, so apparerently we guessed wrong */
is_num = 0;
ph->type = SQL_VARCHAR;
}
}
/* we're at the end of the query, so any placeholders if */
/* after a LIMIT clause will be numbers and should not be quoted */
if (limit_flag == 1)
is_num = TRUE;
if (!is_num)
{
*ptr++ = '\'';
ptr += mysql_real_escape_string(sock, ptr, valbuf, vallen);
*ptr++ = '\'';
}
else
{
for (cp= valbuf; cp < end; cp++)
*ptr++= *cp;
}
}
}
break;
/* in case this is a nested LIMIT */
case ')':
limit_flag = 0;
*ptr++ = *statement_ptr++;
break;
default:
*ptr++ = *statement_ptr++;
break;
}
}
*slen_ptr = ptr - salloc;
*ptr++ = '\0';
return(salloc);
}
int bind_param(imp_sth_ph_t *ph, SV *value, IV sql_type)
{
dTHX;
if (ph->value)
{
if (SvMAGICAL(ph->value))
mg_get(ph->value);
(void) SvREFCNT_dec(ph->value);
}
ph->value= newSVsv(value);
if (sql_type)
ph->type = sql_type;
return TRUE;
}
static const sql_type_info_t SQL_GET_TYPE_INFO_values[]= {
{ "varchar", SQL_VARCHAR, 255, "'", "'", "max length",
1, 0, 3, 0, 0, 0, "variable length string",
0, 0, 0,
SQL_VARCHAR, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_VAR_STRING, 0,
#else
MYSQL_TYPE_STRING, 0,
#endif
},
{ "decimal", SQL_DECIMAL, 15, NULL, NULL, "precision,scale",
1, 0, 3, 0, 0, 0, "double",
0, 6, 2,
SQL_DECIMAL, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_DECIMAL, 1
#else
MYSQL_TYPE_DECIMAL, 1
#endif
},
{ "tinyint", SQL_TINYINT, 3, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "Tiny integer",
0, 0, 10,
SQL_TINYINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_TINY, 1
#else
MYSQL_TYPE_TINY, 1
#endif
},
{ "smallint", SQL_SMALLINT, 5, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "Short integer",
0, 0, 10,
SQL_SMALLINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_SHORT, 1
#else
MYSQL_TYPE_SHORT, 1
#endif
},
{ "integer", SQL_INTEGER, 10, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "integer",
0, 0, 10,
SQL_INTEGER, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONG, 1
#else
MYSQL_TYPE_LONG, 1
#endif
},
{ "float", SQL_REAL, 7, NULL, NULL, NULL,
1, 0, 0, 0, 0, 0, "float",
0, 2, 10,
SQL_FLOAT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_FLOAT, 1
#else
MYSQL_TYPE_FLOAT, 1
#endif
},
{ "double", SQL_FLOAT, 15, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "double",
0, 4, 2,
SQL_FLOAT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_DOUBLE, 1
#else
MYSQL_TYPE_DOUBLE, 1
#endif
},
{ "double", SQL_DOUBLE, 15, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "double",
0, 4, 10,
SQL_DOUBLE, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_DOUBLE, 1
#else
MYSQL_TYPE_DOUBLE, 1
#endif
},
/*
FIELD_TYPE_NULL ?
*/
{ "timestamp", SQL_TIMESTAMP, 14, "'", "'", NULL,
0, 0, 3, 0, 0, 0, "timestamp",
0, 0, 0,
SQL_TIMESTAMP, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_TIMESTAMP, 0
#else
MYSQL_TYPE_TIMESTAMP, 0
#endif
},
{ "bigint", SQL_BIGINT, 19, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "Longlong integer",
0, 0, 10,
SQL_BIGINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONGLONG, 1
#else
MYSQL_TYPE_LONGLONG, 1
#endif
},
{ "mediumint", SQL_INTEGER, 8, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "Medium integer",
0, 0, 10,
SQL_INTEGER, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_INT24, 1
#else
MYSQL_TYPE_INT24, 1
#endif
},
{ "date", SQL_DATE, 10, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "date",
0, 0, 0,
SQL_DATE, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_DATE, 0
#else
MYSQL_TYPE_DATE, 0
#endif
},
{ "time", SQL_TIME, 6, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "time",
0, 0, 0,
SQL_TIME, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_TIME, 0
#else
MYSQL_TYPE_TIME, 0
#endif
},
{ "datetime", SQL_TIMESTAMP, 21, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "datetime",
0, 0, 0,
SQL_TIMESTAMP, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_DATETIME, 0
#else
MYSQL_TYPE_DATETIME, 0
#endif
},
{ "year", SQL_SMALLINT, 4, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "year",
0, 0, 10,
SQL_SMALLINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_YEAR, 0
#else
MYSQL_TYPE_YEAR, 0
#endif
},
{ "date", SQL_DATE, 10, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "date",
0, 0, 0,
SQL_DATE, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_NEWDATE, 0
#else
MYSQL_TYPE_NEWDATE, 0
#endif
},
{ "enum", SQL_VARCHAR, 255, "'", "'", NULL,
1, 0, 1, 0, 0, 0, "enum(value1,value2,value3...)",
0, 0, 0,
0, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_ENUM, 0
#else
MYSQL_TYPE_ENUM, 0
#endif
},
{ "set", SQL_VARCHAR, 255, "'", "'", NULL,
1, 0, 1, 0, 0, 0, "set(value1,value2,value3...)",
0, 0, 0,
0, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_SET, 0
#else
MYSQL_TYPE_SET, 0
#endif
},
{ "blob", SQL_LONGVARBINARY, 65535, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "binary large object (0-65535)",
0, 0, 0,
SQL_LONGVARBINARY, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_BLOB, 0
#else
MYSQL_TYPE_BLOB, 0
#endif
},
{ "tinyblob", SQL_VARBINARY, 255, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "binary large object (0-255) ",
0, 0, 0,
SQL_VARBINARY, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_TINY_BLOB, 0
#else
FIELD_TYPE_TINY_BLOB, 0
#endif
},
{ "mediumblob", SQL_LONGVARBINARY, 16777215, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "binary large object",
0, 0, 0,
SQL_LONGVARBINARY, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_MEDIUM_BLOB, 0
#else
MYSQL_TYPE_MEDIUM_BLOB, 0
#endif
},
{ "longblob", SQL_LONGVARBINARY, 2147483647, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "binary large object, use mediumblob instead",
0, 0, 0,
SQL_LONGVARBINARY, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONG_BLOB, 0
#else
MYSQL_TYPE_LONG_BLOB, 0
#endif
},
{ "char", SQL_CHAR, 255, "'", "'", "max length",
1, 0, 3, 0, 0, 0, "string",
0, 0, 0,
SQL_CHAR, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_STRING, 0
#else
MYSQL_TYPE_STRING, 0
#endif
},
{ "decimal", SQL_NUMERIC, 15, NULL, NULL, "precision,scale",
1, 0, 3, 0, 0, 0, "double",
0, 6, 2,
SQL_NUMERIC, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_DECIMAL, 1
#else
MYSQL_TYPE_DECIMAL, 1
#endif
},
{ "tinyint unsigned", SQL_TINYINT, 3, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "Tiny integer unsigned",
0, 0, 10,
SQL_TINYINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_TINY, 1
#else
MYSQL_TYPE_TINY, 1
#endif
},
{ "smallint unsigned", SQL_SMALLINT, 5, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "Short integer unsigned",
0, 0, 10,
SQL_SMALLINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_SHORT, 1
#else
MYSQL_TYPE_SHORT, 1
#endif
},
{ "mediumint unsigned", SQL_INTEGER, 8, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "Medium integer unsigned",
0, 0, 10,
SQL_INTEGER, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_INT24, 1
#else
MYSQL_TYPE_INT24, 1
#endif
},
{ "int unsigned", SQL_INTEGER, 10, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "integer unsigned",
0, 0, 10,
SQL_INTEGER, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONG, 1
#else
MYSQL_TYPE_LONG, 1
#endif
},
{ "int", SQL_INTEGER, 10, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "integer",
0, 0, 10,
SQL_INTEGER, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONG, 1
#else
MYSQL_TYPE_LONG, 1
#endif
},
{ "integer unsigned", SQL_INTEGER, 10, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "integer",
0, 0, 10,
SQL_INTEGER, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONG, 1
#else
MYSQL_TYPE_LONG, 1
#endif
},
{ "bigint unsigned", SQL_BIGINT, 20, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "Longlong integer unsigned",
0, 0, 10,
SQL_BIGINT, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_LONGLONG, 1
#else
MYSQL_TYPE_LONGLONG, 1
#endif
},
{ "text", SQL_LONGVARCHAR, 65535, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "large text object (0-65535)",
0, 0, 0,
SQL_LONGVARCHAR, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_BLOB, 0
#else
MYSQL_TYPE_BLOB, 0
#endif
},
{ "mediumtext", SQL_LONGVARCHAR, 16777215, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "large text object",
0, 0, 0,
SQL_LONGVARCHAR, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
FIELD_TYPE_MEDIUM_BLOB, 0
#else
MYSQL_TYPE_MEDIUM_BLOB, 0
#endif
},
{ "mediumint unsigned auto_increment", SQL_INTEGER, 8, NULL, NULL, NULL,
0, 0, 3, 1, 0, 1, "Medium integer unsigned auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_INT24, 1,
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_INT24, 1,
#endif
},
{ "tinyint unsigned auto_increment", SQL_TINYINT, 3, NULL, NULL, NULL,
0, 0, 3, 1, 0, 1, "tinyint unsigned auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_TINYINT, 0, 0, FIELD_TYPE_TINY, 1
#else
SQL_TINYINT, 0, 0, MYSQL_TYPE_TINY, 1
#endif
},
{ "smallint auto_increment", SQL_SMALLINT, 5, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "smallint auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_SMALLINT, 0, 0, FIELD_TYPE_SHORT, 1
#else
SQL_SMALLINT, 0, 0, MYSQL_TYPE_SHORT, 1
#endif
},
{ "int unsigned auto_increment", SQL_INTEGER, 10, NULL, NULL, NULL,
0, 0, 3, 1, 0, 1, "integer unsigned auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_LONG, 1
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_LONG, 1
#endif
},
{ "mediumint", SQL_INTEGER, 7, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "Medium integer", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_INT24, 1
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_INT24, 1
#endif
},
{ "bit", SQL_BIT, 1, NULL, NULL, NULL,
1, 0, 3, 0, 0, 0, "char(1)", 0, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_BIT, 0, 0, FIELD_TYPE_TINY, 0
#else
SQL_BIT, 0, 0, MYSQL_TYPE_TINY, 0
#endif
},
{ "numeric", SQL_NUMERIC, 19, NULL, NULL, "precision,scale",
1, 0, 3, 0, 0, 0, "numeric", 0, 19, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_NUMERIC, 0, 0, FIELD_TYPE_DECIMAL, 1,
#else
SQL_NUMERIC, 0, 0, MYSQL_TYPE_DECIMAL, 1,
#endif
},
{ "integer unsigned auto_increment", SQL_INTEGER, 10, NULL, NULL, NULL,
0, 0, 3, 1, 0, 1, "integer unsigned auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_LONG, 1,
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_LONG, 1,
#endif
},
{ "mediumint unsigned", SQL_INTEGER, 8, NULL, NULL, NULL,
1, 0, 3, 1, 0, 0, "Medium integer unsigned", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_INT24, 1
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_INT24, 1
#endif
},
{ "smallint unsigned auto_increment", SQL_SMALLINT, 5, NULL, NULL, NULL,
0, 0, 3, 1, 0, 1, "smallint unsigned auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_SMALLINT, 0, 0, FIELD_TYPE_SHORT, 1
#else
SQL_SMALLINT, 0, 0, MYSQL_TYPE_SHORT, 1
#endif
},
{ "int auto_increment", SQL_INTEGER, 10, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "integer auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_LONG, 1
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_LONG, 1
#endif
},
{ "long varbinary", SQL_LONGVARBINARY, 16777215, "0x", NULL, NULL,
1, 0, 3, 0, 0, 0, "mediumblob", 0, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_LONGVARBINARY, 0, 0, FIELD_TYPE_LONG_BLOB, 0
#else
SQL_LONGVARBINARY, 0, 0, MYSQL_TYPE_LONG_BLOB, 0
#endif
},
{ "double auto_increment", SQL_FLOAT, 15, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "double auto_increment", 0, 4, 2,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_FLOAT, 0, 0, FIELD_TYPE_DOUBLE, 1
#else
SQL_FLOAT, 0, 0, MYSQL_TYPE_DOUBLE, 1
#endif
},
{ "double auto_increment", SQL_DOUBLE, 15, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "double auto_increment", 0, 4, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_DOUBLE, 0, 0, FIELD_TYPE_DOUBLE, 1
#else
SQL_DOUBLE, 0, 0, MYSQL_TYPE_DOUBLE, 1
#endif
},
{ "integer auto_increment", SQL_INTEGER, 10, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "integer auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_LONG, 1,
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_LONG, 1,
#endif
},
{ "bigint auto_increment", SQL_BIGINT, 19, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "bigint auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_BIGINT, 0, 0, FIELD_TYPE_LONGLONG, 1
#else
SQL_BIGINT, 0, 0, MYSQL_TYPE_LONGLONG, 1
#endif
},
{ "bit auto_increment", SQL_BIT, 1, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "char(1) auto_increment", 0, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_BIT, 0, 0, FIELD_TYPE_TINY, 1
#else
SQL_BIT, 0, 0, MYSQL_TYPE_TINY, 1
#endif
},
{ "mediumint auto_increment", SQL_INTEGER, 7, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "Medium integer auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_INTEGER, 0, 0, FIELD_TYPE_INT24, 1
#else
SQL_INTEGER, 0, 0, MYSQL_TYPE_INT24, 1
#endif
},
{ "float auto_increment", SQL_REAL, 7, NULL, NULL, NULL,
0, 0, 0, 0, 0, 1, "float auto_increment", 0, 2, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_FLOAT, 0, 0, FIELD_TYPE_FLOAT, 1
#else
SQL_FLOAT, 0, 0, MYSQL_TYPE_FLOAT, 1
#endif
},
{ "long varchar", SQL_LONGVARCHAR, 16777215, "'", "'", NULL,
1, 0, 3, 0, 0, 0, "mediumtext", 0, 0, 0,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_LONGVARCHAR, 0, 0, FIELD_TYPE_MEDIUM_BLOB, 1
#else
SQL_LONGVARCHAR, 0, 0, MYSQL_TYPE_MEDIUM_BLOB, 1
#endif
},
{ "tinyint auto_increment", SQL_TINYINT, 3, NULL, NULL, NULL,
0, 0, 3, 0, 0, 1, "tinyint auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_TINYINT, 0, 0, FIELD_TYPE_TINY, 1
#else
SQL_TINYINT, 0, 0, MYSQL_TYPE_TINY, 1
#endif
},
{ "bigint unsigned auto_increment", SQL_BIGINT, 20, NULL, NULL, NULL,
0, 0, 3, 1, 0, 1, "bigint unsigned auto_increment", 0, 0, 10,
#if MYSQL_VERSION_ID < MYSQL_VERSION_5_0
SQL_BIGINT, 0, 0, FIELD_TYPE_LONGLONG, 1
#else
SQL_BIGINT, 0, 0, MYSQL_TYPE_LONGLONG, 1
#endif
},
/* END MORE STUFF */
};
/*
static const sql_type_info_t* native2sql (int t)
*/
static const sql_type_info_t *native2sql(int t)
{
switch (t) {
case FIELD_TYPE_VAR_STRING: return &SQL_GET_TYPE_INFO_values[0];
case FIELD_TYPE_DECIMAL: return &SQL_GET_TYPE_INFO_values[1];
#ifdef FIELD_TYPE_NEWDECIMAL
case FIELD_TYPE_NEWDECIMAL: return &SQL_GET_TYPE_INFO_values[1];
#endif
case FIELD_TYPE_TINY: return &SQL_GET_TYPE_INFO_values[2];
case FIELD_TYPE_SHORT: return &SQL_GET_TYPE_INFO_values[3];
case FIELD_TYPE_LONG: return &SQL_GET_TYPE_INFO_values[4];
case FIELD_TYPE_FLOAT: return &SQL_GET_TYPE_INFO_values[5];
/* 6 */
case FIELD_TYPE_DOUBLE: return &SQL_GET_TYPE_INFO_values[7];
case FIELD_TYPE_TIMESTAMP: return &SQL_GET_TYPE_INFO_values[8];
case FIELD_TYPE_LONGLONG: return &SQL_GET_TYPE_INFO_values[9];
case FIELD_TYPE_INT24: return &SQL_GET_TYPE_INFO_values[10];
case FIELD_TYPE_DATE: return &SQL_GET_TYPE_INFO_values[11];
case FIELD_TYPE_TIME: return &SQL_GET_TYPE_INFO_values[12];
case FIELD_TYPE_DATETIME: return &SQL_GET_TYPE_INFO_values[13];
case FIELD_TYPE_YEAR: return &SQL_GET_TYPE_INFO_values[14];
case FIELD_TYPE_NEWDATE: return &SQL_GET_TYPE_INFO_values[15];
case FIELD_TYPE_ENUM: return &SQL_GET_TYPE_INFO_values[16];
case FIELD_TYPE_SET: return &SQL_GET_TYPE_INFO_values[17];
case FIELD_TYPE_BLOB: return &SQL_GET_TYPE_INFO_values[18];
case FIELD_TYPE_TINY_BLOB: return &SQL_GET_TYPE_INFO_values[19];
case FIELD_TYPE_MEDIUM_BLOB: return &SQL_GET_TYPE_INFO_values[20];
case FIELD_TYPE_LONG_BLOB: return &SQL_GET_TYPE_INFO_values[21];
case FIELD_TYPE_STRING: return &SQL_GET_TYPE_INFO_values[22];
default: return &SQL_GET_TYPE_INFO_values[0];
}
}
#define SQL_GET_TYPE_INFO_num \
(sizeof(SQL_GET_TYPE_INFO_values)/sizeof(sql_type_info_t))
/***************************************************************************
*
* Name: dbd_init
*
* Purpose: Called when the driver is installed by DBI
*
* Input: dbistate - pointer to the DBI state variable, used for some
* DBI internal things
*
* Returns: Nothing
*
**************************************************************************/
void dbd_init(dbistate_t* dbistate)
{
dTHX;
DBISTATE_INIT;
}
/**************************************************************************
*
* Name: do_error, do_warn
*
* Purpose: Called to associate an error code and an error message
* to some handle
*
* Input: h - the handle in error condition
* rc - the error code
* what - the error message
*
* Returns: Nothing
*
**************************************************************************/
void do_error(SV* h, int rc, const char* what, const char* sqlstate)
{
dTHX;
D_imp_xxh(h);
STRLEN lna;
SV *errstr;
SV *errstate;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\t--> do_error\n");
errstr= DBIc_ERRSTR(imp_xxh);
sv_setiv(DBIc_ERR(imp_xxh), (IV)rc); /* set err early */
sv_setpv(errstr, what);
#if MYSQL_VERSION_ID >= SQL_STATE_VERSION
if (sqlstate)
{
errstate= DBIc_STATE(imp_xxh);
sv_setpvn(errstate, sqlstate, 5);
}
#endif
/* NO EFFECT DBIh_EVENT2(h, ERROR_event, DBIc_ERR(imp_xxh), errstr); */
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "%s error %d recorded: %s\n",
what, rc, SvPV(errstr,lna));
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\t<-- do_error\n");
}
/*
void do_warn(SV* h, int rc, char* what)
*/
void do_warn(SV* h, int rc, char* what)
{
dTHX;
D_imp_xxh(h);
STRLEN lna;
SV *errstr = DBIc_ERRSTR(imp_xxh);
sv_setiv(DBIc_ERR(imp_xxh), (IV)rc); /* set err early */
sv_setpv(errstr, what);
/* NO EFFECT DBIh_EVENT2(h, WARN_event, DBIc_ERR(imp_xxh), errstr);*/
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "%s warning %d recorded: %s\n",
what, rc, SvPV(errstr,lna));
warn("%s", what);
}
#if defined(DBD_MYSQL_EMBEDDED)
#define DBD_MYSQL_NAMESPACE "DBD::mysqlEmb::QUIET";
#else
#define DBD_MYSQL_NAMESPACE "DBD::mysql::QUIET";
#endif
#define doquietwarn(s) \
{ \
SV* sv = perl_get_sv(DBD_MYSQL_NAMESPACE, FALSE); \
if (!sv || !SvTRUE(sv)) { \
warn s; \
} \
}
/***************************************************************************
*
* Name: mysql_dr_connect
*
* Purpose: Replacement for mysql_connect
*
* Input: MYSQL* sock - Pointer to a MYSQL structure being
* initialized
* char* mysql_socket - Name of a UNIX socket being used
* or NULL
* char* host - Host name being used or NULL for localhost
* char* port - Port number being used or NULL for default
* char* user - User name being used or NULL
* char* password - Password being used or NULL
* char* dbname - Database name being used or NULL
* char* imp_dbh - Pointer to internal dbh structure
*
* Returns: The sock argument for success, NULL otherwise;
* you have to call do_error in the latter case.
*
**************************************************************************/
MYSQL *mysql_dr_connect(
SV* dbh,
MYSQL* sock,
char* mysql_socket,
char* host,
char* port,
char* user,
char* password,
char* dbname,
imp_dbh_t *imp_dbh)
{
int portNr;
unsigned int client_flag;
MYSQL* result;
dTHX;
D_imp_xxh(dbh);
/* per Monty, already in client.c in API */
/* but still not exist in libmysqld.c */
#if defined(DBD_MYSQL_EMBEDDED)
if (host && !*host) host = NULL;
#endif
portNr= (port && *port) ? atoi(port) : 0;
/* already in client.c in API */
/* if (user && !*user) user = NULL; */
/* if (password && !*password) password = NULL; */
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: host = |%s|, port = %d," \
" uid = %s, pwd = %s\n",
host ? host : "NULL", portNr,
user ? user : "NULL",
password ? password : "NULL");
{
#if defined(DBD_MYSQL_EMBEDDED)
if (imp_dbh)
{
D_imp_drh_from_dbh;
SV* sv = DBIc_IMP_DATA(imp_dbh);
if (sv && SvROK(sv))
{
SV** svp;
STRLEN lna;
char * options;
int server_args_cnt= 0;
int server_groups_cnt= 0;
int rc= 0;
char ** server_args = NULL;
char ** server_groups = NULL;
HV* hv = (HV*) SvRV(sv);
if (SvTYPE(hv) != SVt_PVHV)
return NULL;
if (!imp_drh->embedded.state)
{
/* Init embedded server */
if ((svp = hv_fetch(hv, "mysql_embedded_groups", 21, FALSE)) &&
*svp && SvTRUE(*svp))
{
options = SvPV(*svp, lna);
imp_drh->embedded.groups=newSVsv(*svp);
if ((server_groups_cnt=count_embedded_options(options)))
{
/* number of server_groups always server_groups+1 */
server_groups=fill_out_embedded_options(options, 0,
(int)lna, ++server_groups_cnt);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
{
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"Groups names passed to embedded server:\n");
print_embedded_options(DBIc_LOGPIO(imp_xxh), server_groups, server_groups_cnt);
}
}
}
if ((svp = hv_fetch(hv, "mysql_embedded_options", 22, FALSE)) &&
*svp && SvTRUE(*svp))
{
options = SvPV(*svp, lna);
imp_drh->embedded.args=newSVsv(*svp);
if ((server_args_cnt=count_embedded_options(options)))
{
/* number of server_options always server_options+1 */
server_args=fill_out_embedded_options(options, 1, (int)lna, ++server_args_cnt);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
{
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "Server options passed to embedded server:\n");
print_embedded_options(DBIc_LOGPIO(imp_xxh), server_args, server_args_cnt);
}
}
}
if (mysql_server_init(server_args_cnt, server_args, server_groups))
{
do_warn(dbh, AS_ERR_EMBEDDED, "Embedded server was not started. \
Could not initialize environment.");
return NULL;
}
imp_drh->embedded.state=1;
if (server_args_cnt)
free_embedded_options(server_args, server_args_cnt);
if (server_groups_cnt)
free_embedded_options(server_groups, server_groups_cnt);
}
else
{
/*
* Check if embedded parameters passed to connect() differ from
* first ones
*/
if ( ((svp = hv_fetch(hv, "mysql_embedded_groups", 21, FALSE)) &&
*svp && SvTRUE(*svp)))
rc =+ abs(sv_cmp(*svp, imp_drh->embedded.groups));
if ( ((svp = hv_fetch(hv, "mysql_embedded_options", 22, FALSE)) &&
*svp && SvTRUE(*svp)) )
rc =+ abs(sv_cmp(*svp, imp_drh->embedded.args));
if (rc)
{
do_warn(dbh, AS_ERR_EMBEDDED,
"Embedded server was already started. You cannot pass init\
parameters to embedded server once");
return NULL;
}
}
}
}
#endif
#ifdef MYSQL_NO_CLIENT_FOUND_ROWS
client_flag = 0;
#else
client_flag = CLIENT_FOUND_ROWS;
#endif
mysql_init(sock);
if (imp_dbh)
{
SV* sv = DBIc_IMP_DATA(imp_dbh);
DBIc_set(imp_dbh, DBIcf_AutoCommit, TRUE);
if (sv && SvROK(sv))
{
HV* hv = (HV*) SvRV(sv);
SV** svp;
STRLEN lna;
/* thanks to Peter John Edwards for mysql_init_command */
if ((svp = hv_fetch(hv, "mysql_init_command", 18, FALSE)) &&
*svp && SvTRUE(*svp))
{
char* df = SvPV(*svp, lna);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: Setting" \
" init command (%s).\n", df);
mysql_options(sock, MYSQL_INIT_COMMAND, df);
}
if ((svp = hv_fetch(hv, "mysql_compression", 17, FALSE)) &&
*svp && SvTRUE(*svp))
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: Enabling" \
" compression.\n");
mysql_options(sock, MYSQL_OPT_COMPRESS, NULL);
}
if ((svp = hv_fetch(hv, "mysql_connect_timeout", 21, FALSE))
&& *svp && SvTRUE(*svp))
{
int to = SvIV(*svp);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: Setting" \
" connect timeout (%d).\n",to);
mysql_options(sock, MYSQL_OPT_CONNECT_TIMEOUT,
(const char *)&to);
}
if ((svp = hv_fetch(hv, "mysql_read_default_file", 23, FALSE)) &&
*svp && SvTRUE(*svp))
{
char* df = SvPV(*svp, lna);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: Reading" \
" default file %s.\n", df);
mysql_options(sock, MYSQL_READ_DEFAULT_FILE, df);
}
if ((svp = hv_fetch(hv, "mysql_read_default_group", 24,
FALSE)) &&
*svp && SvTRUE(*svp)) {
char* gr = SvPV(*svp, lna);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: Using" \
" default group %s.\n", gr);
mysql_options(sock, MYSQL_READ_DEFAULT_GROUP, gr);
}
if ((svp = hv_fetch(hv, "mysql_client_found_rows", 23, FALSE)) && *svp)
{
if (SvTRUE(*svp))
client_flag |= CLIENT_FOUND_ROWS;
else
client_flag &= ~CLIENT_FOUND_ROWS;
}
if ((svp = hv_fetch(hv, "mysql_use_result", 16, FALSE)) && *svp)
{
imp_dbh->use_mysql_use_result = SvTRUE(*svp);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->use_mysql_use_result: %d\n",
imp_dbh->use_mysql_use_result);
}
if ((svp = hv_fetch(hv, "mysql_bind_type_guessing", 24, TRUE)) && *svp)
{
imp_dbh->bind_type_guessing= SvTRUE(*svp);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->bind_type_guessing: %d\n",
imp_dbh->bind_type_guessing);
}
if ((svp = hv_fetch(hv, "mysql_bind_comment_placeholders", 31, FALSE)) && *svp)
{
imp_dbh->bind_comment_placeholders = SvTRUE(*svp);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->bind_comment_placeholders: %d\n",
imp_dbh->bind_comment_placeholders);
}
if ((svp = hv_fetch(hv, "mysql_no_autocommit_cmd", 23, FALSE)) && *svp)
{
imp_dbh->no_autocommit_cmd= SvTRUE(*svp);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->no_autocommit_cmd: %d\n",
imp_dbh->no_autocommit_cmd);
}
#if defined(CLIENT_MULTI_STATEMENTS)
if ((svp = hv_fetch(hv, "mysql_multi_statements", 22, FALSE)) && *svp)
{
if (SvTRUE(*svp))
client_flag |= CLIENT_MULTI_STATEMENTS;
else
client_flag &= ~CLIENT_MULTI_STATEMENTS;
}
#endif
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
/* took out client_flag |= CLIENT_PROTOCOL_41; */
/* because libmysql.c already sets this no matter what */
if ((svp = hv_fetch(hv, "mysql_server_prepare", 20, FALSE))
&& *svp)
{
if (SvTRUE(*svp))
{
client_flag |= CLIENT_PROTOCOL_41;
imp_dbh->use_server_side_prepare = TRUE;
}
else
{
client_flag &= ~CLIENT_PROTOCOL_41;
imp_dbh->use_server_side_prepare = FALSE;
}
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->use_server_side_prepare: %d\n",
imp_dbh->use_server_side_prepare);
#endif
/* HELMUT */
#if defined(sv_utf8_decode) && MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
if ((svp = hv_fetch(hv, "mysql_enable_utf8", 17, FALSE)) && *svp) {
/* Do not touch imp_dbh->enable_utf8 as we are called earlier
* than it is set and mysql_options() must be before:
* mysql_real_connect()
*/
mysql_options(sock, MYSQL_SET_CHARSET_NAME,
(SvTRUE(*svp) ? "utf8" : "latin1"));
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"mysql_options: MYSQL_SET_CHARSET_NAME=%s\n",
(SvTRUE(*svp) ? "utf8" : "latin1"));
}
#endif
#if defined(DBD_MYSQL_WITH_SSL) && !defined(DBD_MYSQL_EMBEDDED) && \
(defined(CLIENT_SSL) || (MYSQL_VERSION_ID >= 40000))
if ((svp = hv_fetch(hv, "mysql_ssl", 9, FALSE)) && *svp)
{
if (SvTRUE(*svp))
{
char *client_key = NULL;
char *client_cert = NULL;
char *ca_file = NULL;
char *ca_path = NULL;
char *cipher = NULL;
STRLEN lna;
#if MYSQL_VERSION_ID >= SSL_VERIFY_VERSION
/*
New code to utilise MySQLs new feature that verifies that the
server's hostname that the client connects to matches that of
the certificate
*/
my_bool ssl_verify_true = 0;
if ((svp = hv_fetch(hv, "mysql_ssl_verify_server_cert", 28, FALSE)) && *svp)
ssl_verify_true = SvTRUE(*svp);
#endif
if ((svp = hv_fetch(hv, "mysql_ssl_client_key", 20, FALSE)) && *svp)
client_key = SvPV(*svp, lna);
if ((svp = hv_fetch(hv, "mysql_ssl_client_cert", 21, FALSE)) &&
*svp)
client_cert = SvPV(*svp, lna);
if ((svp = hv_fetch(hv, "mysql_ssl_ca_file", 17, FALSE)) &&
*svp)
ca_file = SvPV(*svp, lna);
if ((svp = hv_fetch(hv, "mysql_ssl_ca_path", 17, FALSE)) &&
*svp)
ca_path = SvPV(*svp, lna);
if ((svp = hv_fetch(hv, "mysql_ssl_cipher", 16, FALSE)) &&
*svp)
cipher = SvPV(*svp, lna);
mysql_ssl_set(sock, client_key, client_cert, ca_file,
ca_path, cipher);
#if MYSQL_VERSION_ID >= SSL_VERIFY_VERSION
mysql_options(sock, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &ssl_verify_true);
#endif
client_flag |= CLIENT_SSL;
}
}
#endif
#if (MYSQL_VERSION_ID >= 32349)
/*
* MySQL 3.23.49 disables LOAD DATA LOCAL by default. Use
* mysql_local_infile=1 in the DSN to enable it.
*/
if ((svp = hv_fetch( hv, "mysql_local_infile", 18, FALSE)) && *svp)
{
unsigned int flag = SvTRUE(*svp);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->mysql_dr_connect: Using" \
" local infile %u.\n", flag);
mysql_options(sock, MYSQL_OPT_LOCAL_INFILE, (const char *) &flag);
}
#endif
}
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "imp_dbh->mysql_dr_connect: client_flags = %d\n",
client_flag);
#if MYSQL_VERSION_ID >= MULTIPLE_RESULT_SET_VERSION
client_flag|= CLIENT_MULTI_RESULTS;
#endif
result = mysql_real_connect(sock, host, user, password, dbname,
portNr, mysql_socket, client_flag);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "imp_dbh->mysql_dr_connect: <-");
if (result)
{
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
/* connection succeeded. */
/* imp_dbh == NULL when mysql_dr_connect() is called from mysql.xs
functions (_admin_internal(),_ListDBs()). */
if (!(result->client_flag & CLIENT_PROTOCOL_41) && imp_dbh)
imp_dbh->use_server_side_prepare = FALSE;
#endif
#if MYSQL_ASYNC
if(imp_dbh) {
imp_dbh->async_query_in_flight = NULL;
}
#endif
/*
we turn off Mysql's auto reconnect and handle re-connecting ourselves
so that we can keep track of when this happens.
*/
result->reconnect=0;
}
return result;
}
}
/*
safe_hv_fetch
*/
static char *safe_hv_fetch(pTHX_ HV *hv, const char *name, int name_length)
{
SV** svp;
STRLEN len;
char *res= NULL;
if ((svp= hv_fetch(hv, name, name_length, FALSE)))
{
res= SvPV(*svp, len);
if (!len)
res= NULL;
}
return res;
}
/*
Frontend for mysql_dr_connect
*/
static int my_login(pTHX_ SV* dbh, imp_dbh_t *imp_dbh)
{
SV* sv;
HV* hv;
char* dbname;
char* host;
char* port;
char* user;
char* password;
char* mysql_socket;
D_imp_xxh(dbh);
/* TODO- resolve this so that it is set only if DBI is 1.607 */
#define TAKE_IMP_DATA_VERSION 1
#if TAKE_IMP_DATA_VERSION
if (DBIc_has(imp_dbh, DBIcf_IMPSET))
{ /* eg from take_imp_data() */
if (DBIc_has(imp_dbh, DBIcf_ACTIVE))
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "my_login skip connect\n");
/* tell our parent we've adopted an active child */
++DBIc_ACTIVE_KIDS(DBIc_PARENT_COM(imp_dbh));
return TRUE;
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"my_login IMPSET but not ACTIVE so connect not skipped\n");
}
#endif
sv = DBIc_IMP_DATA(imp_dbh);
if (!sv || !SvROK(sv))
return FALSE;
hv = (HV*) SvRV(sv);
if (SvTYPE(hv) != SVt_PVHV)
return FALSE;
host= safe_hv_fetch(aTHX_ hv, "host", 4);
port= safe_hv_fetch(aTHX_ hv, "port", 4);
user= safe_hv_fetch(aTHX_ hv, "user", 4);
password= safe_hv_fetch(aTHX_ hv, "password", 8);
dbname= safe_hv_fetch(aTHX_ hv, "database", 8);
mysql_socket= safe_hv_fetch(aTHX_ hv, "mysql_socket", 12);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->my_login : dbname = %s, uid = %s, pwd = %s," \
"host = %s, port = %s\n",
dbname ? dbname : "NULL",
user ? user : "NULL",
password ? password : "NULL",
host ? host : "NULL",
port ? port : "NULL");
if (!imp_dbh->pmysql) {
Newz(908, imp_dbh->pmysql, 1, MYSQL);
}
return mysql_dr_connect(dbh, imp_dbh->pmysql, mysql_socket, host, port, user,
password, dbname, imp_dbh) ? TRUE : FALSE;
}
/**************************************************************************
*
* Name: dbd_db_login
*
* Purpose: Called for connecting to a database and logging in.
*
* Input: dbh - database handle being initialized
* imp_dbh - drivers private database handle data
* dbname - the database we want to log into; may be like
* "dbname:host" or "dbname:host:port"
* user - user name to connect as
* password - passwort to connect with
*
* Returns: TRUE for success, FALSE otherwise; do_error has already
* been called in the latter case
*
**************************************************************************/
int dbd_db_login(SV* dbh, imp_dbh_t* imp_dbh, char* dbname, char* user,
char* password) {
#ifdef dTHR
dTHR;
#endif
dTHX;
D_imp_xxh(dbh);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"imp_dbh->connect: dsn = %s, uid = %s, pwd = %s\n",
dbname ? dbname : "NULL",
user ? user : "NULL",
password ? password : "NULL");
imp_dbh->stats.auto_reconnects_ok= 0;
imp_dbh->stats.auto_reconnects_failed= 0;
imp_dbh->bind_type_guessing= FALSE;
imp_dbh->bind_comment_placeholders= FALSE;
imp_dbh->has_transactions= TRUE;
/* Safer we flip this to TRUE perl side if we detect a mod_perl env. */
imp_dbh->auto_reconnect = FALSE;
/* HELMUT */
#if defined(sv_utf8_decode) && MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
imp_dbh->enable_utf8 = FALSE; /* initialize mysql_enable_utf8 */
#endif
if (!my_login(aTHX_ dbh, imp_dbh))
{
do_error(dbh, mysql_errno(imp_dbh->pmysql),
mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));
return FALSE;
}
/*
* Tell DBI, that dbh->disconnect should be called for this handle
*/
DBIc_ACTIVE_on(imp_dbh);
/* Tell DBI, that dbh->destroy should be called for this handle */
DBIc_on(imp_dbh, DBIcf_IMPSET);
return TRUE;
}
/***************************************************************************
*
* Name: dbd_db_commit
* dbd_db_rollback
*
* Purpose: You guess what they should do.
*
* Input: dbh - database handle being commited or rolled back
* imp_dbh - drivers private database handle data
*
* Returns: TRUE for success, FALSE otherwise; do_error has already
* been called in the latter case
*
**************************************************************************/
int
dbd_db_commit(SV* dbh, imp_dbh_t* imp_dbh)
{
if (DBIc_has(imp_dbh, DBIcf_AutoCommit))
return FALSE;
ASYNC_CHECK_RETURN(dbh, FALSE);
if (imp_dbh->has_transactions)
{
#if MYSQL_VERSION_ID < SERVER_PREPARE_VERSION
if (mysql_real_query(imp_dbh->pmysql, "COMMIT", 6))
#else
if (mysql_commit(imp_dbh->pmysql))
#endif
{
do_error(dbh, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql)
,mysql_sqlstate(imp_dbh->pmysql));
return FALSE;
}
}
else
do_warn(dbh, JW_ERR_NOT_IMPLEMENTED,
"Commit ineffective because transactions are not available");
return TRUE;
}
/*
dbd_db_rollback
*/
int
dbd_db_rollback(SV* dbh, imp_dbh_t* imp_dbh) {
/* croak, if not in AutoCommit mode */
if (DBIc_has(imp_dbh, DBIcf_AutoCommit))
return FALSE;
ASYNC_CHECK_RETURN(dbh, FALSE);
if (imp_dbh->has_transactions)
{
#if MYSQL_VERSION_ID < SERVER_PREPARE_VERSION
if (mysql_real_query(imp_dbh->pmysql, "ROLLBACK", 8))
#else
if (mysql_rollback(imp_dbh->pmysql))
#endif
{
do_error(dbh, mysql_errno(imp_dbh->pmysql),
mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));
return FALSE;
}
}
else
do_error(dbh, JW_ERR_NOT_IMPLEMENTED,
"Rollback ineffective because transactions are not available" ,NULL);
return TRUE;
}
/*
***************************************************************************
*
* Name: dbd_db_disconnect
*
* Purpose: Disconnect a database handle from its database
*
* Input: dbh - database handle being disconnected
* imp_dbh - drivers private database handle data
*
* Returns: TRUE for success, FALSE otherwise; do_error has already
* been called in the latter case
*
**************************************************************************/
int dbd_db_disconnect(SV* dbh, imp_dbh_t* imp_dbh)
{
#ifdef dTHR
dTHR;
#endif
dTHX;
D_imp_xxh(dbh);
/* We assume that disconnect will always work */
/* since most errors imply already disconnected. */
DBIc_ACTIVE_off(imp_dbh);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "imp_dbh->pmysql: %lx\n",
(long) imp_dbh->pmysql);
mysql_close(imp_dbh->pmysql );
/* We don't free imp_dbh since a reference still exists */
/* The DESTROY method is the only one to 'free' memory. */
return TRUE;
}
/***************************************************************************
*
* Name: dbd_discon_all
*
* Purpose: Disconnect all database handles at shutdown time
*
* Input: dbh - database handle being disconnected
* imp_dbh - drivers private database handle data
*
* Returns: TRUE for success, FALSE otherwise; do_error has already
* been called in the latter case
*
**************************************************************************/
int dbd_discon_all (SV *drh, imp_drh_t *imp_drh) {
#if defined(dTHR)
dTHR;
#endif
dTHX;
D_imp_xxh(drh);
#if defined(DBD_MYSQL_EMBEDDED)
if (imp_drh->embedded.state)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "Stop embedded server\n");
mysql_server_end();
if (imp_drh->embedded.groups)
{
(void) SvREFCNT_dec(imp_drh->embedded.groups);
imp_drh->embedded.groups = NULL;
}
if (imp_drh->embedded.args)
{
(void) SvREFCNT_dec(imp_drh->embedded.args);
imp_drh->embedded.args = NULL;
}
}
#else
mysql_server_end();
#endif
/* The disconnect_all concept is flawed and needs more work */
if (!PL_dirty && !SvTRUE(perl_get_sv("DBI::PERL_ENDING",0))) {
sv_setiv(DBIc_ERR(imp_drh), (IV)1);
sv_setpv(DBIc_ERRSTR(imp_drh),
(char*)"disconnect_all not implemented");
/* NO EFFECT DBIh_EVENT2(drh, ERROR_event,
DBIc_ERR(imp_drh), DBIc_ERRSTR(imp_drh)); */
return FALSE;
}
PL_perl_destruct_level = 0;
return FALSE;
}
/****************************************************************************
*
* Name: dbd_db_destroy
*
* Purpose: Our part of the dbh destructor
*
* Input: dbh - database handle being destroyed
* imp_dbh - drivers private database handle data
*
* Returns: Nothing
*
**************************************************************************/
void dbd_db_destroy(SV* dbh, imp_dbh_t* imp_dbh) {
/*
* Being on the safe side never hurts ...
*/
if (DBIc_ACTIVE(imp_dbh))
{
if (imp_dbh->has_transactions)
{
if (!DBIc_has(imp_dbh, DBIcf_AutoCommit))
#if MYSQL_VERSION_ID < SERVER_PREPARE_VERSION
if ( mysql_real_query(imp_dbh->pmysql, "ROLLBACK", 8))
#else
if (mysql_rollback(imp_dbh->pmysql))
#endif
do_error(dbh, TX_ERR_ROLLBACK,"ROLLBACK failed" ,NULL);
}
dbd_db_disconnect(dbh, imp_dbh);
}
Safefree(imp_dbh->pmysql);
/* Tell DBI, that dbh->destroy must no longer be called */
DBIc_off(imp_dbh, DBIcf_IMPSET);
}
/*
***************************************************************************
*
* Name: dbd_db_STORE_attrib
*
* Purpose: Function for storing dbh attributes; we currently support
* just nothing. :-)
*
* Input: dbh - database handle being modified
* imp_dbh - drivers private database handle data
* keysv - the attribute name
* valuesv - the attribute value
*
* Returns: TRUE for success, FALSE otherwise
*
**************************************************************************/
int
dbd_db_STORE_attrib(
SV* dbh,
imp_dbh_t* imp_dbh,
SV* keysv,
SV* valuesv
)
{
dTHX;
STRLEN kl;
char *key = SvPV(keysv, kl);
SV *cachesv = Nullsv;
int cacheit = FALSE;
bool bool_value = SvTRUE(valuesv);
if (kl==10 && strEQ(key, "AutoCommit"))
{
if (imp_dbh->has_transactions)
{
bool oldval = DBIc_has(imp_dbh,DBIcf_AutoCommit) ? 1 : 0;
if (bool_value == oldval)
return TRUE;
/* if setting AutoCommit on ... */
if (!imp_dbh->no_autocommit_cmd)
{
if (
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
mysql_autocommit(imp_dbh->pmysql, bool_value)
#else
mysql_real_query(imp_dbh->pmysql,
bool_value ? "SET AUTOCOMMIT=1" : "SET AUTOCOMMIT=0",
16)
#endif
)
{
do_error(dbh, TX_ERR_AUTOCOMMIT,
bool_value ?
"Turning on AutoCommit failed" :
"Turning off AutoCommit failed"
,NULL);
return TRUE; /* TRUE means we handled it - important to avoid spurious errors */
}
}
DBIc_set(imp_dbh, DBIcf_AutoCommit, bool_value);
}
else
{
/*
* We do support neither transactions nor "AutoCommit".
* But we stub it. :-)
*/
if (!bool_value)
{
do_error(dbh, JW_ERR_NOT_IMPLEMENTED,
"Transactions not supported by database" ,NULL);
croak("Transactions not supported by database");
}
}
}
else if (kl == 16 && strEQ(key,"mysql_use_result"))
imp_dbh->use_mysql_use_result = bool_value;
else if (kl == 20 && strEQ(key,"mysql_auto_reconnect"))
imp_dbh->auto_reconnect = bool_value;
else if (kl == 20 && strEQ(key, "mysql_server_prepare"))
imp_dbh->use_server_side_prepare=SvTRUE(valuesv);
else if (kl == 23 && strEQ(key,"mysql_no_autocommit_cmd"))
imp_dbh->no_autocommit_cmd= SvTRUE(valuesv);
else if (kl == 24 && strEQ(key,"mysql_bind_type_guessing"))
imp_dbh->bind_type_guessing = SvTRUE(valuesv);
else if (kl == 31 && strEQ(key,"mysql_bind_comment_placeholders"))
imp_dbh->bind_type_guessing = SvTRUE(valuesv);
#if defined(sv_utf8_decode) && MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
else if (kl == 17 && strEQ(key, "mysql_enable_utf8"))
imp_dbh->enable_utf8 = bool_value;
#endif
else
return FALSE; /* Unknown key */
if (cacheit) /* cache value for later DBI 'quick' fetch? */
hv_store((HV*)SvRV(dbh), key, kl, cachesv, 0);
return TRUE;
}
/***************************************************************************
*
* Name: dbd_db_FETCH_attrib
*
* Purpose: Function for fetching dbh attributes
*
* Input: dbh - database handle being queried
* imp_dbh - drivers private database handle data
* keysv - the attribute name
*
* Returns: An SV*, if sucessfull; NULL otherwise
*
* Notes: Do not forget to call sv_2mortal in the former case!
*
**************************************************************************/
static SV*
my_ulonglong2str(pTHX_ my_ulonglong val)
{
char buf[64];
char *ptr = buf + sizeof(buf) - 1;
if (val == 0)
return newSVpv("0", 1);
*ptr = '\0';
while (val > 0)
{
*(--ptr) = ('0' + (val % 10));
val = val / 10;
}
return newSVpv(ptr, (buf+ sizeof(buf) - 1) - ptr);
}
SV* dbd_db_FETCH_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv)
{
dTHX;
STRLEN kl;
char *key = SvPV(keysv, kl);
char* fine_key = NULL;
SV* result = NULL;
dbh= dbh;
switch (*key) {
case 'A':
if (strEQ(key, "AutoCommit"))
{
if (imp_dbh->has_transactions)
return sv_2mortal(boolSV(DBIc_has(imp_dbh,DBIcf_AutoCommit)));
/* Default */
return &PL_sv_yes;
}
break;
}
if (strncmp(key, "mysql_", 6) == 0) {
fine_key = key;
key = key+6;
kl = kl-6;
}
/* MONTY: Check if kl should not be used or used everywhere */
switch(*key) {
case 'a':
if (kl == strlen("auto_reconnect") && strEQ(key, "auto_reconnect"))
result= sv_2mortal(newSViv(imp_dbh->auto_reconnect));
break;
case 'b':
if (kl == strlen("bind_type_guessing") &&
strEQ(key, "bind_type_guessing"))
{
result = sv_2mortal(newSViv(imp_dbh->bind_type_guessing));
}
else if (kl == strlen("bind_comment_placeholders") &&
strEQ(key, "bind_comment_placeholders"))
{
result = sv_2mortal(newSViv(imp_dbh->bind_comment_placeholders));
}
break;
case 'c':
if (kl == 10 && strEQ(key, "clientinfo"))
{
const char* clientinfo = mysql_get_client_info();
result= clientinfo ?
sv_2mortal(newSVpv(clientinfo, strlen(clientinfo))) : &PL_sv_undef;
}
else if (kl == 13 && strEQ(key, "clientversion"))
{
result= sv_2mortal(my_ulonglong2str(aTHX_ mysql_get_client_version()));
}
break;
case 'e':
if (strEQ(key, "errno"))
result= sv_2mortal(newSViv((IV)mysql_errno(imp_dbh->pmysql)));
else if ( strEQ(key, "error") || strEQ(key, "errmsg"))
{
/* Note that errmsg is obsolete, as of 2.09! */
const char* msg = mysql_error(imp_dbh->pmysql);
result= sv_2mortal(newSVpv(msg, strlen(msg)));
}
/* HELMUT */
#if defined(sv_utf8_decode) && MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
else if (kl == strlen("enable_utf8") && strEQ(key, "enable_utf8"))
result = sv_2mortal(newSViv(imp_dbh->enable_utf8));
#endif
break;
case 'd':
if (strEQ(key, "dbd_stats"))
{
HV* hv = newHV();
hv_store(
hv,
"auto_reconnects_ok",
strlen("auto_reconnects_ok"),
newSViv(imp_dbh->stats.auto_reconnects_ok),
0
);
hv_store(
hv,
"auto_reconnects_failed",
strlen("auto_reconnects_failed"),
newSViv(imp_dbh->stats.auto_reconnects_failed),
0
);
result= sv_2mortal((newRV_noinc((SV*)hv)));
}
case 'h':
if (strEQ(key, "hostinfo"))
{
const char* hostinfo = mysql_get_host_info(imp_dbh->pmysql);
result= hostinfo ?
sv_2mortal(newSVpv(hostinfo, strlen(hostinfo))) : &PL_sv_undef;
}
break;
case 'i':
if (strEQ(key, "info"))
{
const char* info = mysql_info(imp_dbh->pmysql);
result= info ? sv_2mortal(newSVpv(info, strlen(info))) : &PL_sv_undef;
}
else if (kl == 8 && strEQ(key, "insertid"))
/* We cannot return an IV, because the insertid is a long. */
result= sv_2mortal(my_ulonglong2str(aTHX_ mysql_insert_id(imp_dbh->pmysql)));
break;
case 'n':
if (kl == strlen("no_autocommit_cmd") &&
strEQ(key, "no_autocommit_cmd"))
result = sv_2mortal(newSViv(imp_dbh->no_autocommit_cmd));
break;
case 'p':
if (kl == 9 && strEQ(key, "protoinfo"))
result= sv_2mortal(newSViv(mysql_get_proto_info(imp_dbh->pmysql)));
break;
case 's':
if (kl == 10 && strEQ(key, "serverinfo"))
{
const char* serverinfo = mysql_get_server_info(imp_dbh->pmysql);
result= serverinfo ?
sv_2mortal(newSVpv(serverinfo, strlen(serverinfo))) : &PL_sv_undef;
}
else if (kl == 13 && strEQ(key, "serverversion"))
result= sv_2mortal(my_ulonglong2str(aTHX_ mysql_get_server_version(imp_dbh->pmysql)));
else if (strEQ(key, "sock"))
result= sv_2mortal(newSViv((IV) imp_dbh->pmysql));
else if (strEQ(key, "sockfd"))
result= sv_2mortal(newSViv((IV) imp_dbh->pmysql->net.fd));
else if (strEQ(key, "stat"))
{
const char* stats = mysql_stat(imp_dbh->pmysql);
result= stats ?
sv_2mortal(newSVpv(stats, strlen(stats))) : &PL_sv_undef;
}
else if (strEQ(key, "stats"))
{
/* Obsolete, as of 2.09 */
const char* stats = mysql_stat(imp_dbh->pmysql);
result= stats ?
sv_2mortal(newSVpv(stats, strlen(stats))) : &PL_sv_undef;
}
else if (kl == 14 && strEQ(key,"server_prepare"))
result= sv_2mortal(newSViv((IV) imp_dbh->use_server_side_prepare));
break;
case 't':
if (kl == 9 && strEQ(key, "thread_id"))
result= sv_2mortal(newSViv(mysql_thread_id(imp_dbh->pmysql)));
break;
}
if (result== NULL)
return Nullsv;
return result;
}
/*
**************************************************************************
*
* Name: dbd_st_prepare
*
* Purpose: Called for preparing an SQL statement; our part of the
* statement handle constructor
*
* Input: sth - statement handle being initialized
* imp_sth - drivers private statement handle data
* statement - pointer to string with SQL statement
* attribs - statement attributes, currently not in use
*
* Returns: TRUE for success, FALSE otherwise; do_error will
* be called in the latter case
*
**************************************************************************/
int
dbd_st_prepare(
SV *sth,
imp_sth_t *imp_sth,
char *statement,
SV *attribs)
{
int i;
SV **svp;
dTHX;
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
char *str_ptr;
int col_type, prepare_retval, limit_flag=0;
MYSQL_BIND *bind, *bind_end;
imp_sth_phb_t *fbind;
#endif
D_imp_xxh(sth);
D_imp_dbh_from_sth;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t-> dbd_st_prepare MYSQL_VERSION_ID %d, SQL statement: %s\n",
MYSQL_VERSION_ID, statement);
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
/* Set default value of 'mysql_server_prepare' attribute for sth from dbh */
imp_sth->use_server_side_prepare= imp_dbh->use_server_side_prepare;
if (attribs)
{
svp= DBD_ATTRIB_GET_SVP(attribs, "mysql_server_prepare", 20);
imp_sth->use_server_side_prepare = (svp) ?
SvTRUE(*svp) : imp_dbh->use_server_side_prepare;
svp = DBD_ATTRIB_GET_SVP(attribs, "async", 5);
if(svp && SvTRUE(*svp)) {
#if MYSQL_ASYNC
imp_sth->is_async = TRUE;
imp_sth->use_server_side_prepare = FALSE;
#else
do_error(sth, 2000,
"Async support was not built into this version of DBD::mysql", "HY000");
return 0;
#endif
}
}
imp_sth->fetch_done= 0;
#endif
imp_sth->done_desc= 0;
imp_sth->result= NULL;
imp_sth->currow= 0;
/* Set default value of 'mysql_use_result' attribute for sth from dbh */
svp= DBD_ATTRIB_GET_SVP(attribs, "mysql_use_result", 16);
imp_sth->use_mysql_use_result= svp ?
SvTRUE(*svp) : imp_dbh->use_mysql_use_result;
for (i= 0; i < AV_ATTRIB_LAST; i++)
imp_sth->av_attr[i]= Nullav;
/*
Clean-up previous result set(s) for sth to prevent
'Commands out of sync' error
*/
mysql_st_free_result_sets(sth, imp_sth);
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
if (imp_sth->use_server_side_prepare)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tuse_server_side_prepare set, check LIMIT\n");
/*
This code is here because mysql < 5.1 didn't support placeholders
in prepared statements and also we have to disable some statements
for PS mode
*/
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tneed to test for LIMIT\n");
for (str_ptr= statement; *str_ptr; str_ptr++)
{
/*
Processing of multi-result-set is not possible due to lack
of some calls in PS API. CALL() statement is disabled for PS
mode as it may cause multi-resut-set.
*/
if ( (tolower(*(str_ptr + 0)) == 'c') &&
(tolower(*(str_ptr + 1)) == 'a') &&
(tolower(*(str_ptr + 2)) == 'l') &&
(tolower(*(str_ptr + 3)) == 'l') &&
(tolower(*(str_ptr + 4)) == ' '))
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "Disable PS mode for CALL()\n");
imp_sth->use_server_side_prepare= 0;
}
#if MYSQL_VERSION_ID < LIMIT_PLACEHOLDER_VERSION
/*
If there is a 'limit' in the statement and placeholders are
NOT supported
*/
if ( (tolower(*(str_ptr + 0)) == 'l') &&
(tolower(*(str_ptr + 1)) == 'i') &&
(tolower(*(str_ptr + 2)) == 'm') &&
(tolower(*(str_ptr + 3)) == 'i') &&
(tolower(*(str_ptr + 4)) == 't'))
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "LIMIT set limit flag to 1\n");
limit_flag= 1;
}
if (limit_flag)
{
/* ... and place holders after the limit flag is set... */
if (*str_ptr == '?')
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tLIMIT and ? found, set to use_server_side_prepare=0\n");
/* ... then we do not want to try server side prepare (use emulation) */
imp_sth->use_server_side_prepare= 0;
break;
}
}
#endif
}
}
#endif
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
if (imp_sth->use_server_side_prepare)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tuse_server_side_prepare set\n");
/* do we really need this? If we do, we should return, not just continue */
if (imp_sth->stmt)
fprintf(stderr,
"ERROR: Trying to prepare new stmt while we have \
already not closed one \n");
imp_sth->stmt= mysql_stmt_init(imp_dbh->pmysql);
if (! imp_sth->stmt)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tERROR: Unable to return MYSQL_STMT structure \
from mysql_stmt_init(): ERROR NO: %d ERROR MSG:%s\n",
mysql_errno(imp_dbh->pmysql),
mysql_error(imp_dbh->pmysql));
}
prepare_retval= mysql_stmt_prepare(imp_sth->stmt,
statement,
strlen(statement));
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tmysql_stmt_prepare returned %d\n",
prepare_retval);
if (prepare_retval)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tmysql_stmt_prepare %d %s\n",
mysql_stmt_errno(imp_sth->stmt),
mysql_stmt_error(imp_sth->stmt));
/* For commands that are not supported by server side prepared statement
mechanism lets try to pass them through regular API */
if (mysql_stmt_errno(imp_sth->stmt) == ER_UNSUPPORTED_PS)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tSETTING imp_sth->use_server_side_prepare to 0\n");
imp_sth->use_server_side_prepare= 0;
}
else
{
do_error(sth, mysql_stmt_errno(imp_sth->stmt),
mysql_stmt_error(imp_sth->stmt),
mysql_sqlstate(imp_dbh->pmysql));
mysql_stmt_close(imp_sth->stmt);
imp_sth->stmt= NULL;
return FALSE;
}
}
else
{
DBIc_NUM_PARAMS(imp_sth)= mysql_stmt_param_count(imp_sth->stmt);
/* mysql_stmt_param_count */
if (DBIc_NUM_PARAMS(imp_sth) > 0)
{
int has_statement_fields= imp_sth->stmt->fields != 0;
/* Allocate memory for bind variables */
imp_sth->bind= alloc_bind(DBIc_NUM_PARAMS(imp_sth));
imp_sth->fbind= alloc_fbind(DBIc_NUM_PARAMS(imp_sth));
imp_sth->has_been_bound= 0;
/* Initialize ph variables with NULL values */
for (i= 0,
bind= imp_sth->bind,
fbind= imp_sth->fbind,
bind_end= bind+DBIc_NUM_PARAMS(imp_sth);
bind < bind_end ;
bind++, fbind++, i++ )
{
/*
if this statement has a result set, field types will be
correctly identified. If there is no result set, such as
with an INSERT, fields will not be defined, and all buffer_type
will default to MYSQL_TYPE_VAR_STRING
*/
col_type= (has_statement_fields ?
imp_sth->stmt->fields[i].type : MYSQL_TYPE_STRING);
bind->buffer_type= mysql_to_perl_type(col_type);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmysql_to_perl_type returned %d\n", col_type);
bind->buffer= NULL;
bind->length= &(fbind->length);
bind->is_null= (char*) &(fbind->is_null);
fbind->is_null= 1;
fbind->length= 0;
}
}
}
}
#endif
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
/* Count the number of parameters (driver, vs server-side) */
if (imp_sth->use_server_side_prepare == 0)
DBIc_NUM_PARAMS(imp_sth) = count_params((imp_xxh_t *)imp_dbh, aTHX_ statement,
imp_dbh->bind_comment_placeholders);
#else
DBIc_NUM_PARAMS(imp_sth) = count_params((imp_xxh_t *)imp_dbh, aTHX_ statement,
imp_dbh->bind_comment_placeholders);
#endif
/* Allocate memory for parameters */
imp_sth->params= alloc_param(DBIc_NUM_PARAMS(imp_sth));
DBIc_IMPSET_on(imp_sth);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_prepare\n");
return 1;
}
/***************************************************************************
* Name: dbd_st_free_result_sets
*
* Purpose: Clean-up single or multiple result sets (if any)
*
* Inputs: sth - Statement handle
* imp_sth - driver's private statement handle
*
* Returns: 1 ok
* 0 error
*************************************************************************/
int mysql_st_free_result_sets (SV * sth, imp_sth_t * imp_sth)
{
dTHX;
D_imp_dbh_from_sth;
D_imp_xxh(sth);
int next_result_rc= -1;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t>- dbd_st_free_result_sets\n");
#if MYSQL_VERSION_ID >= MULTIPLE_RESULT_SET_VERSION
do
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_free_result_sets RC %d\n", next_result_rc);
if (next_result_rc == 0)
{
if (!(imp_sth->result = mysql_use_result(imp_dbh->pmysql)))
{
/* Check for possible error */
if (mysql_field_count(imp_dbh->pmysql))
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_free_result_sets ERROR: %s\n",
mysql_error(imp_dbh->pmysql));
do_error(sth, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql),
mysql_sqlstate(imp_dbh->pmysql));
return 0;
}
}
}
if (imp_sth->result)
{
mysql_free_result(imp_sth->result);
imp_sth->result=NULL;
}
} while ((next_result_rc=mysql_next_result(imp_dbh->pmysql))==0);
if (next_result_rc > 0)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_free_result_sets: Error while processing multi-result set: %s\n",
mysql_error(imp_dbh->pmysql));
do_error(sth, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql),
mysql_sqlstate(imp_dbh->pmysql));
}
#else
if (imp_sth->result)
{
mysql_free_result(imp_sth->result);
imp_sth->result=NULL;
}
#endif
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_free_result_sets\n");
return 1;
}
#if MYSQL_VERSION_ID >= MULTIPLE_RESULT_SET_VERSION
/***************************************************************************
* Name: dbd_st_more_results
*
* Purpose: Move onto the next result set (if any)
*
* Inputs: sth - Statement handle
* imp_sth - driver's private statement handle
*
* Returns: 1 if there are more results sets
* 0 if there are not
* -1 for errors.
*************************************************************************/
int dbd_st_more_results(SV* sth, imp_sth_t* imp_sth)
{
dTHX;
D_imp_dbh_from_sth;
D_imp_xxh(sth);
int use_mysql_use_result=imp_sth->use_mysql_use_result;
int next_result_return_code, i;
MYSQL* svsock= imp_dbh->pmysql;
if (!SvROK(sth) || SvTYPE(SvRV(sth)) != SVt_PVHV)
croak("Expected hash array");
if (!mysql_more_results(svsock))
{
/* No more pending result set(s)*/
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\n <- dbs_st_more_results no more results\n");
return 0;
}
if (imp_sth->use_server_side_prepare)
{
do_warn(sth, JW_ERR_NOT_IMPLEMENTED,
"Processing of multiple result set is not possible with server side prepare");
return 0;
}
/*
* Free cached array attributes
*/
for (i= 0; i < AV_ATTRIB_LAST; i++)
{
if (imp_sth->av_attr[i])
SvREFCNT_dec(imp_sth->av_attr[i]);
imp_sth->av_attr[i]= Nullav;
}
/* Release previous MySQL result*/
if (imp_sth->result)
mysql_free_result(imp_sth->result);
if (DBIc_ACTIVE(imp_sth))
DBIc_ACTIVE_off(imp_sth);
next_result_return_code= mysql_next_result(svsock);
imp_sth->warning_count = mysql_warning_count(imp_dbh->pmysql);
/*
mysql_next_result returns
0 if there are more results
-1 if there are no more results
>0 if there was an error
*/
if (next_result_return_code > 0)
{
do_error(sth, mysql_errno(svsock), mysql_error(svsock),
mysql_sqlstate(svsock));
return 0;
}
else
{
/* Store the result from the Query */
imp_sth->result = use_mysql_use_result ?
mysql_use_result(svsock) : mysql_store_result(svsock);
if (mysql_errno(svsock))
do_error(sth, mysql_errno(svsock), mysql_error(svsock),
mysql_sqlstate(svsock));
imp_sth->row_num= mysql_affected_rows(imp_dbh->pmysql);
if (imp_sth->result == NULL)
{
/* No "real" rowset*/
return 1;
}
else
{
/* We have a new rowset */
imp_sth->currow=0;
/* delete cached handle attributes */
/* XXX should be driven by a list to ease maintenance */
hv_delete((HV*)SvRV(sth), "NAME", 4, G_DISCARD);
hv_delete((HV*)SvRV(sth), "NULLABLE", 8, G_DISCARD);
hv_delete((HV*)SvRV(sth), "NUM_OF_FIELDS", 13, G_DISCARD);
hv_delete((HV*)SvRV(sth), "PRECISION", 9, G_DISCARD);
hv_delete((HV*)SvRV(sth), "SCALE", 5, G_DISCARD);
hv_delete((HV*)SvRV(sth), "TYPE", 4, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_insertid", 14, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_is_auto_increment", 23, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_is_blob", 13, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_is_key", 12, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_is_num", 12, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_is_pri_key", 16, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_length", 12, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_max_length", 16, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_table", 11, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_type", 10, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_type_name", 15, G_DISCARD);
hv_delete((HV*)SvRV(sth), "mysql_warning_count", 20, G_DISCARD);
/* Adjust NUM_OF_FIELDS - which also adjusts the row buffer size */
DBIc_NUM_FIELDS(imp_sth)= 0; /* for DBI <= 1.53 */
DBIc_DBISTATE(imp_sth)->set_attr_k(sth, sv_2mortal(newSVpvn("NUM_OF_FIELDS",13)), 0,
sv_2mortal(newSViv(mysql_num_fields(imp_sth->result)))
);
DBIc_ACTIVE_on(imp_sth);
imp_sth->done_desc = 0;
}
imp_dbh->pmysql->net.last_errno= 0;
return 1;
}
}
#endif
/**************************************************************************
*
* Name: mysql_st_internal_execute
*
* Purpose: Internal version for executing a statement, called both from
* within the "do" and the "execute" method.
*
* Inputs: h - object handle, for storing error messages
* statement - query being executed
* attribs - statement attributes, currently ignored
* num_params - number of parameters being bound
* params - parameter array
* result - where to store results, if any
* svsock - socket connected to the database
*
**************************************************************************/
my_ulonglong mysql_st_internal_execute(
SV *h, /* could be sth or dbh */
SV *statement,
SV *attribs,
int num_params,
imp_sth_ph_t *params,
MYSQL_RES **result,
MYSQL *svsock,
int use_mysql_use_result
)
{
dTHX;
bool bind_type_guessing= FALSE;
bool bind_comment_placeholders= TRUE;
STRLEN slen;
char *sbuf = SvPV(statement, slen);
char *table;
char *salloc;
int htype;
int errno;
#if MYSQL_ASYNC
bool async = FALSE;
#endif
my_ulonglong rows= 0;
/* thank you DBI.c for this info! */
D_imp_xxh(h);
attribs= attribs;
htype= DBIc_TYPE(imp_xxh);
/*
It is important to import imp_dbh properly according to the htype
that it is! Also, one might ask why bind_type_guessing is assigned
in each block. Well, it's because D_imp_ macros called in these
blocks make it so imp_dbh is not "visible" or defined outside of the
if/else (when compiled, it fails for imp_dbh not being defined).
*/
/* h is a dbh */
if (htype == DBIt_DB)
{
D_imp_dbh(h);
/* if imp_dbh is not available, it causes segfault (proper) on OpenBSD */
if (imp_dbh && imp_dbh->bind_type_guessing)
{
bind_type_guessing= imp_dbh->bind_type_guessing;
bind_comment_placeholders= bind_comment_placeholders;
}
#if MYSQL_ASYNC
async = (bool) (imp_dbh->async_query_in_flight != NULL);
#endif
}
/* h is a sth */
else
{
D_imp_sth(h);
D_imp_dbh_from_sth;
/* if imp_dbh is not available, it causes segfault (proper) on OpenBSD */
if (imp_dbh)
{
bind_type_guessing= imp_dbh->bind_type_guessing;
bind_comment_placeholders= imp_dbh->bind_comment_placeholders;
}
#if MYSQL_ASYNC
async = imp_sth->is_async;
if(async) {
imp_dbh->async_query_in_flight = imp_sth;
} else {
imp_dbh->async_query_in_flight = NULL;
}
#endif
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "mysql_st_internal_execute MYSQL_VERSION_ID %d\n",
MYSQL_VERSION_ID );
salloc= parse_params(imp_xxh,
aTHX_ svsock,
sbuf,
&slen,
params,
num_params,
bind_type_guessing,
bind_comment_placeholders);
if (salloc)
{
sbuf= salloc;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "Binding parameters: %s\n", sbuf);
}
if (slen >= 11 && (!strncmp(sbuf, "listfields ", 11) ||
!strncmp(sbuf, "LISTFIELDS ", 11)))
{
/* remove pre-space */
slen-= 10;
sbuf+= 10;
while (slen && isspace(*sbuf)) { --slen; ++sbuf; }
if (!slen)
{
do_error(h, JW_ERR_QUERY, "Missing table name" ,NULL);
return -2;
}
if (!(table= malloc(slen+1)))
{
do_error(h, JW_ERR_MEM, "Out of memory" ,NULL);
return -2;
}
strncpy(table, sbuf, slen);
sbuf= table;
while (slen && !isspace(*sbuf))
{
--slen;
++sbuf;
}
*sbuf++= '\0';
*result= mysql_list_fields(svsock, table, NULL);
free(table);
if (!(*result))
{
do_error(h, mysql_errno(svsock), mysql_error(svsock)
,mysql_sqlstate(svsock));
return -2;
}
return 0;
}
#if MYSQL_ASYNC
if(async) {
if((mysql_send_query(svsock, sbuf, slen)) &&
(!mysql_db_reconnect(h) ||
(mysql_send_query(svsock, sbuf, slen))))
{
rows = -2;
} else {
rows = 0;
}
} else {
#endif
if ((mysql_real_query(svsock, sbuf, slen)) &&
(!mysql_db_reconnect(h) ||
(mysql_real_query(svsock, sbuf, slen))))
{
rows = -2;
} else {
/** Store the result from the Query */
*result= use_mysql_use_result ?
mysql_use_result(svsock) : mysql_store_result(svsock);
if (mysql_errno(svsock))
do_error(h, mysql_errno(svsock), mysql_error(svsock)
,mysql_sqlstate(svsock));
if (!*result)
rows= mysql_affected_rows(svsock);
else
rows= mysql_num_rows(*result);
}
#if MYSQL_ASYNC
}
Safefree(salloc);
if(rows == -2) {
do_error(h, mysql_errno(svsock), mysql_error(svsock),
mysql_sqlstate(svsock));
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "IGNORING ERROR errno %d\n", errno);
rows = -2;
}
#endif
return(rows);
}
/**************************************************************************
*
* Name: mysql_st_internal_execute41
*
* Purpose: Internal version for executing a prepared statement, called both
* from within the "do" and the "execute" method.
* MYSQL 4.1 API
*
*
* Inputs: h - object handle, for storing error messages
* statement - query being executed
* attribs - statement attributes, currently ignored
* num_params - number of parameters being bound
* params - parameter array
* result - where to store results, if any
* svsock - socket connected to the database
*
**************************************************************************/
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
my_ulonglong mysql_st_internal_execute41(
SV *sth,
int num_params,
MYSQL_RES **result,
MYSQL_STMT *stmt,
MYSQL_BIND *bind,
int *has_been_bound
)
{
int i;
enum enum_field_types enum_type;
dTHX;
int execute_retval;
my_ulonglong rows=0;
D_imp_xxh(sth);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t-> mysql_st_internal_execute41\n");
/* free result if exists */
if (*result)
{
mysql_free_result(*result);
*result= 0;
}
/*
If were performed any changes with ph variables
we have to rebind them
*/
if (num_params > 0 && !(*has_been_bound))
{
if (mysql_stmt_bind_param(stmt,bind))
goto error;
*has_been_bound= 1;
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tmysql_st_internal_execute41 calling mysql_execute with %d num_params\n",
num_params);
execute_retval= mysql_stmt_execute(stmt);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tmysql_stmt_execute returned %d\n",
execute_retval);
if (execute_retval)
goto error;
/*
This statement does not return a result set (INSERT, UPDATE...)
*/
if (!(*result= mysql_stmt_result_metadata(stmt)))
{
if (mysql_stmt_errno(stmt))
goto error;
rows= mysql_stmt_affected_rows(stmt);
}
/*
This statement returns a result set (SELECT...)
*/
else
{
for (i = mysql_stmt_field_count(stmt) - 1; i >=0; --i) {
enum_type = mysql_to_perl_type(stmt->fields[i].type);
if (enum_type != MYSQL_TYPE_DOUBLE && enum_type != MYSQL_TYPE_LONG)
{
/* mysql_stmt_store_result to update MYSQL_FIELD->max_length */
my_bool on = 1;
mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &on);
break;
}
}
/* Get the total rows affected and return */
if (mysql_stmt_store_result(stmt))
goto error;
else
rows= mysql_stmt_num_rows(stmt);
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t<- mysql_internal_execute_41 returning %d rows\n",
(int) rows);
return(rows);
error:
if (*result)
{
mysql_free_result(*result);
*result= 0;
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
" errno %d err message %s\n",
mysql_stmt_errno(stmt),
mysql_stmt_error(stmt));
do_error(sth, mysql_stmt_errno(stmt), mysql_stmt_error(stmt),
mysql_stmt_sqlstate(stmt));
mysql_stmt_reset(stmt);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t<- mysql_st_internal_execute41\n");
return -2;
}
#endif
/***************************************************************************
*
* Name: dbd_st_execute
*
* Purpose: Called for preparing an SQL statement; our part of the
* statement handle constructor
*
* Input: sth - statement handle being initialized
* imp_sth - drivers private statement handle data
*
* Returns: TRUE for success, FALSE otherwise; do_error will
* be called in the latter case
*
**************************************************************************/
int dbd_st_execute(SV* sth, imp_sth_t* imp_sth)
{
dTHX;
char actual_row_num[64];
int i;
SV **statement;
D_imp_dbh_from_sth;
D_imp_xxh(sth);
#if defined (dTHR)
dTHR;
#endif
ASYNC_CHECK_RETURN(sth, -2);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
" -> dbd_st_execute for %08lx\n", (u_long) sth);
if (!SvROK(sth) || SvTYPE(SvRV(sth)) != SVt_PVHV)
croak("Expected hash array");
/* Free cached array attributes */
for (i= 0; i < AV_ATTRIB_LAST; i++)
{
if (imp_sth->av_attr[i])
SvREFCNT_dec(imp_sth->av_attr[i]);
imp_sth->av_attr[i]= Nullav;
}
statement= hv_fetch((HV*) SvRV(sth), "Statement", 9, FALSE);
/*
Clean-up previous result set(s) for sth to prevent
'Commands out of sync' error
*/
mysql_st_free_result_sets (sth, imp_sth);
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
if (imp_sth->use_server_side_prepare && ! imp_sth->use_mysql_use_result)
{
imp_sth->row_num= mysql_st_internal_execute41(
sth,
DBIc_NUM_PARAMS(imp_sth),
&imp_sth->result,
imp_sth->stmt,
imp_sth->bind,
&imp_sth->has_been_bound
);
}
else {
#endif
imp_sth->row_num= mysql_st_internal_execute(
sth,
*statement,
NULL,
DBIc_NUM_PARAMS(imp_sth),
imp_sth->params,
&imp_sth->result,
imp_dbh->pmysql,
imp_sth->use_mysql_use_result
);
#if MYSQL_ASYNC
if(imp_dbh->async_query_in_flight) {
DBIc_ACTIVE_on(imp_sth);
return 0;
}
#endif
}
if (imp_sth->row_num+1 != (my_ulonglong)-1)
{
if (!imp_sth->result)
{
imp_sth->insertid= mysql_insert_id(imp_dbh->pmysql);
#if MYSQL_VERSION_ID >= MULTIPLE_RESULT_SET_VERSION
if (mysql_more_results(imp_dbh->pmysql))
DBIc_ACTIVE_on(imp_sth);
#endif
}
else
{
/** Store the result in the current statement handle */
DBIc_NUM_FIELDS(imp_sth)= mysql_num_fields(imp_sth->result);
DBIc_ACTIVE_on(imp_sth);
imp_sth->done_desc= 0;
imp_sth->fetch_done= 0;
}
}
imp_sth->warning_count = mysql_warning_count(imp_dbh->pmysql);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
{
/*
PerlIO_printf doesn't always handle imp_sth->row_num %llu
consistantly!!
*/
sprintf(actual_row_num, "%llu", imp_sth->row_num);
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
" <- dbd_st_execute returning imp_sth->row_num %s\n",
actual_row_num);
}
return (int)imp_sth->row_num;
}
/**************************************************************************
*
* Name: dbd_describe
*
* Purpose: Called from within the fetch method to describe the result
*
* Input: sth - statement handle being initialized
* imp_sth - our part of the statement handle, there's no
* need for supplying both; Tim just doesn't remove it
*
* Returns: TRUE for success, FALSE otherwise; do_error will
* be called in the latter case
*
**************************************************************************/
int dbd_describe(SV* sth, imp_sth_t* imp_sth)
{
dTHX;
D_imp_xxh(sth);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t--> dbd_describe\n");
#if MYSQL_VERSION_ID >= SERVER_PREPARE_VERSION
if (imp_sth->use_server_side_prepare)
{
int i;
int col_type;
int num_fields= DBIc_NUM_FIELDS(imp_sth);
imp_sth_fbh_t *fbh;
MYSQL_BIND *buffer;
MYSQL_FIELD *fields;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tdbd_describe() num_fields %d\n",
num_fields);
if (imp_sth->done_desc)
return TRUE;
if (!num_fields || !imp_sth->result)
{
/* no metadata */
do_error(sth, JW_ERR_SEQUENCE,
"no metadata information while trying describe result set",
NULL);
return 0;
}
/* allocate fields buffers */
if ( !(imp_sth->fbh= alloc_fbuffer(num_fields))
|| !(imp_sth->buffer= alloc_bind(num_fields)) )
{
/* Out of memory */
do_error(sth, JW_ERR_SEQUENCE,
"Out of memory in dbd_sescribe()",NULL);
return 0;
}
fields= mysql_fetch_fields(imp_sth->result);
for (
fbh= imp_sth->fbh, buffer= (MYSQL_BIND*)imp_sth->buffer, i= 0;
i < num_fields;
i++, fbh++, buffer++
)
{
/* get the column type */
col_type = fields ? fields[i].type : MYSQL_TYPE_STRING;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
{
PerlIO_printf(DBIc_LOGPIO(imp_xxh),"\t\ti %d col_type %d fbh->length %d\n",
i, col_type, (int) fbh->length);
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tfields[i].length %lu fields[i].max_length %lu fields[i].type %d fields[i].charsetnr %d\n",
(long unsigned int) fields[i].length, (long unsigned int) fields[i].max_length, fields[i].type,
fields[i].charsetnr);
}
fbh->charsetnr = fields[i].charsetnr;
#if MYSQL_VERSION_ID < FIELD_CHARSETNR_VERSION
fbh->flags = fields[i].flags;
#endif
buffer->buffer_type= mysql_to_perl_type(col_type);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmysql_to_perl_type returned %d\n",
col_type);
buffer->length= &(fbh->length);
buffer->is_null= &(fbh->is_null);
switch (buffer->buffer_type) {
case MYSQL_TYPE_DOUBLE:
buffer->buffer_length= sizeof(fbh->ddata);
buffer->buffer= (char*) &fbh->ddata;
break;
case MYSQL_TYPE_LONG:
buffer->buffer_length= sizeof(fbh->ldata);
buffer->buffer= (char*) &fbh->ldata;
buffer->is_unsigned= (fields[i].flags & UNSIGNED_FLAG) ? 1 : 0;
break;
default:
buffer->buffer_length= fields[i].max_length ? fields[i].max_length : 1;
Newz(908, fbh->data, buffer->buffer_length, char);
buffer->buffer= (char *) fbh->data;
}
}
if (mysql_stmt_bind_result(imp_sth->stmt, imp_sth->buffer))
{
do_error(sth, mysql_stmt_errno(imp_sth->stmt),
mysql_stmt_error(imp_sth->stmt),
mysql_stmt_sqlstate(imp_sth->stmt));
return 0;
}
}
#endif
imp_sth->done_desc= 1;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_describe\n");
return TRUE;
}
/**************************************************************************
*
* Name: dbd_st_fetch
*
* Purpose: Called for fetching a result row
*
* Input: sth - statement handle being initialized
* imp_sth - drivers private statement handle data
*
* Returns: array of columns; the array is allocated by DBI via
* DBIc_DBISTATE(imp_sth)->get_fbav(imp_sth), even the values
* of the array are prepared, we just need to modify them
* appropriately
*
**************************************************************************/
AV*
dbd_st_fetch(SV *sth, imp_sth_t* imp_sth)
{
dTHX;
int num_fields, ChopBlanks, i, rc;
unsigned long *lengths;
AV *av;
int av_length, av_readonly;
MYSQL_ROW cols;
D_imp_dbh_from_sth;
MYSQL* svsock= imp_dbh->pmysql;
imp_sth_fbh_t *fbh;
D_imp_xxh(sth);
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
MYSQL_BIND *buffer;
#endif
MYSQL_FIELD *fields;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t-> dbd_st_fetch\n");
#if MYSQL_ASYNC
if(imp_dbh->async_query_in_flight) {
if(mysql_db_async_result(sth, &imp_sth->result) <= 0) {
return Nullav;
}
}
#endif
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
if (imp_sth->use_server_side_prepare)
{
if (!DBIc_ACTIVE(imp_sth) )
{
do_error(sth, JW_ERR_SEQUENCE, "no statement executing\n",NULL);
return Nullav;
}
if (imp_sth->fetch_done)
{
do_error(sth, JW_ERR_SEQUENCE, "fetch() but fetch already done",NULL);
return Nullav;
}
if (!imp_sth->done_desc)
{
if (!dbd_describe(sth, imp_sth))
{
do_error(sth, JW_ERR_SEQUENCE, "Error while describe result set.",
NULL);
return Nullav;
}
}
}
#endif
ChopBlanks = DBIc_is(imp_sth, DBIcf_ChopBlanks);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tdbd_st_fetch for %08lx, chopblanks %d\n",
(u_long) sth, ChopBlanks);
if (!imp_sth->result)
{
do_error(sth, JW_ERR_SEQUENCE, "fetch() without execute()" ,NULL);
return Nullav;
}
/* fix from 2.9008 */
imp_dbh->pmysql->net.last_errno = 0;
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
if (imp_sth->use_server_side_prepare)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tdbd_st_fetch calling mysql_fetch\n");
if ((rc= mysql_stmt_fetch(imp_sth->stmt)))
{
if (rc == 1)
do_error(sth, mysql_stmt_errno(imp_sth->stmt),
mysql_stmt_error(imp_sth->stmt),
mysql_stmt_sqlstate(imp_sth->stmt));
#if MYSQL_VERSION_ID >= MYSQL_VERSION_5_0
if (rc == MYSQL_DATA_TRUNCATED)
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tdbd_st_fetch data truncated\n");
#endif
if (rc == MYSQL_NO_DATA)
{
/* Update row_num to affected_rows value */
imp_sth->row_num= mysql_stmt_affected_rows(imp_sth->stmt);
imp_sth->fetch_done=1;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tdbd_st_fetch no data\n");
}
dbd_st_finish(sth, imp_sth);
return Nullav;
}
imp_sth->currow++;
av= DBIc_DBISTATE(imp_sth)->get_fbav(imp_sth);
num_fields=mysql_stmt_field_count(imp_sth->stmt);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),
"\t\tdbd_st_fetch called mysql_fetch, rc %d num_fields %d\n",
rc, num_fields);
for (
buffer= imp_sth->buffer,
fbh= imp_sth->fbh,
i= 0;
i < num_fields;
i++,
fbh++,
buffer++
)
{
SV *sv= AvARRAY(av)[i]; /* Note: we (re)use the SV in the AV */
/* This is wrong, null is not being set correctly
* This is not the way to determine length (this would break blobs!)
*/
if (fbh->is_null)
(void) SvOK_off(sv); /* Field is NULL, return undef */
else
{
/* In case of BLOB/TEXT fields we allocate only 8192 bytes
in dbd_describe() for data. Here we know real size of field
so we should increase buffer size and refetch column value
*/
if (fbh->length > buffer->buffer_length)
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh),"\t\tRefetch BLOB/TEXT column: %d\n", i);
Renew(fbh->data, fbh->length, char);
buffer->buffer_length= fbh->length;
buffer->buffer= (char *) fbh->data;
/*TODO: Use offset instead of 0 to fetch only remain part of data*/
if (mysql_stmt_fetch_column(imp_sth->stmt, buffer , i, 0))
do_error(sth, mysql_stmt_errno(imp_sth->stmt),
mysql_stmt_error(imp_sth->stmt),
mysql_stmt_sqlstate(imp_sth->stmt));
}
/* This does look a lot like Georg's PHP driver doesn't it? --Brian */
/* Credit due to Georg - mysqli_api.c ;) --PMG */
switch (buffer->buffer_type) {
case MYSQL_TYPE_DOUBLE:
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tst_fetch double data %f\n", fbh->ddata);
sv_setnv(sv, fbh->ddata);
break;
case MYSQL_TYPE_LONG:
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tst_fetch int data %d, unsigned? %d\n",
(int) fbh->ldata, buffer->is_unsigned);
if (buffer->is_unsigned)
sv_setuv(sv, fbh->ldata);
else
sv_setiv(sv, fbh->ldata);
break;
default:
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tERROR IN st_fetch_string");
STRLEN len= fbh->length;
/* ChopBlanks */
if (ChopBlanks)
{
#if MYSQL_VERSION_ID >= FIELD_CHARSETNR_VERSION
/* see bottom of: http://www.mysql.org/doc/refman/5.0/en/c-api-datatypes.html */
if (fbh->charsetnr != 63)
#else
if (!(fbh->flags & BINARY_FLAG))
#endif
while (len && fbh->data[len-1] == ' ')
{ --len; }
}
/* END OF ChopBlanks */
sv_setpvn(sv, fbh->data, len);
/* UTF8 */
/*HELMUT*/
#if defined(sv_utf8_decode) && MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
#if MYSQL_VERSION_ID >= FIELD_CHARSETNR_VERSION
/* see bottom of: http://www.mysql.org/doc/refman/5.0/en/c-api-datatypes.html */
if (imp_dbh->enable_utf8 && fbh->charsetnr != 63)
#else
if (imp_dbh->enable_utf8 && !(fbh->flags & BINARY_FLAG))
#endif
sv_utf8_decode(sv);
#endif
/* END OF UTF8 */
break;
}
}
}
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_fetch, %d cols\n", num_fields);
return av;
}
else
{
#endif
imp_sth->currow++;
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
{
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tdbd_st_fetch result set details\n");
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\timp_sth->result=%08lx\n",(long unsigned int) imp_sth->result);
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmysql_num_fields=%llu\n",
(long long unsigned int) mysql_num_fields(imp_sth->result));
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmysql_num_rows=%llu\n",
mysql_num_rows(imp_sth->result));
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmysql_affected_rows=%llu\n",
mysql_affected_rows(imp_dbh->pmysql));
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tdbd_st_fetch for %08lx, currow= %d\n",
(u_long) sth,imp_sth->currow);
}
if (!(cols= mysql_fetch_row(imp_sth->result)))
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
{
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tdbd_st_fetch, no more rows to fetch");
}
if (mysql_errno(imp_dbh->pmysql))
do_error(sth, mysql_errno(imp_dbh->pmysql),
mysql_error(imp_dbh->pmysql),
mysql_sqlstate(imp_dbh->pmysql));
#if MYSQL_VERSION_ID >= MULTIPLE_RESULT_SET_VERSION
if (!mysql_more_results(svsock))
#endif
dbd_st_finish(sth, imp_sth);
return Nullav;
}
num_fields= mysql_num_fields(imp_sth->result);
fields= mysql_fetch_fields(imp_sth->result);
lengths= mysql_fetch_lengths(imp_sth->result);
if ((av= DBIc_FIELDS_AV(imp_sth)) != Nullav)
{
av_length= av_len(av)+1;
if (av_length != num_fields) /* Resize array if necessary */
{
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_fetch, size of results array(%d) != num_fields(%d)\n",
av_length, num_fields);
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- dbd_st_fetch, result fields(%d)\n",
DBIc_NUM_FIELDS(imp_sth));
av_readonly = SvREADONLY(av);
if (av_readonly)
SvREADONLY_off( av ); /* DBI sets this readonly */
while (av_length < num_fields)
{
av_store(av, av_length++,