Commit 9ac5194f authored by Lisa's avatar Lisa Committed by Daniele Cocca
Browse files

sqlite3++: more advanced method of retrieving data from queries

parent 5d5635e2
============================== INTERFACE USAGE ==============================
This is a very small C++ interface for the SQLite3 library.
SQLiteConnection.h is the only header to include.
1. Opening a connection
Create an object SQLiteConnection:
......@@ -14,20 +15,25 @@ Very easy:
connection.query("Your query here;");
In case of queries with a result, a QList<QVariantMap> is returned with the
affected rows.
If no row is affected or the query doesn't provide a result, this list is empty.
The ending semicolon is mandatory. See the SQLite3 tutorial for explanations
about the syntax used.
Supported data types are integer, double and string: everything else is treated
as "blob" by the SQLite3 library, and converted to a binary string by me.
In case of queries with a result, the result is saved internally until a new
query is called (even if the second query doesn't provide any result!).
You can have access to those data calling
connection.getDataAt(row number starting from 0, column name);
which returns a QVariant, or
connection.getColumn(column name);
which returns a QList<QVariant>, if you want an entire column in one passage.
3. Closing the connection
The destructor does this for you.
4. Error checking
A signal sqlite3Error(QString) is sent whenever any error is detected by the
sqlite3 library. The QString is the error message provided by the library.
An exception of type SQLiteException is thrown whatever any error occurs.
For database opening, closing or for queries, the sqlite3 error message is
provided.
5. Testing
For now, an independent sqlite3++ executable is created in the same directory
......
......@@ -10,21 +10,24 @@
#include "SQLiteConnection.h"
#include <string>
#include <iostream>
using namespace std;
/*
* Constructor
* Establish a connection with dbName
*/
SQLiteConnection::SQLiteConnection(QString const& dbName, bool readonly)
{
int retvalue;
if (readonly)
{
retvalue = sqlite3_open_v2(getCharArray(dbName), &dbHandle, SQLITE_OPEN_READONLY, NULL);
retvalue = sqlite3_open_v2(dbName.toStdString().c_str(), &dbHandle, SQLITE_OPEN_READONLY, NULL);
}
else
{
retvalue = sqlite3_open(getCharArray(dbName), &dbHandle);
retvalue = sqlite3_open(dbName.toStdString().c_str(), &dbHandle);
}
checkForErrors(retvalue);
}
......@@ -40,112 +43,116 @@ SQLiteConnection::~SQLiteConnection()
}
/*
* Executes a query and returns the affected rows (if any)
* Elements in the list are a map <"Column name", "Column value">
* Executes a query and saves any result for further interrogation
*/
QList<QVariantMap> SQLiteConnection::query(QString const& query)
void SQLiteConnection::query(QString query)
{
sqlite3_stmt *statement;
int retvalue;
QList<QVariantMap> rows;
int retvalue;
rows.clear();
/*
* Prepares the statement. _v2 assures us a better error checking
*/
retvalue = sqlite3_prepare_v2(dbHandle, getCharArray(query), -1, &statement, NULL);
string tmp = query.toStdString();
retvalue = sqlite3_prepare_v2(dbHandle, tmp.c_str(), -1, &statement, NULL);
checkForErrors(retvalue);
/*
* This method retrieves the affected rows one by one, if any
*/
while ((retvalue = sqlite3_step(statement)) == SQLITE_ROW) {
while ((retvalue = sqlite3_step(statement)) == SQLITE_ROW)
{
QVariantMap row;
QString tmp; /* needed for some conversions */
/*
* Every column is scanned and added to the map for a particular row
*/
for (int col = 0; col < sqlite3_column_count(statement); col++) {
QVariant data;
QString columnName(sqlite3_column_origin_name(statement, col));
for (int i = 0; i < sqlite3_column_count(statement); i++)
{
const char *name = sqlite3_column_name(statement, i);
QString cName(name);
QVariant record;
/*
* Try to understand the type of the column data
* When "blob" (anything not text, integer or float), a raw string of binary data is used
* I know that's quite ugly, but I couldn't find a better method starting with what SQLite3 provides
*/
switch (sqlite3_column_type(statement, col))
switch(sqlite3_column_type(statement, i))
{
case SQLITE_BLOB:
case SQLITE_TEXT:
{
const void *blob = sqlite3_column_blob(statement, col);
tmp = QString::fromUtf8((const char *)blob);
data.setValue(tmp);
const unsigned char *text = sqlite3_column_text(statement, i);
QString textValue((const char *)text);
record.setValue(textValue);
break;
}
case SQLITE_INTEGER:
{
int intValue = sqlite3_column_int(statement, col);
data.setValue(intValue);
int integer = sqlite3_column_int(statement, i);
record.setValue(integer);
break;
}
case SQLITE_FLOAT:
{
double dValue = sqlite3_column_double(statement, col);
data.setValue(dValue);
double floating = sqlite3_column_double(statement, i);
record.setValue(floating);
break;
}
case SQLITE_TEXT:
/* I don't use _column_blob because _text adds a NULL terminator at the end */
case SQLITE_BLOB:
{
const unsigned char *text = sqlite3_column_text(statement, col);
QString tmp((const char *)text);
data.setValue(tmp);
const unsigned char *blob = sqlite3_column_text(statement, i);
QByteArray byte = QByteArray::fromRawData((const char *)blob, sqlite3_column_bytes(statement, i) + 1);
record.setValue(byte);
break;
}
/* SQLITE_NULL */
default:
{
emit sqlite3Error("Null data in record");
throw SQLiteException("Null data in record");
break;
}
}
row.insert(columnName, data);
row[cName] = record;
}
rows.append(row);
}
/*
* In this case, DONE indicates no more row needs processing, not an error
*/
if (retvalue != SQLITE_DONE && retvalue != SQLITE_OK)
{
emit sqlite3Error(sqlite3_errmsg(dbHandle));
throw SQLiteException(sqlite3_errmsg(dbHandle));
}
/*
* Destroys the statement
*/
retvalue = sqlite3_finalize(statement);
retvalue = sqlite3_finalize(statement); /* destroys the statement */
checkForErrors(retvalue);
return rows;
}
const char *SQLiteConnection::getCharArray(QString const& s)
QVariant SQLiteConnection::getDataAt(int rowNumber, QString const& columnName)
{
if (rows.isEmpty() || rowNumber > rows.length() || rows[rowNumber].count(columnName) == 0)
{
throw SQLiteException("Access to non-existent data");
}
QVariantMap map = rows[rowNumber];
return map[columnName];
}
QList<QVariant> SQLiteConnection::getColumn(const QString& columnName)
{
string tmp = s.toStdString();
return tmp.c_str();
QList<QVariant> data;
for (QList<QVariantMap>::iterator it = rows.begin(); it < rows.end(); it++)
{
QVariantMap map = (*it);
if (map.count(columnName) == 0)
{
throw SQLiteException("Unexistent column in table");
}
data.append(map[columnName]);
}
return data;
}
void SQLiteConnection::checkForErrors(int r)
{
if (r != SQLITE_OK)
{
emit sqlite3Error(sqlite3_errmsg(dbHandle));
throw SQLiteException(sqlite3_errmsg(dbHandle));
}
}
......@@ -21,12 +21,13 @@
* Instead, these are rather good people indeed
*/
#include <QString>
#include <QList>
#include <QVariantMap>
#include <QObject>
#include <QMap>
#include <QList>
#include <QVariant>
#include <QByteArray>
using namespace std;
#include <exception>
/*
* Public interface for SQLite3 main functions and utilities
......@@ -37,19 +38,40 @@ class SQLiteConnection : QObject
private:
sqlite3 *dbHandle;
sqlite3_stmt *statement;
QList<QVariantMap> rows;
const char *getCharArray(QString const&);
const char *getCharArray(QString);
void checkForErrors(int);
public:
SQLiteConnection(QString const&, bool);
SQLiteConnection(QString const& , bool);
~SQLiteConnection();
QList<QVariantMap> query(QString const&);
void query(QString);
QVariant getDataAt(int, QString const&);
QList<QVariant> getColumn(QString const&);
signals:
void sqlite3Error(QString);
};
class SQLiteException : public std::exception
{
private:
const char *message;
public:
SQLiteException(const char *m)
{
message = m;
}
virtual const char *what() const throw()
{
return message;
}
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment