00001 #include <iostream>
00002 #include <unistd.h>
00003
00004 #include "dialogbox.h"
00005 #include "mythcorecontext.h"
00006 #include "schemawizard.h"
00007 #include "mythmiscutil.h"
00008
00009 #include "mythtimer.h"
00010 #include "mythlogging.h"
00011 #include "mythmainwindow.h"
00012 #include "mythuihelper.h"
00013 #include "mythdb.h"
00014
00015
00016 static SchemaUpgradeWizard * c_wizard = 0;
00017
00018
00019 SchemaUpgradeWizard::SchemaUpgradeWizard(const QString &DBSchemaSetting,
00020 const QString &appName,
00021 const QString &upgradeSchemaVal)
00022 : DBver(), emptyDB(false), versionsBehind(-1),
00023 backupStatus(kDB_Backup_Unknown),
00024 m_autoUpgrade(false),
00025 m_backupResult(),
00026 m_busyPopup(NULL),
00027 m_expertMode(false),
00028 m_schemaSetting(DBSchemaSetting),
00029 m_schemaName(appName),
00030 m_newSchemaVer(upgradeSchemaVal)
00031 {
00032 c_wizard = this;
00033
00034
00035
00036
00037 switch (gCoreContext->GetNumSetting("DBSchemaAutoUpgrade"))
00038 {
00039 case 1: m_autoUpgrade = true; break;
00040 #if ENABLE_SCHEMA_DEVELOPER_MODE
00041 case -1: m_expertMode = true; break;
00042 #endif
00043 default: break;
00044 }
00045 }
00046
00047 SchemaUpgradeWizard::~SchemaUpgradeWizard()
00048 {
00049 c_wizard = 0;
00050 }
00051
00052 SchemaUpgradeWizard *
00053 SchemaUpgradeWizard::Get(const QString &DBSchemaSetting,
00054 const QString &appName,
00055 const QString &upgradeSchemaVal)
00056 {
00057 if (c_wizard == 0)
00058 c_wizard = new SchemaUpgradeWizard(DBSchemaSetting, appName,
00059 upgradeSchemaVal);
00060 else
00061 {
00062 c_wizard->DBver = QString();
00063 c_wizard->versionsBehind = -1;
00064 c_wizard->m_schemaSetting = DBSchemaSetting;
00065 c_wizard->m_schemaName = appName;
00066 c_wizard->m_newSchemaVer = upgradeSchemaVal;
00067 }
00068
00069 return c_wizard;
00070 }
00071
00077 void SchemaUpgradeWizard::BusyPopup(const QString &message)
00078 {
00079 if (m_busyPopup)
00080 m_busyPopup->Close();
00081
00082 m_busyPopup = ShowBusyPopup(message);
00083 }
00084
00085 MythDBBackupStatus SchemaUpgradeWizard::BackupDB(void)
00086 {
00087 if (emptyDB)
00088 {
00089 LOG(VB_GENERAL, LOG_INFO,
00090 "The database seems to be empty - not attempting a backup");
00091 return kDB_Backup_Empty_DB;
00092 }
00093
00094 backupStatus = DBUtil::BackupDB(m_backupResult, true);
00095
00096 return backupStatus;
00097 }
00098
00099 int SchemaUpgradeWizard::Compare(void)
00100 {
00101 DBver = gCoreContext->GetSetting(m_schemaSetting);
00102
00103
00104 if (DBver.isEmpty() || DBver == "0")
00105 {
00106 LOG(VB_GENERAL, LOG_INFO, "No current database version?");
00107
00108 if (DBUtil::IsNewDatabase())
00109 {
00110 LOG(VB_GENERAL, LOG_INFO, "Database appears to be empty/new!");
00111 emptyDB = true;
00112 }
00113 }
00114 else
00115 LOG(VB_GENERAL, LOG_INFO,
00116 QString("Current %1 Schema Version (%2): %3")
00117 .arg(m_schemaName).arg(m_schemaSetting).arg(DBver));
00118
00119 #if TESTING
00120
00121 DBver += "-testing";
00122 return 0;
00123 #endif
00124
00125 if (m_newSchemaVer == DBver)
00126 {
00127 versionsBehind = 0;
00128 }
00129 else
00130 {
00131
00132 bool new_ok, old_ok;
00133 int new_version = m_newSchemaVer.toInt(&new_ok);
00134 int old_version = DBver.toInt(&old_ok);
00135 if (new_ok && old_ok)
00136 versionsBehind = new_version - old_version;
00137 else
00138 versionsBehind = 5000;
00139 }
00140 return versionsBehind;
00141 }
00142
00143 MythSchemaUpgrade SchemaUpgradeWizard::GuiPrompt(const QString &message,
00144 bool upgradable, bool expert)
00145 {
00146 DialogBox * dlg;
00147 MythMainWindow * win = GetMythMainWindow();
00148
00149 if (!win)
00150 return MYTH_SCHEMA_ERROR;
00151
00152 dlg = new DialogBox(win, message);
00153 dlg->AddButton(tr("Exit"));
00154 if (upgradable)
00155 dlg->AddButton(tr("Upgrade"));
00156 if (expert)
00157
00158 dlg->AddButton("Use current schema");
00159
00160 DialogCode selected = dlg->exec();
00161 dlg->deleteLater();
00162
00163 switch (selected)
00164 {
00165 case kDialogCodeRejected:
00166 case kDialogCodeButton0:
00167 return MYTH_SCHEMA_EXIT;
00168 case kDialogCodeButton1:
00169 return upgradable ? MYTH_SCHEMA_UPGRADE: MYTH_SCHEMA_USE_EXISTING;
00170 case kDialogCodeButton2:
00171 return MYTH_SCHEMA_USE_EXISTING;
00172 default:
00173 return MYTH_SCHEMA_ERROR;
00174 }
00175 }
00176
00196 enum MythSchemaUpgrade
00197 SchemaUpgradeWizard::PromptForUpgrade(const char *name,
00198 const bool upgradeAllowed,
00199 const bool upgradeIfNoUI,
00200 const int minDBMSmajor,
00201 const int minDBMSminor,
00202 const int minDBMSpoint)
00203 {
00204 bool connections;
00205 bool gui;
00206 bool upgradable;
00207 bool validDBMS;
00208 QString warnOldDBMS;
00209 QString warnOtherCl;
00210
00211
00212
00213 if (versionsBehind == -1)
00214 Compare();
00215
00216 #if minDBMS_is_only_for_schema_upgrades
00217 if (versionsBehind == 0)
00218 return MYTH_SCHEMA_USE_EXISTING;
00219 #endif
00220
00221
00222
00223
00224
00225 if (((backupStatus == kDB_Backup_Unknown) ||
00226 (backupStatus == kDB_Backup_Failed)) &&
00227 ((upgradeAllowed && (versionsBehind > 0)) ||
00228 m_expertMode))
00229 BackupDB();
00230
00231 connections = CountClients() > 1;
00232 gui = GetMythUI()->IsScreenSetup() && GetMythMainWindow();
00233 validDBMS = (minDBMSmajor == 0)
00234 ? true
00235 : CompareDBMSVersion(minDBMSmajor,
00236 minDBMSminor, minDBMSpoint) >= 0;
00237 upgradable = validDBMS && (versionsBehind > 0)
00238 && (upgradeAllowed || m_expertMode);
00239
00240
00241
00242 if (connections)
00243 warnOtherCl = tr("There are also other clients using this"
00244 " database. They should be shut down first.");
00245 if (!validDBMS)
00246 warnOldDBMS = tr("Error: This version of Myth%1"
00247 " requires MySQL %2.%3.%4 or later."
00248 " You seem to be running MySQL version %5.")
00249 .arg(name).arg(minDBMSmajor).arg(minDBMSminor)
00250 .arg(minDBMSpoint).arg(GetDBMSVersion());
00251
00252
00253
00254
00255
00256
00257 if (validDBMS)
00258 {
00259
00260 if (emptyDB)
00261 return MYTH_SCHEMA_UPGRADE;
00262
00263 if (m_autoUpgrade && !connections && upgradable)
00264 return MYTH_SCHEMA_UPGRADE;
00265 }
00266
00267 if (!gui && (!isatty(fileno(stdin)) || !isatty(fileno(stdout))))
00268 {
00269 LOG(VB_GENERAL, LOG_INFO,
00270 "Console is non-interactive, can't prompt user...");
00271
00272 if (m_expertMode)
00273 {
00274 LOG(VB_GENERAL, LOG_CRIT, "Using existing schema.");
00275 return MYTH_SCHEMA_USE_EXISTING;
00276 }
00277
00278 if (!validDBMS)
00279 {
00280 LOG(VB_GENERAL, LOG_CRIT, warnOldDBMS);
00281 return MYTH_SCHEMA_EXIT;
00282 }
00283
00284 if (versionsBehind < 0)
00285 {
00286 LOG(VB_GENERAL, LOG_CRIT,
00287 QString("Error: MythTV database has newer %1 schema (%2) "
00288 "than expected (%3).")
00289 .arg(name).arg(DBver).arg(m_newSchemaVer));
00290 return MYTH_SCHEMA_ERROR;
00291 }
00292
00293 if (upgradeIfNoUI && validDBMS)
00294 {
00295 LOG(VB_GENERAL, LOG_CRIT, "Upgrading.");
00296 return MYTH_SCHEMA_UPGRADE;
00297 }
00298
00299 return MYTH_SCHEMA_EXIT;
00300 }
00301
00302
00303
00304
00305
00306
00307 enum MythSchemaUpgrade returnValue = MYTH_SCHEMA_UPGRADE;
00308 QString message;
00309
00310 if (upgradable)
00311 {
00312 if (m_autoUpgrade && connections)
00313 {
00314 message = tr("Error: MythTV cannot upgrade the schema of this"
00315 " datatase because other clients are using it.\n\n"
00316 "Please shut them down before upgrading.");
00317 returnValue = MYTH_SCHEMA_ERROR;
00318 }
00319 else
00320 {
00321 message = tr("Warning: MythTV wants to upgrade your database,")
00322 + "\n" + tr("for the %1 schema, from %2 to %3.");
00323 if (m_expertMode)
00324
00325 message += "\n\nYou can try using the old schema,"
00326 " but that may cause problems.";
00327 }
00328 }
00329 else if (!validDBMS)
00330 {
00331 message = warnOldDBMS;
00332 returnValue = MYTH_SCHEMA_ERROR;
00333 }
00334 else if (versionsBehind > 0)
00335 {
00336 message = tr("This version of MythTV requires an updated database. ")
00337 + tr("(schema is %1 versions behind)").arg(versionsBehind)
00338 + "\n\n" + tr("Please run mythtv-setup or mythbackend "
00339 "to update your database.");
00340 returnValue = MYTH_SCHEMA_ERROR;
00341 }
00342 else
00343 {
00344 if (m_expertMode)
00345
00346 message = "Warning: MythTV database has newer"
00347 " %1 schema (%2) than expected (%3).";
00348 else
00349 {
00350 message = tr("Error: MythTV database has newer"
00351 " %1 schema (%2) than expected (%3).");
00352 returnValue = MYTH_SCHEMA_ERROR;
00353 }
00354 }
00355
00356 if (backupStatus == kDB_Backup_Failed)
00357 message += "\n" + tr("MythTV was unable to backup your database.");
00358
00359 if (message.contains("%1"))
00360 message = message.arg(name).arg(DBver).arg(m_newSchemaVer);
00361
00362
00363 DatabaseParams dbParam = MythDB::getMythDB()->GetDatabaseParams();
00364 message += "\n\n" + tr("Database Host: %1\nDatabase Name: %2")
00365 .arg(dbParam.dbHostName).arg(dbParam.dbName);
00366
00367 if (gui)
00368 {
00369 if (returnValue == MYTH_SCHEMA_ERROR)
00370 {
00371
00372 MythPopupBox::showOkPopup(GetMythMainWindow(), "", message);
00373 return MYTH_SCHEMA_ERROR;
00374 }
00375
00376 returnValue = GuiPrompt(message, upgradable, m_expertMode);
00377 if (returnValue == MYTH_SCHEMA_EXIT)
00378 return MYTH_SCHEMA_EXIT;
00379
00380 if (m_expertMode)
00381 return returnValue;
00382
00383
00384 if (backupStatus == kDB_Backup_Completed)
00385 {
00386 int dirPos = m_backupResult.lastIndexOf('/');
00387 QString dirName;
00388 QString fileName;
00389 if (dirPos > 0)
00390 {
00391 fileName = m_backupResult.mid(dirPos + 1);
00392 dirName = m_backupResult.left(dirPos);
00393 }
00394 message = tr("If your system becomes unstable, a database"
00395 " backup file called\n%1\nis located in %2")
00396 .arg(fileName).arg(dirName);
00397 }
00398 else
00399 message = tr("This cannot be un-done, so having a"
00400 " database backup would be a good idea.");
00401 if (connections)
00402 message += "\n\n" + warnOtherCl;
00403
00404 return GuiPrompt(message, upgradable, m_expertMode);
00405 }
00406
00407
00408 QString resp;
00409
00410 cout << endl << message.toLocal8Bit().constData() << endl << endl;
00411
00412 if (returnValue == MYTH_SCHEMA_ERROR)
00413 return MYTH_SCHEMA_ERROR;
00414
00415 if (backupStatus == kDB_Backup_Failed)
00416 cout << "WARNING: MythTV was unable to backup your database."
00417 << endl << endl;
00418 else if ((backupStatus == kDB_Backup_Completed) &&
00419 (m_backupResult != ""))
00420 cout << "If your system becomes unstable, "
00421 "a database backup is located in "
00422 << m_backupResult.toLocal8Bit().constData() << endl << endl;
00423
00424 if (m_expertMode)
00425 {
00426 resp = getResponse("Would you like to use the existing schema?", "yes");
00427 if (resp.isEmpty() || resp.left(1).toLower() == "y")
00428 return MYTH_SCHEMA_USE_EXISTING;
00429 }
00430
00431 resp = getResponse("\nShall I upgrade this database?", "yes");
00432 if (!resp.isEmpty() && resp.left(1).toLower() != "y")
00433 return MYTH_SCHEMA_EXIT;
00434
00435 if (connections)
00436 cout << endl << warnOtherCl.toLocal8Bit().constData() << endl;
00437
00438 if ((backupStatus != kDB_Backup_Completed) &&
00439 (backupStatus != kDB_Backup_Empty_DB))
00440 {
00441 resp = getResponse("\nA database backup might be a good idea"
00442 "\nAre you sure you want to upgrade?", "no");
00443 if (resp.isEmpty() || resp.left(1).toLower() == "n")
00444 return MYTH_SCHEMA_EXIT;
00445 }
00446
00447 return MYTH_SCHEMA_UPGRADE;
00448 }