Ticket #5946: mythtv-5946-fix_database_utf8_conversion_corruption.patch

File mythtv-5946-fix_database_utf8_conversion_corruption.patch, 8.0 KB (added by sphery <mtdean@…>, 15 years ago)

Fixes corruption caused by original implementation of UTF-8 conversion

  • libs/libmythtv/dbcheck.cpp

    old new  
    1818#define MINIMUM_DBMS_VERSION 5,0,15
    1919
    2020/// This is the DB schema version expected by the running MythTV instance.
    21 const QString currentDatabaseVersion = "1224";
     21const QString currentDatabaseVersion = "1225";
    2222
    2323static bool UpdateDBVersionNumber(const QString &newnumber);
    2424static bool performActualUpdate(
     
    43124312            return false;
    43134313    }
    43144314
     4315    if (dbver == "1224")
     4316    {
     4317        // Fix data corruption caused by a data conversion issue in the
     4318        // original implementation of the 1215-1216 database upgrade
     4319        MSqlQuery peopleQuery(MSqlQuery::InitCon());
     4320        peopleQuery.prepare("SELECT person FROM people "
     4321                            " WHERE LENGTH(name) = 128;");
     4322        if (peopleQuery.exec() && peopleQuery.isActive() &&
     4323            peopleQuery.size() > 0)
     4324        {
     4325            MSqlQuery duplicateQuery(MSqlQuery::InitCon());
     4326            duplicateQuery.prepare("SELECT person "
     4327                                   "  FROM people "
     4328                                   " WHERE name = REPLACE( "
     4329                                   "               (SELECT name FROM people "
     4330                                   "                 WHERE person = :ID), "
     4331                                   "               '\\0', '');");
     4332            // Rather than build an updateQuery with the table name inserted in
     4333            // the QString used to build the SQL and reusing it for credits and
     4334            // recordedcredits, use 2 prepared queries since we'll be executing
     4335            // the updates thousands (or tens of thousands) of times each
     4336            MSqlQuery updateCreditsQuery(MSqlQuery::InitCon());
     4337            updateCreditsQuery.prepare("UPDATE credits "
     4338                                       "   SET person = :ID "
     4339                                       " WHERE person = :DUPLICATEID;");
     4340            MSqlQuery updateRecordedCreditsQuery(MSqlQuery::InitCon());
     4341            updateRecordedCreditsQuery.prepare("UPDATE recordedcredits "
     4342                                               "   SET person = :ID "
     4343                                               " WHERE person = :DUPLICATEID;");
     4344            MSqlQuery deleteQuery(MSqlQuery::InitCon());
     4345            deleteQuery.prepare("DELETE FROM people "
     4346                                " WHERE person = :ID;");
     4347            MSqlQuery updatePeopleQuery(MSqlQuery::InitCon());
     4348            updatePeopleQuery.prepare("UPDATE people "
     4349                                      "   SET name = REPLACE(name, '\\0', '') "
     4350                                      " WHERE person = :ID;");
     4351            VERBOSE(VB_IMPORTANT, "Fixing corrupt data in the people table. "
     4352                                  "This may take a while. Please do not stop "
     4353                                  "the program until it has finished.");
     4354            while (peopleQuery.next())
     4355            {
     4356                int person = peopleQuery.value(0).toInt();
     4357                duplicateQuery.bindValue(":ID", person);
     4358                if (duplicateQuery.exec() && duplicateQuery.isActive() &&
     4359                    duplicateQuery.size() > 0)
     4360                {
     4361                    // Duplicate record, replace references and remove dup
     4362                    while (duplicateQuery.next())
     4363                    {
     4364                        int duplicatePerson = duplicateQuery.value(0).toInt();
     4365                        bool success = true;
     4366                        updateCreditsQuery.bindValue(":ID", person);
     4367                        updateCreditsQuery.bindValue(":DUPLICATEID",
     4368                                                     duplicatePerson);
     4369                        if (!updateCreditsQuery.exec())
     4370                        {
     4371                            success = false;
     4372                            MythDB::DBError(QString("Updating references to "
     4373                                                    "duplicate person record "
     4374                                                    "in credits: %1 -> %2")
     4375                                                    .arg(duplicatePerson)
     4376                                                    .arg(person),
     4377                                            updateCreditsQuery);
     4378                        }
     4379                        updateRecordedCreditsQuery.bindValue(":ID", person);
     4380                        updateRecordedCreditsQuery.bindValue(":DUPLICATEID",
     4381                                                             duplicatePerson);
     4382                        if (!updateRecordedCreditsQuery.exec())
     4383                        {
     4384                            success = false;
     4385                            MythDB::DBError(QString("Updating references to "
     4386                                                    "duplicate person record "
     4387                                                    "in recordedcredits: "
     4388                                                    "%1 -> %2")
     4389                                                    .arg(duplicatePerson)
     4390                                                    .arg(person),
     4391                                            updateRecordedCreditsQuery);
     4392                        }
     4393                        if (success)
     4394                        {
     4395                            // We need to keep the original person and fix the
     4396                            // corrupt name since we may have more than one
     4397                            // duplicate in the wild (with varying numbers
     4398                            // of null-pad characters) if users have edited
     4399                            // the database directly
     4400                            deleteQuery.bindValue(":ID", duplicatePerson);
     4401                            if (!deleteQuery.exec())
     4402                            {
     4403                                MythDB::DBError(
     4404                                    QString("Deleting duplicate person "
     4405                                            "record: %1").arg(duplicatePerson),
     4406                                            deleteQuery);
     4407                            }
     4408                            // This cannot be done outside the duplicate-check
     4409                            // conditional, even though it's done in the if and
     4410                            // the else because it's only safe to do if success
     4411                            updatePeopleQuery.bindValue(":ID", person);
     4412                            if (!updatePeopleQuery.exec())
     4413                            {
     4414                                MythDB::DBError(
     4415                                    QString("Fixing corrupt people.name "
     4416                                            "for person: %1)").arg(person),
     4417                                    updatePeopleQuery);
     4418                            }
     4419                        }
     4420                    }
     4421                }
     4422                else
     4423                {
     4424                    // Not duplicated, corrupt record, fix name
     4425                    updatePeopleQuery.bindValue(":ID", person);
     4426                    if (!updatePeopleQuery.exec())
     4427                    {
     4428                        MythDB::DBError(QString("Fixing corrupt people.name "
     4429                                                "for person: %1)").arg(person),
     4430                                                updatePeopleQuery);
     4431                    }
     4432                }
     4433            }
     4434            VERBOSE(VB_IMPORTANT, "Finished fixing corrupt data in the people "
     4435                                  "table.");
     4436        }
     4437
     4438        const char *updates[] = {
     4439"UPDATE programgenres SET genre = REPLACE(genre, '\\0', '');",
     4440"UPDATE programrating SET system = REPLACE(system, '\\0', ''), "
     4441"                         rating = REPLACE(rating, '\\0', '');",
     4442"UPDATE recordedrating SET system = REPLACE(system, '\\0', ''), "
     4443"                          rating = REPLACE(rating, '\\0', '');",
     4444NULL
     4445};
     4446        if (!performActualUpdate(updates, "1225", dbver))
     4447            return false;
     4448    }
     4449
    43154450    return true;
    43164451}
    43174452