MythTV  0.26-pre
dbutil.cpp
Go to the documentation of this file.
00001 #include <climits>
00002 #include <stdlib.h>
00003 #include <stdio.h>
00004 #include <sys/types.h>
00005 #include <sys/stat.h>
00006 #include <unistd.h>
00007 
00008 #include <QDir>
00009 #include <QFile>
00010 #include <QRegExp>
00011 #include <QDateTime>
00012 #include <QSqlError>
00013 #include <QSqlRecord>
00014 
00015 #include "dbutil.h"
00016 #include "mythcorecontext.h"
00017 #include "storagegroup.h"
00018 #include "mythmiscutil.h"
00019 #include "mythdb.h"
00020 #include "mythdirs.h"
00021 #include "mythlogging.h"
00022 #include "mythsystem.h"
00023 #include "exitcodes.h"
00024 
00025 #define LOC QString("DBUtil: ")
00026 
00027 const int DBUtil::kUnknownVersionNumber = INT_MIN;
00028 
00032 DBUtil::DBUtil(void)
00033     : m_versionString(QString::null), m_versionMajor(-1), m_versionMinor(-1),
00034       m_versionPoint(-1)
00035 {
00036 }
00037 
00042 QString DBUtil::GetDBMSVersion(void)
00043 {
00044     if (m_versionString.isEmpty())
00045         QueryDBMSVersion();
00046     return m_versionString;
00047 }
00048 
00061 int DBUtil::CompareDBMSVersion(int major, int minor, int point)
00062 {
00063     if (m_versionMajor < 0)
00064         if (!ParseDBMSVersion())
00065            return kUnknownVersionNumber;
00066 
00067     int result = 0;
00068     int version[3] = {m_versionMajor, m_versionMinor, m_versionPoint};
00069     int compareto[3] = {major, minor, point};
00070     for (int i = 0; i < 3 && !result; i++)
00071     {
00072         if ((version[i] > -1) || (compareto[i] != 0))
00073             result = version[i] - compareto[i];
00074     }
00075 
00076     return result;
00077 }
00078 
00082 bool DBUtil::IsNewDatabase(void)
00083 {
00084     const QStringList tables = GetTables();
00085     const int size = tables.size();
00086     // Usually there will be a single table called schemalock, but check for
00087     // no tables, also, just in case.
00088     return (((size == 1) && (tables.at(0).endsWith(".`schemalock`"))) ||
00089             (size == 0));
00090 }
00091 
00096 bool DBUtil::IsBackupInProgress(void)
00097 {
00098     QString backupStartTimeStr =
00099         gCoreContext->GetSetting("BackupDBLastRunStart");
00100     QString backupEndTimeStr = gCoreContext->GetSetting("BackupDBLastRunEnd");
00101 
00102     if (backupStartTimeStr.isEmpty())
00103     {
00104         LOG(VB_DATABASE, LOG_ERR, "DBUtil::BackupInProgress(): No start time "
00105                                   "found, database backup is not in progress.");
00106         return false;
00107     }
00108 
00109     backupStartTimeStr.replace(" ", "T");
00110 
00111     QDateTime backupStartTime =
00112         QDateTime::fromString(backupStartTimeStr, Qt::ISODate);
00113 
00114     // No end time set
00115     if (backupEndTimeStr.isEmpty())
00116     {
00117         // If DB Backup started less then 10 minutes ago, assume still running
00118         if (backupStartTime.secsTo(QDateTime::currentDateTime()) < 600)
00119         {
00120             LOG(VB_DATABASE, LOG_INFO,
00121                 QString("DBUtil::BackupInProgress(): Found "
00122                     "database backup start time of %1 which was %2 seconds "
00123                     "ago, therefore it appears the backup is still running.")
00124                     .arg(backupStartTimeStr)
00125                     .arg(backupStartTime.secsTo(QDateTime::currentDateTime())));
00126             return true;
00127         }
00128         else
00129         {
00130             LOG(VB_DATABASE, LOG_ERR, QString("DBUtil::BackupInProgress(): "
00131                     "Database backup started at %1, but no end time was found. "
00132                     "The backup started %2 seconds ago and should have "
00133                     "finished by now therefore it appears it is not running .")
00134                     .arg(backupStartTimeStr)
00135                     .arg(backupStartTime.secsTo(QDateTime::currentDateTime())));
00136             return false;
00137         }
00138     }
00139     else
00140     {
00141         backupEndTimeStr.replace(" ", "T");
00142 
00143         QDateTime backupEndTime =
00144             QDateTime::fromString(backupEndTimeStr, Qt::ISODate);
00145 
00146         if (backupEndTime >= backupStartTime)
00147         {
00148             LOG(VB_DATABASE, LOG_ERR,
00149                 QString("DBUtil::BackupInProgress(): Found "
00150                     "database backup end time of %1 later than start time "
00151                     "of %2, therefore backup is not running.")
00152                     .arg(backupEndTimeStr).arg(backupStartTimeStr));
00153             return false;
00154         }
00155         else if (backupStartTime.secsTo(QDateTime::currentDateTime()) > 600)
00156         {
00157             LOG(VB_DATABASE, LOG_ERR,
00158                 QString("DBUtil::BackupInProgress(): "
00159                     "Database backup started at %1, but has not ended yet.  "
00160                     "The backup started %2 seconds ago and should have "
00161                     "finished by now therefore it appears it is not running")
00162                     .arg(backupStartTimeStr)
00163                     .arg(backupStartTime.secsTo(QDateTime::currentDateTime())));
00164             return false;
00165         }
00166         else
00167         {
00168             // start > end and started less than 10 minutes ago
00169             LOG(VB_DATABASE, LOG_INFO, QString("DBUtil::BackupInProgress(): "
00170                     "Database backup started at %1, and is still running.")
00171                     .arg(backupStartTimeStr));
00172             return true;
00173         }
00174     }
00175 
00176     // Shouldn't get here
00177     return false;
00178 }
00179 
00205 MythDBBackupStatus DBUtil::BackupDB(QString &filename, bool disableRotation)
00206 {
00207     filename = QString();
00208 
00209 #ifdef USING_MINGW
00210     LOG(VB_GENERAL, LOG_CRIT, "Database backups disabled on Windows.");
00211     return kDB_Backup_Disabled;
00212 #else
00213 
00214     if (gCoreContext->GetNumSetting("DisableAutomaticBackup", 0))
00215     {
00216         LOG(VB_GENERAL, LOG_CRIT, 
00217             "Database backups disabled.  Skipping backup.");
00218         return kDB_Backup_Disabled;
00219     }
00220 
00221     if (IsNewDatabase())
00222     {
00223         LOG(VB_GENERAL, LOG_CRIT, "New database detected.  Skipping backup.");
00224         return kDB_Backup_Empty_DB;
00225     }
00226 
00227     QString backupScript = GetShareDir() + "mythconverg_backup.pl";
00228     backupScript = gCoreContext->GetSetting("DatabaseBackupScript",
00229                                             backupScript);
00230 
00231     if (!QFile::exists(backupScript))
00232     {
00233         LOG(VB_GENERAL, LOG_CRIT, QString("Database backup script does "
00234                                           "not exist: %1").arg(backupScript));
00235         backupScript = QString::null;
00236     }
00237 
00238     bool result = false;
00239     MSqlQuery query(MSqlQuery::InitCon());
00240 
00241     gCoreContext->SaveSettingOnHost("BackupDBLastRunStart",
00242                                 QDateTime::currentDateTime()
00243                                 .toString("yyyy-MM-dd hh:mm:ss"), NULL);
00244 
00245     if (!backupScript.isEmpty())
00246     {
00247         result = DoBackup(backupScript, filename, disableRotation);
00248         if (!result)
00249             LOG(VB_GENERAL, LOG_CRIT, "Script-based database backup failed. "
00250                                       "Retrying with internal backup.");
00251     }
00252 
00253     if (!result)
00254         result = DoBackup(filename);
00255 
00256     gCoreContext->SaveSettingOnHost("BackupDBLastRunEnd",
00257                                 QDateTime::currentDateTime()
00258                                 .toString("yyyy-MM-dd hh:mm:ss"), NULL);
00259 
00260     if (query.isConnected())
00261     {
00262         QString dbTag("BackupDB");
00263         query.prepare("DELETE FROM housekeeping WHERE tag = :TAG ;");
00264         query.bindValue(":TAG", dbTag);
00265         if (!query.exec())
00266             MythDB::DBError("DBUtil::BackupDB", query);
00267 
00268         query.prepare("INSERT INTO housekeeping(tag,lastrun) "
00269                        "values(:TAG ,now()) ;");
00270         query.bindValue(":TAG", dbTag);
00271         if (!query.exec())
00272             MythDB::DBError("DBUtil::BackupDB", query);
00273     }
00274 
00275     if (result)
00276         return kDB_Backup_Completed;
00277 
00278     return kDB_Backup_Failed;
00279 #endif // USING_MINGW
00280 }
00281 
00293 bool DBUtil::CheckTables(const bool repair, const QString options)
00294 {
00295     MSqlQuery query(MSqlQuery::InitCon());
00296     if (!query.isConnected())
00297         return false;
00298 
00299     const QStringList all_tables = GetTables(QStringList("MyISAM"));
00300 
00301     if (all_tables.empty())
00302         return true;
00303 
00304     QString sql = QString("CHECK TABLE %1 %2;").arg(all_tables.join(", "))
00305                                                .arg(options);
00306 
00307     LOG(VB_GENERAL, LOG_CRIT, "Checking database tables.");
00308     if (!query.exec(sql))
00309     {
00310         MythDB::DBError("DBUtil Checking Tables", query);
00311         return false;
00312     }
00313 
00314     QStringList tables = CheckRepairStatus(query);
00315     bool result = true;
00316     if (!tables.empty())
00317     {
00318         LOG(VB_GENERAL, LOG_CRIT, QString("Found crashed database table(s): %1")
00319                                       .arg(tables.join(", ")));
00320         if (repair == true)
00321             // If RepairTables() repairs the crashed tables, return true
00322             result = RepairTables(tables);
00323         else
00324             result = false;
00325     }
00326 
00327     return result;
00328 }
00329 
00347 bool DBUtil::RepairTables(const QStringList &tables)
00348 {
00349     MSqlQuery query(MSqlQuery::InitCon());
00350     if (!query.isConnected())
00351         return false;
00352 
00353     QString all_tables = tables.join(", ");
00354     LOG(VB_GENERAL, LOG_CRIT, QString("Repairing database tables: %1")
00355                                   .arg(all_tables));
00356 
00357     QString sql = QString("REPAIR TABLE %1;").arg(all_tables);
00358     if (!query.exec(sql))
00359     {
00360         MythDB::DBError("DBUtil Repairing Tables", query);
00361         return false;
00362     }
00363 
00364     QStringList bad_tables = CheckRepairStatus(query);
00365     bool result = true;
00366     if (!bad_tables.empty())
00367     {
00368         LOG(VB_GENERAL, LOG_CRIT, 
00369             QString("Unable to repair crashed table(s): %1")
00370                 .arg(bad_tables.join(", ")));
00371         result = false;
00372     }
00373     return result;
00374 }
00375 
00394 QStringList DBUtil::CheckRepairStatus(MSqlQuery &query)
00395 {
00396     QStringList tables;
00397     QSqlRecord record = query.record();
00398     int table_index = record.indexOf("Table");
00399     int type_index = record.indexOf("Msg_type");
00400     int text_index = record.indexOf("Msg_text");
00401     QString table, type, text, previous_table;
00402     bool ok = true;
00403     while (query.next())
00404     {
00405         table = query.value(table_index).toString();
00406         type = query.value(type_index).toString();
00407         text = query.value(text_index).toString();
00408         if (table != previous_table)
00409         {
00410             if (!ok)
00411             {
00412                 tables.append(previous_table);
00413                 ok = true;
00414             }
00415             previous_table = table;
00416         }
00417         // If the final row shows status OK, the table is now good
00418         if ("status" == type.toLower() && "ok" == text.toLower())
00419             ok = true;
00420         else if ("error" == type.toLower() ||
00421                  ("status" == type.toLower() && "ok" != text.toLower()))
00422             ok = false;
00423     }
00424     // Check the last table in the list
00425     if (!ok)
00426         tables.append(table);
00427     return tables;
00428 }
00429 
00435 QStringList DBUtil::GetTables(const QStringList &engines)
00436 {
00437     QStringList result;
00438 
00439     MSqlQuery query(MSqlQuery::InitCon());
00440     if (!query.isConnected())
00441         return result;
00442 
00443     QString sql = "SELECT CONCAT('`', INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA, "
00444                   "              '`.`', INFORMATION_SCHEMA.TABLES.TABLE_NAME, "
00445                   "              '`') AS `TABLE_NAME` "
00446                   "  FROM INFORMATION_SCHEMA.TABLES "
00447                   " WHERE INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA = DATABASE() "
00448                   "   AND INFORMATION_SCHEMA.TABLES.TABLE_TYPE = 'BASE TABLE'";
00449     if (!engines.empty())
00450         sql.append(QString("   AND INFORMATION_SCHEMA.TABLES.ENGINE IN ('%1')")
00451                            .arg(engines.join("', '")));
00452     if (!query.exec(sql))
00453     {
00454         MythDB::DBError("DBUtil Finding Tables", query);
00455         return result;
00456     }
00457 
00458     while (query.next())
00459     {
00460         result.append(query.value(0).toString());
00461     }
00462 
00463     return result;
00464 }
00465 
00478 QString DBUtil::CreateBackupFilename(QString prefix, QString extension)
00479 {
00480     QDateTime now = QDateTime::currentDateTime();
00481     QString time = now.toString("yyyyMMddhhmmss");
00482     return QString("%1-%2%3").arg(prefix).arg(time).arg(extension);
00483 }
00484 
00494 QString DBUtil::GetBackupDirectory()
00495 {
00496     QString directory;
00497     StorageGroup sgroup("DB Backups", gCoreContext->GetHostName());
00498     QStringList dirList = sgroup.GetDirList();
00499     if (dirList.size())
00500     {
00501         directory = sgroup.FindNextDirMostFree();
00502 
00503         if (!QDir(directory).exists())
00504         {
00505             LOG(VB_FILE, LOG_INFO, "GetBackupDirectory() - ignoring " +
00506                                    directory + ", using /tmp");
00507             directory = QString::null;
00508         }
00509     }
00510 
00511     if (directory.isNull())
00512         // Rather than use kDefaultStorageDir, the default for
00513         // FindNextDirMostFree() when no dirs are defined for the StorageGroup,
00514         // use /tmp as it's possible that kDefaultStorageDir doesn't exist
00515         // and (at least on *nix) less possible that /tmp doesn't exist
00516         directory = "/tmp";
00517 
00518     return directory;
00519 }
00520 
00529 bool DBUtil::CreateTemporaryDBConf(
00530     const QString &privateinfo, QString &filename)
00531 {
00532     bool ok = true;
00533     filename = createTempFile("/tmp/mythtv_db_backup_conf_XXXXXX");
00534     const QByteArray     tmpfile     = filename.toLocal8Bit();
00535     const DatabaseParams dbParams    = gCoreContext->GetDatabaseParams();
00536     const QString        dbSchemaVer = gCoreContext->GetSetting("DBSchemaVer");
00537 
00538     FILE *fp = fopen(tmpfile.constData(), "w");
00539     if (!fp)
00540     {
00541         LOG(VB_GENERAL, LOG_ERR, LOC +
00542             QString("Unable to create temporary "
00543                     "configuration file for creating DB backup: %1")
00544                 .arg(tmpfile.constData()));
00545         filename = "";
00546         ok = false;
00547     }
00548     else
00549     {
00550         chmod(tmpfile.constData(), S_IRUSR);
00551 
00552         QByteArray outarr = privateinfo.toLocal8Bit();
00553         fprintf(fp, "%s", outarr.constData());
00554 
00555         if (fclose(fp))
00556         {
00557             LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error closing '%1'")
00558                     .arg(tmpfile.constData()) + ENO);
00559         }
00560     }
00561 
00562     return ok;
00563 }
00564 
00571 bool DBUtil::DoBackup(const QString &backupScript, QString &filename,
00572                       bool disableRotation)
00573 {
00574     DatabaseParams dbParams = gCoreContext->GetDatabaseParams();
00575     QString     dbSchemaVer = gCoreContext->GetSetting("DBSchemaVer");
00576     QString backupDirectory = GetBackupDirectory();
00577     QString  backupFilename = CreateBackupFilename(dbParams.dbName + "-" +
00578                                                    dbSchemaVer, ".sql");
00579     QString      scriptArgs = gCoreContext->GetSetting("BackupDBScriptArgs");
00580     QString rotate = "";
00581     if (disableRotation)
00582     {
00583         if (!(scriptArgs.contains("rotate", Qt::CaseInsensitive)))
00584             rotate = "rotate=-1";
00585     }
00586 
00587 
00588     QString privateinfo =
00589         QString("DBHostName=%1\nDBPort=%2\n"
00590                 "DBUserName=%3\nDBPassword=%4\n"
00591                 "DBName=%5\nDBSchemaVer=%6\n"
00592                 "DBBackupDirectory=%7\nDBBackupFilename=%8\n%9\n")
00593         .arg(dbParams.dbHostName).arg(dbParams.dbPort)
00594         .arg(dbParams.dbUserName).arg(dbParams.dbPassword)
00595         .arg(dbParams.dbName).arg(dbSchemaVer)
00596         .arg(backupDirectory).arg(backupFilename).arg(rotate);
00597     QString tempDatabaseConfFile = QString::null;
00598     bool hastemp = CreateTemporaryDBConf(privateinfo, tempDatabaseConfFile);
00599     if (!hastemp)
00600         LOG(VB_GENERAL, LOG_ERR, LOC + "Attempting backup, anyway.");
00601 
00602     LOG(VB_GENERAL, LOG_ERR, QString("Backing up database with script: '%1'")
00603             .arg(backupScript));
00604 
00605     QString command = backupScript + " " + scriptArgs + " " +
00606                       tempDatabaseConfFile;
00607     uint status = myth_system(command, kMSDontBlockInputDevs|kMSAnonLog);
00608 
00609     if (hastemp)
00610     {
00611         QByteArray tmpfile = tempDatabaseConfFile.toLocal8Bit();
00612         unlink(tmpfile.constData());
00613     }
00614 
00615     if (status != GENERIC_EXIT_OK)
00616     {
00617         LOG(VB_GENERAL, LOG_ERR, LOC +
00618             QString("Error backing up database: %1 (%2)")
00619                 .arg(command).arg(status));
00620         filename = "__FAILED__";
00621         return false;
00622     }
00623 
00624     LOG(VB_GENERAL, LOG_CRIT, "Database Backup complete.");
00625 
00626     QDir dir(backupDirectory, backupFilename + "*");
00627     uint numfiles = dir.count();
00628     if (numfiles < 1)
00629     {
00630         // If no file begins with the suggested filename, don't show the backup
00631         // filename in the GUI message -- the script probably used some other
00632         // filename
00633         filename = "";
00634         LOG(VB_FILE, LOG_ERR, LOC +
00635             QString("No files beginning with the suggested database backup "
00636                     "filename '%1' were found in '%2'.")
00637                 .arg(backupFilename).arg(backupDirectory));
00638     }
00639     else
00640     {
00641         filename = dir.path() + "/" + dir[0];;
00642         if (numfiles > 1)
00643         {
00644             LOG(VB_FILE, LOG_ERR, LOC +
00645                 QString("Multiple files beginning with the suggested database "
00646                         "backup filename '%1' were found in '%2'. "
00647                         "Assuming the first is the backup.")
00648                     .arg(backupFilename).arg(backupDirectory));
00649         }
00650     }
00651 
00652     if (!filename.isEmpty())
00653     {
00654         LOG(VB_GENERAL, LOG_CRIT, QString("Backed up database to file: '%1'")
00655                 .arg(filename));
00656     }
00657 
00658     return true;
00659 }
00660 
00667 bool DBUtil::DoBackup(QString &filename)
00668 {
00669     DatabaseParams dbParams = gCoreContext->GetDatabaseParams();
00670     QString     dbSchemaVer = gCoreContext->GetSetting("DBSchemaVer");
00671     QString backupDirectory = GetBackupDirectory();
00672 
00673     QString command;
00674     QString compressCommand("");
00675     QString extension = ".sql";
00676     if (QFile::exists("/bin/gzip"))
00677         compressCommand = "/bin/gzip";
00678     else if (QFile::exists("/usr/bin/gzip"))
00679         compressCommand = "/usr/bin/gzip";
00680     else
00681         LOG(VB_GENERAL, LOG_CRIT, "Neither /bin/gzip nor /usr/bin/gzip exist. "
00682                                   "The database backup will be uncompressed.");
00683 
00684     QString backupFilename = CreateBackupFilename(
00685         dbParams.dbName + "-" + dbSchemaVer, extension);
00686     QString backupPathname = backupDirectory + "/" + backupFilename;
00687 
00688     QString privateinfo = QString(
00689         "[client]\npassword=%1\n[mysqldump]\npassword=%2\n")
00690         .arg(dbParams.dbPassword).arg(dbParams.dbPassword);
00691     QString tempExtraConfFile = QString::null;
00692     if (!CreateTemporaryDBConf(privateinfo, tempExtraConfFile))
00693         return false;
00694 
00695     QString portArg = "";
00696     if (dbParams.dbPort > 0)
00697         portArg = QString(" --port='%1'").arg(dbParams.dbPort);
00698     command = QString("mysqldump --defaults-extra-file='%1' --host='%2'%3"
00699                       " --user='%4' --add-drop-table --add-locks"
00700                       " --allow-keywords --complete-insert"
00701                       " --extended-insert --lock-tables --no-create-db --quick"
00702                       " '%5' > '%6' 2>/dev/null")
00703                       .arg(tempExtraConfFile).arg(dbParams.dbHostName)
00704                       .arg(portArg).arg(dbParams.dbUserName)
00705                       .arg(dbParams.dbName).arg(backupPathname);
00706 
00707     LOG(VB_FILE, LOG_INFO, QString("Backing up database with command: '%1'")
00708             .arg(command));
00709     LOG(VB_GENERAL, LOG_CRIT, QString("Backing up database to file: '%1'")
00710             .arg(backupPathname));
00711 
00712     uint status = myth_system(command, kMSDontBlockInputDevs|kMSAnonLog);
00713 
00714     QByteArray tmpfile = tempExtraConfFile.toLocal8Bit();
00715     unlink(tmpfile.constData());
00716 
00717     if (status != GENERIC_EXIT_OK)
00718     {
00719         LOG(VB_GENERAL, LOG_ERR, LOC +
00720             QString("Error backing up database: '%1' (%2)")
00721                 .arg(command).arg(status));
00722         filename = "__FAILED__";
00723         return false;
00724     }
00725 
00726     if (compressCommand != "")
00727     {
00728         LOG(VB_GENERAL, LOG_CRIT, "Compressing database backup file.");
00729         compressCommand += " " + backupPathname;
00730         status = myth_system(compressCommand, kMSDontBlockInputDevs);
00731 
00732         if (status != GENERIC_EXIT_OK)
00733         {
00734             LOG(VB_GENERAL, LOG_CRIT,
00735                    "Compression failed, backup file will remain uncompressed.");
00736         }
00737         else
00738         {
00739             backupPathname += ".gz";
00740 
00741             LOG(VB_GENERAL, LOG_CRIT, QString("Database Backup filename: '%1'")
00742                     .arg(backupPathname));
00743         }
00744     }
00745 
00746     LOG(VB_GENERAL, LOG_CRIT, "Database Backup complete.");
00747 
00748     filename = backupPathname;
00749     return true;
00750 }
00751 
00756 bool DBUtil::QueryDBMSVersion(void)
00757 {
00758     // Allow users to override the string provided by the database server in
00759     // case the value was changed to an unrecognizable string by whomever
00760     // compiled the MySQL server
00761     QString dbmsVersion = gCoreContext->GetSetting("DBMSVersionOverride");
00762 
00763     if (dbmsVersion.isEmpty())
00764     {
00765         MSqlQuery query(MSqlQuery::InitCon());
00766         query.prepare("SELECT VERSION();");
00767         if (!query.exec() || !query.next())
00768         {
00769             LOG(VB_GENERAL, LOG_ERR, LOC + 
00770                 "Unable to determine MySQL version.");
00771             MythDB::DBError("DBUtil Querying DBMS version", query);
00772             dbmsVersion = QString::null;
00773         }
00774         else
00775             dbmsVersion = query.value(0).toString();
00776     }
00777     m_versionString = dbmsVersion;
00778 
00779     return !m_versionString.isEmpty();
00780 }
00781 
00785 bool DBUtil::ParseDBMSVersion()
00786 {
00787     if (m_versionString.isEmpty())
00788         if (!QueryDBMSVersion())
00789             return false;
00790 
00791     bool ok;
00792     QString section;
00793     int pos = 0, i = 0;
00794     int version[3] = {-1, -1, -1};
00795     QRegExp digits("(\\d+)");
00796 
00797     while ((i < 3) && ((pos = digits.indexIn(m_versionString, pos)) > -1))
00798     {
00799         section = digits.cap(1);
00800         pos += digits.matchedLength();
00801         version[i] = section.toInt(&ok, 10);
00802         if (!ok)
00803             version[i] = -1;
00804         i++;
00805     }
00806 
00807     m_versionMajor = version[0];
00808     m_versionMinor = version[1];
00809     m_versionPoint = version[2];
00810 
00811     return m_versionMajor > -1;
00812 }
00813 
00817 int DBUtil::CountClients(void)
00818 {
00819     int count = 0;
00820 
00821     MSqlQuery query(MSqlQuery::InitCon());
00822     if (!query.isConnected())
00823     {
00824         LOG(VB_GENERAL, LOG_DEBUG, "Not connected to DB");
00825         return count;
00826     }
00827 
00828     if (!query.exec("SHOW PROCESSLIST;"))
00829     {
00830         MythDB::DBError("DBUtil CountClients", query);
00831         return count;
00832     }
00833 
00834     QSqlRecord record = query.record();
00835     int db_index = record.indexOf("db");
00836     QString dbName = gCoreContext->GetDatabaseParams().dbName;
00837     QString inUseDB;
00838 
00839     while (query.next())
00840     {
00841         inUseDB = query.value(db_index).toString();
00842         if (inUseDB == dbName)
00843             ++count;
00844     }
00845 
00846     // On average, each myth program has 4 database connections,
00847     // but we round up just in case a new program is loading:
00848     count = (count + 3)/4;
00849 
00850     LOG(VB_GENERAL, LOG_DEBUG,
00851             QString("DBUtil::CountClients() found %1").arg(count));
00852 
00853     return count;
00854 }
00855 
00859 bool DBUtil::TryLockSchema(MSqlQuery &query, uint timeout_secs)
00860 {
00861     query.prepare("SELECT GET_LOCK('schemaLock', :TIMEOUT)");
00862     query.bindValue(":TIMEOUT", timeout_secs);
00863     return query.exec() && query.first() && query.value(0).toBool();
00864 }
00865 
00866 void DBUtil::UnlockSchema(MSqlQuery &query)
00867 {
00868     query.prepare("SELECT RELEASE_LOCK('schemaLock')");
00869     query.exec();
00870 }
00871 
00872 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends