1

I'm trying to use SQLBindParameter to prepare my driver for input via SQLPutData. The field in the database is a TEXT field. My function is crafted based on MS's example here: http://msdn.microsoft.com/en-us/library/ms713824(VS.85).aspx.

I've setup the environment, made the connection, and prepared my statement successfully but when I call SQLBindParam (using code below) it consistently fails reporting: [Microsoft][SQL Native Client]Invalid precision value

int col_num = 1;
SQLINTEGER length = very_long_string.length( );
retcode = SQLBindParameter( StatementHandle,
            col_num,
            SQL_PARAM_INPUT,
            SQL_C_BINARY,
            SQL_LONGVARBINARY,
            NULL,
            NULL,            
            (SQLPOINTER) col_num,     
            NULL,                 
            &length ); 

The above relies on the driver in use returning "N" for the SQL_NEED_LONG_DATA_LEN information type in SQLGetInfo. My driver returns "Y". How do I bind so that I can use SQLPutData?

ConcernedOfTunbridgeWells
  • 64,444
  • 15
  • 143
  • 197
antik
  • 5,282
  • 1
  • 34
  • 47

2 Answers2

3

Though it doesn't look just like the documentation's example code, I found the following solution to work for what I'm trying to accomplish. Thanks gbjbaanb for making me retest my input combinations to SQLBindParameter.

    SQLINTEGER length;
    RETCODE retcode = SQLBindParameter( StatementHandle,
        col_num,      // position of the parameter in the query
        SQL_PARAM_INPUT,
        SQL_C_CHAR,
        SQL_VARCHAR,
        data_length,        // size of our data
        NULL,               // decimal precision: not used our data types
        &my_string,         // SQLParamData will return this value later to indicate what data it's looking for so let's pass in the address of our std::string
        data_length,
        &length );          // it needs a length buffer

    // length in the following operation must still exist when SQLExecDirect or SQLExecute is called
    // in my code, I used a pointer on the heap for this.
    length = SQL_LEN_DATA_AT_EXEC( data_length ); 

After a statement is executed, you can use SQLParamData to determine what data SQL wants you to send it as follows:

    std::string* my_string;
    // set string pointer to value given to SQLBindParameter
    retcode = SQLParamData( StatementHandle, (SQLPOINTER*) &my_string ); 

Finally, use SQLPutData to send the contents of your string to SQL:

    // send data in chunks until everything is sent
    SQLINTEGER len;
    for ( int i(0); i < my_string->length( ); i += CHUNK_SIZE )
    {
        std::string substr = my_string->substr( i, CHUNK_SIZE );

        len = substr.length( );

        retcode = SQLPutData( StatementHandle, (SQLPOINTER) substr.c_str( ), len );
    }
antik
  • 5,282
  • 1
  • 34
  • 47
  • +1 for the comment "length in the following operation must still exist when SQLExecDirect or SQLExecute is called". Since ODBC seems to optimize by sending the results of Bind and Execute calls together, I've run into a few scope errors along the way. – rstackhouse Dec 03 '15 at 23:00
1

you're passing NULL as the buffer length, this is an in/out param that shoudl be the size of the col_num parameter. Also, you should pass a value for the ColumnSize or DecimalDigits parameters.

http://msdn.microsoft.com/en-us/library/ms710963(VS.85).aspx

gbjbaanb
  • 51,617
  • 12
  • 104
  • 148