SQLRETURN insert_param()

in driver/execute.cc [863:1325]


SQLRETURN insert_param(STMT *stmt, MYSQL_BIND *bind, DESC* apd,
                       DESCREC *aprec, DESCREC *iprec, SQLULEN row)
{
    long length = 0;
    char buff[128], *data= NULL;
    BOOL convert= FALSE, free_data= FALSE;
    DBC *dbc= stmt->dbc;
    SQLLEN *octet_length_ptr= NULL;
    SQLLEN *indicator_ptr= NULL;
    SQLRETURN result= SQL_SUCCESS;

    if (aprec->octet_length_ptr)
    {
      octet_length_ptr= (SQLLEN*)ptr_offset_adjust(aprec->octet_length_ptr,
                                          apd->bind_offset_ptr,
                                          apd->bind_type,
                                          sizeof(SQLLEN), row);
      length = (long)*octet_length_ptr;
    }

    indicator_ptr= (SQLLEN*)ptr_offset_adjust(aprec->indicator_ptr,
                                     apd->bind_offset_ptr,
                                     apd->bind_type,
                                     sizeof(SQLLEN), row);

    if (aprec->data_ptr)
    {
      SQLINTEGER default_size= bind_length(aprec->concise_type,
                                           (ulong)aprec->octet_length);
      data= (char*)ptr_offset_adjust(aprec->data_ptr, apd->bind_offset_ptr,
                              apd->bind_type, default_size, row);
    }

    if (indicator_ptr && *indicator_ptr == SQL_NULL_DATA)
    {
      put_null_param(stmt, bind);

      return SQL_SUCCESS;
    }
    /*
      According to http://msdn.microsoft.com/en-us/library/ms710963%28VS.85%29.aspx

      "... If StrLen_or_IndPtr is a null pointer, the driver assumes that all
      input parameter values are non-NULL and that character and *binary* data
      is null-terminated."
    */
    else if (!octet_length_ptr || *octet_length_ptr == SQL_NTS)
    {
      if (data)
      {
        if (aprec->concise_type == SQL_C_WCHAR)
        {
          length = (long)sqlwcharlen((SQLWCHAR *)data) * sizeof(SQLWCHAR);
        }
        else
        {
          length = (long)strlen(data);
        }

        if (!octet_length_ptr && aprec->octet_length > 0 &&
            aprec->octet_length != SQL_SETPARAM_VALUE_MAX)
        {
          length = (long)myodbc_min(length, aprec->octet_length);
        }
      }
      else
      {
        length= 0;     /* TODO? This is actually an error */
      }
    }
    /*
      We may see SQL_COLUMN_IGNORE from bulk INSERT operations, where we
      may have been told to ignore a column in one particular row. So we
      try to insert DEFAULT, or NULL for really old servers.

      In case there are less parameters than result columns we have to
      insert NULL or DEFAULT.
    */
    else if (*octet_length_ptr == SQL_COLUMN_IGNORE ||
             /* empty values mean it's an unbound column */
             (*octet_length_ptr == 0 &&
              aprec->concise_type == SQL_C_DEFAULT &&
              aprec->par.val() == NULL))
    {
      put_default_value(stmt, bind);
      return SQL_SUCCESS;
    }
    else if (IS_DATA_AT_EXEC(octet_length_ptr))
    {
        length = (long)aprec->par.val_length();
        if ( !(data= aprec->par.val()) )
        {
          put_default_value(stmt, bind);
          return SQL_SUCCESS;
        }
    }

    PUSH_ERROR(check_c2sql_conversion_supported(stmt, aprec, iprec));

    switch ( aprec->concise_type )
    {

      case SQL_C_BINARY:
      case SQL_C_CHAR:
          convert= 1;
          break;

      default:
        switch(convert_c_type2str(stmt, aprec->concise_type, iprec,
                             &data, &length, buff, sizeof(buff)))
        {
        case SQL_ERROR:
          return SQL_ERROR;
        case SQL_SUCCESS_WITH_INFO:
          result= SQL_SUCCESS_WITH_INFO;
        }

        if (data == NULL)
        {
          goto memerror;
        }

        if (!(data >= buff && data < buff + sizeof(buff)))
        {
          free_data= TRUE;
        }
    }

    switch ( iprec->concise_type )
    {
      case SQL_DATE:
      case SQL_TYPE_DATE:
      case SQL_TYPE_TIMESTAMP:
      case SQL_TIMESTAMP:
        if (data[0] == '{')       /* Of type {d date } */
        {
          /* TODO: check if we need to check for truncation here as well? */
          if (bind != NULL)
          {
            /* First the datetime has to be filtered out of the brackets
               and other stuff. The validity of the format is up to the
               user.
            */
            const char *tt = get_date_time_substr(data, length);

            /* The length parameter is changed by get_date_time_substr()
               because it is passed as a reference */
            if (bind_param(bind, tt, length, MYSQL_TYPE_STRING))
            {
              goto memerror;
            }
          }
          else
          {
            stmt->add_to_buffer(data, length);
          }
          goto out;
        }

        if (iprec->concise_type == SQL_DATE
          || iprec->concise_type == SQL_TYPE_DATE)
        {
          TIMESTAMP_STRUCT ts;

          str_to_ts(&ts, data, length, 1, TRUE);

          /* Overflow also possible if converted from other C types
              http://msdn.microsoft.com/en-us/library/ms709385%28v=vs.85%29.aspx
              (if time fields nonzero sqlstate 22008 )
              http://msdn.microsoft.com/en-us/library/aa937531%28v=sql.80%29.aspx
              (Class values other than 01, except for the class IM,
              indicate an error and are accompanied by a return code
              of SQL_ERROR)

              Not sure if fraction should be considered as an overflow.
              In fact specs say about "time fields only"
            */
          if (!stmt->dbc->ds->opt_NO_DATE_OVERFLOW && TIME_FIELDS_NONZERO(ts))
          {
            return stmt->set_error("22008", "Date overflow", 0);
          }
        }
        /* else _binary introducer for binary data */
      case SQL_BINARY:
      case SQL_VARBINARY:
      case SQL_LONGVARBINARY:
        {
          if (bind != NULL)
          {
            /* i guess for ssps we do not need introducer */
            convert= 0;
          }
          else
          {
            if (dbc->cxn_charset_info->number !=
                dbc->ansi_charset_info->number)
            {
              stmt->add_to_buffer("_binary", 7);
            }
          }
          /* We have only added the introducer, data is added below. */
          break;
          }
          /* else treat as a string */
      case SQL_CHAR:
      case SQL_VARCHAR:
      case SQL_LONGVARCHAR:
      case SQL_WCHAR:
      case SQL_WVARCHAR:
      case SQL_WLONGVARCHAR:
        {
          if (aprec->concise_type == SQL_C_WCHAR &&
              dbc->cxn_charset_info->number != UTF8_CHARSET_NUMBER)
          {
            if (bind != NULL)
            {
              if (bind_param(bind, data, length, MYSQL_TYPE_BLOB))
              {
                goto memerror;
              }

              goto out;
            }
          }
          else if (aprec->concise_type != SQL_C_WCHAR &&
                    dbc->cxn_charset_info->number !=
                    dbc->ansi_charset_info->number)
          {
            if (bind != NULL)
            {
              if (bind_param(bind, data, length, MYSQL_TYPE_BLOB))
              {
                goto memerror;
              }

              goto out;
            }
          }
          /* We have only added the introducer, data is added below. */
          break;
        }
      case SQL_INTERVAL_HOUR_TO_MINUTE:
      case SQL_INTERVAL_HOUR_TO_SECOND:
        if (aprec->concise_type != SQL_C_INTERVAL_HOUR_TO_MINUTE &&
            aprec->concise_type != SQL_C_INTERVAL_HOUR_TO_SECOND)
        {
          /* Allow only interval to interval conversion */
          stmt->set_error("07006", "Conversion is not supported", 0);
        }
        else
        {
          if (put_param_value(stmt, bind, buff, length))
          {
            goto memerror;
          }
        }
        goto out;

      case SQL_TIME:
      case SQL_TYPE_TIME:
        if ( aprec->concise_type == SQL_C_TIMESTAMP ||
              aprec->concise_type == SQL_C_TYPE_TIMESTAMP )
        {
          TIMESTAMP_STRUCT *time= (TIMESTAMP_STRUCT*) aprec->data_ptr;

          if (time->hour > 23)
          {
            return stmt->set_error("22008", "Not a valid time value supplied", 0);
          }
          if (time->fraction)
          {
            /* fractional seconds truncated, need to set correct sqlstate 22008
            http://msdn.microsoft.com/en-us/library/ms709385%28v=vs.85%29.aspx */

            return stmt->set_error("22008", "Fractional truncation", 0);
          }

          if (bind != NULL)
          {
            length = myodbc_snprintf(buff, sizeof(buff), "%02d:%02d:%02d",
                                     time->hour, time->minute, time->second);
          }
          else
          {
            length = myodbc_snprintf(buff, sizeof(buff), "'%02d:%02d:%02d'",
                                     time->hour, time->minute, time->second);
          }

          if (put_param_value(stmt, bind, buff, length))
          {
            goto memerror;
          }
        }
        else
        {
          ulong time;
          int hours;
          SQLUINTEGER fraction;

          /* For now it is safer to assume a dot is always a separator */
          /* stmt->dbc->ds->dont_use_set_locale */
          get_fractional_part(data, length, TRUE, &fraction);

          if (fraction)
          {
            /* truncation need SQL_ERROR and sqlstate 22008*/
            return stmt->set_error("22008", "Fractional truncation", 0);
          }

          time= str_to_time_as_long(data,length);
          hours= time/10000;

          if (hours > 23)
          {
            return stmt->set_error("22008", "Not a valid time value supplied", 0);
          }

          if (bind != NULL)
          {
            length = myodbc_snprintf(buff, sizeof(buff), "%02d:%02d:%02d",
                                     hours,
                                     (int) time/100%100,
                                     (int) time%100);
          }
          else
          {
            length = myodbc_snprintf(buff, sizeof(buff), "'%02d:%02d:%02d'",
                                     hours,
                                     (int) time/100%100,
                                     (int) time%100);
          }

          if (put_param_value(stmt, bind, buff, length))
          {
            goto memerror;
          }
        }

        goto out;

      case SQL_BIT:
        {
          if (bind != NULL)
          {
            char bit_val= atoi(data)!= 0 ? 1 : 0;
            /* Generic ODBC supports only BIT(1) */
            bind_param(bind, &bit_val, 1, MYSQL_TYPE_TINY);
          }
          else if (!convert)
          {
            stmt->add_to_buffer(data, 1);
          }
          goto out;
        }
      case SQL_FLOAT:
      case SQL_REAL:
      case SQL_DOUBLE:
        /* If we have string -> float ; Fix locale characters for number */
        if (convert)
        {
          char *to= buff, *from= data;
          char *end= from+length;
          const char *local_thousands_sep = thousands_sep.c_str();
          const char *local_decimal_point = decimal_point.c_str();
          size_t local_thousands_sep_length = thousands_sep.length();
          size_t local_decimal_point_length = decimal_point.length();

          if (!stmt->dbc->ds->opt_NO_LOCALE)
          {
            /* force use of . as decimal point */
            local_thousands_sep= ",";
            local_thousands_sep_length= 1;
            local_decimal_point= ".";
            local_decimal_point_length= 1;
          }

          while ( *from && from < end )
          {
            if ( from[0] == local_thousands_sep[0] && is_prefix(from,local_thousands_sep) )
            {
              from+= local_thousands_sep_length;
            }
            else if ( from[0] == local_decimal_point[0] && is_prefix(from,local_decimal_point) )
            {
              from+= local_decimal_point_length;
              *to++='.';
            }
            else
            {
              *to++= *from++;
            }
          }
          if ( to == buff )
          {
            *to++='0';    /* Fix for empty strings */
          }
          data= buff;
          length= (uint) (to-buff);

          convert= 0;

        }
        /* Fall through */
      default:
        if (!convert)
        {
          put_param_value(stmt, bind, data, length);
          goto out;
        }
    }

    if (bind != NULL && stmt->setpos_op == 0)
    {
      bind_param(bind, data, length, MYSQL_TYPE_STRING);
    }
    else
    {
    /* Convert binary data to hex sequence */
      if(is_no_backslashes_escape_mode(stmt->dbc) &&
       is_binary_sql_type(iprec->concise_type))
      {
        SQLLEN transformed_len = 0;
        stmt->add_to_buffer(" 0x", 3);
        /* Make sure we have room for a fully-escaped string. */
        if (!stmt->extend_buffer(length * 2))
        {
          goto memerror;
        }

        copy_binhex_result(stmt, (SQLCHAR*)stmt->endbuf(),
          length * 2 + 1, &transformed_len, data, length);
        stmt->buf_add_pos(transformed_len);
      }
      else
      {
        stmt->add_to_buffer("'", 1);
        /* Make sure we have room for a fully-escaped string. */
        if (!(stmt->extend_buffer(length * 2)))
        {
          goto memerror;
        }

        size_t added = dbc->connection_proxy->real_escape_string(stmt->endbuf(), data, length);
        stmt->buf_add_pos(added);
        stmt->add_to_buffer("'", 1);
      }
    }

out:
    if (free_data)
    {
      x_free(data);
    }

    return result;

memerror:
    if (free_data)
    {
      x_free(data);
    }
    return stmt->set_error( MYERR_S1001, NULL, 4001);
}