/* * Copyright (C) 2007-2008 Patrick Ohly */ #include "config.h" #ifdef ENABLE_SQLITE #include "SQLiteUtil.h" #include "base/util/StringBuffer.h" #include "vocl/VConverter.h" #include #include void SQLiteUtil::throwError(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"; } throw runtime_error(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, ...) { StringBuffer sql; va_list ap; va_start(ap, sqlfmt); sql.vsprintf(sqlfmt, ap); va_end(ap); return prepareSQLWrapper(sql.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::rowToVObject(sqlite3_stmt *stmt, vocl::VObject &vobj) { const unsigned char *text; const char *tablename; int i; for (i = 0; m_mapping[i].colname; i++) { if (m_mapping[i].colindex < 0 || !m_mapping[i].propname) { continue; } tablename = sqlite3_column_table_name(stmt, m_mapping[i].colindex); if (!tablename || strcasecmp(tablename, m_mapping[i].tablename)) { continue; } text = sqlite3_column_text(stmt, m_mapping[i].colindex); if (text) { vobj.addProperty(m_mapping[i].propname, (const char *)text); } } } sqlite3_stmt *SQLiteUtil::vObjectToRow(vocl::VObject &vobj, const string &tablename, int numparams, const string &cols, const string &values) { stringstream cols_stream; stringstream values_stream; int i; cols_stream << cols; values_stream << values; // figure out which columns we will fill for (i = 0; m_mapping[i].colname; i++) { if (m_mapping[i].colindex < 0 || !m_mapping[i].propname || tablename != m_mapping[i].tablename) { continue; } vocl::VProperty *vprop = vobj.getProperty(m_mapping[i].propname); if (vprop) { if (cols.size()) { cols_stream << ", "; values_stream << ", "; } cols_stream << m_mapping[i].colname; values_stream << "?"; } } // create statement sqliteptr insert(prepareSQL("INSERT INTO ABPerson( %s ) " "VALUES( %s );", cols_stream.str().c_str(), values_stream.str().c_str())); // now bind our parameters int param = numparams + 1; for (i = 0; m_mapping[i].colname; i++) { if (m_mapping[i].colindex < 0 || !m_mapping[i].propname || tablename != m_mapping[i].tablename) { continue; } vocl::VProperty *vprop = vobj.getProperty(m_mapping[i].propname); if (vprop) { const char *text = vprop->getValue(); checkSQL(sqlite3_bind_text(insert, param++, text ? text : "", -1, SQLITE_TRANSIENT)); } } return insert.release(); } 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("creating database");\ } } } break; } default: throwError("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; } #endif /* ENABLE_SQLITE */