MythTV  0.26-pre
schemawizard.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 #include <unistd.h>      // for isatty() on Windows
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     // Users and developers can choose to live dangerously,
00035     // either to silently and automatically upgrade,
00036     // or an expert option to allow use of existing:
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     // No current schema? Investigate further:
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     //DBver = "9" + DBver + "-testing";
00121     DBver += "-testing";
00122     return 0;
00123 #endif
00124 
00125     if (m_newSchemaVer == DBver)
00126     {
00127         versionsBehind = 0;
00128     }
00129     else
00130     {
00131         // Branch DB versions may not be integer version numbers.
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         // Not translated. This string can't appear in released builds.
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;   // Are (other) FE/BEs connected?
00205     bool     gui;           // Was gContext Init'ed gui=true?
00206     bool     upgradable;    // Can/should we upgrade?
00207     bool     validDBMS;     // Do we measure up to minDBMS* ?
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)              // Why was this method even called?
00218         return MYTH_SCHEMA_USE_EXISTING;
00219 #endif
00220 
00221     // Only back up the database if we haven't already successfully made a
00222     // backup and the database is old/about to be upgraded (not if it's too
00223     // new) or if a user is doing something they probably shouldn't ("expert
00224     // mode")
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)   // If the caller provided no version,
00234                   ? true                // the upgrade code can't be fussy!
00235                   : CompareDBMSVersion(minDBMSmajor,
00236                                        minDBMSminor, minDBMSpoint) >= 0;
00237     upgradable  = validDBMS && (versionsBehind > 0)
00238                             && (upgradeAllowed || m_expertMode);
00239 
00240 
00241     // Build up strings used both in GUI and command shell contexts:
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     // 1. Deal with the trivial cases (No user prompting required)
00256     //
00257     if (validDBMS)
00258     {
00259         // Empty database? Always upgrade, to create tables
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     // 2. Build up a compound message to show the user, wait for a response
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                 // Not translated. This string can't appear in released builds.
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   // This client is too old
00343     {
00344         if (m_expertMode)
00345             // Not translated. This string can't appear in released builds.
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             // Display error, return warning to caller
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         // The annoying extra confirmation:
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     // We are not in a GUI environment, so try to prompt the user in the shell
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends