diff options
Diffstat (limited to 'fbreader/src/database/booksdb')
23 files changed, 2850 insertions, 0 deletions
diff --git a/fbreader/src/database/booksdb/BooksDB.cpp b/fbreader/src/database/booksdb/BooksDB.cpp new file mode 100644 index 0000000..bf6d2b3 --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLibrary.h> +#include <ZLFile.h> +#include <ZLDir.h> +#include <ZLLanguageUtil.h> + +#include "BooksDB.h" +#include "BooksDBQuery.h" + +#include "../../library/Book.h" +#include "../../library/Author.h" +#include "../../library/Tag.h" + +#include "../sqldb/implsqlite/SQLiteFactory.h" + +shared_ptr<BooksDB> BooksDB::ourInstance = 0; + +const std::string BooksDB::DATABASE_NAME = "books.db"; +const std::string BooksDB::STATE_DATABASE_NAME = "state.db"; + +BooksDB &BooksDB::Instance() { + if (ourInstance.isNull()) { + ZLFile dir(databaseDirName()); + dir.directory(true); + ZLFile file(databaseDirName() + ZLibrary::FileNameDelimiter + DATABASE_NAME); + ourInstance = new BooksDB(file.physicalFilePath()); + ourInstance->initDatabase(); + } + return *ourInstance; +} + +BooksDB::BooksDB(const std::string &path) : SQLiteDataBase(path), myInitialized(false) { + initCommands(); +} + +BooksDB::~BooksDB() { +} + +bool BooksDB::initDatabase() { + if (isInitialized()) { + return true; + } + + if (!open()) { + return false; + } + + myInitialized = true; + + ZLFile stateFile(databaseDirName() + ZLibrary::FileNameDelimiter + STATE_DATABASE_NAME); + shared_ptr<DBCommand> cmd = SQLiteFactory::createCommand(BooksDBQuery::PREINIT_DATABASE, connection(), "@stateFile", DBValue::DBTEXT); + ((DBTextValue&)*cmd->parameter("@stateFile").value()) = stateFile.physicalFilePath(); + if (!cmd->execute()) { + myInitialized = false; + close(); + return false; + } + + shared_ptr<DBRunnable> runnable = new InitBooksDBRunnable(connection()); + if (!executeAsTransaction(*runnable)) { + myInitialized = false; + close(); + return false; + } + + return true; +} + +void BooksDB::initCommands() { + myLoadBook = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK, connection(), "@file_id", DBValue::DBINT); + myGetFileSize = SQLiteFactory::createCommand(BooksDBQuery::GET_FILE_SIZE, connection(), "@file_id", DBValue::DBINT); + mySetFileSize = SQLiteFactory::createCommand(BooksDBQuery::SET_FILE_SIZE, connection(), "@file_id", DBValue::DBINT, "@size", DBValue::DBINT); + myFindFileName = SQLiteFactory::createCommand(BooksDBQuery::FIND_FILE_NAME, connection(), "@file_id", DBValue::DBINT); + myFindAuthorId = SQLiteFactory::createCommand(BooksDBQuery::FIND_AUTHOR_ID, connection(), "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT); + + myLoadBooks = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOKS, connection()); + + myLoadBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK_STATE_STACK, connection(), "@book_id", DBValue::DBINT); + + myGetPalmType = SQLiteFactory::createCommand(BooksDBQuery::GET_PALM_TYPE, connection(), "@file_id", DBValue::DBINT); + mySetPalmType = SQLiteFactory::createCommand(BooksDBQuery::SET_PALM_TYPE, connection(), "@file_id", DBValue::DBINT, "@type", DBValue::DBTEXT); + + myLoadStackPos = SQLiteFactory::createCommand(BooksDBQuery::LOAD_STACK_POS, connection(), "@book_id", DBValue::DBINT); + mySetStackPos = SQLiteFactory::createCommand(BooksDBQuery::SET_STACK_POS, connection(), "@book_id", DBValue::DBINT, "@stack_pos", DBValue::DBINT); + + myLoadBookState = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK_STATE, connection(), "@book_id", DBValue::DBINT); + mySetBookState = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_STATE, connection(), "@book_id", DBValue::DBINT, "@paragraph", DBValue::DBINT, "@word", DBValue::DBINT, "@char", DBValue::DBINT); + + myInsertBookList = SQLiteFactory::createCommand(BooksDBQuery::INSERT_BOOK_LIST, connection(), "@book_id", DBValue::DBINT); + myDeleteBookList = SQLiteFactory::createCommand(BooksDBQuery::DELETE_BOOK_LIST, connection(), "@book_id", DBValue::DBINT); + myCheckBookList = SQLiteFactory::createCommand(BooksDBQuery::CHECK_BOOK_LIST, connection(), "@book_id", DBValue::DBINT); + + mySaveTableBook = new SaveTableBookRunnable(connection()); + mySaveAuthors = new SaveAuthorsRunnable(connection()); + mySaveSeries = new SaveSeriesRunnable(connection()); + mySaveTags = new SaveTagsRunnable(connection()); + mySaveBook = new SaveBookRunnable(*mySaveTableBook, *mySaveAuthors, *mySaveSeries, *mySaveTags); + mySaveFileEntries = new SaveFileEntriesRunnable(connection()); + + myFindFileId = new FindFileIdRunnable(connection()); + + myLoadFileEntries = new LoadFileEntriesRunnable(connection()); + + myLoadRecentBooks = new LoadRecentBooksRunnable(connection()); + mySaveRecentBooks = new SaveRecentBooksRunnable(connection()); + + mySaveBookStateStack = new SaveBookStateStackRunnable(connection()); + + myDeleteBook = new DeleteBookRunnable(connection()); +} + +bool BooksDB::clearDatabase() { + if (!isInitialized()) { + return false; + } + shared_ptr<DBRunnable> runnable = new ClearBooksDBRunnable(connection()); + return executeAsTransaction(*runnable); +} + +shared_ptr<Book> BooksDB::loadBook(const std::string &fileName) { + if (!isInitialized()) { + return 0; + } + + myFindFileId->setFileName(fileName); + if (!myFindFileId->run()) { + return 0; + } + ((DBIntValue&)*myLoadBook->parameter("@file_id").value()) = myFindFileId->fileId(); + shared_ptr<DBDataReader> reader = myLoadBook->executeReader(); + + if (reader.isNull() || !reader->next() || + reader->type(0) != DBValue::DBINT /* book_id */) { + return 0; + } + const int bookId = reader->intValue(0); + + shared_ptr<Book> book = Book::createBook( + ZLFile(fileName), bookId, + reader->textValue(1, Book::AutoEncoding), + reader->textValue(2, ZLLanguageUtil::OtherLanguageCode), + reader->textValue(3, std::string()) + ); + + loadSeries(*book); + loadAuthors(*book); + loadTags(*book); + + return book; +} + + +bool BooksDB::saveBook(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveBook->setBook(book); + return executeAsTransaction(*mySaveBook); +} + +bool BooksDB::saveAuthors(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveAuthors->setBook(book); + return executeAsTransaction(*mySaveAuthors); +} + +bool BooksDB::saveSeries(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveSeries->setBook(book); + return executeAsTransaction(*mySaveSeries); +} + +bool BooksDB::saveTags(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveTags->setBook(book); + return executeAsTransaction(*mySaveTags); +} + +int BooksDB::getFileSize(const std::string fileName) { + if (!isInitialized()) { + return -1; + } + myFindFileId->setFileName(fileName); + if (!myFindFileId->run()) { + return 0; + } + ((DBIntValue&)*myGetFileSize->parameter("@file_id").value()) = myFindFileId->fileId(); + + shared_ptr<DBValue> fileSize = myGetFileSize->executeScalar(); + + if (fileSize.isNull()) { + return -1; + } + if (fileSize->type() == DBValue::DBNULL) { + return 0; + } + if (fileSize->type() != DBValue::DBINT) { + return -1; + } + return ((DBIntValue&)*fileSize).value(); +} + +bool BooksDB::setFileSize(const std::string fileName, int size) { + if (!isInitialized()) { + return false; + } + myFindFileId->setFileName(fileName, true); + if (!executeAsTransaction(*myFindFileId)) { + return false; + } + ((DBIntValue&)*mySetFileSize->parameter("@file_id").value()) = myFindFileId->fileId(); + ((DBIntValue&)*mySetFileSize->parameter("@size").value()) = size; + return mySetFileSize->execute(); +} + +bool BooksDB::setEncoding(const Book &book, const std::string &encoding) { + if (!isInitialized()) { + return false; + } + + shared_ptr<DBCommand> command = SQLiteFactory::createCommand(BooksDBQuery::SET_ENCODING, connection()); + + command->parameters().push_back(DBCommandParameter("@book_id", new DBIntValue(book.bookId()))); + command->parameters().push_back(DBCommandParameter("@encoding", new DBTextValue(encoding))); + + return command->execute(); +} + +bool BooksDB::loadFileEntries(const std::string &fileName, std::vector<std::string> &entries) { + myLoadFileEntries->setFileName(fileName); + if (!myLoadFileEntries->run()) { + return false; + } + myLoadFileEntries->collectEntries(entries); + return true; +} + +bool BooksDB::saveFileEntries(const std::string &fileName, const std::vector<std::string> &entries) { + if (!isInitialized()) { + return false; + } + mySaveFileEntries->setEntries(fileName, entries); + return executeAsTransaction(*mySaveFileEntries); +} + +bool BooksDB::loadRecentBooks(std::vector<std::string> &fileNames) { + std::vector<int> fileIds; + if (!myLoadRecentBooks->run()) { + return false; + } + myLoadRecentBooks->collectFileIds(fileIds); + for (std::vector<int>::const_iterator it = fileIds.begin(); it != fileIds.end(); ++it) { + const int fileId = *it; + const std::string fileName = getFileName(fileId); + fileNames.push_back(fileName); + } + return true; +} + +bool BooksDB::saveRecentBooks(const BookList &books) { + if (!isInitialized()) { + return false; + } + mySaveRecentBooks->setBooks(books); + return executeAsTransaction(*mySaveRecentBooks); +} + +std::string BooksDB::getFileName(int fileId) { + std::string fileName; + DBIntValue &findFileId = (DBIntValue&)*myFindFileName->parameter("@file_id").value(); + findFileId = fileId; + while (true) { + shared_ptr<DBDataReader> reader = myFindFileName->executeReader(); + if (reader.isNull() || !reader->next()) { + return std::string(); + } + const std::string namePart = reader->textValue(0, std::string()); + switch (reader->type(1)) { /* parent_id */ + default: + return std::string(); + case DBValue::DBNULL: + return namePart + ZLibrary::FileNameDelimiter + fileName; + case DBValue::DBINT: + if (fileName.empty()) { + fileName = namePart; + } else { + fileName = namePart + BooksDBQuery::ArchiveEntryDelimiter + fileName; + } + findFileId = reader->intValue(1); + break; + } + } +} + +bool BooksDB::loadBooks(BookList &books) { + shared_ptr<DBDataReader> reader = myLoadBooks->executeReader(); + + books.clear(); + std::map<int,shared_ptr<Book> > bookMap; + + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT || /* book_id */ + reader->type(4) != DBValue::DBINT) { /* file_id */ + return false; + } + const int bookId = reader->intValue(0); + const int fileId = reader->intValue(4); + const std::string fileName = getFileName(fileId); + + shared_ptr<Book> book = Book::createBook( + ZLFile(fileName), + bookId, + reader->textValue(1, Book::AutoEncoding), + reader->textValue(2, ZLLanguageUtil::OtherLanguageCode), + reader->textValue(3, std::string()) + ); + books.push_back(book); + bookMap[bookId] = book; + } + + loadSeries(bookMap); + loadAuthors(bookMap); + loadTags(bookMap); + + return true; +} + +bool BooksDB::loadBookStateStack(const Book &book, std::deque<ReadingState> &stack) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myLoadBookStateStack->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = myLoadBookStateStack->executeReader(); + if (reader.isNull()) { + return false; + } + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT /* paragraph */ + || reader->type(1) != DBValue::DBINT /* word */ + || reader->type(2) != DBValue::DBINT /* char */) { + return false; + } + const int paragraph = reader->intValue(0); + const int word = reader->intValue(1); + const int character = reader->intValue(2); + stack.push_back(ReadingState(paragraph, word, character)); + } + return true; +} + +bool BooksDB::saveBookStateStack(const Book &book, const std::deque<ReadingState> &stack) { + if (!isInitialized() || book.bookId() == 0) { + return false; + } + mySaveBookStateStack->setState(book.bookId(), stack); + return executeAsTransaction(*mySaveBookStateStack); +} + + +bool BooksDB::removeBook(const Book &book) { + if (!isInitialized() || book.bookId() == 0) { + return false; + } + myDeleteBook->setFileName(book.file().path()); + return executeAsTransaction(*myDeleteBook); +} + +std::string BooksDB::getPalmType(const std::string &fileName) { + if (!isInitialized()) { + return ""; + } + myFindFileId->setFileName(fileName); + if (!myFindFileId->run()) { + return ""; + } + ((DBIntValue&)*myGetPalmType->parameter("@file_id").value()) = myFindFileId->fileId(); + shared_ptr<DBValue> value = myGetPalmType->executeScalar(); + if (value.isNull() || value->type() != DBValue::DBTEXT) { + return ""; + } + return ((DBTextValue&)*value).value(); +} + +bool BooksDB::setPalmType(const std::string &fileName, const std::string &type) { + if (!isInitialized()) { + return false; + } + myFindFileId->setFileName(fileName, true); + if (!myFindFileId->run()) { + return ""; + } + ((DBIntValue&)*mySetPalmType->parameter("@file_id").value()) = myFindFileId->fileId(); + ((DBTextValue&)*mySetPalmType->parameter("@type").value()) = type; + return mySetPalmType->execute(); +} + +bool BooksDB::loadBookState(const Book &book, ReadingState &state) { + state.Paragraph = state.Word = state.Character = 0; + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myLoadBookState->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = myLoadBookState->executeReader(); + if (reader.isNull()) { + return false; + } + if (!reader->next() + || reader->type(0) != DBValue::DBINT /* paragraph */ + || reader->type(1) != DBValue::DBINT /* word */ + || reader->type(2) != DBValue::DBINT /* char */) { + return false; + } + state.Paragraph = reader->intValue(0); + state.Word = reader->intValue(1); + state.Character = reader->intValue(2); + return true; +} + +bool BooksDB::setBookState(const Book &book, const ReadingState &state) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*mySetBookState->parameter("@book_id").value()) = book.bookId(); + ((DBIntValue&)*mySetBookState->parameter("@paragraph").value()) = state.Paragraph; + ((DBIntValue&)*mySetBookState->parameter("@word").value()) = state.Word; + ((DBIntValue&)*mySetBookState->parameter("@char").value()) = state.Character; + return mySetBookState->execute(); +} + +int BooksDB::loadStackPos(const Book &book) { + if (book.bookId() == 0) { + return 0; + } + ((DBIntValue&)*myLoadStackPos->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBValue> stackPosValue = myLoadStackPos->executeScalar(); + if (stackPosValue.isNull() + || stackPosValue->type() != DBValue::DBINT) { + return 0; + } + return ((DBIntValue&)*stackPosValue).value(); +} + +bool BooksDB::setStackPos(const Book &book, int stackPos) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*mySetStackPos->parameter("@book_id").value()) = book.bookId(); + ((DBIntValue&)*mySetStackPos->parameter("@stack_pos").value()) = stackPos; + return mySetStackPos->execute(); +} + +bool BooksDB::insertIntoBookList(const Book &book) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myInsertBookList->parameter("@book_id").value()) = book.bookId(); + return myInsertBookList->execute(); +} + +bool BooksDB::deleteFromBookList(const Book &book) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myDeleteBookList->parameter("@book_id").value()) = book.bookId(); + return myDeleteBookList->execute(); +} + +bool BooksDB::checkBookList(const Book &book) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myCheckBookList->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBValue> res = myCheckBookList->executeScalar(); + if (res.isNull() || res->type() != DBValue::DBINT) { + return false; + } + const int checkRes = ((DBIntValue&)*res).value(); + return checkRes > 0; +} diff --git a/fbreader/src/database/booksdb/BooksDB.h b/fbreader/src/database/booksdb/BooksDB.h new file mode 100644 index 0000000..634b31b --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __BOOKSDB_H__ +#define __BOOKSDB_H__ + +#include <set> +#include <map> +#include <deque> + +#include "../sqldb/implsqlite/SQLiteDataBase.h" +#include "DBRunnables.h" + +#include "../../fbreader/ReadingState.h" + +class Book; + +class BooksDB : public SQLiteDataBase { + +public: + static const std::string DATABASE_NAME; + static const std::string STATE_DATABASE_NAME; + static const std::string NET_DATABASE_NAME; + + static BooksDB &Instance(); + +private: + static shared_ptr<BooksDB> ourInstance; + + BooksDB(const std::string &path); + +public: + virtual ~BooksDB(); + +public: + bool initDatabase(); + bool isInitialized() const; + bool clearDatabase(); + + shared_ptr<Book> loadBook(const std::string &fileName); + bool saveBook(const shared_ptr<Book> book); + bool saveAuthors(const shared_ptr<Book> book); + bool saveSeries(const shared_ptr<Book> book); + bool saveTags(const shared_ptr<Book> book); + + int getFileSize(const std::string fileName); + bool setFileSize(const std::string fileName, int size); + + bool setEncoding(const Book &book, const std::string &encoding); + + bool loadFileEntries(const std::string &fileName, std::vector<std::string> &entries); + bool saveFileEntries(const std::string &fileName, const std::vector<std::string> &entries); + + bool loadRecentBooks(std::vector<std::string> &fileNames); + bool saveRecentBooks(const BookList &books); + + bool loadBooks(BookList &books); + + bool loadBookStateStack(const Book &book, std::deque<ReadingState> &stack); + bool saveBookStateStack(const Book &book, const std::deque<ReadingState> &stack); + + bool removeBook(const Book &book); + + std::string getPalmType(const std::string &fileName); + bool setPalmType(const std::string &fileName, const std::string &type); + + bool loadBookState(const Book &book, ReadingState &state); + bool setBookState(const Book &book, const ReadingState &state); + + int loadStackPos(const Book &book); + bool setStackPos(const Book &book, int stackPos); + + bool insertIntoBookList(const Book &book); + bool deleteFromBookList(const Book &book); + bool checkBookList(const Book &book); + +private: +public: + shared_ptr<Tag> getTagById(int id) const; + void loadAllTagsById() const; + +private: + void loadSeries(Book &book); + void loadSeries(const std::map<int,shared_ptr<Book> > &books); + void loadAuthors(Book &book); + void loadAuthors(const std::map<int,shared_ptr<Book> > &books); + void loadTags(Book &book); + void loadTags(const std::map<int,shared_ptr<Book> > &books); + + std::string getFileName(int fileId); + +private: + void initCommands(); + +private: + bool myInitialized; + + shared_ptr<SaveTableBookRunnable> mySaveTableBook; + shared_ptr<SaveAuthorsRunnable> mySaveAuthors; + shared_ptr<SaveSeriesRunnable> mySaveSeries; + shared_ptr<SaveTagsRunnable> mySaveTags; + shared_ptr<SaveBookRunnable> mySaveBook; + shared_ptr<SaveFileEntriesRunnable> mySaveFileEntries; + + shared_ptr<FindFileIdRunnable> myFindFileId; + + shared_ptr<LoadFileEntriesRunnable> myLoadFileEntries; + + shared_ptr<LoadRecentBooksRunnable> myLoadRecentBooks; + shared_ptr<SaveRecentBooksRunnable> mySaveRecentBooks; + + shared_ptr<SaveBookStateStackRunnable> mySaveBookStateStack; + + shared_ptr<DeleteBookRunnable> myDeleteBook; + + shared_ptr<DBCommand> myLoadNetworkLinks; + shared_ptr<DBCommand> myFindNetworkLinkId; + shared_ptr<DBCommand> myDeleteNetworkLink; + shared_ptr<DBCommand> myDeleteNetworkLinkUrls; + shared_ptr<DBCommand> myLoadNetworkLinkUrls; + + shared_ptr<DBCommand> myLoadBook; + + shared_ptr<DBCommand> myGetFileSize; + shared_ptr<DBCommand> mySetFileSize; + + shared_ptr<DBCommand> myFindFileName; + + shared_ptr<DBCommand> myFindAuthorId; + + shared_ptr<DBCommand> myLoadBooks; + + shared_ptr<DBCommand> myLoadBookStateStack; + + shared_ptr<DBCommand> myGetPalmType; + shared_ptr<DBCommand> mySetPalmType; + + shared_ptr<DBCommand> myGetNetFile; + shared_ptr<DBCommand> mySetNetFile; + + shared_ptr<DBCommand> myLoadStackPos; + shared_ptr<DBCommand> mySetStackPos; + shared_ptr<DBCommand> myLoadBookState; + shared_ptr<DBCommand> mySetBookState; + + shared_ptr<DBCommand> myInsertBookList; + shared_ptr<DBCommand> myDeleteBookList; + shared_ptr<DBCommand> myCheckBookList; + +private: // disable copying + BooksDB(const BooksDB &); + const BooksDB &operator = (const BooksDB &); +}; + + +inline bool BooksDB::isInitialized() const { return myInitialized; } + +#endif /* __BOOKSDB_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDBQuery.cpp b/fbreader/src/database/booksdb/BooksDBQuery.cpp new file mode 100644 index 0000000..134e43b --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBQuery.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLibrary.h> + +#include "BooksDBQuery.h" + +const std::string BooksDBQuery::ArchiveEntryDelimiter = ":"; + +const std::string BooksDBQuery::PREINIT_DATABASE = \ + "ATTACH @stateFile AS State; "; + +const std::string BooksDBQuery::INIT_DATABASE = \ + "CREATE TABLE IF NOT EXISTS Files ( " \ + " file_id INTEGER PRIMARY KEY, " \ + " name TEXT NOT NULL, " \ + " parent_id INTEGER REFERENCES Files (file_id), " \ + " size INTEGER, " \ + " CONSTRAINT Files_Unique UNIQUE (name, parent_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Books ( " \ + " book_id INTEGER PRIMARY KEY, " \ + " encoding TEXT, " \ + " language TEXT, " \ + " title TEXT NOT NULL, " \ + " file_id INTEGER UNIQUE NOT NULL REFERENCES Files (file_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Authors ( " \ + " author_id INTEGER PRIMARY KEY, " \ + " name TEXT NOT NULL, " \ + " sort_key TEXT NOT NULL, " \ + " CONSTRAINT Authors_Unique UNIQUE (name, sort_key) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Series ( " \ + " series_id INTEGER PRIMARY KEY, " \ + " name TEXT UNIQUE NOT NULL " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Tags ( " \ + " tag_id INTEGER PRIMARY KEY, " \ + " name TEXT NOT NULL, " \ + " parent_id INTEGER REFERENCES Tags (tag_id), " \ + " CONSTRAINT Tags_Unique UNIQUE (parent_id, name) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS BookAuthor ( " \ + " author_id INTEGER NOT NULL REFERENCES Authors (author_id), " \ + " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \ + " author_index INTEGER NOT NULL, " \ + " CONSTRAINT BookAuthor_Unique0 UNIQUE (author_id, book_id), " \ + " CONSTRAINT BookAuthor_Unique1 UNIQUE (book_id, author_index) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS BookSeries ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \ + " series_id INTEGER NOT NULL REFERENCES Series (series_id), " \ + " book_index INTEGER " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS BookTag ( " \ + " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \ + " tag_id INTEGER NOT NULL REFERENCES Tags (tag_id), " \ + " CONSTRAINT BookTag_Unique UNIQUE (book_id, tag_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.RecentBooks ( " \ + " book_index INTEGER PRIMARY KEY, " \ + " book_id INTEGER UNIQUE REFERENCES Books (book_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.BookStateStack ( " \ + " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \ + " position INTEGER NOT NULL, " \ + " paragraph INTEGER NOT NULL, " \ + " word INTEGER NOT NULL, " \ + " char INTEGER NOT NULL, " \ + " CONSTRAINT BookStateStack_Unique UNIQUE (book_id, position) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS PalmType ( " \ + " file_id INTEGER UNIQUE REFERENCES Files (file_id), " \ + " type TEXT NOT NULL " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.StackPosition ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \ + " stack_pos INTEGER NOT NULL " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.BookList ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id) " \ + "); "; + +const std::string BooksDBQuery::SECOND_INIT_DATABASE = \ + "CREATE TRIGGER IF NOT EXISTS Books_Delete BEFORE DELETE " \ + "ON Books FOR EACH ROW " \ + "BEGIN " \ + " DELETE FROM BookAuthor WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookSeries WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookTag WHERE book_id = OLD.book_id; " \ + " DELETE FROM StackPosition WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookStateStack WHERE book_id = OLD.book_id; " \ + " DELETE FROM RecentBooks WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookList WHERE book_id = OLD.book_id; " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Delete BEFORE DELETE " \ + "ON Files FOR EACH ROW " \ + "BEGIN " \ + " DELETE FROM Books WHERE file_id = OLD.file_id; " \ + " DELETE FROM PalmType WHERE file_id = OLD.file_id; " \ + "END; " \ + " " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Unique_Insert BEFORE INSERT " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.parent_id IS NULL AND f.name = NEW.name) " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"columns name, parent_id are not unique\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Directory_Insert BEFORE INSERT " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Directory entry\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_ArchEntry_Insert BEFORE INSERT " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NOT NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.file_id = NEW.parent_id AND f.parent_id IS NOT NULL) " \ + " AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Archive Entry entry\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Unique_Update BEFORE UPDATE " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.parent_id IS NULL AND f.name = NEW.name AND f.file_id != NEW.file_id) " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"columns name, parent_id are not unique\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Directory_Update BEFORE UPDATE " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Directory entry\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_ArchEntry_Update BEFORE UPDATE " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NOT NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.file_id = NEW.parent_id AND f.parent_id IS NOT NULL) " \ + " AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Archive Entry entry\"); " \ + "END; "; + +const std::string BooksDBQuery::CLEAR_DATABASE = \ + "DROP TRIGGER Books_Delete " \ + "DROP TRIGGER Files_Delete " \ + "DROP TRIGGER Files_Unique_Insert " \ + "DROP TRIGGER Files_Directory_Insert " \ + "DROP TRIGGER Files_ArchEntry_Insert " \ + "DROP TRIGGER Files_Unique_Update " \ + "DROP TRIGGER Files_Directory_Update " \ + "DROP TRIGGER Files_ArchEntry_Update " \ + " " \ + "DROP TABLE State.BookList; " \ + "DROP TABLE State.StackPosition; " \ + "DROP TABLE PalmType; " \ + "DROP TABLE State.BookStateStack; " \ + "DROP TABLE State.RecentBooks; " \ + "DROP TABLE BookTag; " \ + "DROP TABLE BookSeries; " \ + "DROP TABLE BookAuthor; " \ + "DROP TABLE Tags; " \ + "DROP TABLE Series; " \ + "DROP TABLE Authors; " \ + "DROP TABLE Books; " \ + "DROP TABLE Files; "; + + +const std::string BooksDBQuery::LOAD_BOOK = \ + "SELECT " \ + " b.book_id AS book_id, " \ + " b.encoding AS encoding, " \ + " b.language AS language, " \ + " b.title AS title, " \ + " b.file_id AS file_id " \ + "FROM Books AS b " \ + "WHERE b.file_id = @file_id; "; + +const std::string BooksDBQuery::ADD_BOOK = \ + "INSERT INTO Books (encoding, language, title, file_id) " \ + " VALUES ( " \ + " nullif(@encoding,\"auto\"), " \ + " nullif(@language,\"other\"), " \ + " @title, " \ + " @file_id " \ + " ); " \ + " " \ + "SELECT last_insert_rowid() AS book_id; "; + +const std::string BooksDBQuery::UPDATE_BOOK = \ + "UPDATE Books SET " \ + " encoding = nullif(@encoding,\"auto\"), " \ + " language = nullif(@language,\"other\"), " \ + " title = @title " \ + "WHERE " \ + " book_id = @book_id; "; + + + +const std::string BooksDBQuery::SET_BOOK_AUTHOR = \ + "INSERT OR REPLACE INTO BookAuthor (author_id, book_id, author_index) VALUES (@author_id, @book_id, @author_index); "; + +const std::string BooksDBQuery::TRIM_BOOK_AUTHORS = \ + "DELETE FROM BookAuthor WHERE book_id = @book_id AND author_index > @authors_number; "; + +const std::string BooksDBQuery::FIND_AUTHOR_ID = "SELECT author_id FROM Authors WHERE name = @name AND sort_key = @sort_key; "; + +const std::string BooksDBQuery::ADD_AUTHOR = \ + "INSERT INTO Authors (name, sort_key) VALUES (@name, @sort_key); " \ + "SELECT last_insert_rowid() AS author_id; "; + + +const std::string BooksDBQuery::SET_BOOKSERIES = \ + "INSERT OR REPLACE INTO BookSeries (book_id, series_id, book_index) VALUES (" \ + " @book_id, " \ + " @series_id, " \ + " nullif(@book_index, \"\") " \ + "); "; + +const std::string BooksDBQuery::DELETE_BOOKSERIES = \ + "DELETE FROM BookSeries " \ + "WHERE " \ + " book_id = @book_id; "; + +const std::string BooksDBQuery::FIND_SERIES_ID = "SELECT series_id FROM Series WHERE name = @name; "; + +const std::string BooksDBQuery::ADD_SERIES = \ + "INSERT INTO Series (name) VALUES (@name); " \ + "SELECT last_insert_rowid() AS series_id; "; + + +const std::string BooksDBQuery::GET_FILE_SIZE = "SELECT size FROM Files WHERE file_id = @file_id"; +const std::string BooksDBQuery::SET_FILE_SIZE = "UPDATE Files SET size = @size WHERE file_id = @file_id"; + +const std::string BooksDBQuery::SET_ENCODING = "UPDATE Books SET encoding = @encoding WHERE book_id = @book_id; "; + +const std::string BooksDBQuery::LOAD_FILE_ENTRIES = "SELECT name FROM Files WHERE coalesce(parent_id, 0) = @file_id; "; + +const std::string BooksDBQuery::INVALIDATE_BOOKS = "UPDATE Books SET title = \"\" WHERE file_id = @file_id; "; + + +const std::string BooksDBQuery::DELETE_FILE = "DELETE FROM Files WHERE file_id = @file_id; "; + +const std::string BooksDBQuery::DELETE_FILE_ENTRIES = "DELETE FROM Files WHERE parent_id = @file_id; "; + +const std::string BooksDBQuery::LOAD_FILE_ENTRY_IDS = "SELECT file_id FROM Files WHERE coalesce(parent_id, 0) = @file_id; "; + +const std::string BooksDBQuery::FIND_BOOK_ID = "SELECT book_id FROM Books WHERE file_id = @file_id; "; + + +const std::string BooksDBQuery::LOAD_RECENT_BOOKS = \ + "SELECT b.file_id " \ + "FROM Books AS b " \ + " INNER JOIN RecentBooks AS rb ON b.book_id = rb.book_id " \ + "ORDER BY rb.book_index; "; + +const std::string BooksDBQuery::CLEAR_RECENT_BOOKS = "DELETE FROM RecentBooks; "; + +const std::string BooksDBQuery::INSERT_RECENT_BOOKS = "INSERT INTO RecentBooks (book_id) VALUES (@book_id); "; + +const std::string BooksDBQuery::FIND_FILE_NAME = "SELECT name, parent_id FROM Files WHERE file_id = @file_id; "; + +const std::string BooksDBQuery::LOAD_BOOKS = "SELECT book_id, encoding, language, title, file_id FROM Books; "; + +const std::string BooksDBQuery::UPDATE_AUTHOR = \ + "UPDATE Authors SET " \ + " name = @newName, " \ + " sort_key = @newSortKey " \ + "WHERE name = @oldName " \ + " AND sort_key = @oldSortKey; "; + +const std::string BooksDBQuery::UPDATE_BOOKS_AUTHOR = "UPDATE OR REPLACE BookAuthor SET author_id = @newAuthorId WHERE author_id = @oldAuthorId; "; + +const std::string BooksDBQuery::LOAD_BOOK_STATE_STACK = "SELECT paragraph, word, char FROM BookStateStack WHERE book_id = @book_id AND position > 0 ORDER BY position; "; +const std::string BooksDBQuery::TRIM_BOOK_STATE_STACK = "DELETE FROM BookStateStack WHERE book_id = @book_id AND position > @stackSize; "; +const std::string BooksDBQuery::SET_BOOK_STATE_STACK = "INSERT OR REPLACE INTO BookStateStack(book_id, position, paragraph, word, char) VALUES (@book_id, @position, @paragraph, @word, @char); "; + +const std::string BooksDBQuery::SET_PALM_TYPE = "INSERT OR REPLACE INTO PalmType (file_id, type) VALUES (@file_id, @type); "; +const std::string BooksDBQuery::GET_PALM_TYPE = "SELECT type FROM PalmType WHERE file_id = @file_id; "; + +const std::string BooksDBQuery::LOAD_STACK_POS = "SELECT stack_pos FROM StackPosition WHERE book_id = @book_id; "; +const std::string BooksDBQuery::SET_STACK_POS = "INSERT OR REPLACE INTO StackPosition(book_id, stack_pos) VALUES (@book_id, @stack_pos); "; + +const std::string BooksDBQuery::LOAD_BOOK_STATE = "SELECT paragraph, word, char FROM BookStateStack WHERE book_id = @book_id AND position = 0; "; +const std::string BooksDBQuery::SET_BOOK_STATE = "INSERT OR REPLACE INTO BookStateStack(book_id, position, paragraph, word, char) VALUES (@book_id, 0, @paragraph, @word, @char); "; + +const std::string BooksDBQuery::INSERT_BOOK_LIST = "INSERT OR IGNORE INTO BookList(book_id) VALUES (@book_id); "; +const std::string BooksDBQuery::DELETE_BOOK_LIST = "DELETE FROM BookList WHERE book_id = @book_id; "; +const std::string BooksDBQuery::CHECK_BOOK_LIST = "SELECT COUNT(*) FROM BookList WHERE book_id = @book_id; "; diff --git a/fbreader/src/database/booksdb/BooksDBQuery.h b/fbreader/src/database/booksdb/BooksDBQuery.h new file mode 100644 index 0000000..c202dea --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBQuery.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __BOOKSDBQUERY_H__ +#define __BOOKSDBQUERY_H__ + +#include <string> + +class BooksDBQuery { + +public: + static const std::string ArchiveEntryDelimiter; + +public: + static const std::string PREINIT_DATABASE; + static const std::string INIT_DATABASE; + static const std::string SECOND_INIT_DATABASE; + static const std::string CLEAR_DATABASE; + + static const std::string LOAD_BOOK; + + static const std::string ADD_BOOK; + static const std::string UPDATE_BOOK; + + static const std::string FIND_AUTHOR_ID; + static const std::string ADD_AUTHOR; + + static const std::string GET_BOOK_AUTHORS_NUMBER; + static const std::string TRIM_BOOK_AUTHORS; + static const std::string SET_BOOK_AUTHOR; + + static const std::string DELETE_BOOKSERIES; + static const std::string FIND_SERIES_ID; + static const std::string ADD_SERIES; + static const std::string SET_BOOKSERIES; + + static const std::string GET_FILE_SIZE; + static const std::string SET_FILE_SIZE; + + static const std::string SET_ENCODING; + + static const std::string LOAD_FILE_ENTRIES; + + static const std::string INVALIDATE_BOOKS; + + static const std::string DELETE_FILE; + static const std::string DELETE_FILE_ENTRIES; + static const std::string LOAD_FILE_ENTRY_IDS; + + static const std::string FIND_BOOK_ID; + + static const std::string LOAD_RECENT_BOOKS; + static const std::string CLEAR_RECENT_BOOKS; + static const std::string INSERT_RECENT_BOOKS; + + static const std::string FIND_FILE_NAME; + + static const std::string LOAD_BOOKS; + + static const std::string UPDATE_AUTHOR; + static const std::string UPDATE_BOOKS_AUTHOR; + + static const std::string LOAD_BOOK_STATE_STACK; + static const std::string TRIM_BOOK_STATE_STACK; + static const std::string SET_BOOK_STATE_STACK; + + static const std::string GET_PALM_TYPE; + static const std::string SET_PALM_TYPE; + + static const std::string LOAD_BOOK_STATE; + static const std::string SET_BOOK_STATE; + static const std::string LOAD_STACK_POS; + static const std::string SET_STACK_POS; + + static const std::string INSERT_BOOK_LIST; + static const std::string DELETE_BOOK_LIST; + static const std::string CHECK_BOOK_LIST; + +private: // disable creation Instances + BooksDBQuery(); +}; + +#endif /* __BOOKSDBQUERY_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDBUtil.cpp b/fbreader/src/database/booksdb/BooksDBUtil.cpp new file mode 100644 index 0000000..3a7de96 --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBUtil.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLFile.h> +#include <ZLDir.h> +#include <ZLStringUtil.h> + +#include "BooksDBUtil.h" +#include "BooksDB.h" + +#include "../../library/Book.h" +#include "../../library/Tag.h" +#include "../../library/Author.h" + +shared_ptr<Book> BooksDBUtil::getBook(const std::string &filePath, bool checkFile) { + const std::string physicalFilePath = ZLFile(filePath).physicalFilePath(); + + ZLFile file(physicalFilePath); + if (checkFile && !file.exists()) { + return 0; + } + + if (!checkFile || checkInfo(file)) { + shared_ptr<Book> book = loadFromDB(filePath); + if (!book.isNull() && isBookFull(*book)) { + return book; + } + } else { + if (physicalFilePath != filePath) { + resetZipInfo(file); + } + saveInfo(file); + } + + shared_ptr<Book> book = Book::loadFromFile(ZLFile(filePath)); + if (book.isNull()) { + return 0; + } + BooksDB::Instance().saveBook(book); + return book; +} + +bool BooksDBUtil::getRecentBooks(BookList &books) { + std::vector<std::string> fileNames; + if (!BooksDB::Instance().loadRecentBooks(fileNames)) { + return false; + } + for (std::vector<std::string>::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) { + shared_ptr<Book> book = getBook(*it /*, true OR false ? */); // TODO: check file ??? + if (!book.isNull()) { + books.push_back(book); + } + } + return true; +} + +bool BooksDBUtil::getBooks(std::map<std::string, shared_ptr<Book> > &booksmap, bool checkFile) { + BookList books; + if (!BooksDB::Instance().loadBooks(books)) { + return false; + } + for (BookList::iterator it = books.begin(); it != books.end(); ++it) { + Book &book = **it; + const std::string physicalFilePath = book.file().physicalFilePath(); + ZLFile file(physicalFilePath); + if (!checkFile || file.exists()) { + if (!checkFile || checkInfo(file)) { + if (isBookFull(book)) { + booksmap.insert(std::make_pair(book.file().path(), *it)); + continue; + } + } else { + if (physicalFilePath != book.file().path()) { + resetZipInfo(file); + } + saveInfo(file); + } + shared_ptr<Book> bookptr = Book::loadFromFile(book.file()); + if (!bookptr.isNull()) { + BooksDB::Instance().saveBook(bookptr); + booksmap.insert(std::make_pair(book.file().path(), bookptr)); + } + } + } + return true; +} + +bool BooksDBUtil::isBookFull(const Book &book) { + return + !book.title().empty() && + !book.encoding().empty(); +} + +shared_ptr<Book> BooksDBUtil::loadFromDB(const std::string &filePath) { + if (filePath.empty()) { + return 0; + } + return BooksDB::Instance().loadBook(filePath); +} + +bool BooksDBUtil::checkInfo(const ZLFile &file) { + return BooksDB::Instance().getFileSize(file.path()) == (int) file.size(); +} + +void BooksDBUtil::saveInfo(const ZLFile &file) { + BooksDB::Instance().setFileSize(file.path(), file.size()); +} + +void BooksDBUtil::listZipEntries(const ZLFile &zipFile, std::vector<std::string> &entries) { + entries.clear(); + BooksDB::Instance().loadFileEntries(zipFile.path(), entries); + if (entries.empty()) { + resetZipInfo(zipFile); + BooksDB::Instance().loadFileEntries(zipFile.path(), entries); + } +} + +void BooksDBUtil::resetZipInfo(const ZLFile &zipFile) { + shared_ptr<ZLDir> zipDir = zipFile.directory(); + if (!zipDir.isNull()) { + std::vector<std::string> entries; + zipDir->collectFiles(entries, false); + BooksDB::Instance().saveFileEntries(zipFile.path(), entries); + } +} + +bool BooksDBUtil::canRemoveFile(const std::string &filePath) { + ZLFile bookFile(filePath); + std::string physicalPath = bookFile.physicalFilePath(); + if (filePath != physicalPath) { + ZLFile zipFile(physicalPath); + shared_ptr<ZLDir> zipDir = zipFile.directory(); + if (zipDir.isNull()) { + return false; + } + std::vector<std::string> entries; + zipDir->collectFiles(entries, false); // TODO: replace with BooksDB call??? + if (entries.size() != 1) { + return false; + } + if (zipDir->itemPath(entries[0]) != filePath) { + return false; + } + } + return ZLFile(physicalPath).canRemove(); +} + +void BooksDBUtil::addTag(shared_ptr<Book> book, shared_ptr<Tag> tag) { + if (book->addTag(tag)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::renameTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (book->renameTag(from, to, includeSubTags)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::cloneTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (book->cloneTag(from, to, includeSubTags)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::removeAllTags(shared_ptr<Book> book) { + book->removeAllTags(); +} diff --git a/fbreader/src/database/booksdb/BooksDBUtil.h b/fbreader/src/database/booksdb/BooksDBUtil.h new file mode 100644 index 0000000..8d55e1c --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBUtil.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __BOOKSDBUTIL_H__ +#define __BOOKSDBUTIL_H__ + +#include <map> + +#include <shared_ptr.h> + +#include "../../library/Lists.h" + +class Book; +class ZLFile; + +class BooksDBUtil { + +public: + static shared_ptr<Book> getBook(const std::string &fileName, bool checkFile = true); + + static bool getBooks(std::map<std::string, shared_ptr<Book> > &booksmap, bool checkFile = true); + + static bool getRecentBooks(BookList &books); + +public: + static void addTag(shared_ptr<Book> book, shared_ptr<Tag> tag); + static void renameTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + static void cloneTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + static void removeAllTags(shared_ptr<Book> book); + + static bool checkInfo(const ZLFile &file); + static void saveInfo(const ZLFile &file); + + static void listZipEntries(const ZLFile &zipFile, std::vector<std::string> &entries); + static void resetZipInfo(const ZLFile &zipFile); + + static bool isBookFull(const Book &book); + + static bool canRemoveFile(const std::string &fileName); + +private: + static shared_ptr<Book> loadFromDB(const std::string &fileName); +}; + +#endif /* __BOOKSDBUTIL_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp b/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp new file mode 100644 index 0000000..8cdc2a4 --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <string> + +#include "BooksDB.h" +#include "../../library/Book.h" +#include "../../library/Author.h" +#include "../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string LOAD_AUTHORS_QUERY = + "SELECT Authors.name, Authors.sort_key" \ + " FROM BookAuthor" \ + " INNER JOIN Authors ON Authors.author_id = BookAuthor.author_id" \ + " WHERE BookAuthor.book_id = @book_id" \ + " ORDER BY BookAuthor.author_index;"; +static const std::string LOAD_ALL_AUTHORS_QUERY = + "SELECT Authors.name, Authors.sort_key, BookAuthor.book_id" \ + " FROM BookAuthor" \ + " INNER JOIN Authors ON Authors.author_id = BookAuthor.author_id" \ + " ORDER BY BookAuthor.author_index;"; + +void BooksDB::loadAuthors(Book &book) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_AUTHORS_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = command->executeReader(); + + book.removeAllAuthors(); + + while (reader->next()) { + book.addAuthor( + reader->textValue(0, std::string()), + reader->textValue(1, std::string()) + ); + } +} + +void BooksDB::loadAuthors(const std::map<int,shared_ptr<Book> > &books) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_AUTHORS_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + + for (std::map<int,shared_ptr<Book> >::const_iterator it = books.begin(); it != books.end(); ++it) { + it->second->removeAllAuthors(); + } + + while (reader->next()) { + std::map<int,shared_ptr<Book> >::const_iterator it = + books.find((reader->type(2) == DBValue::DBINT) ? reader->intValue(2) : 0); + if (it != books.end()) { + it->second->addAuthor( + reader->textValue(0, std::string()), + reader->textValue(1, std::string()) + ); + } + } +} diff --git a/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp b/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp new file mode 100644 index 0000000..a9b860b --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <string> + +#include "BooksDB.h" +#include "../../library/Book.h" +#include "../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string LOAD_SERIES_QUERY = + "SELECT Series.name, BookSeries.book_index" \ + " FROM BookSeries" \ + " INNER JOIN Series ON Series.series_id = BookSeries.series_id" \ + " WHERE BookSeries.book_id = @book_id;"; +static const std::string LOAD_ALL_SERIES_QUERY = + "SELECT Series.name, BookSeries.book_index, BookSeries.book_id" \ + " FROM BookSeries" \ + " INNER JOIN Series ON Series.series_id = BookSeries.series_id"; + +static Number getSeriesIndex(shared_ptr<DBDataReader> reader) { + Number seriesIndex; + if (reader->type(1) == DBValue::DBTEXT) { + seriesIndex = Number(reader->textValue(1, std::string())); + } else if (reader->type(1) == DBValue::DBREAL){ //for old database scheme + seriesIndex = Number((int)reader->realValue(1)); + } else { //for old database scheme + seriesIndex = Number(reader->intValue(1)); + } + return seriesIndex; +} + +void BooksDB::loadSeries(Book &book) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_SERIES_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = command->executeReader(); + + if (reader->next()) { + std::string seriesTitle = reader->textValue(0, std::string()); + if (!seriesTitle.empty()) { + book.setSeries( + seriesTitle, + getSeriesIndex(reader) + ); + } + } +} + +void BooksDB::loadSeries(const std::map<int,shared_ptr<Book> > &books) { + shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_SERIES_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + + while (reader->next()) { + std::string seriesTitle = reader->textValue(0, std::string()); + std::map<int,shared_ptr<Book> >::const_iterator it = + books.find((reader->type(2) == DBValue::DBINT) ? reader->intValue(2) : 0); + if (!seriesTitle.empty() && it != books.end()) { + it->second->setSeries( + seriesTitle, + getSeriesIndex(reader) + ); + } + } +} diff --git a/fbreader/src/database/booksdb/BooksDB_BookTag.cpp b/fbreader/src/database/booksdb/BooksDB_BookTag.cpp new file mode 100644 index 0000000..a3d36fe --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB_BookTag.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <string> +#include <algorithm> + +#include "BooksDB.h" +#include "DBRunnables.h" +#include "../../library/Book.h" +#include "../../library/Tag.h" +#include "../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string LOAD_BOOK_TAGS_QUERY = + "SELECT tag_id FROM BookTag WHERE book_id = @book_id"; +static const std::string LOAD_ALL_BOOK_TAGS_QUERY = + "SELECT tag_id, book_id FROM BookTag"; +static const std::string LOAD_SINGLE_TAG_QUERY = + "SELECT name, parent_id FROM Tags WHERE tag_id = @tag_id"; +static const std::string LOAD_ALL_TAGS_QUERY = + "SELECT name, parent_id, tag_id FROM Tags ORDER BY tag_id"; +static const std::string ADD_BOOKTAG = + "INSERT INTO BookTag (book_id, tag_id) VALUES (@book_id, @tag_id)"; +static const std::string DELETE_BOOKTAG = + "DELETE FROM BookTag WHERE book_id = @book_id AND tag_id = @tag_id"; +static const std::string FIND_TAG_ID = + "SELECT tag_id FROM Tags" \ + " WHERE name = @name AND coalesce(parent_id, 0) = @parent_id"; +static const std::string ADD_TAG = + "INSERT INTO Tags (name, parent_id) VALUES (@name, nullif(@parent_id, 0));" \ + " SELECT last_insert_rowid() AS tag_id"; + +void BooksDB::loadTags(Book &book) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_BOOK_TAGS_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = command->executeReader(); + + book.removeAllTags(); + while (reader->next()) { + book.addTag(getTagById(reader->intValue(0))); + } +} + +void BooksDB::loadTags(const std::map<int,shared_ptr<Book> > &books) { + loadAllTagsById(); + + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_BOOK_TAGS_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + + for (std::map<int,shared_ptr<Book> >::const_iterator it = books.begin(); it != books.end(); ++it) { + it->second->removeAllTags(); + } + + while (reader->next()) { + std::map<int,shared_ptr<Book> >::const_iterator it = + books.find((reader->type(1) == DBValue::DBINT) ? reader->intValue(1) : 0); + if (it != books.end()) { + it->second->addTag(getTagById(reader->intValue(0))); + } + } +} + +shared_ptr<Tag> BooksDB::getTagById(int id) const { + if (id == 0) { + return 0; + } + + shared_ptr<Tag> tag = Tag::getTagById(id); + if (!tag.isNull()) { + return tag; + } + + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_SINGLE_TAG_QUERY, connection(), "@tag_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@tag_id").value()) = id; + shared_ptr<DBDataReader> reader = command->executeReader(); + if (!reader->next()) { + return 0; + } + const std::string name = reader->textValue(0, std::string()); + const int parentId = (reader->type(1) == DBValue::DBINT) ? + reader->intValue(1) : 0; + reader.reset(); + + return Tag::getTag(name, getTagById(parentId), id); +} + +void BooksDB::loadAllTagsById() const { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_TAGS_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + while (reader->next()) { + if (reader->type(2) != DBValue::DBINT) { + continue; + } + const int id = reader->intValue(2); + if (!Tag::getTagById(id).isNull()) { + continue; + } + Tag::getTag( + reader->textValue(0, std::string()), + Tag::getTagById( + (reader->type(1) == DBValue::DBINT) ? reader->intValue(1) : 0 + ), + id + ); + } +} + +SaveTagsRunnable::SaveTagsRunnable(DBConnection &connection) { + myDeleteBookTag = SQLiteFactory::createCommand( + DELETE_BOOKTAG, connection, + "@book_id", DBValue::DBINT, + "@tag_id", DBValue::DBINT + ); + myFindTagId = SQLiteFactory::createCommand( + FIND_TAG_ID, connection, + "@name", DBValue::DBTEXT, + "@parent_id", DBValue::DBINT + ); + myAddTag = SQLiteFactory::createCommand( + ADD_TAG, connection, + "@name", DBValue::DBTEXT, + "@parent_id", DBValue::DBINT + ); + myAddBookTag = SQLiteFactory::createCommand( + ADD_BOOKTAG, connection, + "@book_id", DBValue::DBINT, + "@tag_id", DBValue::DBINT + ); + myLoadBookTags = SQLiteFactory::createCommand( + LOAD_BOOK_TAGS_QUERY, connection, "@book_id", DBValue::DBINT + ); +} + +bool SaveTagsRunnable::run() { + if (myBook->bookId() == 0) { + return false; + } + + ((DBIntValue&)*myDeleteBookTag->parameter("@book_id").value()) = myBook->bookId(); + DBIntValue &delTagId = (DBIntValue&)*myDeleteBookTag->parameter("@tag_id").value(); + ((DBIntValue&)*myAddBookTag->parameter("@book_id").value()) = myBook->bookId(); + DBIntValue &addTagId = (DBIntValue&)*myAddBookTag->parameter("@tag_id").value(); + + TagList dbTags; + + ((DBIntValue&)*myLoadBookTags->parameter("@book_id").value()) = myBook->bookId(); + shared_ptr<DBDataReader> reader = myLoadBookTags->executeReader(); + + while (reader->next()) { + shared_ptr<Tag> tag = BooksDB::Instance().getTagById(reader->intValue(0)); + if (!tag.isNull()) { + dbTags.push_back(tag); + } + } + + TagList tags = myBook->tags(); // make copy of vector + + for (TagList::const_iterator it = dbTags.begin(); it != dbTags.end(); ++it) { + shared_ptr<Tag> tag = (*it); + findTagId(tag); + TagList::iterator jt = std::find(tags.begin(), tags.end(), tag); + if (jt == tags.end()) { + // Tag `tag` must be removed from BookTag table. + delTagId.setValue(tag->tagId()); + if (!myDeleteBookTag->execute()) { + return false; + } + } else { + // This tag is already in DataBase => need not to be inserted. + tags.erase(jt); + } + } + + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + int tableTagId = findTagId(*it); + if (tableTagId == 0) { + return false; + } + addTagId.setValue(tableTagId); + if (!myAddBookTag->execute()) { + return false; + } + } + return true; +} + + +int SaveTagsRunnable::findTagId(shared_ptr<Tag> tag) { + int tagId = tag->tagId(); + if (tagId != 0) { + return tagId; + } + int parentId = 0; + if (!tag->parent().isNull()) { + parentId = findTagId(tag->parent()); + if (parentId == 0) { + return 0; + } + } + + DBIntValue &findParent = (DBIntValue&)*myFindTagId->parameter("@parent_id").value(); + DBTextValue &findName = (DBTextValue&)*myFindTagId->parameter("@name").value(); + DBIntValue &addParent = (DBIntValue&)*myAddTag->parameter("@parent_id").value(); + DBTextValue &addName = (DBTextValue&)*myAddTag->parameter("@name").value(); + + findParent.setValue(parentId); + findName.setValue(tag->name()); + shared_ptr<DBValue> tableTagId = myFindTagId->executeScalar(); + if (tableTagId.isNull() || tableTagId->type() != DBValue::DBINT || ((DBIntValue&)*tableTagId).value() == 0) { + addParent.setValue(parentId); + addName.setValue(tag->name()); + tableTagId = myAddTag->executeScalar(); + if (tableTagId.isNull() || tableTagId->type() != DBValue::DBINT || ((DBIntValue&)*tableTagId).value() == 0) { + return 0; + } + } + Tag::setTagId(tag, ((DBIntValue&)*tableTagId).value()); + return ((DBIntValue&)*tableTagId).value(); +} + +void SaveTagsRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/booksdb/DBRunnables.h b/fbreader/src/database/booksdb/DBRunnables.h new file mode 100644 index 0000000..4baee65 --- /dev/null +++ b/fbreader/src/database/booksdb/DBRunnables.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __DBRUNNABLES_H__ +#define __DBRUNNABLES_H__ + +#include <deque> +#include <vector> + +#include "../../fbreader/ReadingState.h" + +#include "../sqldb/DBConnection.h" +#include "../sqldb/DBCommand.h" +#include "../sqldb/DBRunnable.h" + +#include "BooksDBQuery.h" + +#include "../../library/Lists.h" + +class FindFileIdRunnable; +class LoadFileEntriesRunnable; +class DeleteFileEntriesRunnable; + +/* + * Save Runnables + */ + + +class InitBooksDBRunnable : public DBRunnable { + +public: + InitBooksDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class ClearBooksDBRunnable : public DBRunnable { + +public: + ClearBooksDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class SaveTableBookRunnable : public DBRunnable { + +public: + SaveTableBookRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + bool addTableBook(const shared_ptr<Book> book, int fileId); + bool updateTableBook(const shared_ptr<Book> book); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> myFindBookId; + + shared_ptr<DBCommand> myAddBook; + shared_ptr<DBCommand> myUpdateBook; + + shared_ptr<FindFileIdRunnable> myFindFileId; +}; + +class SaveAuthorsRunnable : public DBRunnable { + +public: + SaveAuthorsRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> mySetBookAuthor; + shared_ptr<DBCommand> myTrimBookAuthors; + + shared_ptr<DBCommand> myFindAuthorId; + shared_ptr<DBCommand> myAddAuthor; +}; + +class SaveTagsRunnable : public DBRunnable { + +public: + SaveTagsRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + int findTagId(shared_ptr<Tag> tag); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> myAddBookTag; + shared_ptr<DBCommand> myDeleteBookTag; + + shared_ptr<DBCommand> myFindTagId; + shared_ptr<DBCommand> myAddTag; + + shared_ptr<DBCommand> myLoadBookTags; +}; + + +class SaveSeriesRunnable : public DBRunnable { + +public: + SaveSeriesRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> mySetBookSeries; + shared_ptr<DBCommand> myDeleteBookSeries; + + shared_ptr<DBCommand> myFindSeriesId; + shared_ptr<DBCommand> myAddSeries; +}; + +class SaveBookRunnable : public DBRunnable { +public: + SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors, + SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags); + + bool run(); + void setBook(shared_ptr<Book> book); + +private: + SaveTableBookRunnable &mySaveTableBook; + SaveAuthorsRunnable &mySaveAuthors; + SaveSeriesRunnable &mySaveSeries; + SaveTagsRunnable &mySaveTags; +}; + +class SaveFileEntriesRunnable : public DBRunnable { + +public: + SaveFileEntriesRunnable(DBConnection &connection); + bool run(); + void setEntries(const std::string &fileName, const std::vector<std::string> &entries); + +private: + std::string myFileName; + std::vector<std::string> myEntries; + + shared_ptr<DBCommand> myAddFile; + + shared_ptr<FindFileIdRunnable> myFindFileId; + shared_ptr<DeleteFileEntriesRunnable> myDeleteFileEntries; +}; + +class SaveRecentBooksRunnable : public DBRunnable { + +public: + SaveRecentBooksRunnable(DBConnection &connection); + bool run(); + void setBooks(const BookList &books); + +private: + BookList myBooks; + + shared_ptr<DBCommand> myClearRecentBooks; + shared_ptr<DBCommand> myInsertRecentBooks; +}; + +class SaveBookStateStackRunnable : public DBRunnable { + +public: + SaveBookStateStackRunnable(DBConnection &connection); + bool run(); + void setState(int bookId, const std::deque<ReadingState > &stack); + +private: + int myBookId; + std::deque<ReadingState > myStack; + + shared_ptr<DBCommand> myTrimBookStateStack; + shared_ptr<DBCommand> mySetBookStateStack; +}; + +class DeleteFileEntriesRunnable : public DBRunnable { + +public: + DeleteFileEntriesRunnable(DBConnection &connection); + bool run(); + void setFileId(int fileId); + +private: + bool doDelete(int fileId); + +private: + int myFileId; + + shared_ptr<DBCommand> myDeleteFileEntries; + shared_ptr<DBCommand> myLoadFileEntryIds; +}; + +class DeleteBookRunnable : public DBRunnable { + +public: + DeleteBookRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName); + +private: + std::string myFileName; + + shared_ptr<FindFileIdRunnable> myFindFileId; + shared_ptr<DBCommand> myDeleteFile; +}; + + +inline InitBooksDBRunnable::InitBooksDBRunnable(DBConnection &connection) : myConnection(connection) {} +inline ClearBooksDBRunnable::ClearBooksDBRunnable(DBConnection &connection) : myConnection(connection) {} + +inline void SaveFileEntriesRunnable::setEntries(const std::string &fileName, const std::vector<std::string> &entries) { + myFileName = fileName; + myEntries = entries; // copy vector +} + +inline void SaveBookStateStackRunnable::setState(int bookId, const std::deque<ReadingState > &stack) { + myBookId = bookId; + myStack = stack; // copy deque +} + +inline void DeleteFileEntriesRunnable::setFileId(int fileId) { myFileId = fileId; } + +inline void DeleteBookRunnable::setFileName(const std::string &fileName) { myFileName = fileName; } + +/* + * Load & Modify Runnables + */ + +class FindFileIdRunnable : public DBRunnable { + +public: + FindFileIdRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName, bool add = false); + + int fileId() const; + +private: + std::string myFileName; + bool myAdd; + int myFileId; + + shared_ptr<DBCommand> myFindFileId; + shared_ptr<DBCommand> myAddFile; +}; + + +/* + * Load Runnables + */ + +class LoadFileEntriesRunnable : public DBRunnable { + +public: + LoadFileEntriesRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName); + void collectEntries(std::vector<std::string> &entries); + +private: + std::string myFileName; + std::vector<std::string> myEntries; + + shared_ptr<FindFileIdRunnable> myFindFileId; + + shared_ptr<DBCommand> myLoadFileEntries; +}; + +class LoadRecentBooksRunnable : public DBRunnable { + +public: + LoadRecentBooksRunnable(DBConnection &connection); + bool run(); + void collectFileIds(std::vector<int> &fileIds); + +private: + std::vector<int> myFileIds; + + shared_ptr<DBCommand> myLoadRecentBooks; +}; + +#endif /* __DBRUNNABLES_H__ */ diff --git a/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp b/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp new file mode 100644 index 0000000..c0c9063 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool ClearBooksDBRunnable::run() { + shared_ptr<DBCommand> cmd; + + cmd = SQLiteFactory::createCommand(BooksDBQuery::CLEAR_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(BooksDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(BooksDBQuery::SECOND_INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + return true; +} + + diff --git a/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp new file mode 100644 index 0000000..e9e2c14 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +DeleteBookRunnable::DeleteBookRunnable(DBConnection &connection) { + myFindFileId = new FindFileIdRunnable(connection); + myDeleteFile = SQLiteFactory::createCommand(BooksDBQuery::DELETE_FILE, connection, "@file_id", DBValue::DBINT); +} + +bool DeleteBookRunnable::run() { + myFindFileId->setFileName(myFileName, true); + if (!myFindFileId->run()) { + return false; + } + + (DBIntValue &) *myDeleteFile->parameter("@file_id").value() = myFindFileId->fileId(); + return myDeleteFile->execute(); +} + diff --git a/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp new file mode 100644 index 0000000..065a4c2 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +DeleteFileEntriesRunnable::DeleteFileEntriesRunnable(DBConnection &connection) { + myDeleteFileEntries = SQLiteFactory::createCommand(BooksDBQuery::DELETE_FILE_ENTRIES, connection, "@file_id", DBValue::DBINT); + myLoadFileEntryIds = SQLiteFactory::createCommand(BooksDBQuery::LOAD_FILE_ENTRY_IDS, connection, "@file_id", DBValue::DBINT); +} + +bool DeleteFileEntriesRunnable::run() { + return doDelete(myFileId); +} + +bool DeleteFileEntriesRunnable::doDelete(int fileId) { + (DBIntValue &) *myLoadFileEntryIds->parameter("@file_id").value() = fileId; + shared_ptr<DBDataReader> reader = myLoadFileEntryIds->executeReader(); + if (reader.isNull()) { + return false; + } + + std::vector<int> fileIds; + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT /* file_id */ ) { + reader->close(); + return false; + } + fileIds.push_back(reader->intValue(0)); + } + reader->close(); + + if (fileIds.empty()) { + return true; + } + for (std::vector<int>::const_iterator it = fileIds.begin(); it != fileIds.end(); ++it) { + if (!doDelete(*it)) { + return false; + } + } + (DBIntValue &) *myDeleteFileEntries->parameter("@file_id").value() = fileId; + return myDeleteFileEntries->execute(); +} + diff --git a/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp b/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp new file mode 100644 index 0000000..4461c4f --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <ZLibrary.h> +#include <ZLFile.h> + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string FIND_FILE_ID = + "SELECT file_id FROM Files" \ + " WHERE name = @name AND coalesce(parent_id, 0) = @parent_id;"; + +static const std::string ADD_FILE = + "INSERT INTO Files (name, parent_id, size)" \ + " VALUES(@name, nullif(@parent_id, 0), nullif(@size, 0));" \ + " SELECT last_insert_rowid() AS file_id;"; + +FindFileIdRunnable::FindFileIdRunnable(DBConnection &connection) { + myFindFileId = SQLiteFactory::createCommand(FIND_FILE_ID, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT); + myAddFile = SQLiteFactory::createCommand(ADD_FILE, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT, "@size", DBValue::DBINT); +} + +bool FindFileIdRunnable::run() { + const std::string resolvedPath = ZLFile(myFileName).resolvedPath(); + const std::string physPath = ZLFile(resolvedPath).physicalFilePath(); + const std::string dirName = physPath.substr(0, physPath.rfind(ZLibrary::FileNameDelimiter)); + + DBTextValue &findName = (DBTextValue &) *myFindFileId->parameter("@name").value(); + DBIntValue &findParent = (DBIntValue &) *myFindFileId->parameter("@parent_id").value(); + + DBTextValue &addName = (DBTextValue &) *myAddFile->parameter("@name").value(); + DBIntValue &addParent = (DBIntValue &) *myAddFile->parameter("@parent_id").value(); + ((DBIntValue &) *myAddFile->parameter("@size").value()) = 0; + + std::size_t index = dirName.length() + 1; + findName = dirName; + findParent = 0; + while (true) { + shared_ptr<DBValue> physId = myFindFileId->executeScalar(); + if (physId.isNull() || physId->type() != DBValue::DBINT || ((DBIntValue &) *physId).value() == 0) { + if (!myAdd) { + return false; + } + addName = findName.value(); + addParent = findParent.value(); + physId = myAddFile->executeScalar(); + if (physId.isNull() || physId->type() != DBValue::DBINT || ((DBIntValue &) *physId).value() == 0) { + return false; + } + } + if (index == 0) { + myFileId = ((DBIntValue &) *physId).value(); + return true; + } + std::size_t index2 = resolvedPath.find(BooksDBQuery::ArchiveEntryDelimiter, index); + findName = resolvedPath.substr(index, index2 - index); + index = index2 + 1; + findParent = ((DBIntValue &) *physId).value(); + } +} + +void FindFileIdRunnable::setFileName(const std::string &fileName, bool add) { + myFileName = fileName; + myAdd = add; + myFileId = 0; +} + +int FindFileIdRunnable::fileId() const { + return myFileId; +} + +SaveFileEntriesRunnable::SaveFileEntriesRunnable(DBConnection &connection) { + myAddFile = SQLiteFactory::createCommand(ADD_FILE, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT, "@size", DBValue::DBINT); + + myFindFileId = new FindFileIdRunnable(connection); + myDeleteFileEntries = new DeleteFileEntriesRunnable(connection); +} + +bool SaveFileEntriesRunnable::run() { + myFindFileId->setFileName(myFileName, true); + if (!myFindFileId->run()) { + return false; + } + + myDeleteFileEntries->setFileId(myFindFileId->fileId()); + if (!myDeleteFileEntries->run()) { + return false; + } + + DBTextValue &addName = (DBTextValue &) *myAddFile->parameter("@name").value(); + ((DBIntValue &) *myAddFile->parameter("@parent_id").value()) = myFindFileId->fileId(); + ((DBIntValue &) *myAddFile->parameter("@size").value()) = 0; + + for (std::vector<std::string>::const_iterator it = myEntries.begin(); it != myEntries.end(); ++it) { + const std::string &entry = (*it); + if (entry.empty()) { + continue; + } + addName = entry; + if (!myAddFile->execute()) { + return false; + } + } + return true; +} + diff --git a/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp b/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp new file mode 100644 index 0000000..b8a4b01 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool InitBooksDBRunnable::run() { + shared_ptr<DBCommand> cmd; + cmd = SQLiteFactory::createCommand(BooksDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(BooksDBQuery::SECOND_INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + return true; +} + + diff --git a/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp new file mode 100644 index 0000000..3668b83 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +LoadFileEntriesRunnable::LoadFileEntriesRunnable(DBConnection &connection) { + myLoadFileEntries = SQLiteFactory::createCommand(BooksDBQuery::LOAD_FILE_ENTRIES, connection, "@file_id", DBValue::DBINT); + + myFindFileId = new FindFileIdRunnable(connection); +} + +bool LoadFileEntriesRunnable::run() { + DBCommand &cmd = *myLoadFileEntries; + + myFindFileId->setFileName(myFileName); + if (!myFindFileId->run()) { + return false; + } + ((DBIntValue &) *cmd.parameter("@file_id").value()) = myFindFileId->fileId(); + + shared_ptr<DBDataReader> reader = cmd.executeReader(); + + if (reader.isNull()) { + return false; + } + + myEntries.clear(); + + bool res = true; + while (reader->next()) { + if (reader->type(0) != DBValue::DBTEXT /* name */) { + res = false; + continue; + } + myEntries.push_back( + myFileName + BooksDBQuery::ArchiveEntryDelimiter + + reader->textValue(0, std::string()) + ); + } + reader->close(); + return res; +} + +void LoadFileEntriesRunnable::setFileName(const std::string &fileName) { + myFileName = fileName; +} + +void LoadFileEntriesRunnable::collectEntries(std::vector<std::string> &entries) { + myEntries.swap(entries); + myEntries.clear(); +} diff --git a/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp b/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp new file mode 100644 index 0000000..06e6f79 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../../library/Author.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +LoadRecentBooksRunnable::LoadRecentBooksRunnable(DBConnection &connection) { + myLoadRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::LOAD_RECENT_BOOKS, connection); +} + +bool LoadRecentBooksRunnable::run() { + shared_ptr<DBDataReader> reader = myLoadRecentBooks->executeReader(); + if (reader.isNull()) { + return false; + } + myFileIds.clear(); + + bool res = true; + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT /* file_id */) { + res = false; + continue; + } + const int fileId = reader->intValue(0); + myFileIds.push_back(fileId); + } + reader->close(); + return res; +} + +void LoadRecentBooksRunnable::collectFileIds(std::vector<int> &fileIds) { + myFileIds.swap(fileIds); + myFileIds.clear(); +} diff --git a/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp new file mode 100644 index 0000000..7336a74 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <algorithm> + +#include "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../../library/Author.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveAuthorsRunnable::SaveAuthorsRunnable(DBConnection &connection) { + mySetBookAuthor = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_AUTHOR, connection, "@author_id", DBValue::DBINT, "@book_id", DBValue::DBINT, "@author_index", DBValue::DBINT); + myTrimBookAuthors = SQLiteFactory::createCommand(BooksDBQuery::TRIM_BOOK_AUTHORS, connection, "@book_id", DBValue::DBINT, "@authors_number", DBValue::DBINT); + myFindAuthorId = SQLiteFactory::createCommand(BooksDBQuery::FIND_AUTHOR_ID, connection, "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT); + myAddAuthor = SQLiteFactory::createCommand(BooksDBQuery::ADD_AUTHOR, connection, "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT); +} + +bool SaveAuthorsRunnable::run() { + if (myBook->bookId() == 0) { + return false; + } + const AuthorList &bookAuthors = myBook->authors(); // save link to vector + + ((DBIntValue &) *mySetBookAuthor->parameter("@book_id").value()) = myBook->bookId(); + DBIntValue &setAuthorId = (DBIntValue &) *mySetBookAuthor->parameter("@author_id").value(); + DBIntValue &setAuthorIndex = (DBIntValue &) *mySetBookAuthor->parameter("@author_index").value(); + DBTextValue &findAuthor = (DBTextValue &) *myFindAuthorId->parameter("@name").value(); + DBTextValue &findSortKey = (DBTextValue &) *myFindAuthorId->parameter("@sort_key").value(); + DBTextValue &addAuthor = (DBTextValue &) *myAddAuthor->parameter("@name").value(); + DBTextValue &addSortKey = (DBTextValue &) *myAddAuthor->parameter("@sort_key").value(); + + int index = 0; + for (AuthorList::const_iterator it = bookAuthors.begin(); it != bookAuthors.end(); ++it) { + const Author &author = **it; + findAuthor.setValue( author.name() ); + findSortKey.setValue( author.sortKey() ); + shared_ptr<DBValue> tableAuthorId = myFindAuthorId->executeScalar(); + if (tableAuthorId.isNull() || tableAuthorId->type() != DBValue::DBINT || ((DBIntValue &) *tableAuthorId).value() == 0) { + addAuthor.setValue( author.name() ); + addSortKey.setValue( author.sortKey() ); + tableAuthorId = myAddAuthor->executeScalar(); + if (tableAuthorId.isNull() || tableAuthorId->type() != DBValue::DBINT || ((DBIntValue &) *tableAuthorId).value() == 0) { + return false; + } + } + setAuthorId = ((DBIntValue &) *tableAuthorId).value(); + setAuthorIndex = ++index; + if (!mySetBookAuthor->execute()) { + return false; + } + } + ((DBIntValue &) *myTrimBookAuthors->parameter("@book_id").value()) = myBook->bookId(); + ((DBIntValue &) *myTrimBookAuthors->parameter("@authors_number").value()) = index; + return myTrimBookAuthors->execute(); +} + +void SaveAuthorsRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp new file mode 100644 index 0000000..7cf8dff --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../../library/Book.h" + +SaveBookRunnable::SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors, + SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags) : + mySaveTableBook(saveTableBook), + mySaveAuthors(saveAuthors), + mySaveSeries(saveSeries), + mySaveTags(saveTags) { +} + +bool SaveBookRunnable::run() { + return mySaveTableBook.run() + && mySaveAuthors.run() + && mySaveSeries.run() + && mySaveTags.run(); +} + +void SaveBookRunnable::setBook(shared_ptr<Book> book) { + mySaveTableBook.setBook(book); + mySaveAuthors.setBook(book); + mySaveTags.setBook(book); + mySaveSeries.setBook(book); +} diff --git a/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp new file mode 100644 index 0000000..85d4a89 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + + +SaveBookStateStackRunnable::SaveBookStateStackRunnable(DBConnection &connection) { + myTrimBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::TRIM_BOOK_STATE_STACK, connection, "@book_id", DBValue::DBINT, "@stackSize", DBValue::DBINT); + mySetBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_STATE_STACK, connection, "@book_id", DBValue::DBINT, "@position", DBValue::DBINT, "@paragraph", DBValue::DBINT, "@word", DBValue::DBINT, "@char", DBValue::DBINT); +} + +bool SaveBookStateStackRunnable::run() { + ((DBIntValue &) *myTrimBookStateStack->parameter("@book_id").value()) = myBookId; + ((DBIntValue &) *myTrimBookStateStack->parameter("@stackSize").value()) = myStack.size(); + + if (!myTrimBookStateStack->execute()) { + return false; + } + + ((DBIntValue &) *mySetBookStateStack->parameter("@book_id").value()) = myBookId; + + DBIntValue &savePosition = (DBIntValue &) *mySetBookStateStack->parameter("@position").value(); + DBIntValue &saveParagraph = (DBIntValue &) *mySetBookStateStack->parameter("@paragraph").value(); + DBIntValue &saveWord = (DBIntValue &) *mySetBookStateStack->parameter("@word").value(); + DBIntValue &saveChar = (DBIntValue &) *mySetBookStateStack->parameter("@char").value(); + + for (std::size_t i = 0; i < myStack.size(); ++i) { + const ReadingState &pos = myStack[i]; + savePosition = i + 1; + saveParagraph = pos.Paragraph; + saveWord = pos.Word; + saveChar = pos.Character; + if (!mySetBookStateStack->execute()) { + return false; + } + } + return true; +} + diff --git a/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp new file mode 100644 index 0000000..1c355ed --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveRecentBooksRunnable::SaveRecentBooksRunnable(DBConnection &connection) { + myClearRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::CLEAR_RECENT_BOOKS, connection); + myInsertRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::INSERT_RECENT_BOOKS, connection, "@book_id", DBValue::DBINT); +} + +bool SaveRecentBooksRunnable::run() { + if (!myClearRecentBooks->execute()) { + return false; + } + DBIntValue &insertBookId = (DBIntValue &) *myInsertRecentBooks->parameter("@book_id").value(); + for (BookList::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + shared_ptr<Book> book = (*it); + if (book->bookId() == 0) { + return false; + } + insertBookId = book->bookId(); + if (!myInsertRecentBooks->execute()) { + return false; + } + } + return true; +} + +void SaveRecentBooksRunnable::setBooks(const BookList &books) { + myBooks = books; // copy vector +} diff --git a/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp new file mode 100644 index 0000000..e56777b --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveSeriesRunnable::SaveSeriesRunnable(DBConnection &connection) { + mySetBookSeries = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOKSERIES, connection, "@book_id", DBValue::DBINT, "@series_id", DBValue::DBINT, "@book_index", DBValue::DBTEXT); + myDeleteBookSeries = SQLiteFactory::createCommand(BooksDBQuery::DELETE_BOOKSERIES, connection, "@book_id", DBValue::DBINT); + myFindSeriesId = SQLiteFactory::createCommand(BooksDBQuery::FIND_SERIES_ID, connection, "@name", DBValue::DBTEXT); + myAddSeries = SQLiteFactory::createCommand(BooksDBQuery::ADD_SERIES, connection, "@name", DBValue::DBTEXT); +} + +bool SaveSeriesRunnable::run() { + if (myBook->bookId() == 0) { + return false; + } + + if (myBook->seriesTitle().empty()) { + ((DBIntValue &) *myDeleteBookSeries->parameter("@book_id").value()) = myBook->bookId(); + return myDeleteBookSeries->execute(); + } + + ((DBTextValue &) *myFindSeriesId->parameter("@name").value()) = myBook->seriesTitle(); + shared_ptr<DBValue> tableSeriesId = myFindSeriesId->executeScalar(); + if (tableSeriesId.isNull() || tableSeriesId->type() != DBValue::DBINT || ((DBIntValue &) *tableSeriesId).value() == 0) { + ((DBTextValue &) *myAddSeries->parameter("@name").value()) = myBook->seriesTitle(); + tableSeriesId = myAddSeries->executeScalar(); + if (tableSeriesId.isNull() || tableSeriesId->type() != DBValue::DBINT || ((DBIntValue &) *tableSeriesId).value() == 0) { + return false; + } + } + ((DBIntValue &) *mySetBookSeries->parameter("@book_id").value()) = myBook->bookId(); + mySetBookSeries->parameter("@series_id").setValue( tableSeriesId ); + ((DBTextValue &) *mySetBookSeries->parameter("@book_index").value()) = myBook->indexInSeries().value(); + return mySetBookSeries->execute(); +} + +void SaveSeriesRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp new file mode 100644 index 0000000..770963e --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009-2012 Geometer Plus <[email protected]> + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveTableBookRunnable::SaveTableBookRunnable(DBConnection &connection) { + myFindBookId = SQLiteFactory::createCommand(BooksDBQuery::FIND_BOOK_ID, connection, "@file_id", DBValue::DBINT); + + myAddBook = SQLiteFactory::createCommand(BooksDBQuery::ADD_BOOK, connection, "@encoding", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@title", DBValue::DBTEXT, "@file_id", DBValue::DBINT); + myUpdateBook = SQLiteFactory::createCommand(BooksDBQuery::UPDATE_BOOK, connection, "@encoding", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@title", DBValue::DBTEXT, "@book_id", DBValue::DBINT); + + myFindFileId = new FindFileIdRunnable(connection); +} + +bool SaveTableBookRunnable::run() { + if (myBook->bookId() != 0) { + return updateTableBook(myBook); + } + + myFindFileId->setFileName(myBook->file().path(), true); + if (!myFindFileId->run()) { + return false; + } + const int fileId = myFindFileId->fileId(); + + ((DBIntValue &) *myFindBookId->parameter("@file_id").value()) = fileId; + shared_ptr<DBValue> dbBookId = myFindBookId->executeScalar(); + + if (dbBookId.isNull() || dbBookId->type() != DBValue::DBINT || ((DBIntValue &) *dbBookId).value() == 0) { + return addTableBook(myBook, fileId); + } else { + myBook->setBookId( ((DBIntValue &) *dbBookId).value() ); + return updateTableBook(myBook); + } +} + +bool SaveTableBookRunnable::addTableBook(const shared_ptr<Book> book, int fileId) { + + ((DBTextValue &) *myAddBook->parameter("@encoding").value()) = book->encoding(); + ((DBTextValue &) *myAddBook->parameter("@language").value()) = book->language(); + ((DBTextValue &) *myAddBook->parameter("@title").value()) = book->title(); + ((DBIntValue &) *myAddBook->parameter("@file_id").value()) = fileId; + shared_ptr<DBValue> dbBookId = myAddBook->executeScalar(); + + if (dbBookId.isNull() || dbBookId->type() != DBValue::DBINT || ((DBIntValue &) *dbBookId).value() == 0) { + return false; + } + book->setBookId(((DBIntValue&)*dbBookId).value()); + return true; +} + +bool SaveTableBookRunnable::updateTableBook(const shared_ptr<Book> book) { + ((DBTextValue&)*myUpdateBook->parameter("@encoding").value()) = book->encoding(); + ((DBTextValue&)*myUpdateBook->parameter("@language").value()) = book->language(); + ((DBTextValue&)*myUpdateBook->parameter("@title").value()) = book->title(); + ((DBIntValue&)*myUpdateBook->parameter("@book_id").value()) = book->bookId(); + return myUpdateBook->execute(); +} + +void SaveTableBookRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} |