2 #include "tmdatabase.h"
13 // DatabaseBase ---------------------------------------------------------------
15 bool TM::DatabaseBase::open(char const *message)
17 assert(!m_database_name.isEmpty());
18 m_database = QSqlDatabase::addDatabase("QSQLITE", m_database_name);
19 m_database.setDatabaseName(m_database_name);
20 bool ret = m_database.open();
29 QSqlQuery TM::DatabaseBase::prepare(char const *sql, char const *message)
31 QSqlQuery result(m_database);
32 bool ret = result.prepare(sql);
35 error(message, &result);
41 bool TM::DatabaseBase::exec(QSqlQuery &query, char const *message)
43 bool ret = query.exec();
46 error(message, &query);
52 QSqlQuery TM::DatabaseBase::exec(char const *sql, char const *message)
54 QSqlQuery result = m_database.exec(sql);
55 if(result.lastError().isValid())
57 error(message, &result);
63 void TM::DatabaseBase::error(QString message, QSqlQuery *query)
66 if(query) s = query->lastError().text();
67 else s = m_database.lastError().text();
69 qCritical() << message << s;
72 // SiteDatabase ---------------------------------------------------------------
74 TM::SiteDatabase::SiteDatabase(Settings *settings)
77 m_cache_limit = settings->value(
78 "SiteDatabase/cache-limit", QVariant::fromValue(1024)).toInt();
81 m_database_name = settings->value(TMDATABASE_ROOT_PATH_KEY).toString() + "/site.db";
85 exec("CREATE TABLE IF NOT EXISTS sites("
86 "site_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
87 "host_name TEXT UNIQUE);", Q_FUNC_INFO);
89 exec("CREATE INDEX IF NOT EXISTS domain_index ON sites(host_name);", Q_FUNC_INFO);
92 m_find_site_id = prepare("SELECT site_id FROM sites WHERE host_name=?;", Q_FUNC_INFO);
93 m_insert_site = prepare("INSERT INTO sites(host_name) VALUES(?);", Q_FUNC_INFO);
96 TM::SiteDatabase::~SiteDatabase()
98 if(m_database.isOpen()) m_database.close();
102 * \brief 引数として与えられたドメイン名に対して一意な番号を返します。
104 int TM::SiteDatabase::site_id(QString domain)
106 // キャッシュが大きくなりすぎた場合、消去する。
107 if(m_cache_limit < m_cache.size()) m_cache.clear();
109 QMap<QString, int>::const_iterator it = m_cache.find(domain);
110 if(it != m_cache.end()) return *it; // キャッシュに存在する場合、ここで終わり。
112 // キャッシュにない場合、データベースを検索。
113 int result = find_site_id(domain);
114 // データベースにない場合、データベースに登録。
118 result = find_site_id(domain);
122 m_cache.insert(domain, result);
128 * \brief 引数として与えられたドメイン名に対して一意な番号を返します。
132 int TM::SiteDatabase::find_site_id(QString host_name)
134 m_find_site_id.bindValue(0, host_name);
135 exec(m_find_site_id, Q_FUNC_INFO);
136 if(m_find_site_id.next()) return m_find_site_id.value(0).toInt();
141 void TM::SiteDatabase::insert_site(QString host_name)
143 m_insert_site.bindValue(0, host_name);
144 exec(m_insert_site, Q_FUNC_INFO);
147 TM::SiteDatabase::pointer TM::SiteDatabase::create(Settings *settings)
149 return pointer(new SiteDatabase(settings));
152 // WordDatabase ---------------------------------------------------------------
154 TM::WordDatabase::WordDatabase(Settings *settings, QString name)
157 m_cache_limit = settings->value(
158 "WordDatabase/cache-limit", QVariant::fromValue(100 * 1024)).toInt();
161 m_database_name = settings->value(
162 TMDATABASE_ROOT_PATH_KEY).toString() + "/word-" + name.toLower() + ".db";
166 exec("CREATE TABLE IF NOT EXISTS words("
167 "word_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
168 "word TEXT UNIQUE);", Q_FUNC_INFO);
170 exec("CREATE INDEX IF NOT EXISTS word_index ON words(word);", Q_FUNC_INFO);
173 m_find_word_id = prepare(
174 "SELECT word_id FROM words WHERE word=?;", Q_FUNC_INFO);
176 m_insert_word = prepare("INSERT INTO words(word) VALUES(?);", Q_FUNC_INFO);
179 TM::WordDatabase::~WordDatabase()
181 if(m_database.isOpen()) m_database.close();
184 int TM::WordDatabase::word_id(QString word)
186 // キャッシュが大きくなりすぎた場合、消去する。
187 if(m_cache_limit < m_cache.size()) m_cache.clear();
189 QMap<QString, int>::const_iterator it = m_cache.find(word);
190 if(it != m_cache.end()) return *it; // キャッシュに存在する場合、ここで終わり。
192 // キャッシュにない場合、データベースを検索。
193 int result = find_word_id(word);
194 // データベースにない場合、データベースに登録。
198 result = find_word_id(word);
202 m_cache.insert(word, result);
207 int TM::WordDatabase::find_word_id(QString word)
209 m_find_word_id.bindValue(0, word);
210 exec(m_find_word_id, Q_FUNC_INFO);
211 if(m_find_word_id.next()) return m_find_word_id.value(0).toInt();
216 void TM::WordDatabase::insert_word(QString word)
218 m_insert_word.bindValue(0, word);
219 exec(m_insert_word, Q_FUNC_INFO);
222 TM::WordDatabase::pointer TM::WordDatabase::create(Settings *settings, QString name)
224 return pointer(new WordDatabase(settings, name));
227 // SentenceDatabase -----------------------------------------------------------
229 TM::SentenceDatabase::SentenceDatabase(Settings *settings, int site_id, QString name)
233 settings->value(TMDATABASE_ROOT_PATH_KEY).toString()
234 + "/sentence-" + QString::number(site_id) + "-" + name.toLower() + ".db";
238 exec("CREATE TABLE IF NOT EXISTS sentences("
239 "sentence_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
244 "previous_crc INTEGER,"
250 exec("CREATE INDEX IF NOT EXISTS source_id_index ON sentences(source_id);", Q_FUNC_INFO);
251 exec("CREATE INDEX IF NOT EXISTS sentence_index ON sentences(sentence);", Q_FUNC_INFO);
254 m_find_sentence_id = prepare(
255 "SELECT sentence_id FROM sentences WHERE sentence=?;", Q_FUNC_INFO);
257 m_find_sentence_id_with_context = prepare(
258 "SELECT sentence_id FROM sentences "
260 "AND previous_crc=? "
261 "AND next_crc=?;", Q_FUNC_INFO);
263 m_find_sentence_by_crc = prepare("SELECT * FROM sentences WHERE crc=?;", Q_FUNC_INFO);
264 m_find_sentence_by_source_id = prepare("SELECT * FROM sentences WHERE source_id=?;", Q_FUNC_INFO);
265 m_insert_sentence = prepare(
266 "INSERT OR REPLACE INTO sentences("
267 "source_id, sentence, json, crc, previous_crc, next_crc, user_id, time) "
268 "VALUES(?, ?, ?, ?, ?, ?, ?, ?);", Q_FUNC_INFO);
269 m_update_sentence = prepare(
270 "UPDATE sentences SET "
279 "WHERE sentence_id=?;", Q_FUNC_INFO);
282 TM::SentenceDatabase::~SentenceDatabase()
284 if(m_database.isOpen()) m_database.close();
287 quint32 TM::SentenceDatabase::find_sentence_id(QString sentence)
289 m_find_sentence_id.bindValue(0, sentence);
290 exec(m_find_sentence_id, Q_FUNC_INFO);
292 if(!m_find_sentence_id.next()) return 0;
293 return m_find_sentence_id.value(0).toUInt();
296 quint32 TM::SentenceDatabase::find_sentence_id_with_context(
297 QString sentence, quint32 previous_crc, quint32 next_crc)
299 m_find_sentence_id_with_context.bindValue(0, sentence);
300 m_find_sentence_id_with_context.bindValue(1, previous_crc);
301 m_find_sentence_id_with_context.bindValue(2, next_crc);
302 exec(m_find_sentence_id_with_context, Q_FUNC_INFO);
304 if(!m_find_sentence_id_with_context.next()) return 0;
305 return m_find_sentence_id_with_context.value(0).toUInt();
308 TM::sentence_data_type::pointer
309 TM::SentenceDatabase::find_sentence_by_source_id(int source_id)
313 sentence_data_type::pointer result;
315 m_find_sentence_by_source_id.bindValue(0, source_id);
316 exec(m_find_sentence_by_source_id, Q_FUNC_INFO);
318 if(!m_find_sentence_by_source_id.next()) return result;
320 return stuff_value(&m_find_sentence_by_source_id);
323 void TM::SentenceDatabase::insert(sentence_data_type::pointer sentence_data)
325 assert(m_database.isValid());
326 assert(m_database.isOpen());
328 m_insert_sentence.bindValue(0, sentence_data->source_id);
329 m_insert_sentence.bindValue(1, sentence_data->sentence);
330 m_insert_sentence.bindValue(2, sentence_data->json);
331 m_insert_sentence.bindValue(3, sentence_data->crc);
332 m_insert_sentence.bindValue(4, sentence_data->previous_crc);
333 m_insert_sentence.bindValue(5, sentence_data->next_crc);
334 m_insert_sentence.bindValue(6, sentence_data->user_id);
335 m_insert_sentence.bindValue(7, sentence_data->time);
337 exec(m_insert_sentence, Q_FUNC_INFO);
340 void TM::SentenceDatabase::update(sentence_data_type::pointer sentence_data)
342 assert(m_database.isValid());
343 assert(m_database.isOpen());
345 m_update_sentence.bindValue(0, sentence_data->source_id);
346 m_update_sentence.bindValue(1, sentence_data->sentence);
347 m_update_sentence.bindValue(2, sentence_data->json);
348 m_update_sentence.bindValue(3, sentence_data->crc);
349 m_update_sentence.bindValue(4, sentence_data->previous_crc);
350 m_update_sentence.bindValue(5, sentence_data->next_crc);
351 m_update_sentence.bindValue(6, sentence_data->user_id);
352 m_update_sentence.bindValue(7, sentence_data->time);
353 m_update_sentence.bindValue(8, sentence_data->sentence_id);
355 exec(m_update_sentence, Q_FUNC_INFO);
358 TM::sentence_data_type::pointer
359 TM::SentenceDatabase::stuff_value(QSqlQuery *query)
361 assert(query->isValid());
362 sentence_data_type::pointer result = sentence_data_type::create();
364 result->sentence_id = query->value(0).toUInt();
365 result->source_id = query->value(1).toUInt();
366 result->sentence = query->value(2).toString();
367 result->json = query->value(3).toByteArray();
368 result->crc = query->value(4).toUInt();
369 result->previous_crc = query->value(5).toUInt();
370 result->next_crc = query->value(6).toUInt();
371 result->user_id = query->value(7).toUInt();
372 result->time = query->value(8).toUInt();
377 TM::SentenceDatabase::pointer TM::SentenceDatabase::create(
378 Settings *settings, int site_id, QString name)
380 return pointer(new SentenceDatabase(settings, site_id, name));
383 // Database -------------------------------------------------------------------
385 TM::Database::Database(Settings *settings, Service *service)
387 , m_settings(settings)
390 qRegisterMetaType<sentence_data_type::pointer>();
393 TM::Database::~Database()
398 * \brief データベースのセットアップを行います。
400 * データベース接続を開いたスレッド内でしか使えない制限のため、スレッドの
403 void TM::Database::setup()
406 m_sentence_cache_limit = m_settings->value(
407 "Database/sentence-cache-limit", QVariant::fromValue(20)).toInt();
409 // データベース用のフォルダが無ければ作成する。
410 if(!m_settings->contains(TMDATABASE_ROOT_PATH_KEY)) qFatal("An error occured while find settings in Database().");
411 QString path = m_settings->value(TMDATABASE_ROOT_PATH_KEY).toString();
413 if(!dir.exists()) dir.mkpath(path);
416 m_site_database = SiteDatabase::create(m_settings);
420 * \brief 引数で指定された言語の単語データベースを開きます。
424 * 言語プラグインを読み込んだ時に呼び出すことを想定しています。
425 * 言語プラグインは、TM::Serviceで読み込まれます。
427 void TM::Database::open_word_database(int code, QString name)
429 m_language_map.insert(code, name);
430 m_word_databases.insert(code, WordDatabase::create(m_settings, name));
433 quint32 TM::Database::find_site_id(QString host_name)
435 quint32 result = m_site_database->site_id(host_name);
439 void TM::Database::find_sentence(quint32 site_id, int scode, int tcode,
440 int segment_id, int index, sentence_data_type::pointer source,
441 SocketConnection::pointer socket)
443 sentence_data_type::pointer result;
445 SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
447 SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
450 quint32 source_id = sdb->find_sentence_id_with_context(
451 source->sentence, source->previous_crc, source->next_crc);
453 if(source_id) result = tdb->find_sentence_by_source_id(source_id);
455 QMetaObject::invokeMethod(
456 m_service, "sentence_found",
457 Qt::QueuedConnection,
458 Q_ARG(qint32, segment_id),
459 Q_ARG(qint32, index),
460 Q_ARG(sentence_data_type::pointer, result),
461 Q_ARG(SocketConnection::pointer, socket));
465 void TM::Database::insert_sentence(quint32 site_id,
466 quint32 scode, sentence_data_type::pointer source,
467 quint32 tcode, sentence_data_type::pointer target)
473 assert(source->source_id == 0);
474 assert(source->json.isEmpty());
475 assert(target->previous_crc == 0);
476 assert(target->next_crc == 0);
478 SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
480 SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
483 quint32 source_id = source->sentence_id;
484 if(!source_id) source_id = sdb->find_sentence_id_with_context(
485 source->sentence, source->previous_crc, source->next_crc);
488 source->time = QDateTime::currentDateTime().toMSecsSinceEpoch();
490 source_id = sdb->find_sentence_id_with_context(
491 source->sentence, source->previous_crc, source->next_crc);
495 target->source_id = source_id;
496 target->time = QDateTime::currentDateTime().toMSecsSinceEpoch();
498 sentence_data_type::pointer target_sentence =
499 tdb->find_sentence_by_source_id(source_id);
502 target->sentence_id = target_sentence->sentence_id;
505 else tdb->insert(target);
508 QString TM::Database::find_language_name(int code) const
511 QMap<int, QString>::const_iterator it = m_language_map.find(code);
512 if(it != m_language_map.end()) result = it.value();
517 * \brief 引数として与えられた単語に対して一意な番号を返します。
521 int TM::Database::find_word_id(int code, QString word)
523 QMap<int, WordDatabase::pointer>::iterator it = m_word_databases.find(code);
524 assert(it != m_word_databases.end());
525 int result = (*it)->word_id(word);
531 * \brief 引数として与えられたサイトと言語用のセンテンス・データベースを返します。
532 * \param site_id サイトを識別する番号。
535 * センテンス・データベースは、設定によって決められた数まで接続をキャッシュします。
536 * このメンバは、キャッシュ内にデータベース接続があればそれを返し、無ければ接続します。
538 TM::SentenceDatabase::pointer
539 TM::Database::find_sentence_database(quint32 site_id, int code)
541 typedef QMap<QPair<quint32, int>, SentenceDatabase::pointer>::iterator iterator;
545 // キャッシュが大きすぎる場合、消去する。
546 if(m_sentence_cache_limit < m_sentence_databases.size()) m_sentence_databases.clear();
548 // キャッシュにデータベースが無い場合、作成する。
549 QPair<quint32, int> key(site_id, code);
550 iterator it = m_sentence_databases.find(key);
551 if(it == m_sentence_databases.end())
553 QString name = find_language_name(code);
554 assert(!name.isEmpty());
555 it = m_sentence_databases.insert(
556 key, SentenceDatabase::create(m_settings, site_id, name));