I am creating an application in native c++ using odbc. I am calling on to a sql server stored procedure that has a decimal parameter. I am passing a float to that parameter. The code so far:
SQLDECIMAL *sql_param = new SQLDECIMAL( param);
if( SQL_ERROR == ( result = SQLBindParameter( statement_handle, 5, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_DECIMAL, 12, 6, sql_param, 0, NULL ) ) ){
std::wstring error = get_error_message( SQL_HANDLE_STMT, statement_handle );
throw GenericException( error );
}
param is a float.
What gets stored on the table is always 0 instead of the real value which is 1.45. I'm guessing some conversion is taking place but I can't figured out the correct conversion. SQL_C_FLOAT -> SQL_DECIMAL?
In the reference:
SQLRETURN SQLBindParameter(
SQLHSTMT StatementHandle,
SQLUSMALLINT ParameterNumber,
SQLSMALLINT InputOutputType,
SQLSMALLINT ValueType,
SQLSMALLINT ParameterType,
SQLULEN ColumnSize,
SQLSMALLINT DecimalDigits,
SQLPOINTER ParameterValuePtr,
SQLLEN BufferLength,
SQLLEN * StrLen_or_IndPtr);
...
BufferLength
[Input/Output] Length of the ParameterValuePtr buffer in bytes.
You have specified BufferLength as zero. Looking here, you can see the length of decimal for different precision values:
Precision StorageBytes
1-9 5
10-19 9
20-28 13
29-38 17
Therefore, you need to call method SQLBindParameter as:
SQLBindParameter( statement_handle, 1, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_DECIMAL, 12, 6, sql_param, 5, NULL )
The SQLDECIMAL is your problem. According to http://publib.boulder.ibm.com/infocenter/soliddb/v6r3/index.jsp?topic=/com.ibm.swg.im.soliddb.programmer.doc/doc/s0005314.c.data.types.html it is a unsigned char [f].
I suggest something like:
void bind_float(float* param) {
SQLBindParameter( statement_handle, 5, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_DECIMAL, 12, 6, param, 0, NULL ) ) );
}
I got it. It shouldn't have been:
SQLDECIMAL *sql_param = new SQLDECIMAL( param );
it should have been:
SQLDECIMAL *sql_param = (SQLDECIMAL *) param;
Related
I have some problems about ODBC programming with C/C++, PostgreSQL connector.
I just want to put some BLOB data in my PostgreSQL with C/C++, ODBC
here's my DB Table Create Query
CREATE TABLE BLOB_TEST
(
id number(20),
data BYTEA
);
And, here's my code
.
.
.
int retcode = 0;
SQLCHAR sql[1024] =
"BEGIN \n"
"insert into BLOB_TEST "
"values(9, ?); \n"
"EXCEPTION \n"
"when DUP_VAL_ON_INDEX then \n"
"dbms_output.put_line(1); \n"
"END; ";
char * temp_str = "this is a BLOB input TEST";
retcode = SQLPrepareA(hstmt, sql, SQL_NTS);
.
.
.
SQLBindParameter(hstmt,
1, /* Parameter number, starting at 1 */
SQL_PARAM_INPUT, /* in, out, inout */
SQL_C_BINARY, /* C data type of the parameter */
SQL_LONGVARBINARY, /* SQL data type of the parameter : char(8)*/
0, /* size of the column or expression, precision */
0, /* The decimal digits, scale */
temp_str, /* A pointer to a buffer for the parameter’s data */
0, /* Length of the ParameterValuePtr buffer in bytes */
NULL /* indicator */
);
.
.
.
retcode = SQLExecute(hstmt);
if (retcode == SQL_SUCCESS)
{
printf("Query Execute Success\n");
}
else
{
SQLGetDiagRecA(SQL_HANDLE_STMT, hstmt, ++rec, state, &native, message, sizeof(message), &length);
printf("%s : %ld : %ld : %s\n", state, rec, native, message);
printf("Query Execute ERROR : %d\n", retcode);
}
SQLExecute return -1(SQL_ERROR) and ERROR Message says:
SQLSTATE 42601, Missing ";" at the end of Expression
I know that PostgreSQL BLOB(BYTEA) type matched SQL_LONGVARBINARY Option when using SQLBindParameter, But that makes ERROR...
Is there any odd expression in my prepared query?
Or, Is there any way to check value-combind Query that SQLExcute function made?
I'm very confused, Cause the query that I prepared works well when using PgAdmin Querying tools...
So, I Want to check value-combind Query that SQLExcute function made.
You are trying to run a PL/SQL block on a database that is not Oracle. How is that supposed to work?
PostgreSQL has a DO statement that serves a similar purpose, but you cannot use parameters with it. You should send only the INSERT statement and do the exception handling in your C client code.
I have a simple c++ code to insert a record in SQL Server 2014 via ODBC. It works fine with varchar fields, but it does not work with datetime field.
SQLCHAR buf[64];
SQLLEN len;
SQLCHAR buf2[255];
SQLLEN len2;
SQLLEN len3;
double f;
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_TYPE_TIMESTAMP, 19, 0, (SQLCHAR*)buf, 0, &len);
rc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 255, 0, (SQLCHAR*)buf2, 0, &len2);
rc = SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 15, 0, &f, 0, &len3);
rc = SQLPrepare(hstmt, (SQLCHAR*)"insert into test(timepoint,strvalue,floatvalue) values (?,?,?)", SQL_NTS);
strcpy((char*)buf,"20171010 00:08:14");
len=strlen("20171017 00:08:14");
strcpy((char*)buf2,"simple string");
len2=strlen("simple string");
f=1.34e+8;
SQLSMALLINT NumParams;
SQLNumParams(hstmt, &NumParams);
rc = SQLExecute(hstmt);
Can anybody help to understand that is wrong?
I've got always:
"22018:1:0:[Microsoft][ODBC SQL Server Driver]Invalid character value for cast specification\n"
If I'm wrong here with my question, please give a hint which forum fits better. I've spent some days, but can't find the solution. For any help would be very appreciate.
SQL Server can cast various strings in different date formats to a real date implicitly, but you must be very careful how to use them:
There is the unseparated date format "yyyyMMdd", which cannot carry a time!
SELECT CAST('20170102' AS DATETIME) --2017-01-02 00:00:00.000
The most universal format is ISO8601, which is yyyy-MM-ddTHH:mm:ss (beware of the the T in the middle and the usage of 24 hours format!)
SELECT CAST('2017-01-02T12:23:59' AS DATETIME) --2017-01-02 12:23:59.000
And - last but not least - there are three ODBC formats:
SELECT CAST({d'2017-01-02'} AS DATETIME) --2017-01-02 00:00:00.000
SELECT CAST({t'23:35:51'} AS DATETIME) --2018-01-19 23:35:51.000 (today!)
SELECT CAST({ts'2017-01-02 23:35:51'} AS DATETIME) --2017-01-02 23:35:51.000
And a last - important! - hint: Some formats work, but depend on the system's settings. This can work for you and pass all internal tests, but might break on a customer's machine:
SET LANGUAGE ENGLISH;
SELECT CAST('2017-01-02 23:35:51' AS DATETIME) -- Jan 2
SET LANGUAGE GERMAN;
SELECT CAST('2017-01-02 23:35:51' AS DATETIME) -- Feb 1
Im trying to write a programm in c++ to let my computer communicate with a trinamic steprockerboard.
They already provided an example file, to get you on the way. This works perfect, but is really simplistic.
Now I want to read the results from the usb device, and they made a function for that:
//Read the result that is returned by the module
//Parameters: Handle: handle of the serial port, as returned by OpenRS232
//Address: pointer to variable to hold the reply address returned by the module
// Status: pointer to variable to hold the status returned by the module (100 means okay)
//Value: pointer to variable to hold the value returned by the module
prototype:
UCHAR GetResult(HANDLE Handle, UCHAR *Address, UCHAR *Status, int *Value)
Now I wrote the following:
UCHAR* adr;
UCHAR* stat;
int* val;
SendCmd(RS232Handle, 1, TMCL_MVP, 0, 0, -3200); // move to next position
SendCmd(RS232Handle,1, TMCL_GAP, 8, 0, 0); // tell motor to look if position is reached
GetResult(RS232Handle,adr,stat,val); //ask for the result, value must give a 1 if so
printf("results from USB device: adr=%d, stat=%d, val=%d\n", adr, stat, val);
But when I run the program, and try this option, the program chrashes.
Has anybody got an idea what might cause the problem? (the code I supplied is only a part of my program, the whole code can be find below.
It is not my intention that you read the whole code, the problem should be above, but only for who is interested I also put the rest down here)
// TMCLTest.cpp : Show how to communicate with a TMCM module in TMCL
//
#include "stdafx.h"
#include <iostream>
//Opcodes of all TMCL commands that can be used in direct mode
#define TMCL_ROR 1
#define TMCL_ROL 2
#define TMCL_MST 3
#define TMCL_MVP 4
#define TMCL_SAP 5
#define TMCL_GAP 6
#define TMCL_STAP 7
enter code here#define TMCL_RSAP 8
enter code here#define TMCL_SGP 9
#define TMCL_GGP 10
#define TMCL_STGP 11
#define TMCL_RSGP 12
#define TMCL_RFS 13
#define TMCL_SIO 14
#define TMCL_GIO 15
#define TMCL_SCO 30
#define TMCL_GCO 31
#define TMCL_CCO 32
//Opcodes of TMCL control functions (to be used to run or abort a TMCL program in the module)
#define TMCL_APPL_STOP 128
#define TMCL_APPL_RUN 129
#define TMCL_APPL_RESET 131
//Options for MVP commandds
#define MVP_ABS 0
#define MVP_REL 1
#define MVP_COORD 2
//Options for RFS command
#define RFS_START 0
#define RFS_STOP 1
#define RFS_STATUS 2
#define FALSE 0
#define TRUE 1
//Result codes for GetResult
#define TMCL_RESULT_OK 0
#define TMCL_RESULT_NOT_READY 1
#define TMCL_RESULT_CHECKSUM_ERROR 2
//Open serial interface
//Usage: ComHandle=OpenRS232("COM1", CBR_9600)
HANDLE OpenRS232(const char* ComName, DWORD BaudRate)
{
HANDLE ComHandle;
DCB CommDCB;
COMMTIMEOUTS CommTimeouts;
ComHandle=CreateFile(ComName, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(GetLastError()!=ERROR_SUCCESS) return INVALID_HANDLE_VALUE;
else
{
GetCommState(ComHandle, &CommDCB);
CommDCB.BaudRate=BaudRate;
CommDCB.Parity=NOPARITY;
CommDCB.StopBits=ONESTOPBIT;
CommDCB.ByteSize=8;
CommDCB.fBinary=1; //Binary Mode only
CommDCB.fParity=0;
CommDCB.fOutxCtsFlow=0;
CommDCB.fOutxDsrFlow=0;
CommDCB.fDtrControl=0;
CommDCB.fDsrSensitivity=0;
CommDCB.fTXContinueOnXoff=0;
CommDCB.fOutX=0;
CommDCB.fInX=0;
CommDCB.fErrorChar=0;
CommDCB.fNull=0;
CommDCB.fRtsControl=RTS_CONTROL_TOGGLE;
CommDCB.fAbortOnError=0;
SetCommState(ComHandle, &CommDCB);
//Set buffer size
SetupComm(ComHandle, 100, 100);
//Set up timeout values (very important, as otherwise the program will be very slow)
GetCommTimeouts(ComHandle, &CommTimeouts);
CommTimeouts.ReadIntervalTimeout=MAXDWORD;
CommTimeouts.ReadTotalTimeoutMultiplier=0;
CommTimeouts.ReadTotalTimeoutConstant=0;
SetCommTimeouts(ComHandle, &CommTimeouts);
return ComHandle;
}
}
//Close the serial port
//Usage: CloseRS232(ComHandle);
void CloseRS232(HANDLE Handle)
{
CloseHandle(Handle);
}
//Send a binary TMCL command
//e.g. SendCmd(ComHandle, 1, TMCL_MVP, MVP_ABS, 1, 50000); will be MVP ABS, 1, 50000 for module with address 1
//Parameters: Handle: Handle of the serial port (returned by OpenRS232).
// Address: address of the module (factory default is 1).
// Command: the TMCL command (see the constants at the begiining of this file)
// Type: the "Type" parameter of the TMCL command (set to 0 if unused)
// Motor: the motor number (set to 0 if unused)
// Value: the "Value" parameter (depending on the command, set to 0 if unused)
void SendCmd(HANDLE Handle, UCHAR Address, UCHAR Command, UCHAR Type, UCHAR Motor, INT Value)
{
UCHAR TxBuffer[9];
DWORD BytesWritten;
int i;
TxBuffer[0]=Address;
TxBuffer[1]=Command;
TxBuffer[2]=Type;
TxBuffer[3]=Motor;
TxBuffer[4]=Value >> 24;
TxBuffer[5]=Value >> 16;
TxBuffer[6]=Value >> 8;
TxBuffer[7]=Value & 0xff;
TxBuffer[8]=0;
for(i=0; i<8; i++)
TxBuffer[8]+=TxBuffer[i];
//Send the datagram
WriteFile(Handle, TxBuffer, 9, &BytesWritten, NULL);
}
//Read the result that is returned by the module
//Parameters: Handle: handle of the serial port, as returned by OpenRS232
// Address: pointer to variable to hold the reply address returned by the module
// Status: pointer to variable to hold the status returned by the module (100 means okay)
// Value: pointer to variable to hold the value returned by the module
//Return value: TMCL_RESULT_OK: result has been read without errors
// TMCL_RESULT_NOT_READY: not enough bytes read so far (try again)
// TMCL_RESULT_CHECKSUM_ERROR: checksum of reply packet wrong
UCHAR GetResult(HANDLE Handle, UCHAR *Address, UCHAR *Status, int *Value)
{
UCHAR RxBuffer[9], Checksum;
DWORD Errors, BytesRead;
COMSTAT ComStat;
int i;
//Check if enough bytes can be read
ClearCommError(Handle, &Errors, &ComStat);
if(ComStat.cbInQue>8)
{
//Receive
ReadFile(Handle, RxBuffer, 9, &BytesRead, NULL);
Checksum=0;
for(i=0; i<8; i++)
Checksum+=RxBuffer[i];
if(Checksum!=RxBuffer[8]) return TMCL_RESULT_CHECKSUM_ERROR;
*Address=RxBuffer[0];
*Status=RxBuffer[2];
*Value=(RxBuffer[4] << 24) | (RxBuffer[5] << 16) | (RxBuffer[6] << 8) | RxBuffer[7];
} else return TMCL_RESULT_NOT_READY;
return TMCL_RESULT_OK;
}
int main(int argc, char* argv[])
{
int i;
int Type, Motor, Velocity, Position,ref1;
UCHAR Address, Status;
int Value, Timeout;
HANDLE RS232Handle;
RS232Handle=OpenRS232("COM3", 9600);
// set parameters
SendCmd(RS232Handle, 1, TMCL_SAP, 140, 0, 5); //SAP 140, 0, 5 // set microsteps to 32 (32 additional steps per step of 1.8 degr.)
//SAP 4, 0, 500 //set max vel.
//SAP 5, 0, 100 //set max acc.
//SAP 6, 0, 255 //set abs. max current to 2.8 ampere
//SAP 7, 0, 50 //set standby current ((50/250)*2.8A)
printf("VPI Test Setup\n \n" );
do
{
printf("1 - Rotate clockwise (10 rotations)\n");
printf("2 - Rotate counter-clockwise (10 rotations)\n");
printf("3 - Stop motor\n");
printf("4 - Start test (First 100 rotations clockwise, \n then 100 rotations counter clockwise)\n");
printf("\n99 - End\n");
scanf("%d", &i);
switch(i)
{
case 1:
SendCmd(RS232Handle, 1, TMCL_MVP, 0, 0, 32000); //ABS(4th parameter) = 0
break;
case 2:
SendCmd(RS232Handle, 1, TMCL_MVP, 0, 0, -32000); //ABS(4th parameter) = 0
break;
case 3:
SendCmd(RS232Handle, 1, TMCL_MST, 0, 0, 0);
break;
case 4:
//SendCmd(RS232Handle, 1, TMCL_RFS,0,0,0);
printf("Test started \n" );
//UCHAR done;
UCHAR* adr;
UCHAR* stat;
int* val;
//SendCmd(RS232Handle, 1, TMCL_SAP, 193, 1, 2); //SAP 193,1,2
SendCmd(RS232Handle, 1, TMCL_MVP, 0, 0, -3200); //ABS(4th parameter) = 0
SendCmd(RS232Handle,1, TMCL_GAP, 8, 0, 0);
GetResult(RS232Handle,adr,stat,val);
printf("results from USB device: adr=%d, stat=%d, val=%d\n", adr, stat, val);
//CHAR GetResult(HANDLE Handle, UCHAR *Address, UCHAR *Status, int *Value)
// if(done != 2)
// {
// printf("rotation backwards started \n");
// SendCmd(RS232Handle, 1, TMCL_MVP, 0, 0, 3200);
// }
// }
break;
}
SendCmd(RS232Handle, 1, TMCL_ROL, 0, Motor, Velocity);
SendCmd(RS232Handle, 1, TMCL_ROL, 0, Motor, Velocity);
if(i==1 || i==2 || i==3 || i==4)
{
Address=0;
Status=0;
Value=0;
Timeout=GetTickCount();
while(GetResult(RS232Handle, &Address, &Status, &Value) ==TMCL_RESULT_NOT_READY && abs(GetTickCount()-Timeout)<1000);
printf("Result: Address=%d, Status=%d, Value=%d\n", Address, Status, Value);
}
}
while(i!=99);
CloseRS232(RS232Handle);
return 0;
}
Address, Status and Value should point to valid variables. So, your code should look something like:
UCHAR adr;
UCHAR stat;
int val;
GetResult(RS232Handle,&adr,&stat,&val);
What is happening now is that your variables (adr, stat and val) are not initialized, so, they point to random locations in memory. When you pass these variables to GetResult, it tries to write to those random locations.
I'm trying to extract a string from my SQL database, but for some reason my parameters are wrong and I'm not sure why. Here is my code:
SQLHENV environHandle;
SQLHDBC connectHandle;
SQLHSTMT statement;
SQLCHAR* connectString = "MY_CONNECTION_STRING";
string path;
int jobID;
SQLINTEGER pathstrlen = SQL_NTS;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environHandle);
SQLSetEnvAttr(environHandle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
SQLAllocHandle(SQL_HANDLE_DBC, environHandle, &connectHandle);
SQLDriverConnect(connectHandle, NULL, connectString, SQL_NTS, NULL, 1024, NULL, SQL_DRIVER_NOPROMPT);
SQLAllocHandle(SQL_HANDLE_STMT, connectHandle, &statement);
//THIS IS THE BINDPARAMETER WITH THE ISSUE...
SQLBindParameter(statement, 1, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_VARCHAR, 400, 0, (SQLPOINTER)path.c_str(), path.length(), &pathstrlen);
SQLBindParameter(statement, 2, SQL_PARAM_OUTPUT, SQL_INTEGER, SQL_INTEGER, 10, 0, &jobID, 0, &pathstrlen);
SQLExecDirect(statement, (SQLCHAR*)"{CALL SP(?,?)}", SQL_NTS);
It runs fine, but won't get the string information I requested, while the second parameter (to get an integer) works fine. I've tried changing the ParameterType to multiple different things but I either get errors thrown at me (SQL_LONGVARCHAR for example, is deprecated).
In SQL, the data I'm trying to get is as follows:
#Path nvarchar(4000) OUT
set #Path = 'test'
Thanks in advance to anyone who can shed light on this. I've been pulling my hair out all day.
ODBC supports Unicode parameter types so use SQL_C_WCHAR and SQL_WVARCHAR instead of SQL_C_CHAR and SQL_VARCHAR respectively.
You have two other issues as well.
First you are passing an ANSI string as a parameter. If you are using Unicode you need to use a wide string - wstring instead.
Second you are not passing a valid buffer to SQLBindParameter. The value returned by string.c_str() is a const char* that is a read only buffer. It is not valid to pass hat to a function that requires a writable buffer - doing this will corrupt your string. However you won't see any corruption in your case because you the call to path.length() WILL return zero so SQLBindParameter will never return any data.
You will need to declare WCHAR array buffer and pass that to SQLBindParameter which will give it a valid buffer to write data into. You can then transfer that buffer to a wstring if you need it in a C++ object.
So something like this:
WCHAR path[401]; // 401 - width of you column + 1 for the null terminator
SQLBindParameter(statement, 1, SQL_PARAM_OUTPUT, SQL_C_WCHAR, SQL_WVARCHAR, 400, 0, (SQLPOINTER)path, sizeof(path), &pathstrlen);
Edit
Ffrom looking at the ODBC data conversion table it appears that you should be able to get ODBC to convert that data from Unicode to ANSI for you if you to not want to deal with Unicode strings in your application.
char path[401]; // 401 - width of you column + 1 for the null terminator
SQLBindParameter(statement, 1, SQL_PARAM_OUTPUT, SQL_C_WCHAR, SQL_WVARCHAR, 400, 0, (SQLPOINTER)path, sizeof(path), &pathstrlen);
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?
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 );
}
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