Getting int values from SQLite - c++

I heard of using sqlite3_prepare_v2 instead of sqlite_exec to get integers from database, but I failed to find any examples. This page wasn't helpful also. Now I am getting strings from database, so I need to parse them with atoi and this seems to be slow and ineffective.
There a lot of similar questions on SO, but they are about obj-c and iOS SDK. I need C/C++ hint or example.
Thanks in advance.

After sqlite3_prepare has succeeded, you must not forget to clean up the statement with sqlite3_finalize.
To get the result records, call sqlite3_step until it does not return SQLITE_ROW.
To get the values of the current result record, call the sqlite3_column_* functions:
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT 42", -1, &stmt, NULL) != SQLITE_OK)
...error...
else {
for (;;) {
int rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE)
break;
if (rc != SQLITE_ROW) {
...error...
break;
}
printf("value: %d\n", sqlite3_column_int(stmt, 0));
}
sqlite3_finalize(stmt);
}

sqlite3_column_int(result, columnNum); will return one column from the current row of your result as an int.
Your prepare function is to prepare your query, it has nothing to do with how the results are interpreted. All data in sqlite3 is stored textually, you use the appropriate function to retrieve a value in the type you believe it should be.

Related

How does prepared statements in Sqlite C++ work

I do not know how to implement prepared statements in my Sqlite3 code
#include <iostream>
#include <sqlite3.h>
#include <stdio.h>
static int callback (void* NotUsed, int argc, char** argv, char** azColName) {
int i;
for (i = 0; i < argc; i++) {
std::cout << ("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
std::cout << ("\n");
return 0;
}
int main (int argc, char* argv[]) {
sqlite3* db;
char* zErrMsg = 0;
int rc;
char* sql;
/* Open database */
rc = sqlite3_open ("test.db", &db);
if (rc) {
std::cerr << "Can't open database: \n" << sqlite3_errmsg (db);
return (0);
}
else {
std::cout << "Opened database successfully\n";
}
std::string newName;
std::cin >> newName;
/* Create SQL statement */
sql = "UPDATE company SET name = newName WHERE id = 1";
/* Execute SQL statement */
rc = sqlite3_exec (db, sql, callback, 0, &zErrMsg);
if (rc != SQLITE_OK) {
std::cout << "SQL error: \n" << zErrMsg;
sqlite3_free (zErrMsg);
}
else {
std::cout << "Records created successfully\n";
}
sqlite3_close (db);
return 0;
}
The user has to input newName and this variable should be used to Update a field in the Database. This way it does not work, because the Sql script is searching for a column. In the internet I found, that I had to use a prepared statement, but I do not know how to implement it.
You start with an sql statement that has placeholders for the parameters that you wish to bind later. Here, I use a single question mark for the placeholder, but there are other options described in the documentation.
std::string sql = "UPDATE company SET name = ? WHERE id = 1";
Then you construct a prepared statement (or "compile", as they say it in sqlite documentation). You'll normally use sqlite_prepare_v2 function, but there are others (for when your statement is encoded in something else than utf-8, for example).
sqlite3_stmt* stmt; // will point to prepared stamement object
sqlite3_prepare_v2(
db, // the handle to your (opened and ready) database
sql.c_str(), // the sql statement, utf-8 encoded
sql.length(), // max length of sql statement
&stmt, // this is an "out" parameter, the compiled statement goes here
nullptr); // pointer to the tail end of sql statement (when there are
// multiple statements inside the string; can be null)
Then you bind the parameter(s). There's a whole bunch of avaliable functions. Which one exactly you use depends on the type
of data that you're binding to the parameter. Here, we bind text, so we use sqlite3_bind_text:
std::string newName = /* get name from user */;
sqlite3_bind_text(
stmt, // previously compiled prepared statement object
1, // parameter index, 1-based
newName.c_str(), // the data
newName.length(), // length of data
SQLITE_STATIC); // this parameter is a little tricky - it's a pointer to the callback
// function that frees the data after the call to this function.
// It can be null if the data doesn't need to be freed, or like in this case,
// special value SQLITE_STATIC (the data is managed by the std::string
// object and will be freed automatically).
So, the prepared statement is ready to go. Now you execute it by passing it to sqlite3_step:
sqlite3_step(stmt); // you'll want to check the return value, read on...
Now, when you step through a statement that's supposed to return rows of a result table, this function will keep returning SQLITE_ROW as long as there are result rows to process, and SQLITE_DONE when there are none left. You can use sqlite3_column_* family of functions to get the single columns from a result row. I'll let you figure this out on your own.
For a simple update statements that you have, sqlite3_step will return SQLITE_DONE on the first call. More info and possible error codes are here.
When it's all done, you finish by destructing the prepared statement.
sqlite3_finalize(stmt);
I hope this should get you started.

RAM consumption inside a C++ program using Sqlite3 blob

I'm using sqlite3 dbms inside a C++ program that I use mainly to store files as blob objects (I know that's not the best option).
Obviously I write them incrementally since they can be sometimes large (40-80MB) in order to do so I have to create first a placeholder of the blob using the binding function sqlite3_bind_zeroblob(...) and after that I open the blob writing and reading incrementally from and to it.
The problem I'm facing is that when i create the blob placeholder (during sqlite3_step) the RAM consumption of my app reaches 80-160MB for 2-3 seconds, once it has been created the RAM consumption goes back to 2-3MB at most.
I do not get why! If they created a way to write to a blob incrementally there is for sure a way to create that stupid placeholder without wasting 160MB of RAM, but I didn't find it. Have you got any suggestion?
sqlite3_stmt* stm = NULL;
sqlite3_blob *BLOB = NULL;
rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stm, NULL);
rc = sqlite3_bind_blob(stm, 1, wpath.c_str(), wpath.size()*sizeof(wchar_t), SQLITE_STATIC);
rc = sqlite3_bind_text(stm, 2, hash.c_str(), hash.size(), SQLITE_STATIC);
rc = sqlite3_bind_zeroblob(stm, 3, size);
rc = sqlite3_bind_int(stm, 4, versione);
rc = sqlite3_bind_blob(stm, 5, last.c_str(), last.size()*sizeof(wchar_t), SQLITE_STATIC);
rc = sqlite3_step(stm);
if (rc != SQLITE_DONE) {
fprintf(stderr, " This file was already present in the database!\n", rc);
return;
}
else {
fprintf(stdout, "Record FILE created successfully\n");
}
It is an issue reported HERE.
And the oficial answer is:
In order for zeroblobs to work as above (using a fixed amount of
memory no matter how big they are) all zeroblobs must be at the end
of the row. In other words, the columns of the table that are
receiving the zeroblobs must be the last columns in the table. If
any non-zero content follows the zeroblob, then the zeroblob is
expanded into a literal sequence of zero bytes, meaning memory must
be allocated for the entire zeroblob.
So you need to change the order to fix it:
sqlite3_stmt* stm = NULL;
sqlite3_blob *BLOB = NULL;
rc = sqlite3_prepare_v2(db, sql.c_str(), -1, &stm, NULL);
rc = sqlite3_bind_blob(stm, 1, wpath.c_str(), wpath.size()*sizeof(wchar_t), SQLITE_STATIC);
rc = sqlite3_bind_text(stm, 2, hash.c_str(), hash.size(), SQLITE_STATIC);
rc = sqlite3_bind_int(stm, 3, versione);
rc = sqlite3_bind_blob(stm, 4, last.c_str(), last.size()*sizeof(wchar_t), SQLITE_STATIC);
rc = sqlite3_bind_zeroblob(stm, 5, size);
rc = sqlite3_step(stm);
if (rc != SQLITE_DONE) {
fprintf(stderr, " This file was already present in the database!\n", rc);
return;
}
else {
fprintf(stdout, "Record FILE created successfully\n");
}

Getting segmentation fault when using pointers in C

I'm currently working on a project where I want to use SQLite to store some data. Everything is working well except when I want to insert new data into the table. When I run the application, I get segmentation fault, but I can't find the problem.
void sqlite(char *id, char *sensorname, char *sensorvalue){
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char *sql;
const char* data = "Callback function called";
/* Open database */
rc = sqlite3_open("/home/macho/Documents/sensor_database.db", &db);
if( rc ){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}else{
fprintf(stderr, "Opened database successfully\n");
}
sql = "INSERT INTO sensors (id,sensorname,sensorvalue) VALUES(";
char* split = ",";
strcat(sql, id);
strcat(sql, ",");
strcat(sql, sensorname);
strcat(sql, ",");
strcat(sql, sensorvalue);
strcat(sql, ");");
rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
if( rc != SQLITE_OK ){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
fprintf(stdout, "Operation done successfully\n");
}
sqlite3_close(db);
}
And in the main, I'm calling the sqlite() function:
sqlite("1","sensor","sensor1");
Any idea what the problem can be?
Thanks!
You assign sql a static (read-only) string and then attempt to append to it. Instead, create a large writeable array either on the stack or use malloc and then assemble your query in that. So
char sql[4096];
strcpy(sql, "INSERT INTO sensors ...
...
Note that you should check for overflow of the buffer based on the lengths of the values.
BTW, the code as written is just asking for an SQL injection attack if accessible to users. Look up Bobby Tables.

C++ SQLExecDirect INSERT doesn't work

I've wrote a simple SQL C++ Wrapper-Class, where I encountered a real strange Problem. When I call an INSERT-Command with SQLExecDirect, the data does not appear in the SQL Database (SQL Server 2012), although SQLRowCount returns one row. The table, which I'm trying to write to, is named "Person" and has four columns (ID (AUTOINCREMENT), Firstname (nvarchar(100)), Lastname (nvarchar(100)), Birthday (date))
Here is my code:
Class-Constructor (EnvHandle, DBCHandle and StmtHandle are class-members)
SQLDatabase::SQLDatabase()
{
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &EnvHandle);
SQLSetEnvAttr(EnvHandle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, EnvHandle, &DBCHandle);
SQLSetConnectAttr(DBCHandle, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);
}
Query-Member-Function
int SQLDatabase::ExecuteNonQuery(std::wstring Command)
{
SQLRETURN RetCode = -1;
SQLINTEGER RowCount = 0;
SQLAllocHandle(SQL_HANDLE_STMT, DBCHandle, &StmtHandle);
RetCode = SQLExecDirect(StmtHandle, (SQLWCHAR*)Command.c_str(), Command.length());
if (RetCode == SQL_SUCCESS || RetCode == SQL_SUCCESS_WITH_INFO)
{
SQLRowCount(StmtHandle, &RowCount);
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
return RowCount;
}
else
{
SQLFreeHandle(SQL_HANDLE_STMT, StmtHandle);
return -1;
}
}
Function-Call in main.cpp
wcout << "Firstname:\n>";
wcin >> Firstname;
wcout << "Lastname:\n>";
wcin >> Lastname;
wcout << "Birthday:\n>";
wcin >> Birthday;
InsRows = database.ExecuteNonQuery(L"INSERT INTO Person (Firstname,Lastname,Birthday) VALUES ('" + Firstname + L"','" + Lastname + L"','" + Birthday + L"')");
if (InsRows == -1)
database.Error();
else
std::wcout << InsRows << " rows affected!" << std::endl;
As already said, "database.ExecuteNonQuery" returns one row.
When take a look at the table in SQL Management Studio, the datarow hasn't been added. I've already traced the queries on the table. The query appears in the tracelog correctly without any additional info. My IDE is Visual Studio 2013.
Any ideas how I could get the data into the table?
Thanks!
Sebastian
In general, whenever a query is executed that changes a table (without error) and the results are not shown, this means that the changes were not committed.
Looking at your code, you have this:
SQLSetConnectAttr(DBCHandle, SQL_ATTR_AUTOCOMMIT,
(SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_IS_INTEGER);}
You set the connection attribute to have auto commit turned off. Either specify you want auto commit on, or issue a call to SQLEndTran to commit the changes.
You're using SQL_AUTOCOMMIT_OFF, which means your statements won't be automatically committed, so your new record is visible only in your transaction and will never be committed in the database. Try to use the auto-commit feature of the MS SQL Server (setting SQL_ATTR_AUTOCOMMIT to SQL_AUTOCOMMIT_ON or just leave it uninformed since it's the default behaviour) or explicitly begin and commit your transaction.
http://msdn.microsoft.com/en-us/library/ms713605(v=vs.85).aspx

sqlite - delete and get all values of some column from affected rows [duplicate]

I heard of using sqlite3_prepare_v2 instead of sqlite_exec to get integers from database, but I failed to find any examples. This page wasn't helpful also. Now I am getting strings from database, so I need to parse them with atoi and this seems to be slow and ineffective.
There a lot of similar questions on SO, but they are about obj-c and iOS SDK. I need C/C++ hint or example.
Thanks in advance.
After sqlite3_prepare has succeeded, you must not forget to clean up the statement with sqlite3_finalize.
To get the result records, call sqlite3_step until it does not return SQLITE_ROW.
To get the values of the current result record, call the sqlite3_column_* functions:
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT 42", -1, &stmt, NULL) != SQLITE_OK)
...error...
else {
for (;;) {
int rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE)
break;
if (rc != SQLITE_ROW) {
...error...
break;
}
printf("value: %d\n", sqlite3_column_int(stmt, 0));
}
sqlite3_finalize(stmt);
}
sqlite3_column_int(result, columnNum); will return one column from the current row of your result as an int.
Your prepare function is to prepare your query, it has nothing to do with how the results are interpreted. All data in sqlite3 is stored textually, you use the appropriate function to retrieve a value in the type you believe it should be.