Commit 8073e496 authored by Manuel's avatar Manuel

Merge branch 'rebase' of gitorious.org:chakra/akabeicore into server-backend

parents 6243baaf af074407
......@@ -5,7 +5,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
if(COMMAND cmake_policy)
cmake_policy(SET CMP0003 NEW)
endif(COMMAND cmake_policy)
endif(COMMAND cmake_policy)
set(MAJOR_AKABEI_VERSION 0 CACHE INT "Akabei's major version number" FORCE)
set(MINOR_AKABEI_VERSION 0 CACHE INT "Akabei's minor version number" FORCE)
......@@ -17,15 +17,14 @@ set(AKABEI_VERSION_STRING "${MAJOR_AKABEI_VERSION}.${MINOR_AKABEI_VERSION}.${PAT
set(QT_MIN_VERSION "4.4.0")
find_package(Qt4 REQUIRED)
find_package(Sqlite REQUIRED)
find_package(LibArchive REQUIRED)
find_package(Sqlite REQUIRED)
add_definitions(${QT_DEFINITIONS} -Wall)
add_definitions(${QT_DEFINITIONS} -W -Wall)
include_directories(
${QT_INCLUDES}
${LIBARCHIVE_INCLUDE_DIR}
${SQLITE_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/lib
......
CREATE TABLE packages (
"ID" INTEGER PRIMARY KEY AUTOINCREMENT,
"Name" TEXT,
"Version" TEXT,
"Filename" TEXT,
"Description" TEXT,
"Groups" TEXT,
"Size" INTEGER,
"InstalledSize" INTEGER,
"MD5SUM" TEXT,
"URL" TEXT,
"BuildDate" INTEGER,
"Arch" TEXT,
"Packager" TEXT,
"Provides" TEXT,
"Conflicts" TEXT,
"Depends" TEXT,
"OptDepends" TEXT,
"Replaces" TEXT,
"Backup" TEXT,
"License" TEXT,
"ProvidesMimetype" TEXT,
"Flags" TEXT,
"ScreenShotURL" TEXT,
"InstallReason" TEXT,
"InstallDate" INTEGER
);
CREATE INDEX "id" on packages (ID ASC);
CREATE INDEX "name" on packages (Name ASC);
CREATE TABLE "files" (
"Package" INTEGER PRIMARY KEY,
"Files" TEXT,
"Prefix" TEXT
);
CREATE TABLE "scriptlets" (
"Package" INTEGER PRIMARY KEY,
"Scriptlet" TEXT
);
CREATE TABLE "hooks" (
"Name" TEXT PRIMARY KEY,
"Hook" TEXT,
"Packages" TEXT
);
CREATE TABLE "groups" (
"ID" INTEGER PRIMARY KEY AUTOINCREMENT,
"Name" TEXT,
"Description" TEXT,
"IconName" TEXT
);
CREATE TABLE "deltas" (
"ID" INTEGER PRIMARY KEY AUTOINCREMENT,
"Package" INTEGER,
"Filename" TEXT,
"VersionFrom" TEXT,
"VersionTo" TEXT,
"MD5SUM" TEXT
);
CREATE TABLE IF NOT EXISTS packages (
`id` INTEGER PRIMARY KEY ASC ,
`name` TEXT NOT NULL ,
`version` TEXT NOT NULL ,
`filename` TEXT NOT NULL ,
`description` TEXT NOT NULL ,
`size` INTEGER NOT NULL ,
`installedsize` INTEGER NULL DEFAULT NULL ,
`md5sum` VARCHAR(32) NOT NULL ,
`url` TEXT NULL DEFAULT NULL ,
`builddate` INTEGER NULL DEFAULT NULL ,
`arch` TEXT NOT NULL ,
`packager` TEXT NOT NULL ,
`backup` TEXT NULL DEFAULT NULL ,
`license` TEXT NULL DEFAULT NULL ,
`providesmimetype` TEXT NULL DEFAULT NULL ,
`flags` TEXT NULL DEFAULT NULL ,
`screenshoturl` TEXT NULL DEFAULT NULL ,
`installreason` INTEGER NULL DEFAULT 0,
`installdate` INTEGER NULL DEFAULT NULL );
CREATE INDEX `packages_name` ON `packages` (`name` ASC) ;
CREATE TABLE IF NOT EXISTS `files` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`files` TEXT NOT NULL ,
`prefix` TEXT NOT NULL ,
CONSTRAINT `fk_files_packages`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `scriptlets` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`scriptlet` TEXT NOT NULL ,
CONSTRAINT `fk_scriptlets_packages`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `hooks` (
`name` TEXT PRIMARY KEY ,
`hook` TEXT NOT NULL );
CREATE TABLE IF NOT EXISTS `groups` (
`id` INTEGER PRIMARY KEY ASC ,
`name` TEXT NOT NULL ,
`description` TEXT NULL DEFAULT NULL ,
`icon` TEXT NULL DEFAULT NULL);
CREATE TABLE IF NOT EXISTS `deltas` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`filename` TEXT NOT NULL ,
`versionfrom` TEXT NOT NULL ,
`versionto` TEXT NOT NULL ,
`md5sum` VARCHAR(32) NOT NULL ,
CONSTRAINT `fk_deltas_packages`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `depends` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`dependency` TEXT NOT NULL ,
CONSTRAINT `fk_requires_package`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` )
ON UPDATE CASCADE);
CREATE TABLE IF NOT EXISTS `conflicts` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`conflict` TEXT NOT NULL ,
CONSTRAINT `fk_conflicts_package`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` )
ON UPDATE CASCADE);
CREATE TABLE IF NOT EXISTS `provides` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`provides` TEXT NOT NULL ,
CONSTRAINT `fk_provides_package`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `optional` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`dependency` TEXT NOT NULL ,
CONSTRAINT `fk_optional_package`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `replaces` (
`id` INTEGER PRIMARY KEY ASC ,
`package` INTEGER NOT NULL ,
`replaces` TEXT NOT NULL ,
CONSTRAINT `fk_replaces_package`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `belongs` (
`package` INTEGER NOT NULL ,
`group` TEXT NOT NULL ,
CONSTRAINT `fk_belongs_packages`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
CREATE TABLE IF NOT EXISTS `belongshook` (
`package` INTEGER NOT NULL ,
`hookname` TEXT NOT NULL ,
CONSTRAINT `fk_hooks_packages`
FOREIGN KEY (`package` )
REFERENCES `packages` (`id` ) );
\ No newline at end of file
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${AKABEI_SQLITE3_INCLUDE}
PARENT_SCOPE
${CMAKE_CURRENT_BINARY_DIR}
)
......@@ -13,17 +14,17 @@ akabeierror.cpp
akabeigroup.cpp
akabeihelpers.c
akabeihelpers.cpp
akabeihook.cpp
akabeihook.cpp
akabeioperation.cpp
akabeioperationrunner.cpp
akabeipackage.cpp
akabeirunnerrunnable_p.cpp
akabeivalidatorrunnable_p.cpp
md5.c
)
SQLiteConnection.cpp)
set(AKABEI_OPERATION_SRCS
operations/akabeiplainhookoperation.cpp
operations/akabeiplainhookoperation.cpp
operations/akabeiplainreinstalloperation.cpp
operations/akabeiplaininstalloperation.cpp
operations/akabeiplainremoveoperation.cpp
......@@ -44,7 +45,7 @@ akabeioperation.h
akabeioperationrunner.h
akabeipackage.h
akabeicore_global.h
)
SQLiteConnection.h)
set(AKABEI_OPERATION_HDRS
operations/akabeiplainreinstalloperation.h
......@@ -66,14 +67,12 @@ operations/akabeiplainscriptletoperation_p.h
add_library(akabeicore SHARED ${AKABEI_CORE_SRCS} ${AKABEI_OPERATION_SRCS} ${AKABEI_MISSING_MOCS})
set_target_properties(akabeicore PROPERTIES VERSION ${MAJOR_AKABEI_VERSION}.${MINOR_AKABEI_VERSION}.${PATCH_AKABEI_VERSION}
SOVERSION ${MAJOR_AKABEI_VERSION}
)
SOVERSION ${MAJOR_AKABEI_VERSION})
target_link_libraries(akabeicore
${QT_QTCORE_LIBRARY}
${SQLITE_LIBRARIES}
${LIBARCHIVE_LIBRARY}
)
${SQLITE_LIBRARIES})
install(TARGETS akabeicore DESTINATION ${LIB_INSTALL_DIR})
install(FILES ${AKABEI_CORE_HDRS} DESTINATION ${INCLUDES_INSTALL_DIR})
......
============================== 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:
SQLiteConnection connection(database name, readonly);
The first argument is the database name (QString), the second is a boolean for
readonly (true) or read-write access (false).
2. Executing a query
Very easy:
connection.query("your query");
This function returns an object of type SQLiteResource which contains the data
obtained through the query (if any).
On this object you can invoke:
resource.getRowsCount();
to know the number of rows.
resource.getDataAt(row number, column name);
to have access to a single cell in the table, as a QVariant.
Row numbers start from zero. Or:
resource.getColumn(column name);
to get an entire column of the table, as a QList<QVariant>.
3. Closing the connection
The destructor does this for you.
4. Other features
4.1 last_insert_rowid() function
This function:
connection.getLastRowId();
returns the last key inserted in the database (0 if no primary key is present).
4.3 Bindings
Bindings are now supported for integers, strings and QByteArrays. Call:
connection.bind(your name, value);
The binding is valid only for the next call of query().
4. Error checking
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. Notes
Please note that this interface is still in beta testing, so it may be buggy
sometimes.
Cheers!
\ No newline at end of file
/* <SQLite3 C++ interface>
Copyright (C) 2011 Lisa "shainer" Vitolo
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
*/
#include <QDebug>
#include "SQLiteConnection.h"
class SQLiteConnection::Private
{
public:
Private(const QString &dbName, bool readonly) : statement(0) {
int flags(readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE);
std::string tmp = dbName.toStdString();
const char* name(tmp.c_str());
int retvalue(sqlite3_open_v2(name, &dbHandle, flags, NULL));
checkForErrors(retvalue);
}
~Private() {
int retvalue(sqlite3_close(dbHandle));
if (retvalue != SQLITE_OK) {
qDebug() << "Error during destruction of SQLiteConnection object";
}
}
void checkForErrors(int);
Row getRowFromStatement(sqlite3_stmt*);
sqlite3 *dbHandle;
sqlite3_stmt *statement;
QList<Row> rows;
QMap<QString, int> intBindings;
QMap<QString, QString> stringBindings;
};
/*
* Constructor
* Establish a connection with dbName
*/
SQLiteConnection::SQLiteConnection(QString const& dbName, bool readonly)
: d(new Private(dbName, readonly))
{
}
/*
* Destructor
* Close connection and stop any transaction that may still be active
*/
SQLiteConnection::~SQLiteConnection()
{
delete d;
}
//FIXME: If a query fails somewhere we should wipe intBindings and stringBindings... else we run into problems with the new one
/*
* Executes a query
*/
SQLiteResource SQLiteConnection::query(const QString &q)
{
d->rows.clear();
/*
* Prepares the statement. _v2 assures us a better error checking
* To future developers: don't fall in the temptation of calling bound.toStdString().c_str() to make things shorter.
* If you do that, then good luck finding out why akabei is suddenly full of syntax errors :D
*/
std::string tmp = q.toStdString();
const char* query(tmp.c_str());
int retvalue = sqlite3_prepare_v2(d->dbHandle, query, -1, &(d->statement), NULL);
d->checkForErrors(retvalue);
if (!d->intBindings.isEmpty()) {
QMap<QString, int>::iterator it = d->intBindings.begin();
QMap<QString, int>::iterator end = d->intBindings.end();
for (; it != end; it++) {
std::string key = it.key().toStdString();
int ret = sqlite3_bind_int64(d->statement, sqlite3_bind_parameter_index(d->statement, key.c_str()), it.value());
d->checkForErrors(ret);
}
d->intBindings.clear();
}
if (!d->stringBindings.isEmpty()) {
QMap<QString, QString>::iterator it = d->stringBindings.begin();
QMap<QString, QString>::iterator end = d->stringBindings.end();
for (; it != end; it++) {
std::string key = it.key().toStdString();
int ret = sqlite3_bind_text16(d->statement, sqlite3_bind_parameter_index(d->statement, key.c_str()), it.value().utf16(),
(it.value().size() + 1) * sizeof(QChar), SQLITE_TRANSIENT);
d->checkForErrors(ret);
}
d->stringBindings.clear();
}
while ((retvalue = sqlite3_step(d->statement)) == SQLITE_ROW) {
d->rows.append(d->getRowFromStatement(d->statement));
}
if (retvalue != SQLITE_DONE && retvalue != SQLITE_OK) {
throw SQLiteException(sqlite3_errmsg(d->dbHandle));
}
SQLiteResource table(d->rows);
d->checkForErrors(sqlite3_finalize(d->statement));
return table;
}
SQLiteConnection::Row SQLiteConnection::Private::getRowFromStatement(sqlite3_stmt* statement)
{
SQLiteConnection::Row row;
for (int i = 0; i < sqlite3_column_count(statement); i++) {
QString name(sqlite3_column_name(statement, i));
QVariant record;
const unsigned char* ptr(0);
int bytes(0);
switch (sqlite3_column_type(statement, i)) {
case SQLITE_TEXT:
ptr = sqlite3_column_text(statement, i);
record.setValue(QString::fromAscii(reinterpret_cast<const char *>(ptr)));
break;
case SQLITE_INTEGER:
record.setValue(sqlite3_column_int(statement, i));
break;
case SQLITE_FLOAT:
record.setValue(sqlite3_column_double(statement, i));
break;
/* I don't use _column_blob because _text adds a NULL terminator at the end */
case SQLITE_BLOB:
ptr = sqlite3_column_text(statement, i);
bytes = sqlite3_column_bytes(statement, i) + 1;
record.setValue(QByteArray::fromRawData(reinterpret_cast<const char *>(ptr), bytes));
break;
/* SQLITE_NULL */
default:
// FIXME: do we really need to throw an exception?
// NULL values are acceptable in a database record.
// throw SQLiteException("NULL data in record");
break;
}
row[name] = record;
}
return row;
}
/*
* Returns the last key inserted
*/
qint64 SQLiteConnection::getLastRowId()
{
sqlite3_int64 id = sqlite3_last_insert_rowid(d->dbHandle);
return static_cast<qint64>(id);
}
/*
* Bindings management
*/
void SQLiteConnection::bind(const QString& name, int value)
{
if (d->intBindings.contains(name) || d->stringBindings.contains(name)) {
return;
}
d->intBindings.insert(name, value);
}
void SQLiteConnection::bind(const QString& name, QString value)
{
if (d->intBindings.contains(name) || d->stringBindings.contains(name)) {
return;
}
d->stringBindings.insert(name, value);
}
void SQLiteConnection::bind(const QString& name, QByteArray value)
{
if (d->intBindings.contains(name) || d->stringBindings.contains(name)) {
return;
}
d->stringBindings.insert(name, value);
}
void SQLiteConnection::Private::checkForErrors(int r)
{
if (r != SQLITE_OK) {
const char *mess = sqlite3_errmsg(dbHandle);
sqlite3_finalize(statement);
throw SQLiteException(mess);
}
}
class SQLiteResource::Private : public QSharedData
{
public:
Private(const QList<Row> &t) : table(t) {}
Private(const Private &p) : QSharedData(p), table(p.table) {}
~Private() {}
QList<Row> table;
};
/********** SQLiteResource **************/
SQLiteResource::SQLiteResource(QList<Row> table) : d(new Private(table)) {}
SQLiteResource::SQLiteResource(const SQLiteResource& other) : d(other.d)
{
}
SQLiteResource::~SQLiteResource()
{
}
SQLiteResource& SQLiteResource::operator=(const SQLiteResource& other)
{
d = other.d;
return *this;
}
QList<QVariant> SQLiteResource::getColumn(const QString& columnName)
{
QList<QVariant> data;
for (QList<QVariantMap>::iterator it = d->table.begin(); it < d->table.end(); it++) {
QVariantMap map = (*it);
if (map.count(columnName) == 0) {
throw SQLiteException("Unexistent column in table");
}
data.append(map[columnName]);
}
return data;
}
QVariant SQLiteResource::getDataAt(int rowNumber, const QString& columnName)
{
if (d->table.isEmpty() || rowNumber >= d->table.size() || d->table[rowNumber].count(columnName) == 0) {
throw SQLiteException("Access to non-existent data");
}
QVariantMap map = d->table[rowNumber];
return map[columnName];
}
int SQLiteResource::getRowsCount()
{
return d->table.size();
}
/* <SQLite3 C++ interface: header file>
Copyright (C) 2011 Lisa "shainer" Vitolo
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
*/
#ifndef _SQLITE_CONNECTION_H
#define _SQLITE_CONNECTION_H
/*
* If there's a God of programming somewhere,
* please forgive these sinners, for they have lost the way
*/
#include <sqlite3.h>
/*
* Instead, these are rather good people indeed
*/
#include <QString>
#include <QMap>
#include <QList>
#include <QVariant>
#include <QByteArray>
#include <exception>
#include <QSharedDataPointer>
/*
* Stores the result of a query, so that the user can access it later.
*/
class SQLiteResource
{
typedef QVariant Value;
typedef QVariantMap Row;
public:
SQLiteResource(QList<Row>);
SQLiteResource(const SQLiteResource &other);
~SQLiteResource();
QVariant getDataAt(int, QString const&);
QList<QVariant> getColumn(QString const&);
int getRowsCount();
SQLiteResource &operator=(const SQLiteResource &other);
private:
class Private;
QSharedDataPointer<Private> d;
};
/*
* Public interface for SQLite3 main functions and utilities
*/
class SQLiteConnection
{
typedef QVariant Value;
typedef QVariantMap Row;
public:
SQLiteConnection(QString const&, bool);
~SQLiteConnection();
SQLiteResource query(const QString &q);
qint64 getLastRowId();
void bind(QString const&, int);
void bind(QString const&, QString);
void bind(QString const&, QByteArray);
private:
// Disable copy constructor and default constructor
SQLiteConnection(SQLiteConnection const&);
class Private;
Private *d;
};
class SQLiteException : public std::exception
{
public:
SQLiteException(const char *m) : message(m) {}
virtual const char *what() const throw() {
return message;
}
private:
const char *message;
};
#endif
......@@ -99,7 +99,7 @@ QHash<QString, Database*> BackendPrivate::performInitialization()
// Fail
return QHash<QString, Database*>();
}
Database *localDb = new Database(dbDir.absoluteFilePath("local.db"));
if (!localDb->isValid()) {
qWarning() << "Local database isn't valid";