Skip to content

Commit

Permalink
Fix update of older MythTV databases (#844)
Browse files Browse the repository at this point in the history
* Add method CheckTableColumnExists which checks for existence of a column in a database table.

* Execute some blocks of SQL earlier in the migration process to allow older database versions to migrate properly.

* Changes per PR #844 code review.

* Add requested comments.

(cherry picked from commit 512d723)
  • Loading branch information
PatrickJMaloney authored and kmdewaal committed Jan 13, 2024
1 parent bc973e3 commit 634c871
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 19 deletions.
48 changes: 48 additions & 0 deletions mythtv/libs/libmythbase/dbutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,4 +877,52 @@ bool DBUtil::CheckTimeZoneSupport(void)
return !query.value(0).isNull();
}

/** \fn DBUtil::CheckTableColumnExists(const QString &tableName, const QString &columnName)
* \brief Checks for the presence of a column in a table in the current database
*
* This function will check a table in the current database for the presence
* of a named column.
*
* \param tableName Name of table to check
* \param columnName Name of column to look for
* \return true if column exists in the table; false if it does not
* \sa CheckTableColumnExists(const QString &tableName, const QString &columnName)
*/
bool DBUtil::CheckTableColumnExists(const QString &tableName, const QString &columnName)
{
MSqlQuery query(MSqlQuery::InitCon());
if (!query.isConnected())
return false;

QString sql = QString("SELECT COUNT(*) FROM information_schema.columns "
"WHERE table_schema = DATABASE() AND "
"table_name = '%1' AND column_name = '%2';")
.arg(tableName, columnName);
LOG(VB_GENERAL, LOG_DEBUG,
QString("DBUtil::CheckTableColumnExists() SQL: %1").arg(sql));

if (!query.exec(sql))
{
MythDB::DBError("DBUtil Check Table Column Exists", query);
return false;
}

bool result = false;
if (query.next())
{
result = (query.value(0).toInt() > 0);
}
else
{
LOG(VB_GENERAL, LOG_ERR,
QString("DBUtil::CheckTableColumnExists() - Empty result set"));
}

LOG(VB_GENERAL, LOG_DEBUG,
QString("DBUtil::CheckTableColumnExists('%1', '%2') result: %3").arg(tableName,
columnName, QVariant(result).toString()));

return result;
}

/* vim: set expandtab tabstop=4 shiftwidth=4: */
1 change: 1 addition & 0 deletions mythtv/libs/libmythbase/dbutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class MBASE_PUBLIC DBUtil
static void UnlockSchema(MSqlQuery &query);

static bool CheckTimeZoneSupport(void);
static bool CheckTableColumnExists(const QString &tableName, const QString &columnName);

static const int kUnknownVersionNumber;

Expand Down
135 changes: 116 additions & 19 deletions mythtv/libs/libmythtv/dbcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,66 @@ static bool doUpgradeTVDatabaseSchema(void)
if (dbver == "1301")
{
LOG(VB_GENERAL, LOG_CRIT, "Upgrading to MythTV schema version 1302");

// Adding the default template now requires database table recgroups which was
// initially introduced in the upgrade to schema version 1320.
// The initial database created with a "clean install" has schema version 1307
// so upgrading to schema version 1302 is not often done.
DBUpdates updates {
"CREATE TABLE IF NOT EXISTS recgroups ("
"recgroupid SMALLINT(4) NOT NULL AUTO_INCREMENT, "
"recgroup VARCHAR(64) NOT NULL DEFAULT '', "
"displayname VARCHAR(64) NOT NULL DEFAULT '', "
"password VARCHAR(40) NOT NULL DEFAULT '', "
"special TINYINT(1) NOT NULL DEFAULT '0',"
"PRIMARY KEY (recgroupid), "
"UNIQUE KEY recgroup ( recgroup )"
") ENGINE=MyISAM DEFAULT CHARSET=utf8;",

// Create the built-in, 'special', groups
"INSERT IGNORE INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 1, 'Default', '1' );",
"INSERT IGNORE INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 2, 'LiveTV', '1' );",
"INSERT IGNORE INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 3, 'Deleted', '1' );",
// Copy in the passwords for the built-in groups
"DELETE FROM recgrouppassword WHERE password = '';",
"UPDATE recgroups r, recgrouppassword p SET r.password = p.password WHERE r.recgroup = p.recgroup;",
// Copy over all existing recording groups, this information may be split over three tables!
"INSERT IGNORE INTO recgroups ( recgroup, displayname, password ) SELECT DISTINCT recgroup, recgroup, password FROM recgrouppassword;",
"INSERT IGNORE INTO recgroups ( recgroup, displayname ) SELECT DISTINCT recgroup, recgroup FROM record;",
"INSERT IGNORE INTO recgroups ( recgroup, displayname ) SELECT DISTINCT recgroup, recgroup FROM recorded;",

// Create recgroupid columns in record and recorded tables
(!DBUtil::CheckTableColumnExists(QString("record"), QString("recgroupid")) ?
"ALTER TABLE record ADD COLUMN recgroupid SMALLINT(4) NOT NULL DEFAULT '1', ADD INDEX ( recgroupid );" : ""),
(!DBUtil::CheckTableColumnExists(QString("recorded"), QString("recgroupid")) ?
"ALTER TABLE recorded ADD COLUMN recgroupid SMALLINT(4) NOT NULL DEFAULT '1', ADD INDEX ( recgroupid );" : ""),

// Create autoextend column in record
(!DBUtil::CheckTableColumnExists(QString("record"), QString("autoextend")) ?
"ALTER TABLE record ADD COLUMN autoextend TINYINT UNSIGNED DEFAULT 0;" : ""),

"ALTER TABLE record MODIFY COLUMN startdate DATE DEFAULT NULL",
"ALTER TABLE record MODIFY COLUMN enddate DATE DEFAULT NULL",
"ALTER TABLE record MODIFY COLUMN starttime TIME DEFAULT NULL",
"ALTER TABLE record MODIFY COLUMN endtime TIME DEFAULT NULL",

// All next_record, last_record and last_delete to be NULL.
"ALTER TABLE record MODIFY next_record DATETIME NULL",
// NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
"UPDATE record SET next_record = NULL "
" WHERE next_record = '0000-00-00 00:00:00'",
"ALTER TABLE record MODIFY last_record DATETIME NULL",
"UPDATE record SET last_record = NULL "
" WHERE last_record = '0000-00-00 00:00:00'",
"ALTER TABLE record MODIFY last_delete DATETIME NULL",
"UPDATE record SET last_delete = NULL "
" WHERE last_delete = '0000-00-00 00:00:00'",
};

// We don't want to update version yet
if (!performUpdateSeries("MythTV", updates))
return false;

// Create the Default recording rule template
RecordingRule record;
record.MakeTemplate("Default");
Expand Down Expand Up @@ -2000,8 +2060,10 @@ static bool doUpgradeTVDatabaseSchema(void)
gCoreContext->GetBoolSetting("AutoRunUserJob4", false);
record.m_autoMetadataLookup =
gCoreContext->GetBoolSetting("AutoMetadataLookup", true);
record.Save(false);
if (!record.Save(false))
return false;

// Both updates worked, so update version
if (!UpdateDBVersionNumber("MythTV", "DBSchemaVer", "1302", dbver))
return false;
}
Expand Down Expand Up @@ -2508,9 +2570,9 @@ static bool doUpgradeTVDatabaseSchema(void)
"UNIQUE KEY recgroup ( recgroup )"
") ENGINE=MyISAM DEFAULT CHARSET=utf8;",
// Create the built-in, 'special', groups
"INSERT INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 1, 'Default', '1' );",
"INSERT INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 2, 'LiveTV', '1' );",
"INSERT INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 3, 'Deleted', '1' );",
"INSERT IGNORE INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 1, 'Default', '1' );",
"INSERT IGNORE INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 2, 'LiveTV', '1' );",
"INSERT IGNORE INTO recgroups ( recgroupid, recgroup, special ) VALUES ( 3, 'Deleted', '1' );",
// Copy in the passwords for the built-in groups
"DELETE FROM recgrouppassword WHERE password = '';",
"UPDATE recgroups r, recgrouppassword p SET r.password = p.password WHERE r.recgroup = p.recgroup;",
Expand All @@ -2519,8 +2581,10 @@ static bool doUpgradeTVDatabaseSchema(void)
"INSERT IGNORE INTO recgroups ( recgroup, displayname ) SELECT DISTINCT recgroup, recgroup FROM record;",
"INSERT IGNORE INTO recgroups ( recgroup, displayname ) SELECT DISTINCT recgroup, recgroup FROM recorded;",
// Create recgroupid columns in record and recorded tables
"ALTER TABLE record ADD COLUMN recgroupid SMALLINT(4) NOT NULL DEFAULT '1', ADD INDEX ( recgroupid );",
"ALTER TABLE recorded ADD COLUMN recgroupid SMALLINT(4) NOT NULL DEFAULT '1', ADD INDEX ( recgroupid );",
(!DBUtil::CheckTableColumnExists(QString("record"), QString("recgroupid")) ?
"ALTER TABLE record ADD COLUMN recgroupid SMALLINT(4) NOT NULL DEFAULT '1', ADD INDEX ( recgroupid );" : ""),
(!DBUtil::CheckTableColumnExists(QString("recorded"), QString("recgroupid")) ?
"ALTER TABLE recorded ADD COLUMN recgroupid SMALLINT(4) NOT NULL DEFAULT '1', ADD INDEX ( recgroupid );" : ""),
// Populate those columns with the corresponding recgroupid from the new recgroups table
"UPDATE recorded, recgroups SET recorded.recgroupid = recgroups.recgroupid WHERE recorded.recgroup = recgroups.recgroup;",
"UPDATE record, recgroups SET record.recgroupid = recgroups.recgroupid WHERE record.recgroup = recgroups.recgroup;"
Expand Down Expand Up @@ -2693,6 +2757,20 @@ static bool doUpgradeTVDatabaseSchema(void)

if (dbver == "1330")
{
// Adding field lastplay because it is now required in the upgrade to database schema 1331
// but only when the are recordings present in the database.
// Field lastplay is initially introduced in the upgrade to database schema 1373.
if (!DBUtil::CheckTableColumnExists(QString("recorded"), QString("lastplay")))
{
DBUpdates updates {
"ALTER TABLE recorded ADD COLUMN lastplay TINYINT UNSIGNED DEFAULT 0 AFTER bookmark;",
};

// We don't want to update version yet
if (!performUpdateSeries("MythTV", updates))
return false;
}

MSqlQuery query(MSqlQuery::InitCon());
query.prepare("SELECT recordedid FROM recorded");
query.exec();
Expand Down Expand Up @@ -2726,10 +2804,13 @@ static bool doUpgradeTVDatabaseSchema(void)
recInfo->QueryAverageHeight());
recFile->m_videoResolution = resolution;
recFile->m_videoFrameRate = (double)recInfo->QueryAverageFrameRate() / 1000.0;
recFile->Save();
bool result = recFile->Save();
delete recInfo;
if (!result)
return false;
}

// Both updates worked, so update version
if (!UpdateDBVersionNumber("MythTV", "DBSchemaVer", "1331", dbver))
return false;
}
Expand Down Expand Up @@ -3864,22 +3945,38 @@ static bool doUpgradeTVDatabaseSchema(void)
{
// Recording extender tables are now created later.

// If that worked, modify existing tables.
DBUpdates updates = getRecordingExtenderDbInfo(0);
if (!performActualUpdate("MythTV", "DBSchemaVer",
updates, "1372", dbver))
return false;
if (!DBUtil::CheckTableColumnExists(QString("record"), QString("autoextend")))
{
// If that worked, modify existing tables.
DBUpdates updates = getRecordingExtenderDbInfo(0);
if (!performActualUpdate("MythTV", "DBSchemaVer",
updates, "1372", dbver))
return false;
}
else
{
if (!UpdateDBVersionNumber("MythTV", "DBSchemaVer", "1372", dbver))
return false;
}
}

if (dbver == "1372")
{
DBUpdates updates {
"ALTER TABLE recorded ADD COLUMN lastplay "
" TINYINT UNSIGNED DEFAULT 0 AFTER bookmark;",
};
if (!performActualUpdate("MythTV", "DBSchemaVer",
updates, "1373", dbver))
return false;
if (!DBUtil::CheckTableColumnExists(QString("recorded"), QString("lastplay")))
{
DBUpdates updates {
"ALTER TABLE recorded ADD COLUMN lastplay "
" TINYINT UNSIGNED DEFAULT 0 AFTER bookmark;",
};
if (!performActualUpdate("MythTV", "DBSchemaVer",
updates, "1373", dbver))
return false;
}
else
{
if (!UpdateDBVersionNumber("MythTV", "DBSchemaVer", "1373", dbver))
return false;
}
}

if (dbver == "1373")
Expand Down

0 comments on commit 634c871

Please sign in to comment.