Reputation: 33813
I have the following code :
sqlite *sql = new sqlite; // create a new sqlite object.
const char *dbFile = "database.db"; // sqlite database file.
sql->open(dbFile); // open the connection.
sql->query("SELECT * FROM categories"); // make a query.
int numRows = sql->numRows(); // get the number of rows.
const unsigned char *result[numRows]; // an array to store data that will be brought in from the database.
int index = 0; // counter
while(sql->fetch() != SQLITE_DONE){
// store the data into an array
result[index] = sql->getValue("name");
index++;
// print the data directly without storing in an array
cout << sql->getValue("name") << endl;
}
sql->close();
// print the content of the `result` array.
for(int i=0;i<numRows;i++){
cout << result[i] << endl;
}
The result:
Notice:
The method sql->getValue("name");
return const unsigned char*
data.
As you seen in the result image, why when print the data directly, appear without problems, while when print the same data stored in the array does not appear ?
Firstly: I thank everyone who contributed(by comment or answer) to helping me to reach to a solution for my problem. their answers really deserve the best answer.
sqlite *sql = new sqlite;
const char *dbFile = "database.db";
sql->open(dbFile);
sql->query("SELECT * FROM categories");
int numRows = sql->numRows();
char **result = (char**)malloc(sizeof(char*) * numRows);
int index = 0;
while(sql->fetch() != SQLITE_DONE){
result[index] = (char*)malloc(sizeof(char*) * CHAR_MAX);
strcpy(result[index], reinterpret_cast<const char*> (sql->getValue("name")));
index++;
cout << sql->getValue("name") << endl;
}
cout << "\n\n" << endl;
sql->close();
for(int i=0;i<numRows;i++){
cout << *(result+i) << endl;
}
The result:
Upvotes: 0
Views: 2239
Reputation: 145299
Okay, replace this original code:
const unsigned char *result[numRows]; // an array to store data that will be brought in from the database.
int index = 0; // counter
while(sql->fetch() != SQLITE_DONE){
// store the data into an array
result[index] = sql->getValue("name");
index++;
}
with this:
#include <string>
#include <vector>
using std::string; using std::vector;
⋮
vector<string> result;
while( sql->fetch() != SQLITE_DONE )
{
result.emplace_back( reinterpret_cast<const char*>(
sql->getValue("name")
) );
}
And also replace the printf
with cout <<
, or else, in the printf
call replace result[i]
with result[i].c_str()
.
All assuming that the original code works.
Disclaimer: code not touched by compiler.
Addendum:
You most recent code-that-doesn't-compile,
const unsigned char **result = (const unsigned char**)malloc(sizeof(const unsigned char*) * numRows);
int index = 0;
while(sql->fetch() != SQLITE_DONE){
result[index] = (const unsigned char*)malloc(sizeof(const unsigned char*) * CHAR_MAX);
strcpy(result[index], sql->getValue("name"));
index++;
cout << sql->getValue("name") << endl;
}
… can be expressed in C-style C++ as
char** result = new char*[numRows]();
int index = 0;
while( sql->fetch() != SQLITE_DONE )
{
assert( index < numRows );
char const* const s = reinterpret_cast<char*>( sql->getValue( "name" ) );
result[index] = new char[1 + strlen( s )];
strcpy( result[index], s );
cout << s << endl;
++index;
}
Again assuming that the original code worked.
Disclaimer: Again, the code has not been touched by a compiler. Also, readers should note that using new
directly, while that improves on using malloc
, is ungood practice. Good practice would be to use std::vector
and std::string
, as I advised first of all.
Upvotes: 1
Reputation: 2059
Your problem is that you're assigning a pointer rather than copying the string. This is an important distinction.
Assigning the pointer doesn't "store the data" as your comment suggests, it simply copies the pointer. SQLite discards the data after it's done with the record, so its buffers are free to use with another record. By the time you go to print out the string, the pointer is no longer valid.
In order to retain the string for later printing you'll need to copy the string to your own buffer.
This means you'll need to allocate buffer space for the string, copy the string, and then free the space when you're done with it.
It sounds like you want to avoid using C++ features, so you'll want to look at malloc()
, free()
, and strncpy()
to accomplish your task. Otherwise, if you wanted to use the C++ STL String
class, that would solve your problem nicely.
Upvotes: 1
Reputation: 48635
I have recently started looking at Sqlite myself so I happen to have some code I knocked up that I have extracted to give as an exampe. This is not supposed to be particularly performant just to get the job done.
It also includes code to get the SQL REGEXP
function working.
The idea is to add elements to a data structure during the select callback
. The data structure is a std::vector
of std::map
. The std::map
being the table's colum data mapped from the column names and the std::vector
containing the rows of columns:
#include <map>
#include <regex>
#include <string>
#include <vector>
#include <iterator>
#include <iostream>
#include <sqlite3.h>
typedef std::map<std::string, std::string> result;
typedef std::vector<result> result_vec;
#define log(msg) std::cout << msg << std::endl
static int select_callback(void* user, int argc, char** argv, char** azColName)
{
result_vec& v = *static_cast<result_vec*>(user);
result r;
for(int i = 0; i < argc; ++i)
if(argv[i])
r.emplace(azColName[i], argv[i]);
v.emplace_back(std::move(r));
return 0;
}
static void regexp_callback(sqlite3_context* context, int argc,
sqlite3_value** argv)
{
unsigned count = 0;
if(argc == 2)
{
const char* pattern = (const char*) sqlite3_value_text(argv[0]);
const char* value = (const char*) sqlite3_value_text(argv[1]);
if(pattern && value)
{
std::string s = value;
std::regex r(pattern, std::regex::icase);
std::smatch m;
if(std::regex_search(s, m, r))
count = m.size();
}
}
sqlite3_result_int(context, (int) count);
}
sqlite3* open(const std::string& name)
{
sqlite3* db;
if(sqlite3_open(name.c_str(), &db) != SQLITE_OK)
{
log("ERROR: opening db: " << name);
log("ERROR: " << sqlite3_errmsg(db));
return nullptr;
}
sqlite3_create_function_v2(db, "REGEXP", 2, SQLITE_ANY, 0, regexp_callback, NULL, NULL, NULL);
return db;
}
bool select(sqlite3* db, const std::string& sql, result_vec& v)
{
char* error = 0;
if(sqlite3_exec(db, sql.c_str(), select_callback, &v, &error) != SQLITE_OK)
{
log("ERROR: " << error);
log("ERROR: " << sqlite3_errmsg(db));
log(" SQL: " << sql);
sqlite3_free(error);
return false;
}
return true;
}
int main()
{
sqlite3* db = open("mydatabase.db");
if(db)
{
result_vec results;
if(!select(db, "select * from mytable", results))
return 1;
for(auto&& row: results)
for(auto&& col: row)
std::cout << col.first << ": " << col.second << '\n';
}
}
NOTE: I just read that you are not looking to use std::vector
or C++11 features so this answer is not what you are looking for (although it may still be of use to you). However I will leave it for other people who may google this question.
Upvotes: 1