Replaced "is a" SQLiteSyncSource with "has a" SQLiteUtil instance in SQLiteContactSource.

This is a first step towards deriving SQLiteContactSource from a sync source base
class which does change tracking.


git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@483 15ad00c4-1369-45f4-8270-35d70d36bdcd
This commit is contained in:
Patrick Ohly 2008-01-12 23:22:43 +00:00
parent e43f6012b3
commit 420afaf14b
7 changed files with 182 additions and 173 deletions

11
HACKING
View File

@ -140,10 +140,12 @@ Building a Release
- upload new files to sf.net
- remove files from www.estamos.de so that downloads access sf.net
- update entries on the web about the release:
http://maemo.org/downloads/product/syncevolution/
http://maemo.org/downloads/product/OS2006/syncevolution/
http://maemo.org/downloads/product/OS2007/syncevolution/
http://www.modmyiphone.com/ (?)
http://www.estamos.de/blog/wp-admin
http://www.estamos.de/projects/SyncEvolution/Roadmap.html
http://freshmeat.net/projects/syncevolution/
Compiling for Maemo
@ -195,6 +197,13 @@ Compiling for Mac OS X
<path>/configure --with-sync4j-src= --enable-addressbook
make CFLAGS="-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386" \
LDFLAGS="-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386"
./configure --enable-addressbook \
CXXFLAGS="-O -g -arch i386 -arch ppc" \
CFLAGS="-O -g -arch i386 -arch ppc" \
LDFLAGS="-arch i386 -arch ppc" --disable-dependency-tracking
TODO: --disable-dependency-tracking an client-api weiterreichen
Fine-grained memory checking:
MallocStackLogging=1 MallocStackLoggingNoCompact=1 \

View File

@ -240,21 +240,21 @@ AC_ARG_ENABLE(addressbook, AS_HELP_STRING([--enable-addressbook], [enable access
enable_evo="no"
enable_any="no"
if test "$enable_ebook" = "yes"; then
test "x${EBOOKFOUND}" == "xyes" || AC_MSG_ERROR(["--enable-ebook requires pkg-config information for libebook, which was not found"])
test "x${EBOOKFOUND}" == "xyes" || AC_MSG_ERROR([--enable-ebook requires pkg-config information for libebook, which was not found])
AC_DEFINE(ENABLE_EBOOK, 1, [libebook available])
enable_evo="yes"
enable_any="yes"
fi
if test "$enable_ecal" = "yes"; then
test "x${ECALFOUND}" == "xyes" || AC_MSG_ERROR(["--enable-ecal requires pkg-config information for libecal, which was not found"])
test "x${ECALFOUND}" == "xyes" || AC_MSG_ERROR([--enable-ecal requires pkg-config information for libecal, which was not found"])
AC_DEFINE(ENABLE_ECAL, 1, [libecal available])
enable_evo="yes"
enable_any="yes"
fi
if test "$enable_sqlite" = "yes"; then
test "x${SQLITEFOUND}" == "xyes" || AC_MSG_ERROR(["--enable-sqlite requires pkg-config information for sqlite, which was not found"])
test "x${SQLITEFOUND}" == "xyes" || AC_MSG_ERROR([--enable-sqlite requires pkg-config information for sqlite3, which was not found])
AC_DEFINE(ENABLE_SQLITE, 1, [sqlite available])
enable_any="yes"
fi

View File

@ -62,8 +62,8 @@ SYNCEBOOK_SOURCES = \
EvolutionContactSource.cpp
SYNCSQLITE_SOURCES = \
SQLiteSyncSource.h \
SQLiteSyncSource.cpp \
SQLiteUtil.h \
SQLiteUtil.cpp \
SQLiteContactSource.h \
SQLiteContactSource.cpp

View File

@ -32,28 +32,6 @@
using namespace vocl;
const char *SQLiteContactSource::getDefaultSchema()
{
return
"BEGIN TRANSACTION;"
"CREATE TABLE ABMultiValue (UID INTEGER PRIMARY KEY, record_id INTEGER, property INTEGER, identifier INTEGER, label INTEGER, value TEXT);"
"CREATE TABLE ABMultiValueEntry (parent_id INTEGER, key INTEGER, value TEXT, UNIQUE(parent_id, key));"
"CREATE TABLE ABMultiValueEntryKey (value TEXT, UNIQUE(value));"
"CREATE TABLE ABMultiValueLabel (value TEXT, UNIQUE(value));"
"CREATE TABLE ABPerson (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, First TEXT, Last TEXT, Middle TEXT, FirstPhonetic TEXT, MiddlePhonetic TEXT, LastPhonetic TEXT, Organization TEXT, Department TEXT, Note TEXT, Kind INTEGER, Birthday TEXT, JobTitle TEXT, Nickname TEXT, Prefix TEXT, Suffix TEXT, FirstSort TEXT, LastSort TEXT, CreationDate INTEGER, ModificationDate INTEGER, CompositeNameFallback TEXT);"
"INSERT INTO ABMultiValueLabel VALUES('_$!<Mobile>!$_');"
"INSERT INTO ABMultiValueLabel VALUES('_$!<Home>!$_');"
"INSERT INTO ABMultiValueLabel VALUES('_$!<Work>!$_');"
"INSERT INTO ABMultiValueEntryKey VALUES('CountryCode');"
"INSERT INTO ABMultiValueEntryKey VALUES('City');"
"INSERT INTO ABMultiValueEntryKey VALUES('Street');"
"INSERT INTO ABMultiValueEntryKey VALUES('State');"
"INSERT INTO ABMultiValueEntryKey VALUES('ZIP');"
"INSERT INTO ABMultiValueEntryKey VALUES('Country');"
"COMMIT;"
;
}
enum {
PERSON_LAST,
PERSON_MIDDLE,
@ -72,9 +50,9 @@ enum {
LAST_COL
};
const SQLiteSyncSource::Mapping *SQLiteContactSource::getConstMapping()
void SQLiteContactSource::open()
{
static const Mapping mapping[LAST_COL] = {
static const SQLiteUtil::Mapping mapping[LAST_COL + 1] = {
{ "Last", "ABPerson" },
{ "Middle", "ABPerson" },
{ "First", "ABPerson" },
@ -87,44 +65,64 @@ const SQLiteSyncSource::Mapping *SQLiteContactSource::getConstMapping()
{ "Note", "ABPerson", "NOTE" },
{ "Birthday", "ABPerson", "BIRTHDAY" },
{ "JobTitle", "ABPerson", "ROLE" },
{ "Nickname", "ABPerson", "NICKNAME" }
{ "Nickname", "ABPerson", "NICKNAME" },
{ NULL }
};
static const char *schema =
"BEGIN TRANSACTION;"
"CREATE TABLE ABMultiValue (UID INTEGER PRIMARY KEY, record_id INTEGER, property INTEGER, identifier INTEGER, label INTEGER, value TEXT);"
"CREATE TABLE ABMultiValueEntry (parent_id INTEGER, key INTEGER, value TEXT, UNIQUE(parent_id, key));"
"CREATE TABLE ABMultiValueEntryKey (value TEXT, UNIQUE(value));"
"CREATE TABLE ABMultiValueLabel (value TEXT, UNIQUE(value));"
"CREATE TABLE ABPerson (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, First TEXT, Last TEXT, Middle TEXT, FirstPhonetic TEXT, MiddlePhonetic TEXT, LastPhonetic TEXT, Organization TEXT, Department TEXT, Note TEXT, Kind INTEGER, Birthday TEXT, JobTitle TEXT, Nickname TEXT, Prefix TEXT, Suffix TEXT, FirstSort TEXT, LastSort TEXT, CreationDate INTEGER, ModificationDate INTEGER, CompositeNameFallback TEXT);"
"INSERT INTO ABMultiValueLabel VALUES('_$!<Mobile>!$_');"
"INSERT INTO ABMultiValueLabel VALUES('_$!<Home>!$_');"
"INSERT INTO ABMultiValueLabel VALUES('_$!<Work>!$_');"
"INSERT INTO ABMultiValueEntryKey VALUES('CountryCode');"
"INSERT INTO ABMultiValueEntryKey VALUES('City');"
"INSERT INTO ABMultiValueEntryKey VALUES('Street');"
"INSERT INTO ABMultiValueEntryKey VALUES('State');"
"INSERT INTO ABMultiValueEntryKey VALUES('ZIP');"
"INSERT INTO ABMultiValueEntryKey VALUES('Country');"
"COMMIT;";
return mapping;
}
void SQLiteContactSource::open()
{
SQLiteSyncSource::open();
m_sqlite.open(getName(),
m_id,
mapping,
schema);
// query database for certain constant indices
m_addrCountryCode = findKey("ABMultiValueEntryKey", "value", "CountryCode");
m_addrCity = findKey("ABMultiValueEntryKey", "value", "City");
m_addrStreet = findKey("ABMultiValueEntryKey", "value", "Street");
m_addrState = findKey("ABMultiValueEntryKey", "value", "State");
m_addrZIP = findKey("ABMultiValueEntryKey", "value", "ZIP");
m_typeMobile = findKey("ABMultiValueLabel", "value", "_$!<Mobile>!$_");
m_typeHome = findKey("ABMultiValueLabel", "value", "_$!<Home>!$_");
m_typeWork = findKey("ABMultiValueLabel", "value", "_$!<Work>!$_");
m_addrCountryCode = m_sqlite.findKey("ABMultiValueEntryKey", "value", "CountryCode");
m_addrCity = m_sqlite.findKey("ABMultiValueEntryKey", "value", "City");
m_addrStreet = m_sqlite.findKey("ABMultiValueEntryKey", "value", "Street");
m_addrState = m_sqlite.findKey("ABMultiValueEntryKey", "value", "State");
m_addrZIP = m_sqlite.findKey("ABMultiValueEntryKey", "value", "ZIP");
m_typeMobile = m_sqlite.findKey("ABMultiValueLabel", "value", "_$!<Mobile>!$_");
m_typeHome = m_sqlite.findKey("ABMultiValueLabel", "value", "_$!<Home>!$_");
m_typeWork = m_sqlite.findKey("ABMultiValueLabel", "value", "_$!<Work>!$_");
}
void SQLiteContactSource::close()
{
m_sqlite.close();
}
void SQLiteContactSource::beginSyncThrow(bool needAll,
bool needPartial,
bool deleteLocal)
{
syncml_time_t lastSyncTime = anchorToTimestamp(getLastAnchor());
SQLiteUtil::syncml_time_t lastSyncTime = anchorToTimestamp(getLastAnchor());
eptr<sqlite3_stmt> all(prepareSQL("SELECT ROWID, CreationDate, ModificationDate FROM ABPerson;"));
while (checkSQL(sqlite3_step(all)) == SQLITE_ROW) {
string uid = toString(SQLITE3_COLUMN_KEY(all, 0));
eptr<sqlite3_stmt> all(m_sqlite.prepareSQL("SELECT ROWID, CreationDate, ModificationDate FROM ABPerson;"));
while (m_sqlite.checkSQL(sqlite3_step(all)) == SQLITE_ROW) {
string uid = m_sqlite.toString(SQLITE3_COLUMN_KEY(all, 0));
m_allItems.addItem(uid);
// find new and updated items by comparing their creation resp. modification time stamp
// against the end of the last sync
if (needPartial) {
syncml_time_t creationTime = getTimeColumn(all, 1);
syncml_time_t modTime = getTimeColumn(all, 2);
SQLiteUtil::syncml_time_t creationTime = m_sqlite.getTimeColumn(all, 1);
SQLiteUtil::syncml_time_t modTime = m_sqlite.getTimeColumn(all, 2);
if (creationTime > lastSyncTime) {
m_newItems.addItem(uid);
@ -143,8 +141,8 @@ void SQLiteContactSource::beginSyncThrow(bool needAll,
// TODO: currently syncevolution resets the last anchor in case of
// a failure and thus forces a slow sync - avoid that for SQLite
// database sources
eptr<sqlite3_stmt> start(prepareSQL("BEGIN TRANSACTION;"));
checkSQL(sqlite3_step(start));
eptr<sqlite3_stmt> start(m_sqlite.prepareSQL("BEGIN TRANSACTION;"));
m_sqlite.checkSQL(sqlite3_step(start));
if (deleteLocal) {
@ -160,15 +158,15 @@ void SQLiteContactSource::beginSyncThrow(bool needAll,
void SQLiteContactSource::endSyncThrow()
{
// complete the transaction started in beginSyncThrow()
eptr<sqlite3_stmt> end(prepareSQL("COMMIT;"));
checkSQL(sqlite3_step(end));
eptr<sqlite3_stmt> end(m_sqlite.prepareSQL("COMMIT;"));
m_sqlite.checkSQL(sqlite3_step(end));
}
void SQLiteContactSource::exportData(ostream &out)
{
eptr<sqlite3_stmt> all(prepareSQL("SELECT ROWID FROM ABPerson;"));
while (checkSQL(sqlite3_step(all)) == SQLITE_ROW) {
string uid = toString(SQLITE3_COLUMN_KEY(all, 1));
eptr<sqlite3_stmt> all(m_sqlite.prepareSQL("SELECT ROWID FROM ABPerson;"));
while (m_sqlite.checkSQL(sqlite3_step(all)) == SQLITE_ROW) {
string uid = m_sqlite.toString(SQLITE3_COLUMN_KEY(all, 1));
auto_ptr<SyncItem> item(createItem(uid, SYNC_STATE_NONE));
out << item->getData();
@ -180,8 +178,8 @@ SyncItem *SQLiteContactSource::createItem( const string &uid, SyncState state )
{
logItem(uid, "extracting from database");
eptr<sqlite3_stmt> contact(prepareSQL("SELECT * FROM ABPerson WHERE ROWID = '%s';", uid.c_str()));
if (checkSQL(sqlite3_step(contact)) != SQLITE_ROW) {
eptr<sqlite3_stmt> contact(m_sqlite.prepareSQL("SELECT * FROM ABPerson WHERE ROWID = '%s';", uid.c_str()));
if (m_sqlite.checkSQL(sqlite3_step(contact)) != SQLITE_ROW) {
throw runtime_error(string(getName()) + ": contact not found: " + uid);
}
@ -193,26 +191,24 @@ SyncItem *SQLiteContactSource::createItem( const string &uid, SyncState state )
vobj.addProperty("VERSION", "2.1");
vobj.setVersion("2.1");
tmp = getTextColumn(contact, m_mapping[PERSON_LAST].colindex);
tmp = m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_LAST).colindex);
tmp += VObject::SEMICOLON_REPLACEMENT;
tmp += getTextColumn(contact, m_mapping[PERSON_MIDDLE].colindex);
tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_MIDDLE).colindex);
tmp += VObject::SEMICOLON_REPLACEMENT;
tmp += getTextColumn(contact, m_mapping[PERSON_FIRST].colindex);
tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_FIRST).colindex);
if (tmp.size() > 2) {
vobj.addProperty("N", tmp.c_str());
}
tmp = getTextColumn(contact, m_mapping[PERSON_ORGANIZATION].colindex);
tmp = m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_ORGANIZATION).colindex);
tmp += VObject::SEMICOLON_REPLACEMENT;
tmp += getTextColumn(contact, m_mapping[PERSON_DEPARTMENT].colindex);
tmp += m_sqlite.getTextColumn(contact, m_sqlite.getMapping(PERSON_DEPARTMENT).colindex);
if (tmp.size() > 1) {
vobj.addProperty("ORG", tmp.c_str());
}
rowToVObject(contact, vobj);
m_sqlite.rowToVObject(contact, vobj);
vobj.addProperty("END", "VCARD");
vobj.fromNativeEncoding();
arrayptr<char> finalstr(vobj.toString(), "VOCL string");
@ -237,7 +233,7 @@ int SQLiteContactSource::updateItemThrow(SyncItem& item)
// Make sure that there is no contact with this uid,
// then insert the new data. If there was no such uid,
// then this behaves like an add.
string creationTime = findColumn("ABPerson", "ROWID", item.getKey(), "CreationDate", "");
string creationTime = m_sqlite.findColumn("ABPerson", "ROWID", item.getKey(), "CreationDate", "");
deleteItemThrow(item.getKey());
return insertItemThrow(item, item.getKey(), creationTime);
}
@ -279,11 +275,11 @@ int SQLiteContactSource::insertItemThrow(SyncItem &item, const char *uid, const
suffix = fn.substr(sep4 + 1);
}
}
cols << m_mapping[PERSON_FIRST].colname << ", " <<
m_mapping[PERSON_MIDDLE].colname << ", " <<
m_mapping[PERSON_LAST].colname << ", " <<
m_mapping[PERSON_LASTSORT].colname << ", " <<
m_mapping[PERSON_FIRSTSORT].colname;
cols << m_sqlite.getMapping(PERSON_FIRST).colname << ", " <<
m_sqlite.getMapping(PERSON_MIDDLE).colname << ", " <<
m_sqlite.getMapping(PERSON_LAST).colname << ", " <<
m_sqlite.getMapping(PERSON_LASTSORT).colname << ", " <<
m_sqlite.getMapping(PERSON_FIRSTSORT).colname;
values << "?, ?, ?, ?, ?";
// synthesize sort keys: upper case with specific order of first/last name
@ -300,31 +296,31 @@ int SQLiteContactSource::insertItemThrow(SyncItem &item, const char *uid, const
cols << ", CreationDate, ModificationDate";
values << ", ?, ?";
eptr<sqlite3_stmt> insert(prepareSQL("INSERT INTO ABPerson( %s ) "
eptr<sqlite3_stmt> insert(m_sqlite.prepareSQL("INSERT INTO ABPerson( %s ) "
"VALUES( %s );",
cols.str().c_str(),
values.str().c_str()));
// now bind parameter values in the same order as the columns specification above
int param = 1;
checkSQL(sqlite3_bind_text(insert, param++, first.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_bind_text(insert, param++, middle.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_bind_text(insert, param++, last.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_bind_text(insert, param++, lastsort.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_bind_text(insert, param++, firstsort.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, first.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, middle.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, last.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, lastsort.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, firstsort.c_str(), -1, SQLITE_TRANSIENT));
if (uid) {
checkSQL(sqlite3_bind_text(insert, param++, uid, -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_bind_text(insert, param++, creationTime.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, uid, -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_bind_text(insert, param++, creationTime.c_str(), -1, SQLITE_TRANSIENT));
} else {
checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL)));
m_sqlite.checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL)));
}
checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL)));
m_sqlite.checkSQL(sqlite3_bind_int64(insert, param++, (long long)time(NULL)));
checkSQL(sqlite3_step(insert));
m_sqlite.checkSQL(sqlite3_step(insert));
if (!uid) {
// figure out which UID was assigned to the new contact
string newuid = findColumn("SQLITE_SEQUENCE", "NAME", "ABPerson", "SEQ", "");
string newuid = m_sqlite.findColumn("SQLITE_SEQUENCE", "NAME", "ABPerson", "SEQ", "");
item.setKey(newuid.c_str());
}
@ -337,24 +333,24 @@ int SQLiteContactSource::deleteItemThrow(const string &uid)
int status = STC_OK;
// delete address field members of contact
eptr<sqlite3_stmt> del(prepareSQL("DELETE FROM ABMultiValueEntry "
"WHERE ABMultiValueEntry.parent_id IN "
"(SELECT ABMultiValue.uid FROM ABMultiValue WHERE "
" ABMultiValue.record_id = ?);"));
checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_step(del));
eptr<sqlite3_stmt> del(m_sqlite.prepareSQL("DELETE FROM ABMultiValueEntry "
"WHERE ABMultiValueEntry.parent_id IN "
"(SELECT ABMultiValue.uid FROM ABMultiValue WHERE "
" ABMultiValue.record_id = ?);"));
m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_step(del));
// delete addresses and emails of contact
del.set(prepareSQL("DELETE FROM ABMultiValue WHERE "
"ABMultiValue.record_id = ?;"));
checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_step(del));
del.set(m_sqlite.prepareSQL("DELETE FROM ABMultiValue WHERE "
"ABMultiValue.record_id = ?;"));
m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_step(del));
// now delete the contact itself
del.set(prepareSQL("DELETE FROM ABPerson WHERE "
"ABPerson.ROWID = ?;"));
checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
checkSQL(sqlite3_step(del));
del.set(m_sqlite.prepareSQL("DELETE FROM ABPerson WHERE "
"ABPerson.ROWID = ?;"));
m_sqlite.checkSQL(sqlite3_bind_text(del, 1, uid.c_str(), -1, SQLITE_TRANSIENT));
m_sqlite.checkSQL(sqlite3_step(del));
return status;
}
@ -369,11 +365,11 @@ void SQLiteContactSource::logItem(const string &uid, const string &info, bool de
if (LOG.getLevel() >= (debug ? LOG_LEVEL_DEBUG : LOG_LEVEL_INFO)) {
(LOG.*(debug ? &Log::debug : &Log::info))("%s: %s %s",
getName(),
findColumn("ABPerson",
"ROWID",
uid.c_str(),
"FirstSort",
uid.c_str()).c_str(),
m_sqlite.findColumn("ABPerson",
"ROWID",
uid.c_str(),
"FirstSort",
uid.c_str()).c_str(),
info.c_str());
}
}

View File

@ -20,25 +20,30 @@
#ifndef INCL_SQLITECONTACTSOURCE
#define INCL_SQLITECONTACTSOURCE
#include "SQLiteSyncSource.h"
#include "EvolutionSyncSource.h"
#include "SQLiteUtil.h"
#ifdef ENABLE_SQLITE
/**
* Specialization of SQLiteSyncSource for contacts
* Uses SQLiteUtil for contacts
* with a schema as used by Mac OS X.
* The schema has hierarchical tables, which is not
* supported by SQLiteUtil, so only the properties which
* have a 1:1 mapping are currently stored.
*/
class SQLiteContactSource : public SQLiteSyncSource
class SQLiteContactSource : public EvolutionSyncSource
{
public:
SQLiteContactSource( const string name, SyncSourceConfig *sc, const string &changeId, const string &id ) :
SQLiteSyncSource( name, sc, changeId, id)
EvolutionSyncSource(name, sc, changeId, id)
{}
virtual ~SQLiteContactSource() {}
protected:
/* implementation of EvolutionSyncSource interface */
virtual void open();
virtual void close();
virtual sources getSyncBackends() { return sources(); }
virtual SyncItem *createItem( const string &uid, SyncState state );
virtual void exportData(ostream &out);
virtual string fileSuffix() { return "vcf"; }
@ -58,11 +63,10 @@ class SQLiteContactSource : public SQLiteSyncSource
virtual void logItem(const string &uid, const string &info, bool debug = false);
virtual void logItem(SyncItem &item, const string &info, bool debug = false);
/* implementation of SQLiteSyncSource interface */
virtual const char *getDefaultSchema();
virtual const Mapping *getConstMapping();
private:
/** encapsulates access to database */
SQLiteUtil m_sqlite;
/** constant key values defined by tables in the database, queried during open() */
key_t m_addrCountryCode,
m_addrCity,

View File

@ -21,15 +21,15 @@
#ifdef ENABLE_SQLITE
#include "SQLiteSyncSource.h"
#include "SQLiteUtil.h"
#include "base/util/StringBuffer.h"
#include "vocl/VConverter.h"
#include <stdarg.h>
void SQLiteSyncSource::throwError(const string &operation)
void SQLiteUtil::throwError(const string &operation)
{
string descr = string(getName()) + ": '" + m_id + "': " + operation + " failed";
string descr = m_name + ": '" + m_fileid + "': " + operation + " failed";
if (m_db) {
const char *error = sqlite3_errmsg(m_db);
@ -40,7 +40,7 @@ void SQLiteSyncSource::throwError(const string &operation)
throw runtime_error(descr);
}
sqlite3_stmt *SQLiteSyncSource::prepareSQLWrapper(const char *sql, const char **nextsql)
sqlite3_stmt *SQLiteUtil::prepareSQLWrapper(const char *sql, const char **nextsql)
{
sqlite3_stmt *stmt = NULL;
@ -48,7 +48,7 @@ sqlite3_stmt *SQLiteSyncSource::prepareSQLWrapper(const char *sql, const char **
return stmt;
}
sqlite3_stmt *SQLiteSyncSource::prepareSQL(const char *sqlfmt, ...)
sqlite3_stmt *SQLiteUtil::prepareSQL(const char *sqlfmt, ...)
{
StringBuffer sql;
va_list ap;
@ -61,7 +61,7 @@ sqlite3_stmt *SQLiteSyncSource::prepareSQL(const char *sqlfmt, ...)
}
SQLiteSyncSource::key_t SQLiteSyncSource::findKey(const char *database, const char *keyname, const char *key)
SQLiteUtil::key_t SQLiteUtil::findKey(const char *database, const char *keyname, const char *key)
{
eptr<sqlite3_stmt> query(prepareSQL("SELECT ROWID FROM %s WHERE %s = '%s';", database, keyname, key));
@ -73,7 +73,7 @@ SQLiteSyncSource::key_t SQLiteSyncSource::findKey(const char *database, const ch
}
}
string SQLiteSyncSource::findColumn(const char *database, const char *keyname, const char *key, const char *column, const char *def)
string SQLiteUtil::findColumn(const char *database, const char *keyname, const char *key, const char *column, const char *def)
{
eptr<sqlite3_stmt> query(prepareSQL("SELECT %s FROM %s WHERE %s = '%s';", column, database, keyname, key));
@ -87,19 +87,19 @@ string SQLiteSyncSource::findColumn(const char *database, const char *keyname, c
}
}
string SQLiteSyncSource::getTextColumn(sqlite3_stmt *stmt, int col, const char *def)
string SQLiteUtil::getTextColumn(sqlite3_stmt *stmt, int col, const char *def)
{
const unsigned char *text = sqlite3_column_text(stmt, col);
return text ? (const char *)text : def;
}
SQLiteSyncSource::syncml_time_t SQLiteSyncSource::getTimeColumn(sqlite3_stmt *stmt, int col)
SQLiteUtil::syncml_time_t SQLiteUtil::getTimeColumn(sqlite3_stmt *stmt, int col)
{
// assumes that the database stores the result of time() directly
return sqlite3_column_int64(stmt, col);
}
void SQLiteSyncSource::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj)
void SQLiteUtil::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj)
{
const unsigned char *text;
const char *tablename;
@ -121,14 +121,21 @@ void SQLiteSyncSource::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj)
}
}
void SQLiteSyncSource::open()
void SQLiteUtil::open(const string &name,
const string &fileid,
const SQLiteUtil::Mapping *mapping,
const char *schema)
{
close();
m_name = name;
m_fileid = fileid;
const string prefix("file://");
bool create = m_id.substr(0, prefix.size()) == prefix;
string filename = create ? m_id.substr(prefix.size()) : m_id;
bool create = fileid.substr(0, prefix.size()) == prefix;
string filename = create ? fileid.substr(prefix.size()) : fileid;
if (!create && access(filename.c_str(), F_OK)) {
throw runtime_error(string(getName()) + ": no such database: '" + filename + "'");
throw runtime_error(m_name + ": no such database: '" + filename + "'");
}
sqlite3 *db;
@ -144,7 +151,6 @@ void SQLiteSyncSource::open()
break;
case SQLITE_DONE: {
// empty
const char *schema = getDefaultSchema();
const char *nextsql = schema;
while (nextsql && *nextsql) {
const char *sql = nextsql;
@ -168,7 +174,6 @@ void SQLiteSyncSource::open()
}
// query database schema to find columns we need
const Mapping *mapping = getConstMapping();
int i;
for (i = 0; mapping[i].colname; i++) ;
m_mapping.set(new Mapping[i + 1]);
@ -196,8 +201,10 @@ void SQLiteSyncSource::open()
memset(&m_mapping[i], 0, sizeof(m_mapping[i]));
}
void SQLiteSyncSource::close()
void SQLiteUtil::close()
{
m_db = NULL;
}
#endif /* ENABLE_SQLITE */

View File

@ -20,13 +20,14 @@
#ifndef INCL_SQLITESYNCSOURCE
#define INCL_SQLITESYNCSOURCE
#include "EvolutionSyncSource.h"
#ifdef ENABLE_SQLITE
#include <sqlite3.h>
#include "EvolutionSmartPtr.h"
#include <string>
using namespace std;
namespace vocl {
class VObject;
}
@ -37,34 +38,40 @@ void inline unref(sqlite3_stmt *stmt) { sqlite3_finalize(stmt); }
* This class implements access to SQLite database files:
* - opening the database file
* - error reporting
* - creating a database file in debugging mode
* - creating a database file
* - converting to and from a VObject via a simple property<->column name mapping
*/
class SQLiteSyncSource : public EvolutionSyncSource
class SQLiteUtil
{
public:
/** information about the database mapping */
struct Mapping {
const char *colname; /**< column name in SQL table */
const char *tablename; /**< name of the SQL table which has this column */
const char *propname; /**< optional: vcard/vcalendar property which corresponds to this */
int colindex; /**< determined dynamically in open(): index of the column, -1 if not present */
};
const Mapping &getMapping(int i) { return m_mapping[i]; }
/**
* Creates a new Evolution sync source.
*
* @param name the named needed by SyncSource
* @param sc obligatory config for this source, must remain valid throughout the lifetime of the source
* @param changeId is used to track changes in the Evolution backend
* @param id identifies the backend; not specifying it makes this instance
* unusable for anything but listing backend databases
* @param name a name for the data source, used for error messages
* @param fileid a descriptor which identifies the file to be opened:
* currently valid syntax is file:// followed by path
* @param mapping array with database mapping, terminated by NULL colname
* @param schema database schema to use when creating new databases, may be NULL
*/
SQLiteSyncSource( const string name, SyncSourceConfig *sc, const string &changeId, const string &id ) :
EvolutionSyncSource( name, sc, changeId, id),
m_db(NULL)
{}
virtual ~SQLiteSyncSource() {}
void open(const string &name,
const string &fileid,
const Mapping *mapping,
const char *schema);
/* implementation of EvolutionSyncSource interface */
virtual sources getSyncBackends() { return sources(); /* we cannot list available databases */ }
virtual void open();
virtual void close();
void close();
protected:
/** throw error for a specific sqlite3 operation on m_db */
/**
* throw error for a specific sqlite3 operation on m_db
* @param operation a description of the operation which failed
*/
void throwError(const string &operation);
/**
@ -114,27 +121,13 @@ class SQLiteSyncSource : public EvolutionSyncSource
/** copies all columns which directly map to a property into the vobj */
void rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj);
/** database schema to use when creating new databases, may be NULL */
virtual const char *getDefaultSchema() = 0;
/** information about the database mapping */
struct Mapping {
const char *colname; /**< column name in SQL table */
const char *tablename; /**< name of the SQL table which has this column */
const char *propname; /**< optional: vcard/vcalendar property which corresponds to this */
int colindex; /**< determined dynamically in open(): index of the column, -1 if not present */
};
/**
* array with database mapping, terminated by NULL colname:
* variable fields are stored in a copy maintained by the SQLiteSyncSource class
*/
virtual const Mapping *getConstMapping() = 0;
/** filled in by SQLiteSyncSource::open() */
private:
/* copy of open() parameters */
arrayptr<Mapping> m_mapping;
string m_name;
string m_fileid;
/** after opening: current databse */
/** current database */
eptr<sqlite3> m_db;
};