10 #include <QRegularExpression>
26 #define LOC QString("DBUtil: ")
28 #if QT_VERSION < QT_VERSION_CHECK(5,15,2)
29 #define capturedView capturedRef
65 std::array<int,3> compareto {major,
minor, point};
66 for (
int i = 0; i < 3 && !result; i++)
68 if ((
version[i] > -1) || (compareto[i] != 0))
69 result =
version[i] - compareto[i];
81 const int size = tables.size();
84 return (((size == 1) && (tables.at(0).endsWith(
".`schemalock`"))) ||
94 QString backupStartTimeStr =
98 if (backupStartTimeStr.isEmpty())
100 LOG(VB_DATABASE, LOG_ERR,
"DBUtil::BackupInProgress(): No start time "
101 "found, database backup is not in progress.");
105 backupStartTimeStr.replace(
" ",
"T");
111 if (backupEndTimeStr.isEmpty())
114 if (backupElapsed < 10min)
116 LOG(VB_DATABASE, LOG_INFO,
117 QString(
"DBUtil::BackupInProgress(): Found "
118 "database backup start time of %1 which was %2 seconds "
119 "ago, therefore it appears the backup is still running.")
120 .arg(backupStartTimeStr)
121 .arg(backupElapsed.count()));
124 LOG(VB_DATABASE, LOG_ERR, QString(
"DBUtil::BackupInProgress(): "
125 "Database backup started at %1, but no end time was found. "
126 "The backup started %2 seconds ago and should have "
127 "finished by now therefore it appears it is not running .")
128 .arg(backupStartTimeStr)
129 .arg(backupElapsed.count()));
133 backupEndTimeStr.replace(
" ",
"T");
137 if (backupEndTime >= backupStartTime)
139 LOG(VB_DATABASE, LOG_ERR,
140 QString(
"DBUtil::BackupInProgress(): Found "
141 "database backup end time of %1 later than start time "
142 "of %2, therefore backup is not running.")
143 .arg(backupEndTimeStr, backupStartTimeStr));
146 if (backupElapsed > 10min)
148 LOG(VB_DATABASE, LOG_ERR,
149 QString(
"DBUtil::BackupInProgress(): "
150 "Database backup started at %1, but has not ended yet. "
151 "The backup started %2 seconds ago and should have "
152 "finished by now therefore it appears it is not running")
153 .arg(backupStartTimeStr)
154 .arg(backupElapsed.count()));
159 LOG(VB_DATABASE, LOG_INFO, QString(
"DBUtil::BackupInProgress(): "
160 "Database backup started at %1, and is still running.")
161 .arg(backupStartTimeStr));
191 [[maybe_unused]]
bool disableRotation)
196 LOG(VB_GENERAL, LOG_CRIT,
"Database backups disabled on Windows.");
202 LOG(VB_GENERAL, LOG_CRIT,
203 "Database backups disabled. Skipping backup.");
209 LOG(VB_GENERAL, LOG_CRIT,
"New database detected. Skipping backup.");
213 QString backupScript =
GetShareDir() +
"mythconverg_backup.pl";
217 if (!QFile::exists(backupScript))
219 LOG(VB_GENERAL, LOG_CRIT, QString(
"Database backup script does "
220 "not exist: %1").arg(backupScript));
221 backupScript.clear();
228 "BackupDBLastRunStart",
231 if (!backupScript.isEmpty())
235 LOG(VB_GENERAL, LOG_CRIT,
"Script-based database backup failed. "
236 "Retrying with internal backup.");
243 "BackupDBLastRunEnd",
248 QString dbTag(
"BackupDB");
249 query.
prepare(
"DELETE FROM housekeeping WHERE tag = :TAG ;");
254 query.
prepare(
"INSERT INTO housekeeping(tag,lastrun) "
255 "values(:TAG ,now()) ;");
285 const QStringList all_tables =
GetTables(QStringList(
"MyISAM"));
287 if (all_tables.empty())
290 QString sql = QString(
"CHECK TABLE %1 %2;")
291 .arg(all_tables.join(
", "),
options);
293 LOG(VB_GENERAL, LOG_CRIT,
"Checking database tables.");
294 if (!query.
exec(sql))
304 LOG(VB_GENERAL, LOG_CRIT, QString(
"Found crashed database table(s): %1")
305 .arg(tables.join(
", ")));
343 QString all_tables = tables.join(
", ");
344 LOG(VB_GENERAL, LOG_CRIT, QString(
"Repairing database tables: %1")
347 QString sql = QString(
"REPAIR TABLE %1;").arg(all_tables);
348 if (!query.
exec(sql))
356 if (!bad_tables.empty())
358 LOG(VB_GENERAL, LOG_CRIT,
359 QString(
"Unable to repair crashed table(s): %1")
360 .arg(bad_tables.join(
", ")));
387 QSqlRecord record = query.
record();
388 int table_index = record.indexOf(
"Table");
389 int type_index = record.indexOf(
"Msg_type");
390 int text_index = record.indexOf(
"Msg_text");
394 QString previous_table;
398 table = query.
value(table_index).toString();
399 type = query.
value(type_index).toString();
400 text = query.
value(text_index).toString();
401 if (table != previous_table)
405 tables.append(previous_table);
408 previous_table = table;
411 if (
"status" ==
type.toLower() &&
"ok" == text.toLower())
413 else if (
"error" ==
type.toLower() ||
414 (
"status" ==
type.toLower() &&
"ok" != text.toLower()))
419 tables.append(table);
436 QString sql =
"SELECT CONCAT('`', TABLE_SCHEMA, "
437 " '`.`', TABLE_NAME, "
438 " '`') AS `TABLE_NAME` "
439 " FROM INFORMATION_SCHEMA.TABLES "
440 " WHERE TABLE_SCHEMA = DATABASE() "
441 " AND TABLE_TYPE = 'BASE TABLE'";
442 if (!engines.empty())
443 sql.append(QString(
" AND ENGINE IN ('%1')")
444 .arg(engines.join(
"', '")));
445 if (!query.
exec(sql))
453 result.append(query.
value(0).toString());
474 return QString(
"%1-%2%3").arg(
prefix, time, extension);
491 if (!dirList.empty())
495 if (!QDir(directory).exists())
497 LOG(VB_FILE, LOG_INFO,
"GetBackupDirectory() - ignoring " +
498 directory +
", using /tmp");
503 if (directory.isNull())
524 const QString &privateinfo, QString &
filename)
528 const QByteArray tmpfile =
filename.toLocal8Bit();
530 FILE *fp = fopen(tmpfile.constData(),
"w");
533 LOG(VB_GENERAL, LOG_ERR,
LOC +
534 QString(
"Unable to create temporary "
535 "configuration file for creating DB backup: %1")
536 .arg(tmpfile.constData()));
542 if (chmod(tmpfile.constData(), S_IRUSR))
544 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error changing permissions '%1'")
545 .arg(tmpfile.constData()) +
ENO);
548 QByteArray outarr = privateinfo.toLocal8Bit();
549 fprintf(fp,
"%s", outarr.constData());
553 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error closing '%1'")
554 .arg(tmpfile.constData()) +
ENO);
568 bool disableRotation)
574 dbSchemaVer,
".sql");
579 if (!(scriptArgs.contains(
"rotate", Qt::CaseInsensitive)))
580 rotate =
"rotate=-1";
584 QString privateinfo =
585 QString(
"DBHostName=%1\nDBPort=%2\n"
586 "DBUserName=%3\nDBPassword=%4\n"
587 "DBName=%5\nDBSchemaVer=%6\n"
588 "DBBackupDirectory=%7\nDBBackupFilename=%8\n%9\n")
592 backupDirectory, backupFilename,
594 QString tempDatabaseConfFile;
597 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Attempting backup, anyway.");
599 LOG(VB_GENERAL, LOG_ERR, QString(
"Backing up database with script: '%1'")
602 QString command = backupScript +
" " + scriptArgs +
" " +
603 tempDatabaseConfFile;
608 QByteArray tmpfile = tempDatabaseConfFile.toLocal8Bit();
609 unlink(tmpfile.constData());
614 LOG(VB_GENERAL, LOG_ERR,
LOC +
615 QString(
"Error backing up database: %1 (%2)")
616 .arg(command).arg(status));
621 LOG(VB_GENERAL, LOG_CRIT,
"Database Backup complete.");
623 QDir dir(backupDirectory, backupFilename +
"*");
624 uint numfiles = dir.count();
631 LOG(VB_FILE, LOG_ERR,
LOC +
632 QString(
"No files beginning with the suggested database backup "
633 "filename '%1' were found in '%2'.")
634 .arg(backupFilename, backupDirectory));
638 filename = dir.path() +
"/" + dir[0];;
641 LOG(VB_FILE, LOG_ERR,
LOC +
642 QString(
"Multiple files beginning with the suggested database "
643 "backup filename '%1' were found in '%2'. "
644 "Assuming the first is the backup.")
645 .arg(backupFilename, backupDirectory));
651 LOG(VB_GENERAL, LOG_CRIT, QString(
"Backed up database to file: '%1'")
671 QString compressCommand(
"");
672 QString extension =
".sql";
673 if (QFile::exists(
"/bin/gzip"))
674 compressCommand =
"/bin/gzip";
675 else if (QFile::exists(
"/usr/bin/gzip"))
676 compressCommand =
"/usr/bin/gzip";
678 LOG(VB_GENERAL, LOG_CRIT,
"Neither /bin/gzip nor /usr/bin/gzip exist. "
679 "The database backup will be uncompressed.");
682 dbParams.
m_dbName +
"-" + dbSchemaVer, extension);
683 QString backupPathname = backupDirectory +
"/" + backupFilename;
685 QString privateinfo = QString(
686 "[client]\npassword=%1\n[mysqldump]\npassword=%2\n")
688 QString tempExtraConfFile;
692 QString portArg =
"";
694 portArg = QString(
" --port='%1'").arg(dbParams.
m_dbPort);
695 command = QString(
"mysqldump --defaults-extra-file='%1' --host='%2'%3"
696 " --user='%4' --add-drop-table --add-locks"
697 " --allow-keywords --complete-insert"
698 " --extended-insert --lock-tables --no-create-db --quick"
699 " '%5' > '%6' 2>/dev/null")
704 LOG(VB_FILE, LOG_INFO, QString(
"Backing up database with command: '%1'")
706 LOG(VB_GENERAL, LOG_CRIT, QString(
"Backing up database to file: '%1'")
707 .arg(backupPathname));
711 QByteArray tmpfile = tempExtraConfFile.toLocal8Bit();
712 unlink(tmpfile.constData());
716 LOG(VB_GENERAL, LOG_ERR,
LOC +
717 QString(
"Error backing up database: '%1' (%2)")
718 .arg(command).arg(status));
723 if (compressCommand !=
"")
725 LOG(VB_GENERAL, LOG_CRIT,
"Compressing database backup file.");
726 compressCommand +=
" " + backupPathname;
731 LOG(VB_GENERAL, LOG_CRIT,
732 "Compression failed, backup file will remain uncompressed.");
736 backupPathname +=
".gz";
738 LOG(VB_GENERAL, LOG_CRIT, QString(
"Database Backup filename: '%1'")
739 .arg(backupPathname));
743 LOG(VB_GENERAL, LOG_CRIT,
"Database Backup complete.");
760 if (dbmsVersion.isEmpty())
763 query.
prepare(
"SELECT VERSION();");
766 LOG(VB_GENERAL, LOG_ERR,
LOC +
767 "Unable to determine MySQL version.");
772 dbmsVersion = query.
value(0).toString();
788 static const QRegularExpression parseVersion
789 { R
"(^(\d+)(?:\.(\d+)(?:\.(\d+))?)?)" };
791 if (!match.hasMatch())
813 LOG(VB_GENERAL, LOG_DEBUG,
"Not connected to DB");
817 if (!query.
exec(
"SHOW PROCESSLIST;"))
823 QSqlRecord record = query.
record();
824 int db_index = record.indexOf(
"db");
825 QString dbName =
GetMythDB()->GetDatabaseName();
830 inUseDB = query.
value(db_index).toString();
831 if (inUseDB == dbName)
837 count = (count + 3)/4;
839 LOG(VB_GENERAL, LOG_DEBUG,
840 QString(
"DBUtil::CountClients() found %1").arg(count));
850 query.
prepare(
"SELECT GET_LOCK('schemaLock', :TIMEOUT)");
851 query.
bindValue(
":TIMEOUT", timeout_secs);
857 query.
prepare(
"SELECT RELEASE_LOCK('schemaLock')");
870 query.
prepare(
"SELECT CONVERT_TZ(NOW(), 'SYSTEM', 'Etc/UTC')");
873 LOG(VB_GENERAL, LOG_ERR,
"MySQL time zone support check failed");
877 return !query.
value(0).isNull();