/*
* DBD::mysql - DBI driver for the mysql database
*
* Copyright (c) 2004-2014 Patrick Galbraith
* Copyright (c) 2013-2014 Michiel Beijen
* Copyright (c) 2004-2007 Alexey Stroganov
* 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.
*/
#ifdef WIN32
#include "windows.h"
#include "winsock.h"
#endif
#include "dbdimp.h"
#if defined(WIN32) && defined(WORD)
#undef WORD
typedef short WORD;
#endif
#ifdef WIN32
#define MIN min
#else
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#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;
swit