Can't transmate string through MariaDB connect/c Prepared Statement - c

I'm using "MariaDB Connector/C" for my homework, but I got a problem: I always get an empty string when I pass in a string parameter, the db table is:
MariaDB none#(none):test> SELECT * FROM t3
a
b
0
abc
1
bcd
2
af
3 rows in set
Time: 0.010s
MariaDB none#(none):test> DESC t3
Field
Type
Null
Key
Default
Extra
a
int(11)
NO
PRI
b
char(10)
YES
2 rows in set
Time: 0.011s
And the code I use to test:
#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
MYSQL *mysql;
mysql = mysql_init(NULL);
if (!mysql_real_connect(mysql,NULL , "none", "linux", "test", 0,"/tmp/mariadb.sock",0)){
printf( "Error connecting to database: %s",mysql_error(mysql));
} else
printf("Connected...\n");
if(mysql_real_query(mysql,"SET CHARACTER SET utf8",(unsigned int)sizeof("SET CHARACTER SET utf8"))){
printf("Failed to set Encode!\n");
}
char query_stmt_2[]="select * from t3 where b=?";
MYSQL_STMT *stmt2 = mysql_stmt_init(mysql);
if(mysql_stmt_prepare(stmt2, query_stmt_2, -1))
{
printf("STMT2 prepare failed.\n");
}
MYSQL_BIND instr_bind;
char instr[50]="abc";
my_bool in_is_null = 0;
my_bool in_error = 0;
instr_bind.buffer_type = MYSQL_TYPE_STRING;
instr_bind.buffer = &instr[0];
char in_ind = STMT_INDICATOR_NTS;
instr_bind.u.indicator = &in_ind;
unsigned long instr_len=sizeof(instr);
// instr_bind.length = &instr_len;
// instr_bind.buffer_length=instr_len;
instr_bind.is_null = &in_is_null;
instr_bind.error = &in_error;
MYSQL_BIND out_bind[2];
memset(out_bind, 0, sizeof(out_bind));
int out_int[2];
char outstr[50];
my_bool out_int_is_null[2]={0,0};
my_bool out_int_error[2]={0,0};
unsigned long out_int_length[2]={0,0};
out_bind[0].buffer = out_int+0;
out_bind[0].buffer_type = MYSQL_TYPE_LONG;
out_bind[0].is_null = out_int_is_null+0;
out_bind[0].error = out_int_error+0;
out_bind[0].length = out_int_length+0;
out_bind[1].buffer = outstr;
out_bind[1].buffer_type = MYSQL_TYPE_STRING;
out_bind[1].buffer_length = 50;
out_bind[1].is_null = out_int_is_null+1;
out_bind[1].error = out_int_error+1;
out_bind[1].length = out_int_length+1;
if(mysql_stmt_bind_param(stmt2, &instr_bind) ||
mysql_stmt_bind_result(stmt2, out_bind)){
printf("Bind error\n");
}
if(mysql_stmt_execute(stmt2))
{
printf("Exec error: %s",mysql_stmt_error(stmt2));
}
if(mysql_stmt_store_result(stmt2)){
printf("Store result error!\n");
printf("%s\n",mysql_stmt_error(stmt2));
}
while(!mysql_stmt_fetch(stmt2))
{
printf("%d\t%s\n", out_int[0], outstr);
}
mysql_stmt_close(stmt2);
end:
mysql_close(mysql);
}
I only got an empty result:
❯ ./Exec/test/stmt_test
Connected...
I have been in trouble with this for two days, and tomorrow is the deadline, I'm very anxious. Can you help? Thanks a lot!

1) General
Avoid "it was hard to write, so it should be hard to read" code
add variable declarations at the beginning of the function, not in the middle of code (Wdeclaration-after-statement)
don't use c++ comments in C
set character set with api function mysql_set_character_set()
write proper error handling, including mysql_error/mysql_stmt_error results and don't continue executing subsequent code after error.
always initialize MYSQL_BIND
2) input bind buffer
u.indicator is used for bulk operations and doesn't make sense here
bind.is_null is not required, since you specified a valid buffer address
buffer_length is not set (in comments)
3) Output bind buffer
Always bind output parameters after mysql_stmt_execute(), since mysql_stmt_prepare can't always determine the number of parameters, e.g. when calling a stored procedure: In this case mysql_stmt_bind_param will return an error.
binding an error indicator doesn't make much sense without setting MYSQL_REPORT_DATA_TRUNCATION (mysql_optionsv)
For some examples how to deal with prepared statements check the file ps.c of MariaDB Connector/C unit tests

Related

GCC C and passing integer to PostgreSQL

I know that there probably was plenty on that but after several days of searching I am unable to find how to do one simple passing of integer and char in one go to PostgreSQL from C under Linux.
In PHP it is easy, like 123, and in C using libpq it seem to be like something out of ordinary.
I had a look at PQexecParams but is seem to be not helping. Examples on the net are not helping as well and it seems to be an impossible mission.
Would someone be kind enough to translate this simple PHP statement to C and show me how to pass multiple vars of different types in one INSERT query.
col1 is INT
col2 is CHAR
$int1 = 1;
$char1 = 'text';
$query = "INSERT INTO table (col1, col2) values ('$int1',$char1)";
$result = ibase_query($query);
This would show what I am trying to do (please mind the code is very wrong):
void insert_CommsDb(PGconn *conn, PGresult *pgres, int csrv0) { const char * params[1];
params[0] = csrv0;
pgres = PQexecParams(conn, "INSERT INTO comms_db (srv0::int) values ($1)",
1,
NULL,
params,
1,
NULL,
0);
if (PQresultStatus(pgres) != PGRES_COMMAND_OK)
{
fprintf(stderr, "INSERT failed: %s", PQerrorMessage(conn));
exit_nicely(conn,pgres);
}
PQclear(pgres);
}
https://www.postgresql.org/docs/current/static/libpq-exec.html
As #joop commented above:
If the paramTypes argument is NULL, all the params are assumed to be strings.
So, you should transform your int argument to a string.
void insert_CommsDb(PGconn *conn, int csrv0)
{
PGresult *pgres;
char * params[1];
char buff[12];
sprintf(buff, "%d", csrv0);
params[0] = buff;
pgres = PQexecParams(conn
, "INSERT INTO comms_db (srv0::int) values ($1)" // The query (we dont need the cast here)
, 1 // number of params
, NULL // array with types, or NULL
, params // array with parameter values
, NULL // ARRAY with parameter lenghts
, NULL // array with per-param flags indicating binary/non binary
, 0 // set to 1 if we want BINARY results, 0 for txt
);
if (PQrresultStatus(pgres) != PGRES_COMMAND_OK)
{
fprintf(stderr, "INSERT failed: %s", PQerrorMessage(conn));
exit_nicely(conn,pgres);
}
PQclear(pgres);
}
wildplasser's answer shows the way in general.
Since you explicitly asked about several parameters, I'll add an example for that.
If you are not happy to convert integers to strings, the alternative would be to use the external binary format of the data type in question. That requires inside knowledge and probably reading the PostgreSQL source. For some data types, it can also depend on the hardware.
PGresult *res;
PGconn *conn;
Oid types[2];
char * values[2];
int lengths[2], formats[2];
int arg0;
/* connect to the database */
/*
* The first argument is in binary format.
* Apart from having to use the "external binary
* format" for the data, we have to specify
* type and length.
*/
arg0 = htonl(42); /* external binary format: network byte order */
types[0] = 23; /* OID of "int4" */
values[0] = (char *) &arg0;
lengths[0] = sizeof(int);
formats[0] = 1;
/* second argument is in text format */
types[1] = 0;
values[1] = "something";
lengths[1] = 0;
formats[1] = 0;
res = PQexecParams(
conn,
"INSERT INTO mytab (col1, col2) values ($1, $2)",
2,
types,
(const char * const *)values,
lengths,
formats,
0 /* results in text format */
);
I'd recommend that you use the text format for most data types.
The notable exception is bytea, where it usually is an advantage to use the binary format, as it saves space and CPU power. In this case, the external binary format is simply the bytes.
VS C++ not liking htonl(42):
arg0 = htonl(42); /* external binary format: network byte order */

How to get COUNT(*) value from sqlite3_exec()?

In my C program I have to check if count of a table in database is one or zero and to do that i am executing query as follows:
char *sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=family;";
int table_count = sqlite3_exec(db, sql, 0, 0, &err_msg);
printf("\n%d\n", table_count);
I'm expecting table_count to be 1 as only one table exists with name family but printf outputs table_count as '21' which is incorrect. How can we get the COUNT(*) value from C/C++ API in C program the right/correct way?
After reading SQLite Documentation and following other kind implicit/explicit suggestions in the comments on the question, I have realized my mistakes in that code snippet quoted in the question.
Mistake 1:
I did not implement callback function to receive the result set after the SQL query gets executed. [Have implemented this callback: see checkTable_Callback in code below]
Mistake 2:
That output of '21' is actually the error code and as per the SQLite documentation that error code corresponds to SQLite_MISUSE Which was being generated, perhaps, because I was using a separate function to open my test database file and instance of that opened database, i assume, stayed inside that openDb function, and when i used another function checkTableCount from where i took that messy snippet to quote in my question, there db instance perhaps was null, hence 21. Experts can elaborate further if that's why i was receiving error code 21. Anyways, now i have fixed that function and made that openDb return an opened db instance (better word?) and now 21 error is gone. [see code below]
Here is fixed and 'adapted-for-my-case' code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sqlite3.h" /* sqlite3.dll and sqlite3.h both reside in
my <program_source.c>'s folder */
static int checkTable_Callback(
void *unnecessary_here,
int number_of_columns_in_result_row, /* will always be 1 in this case */
char **value_of_count, /* will be either 0 or 1 in this case */
char **label_for_count) { /* will be COUNT(*) normally,
but modified via 'AS table_tablename' in this case*/
printf("COUNT(*) %s\t=>\t%s\n", label_for_count[0], value_of_count[0] );
return 0;
} // end of checkTable_Callback()
char * build_sql(const char *sql_partA, const char *sql_partB) {
size_t size = strlen(sql_partA) + strlen(sql_partB);
char *sql_final = malloc(size + 1);
strcpy(sql_final, sql_partA);
strcat(sql_final, sql_partB);
return sql_final; /* allocated memory to be freed at the end of calling function */
} // end of build_sql()
checkTableCount(sqlite3 *db, char *tablename) {
char *sql = build_sql(
build_sql(
build_sql(
build_sql(
"SELECT COUNT(*) AS table_",
tablename),
" FROM sqlite_master WHERE type='table' AND name='"),
tablename),
"';");
sqlite3_exec(db, sql, checkTable_Callback, 0, NULL);
/* error checking sacrificed for brevity of sample */
free(sql);
}// end of checkTableCount()
sqlite3 * openDb(char * db_name){
sqlite3 *db;
int result_code = sqlite3_open(db_name, &db);
if( result_code != 0 )
fprintf(stderr, "\tError: %s\n\n", sqlite3_errmsg(db));
return db;
} // end of openDb()
int main() {
sqlite3 * db = openDb("testing.db"); /* testing.db already has one table 'family'*/
checkTableCount(db, "family");
checkTableCount(db, "fam"); /* no such table exist */
sqlite3_close(db);
return 0;
} // end of main()
Now this quoted 'adapted-for-my-case' code rightly and correctly outputs the COUNT(*) as follows:
OUTPUT
COUNT(*) table_family => 1
COUNT(*) table_fam => 0
Note that I didn't bother to write a for-loop inside my callback function named checkTable_Callback to iterate through columns as shown in the official sample of callback function on this page because of the fact that our expected result row is certainly going to be only one containing only one column with label modified, via 'AS' clause, into 'table_tablename'. If not modified via 'AS clause', the returned column label would be 'COUNT(*)' in the result row.

HDF5 attributes: Strings are only one character long

I am writing code to produce HDFs, and I'm having a problem with attributes. These attributes are all variable length strings, read in from a text file, but I'm capping them quite generously at 256 characters.
My code compiles and runs with no errors. When I open the file in HDFView, the attributes all have the correct names, but only the first character in the string shows up.
I have started to code for accepting attributes as either single strings, or arrays of strings. I haven't finished that part, so right now it doesn't do anything with the string arrays.
Here is the input file:
year 2013
julian_date 23
start_time_utc 13:54:03
end_time_utc 14:32:05
pixels_per_degree 1000
latitude_corners 34.988644 35.503284 35.960529 36.364529
longitude_corners -119.571877 -118.467979 -120.158424 -119.004395
And here is the code snippet:
#define FIELDSTRINGLENGTH 256
/*...*/
while(fgets(line, ATTRSTRINGLENGTH, topattributefile)!=NULL) {
//parse line into individual words
field=strtok(line," \n");
strncpy(attributename, field, FIELDSTRINGLENGTH);
numfields=0;
field=strtok(NULL," \n");
while(field!=NULL) {
strncpy(attributevalue[numfields++], field, FIELDSTRINGLENGTH);
field=strtok(NULL," \n");
}
if(numfields==0) {printf("ERROR: Attribute %s had no value; skipping\n", attributename);}
else if(numfields>1) {
if(verboseflag) {printf("Making array of %d attributes with name %s:\n",
numfields, attributename);}
for(i=0;i<numfields;i++) {
if(verboseflag) {printf("\t%d: %s\n", i, attributevalue[i]);}
}
}
else {
printf("Making single attribute: %s: %s\n",
attributename, attributevalue[0]);}
//make single attribute
attrdataspaceid = H5Screate(H5S_SCALAR);
attrdatatypeid = H5Tcopy(H5T_C_S1);
status = H5Tset_size(attrdatatypeid, FIELDSTRINGLENGTH);
status = H5Tset_strpad(attrdatatypeid, H5T_STR_NULLTERM);
attributeid = H5Acreate2(fileid, attributename, attrdatatypeid, attrdataspaceid, H5P_DEFAULT, H5P_DEFAULT);
status = H5Awrite(attributeid, H5T_C_S1, attributevalue[0]);
}
}
Here is the stdout of the relevant snippet:
Making top level attributes...
Making single attribute: year: 2013
Making single attribute: julian_date: 23
Making single attribute: start_time_utc: 13:54:03
Making single attribute: end_time_utc: 14:32:05
Making single attribute: pixels_per_degree: 1000
Making array of 4 attributes with name latitude_corners:
0: 34.988644
1: 35.503284
2: 35.960529
3: 36.364529
Making array of 4 attributes with name longitude_corners:
0: -119.571877
1: -118.467979
2: -120.158424
3: -119.004395
Finished making top level attributes.
Finally, here is the metadata of the HDF as read in HDFView.
XXXX.XXXXXX.XXXXXX.hdf (0)
Group size = 1
Number of attributes = 5
year = 2
julian_date = 2
start_time_utc = 1
end_time_utc = 1
pixels_per_degree = 1
Anything strike you as odd here?
Your error is in your call to H5Awrite().
status = H5Awrite(attributeid, H5T_C_S1, attributevalue[0]);
This is writing only a single character. As the definition of H5T_C_S1 is
One-byte, null-terminated string of eight-bit characters.
I normally create a derived type and set it's size. Then call H5Awrite with this type, as the write calls copies the size of this in memory type.
Here's a simple example.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hdf5.h>
#define FILENAME "att_str.h5"
int main(){
hid_t fid;
hid_t att;
hid_t ds;
hid_t type;
herr_t status;
char x[] = "lah lah lah";
int32_t len = 0;
fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
type = H5Tcopy(H5T_C_S1);
len = strlen(x);
status = H5Tset_size(type, len);
ds = H5Screate(H5S_SCALAR);
att = H5Acreate(fid, "test", type, ds, H5P_DEFAULT, H5P_DEFAULT);
status = H5Awrite(att, type, &x);
status = H5Aclose(att);
status = H5Tclose(type);
status = H5Sclose(ds);
status = H5Fclose(fid);
return(EXIT_SUCCESS);
}
Once it is compiled and run the generated file contains:
HDF5 "att_str.h5" {
GROUP "/" {
ATTRIBUTE "test" {
DATATYPE H5T_STRING {
STRSIZE 11;
STRPAD H5T_STR_NULLTERM;
CSET H5T_CSET_ASCII;
CTYPE H5T_C_S1;
}
DATASPACE SCALAR
DATA {
(0): "lah lah lah"
}
}
}
}
Notice how I create a derived type that is the length of string to be written:
len = strlen(x);
status = H5Tset_size(type, len);
The HDF5 group does have an example of writing variable length attributes.

Splitting a comma-delimited string of integers

My background is not in C (it's in Real Studio - similar to VB) and I'm really struggling to split a comma-delimited string since I'm not used to low-level string handling.
I'm sending strings to an Arduino over serial. These strings are commands in a certain format. For instance:
#20,2000,5!
#10,423,0!
'#' is the header indicating a new command and '!' is the terminating footer marking the end of a command. The first integer after '#' is the command id and the remaining integers are data (the number of integers passed as data may be anywhere from 0 - 10 integers).
I've written a sketch that gets the command (stripped of the '#' and '!') and calls a function called handleCommand() when there is a command to handle. The problem is, I really don't know how to split this command up to handle it!
Here's the sketch code:
String command; // a string to hold the incoming command
boolean commandReceived = false; // whether the command has been received in full
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// main loop
handleCommand();
}
void serialEvent(){
while (Serial.available()) {
// all we do is construct the incoming command to be handled in the main loop
// get the incoming byte from the serial stream
char incomingByte = (char)Serial.read();
if (incomingByte == '!')
{
// marks the end of a command
commandReceived = true;
return;
}
else if (incomingByte == '#')
{
// marks the start of a new command
command = "";
commandReceived = false;
return;
}
else
{
command += incomingByte;
return;
}
}
}
void handleCommand() {
if (!commandReceived) return; // no command to handle
// variables to hold the command id and the command data
int id;
int data[9];
// NOT SURE WHAT TO DO HERE!!
// flag that we've handled the command
commandReceived = false;
}
Say my PC sends the Arduino the string "#20,2000,5!". My sketch ends up with a String variable (called command) that contains "20,2000,5" and the commandRecieved boolean variable is set to True so the handleCommand() function is called.
What I would like to do in the (currently useless) handleCommand() function is assign 20 to a variable called id and 2000 and 5 to an array of integers called data, i.e: data[0] = 2000, data[1] = 5, etc.
I've read about strtok() and atoi() but frankly I just can't get my head around them and the concept of pointers. I'm sure my Arduino sketch could be optimised too.
Since you're using the Arduino core String type, strtok and other string.h functions aren't appropriate. Note that you can change your code to use standard C null-terminated strings instead, but using Arduino String will let you do this without using pointers.
The String type gives you indexOf and substring.
Assuming a String with the # and ! stripped off, finding your command and arguments would look something like this:
// given: String command
int data[MAX_ARGS];
int numArgs = 0;
int beginIdx = 0;
int idx = command.indexOf(",");
String arg;
char charBuffer[16];
while (idx != -1)
{
arg = command.substring(beginIdx, idx);
arg.toCharArray(charBuffer, 16);
// add error handling for atoi:
data[numArgs++] = atoi(charBuffer);
beginIdx = idx + 1;
idx = command.indexOf(",", beginIdx);
}
data[numArgs++] = command.substring(beginIdx);
This will give you your entire command in the data array, including the command number at data[0], while you've specified that only the args should be in data. But the necessary changes are minor.
seems to work, could be buggy:
#include<stdio.h>
#include <string.h>
int main(){
char string[]="20,2000,5";
int a,b,c;
sscanf(string,"%i,%i,%i",&a,&b,&c);
printf("%i %i %i\n",a,b,c);
a=b=c=0;
a=atoi(strtok(string,","));
b=atoi(strtok(0,","));
c=atoi(strtok(0,","));
printf("%i %i %i\n",a,b,c);
return 0;
}

Sybase error while fetching columns more then 255 characters

I'm having problems while fetching data from columns which have more than 255 characters
I got such an error message:
Open Client Message:
Message number: LAYER = (1) ORIGIN = (4) SEVERITY = (1) NUMBER = (132)
Message String: ct_fetch(): user api layer: internal common library error: The bind of result set item 3 resulted in truncation.
It fetches only the first 255 rows and truncates the rest.
I have tried to imply below lines before ct_connect but didn't work
CS_BOOL boolv = CS_TRUE;
CS_RETCODE retcode2 = ct_capability ( *connection, CS_GET, CS_CAP_REQUEST, CS_WIDETABLES, &boolv);
here are some part of the code , do you have any suggestion
for (i = 0; i < num_cols; i++) {
/*
** Get the column description. ct_describe() fills the
** datafmt parameter with a description of the column.
*/
retcode = ct_describe(cmd, (i + 1), &datafmt[i]);
if (retcode != CS_SUCCEED) {
ex_error("ex_fetch_data: ct_describe() failed");
break;
}
/*
** update the datafmt structure to indicate that we want the
** results in a null terminated character string.
**
** First, update datafmt.maxlength to contain the maximum
** possible length of the column. To do this, call
** ex_display_len() to determine the number of bytes needed
** for the character string representation, given the
** datatype described above. Add one for the null
** termination character.
*/
datafmt[i].maxlength = ex_display_dlen(&datafmt[i]) + 1;
/*
** Set datatype and format to tell bind we want things
** converted to null terminated strings
*/
datafmt[i].datatype = CS_LONGCHAR_TYPE;
datafmt[i].format = CS_FMT_NULLTERM;
/*
** Allocate memory for the column string
*/
coldata[i].value = (CS_CHAR *) malloc(datafmt[i].maxlength);
if (coldata[i].value == NULL) {
ex_error("ex_fetch_data: malloc() failed");
retcode = CS_MEM_ERROR;
break;
}
/*
** Now bind.
*/
retcode = ct_bind(cmd, (i + 1), &datafmt[i], coldata[i].value,
&coldata[i].valuelen, (CS_SMALLINT *) &coldata[i].indicator);
if (retcode != CS_SUCCEED) {
ex_error("ex_fetch_data: ct_bind() failed");
break;
}
}
.............
.............
.............
/*
** Fetch the rows. Loop while ct_fetch() returns CS_SUCCEED or
** CS_ROW_FAIL
*/
while (((retcode = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED,
&rows_read)) == CS_SUCCEED) || (retcode == CS_ROW_FAIL)) {
Even we have faced problem when we are using Sybase with Uniface,but uniface sybase driver has got a option which will truncate the data and save in some other table but while fetching we have to fetch data from all the tables.
When using the native jconn*.jar drivers you have to set the "?CHARSET=iso_1" parameter on the JDBC driver to connect to a Sybase server with a default charset of Roman 8, otherwise you see this 255 characters truncation issue.
Could this be the problem you are experiencing?

Resources