~ubuntu-branches/ubuntu/saucy/clementine/saucy

« back to all changes in this revision

Viewing changes to src/core/database.cpp

  • Committer: Package Import Robot
  • Author(s): Thomas PIERSON
  • Date: 2012-01-01 20:43:39 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120101204339-lsb6nndwhfy05sde
Tags: 1.0.1+dfsg-1
New upstream release. (Closes: #653926, #651611, #657391)

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
#include "database.h"
20
20
#include "scopedtransaction.h"
21
21
#include "utilities.h"
 
22
#include "core/logging.h"
22
23
 
23
24
#include <QCoreApplication>
24
25
#include <QDir>
28
29
#include <QSqlQuery>
29
30
#include <QtDebug>
30
31
#include <QThread>
 
32
#include <QUrl>
31
33
#include <QVariant>
32
34
 
33
35
const char* Database::kDatabaseFilename = "clementine.db";
34
 
const int Database::kSchemaVersion = 28;
 
36
const int Database::kSchemaVersion = 36;
35
37
const char* Database::kMagicAllSongsTables = "%allsongstables";
36
38
 
37
39
int Database::sNextConnectionId = 1;
237
239
 
238
240
  QLibrary library(plugin_path);
239
241
  if (!library.load()) {
240
 
    qDebug() << "QLibrary::load() failed for " << plugin_path;
 
242
    qLog(Error) << "QLibrary::load() failed for " << plugin_path;
241
243
    return;
242
244
  }
243
245
 
260
262
      !_sqlite3_value_text ||
261
263
      !_sqlite3_result_int64 ||
262
264
      !_sqlite3_user_data) {
263
 
    qDebug() << "Couldn't resolve sqlite symbols";
 
265
    qLog(Error) << "Couldn't resolve sqlite symbols";
264
266
    sLoadedSqliteSymbols = false;
265
267
  } else {
266
268
    sLoadedSqliteSymbols = true;
384
386
  set_fts_tokenizer.bindValue(":pointer", QByteArray(
385
387
      reinterpret_cast<const char*>(&sFTSTokenizer), sizeof(&sFTSTokenizer)));
386
388
  if (!set_fts_tokenizer.exec()) {
387
 
    qWarning() << "Couldn't register FTS3 tokenizer";
 
389
    qLog(Warning) << "Couldn't register FTS3 tokenizer";
388
390
  }
389
391
 
390
392
  // We want Unicode aware LIKE clauses and FTS if possible.
438
440
                        " WHERE type='table'").arg(key), db);
439
441
    if (!q.exec() || !q.next()) {
440
442
      ScopedTransaction t(&db);
441
 
      ExecFromFile(attached_databases_[key].schema_, db);
 
443
      ExecFromFile(attached_databases_[key].schema_, db, 0);
442
444
      t.Commit();
443
445
    }
444
446
  }
456
458
  startup_schema_version_ = schema_version;
457
459
 
458
460
  if (schema_version > kSchemaVersion) {
459
 
    qWarning() << "The database schema (version" << schema_version << ") is newer than I was expecting";
 
461
    qLog(Warning) << "The database schema (version" << schema_version << ") is newer than I was expecting";
460
462
    return;
461
463
  }
462
464
  if (schema_version < kSchemaVersion) {
469
471
 
470
472
void Database::RecreateAttachedDb(const QString& database_name) {
471
473
  if (!attached_databases_.contains(database_name)) {
472
 
    qWarning() << "Attached database does not exist:" << database_name;
 
474
    qLog(Warning) << "Attached database does not exist:" << database_name;
473
475
    return;
474
476
  }
475
477
 
482
484
    QSqlQuery q("DETACH DATABASE :alias", db);
483
485
    q.bindValue(":alias", database_name);
484
486
    if (!q.exec()) {
485
 
      qWarning() << "Failed to detach database" << database_name;
 
487
      qLog(Warning) << "Failed to detach database" << database_name;
486
488
      return;
487
489
    }
488
490
 
489
491
    if (!QFile::remove(filename)) {
490
 
      qWarning() << "Failed to remove file" << filename;
 
492
      qLog(Warning) << "Failed to remove file" << filename;
491
493
    }
492
494
  }
493
495
 
505
507
    filename = ":/schema/schema.sql";
506
508
  else
507
509
    filename = QString(":/schema/schema-%1.sql").arg(version);
508
 
 
 
510
  
509
511
  ScopedTransaction t(&db);
510
 
  ExecFromFile(filename, db);
 
512
  
 
513
  if (version == 31) {
 
514
    // This version used to do a bad job of converting filenames in the songs
 
515
    // table to file:// URLs.  Now we do it properly here instead.
 
516
    
 
517
    UrlEncodeFilenameColumn("songs", db);
 
518
    UrlEncodeFilenameColumn("playlist_items", db);
 
519
 
 
520
    foreach (const QString& table, db.tables()) {
 
521
      if (table.startsWith("device_") && table.endsWith("_songs")) {
 
522
        UrlEncodeFilenameColumn(table, db);
 
523
      }
 
524
    }
 
525
  }
 
526
  
 
527
  ExecFromFile(filename, db, version - 1);
511
528
  t.Commit();
512
529
}
513
530
 
514
 
void Database::ExecFromFile(const QString &filename, QSqlDatabase &db) {
 
531
void Database::UrlEncodeFilenameColumn(const QString& table, QSqlDatabase& db) {
 
532
  QSqlQuery select(QString("SELECT ROWID, filename FROM %1").arg(table), db);
 
533
  QSqlQuery update(QString("UPDATE %1 SET filename=:filename WHERE ROWID=:id").arg(table), db);
 
534
  
 
535
  select.exec();
 
536
  if (CheckErrors(select)) return;
 
537
  while (select.next()) {
 
538
    const int rowid = select.value(0).toInt();
 
539
    const QString filename = select.value(1).toString();
 
540
    
 
541
    if (filename.isEmpty() || filename.contains("://")) {
 
542
      continue;
 
543
    }
 
544
    
 
545
    const QUrl url = QUrl::fromLocalFile(filename);
 
546
    
 
547
    update.bindValue(":filename", url.toEncoded());
 
548
    update.bindValue(":id", rowid);
 
549
    update.exec();
 
550
    CheckErrors(update);
 
551
  }
 
552
}
 
553
 
 
554
void Database::ExecFromFile(const QString &filename, QSqlDatabase &db,
 
555
                            int schema_version) {
515
556
  // Open and read the database schema
516
557
  QFile schema_file(filename);
517
558
  if (!schema_file.open(QIODevice::ReadOnly))
518
559
    qFatal("Couldn't open schema file %s", filename.toUtf8().constData());
519
 
  ExecCommands(QString::fromUtf8(schema_file.readAll()), db);
 
560
  ExecCommands(QString::fromUtf8(schema_file.readAll()), db, schema_version);
520
561
}
521
562
 
522
 
void Database::ExecCommands(const QString &schema, QSqlDatabase &db) {
 
563
void Database::ExecCommands(const QString& schema, QSqlDatabase& db,
 
564
                            int schema_version) {
523
565
  // Run each command
524
566
  QStringList commands(schema.split(";\n\n"));
525
567
 
526
568
  // We don't want this list to reflect possible DB schema changes
527
569
  // so we initialize it before executing any statements.
528
 
  QStringList tables = SongsTables(db);
 
570
  QStringList tables = SongsTables(db, schema_version);
529
571
 
530
572
  foreach (const QString& command, commands) {
531
573
    // There are now lots of "songs" tables that need to have the same schema:
533
575
    // in the schema files to update all songs tables at once.
534
576
    if (command.contains(kMagicAllSongsTables)) {
535
577
      foreach (const QString& table, tables) {
536
 
        qDebug() << "Updating" << table << "for" << kMagicAllSongsTables;
 
578
        qLog(Info) << "Updating" << table << "for" << kMagicAllSongsTables;
537
579
        QString new_command(command);
538
580
        new_command.replace(kMagicAllSongsTables, table);
539
581
        QSqlQuery query(db.exec(new_command));
548
590
  }
549
591
}
550
592
 
551
 
QStringList Database::SongsTables(QSqlDatabase& db) const {
 
593
QStringList Database::SongsTables(QSqlDatabase& db, int schema_version) const {
552
594
  QStringList ret;
553
595
 
554
596
  // look for the tables in the main db
569
611
    }
570
612
  }
571
613
 
 
614
  if (schema_version > 29) {
 
615
    // The playlist_items table became a songs table in version 29.
 
616
    ret << "playlist_items";
 
617
  }
 
618
 
572
619
  return ret;
573
620
}
574
621
 
575
622
bool Database::CheckErrors(const QSqlQuery& query) {
576
623
  QSqlError last_error = query.lastError();
577
624
  if (last_error.isValid()) {
578
 
    qDebug() << "db error: " << last_error;
579
 
    qDebug() << "faulty query: " << query.lastQuery();
580
 
    qDebug() << "bound values: " << query.boundValues();
 
625
    qLog(Error) << "db error: " << last_error;
 
626
    qLog(Error) << "faulty query: " << query.lastQuery();
 
627
    qLog(Error) << "bound values: " << query.boundValues();
581
628
 
582
629
    emit Error("LibraryBackend: " + last_error.text());
583
630
    return true;