syncevolution/src/backends/sqlite/SQLiteUtil.cpp

203 lines
5.7 KiB
C++

/*
* Copyright (C) 2007-2009 Patrick Ohly <patrick.ohly@gmx.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef ENABLE_SQLITE
#include "SQLiteUtil.h"
#include <syncevo/util.h>
#include <stdarg.h>
#include <sstream>
#include <cstring>
#include <syncevo/declarations.h>
SE_BEGIN_CXX
void SQLiteUtil::throwError(const SourceLocation &here, const string &operation)
{
string descr = m_name + ": '" + m_fileid + "': " + operation + " failed";
if (m_db) {
const char *error = sqlite3_errmsg(m_db);
descr += ": ";
descr += error ? error : "unspecified error";
}
Exception::throwError(here, descr);
}
sqlite3_stmt *SQLiteUtil::prepareSQLWrapper(const char *sql, const char **nextsql)
{
sqlite3_stmt *stmt = NULL;
checkSQL(sqlite3_prepare(m_db, sql, -1, &stmt, nextsql), sql);
return stmt;
}
sqlite3_stmt *SQLiteUtil::prepareSQL(const char *sqlfmt, ...)
{
va_list ap;
va_start(ap, sqlfmt);
string s = StringPrintfV (sqlfmt, ap);
va_end(ap);
return prepareSQLWrapper(s.c_str());
}
SQLiteUtil::key_t SQLiteUtil::findKey(const char *database, const char *keyname, const char *key)
{
sqliteptr query(prepareSQL("SELECT ROWID FROM %s WHERE %s = '%s';", database, keyname, key));
int res = checkSQL(sqlite3_step(query), "getting key");
if (res == SQLITE_ROW) {
return sqlite3_column_int64(query, 0);
} else {
return -1;
}
}
string SQLiteUtil::findColumn(const char *database, const char *keyname, const char *key, const char *column, const char *def)
{
sqliteptr query(prepareSQL("SELECT %s FROM %s WHERE %s = '%s';", column, database, keyname, key));
int res = checkSQL(sqlite3_step(query), "getting key");
if (res == SQLITE_ROW) {
const unsigned char *text = sqlite3_column_text(query, 0);
return text ? (const char *)text : def;
} else {
return 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;
}
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);
}
string SQLiteUtil::time2str(SQLiteUtil::syncml_time_t t)
{
char buffer[128];
sprintf(buffer, "%lu", t);
return buffer;
}
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 = 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(m_name + ": no such database: '" + filename + "'");
}
sqlite3 *db;
int res = sqlite3_open(filename.c_str(), &db);
m_db = db;
checkSQL(res, "opening");
// check whether file is empty = newly created, define schema if that's the case
sqliteptr check(prepareSQLWrapper("SELECT * FROM sqlite_master;"));
switch (sqlite3_step(check)) {
case SQLITE_ROW:
// okay
break;
case SQLITE_DONE: {
// empty
const char *nextsql = schema;
while (nextsql && *nextsql) {
const char *sql = nextsql;
sqliteptr create(prepareSQLWrapper(sql, &nextsql));
while (true) {
int res = sqlite3_step(create);
if (res == SQLITE_DONE) {
break;
} else if (res == SQLITE_ROW) {
// continue
} else {
throwError(SE_HERE, "creating database");\
}
}
}
break;
}
default:
throwError(SE_HERE, "checking content");
break;
}
// query database schema to find columns we need
int i;
for (i = 0; mapping[i].colname; i++) ;
m_mapping.set(new Mapping[i + 1]);
sqliteptr query;
string tablename;
for (i = 0; mapping[i].colname; i++) {
m_mapping[i] = mapping[i];
// switching to a different table?
if (tablename != m_mapping[i].tablename) {
tablename = m_mapping[i].tablename;
query.set(prepareSQL("SELECT * FROM %s;", tablename.c_str()));
}
// search for this column name
for (m_mapping[i].colindex = sqlite3_column_count(query) - 1;
m_mapping[i].colindex >= 0;
m_mapping[i].colindex--) {
const char *name = sqlite3_column_name(query, m_mapping[i].colindex);
if (name && !strcasecmp(m_mapping[i].colname, name)) {
break;
}
}
}
memset(&m_mapping[i], 0, sizeof(m_mapping[i]));
}
void SQLiteUtil::close()
{
m_db = NULL;
}
SE_END_CXX
#endif /* ENABLE_SQLITE */