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.
Related
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
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 */
The function below is supposed to check whether or not a specific entry exists in a table. If the entry exists, it returns a 1. If it doesn't, it returns a 0. However, it only returns a 0.
Table used: Database( ID int Primary Key, char(100) Place, char(100) Room ), all are NOT NULL. All of the queries shown in the results below are in Database.
//database is already open at this point
int searchForRoom(sqlite3 *db, char *place, char *room){
int result = 1;
sqlite3_stmt *stmt;
//Create SQL statement
char * sql = sqlite3_mprintf(
"Select EXISTS(Select * From Database "
"Where Place = '%q' and Room = '%q');"
, place, room);
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
if (sqlite3_step(stmt) != SQLITE_ROW) {
printf("ERROR 1 reading data: %s\n", sqlite3_errmsg(db));
}
//only this line of code does not output the correct result
result = sqlite3_column_int(stmt, 0);
if (sqlite3_step(stmt) != SQLITE_DONE) {
printf("ERROR 2 reading data: %s\n", sqlite3_errmsg(db));
}
printf("%s, %s evals to %d\n", place, room, result);
sqlite3_finalize(stmt);
return result;
}
The result:
place1, venue1 evals to 0
place2, venue2 evals to 0
place3, venue3 evals to 0
"Select EXISTS(Select * From Database "
The EXISTS query returns either 0 (when no such row(s) exist) or 1 (when they do). It returned 0, so no such rows existed.
I don't know why or how, but it seems that restarting my computer actually solved the problem. The function now works perfectly fine. Sorry for wasting everyone's time.
So I'm trying to build my sql statement in the for loop.
But on executing, it shows error at the very first point of the string.
Is the concatenation method wrong, cause I printed the sql statement and manually execute in postgresql and it works.
Also I had tried
char *sqlStatement = "INSERT INTO record()..VALUES();\
INSERT INTO record()..VALUES();\
INSERT INTO record()..VALUES();
"
That works.
Note that I have reduced the loop to only once and reduced the number of columns for brevity.
The code:
char sqlStatement[8000];
for(int i=0;i<1;i++) {
sprintf(&sqlStatement[0] + strlen(sqlStatement), "INSERT INTO record (\"user_id\", \"filename\", \"ports\", \"timestamp\" ... )VALUES (1, 'example%d', 0, '123456789%d', ... );", i, i,);
}
pgResult = PQexec(pgConn, sqlStatement);
if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) {
printf("%s", PQresultErrorMessage(pgResult));
PQclear(pgResult);
closeSQL(pgConn);
exit(-1);
}
Error message:
ERROR: syntax error at or near ""
LINE 1: INSERT INTO captures ("user_id", "filename", "ports", "time...
^
You're calling strlen(sqlStatement) but sqlStatement is uninitialized at that point. That's undefined behavior.
Put
sqlStatement[0] = '\0';
before the loop to start out with an empty string.
By the way, exit(-1) is wrong. The only standard C exit values are 0/EXIT_SUCCESS and EXIT_FAILURE. On Unix you can use any value between 0 and 255. I'm not sure about Windows but it's probably similar. I don't know any OS where -1 is valid.
char sqlStatement[11111];
size_t_pos;
pos=0;
pos += sprintf(sqlStatement+pos, "INSERT INTO record() VALUES(%s);\n" , "Argh!" );
pos += sprintf(sqlStatement+pos, "INSERT INTO record(...);\n" ... );
pos += sprintf(sqlStatement+pos, "INSERT INTO record(...);\n" ... );
...
A real program should of course first check the return value from sprintf, before using it to increment the pos position.
I've got stuck on my homework with SQLite. I use 2 columns; the first for product, the second for count. A user adds new products, which updates the count. We have to control, that the user doesn't add the same product again, or prevent him from picking more units than are available. We have to use it frequently, so I created functions:
int exists(char *param, sqlite3** ppDb) //0 if product exists
{
int error = 0;
char *a = NULL;
sqlite3_stmt **ppStmt = NULL;
const char **pzTail = NULL;
char *zSQL = sqlite3_mprintf("SELECT 'products' FROM 'table' WHERE 'products' LIKE '%q'", param);
//HERE IT FALS
error = sqlite3_prepare_v2(
*ppDb, /* Database handle */
zSQL, /* SQL statement, UTF-8 encoded */
(sizeof(zSQL)+1), /* Maximum length of zSql in bytes. */
ppStmt, /* OUT: Statement handle */
pzTail /* OUT: Pointer to unused portion of zSql */
);
sqlite3_free(zSQL);
a = (char*) sqlite3_column_text(*ppStmt, 0);
return strcmp(a, param); //0 if same -> product is in db yet
}
//similar one for count
Call
int main(int argc, const char *argv[])
{
sqlite3 *pDb;
int error = 0;
//parsing input
error = sqlite3_open(argv[1], &pDb);
if (error == 0)
{
sqlite3_exec(
pDb, /* An open database */
"CREATE TABLE 'table' ('products', 'quantity')", /* SQL */
0, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
if (exists(param[1], &pDb) == 0)
{
fprintf (stderr, "ERROR: Product exists yet\n");
}
else
{
char *zSQL = sqlite3_mprintf("INSERT INTO 'table' VALUES ('%q', '0')", param[1]);
error = sqlite3_exec(
pDb, /* An open database */
zSQL, /* SQL to be evaluated */
0, /* Callback function */
NULL, /* 1st argument to callback */
NULL /* Error msg written here */
);
sqlite3_free(zSQL);
if (error == 0) printf("Added\n");
else printf("%i", error);
}
}
else return 1;
return 0;
}
It fails on sqlite3_prepare_v2. I expect there is a problem with the pointer on pDb, but I wasn't able to fix it (I'm not fan of pointers - too strong a tool for beginner). When it fails, the debugger stacked on line 93396 in sqlite3.c (*ppStmt = 0; - it writes somewhere, where it should'nt).
Compiled on linux x64:
gcc -std=c99 -Wall -pedantic -Wextra -Werror -DSQLITE_THREADSAFE=0 -ldl -o sqlite main.c sqlite3.c
Nothing wrong (if I've copied wrongly brackets, ignore it - it's not the problem), SQLite 3.7.14.1
Sorry for my English, I'm from Czech.
sqlite3_prepare_v2 wants to write the statement pointer into your output variable, but you are not giving it a pointer to this pointer variable, you are giving it a NULL pointer.
Use:
sqlite3_stmt *pStmt;
sqlite3_prepare_v2(..., &pStmt, ...);
Also note that identifiers should be quoted with "double quotes" or [brackets] or
`backticks`
but not with 'single quotes', which are used for literal strings.