You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/digikam/upgradedb_sqlite2tosqlite3.cpp

610 lines
16 KiB

/* ============================================================
*
* 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 <renchi@pooh.tam.uiuc.edu>
*
* 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 <tqmap.h>
#include <tqpair.h>
#include <tqdir.h>
#include <tqstringlist.h>
#include <tqfileinfo.h>
// KDE includes.
#include <kstandarddirs.h>
#include <tdeio/global.h>
#include <iostream>
// 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<TQString, int> 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