C:\Users\lyujinhee\server\myodbcBug1017\mysql-connector-odbc-5.2.6-src\driver\my_prepared_stmt.c C:\Users\lyujinhee\server\myodbcBug1017\mysql-connector-odbc-5.2.6-src-patched\driver\my_prepared_stmt.c
/* /*
 Copyright (c) 2012-2013, Oracle and/or its affiliates. All rights reserved.  Copyright (c) 2012-2013, Oracle and/or its affiliates. All rights reserved.
   
 The MySQL Connector/ODBC is licensed under the terms of the GPLv2  The MySQL Connector/ODBC is licensed under the terms of the GPLv2
 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
 MySQL Connectors. There are special exceptions to the terms and  MySQL Connectors. There are special exceptions to the terms and
 conditions of the GPLv2 as it is applied to this software, see the  conditions of the GPLv2 as it is applied to this software, see the
 FLOSS License Exception  FLOSS License Exception
 <http://www.mysql.com/about/legal/licensing/foss-exception.html>.  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
     
 This program is free software; you can redistribute it and/or modify  This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published  it under the terms of the GNU General Public License as published
 by the Free Software Foundation; version 2 of the License.  by the Free Software Foundation; version 2 of the License.
     
 This program is distributed in the hope that it will be useful, but  This program is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 for more details.  for more details.
     
 You should have received a copy of the GNU General Public License along  You should have received a copy of the GNU General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,  with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA  51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/  */ 
   
/** /**
 @file  ssps.c  @file  ssps.c
 @brief Functions to support use of Server Side Prepared Statements.  @brief Functions to support use of Server Side Prepared Statements.
*/  */ 
   
#include "driver.h" #include "driver.h"
   
   
   
/* {{{ my_l_to_a() -I- */  /* {{{ my_l_to_a() -I- */ 
static char * my_l_to_a(char * buf, size_t buf_size, long long a) static char * my_l_to_a(char * buf, size_t buf_size, long long a)
{ {
   snprintf(buf, buf_size, "%lld", (long long) a);    snprintf(buf, buf_size, "%lld", (long long) a);
   return buf;    return buf;
} }
/* }}} */  /* }}} */ 
   
   
/* {{{ my_ul_to_a() -I- */  /* {{{ my_ul_to_a() -I- */ 
static char * my_ul_to_a(char * buf, size_t buf_size, unsigned long long a) static char * my_ul_to_a(char * buf, size_t buf_size, unsigned long long a)
{ {
   snprintf(buf, buf_size, "%llu", (unsigned long long) a);    snprintf(buf, buf_size, "%llu", (unsigned long long) a);
   return buf;    return buf;
} }
/* }}} */  /* }}} */ 
   
   
/* {{{ my_f_to_a() -I- */  /* {{{ my_f_to_a() -I- */ 
static char * my_f_to_a(char * buf, size_t buf_size, double a) static char * my_f_to_a(char * buf, size_t buf_size, double a)
{ {
   snprintf(buf, buf_size, "%f", a);    snprintf(buf, buf_size, "%f", a);
   return buf;    return buf;
} }
/* }}} */  /* }}} */ 
   
   
/* {{{ ssps_init() -I- */  /* {{{ ssps_init() -I- */ 
void ssps_init(STMT *stmt) void ssps_init(STMT *stmt)
{ {
 stmt->ssps= mysql_stmt_init(&stmt->dbc->mysql);  stmt->ssps= mysql_stmt_init(&stmt->dbc->mysql);
   
 stmt->result_bind= 0;  stmt->result_bind= 0;
} }
/* }}} */  /* }}} */ 
   
   
char * numeric2binary(char * dst, long long src, unsigned int byte_count) char * numeric2binary(char * dst, long long src, unsigned int byte_count)
{ {
 char byte;  char byte;
   
 while (byte_count)  while (byte_count)
 {  {
   byte= src & 0xff;    byte= src & 0xff;
   *(dst+(--byte_count))= byte;    *(dst+(--byte_count))= byte;
   src= src >> 8;    src= src >> 8;
 }  }
   
 return dst;  return dst;
} }
   
   
SQLRETURN SQL_API SQLRETURN SQL_API
sql_get_data(STMT *stmt, SQLSMALLINT fCType, uint column_number, sql_get_data(STMT *stmt, SQLSMALLINT fCType, uint column_number,
            SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue,             SQLPOINTER rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue,
            char *value, ulong length, DESCREC *arrec);             char *value, ulong length, DESCREC *arrec);
   
/** /**
 @returns TRUE if the resultset is SP OUT params  @returns TRUE if the resultset is SP OUT params
 Basically it makes sense with prepared statements only  Basically it makes sense with prepared statements only
 */   */ 
BOOL ssps_get_out_params(STMT *stmt) BOOL ssps_get_out_params(STMT *stmt)
{ {
   /* If we use prepared statement, and the query is CALL and we have any    /* If we use prepared statement, and the query is CALL and we have any
    user's parameter described as INOUT or OUT and that is only result */      user's parameter described as INOUT or OUT and that is only result */ 
 if (is_call_procedure(&stmt->query))  if (is_call_procedure(&stmt->query))
 {  {
   MYSQL_ROW values= NULL;    MYSQL_ROW values= NULL;
   DESCREC   *iprec, *aprec;    DESCREC   *iprec, *aprec;
   uint      counter= 0;    uint      counter= 0;
   int       i;    int       i;
   
   /*Since OUT parameters can be completely different - we have to free current    /*Since OUT parameters can be completely different - we have to free current
     bind and bind new */       bind and bind new */ 
   
   free_result_bind(stmt);    free_result_bind(stmt);
   /* Thus function interface has to be changed */     /* Thus function interface has to be changed */ 
   if (ssps_bind_result(stmt) == 0)    if (ssps_bind_result(stmt) == 0)
   {    {
     values= fetch_row(stmt);      values= fetch_row(stmt);
   
     if (stmt->fix_fields)      if (stmt->fix_fields)
     {      {
       values= (*stmt->fix_fields)(stmt,values);        values= (*stmt->fix_fields)(stmt,values);
     }      }
   }    }
   
   assert(values);    assert(values);
   
   /* Then current result is out params */     /* Then current result is out params */ 
   stmt->out_params_state= 2;    stmt->out_params_state= 2;
   
   if (values != NULL && got_out_parameters(stmt))    if (values != NULL && got_out_parameters(stmt))
   {    {
     for (i= 0; i < myodbc_min(stmt->ipd->count, stmt->apd->count); ++i)      for (i= 0; i < myodbc_min(stmt->ipd->count, stmt->apd->count); ++i)
     {      {
       /* Making bit field look "normally" */   
       if (stmt->result_bind[counter].buffer_type == MYSQL_TYPE_BIT)  
       {  
         MYSQL_FIELD *field= mysql_fetch_field_direct(stmt->result, counter);  
         unsigned long long numeric;  
   
         assert(field->type == MYSQL_TYPE_BIT);  
         /* terminating with NULL */   
         values[counter][*stmt->result_bind[counter].length]= '\0';  
         numeric= strtoull(values[counter], NULL, 10);  
   
         *stmt->result_bind[counter].length= (field->length+7)/8;  
         numeric2binary(values[counter], numeric,  
                       *stmt->result_bind[counter].length);  
   
       }  
   
       iprec= desc_get_rec(stmt->ipd, i, FALSE);        iprec= desc_get_rec(stmt->ipd, i, FALSE);
       aprec= desc_get_rec(stmt->apd, i, FALSE);        aprec= desc_get_rec(stmt->apd, i, FALSE);
       assert(iprec && aprec);        assert(iprec && aprec);
   
       if ((iprec->parameter_type == SQL_PARAM_INPUT_OUTPUT        if ((iprec->parameter_type == SQL_PARAM_INPUT_OUTPUT
       || iprec->parameter_type == SQL_PARAM_OUTPUT))        || iprec->parameter_type == SQL_PARAM_OUTPUT))
       {        {
            /* Making bit field look "normally" */ 
             if (stmt->result_bind[counter].buffer_type == MYSQL_TYPE_BIT)
             {
                 MYSQL_FIELD *field= mysql_fetch_field_direct(stmt->result, counter);
                 unsigned long long numeric;
              
                 assert(field->type == MYSQL_TYPE_BIT);
                 /* terminating with NULL */ 
                 values[counter][*stmt->result_bind[counter].length]= '\0';
                 numeric= strtoull(values[counter], NULL, 10);
              
                 *stmt->result_bind[counter].length= (field->length+7)/8;
                 numeric2binary(values[counter], numeric,
                             *stmt->result_bind[counter].length);
              
             }
   
         if (aprec->data_ptr)          if (aprec->data_ptr)
         {          {
           unsigned long length= *stmt->result_bind[counter].length;            unsigned long length= *stmt->result_bind[counter].length;
           char *target= NULL;            char *target= NULL;
           SQLLEN *octet_length_ptr= NULL;            SQLLEN *octet_length_ptr= NULL;
           SQLLEN *indicator_ptr= NULL;            SQLLEN *indicator_ptr= NULL;
           SQLINTEGER default_size;            SQLINTEGER default_size;
   
           if (aprec->octet_length_ptr)            if (aprec->octet_length_ptr)
           {            {
             octet_length_ptr= ptr_offset_adjust(aprec->octet_length_ptr,              octet_length_ptr= ptr_offset_adjust(aprec->octet_length_ptr,
                                           stmt->apd->bind_offset_ptr,                                            stmt->apd->bind_offset_ptr,
                                           stmt->apd->bind_type,                                            stmt->apd->bind_type,
                                           sizeof(SQLLEN), 0);                                            sizeof(SQLLEN), 0);
           }            }
   
           indicator_ptr= ptr_offset_adjust(aprec->indicator_ptr,            indicator_ptr= ptr_offset_adjust(aprec->indicator_ptr,
                                        stmt->apd->bind_offset_ptr,                                         stmt->apd->bind_offset_ptr,
                                        stmt->apd->bind_type,                                         stmt->apd->bind_type,
                                        sizeof(SQLLEN), 0);                                         sizeof(SQLLEN), 0);
   
           default_size= bind_length(aprec->concise_type,            default_size= bind_length(aprec->concise_type,
                                     aprec->octet_length);                                      aprec->octet_length);
           target= ptr_offset_adjust(aprec->data_ptr, stmt->apd->bind_offset_ptr,            target= ptr_offset_adjust(aprec->data_ptr, stmt->apd->bind_offset_ptr,
                                 stmt->apd->bind_type, default_size, 0);                                  stmt->apd->bind_type, default_size, 0);
   
           reset_getdata_position(stmt);            reset_getdata_position(stmt);
   
           sql_get_data(stmt, aprec->concise_type, counter,            sql_get_data(stmt, aprec->concise_type, counter,
                        target, aprec->octet_length, indicator_ptr,                         target, aprec->octet_length, indicator_ptr,
                        values[counter], length, aprec);                         values[counter], length, aprec);
   
           /* TODO: solve that globally */             /* TODO: solve that globally */ 
           if (octet_length_ptr != NULL && indicator_ptr != NULL            if (octet_length_ptr != NULL && indicator_ptr != NULL
             && octet_length_ptr != indicator_ptr              && octet_length_ptr != indicator_ptr
             && *indicator_ptr != SQL_NULL_DATA)              && *indicator_ptr != SQL_NULL_DATA)
           {            {
             *octet_length_ptr= *indicator_ptr;              *octet_length_ptr= *indicator_ptr;
           }            }
         }          }
         ++counter;          ++counter;
       }        }
     }      }
   }    }
   /* This MAGICAL fetch is required */     /* This MAGICAL fetch is required */ 
   mysql_stmt_fetch(stmt->ssps);    mysql_stmt_fetch(stmt->ssps);
   
   return TRUE;    return TRUE;
 }  }
 return FALSE;  return FALSE;
} }
   
   
int ssps_get_result(STMT *stmt) int ssps_get_result(STMT *stmt)
{ {
 if (stmt->result)  if (stmt->result)
 {  {
   if (!if_forward_cache(stmt))    if (!if_forward_cache(stmt))
   {    {
     return mysql_stmt_store_result(stmt->ssps);      return mysql_stmt_store_result(stmt->ssps);
   }    }
   
 }  }
     
 return 0;  return 0;
} }
   
   
void free_result_bind(STMT *stmt) void free_result_bind(STMT *stmt)
{ {
 if (stmt->result_bind != NULL)  if (stmt->result_bind != NULL)
 {  {
   int i, field_cnt= field_count(stmt);    int i, field_cnt= field_count(stmt);
   
   x_free(stmt->result_bind[0].is_null);    x_free(stmt->result_bind[0].is_null);
   x_free(stmt->result_bind[0].length);    x_free(stmt->result_bind[0].length);
   x_free(stmt->result_bind[0].error);    x_free(stmt->result_bind[0].error);
   
   /* buffer was allocated for each column */     /* buffer was allocated for each column */ 
   for (i= 0; i < field_cnt; i++)    for (i= 0; i < field_cnt; i++)
   {    {
     x_free(stmt->result_bind[i].buffer);      x_free(stmt->result_bind[i].buffer);
   }    }
   
   x_free(stmt->result_bind);    x_free(stmt->result_bind);
   stmt->result_bind= 0;    stmt->result_bind= 0;
   
   x_free(stmt->array);    x_free(stmt->array);
   stmt->array= 0;    stmt->array= 0;
 }  }
} }
   
   
void ssps_close(STMT *stmt) void ssps_close(STMT *stmt)
{ {
 if (stmt->ssps != NULL)  if (stmt->ssps != NULL)
 {  {
   free_result_bind(stmt);    free_result_bind(stmt);
   
   if (mysql_stmt_close(stmt->ssps) != '\0')    if (mysql_stmt_close(stmt->ssps) != '\0')
     assert(!"Could not close stmt");      assert(!"Could not close stmt");
   
   stmt->ssps= NULL;    stmt->ssps= NULL;
 }  }
} }
   
   
/* The structure and following allocation function are borrowed from c/c++ and adopted */  /* The structure and following allocation function are borrowed from c/c++ and adopted */ 
typedef struct tagBST typedef struct tagBST
{  char * buffer; {  char * buffer;
 size_t size;  size_t size;
 enum enum_field_types type;  enum enum_field_types type;
} st_buffer_size_type; } st_buffer_size_type;
   
   
/* {{{ allocate_buffer_for_field() -I- */  /* {{{ allocate_buffer_for_field() -I- */ 
static st_buffer_size_type static st_buffer_size_type
allocate_buffer_for_field(const MYSQL_FIELD * const field, BOOL outparams) allocate_buffer_for_field(const MYSQL_FIELD * const field, BOOL outparams)
{ {
 st_buffer_size_type result= {NULL, 0, field->type};  st_buffer_size_type result= {NULL, 0, field->type};
   
 switch (field->type) {  switch (field->type) {
   case MYSQL_TYPE_NULL:    case MYSQL_TYPE_NULL:
     break;      break;
   
   case MYSQL_TYPE_TINY:    case MYSQL_TYPE_TINY:
     result.size= 1;      result.size= 1;
     break;      break;
   
   case MYSQL_TYPE_SHORT:    case MYSQL_TYPE_SHORT:
     result.size=2;      result.size=2;
     break;      break;
   
   case MYSQL_TYPE_INT24:    case MYSQL_TYPE_INT24:
   case MYSQL_TYPE_LONG:    case MYSQL_TYPE_LONG:
   case MYSQL_TYPE_FLOAT:    case MYSQL_TYPE_FLOAT:
     result.size=4;      result.size=4;
     break;      break;
   
   case MYSQL_TYPE_DOUBLE:    case MYSQL_TYPE_DOUBLE:
   case MYSQL_TYPE_LONGLONG:    case MYSQL_TYPE_LONGLONG:
     result.size=8;      result.size=8;
     break;      break;
   
   case MYSQL_TYPE_YEAR:    case MYSQL_TYPE_YEAR:
     result.size=2;      result.size=2;
     break;      break;
   
   case MYSQL_TYPE_TIMESTAMP:    case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_DATE:    case MYSQL_TYPE_DATE:
   case MYSQL_TYPE_TIME:    case MYSQL_TYPE_TIME:
   case MYSQL_TYPE_DATETIME:    case MYSQL_TYPE_DATETIME:
     result.size=sizeof(MYSQL_TIME);      result.size=sizeof(MYSQL_TIME);
     break;      break;
   
   case MYSQL_TYPE_TINY_BLOB:    case MYSQL_TYPE_TINY_BLOB:
   case MYSQL_TYPE_MEDIUM_BLOB:    case MYSQL_TYPE_MEDIUM_BLOB:
   case MYSQL_TYPE_LONG_BLOB:    case MYSQL_TYPE_LONG_BLOB:
   case MYSQL_TYPE_BLOB:    case MYSQL_TYPE_BLOB:
   case MYSQL_TYPE_STRING:    case MYSQL_TYPE_STRING:
   case MYSQL_TYPE_VAR_STRING:    case MYSQL_TYPE_VAR_STRING:
     /* We will get length with fetch and then fetch column */       /* We will get length with fetch and then fetch column */ 
     if (field->length > 0 && field->length < 1025)      if (field->length > 0 && field->length < 1025)
       result.size= field->length + 1;        result.size= field->length + 1;
     break;      break;
   
   case MYSQL_TYPE_DECIMAL:    case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:    case MYSQL_TYPE_NEWDECIMAL:
     result.size=64;      result.size=64;
     break;      break;
   
   #if A1    #if A1
   case MYSQL_TYPE_TIMESTAMP:    case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_YEAR:    case MYSQL_TYPE_YEAR:
     result.size= 10;      result.size= 10;
     break;      break;
   #endif    #endif
   #if A0    #if A0
   // There two are not sent over the wire    // There two are not sent over the wire
   case MYSQL_TYPE_ENUM:    case MYSQL_TYPE_ENUM:
   case MYSQL_TYPE_SET:    case MYSQL_TYPE_SET:
   #endif    #endif
   case MYSQL_TYPE_BIT:    case MYSQL_TYPE_BIT:
     result.type= MYSQL_TYPE_BIT;      result.type= MYSQL_TYPE_BIT;
     if (outparams)      if (outparams)
     {      {
       /* For out params we surprisingly get it as string representation of a        /* For out params we surprisingly get it as string representation of a
          number representing those bits. Allocating buffer to accommodate           number representing those bits. Allocating buffer to accommodate
          largest string possible - 8 byte number + NULL terminator.           largest string possible - 8 byte number + NULL terminator.
          We will need to terminate the string to convert it to a number */            We will need to terminate the string to convert it to a number */ 
       result.size= 30;        result.size= 30;
     }      }
     else      else
     {      {
       result.size= (field->length + 7)/8;        result.size= (field->length + 7)/8;
     }      }
   
     break;      break;
   case MYSQL_TYPE_GEOMETRY:    case MYSQL_TYPE_GEOMETRY:
   default:    default:
     /* Error? */       /* Error? */ 
     1;      1;
 }  }
   
 if (result.size > 0)  if (result.size > 0)
 {  {
   result.buffer=my_malloc(result.size, MYF(0));    result.buffer=my_malloc(result.size, MYF(0));
 }  }
   
 return result;  return result;
} }
/* }}} */  /* }}} */ 
   
   
static MYSQL_ROW fetch_varlength_columns(STMT *stmt, MYSQL_ROW columns) static MYSQL_ROW fetch_varlength_columns(STMT *stmt, MYSQL_ROW columns)
{ {
 const unsigned int  num_fields= field_count(stmt);  const unsigned int  num_fields= field_count(stmt);
 unsigned int i;  unsigned int i;
   
 for (i= 0; i < num_fields; ++i)  for (i= 0; i < num_fields; ++i)
 {  {
   if (stmt->result_bind[i].buffer == NULL)    if (stmt->result_bind[i].buffer == NULL)
   {    {
     if (stmt->lengths[i] < *stmt->result_bind[i].length)      if (stmt->lengths[i] < *stmt->result_bind[i].length)
     {      {
       /* TODO Realloc error proc */         /* TODO Realloc error proc */ 
       stmt->array[i] = my_realloc(stmt->array[i], *stmt->result_bind[i].length,        stmt->array[i] = my_realloc(stmt->array[i], *stmt->result_bind[i].length,
         MYF(MY_ALLOW_ZERO_PTR));          MYF(MY_ALLOW_ZERO_PTR));
       stmt->lengths[i]= *stmt->result_bind[i].length;        stmt->lengths[i]= *stmt->result_bind[i].length;
     }      }
   
     stmt->result_bind[i].buffer= stmt->array[i];      stmt->result_bind[i].buffer= stmt->array[i];
     stmt->result_bind[i].buffer_length= stmt->lengths[i];      stmt->result_bind[i].buffer_length= stmt->lengths[i];
   
     mysql_stmt_fetch_column(stmt->ssps, &stmt->result_bind[i], i, 0);      mysql_stmt_fetch_column(stmt->ssps, &stmt->result_bind[i], i, 0);
   }    }
 }  }
   
 fill_ird_data_lengths(stmt->ird, stmt->result_bind[0].length,  fill_ird_data_lengths(stmt->ird, stmt->result_bind[0].length,
                                 stmt->result->field_count);                                  stmt->result->field_count);
   
 return stmt->array;  return stmt->array;
} }
   
   
int ssps_bind_result(STMT *stmt) int ssps_bind_result(STMT *stmt)
{ {
 const unsigned int  num_fields= field_count(stmt);  const unsigned int  num_fields= field_count(stmt);
 unsigned int        i;  unsigned int        i;
   
 if (num_fields == 0)  if (num_fields == 0)
 {  {
   return 0;    return 0;
 }  }
   
 if (stmt->result_bind)  if (stmt->result_bind)
 {  {
   /* We have fields requiring to read real length first */     /* We have fields requiring to read real length first */ 
   if (stmt->fix_fields != NULL)    if (stmt->fix_fields != NULL)
   {    {
     for (i=0; i < num_fields; ++i)      for (i=0; i < num_fields; ++i)
     {      {
       /* length marks such fields */         /* length marks such fields */ 
       if (stmt->lengths[i] > 0)        if (stmt->lengths[i] > 0)
       {        {
         /* Resetting buffer and buffer_length for those fields */           /* Resetting buffer and buffer_length for those fields */ 
         stmt->result_bind[i].buffer       = 0;          stmt->result_bind[i].buffer       = 0;
         stmt->result_bind[i].buffer_length= 0;          stmt->result_bind[i].buffer_length= 0;
       }        }
     }      }
   }    }
 }  }
 else  else
 {  {
   my_bool       *is_null= my_malloc(sizeof(my_bool)*num_fields,    my_bool       *is_null= my_malloc(sizeof(my_bool)*num_fields,
                                     MYF(MY_ZEROFILL));                                      MYF(MY_ZEROFILL));
   my_bool       *err=     my_malloc(sizeof(my_bool)*num_fields,    my_bool       *err=     my_malloc(sizeof(my_bool)*num_fields,
                                     MYF(MY_ZEROFILL));                                      MYF(MY_ZEROFILL));
   unsigned long *len=     my_malloc(sizeof(unsigned long)*num_fields,    unsigned long *len=     my_malloc(sizeof(unsigned long)*num_fields,
                                     MYF(MY_ZEROFILL));                                      MYF(MY_ZEROFILL));
   
   /*TODO care about memory allocation errors */     /*TODO care about memory allocation errors */ 
   stmt->result_bind=  (MYSQL_BIND*)my_malloc(sizeof(MYSQL_BIND)*num_fields,    stmt->result_bind=  (MYSQL_BIND*)my_malloc(sizeof(MYSQL_BIND)*num_fields,
                                             MYF(MY_ZEROFILL));                                              MYF(MY_ZEROFILL));
   stmt->array=        (MYSQL_ROW)my_malloc(sizeof(char*)*num_fields,    stmt->array=        (MYSQL_ROW)my_malloc(sizeof(char*)*num_fields,
                                             MYF(MY_ZEROFILL));                                              MYF(MY_ZEROFILL));
   
   for (i= 0; i < num_fields; ++i)    for (i= 0; i < num_fields; ++i)
   {    {
     MYSQL_FIELD    *field= mysql_fetch_field_direct(stmt->result, i);      MYSQL_FIELD    *field= mysql_fetch_field_direct(stmt->result, i);
     st_buffer_size_type p= allocate_buffer_for_field(field,      st_buffer_size_type p= allocate_buffer_for_field(field,
                                                     IS_PS_OUT_PARAMS(stmt));                                                      IS_PS_OUT_PARAMS(stmt));
   
     stmt->result_bind[i].buffer_type  = p.type;      stmt->result_bind[i].buffer_type  = p.type;
       stmt->result_bind[i].buffer       = p.buffer;        stmt->result_bind[i].buffer       = p.buffer;
       stmt->result_bind[i].buffer_length= (unsigned long)p.size;        stmt->result_bind[i].buffer_length= (unsigned long)p.size;
       stmt->result_bind[i].length       = &len[i];        stmt->result_bind[i].length       = &len[i];
       stmt->result_bind[i].is_null      = &is_null[i];        stmt->result_bind[i].is_null      = &is_null[i];
       stmt->result_bind[i].error        = &err[i];        stmt->result_bind[i].error        = &err[i];
     stmt->result_bind[i].is_unsigned  = (field->flags & UNSIGNED_FLAG)? 1: 0;      stmt->result_bind[i].is_unsigned  = (field->flags & UNSIGNED_FLAG)? 1: 0;
   
     stmt->array[i]= p.buffer;      stmt->array[i]= p.buffer;
   
     /* Marking that there are columns that will require buffer (re) allocating      /* Marking that there are columns that will require buffer (re) allocating
      */        */ 
     if (  stmt->result_bind[i].buffer       == 0      if (  stmt->result_bind[i].buffer       == 0
       &&  stmt->result_bind[i].buffer_type  != MYSQL_TYPE_NULL)        &&  stmt->result_bind[i].buffer_type  != MYSQL_TYPE_NULL)
     {      {
       stmt->fix_fields= fetch_varlength_columns;        stmt->fix_fields= fetch_varlength_columns;
                 
       /* Need to alloc it only once*/         /* Need to alloc it only once*/ 
       if (stmt->lengths == NULL)        if (stmt->lengths == NULL)
       {        {
         stmt->lengths= my_malloc(sizeof(unsigned long)*num_fields, MYF(MY_ZEROFILL));          stmt->lengths= my_malloc(sizeof(unsigned long)*num_fields, MYF(MY_ZEROFILL));
       }        }
       /* Buffer of initial length? */         /* Buffer of initial length? */ 
     }      }
   }    }
         
   return mysql_stmt_bind_result(stmt->ssps, stmt->result_bind);    return mysql_stmt_bind_result(stmt->ssps, stmt->result_bind);
 }  }
   
 return 0;  return 0;
} }
   
   
BOOL ssps_0buffers_truncated_only(STMT *stmt) BOOL ssps_0buffers_truncated_only(STMT *stmt)
{ {
 if (stmt->fix_fields == NULL)  if (stmt->fix_fields == NULL)
 {  {
   /* That is enough to tell that not */     /* That is enough to tell that not */ 
   return FALSE;    return FALSE;
 }  }
 else  else
 {  {
   const unsigned int  num_fields= field_count(stmt);    const unsigned int  num_fields= field_count(stmt);
   unsigned int i;    unsigned int i;
   
   for (i= 0; i < num_fields; ++i)    for (i= 0; i < num_fields; ++i)
   {    {
     if (*stmt->result_bind[i].error != 0      if (*stmt->result_bind[i].error != 0
       && stmt->result_bind[i].buffer_length > 0        && stmt->result_bind[i].buffer_length > 0
       && stmt->result_bind[i].buffer != NULL)        && stmt->result_bind[i].buffer != NULL)
     {      {
       return FALSE;        return FALSE;
     }      }
   }    }
 }  }
   
 return TRUE;  return TRUE;
} }
   
   
/* --------------- Type conversion functions -------------- */  /* --------------- Type conversion functions -------------- */ 
   
#define ALLOC_IFNULL(buff,size) ((buff)==NULL?(char*)my_malloc(size,MYF(0)):buff) #define ALLOC_IFNULL(buff,size) ((buff)==NULL?(char*)my_malloc(size,MYF(0)):buff)
   
/* {{{ Prepared ResultSet ssps_get_string() -I- */  /* {{{ Prepared ResultSet ssps_get_string() -I- */ 
/* caller should care to make buffer long enough, /* caller should care to make buffer long enough,
  if buffer is not null function allocates memory and that is caller's duty to clean it   if buffer is not null function allocates memory and that is caller's duty to clean it
*/  */ 
char * ssps_get_string(STMT *stmt, ulong column_number, char *value, ulong *length, char * ssps_get_string(STMT *stmt, ulong column_number, char *value, ulong *length,
                      char * buffer)                       char * buffer)
{ {
 MYSQL_BIND *col_rbind= &stmt->result_bind[column_number];  MYSQL_BIND *col_rbind= &stmt->result_bind[column_number];
   
 if (*col_rbind->is_null)  if (*col_rbind->is_null)
 {  {
   return NULL;    return NULL;
 }  }
   
 switch (col_rbind->buffer_type)  switch (col_rbind->buffer_type)
 {  {
   case MYSQL_TYPE_TIMESTAMP:    case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_DATETIME:    case MYSQL_TYPE_DATETIME:
   {    {
     MYSQL_TIME * t = (MYSQL_TIME *)(col_rbind->buffer);      MYSQL_TIME * t = (MYSQL_TIME *)(col_rbind->buffer);
   
     buffer= ALLOC_IFNULL(buffer, 30);      buffer= ALLOC_IFNULL(buffer, 30);
     snprintf(buffer, 20, "%04u-%02u-%02u %02u:%02u:%02u",      snprintf(buffer, 20, "%04u-%02u-%02u %02u:%02u:%02u",
                     t->year, t->month, t->day, t->hour, t->minute, t->second);                      t->year, t->month, t->day, t->hour, t->minute, t->second);
   
     *length= 19;      *length= 19;
   
     if (t->second_part > 0)      if (t->second_part > 0)
     {      {
       snprintf(buffer+*length, 8, ".%06lu", t->second_part);        snprintf(buffer+*length, 8, ".%06lu", t->second_part);
       *length= 26;        *length= 26;
     }      }
   
     return buffer;      return buffer;
   }    }
   case MYSQL_TYPE_DATE:    case MYSQL_TYPE_DATE:
   {    {
     MYSQL_TIME * t = (MYSQL_TIME *)(col_rbind->buffer);      MYSQL_TIME * t = (MYSQL_TIME *)(col_rbind->buffer);
   
     buffer= ALLOC_IFNULL(buffer, 12);      buffer= ALLOC_IFNULL(buffer, 12);
     snprintf(buffer, 11, "%04u-%02u-%02u", t->year, t->month, t->day);      snprintf(buffer, 11, "%04u-%02u-%02u", t->year, t->month, t->day);
     *length= 10;      *length= 10;
   
     return buffer;      return buffer;
   }    }
   case MYSQL_TYPE_TIME:    case MYSQL_TYPE_TIME:
   {    {
     MYSQL_TIME * t = (MYSQL_TIME *)(col_rbind->buffer);      MYSQL_TIME * t = (MYSQL_TIME *)(col_rbind->buffer);
   
     buffer= ALLOC_IFNULL(buffer, 20);      buffer= ALLOC_IFNULL(buffer, 20);
     snprintf(buffer, 10, "%s%02u:%02u:%02u", t->neg? "-":"", t->hour,      snprintf(buffer, 10, "%s%02u:%02u:%02u", t->neg? "-":"", t->hour,
                                             t->minute, t->second);                                              t->minute, t->second);
     *length= t->neg ? 9 : 8;      *length= t->neg ? 9 : 8;
   
     if (t->second_part > 0)      if (t->second_part > 0)
     {      {
       snprintf(buffer+*length, 8, ".%06lu", t->second_part);        snprintf(buffer+*length, 8, ".%06lu", t->second_part);
       *length+= 7;        *length+= 7;
     }      }
     return buffer;      return buffer;
   }    }
   case MYSQL_TYPE_BIT:    case MYSQL_TYPE_BIT:
   case MYSQL_TYPE_YEAR:  // fetched as a SMALLINT    case MYSQL_TYPE_YEAR:  // fetched as a SMALLINT
   case MYSQL_TYPE_TINY:    case MYSQL_TYPE_TINY:
   case MYSQL_TYPE_SHORT:    case MYSQL_TYPE_SHORT:
   case MYSQL_TYPE_INT24:    case MYSQL_TYPE_INT24:
   case MYSQL_TYPE_LONG:    case MYSQL_TYPE_LONG:
   case MYSQL_TYPE_LONGLONG:    case MYSQL_TYPE_LONGLONG:
   {    {
     buffer= ALLOC_IFNULL(buffer, 30);      buffer= ALLOC_IFNULL(buffer, 30);
   
     if (col_rbind->is_unsigned)      if (col_rbind->is_unsigned)
     {      {
       my_ul_to_a(buffer, 29,        my_ul_to_a(buffer, 29,
         (unsigned long long)ssps_get_int64(stmt, column_number, value, *length));          (unsigned long long)ssps_get_int64(stmt, column_number, value, *length));
     }      }
     else      else
     {      {
       my_l_to_a(buffer, 29,        my_l_to_a(buffer, 29,
                 ssps_get_int64(stmt, column_number, value, *length));                  ssps_get_int64(stmt, column_number, value, *length));
     }      }
   
     *length= strlen(buffer);      *length= strlen(buffer);
     return buffer;      return buffer;
   }    }
   case MYSQL_TYPE_FLOAT:    case MYSQL_TYPE_FLOAT:
   case MYSQL_TYPE_DOUBLE:    case MYSQL_TYPE_DOUBLE:
   {    {
     buffer= ALLOC_IFNULL(buffer, 50);      buffer= ALLOC_IFNULL(buffer, 50);
     my_f_to_a(buffer, 49, ssps_get_double(stmt, column_number, value,      my_f_to_a(buffer, 49, ssps_get_double(stmt, column_number, value,
                                           *length));                                            *length));
   
     *length= strlen(buffer);      *length= strlen(buffer);
     return buffer;      return buffer;
   }    }
   
   case MYSQL_TYPE_DECIMAL:    case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:    case MYSQL_TYPE_NEWDECIMAL:
   case MYSQL_TYPE_STRING:    case MYSQL_TYPE_STRING:
   case MYSQL_TYPE_LONG_BLOB:    case MYSQL_TYPE_LONG_BLOB:
   case MYSQL_TYPE_BLOB:    case MYSQL_TYPE_BLOB:
   case MYSQL_TYPE_VARCHAR:    case MYSQL_TYPE_VARCHAR:
   case MYSQL_TYPE_VAR_STRING:    case MYSQL_TYPE_VAR_STRING:
     *length= *col_rbind->length;      *length= *col_rbind->length;
     return (char *)(col_rbind->buffer);      return (char *)(col_rbind->buffer);
   default:    default:
     break;      break;
   // TODO : Geometry? default ?    // TODO : Geometry? default ?
 }  }
   
 /* Basically should be prevented by earlied tests of  /* Basically should be prevented by earlied tests of
   conversion possibility */     conversion possibility */ 
 return col_rbind->buffer;  return col_rbind->buffer;
} }
/* }}} */  /* }}} */ 
   
   
long double ssps_get_double(STMT *stmt, ulong column_number, char *value, ulong length) long double ssps_get_double(STMT *stmt, ulong column_number, char *value, ulong length)
{ {
 MYSQL_BIND *col_rbind= &stmt->result_bind[column_number];  MYSQL_BIND *col_rbind= &stmt->result_bind[column_number];
   
 if (*col_rbind->is_null)  if (*col_rbind->is_null)
 {  {
   return 0.0;    return 0.0;
 }  }
   
 switch (col_rbind->buffer_type) {  switch (col_rbind->buffer_type) {
   case MYSQL_TYPE_BIT:    case MYSQL_TYPE_BIT:
   case MYSQL_TYPE_YEAR:  // fetched as a SMALLINT    case MYSQL_TYPE_YEAR:  // fetched as a SMALLINT
   case MYSQL_TYPE_TINY:    case MYSQL_TYPE_TINY:
   case MYSQL_TYPE_SHORT:    case MYSQL_TYPE_SHORT:
   case MYSQL_TYPE_INT24:    case MYSQL_TYPE_INT24:
   case MYSQL_TYPE_LONG:    case MYSQL_TYPE_LONG:
   case MYSQL_TYPE_LONGLONG:    case MYSQL_TYPE_LONGLONG:
   {    {
     long double ret;      long double ret;
     BOOL is_it_unsigned = col_rbind->is_unsigned != 0;      BOOL is_it_unsigned = col_rbind->is_unsigned != 0;
   
     if (is_it_unsigned)      if (is_it_unsigned)
     {      {
       unsigned long long ival = (unsigned long long)ssps_get_int64(stmt, column_number, value, length);        unsigned long long ival = (unsigned long long)ssps_get_int64(stmt, column_number, value, length);
       ret = (long double)(ival);        ret = (long double)(ival);
     }      }
     else      else
     {      {
       long long ival = ssps_get_int64(stmt, column_number, value, length);        long long ival = ssps_get_int64(stmt, column_number, value, length);
       ret = (long double)(ival);        ret = (long double)(ival);
     }      }
   
     return ret;      return ret;
   }    }
   case MYSQL_TYPE_DECIMAL:    case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:    case MYSQL_TYPE_NEWDECIMAL:
   case MYSQL_TYPE_TIMESTAMP:    case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_DATETIME:    case MYSQL_TYPE_DATETIME:
   case MYSQL_TYPE_DATE:    case MYSQL_TYPE_DATE:
   case MYSQL_TYPE_TIME:    case MYSQL_TYPE_TIME:
   case MYSQL_TYPE_STRING:    case MYSQL_TYPE_STRING:
   case MYSQL_TYPE_BLOB:    case MYSQL_TYPE_BLOB:
   case MYSQL_TYPE_VARCHAR:    case MYSQL_TYPE_VARCHAR:
   case MYSQL_TYPE_VAR_STRING:    case MYSQL_TYPE_VAR_STRING:
   {    {
     char buf[50];      char buf[50];
     long double ret = strtold(ssps_get_string(stmt, column_number, value,      long double ret = strtold(ssps_get_string(stmt, column_number, value,
                                               &length, buf), NULL);                                                &length, buf), NULL);
     return ret;      return ret;
   }    }
   
   case MYSQL_TYPE_FLOAT:    case MYSQL_TYPE_FLOAT:
   {    {
     long double ret = !*col_rbind->is_null? *(float *)(col_rbind->buffer):0.;      long double ret = !*col_rbind->is_null? *(float *)(col_rbind->buffer):0.;
     return ret;      return ret;
   }    }
   
   case MYSQL_TYPE_DOUBLE:    case MYSQL_TYPE_DOUBLE:
   {    {
     long double ret = !*col_rbind->is_null? *(double *)(col_rbind->buffer):0.;      long double ret = !*col_rbind->is_null? *(double *)(col_rbind->buffer):0.;
     return ret;      return ret;
   }    }
   
   /* TODO : Geometry? default ? */     /* TODO : Geometry? default ? */ 
 }  }
     
 /* Basically should be prevented by earlied tests of  /* Basically should be prevented by earlied tests of
    conversion possibility */      conversion possibility */ 
 return .0;  return .0;
} }
   
   
/* {{{ Prepared ResultSet ssps_get_int64() -I- */  /* {{{ Prepared ResultSet ssps_get_int64() -I- */ 
long long ssps_get_int64(STMT *stmt, ulong column_number, char *value, ulong length) long long ssps_get_int64(STMT *stmt, ulong column_number, char *value, ulong length)
{ {
 MYSQL_BIND *col_rbind= &stmt->result_bind[column_number];  MYSQL_BIND *col_rbind= &stmt->result_bind[column_number];
   
 switch (col_rbind->buffer_type)  switch (col_rbind->buffer_type)
 {  {
   case MYSQL_TYPE_FLOAT:    case MYSQL_TYPE_FLOAT:
   case MYSQL_TYPE_DOUBLE:    case MYSQL_TYPE_DOUBLE:
   
     return (long long)ssps_get_double(stmt, column_number, value, length);      return (long long)ssps_get_double(stmt, column_number, value, length);
   
   case MYSQL_TYPE_DECIMAL:    case MYSQL_TYPE_DECIMAL:
   case MYSQL_TYPE_NEWDECIMAL:    case MYSQL_TYPE_NEWDECIMAL:
   case MYSQL_TYPE_TIMESTAMP:    case MYSQL_TYPE_TIMESTAMP:
   case MYSQL_TYPE_DATETIME:    case MYSQL_TYPE_DATETIME:
   case MYSQL_TYPE_DATE:    case MYSQL_TYPE_DATE:
   case MYSQL_TYPE_TIME:    case MYSQL_TYPE_TIME:
   case MYSQL_TYPE_STRING:    case MYSQL_TYPE_STRING:
   case MYSQL_TYPE_BLOB:    case MYSQL_TYPE_BLOB:
   case MYSQL_TYPE_VARCHAR:    case MYSQL_TYPE_VARCHAR:
   case MYSQL_TYPE_VAR_STRING:    case MYSQL_TYPE_VAR_STRING:
   {    {
     char buf[30];      char buf[30];
     return strtoll(ssps_get_string(stmt, column_number, value, &length, buf),      return strtoll(ssps_get_string(stmt, column_number, value, &length, buf),
                     NULL, 10);                      NULL, 10);
   }    }
   case MYSQL_TYPE_BIT:    case MYSQL_TYPE_BIT:
   {    {
     long long uval = 0;      long long uval = 0;
     /* This length is in bytes, on the contrary to what can be seen in mysql_resultset.cpp where the Meta is used */       /* This length is in bytes, on the contrary to what can be seen in mysql_resultset.cpp where the Meta is used */ 
     return binary2numeric(&uval, col_rbind->buffer, *col_rbind->length);      return binary2numeric(&uval, col_rbind->buffer, *col_rbind->length);
   }    }
   
   case MYSQL_TYPE_YEAR:  // fetched as a SMALLINT    case MYSQL_TYPE_YEAR:  // fetched as a SMALLINT
   case MYSQL_TYPE_TINY:    case MYSQL_TYPE_TINY:
   case MYSQL_TYPE_SHORT:    case MYSQL_TYPE_SHORT:
   case MYSQL_TYPE_INT24:    case MYSQL_TYPE_INT24:
   case MYSQL_TYPE_LONG:    case MYSQL_TYPE_LONG:
   case MYSQL_TYPE_LONGLONG:    case MYSQL_TYPE_LONGLONG:
   {    {
     // MYSQL_TYPE_YEAR is fetched as a SMALLINT, thus should not be in the switch      // MYSQL_TYPE_YEAR is fetched as a SMALLINT, thus should not be in the switch
     long long ret;      long long ret;
     BOOL is_it_null = *col_rbind->is_null != 0;      BOOL is_it_null = *col_rbind->is_null != 0;
     BOOL is_it_unsigned = col_rbind->is_unsigned != 0;      BOOL is_it_unsigned = col_rbind->is_unsigned != 0;
   
     switch (col_rbind->buffer_length)      switch (col_rbind->buffer_length)
     {      {
       case 1:        case 1:
         if (is_it_unsigned)          if (is_it_unsigned)
         {          {
           ret = !is_it_null? ((char *)col_rbind->buffer)[0]:0;            ret = !is_it_null? ((char *)col_rbind->buffer)[0]:0;
         }          }
         else          else
         {          {
           ret = !is_it_null? *(char *)(col_rbind->buffer):0;            ret = !is_it_null? *(char *)(col_rbind->buffer):0;
         }          }
         break;          break;
   
       case 2:        case 2:
   
         if (is_it_unsigned)          if (is_it_unsigned)
         {          {
           ret = !is_it_null? *(unsigned short *)(col_rbind->buffer):0;            ret = !is_it_null? *(unsigned short *)(col_rbind->buffer):0;
         }          }
         else          else
         {          {
           ret = !is_it_null? *(short *)(col_rbind->buffer):0;            ret = !is_it_null? *(short *)(col_rbind->buffer):0;
         }          }
         break;          break;
   
       case 4:        case 4:
   
         if (is_it_unsigned)          if (is_it_unsigned)
         {          {
           ret =  !is_it_null? *(unsigned int *)(col_rbind->buffer):0;            ret =  !is_it_null? *(unsigned int *)(col_rbind->buffer):0;
         }          }
         else          else
         {          {
           ret =  !is_it_null? *(int *)(col_rbind->buffer):0;            ret =  !is_it_null? *(int *)(col_rbind->buffer):0;
         }          }
         break;          break;
   
       case 8:        case 8:
   
         if (is_it_unsigned)          if (is_it_unsigned)
         {          {
           ret =  !is_it_null? *(unsigned long long *)col_rbind->buffer:0;            ret =  !is_it_null? *(unsigned long long *)col_rbind->buffer:0;
   
#if WE_WANT_TO_SEE_MORE_FAILURES_IN_UNIT_RESULTSET #if WE_WANT_TO_SEE_MORE_FAILURES_IN_UNIT_RESULTSET
           if (cutTooBig &&            if (cutTooBig &&
             ret &&              ret &&
             *(unsigned long long *)(col_rbind->buffer) > UL64(9223372036854775807))              *(unsigned long long *)(col_rbind->buffer) > UL64(9223372036854775807))
           {            {
             ret = UL64(9223372036854775807);              ret = UL64(9223372036854775807);
           }            }
#endif #endif
         }          }
         else          else
         {          {
           ret =  !is_it_null? *(long long *)(col_rbind->buffer):0;            ret =  !is_it_null? *(long long *)(col_rbind->buffer):0;
         }          }
         break;          break;
       default:        default:
         return 0;            return 0;  
     }      }
     return ret;      return ret;
   }    }
   default:    default:
     break;/* Basically should be prevented by earlied tests of      break;/* Basically should be prevented by earlied tests of
                      conversion possibility */                        conversion possibility */ 
   /* TODO : Geometry? default ? */     /* TODO : Geometry? default ? */ 
 }  }
   
 return 0; // fool compilers  return 0; // fool compilers
} }
/* }}} */  /* }}} */