/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-06-05 * Description : SQlite 2 to SQlite 3 interface. * * Copyright (C) 2005 by Renchi Raju * * 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, or (at your option) * any later version. * * This program 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 General Public License for more details. * * ============================================================ */ // TQt includes. #include #include #include #include #include // KDE includes. #include #include #include // Local includes. #include "ddebug.h" #include "albumdb.h" #include "albumdb_sqlite2.h" #include "upgradedb_sqlite2tosqlite3.h" namespace Digikam { struct _Album { int id; TQString url; TQString date; TQString caption; TQString collection; TQString icon; }; struct _Tag { int id; int pid; TQString name; TQString icon; }; static TQString escapeString(TQString str) { str.replace( "'", "''" ); return str; } TQ_LLONG findOrAddImage(AlbumDB* db, int dirid, const TQString& name, const TQString& caption) { TQStringList values; db->execSql(TQString("SELECT id FROM Images WHERE dirid=%1 AND name='%2'") .arg(dirid) .arg(escapeString(name)), &values); if (!values.isEmpty()) { return values.first().toLongLong(); } db->execSql(TQString("INSERT INTO Images (dirid, name, caption) \n " "VALUES(%1, '%2', '%3');") .arg(dirid) .arg(escapeString(name)) .arg(escapeString(caption)), &values); return db->lastInsertedRow(); } bool upgradeDB_Sqlite2ToSqlite3(const TQString& _libraryPath) { TQString libraryPath = TQDir::cleanDirPath(_libraryPath); TQString newDB= libraryPath + "/digikam3.db"; #ifdef NFS_HACK newDB = locateLocal("appdata", TDEIO::encodeFileName(TQDir::cleanDirPath(newDB))); DDebug() << "NFS: " << newDB << endl; #endif AlbumDB db3; db3.setDBPath(newDB); if (!db3.isValid()) { DWarning() << "Failed to open new Album Database" << endl; DWarning() << "The directory <" << libraryPath << "> may not exist or is write-protected" << endl; return false; } if (db3.getSetting("UpgradedFromSqlite2") == "yes") return true; TQString dbPath = libraryPath + "/digikam.db"; #ifdef NFS_HACK dbPath = locateLocal("appdata", TDEIO::encodeFileName(TQDir::cleanDirPath(dbPath))); DDebug() << "From NFS: " << dbPath << endl; #endif TQFileInfo fi(dbPath); if (!fi.exists()) { DDebug() << "No old database present. Not upgrading" << endl; db3.setSetting("UpgradedFromSqlite2", "yes"); return true; } AlbumDB_Sqlite2 db2; db2.setDBPath( dbPath ); if (!db2.isValid()) { DDebug() << "Failed to initialize Old Album Database" << endl; return false; } // delete entries from sqlite3 database db3.execSql("DELETE FROM Albums;"); db3.execSql("DELETE FROM Tags;"); db3.execSql("DELETE FROM TagsTree;"); db3.execSql("DELETE FROM Images;"); db3.execSql("DELETE FROM ImageTags;"); db3.execSql("DELETE FROM ImageProperties;"); TQStringList values; // update albums ------------------------------------------------- values.clear(); db2.execSql("SELECT id, url, date, caption, collection, icon FROM Albums;", &values); typedef TQValueList<_Album> AlbumList; AlbumList albumList; typedef TQMap AlbumMap; AlbumMap albumMap; db3.beginTransaction(); for (TQStringList::iterator it=values.begin(); it!=values.end();) { _Album album; album.id = (*it).toInt(); ++it; album.url = (*it); ++it; album.date = (*it); ++it; album.caption = (*it); ++it; album.collection = (*it); ++it; album.icon = (*it); ++it; albumList.append(album); albumMap.insert(album.url, album.id); db3.execSql(TQString("INSERT INTO Albums (id, url, date, caption, collection) " "VALUES(%1, '%2', '%3', '%4', '%5');") .arg(album.id) .arg(escapeString(album.url)) .arg(escapeString(album.date)) .arg(escapeString(album.caption)) .arg(escapeString(album.collection))); } db3.commitTransaction(); // update tags ------------------------------------------------- values.clear(); db2.execSql("SELECT id, pid, name, icon FROM Tags;", &values); typedef TQValueList<_Tag> TagList; TagList tagList; db3.beginTransaction(); for (TQStringList::iterator it=values.begin(); it!=values.end();) { _Tag tag; tag.id = (*it).toInt(); ++it; tag.pid = (*it).toInt(); ++it; tag.name = (*it); ++it; tag.icon = (*it); ++it; tagList.append(tag); db3.execSql(TQString("INSERT INTO Tags (id, pid, name) " "VALUES(%1, %2, '%3');") .arg(tag.id) .arg(tag.pid) .arg(escapeString(tag.name))); } db3.commitTransaction(); // update images ------------------------------------------------- values.clear(); db2.execSql("SELECT dirid, name, caption FROM Images;", &values); db3.beginTransaction(); for (TQStringList::iterator it=values.begin(); it!=values.end();) { int dirid = (*it).toInt(); ++it; TQString name = (*it); ++it; TQString caption = (*it); ++it; findOrAddImage(&db3, dirid, name, caption); } db3.commitTransaction(); // update imagetags ----------------------------------------------- values.clear(); db2.execSql("SELECT dirid, name, tagid FROM ImageTags;", &values); db3.beginTransaction(); for (TQStringList::iterator it=values.begin(); it!=values.end();) { int dirid = (*it).toInt(); ++it; TQString name = (*it); ++it; int tagid = (*it).toInt(); ++it; TQ_LLONG imageid = findOrAddImage(&db3, dirid, name, TQString()); db3.execSql(TQString("INSERT INTO ImageTags VALUES( %1, %2 )") .arg(imageid).arg(tagid)); } db3.commitTransaction(); // update album icons ------------------------------------------------- db3.beginTransaction(); for (AlbumList::iterator it = albumList.begin(); it != albumList.end(); ++it) { _Album album = *it; if (album.icon.isEmpty()) continue; TQ_LLONG imageid = findOrAddImage(&db3, album.id, album.icon, TQString()); db3.execSql(TQString("UPDATE Albums SET icon=%1 WHERE id=%2") .arg(imageid) .arg(album.id)); } db3.commitTransaction(); // -- update tag icons --------------------------------------------------- db3.beginTransaction(); for (TagList::iterator it = tagList.begin(); it != tagList.end(); ++it) { _Tag tag = *it; if (tag.icon.isEmpty()) continue; TQFileInfo fi(tag.icon); if (fi.isRelative()) { db3.execSql(TQString("UPDATE Tags SET iconkde='%1' WHERE id=%2") .arg(escapeString(tag.icon)) .arg(tag.id)); continue; } tag.icon = TQDir::cleanDirPath(tag.icon); fi.setFile(tag.icon.remove(libraryPath)); TQString url = fi.dirPath(true); TQString name = fi.fileName(); AlbumMap::iterator it1 = albumMap.find(url); if (it1 == albumMap.end()) { DDebug() << "Could not find album with url: " << url << endl; DDebug() << "Most likely an external directory. Rejecting." << endl; continue; } int dirid = it1.data(); TQ_LLONG imageid = findOrAddImage(&db3, dirid, name, TQString());; db3.execSql(TQString("UPDATE Tags SET icon=%1 WHERE id=%2") .arg(imageid) .arg(tag.id)); } db3.commitTransaction(); // -- Remove invalid entries ---------------------------------------- db3.execSql("DELETE FROM Images WHERE dirid=-1"); // -- update setting entry ------------------------------------------ db3.setSetting("UpgradedFromSqlite2", "yes"); DDebug() << "Successfully upgraded database to sqlite3 " << endl; // -- Check for db consistency ---------------------------------------- std::cout << "Checking database consistency" << std::endl; std::cout << "Checking Albums.................."; values.clear(); db2.execSql("SELECT id, url, date, caption, collection FROM Albums;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { _Album album; album.id = (*it).toInt(); ++it; album.url = (*it); ++it; album.date = (*it); ++it; album.caption = (*it); ++it; album.collection = (*it); ++it; TQStringList list; db3.execSql(TQString("SELECT id FROM Albums WHERE \n" " id=%1 AND \n" " url='%2' AND \n" " date='%3' AND \n" " caption='%4' AND \n" " collection='%5';") .arg(album.id) .arg(escapeString(album.url)) .arg(escapeString(album.date)) .arg(escapeString(album.caption)) .arg(escapeString(album.collection)), &list, false); if (list.size() != 1) { std::cerr << "Failed" << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for Album: " << album.url << endl; return false; } } std::cout << "(" << values.count()/5 << " Albums) " << "OK" << std::endl; std::cout << "Checking Tags...................."; values.clear(); db2.execSql("SELECT id, pid, name FROM Tags;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { int id = (*it).toInt(); ++it; int pid = (*it).toInt(); ++it; TQString name = (*it); ++it; TQStringList list; db3.execSql(TQString("SELECT id FROM Tags WHERE \n" " id=%1 AND \n" " pid=%2 AND \n" " name='%3';") .arg(id) .arg(pid) .arg(escapeString(name)), &list, false); if (list.size() != 1) { std::cerr << "Failed" << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for Tag: " << name << endl; return false; } } std::cout << "(" << values.count()/3 << " Tags) " << "OK" << std::endl; std::cout << "Checking Images.................."; values.clear(); db2.execSql("SELECT Albums.url, Images.name, Images.caption " "FROM Images, Albums WHERE Albums.id=Images.dirid;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { TQString url = (*it); ++it; TQString name = (*it); ++it; TQString caption = (*it); ++it; TQStringList list; db3.execSql(TQString("SELECT Images.id FROM Images, Albums WHERE \n " "Albums.url = '%1' AND \n " "Images.dirid = Albums.id AND \n " "Images.name = '%2' AND \n " "Images.caption = '%3';") .arg(escapeString(url)) .arg(escapeString(name)) .arg(escapeString(caption)), &list, false); if (list.size() != 1) { std::cerr << "Failed" << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for Image: " << url << ", " << name << ", " << caption << endl; return false; } } std::cout << "(" << values.count()/3 << " Images) " << "OK" << std::endl; std::cout << "Checking ImageTags..............."; values.clear(); db2.execSql("SELECT Albums.url, ImageTags.name, ImageTags.tagid " "FROM ImageTags, Albums WHERE \n " " Albums.id=ImageTags.dirid;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { TQString url = (*it); ++it; TQString name = (*it); ++it; int tagid = (*it).toInt(); ++it; TQStringList list; db3.execSql(TQString("SELECT Images.id FROM Albums, Images, ImageTags WHERE \n " "Albums.url = '%1' AND \n " "Images.dirid = Albums.id AND \n " "Images.name = '%2' AND \n " "ImageTags.imageid = Images.id AND \n " "ImageTags.tagid = %3;") .arg(escapeString(url)) .arg(escapeString(name)) .arg(tagid), &list, false); if (list.size() != 1) { std::cerr << "Failed" << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for ImageTag: " << url << ", " << name << ", " << tagid << endl; return false; } } std::cout << "(" << values.count()/3 << " ImageTags) " << "OK" << std::endl; std::cout << "Checking Album icons ..............."; values.clear(); db2.execSql("SELECT url, icon FROM Albums;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { TQString url = (*it); ++it; TQString icon = (*it); ++it; if (icon.isEmpty()) continue; TQStringList list; db3.execSql(TQString("SELECT Images.id FROM Images, Albums WHERE \n " "Albums.url = '%1' AND \n " "Images.id = Albums.icon AND \n " "Images.name = '%2';") .arg(escapeString(url)) .arg(escapeString(icon)), &list); if (list.size() != 1) { std::cerr << "Failed" << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for Album Icon: " << url << ", " << icon << endl; return false; } } std::cout << "(" << values.count()/2 << " Album Icons) " << "OK" << std::endl; std::cout << "Checking Tag icons ..............."; values.clear(); db2.execSql("SELECT id, icon FROM Tags;", &values); for (TQStringList::iterator it = values.begin(); it != values.end();) { int id = (*it).toInt(); ++it; TQString icon = (*it); ++it; if (icon.isEmpty()) continue; if (!icon.startsWith("/")) { TQStringList list; db3.execSql(TQString("SELECT id FROM Tags WHERE \n " "id = %1 AND \n " "iconkde = '%2';") .arg(id) .arg(escapeString(icon)), &list); if (list.size() != 1) { std::cerr << "Failed" << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for Tag Icon: " << id << ", " << icon << endl; return false; } } else { icon = TQDir::cleanDirPath(icon); TQFileInfo fi(icon.remove(libraryPath)); TQString url = fi.dirPath(true); TQString name = fi.fileName(); TQStringList list; list.clear(); db3.execSql(TQString("SELECT id FROM Albums WHERE url='%1'") .arg(escapeString(url)), &list); if (list.isEmpty()) { DWarning() << "Tag icon not in Album Library Path, Rejecting " << endl; DWarning() << "(" << icon << ")" << endl; continue; } list.clear(); db3.execSql(TQString("SELECT Images.id FROM Images, Tags WHERE \n " " Images.dirid=(SELECT id FROM Albums WHERE url='%1') AND \n " " Images.name='%2' AND \n " " Tags.id=%3 AND \n " " Tags.icon=Images.id") .arg(escapeString(url)) .arg(escapeString(name)) .arg(id), &list); if (list.size() != 1) { std::cerr << "Failed." << std::endl; DWarning() << "" << endl; DWarning() << "Consistency check failed for Tag Icon: " << id << ", " << icon << endl; return false; } } } std::cout << "(" << values.count()/2 << " Tag Icons) " << "OK" << std::endl; std::cout << "" << std::endl; std::cout << "All Tests: A-OK" << std::endl; return true; } } // namespace Digikam