10 #include <QRegularExpression>
26 #define LOC QString("DBUtil: ")
61 std::array<int,3> compareto {major,
minor, point};
62 for (
int i = 0; i < 3 && !result; i++)
64 if ((
version[i] > -1) || (compareto[i] != 0))
65 result =
version[i] - compareto[i];
77 const int size = tables.size();
80 return (((size == 1) && (tables.at(0).endsWith(
".`schemalock`"))) ||
90 QString backupStartTimeStr =
94 if (backupStartTimeStr.isEmpty())
96 LOG(VB_DATABASE, LOG_ERR,
"DBUtil::BackupInProgress(): No start time "
97 "found, database backup is not in progress.");
101 backupStartTimeStr.replace(
" ",
"T");
107 if (backupEndTimeStr.isEmpty())
110 if (backupElapsed < 10min)
112 LOG(VB_DATABASE, LOG_INFO,
113 QString(
"DBUtil::BackupInProgress(): Found "
114 "database backup start time of %1 which was %2 seconds "
115 "ago, therefore it appears the backup is still running.")
116 .arg(backupStartTimeStr)
117 .arg(backupElapsed.count()));
120 LOG(VB_DATABASE, LOG_ERR, QString(
"DBUtil::BackupInProgress(): "
121 "Database backup started at %1, but no end time was found. "
122 "The backup started %2 seconds ago and should have "
123 "finished by now therefore it appears it is not running .")
124 .arg(backupStartTimeStr)
125 .arg(backupElapsed.count()));
129 backupEndTimeStr.replace(
" ",
"T");
133 if (backupEndTime >= backupStartTime)
135 LOG(VB_DATABASE, LOG_ERR,
136 QString(
"DBUtil::BackupInProgress(): Found "
137 "database backup end time of %1 later than start time "
138 "of %2, therefore backup is not running.")
139 .arg(backupEndTimeStr, backupStartTimeStr));
142 if (backupElapsed > 10min)
144 LOG(VB_DATABASE, LOG_ERR,
145 QString(
"DBUtil::BackupInProgress(): "
146 "Database backup started at %1, but has not ended yet. "
147 "The backup started %2 seconds ago and should have "
148 "finished by now therefore it appears it is not running")
149 .arg(backupStartTimeStr)
150 .arg(backupElapsed.count()));
155 LOG(VB_DATABASE, LOG_INFO, QString(
"DBUtil::BackupInProgress(): "
156 "Database backup started at %1, and is still running.")
157 .arg(backupStartTimeStr));
187 [[maybe_unused]]
bool disableRotation)
192 LOG(VB_GENERAL, LOG_CRIT,
"Database backups disabled on Windows.");
198 LOG(VB_GENERAL, LOG_CRIT,
199 "Database backups disabled. Skipping backup.");
205 LOG(VB_GENERAL, LOG_CRIT,
"New database detected. Skipping backup.");
209 QString backupScript =
GetShareDir() +
"mythconverg_backup.pl";
215 LOG(VB_GENERAL, LOG_CRIT, QString(
"Database backup script does "
216 "not exist: %1").arg(backupScript));
217 backupScript.clear();
224 "BackupDBLastRunStart",
227 if (!backupScript.isEmpty())
231 LOG(VB_GENERAL, LOG_CRIT,
"Script-based database backup failed. "
232 "Retrying with internal backup.");
239 "BackupDBLastRunEnd",
244 QString dbTag(
"BackupDB");
245 query.
prepare(
"DELETE FROM housekeeping WHERE tag = :TAG ;");
250 query.
prepare(
"INSERT INTO housekeeping(tag,lastrun) "
251 "values(:TAG ,now()) ;");
281 const QStringList all_tables =
GetTables(QStringList(
"MyISAM"));
283 if (all_tables.empty())
286 QString sql = QString(
"CHECK TABLE %1 %2;")
287 .arg(all_tables.join(
", "),
options);
289 LOG(VB_GENERAL, LOG_CRIT,
"Checking database tables.");
290 if (!query.
exec(sql))
300 LOG(VB_GENERAL, LOG_CRIT, QString(
"Found crashed database table(s): %1")
301 .arg(tables.join(
", ")));
339 QString all_tables = tables.join(
", ");
340 LOG(VB_GENERAL, LOG_CRIT, QString(
"Repairing database tables: %1")
343 QString sql = QString(
"REPAIR TABLE %1;").arg(all_tables);
344 if (!query.
exec(sql))
352 if (!bad_tables.empty())
354 LOG(VB_GENERAL, LOG_CRIT,
355 QString(
"Unable to repair crashed table(s): %1")
356 .arg(bad_tables.join(
", ")));
383 QSqlRecord record = query.
record();
384 int table_index = record.indexOf(
"Table");
385 int type_index = record.indexOf(
"Msg_type");
386 int text_index = record.indexOf(
"Msg_text");
390 QString previous_table;
394 table = query.
value(table_index).toString();
395 type = query.
value(type_index).toString();
396 text = query.
value(text_index).toString();
397 if (table != previous_table)
401 tables.append(previous_table);
404 previous_table = table;
407 if (
"status" ==
type.toLower() &&
"ok" == text.toLower())
409 else if (
"error" ==
type.toLower() ||
410 (
"status" ==
type.toLower() &&
"ok" != text.toLower()))
415 tables.append(table);
432 QString sql =
"SELECT CONCAT('`', TABLE_SCHEMA, "
433 " '`.`', TABLE_NAME, "
434 " '`') AS `TABLE_NAME` "
435 " FROM INFORMATION_SCHEMA.TABLES "
436 " WHERE TABLE_SCHEMA = DATABASE() "
437 " AND TABLE_TYPE = 'BASE TABLE'";
438 if (!engines.empty())
439 sql.append(QString(
" AND ENGINE IN ('%1')")
440 .arg(engines.join(
"', '")));
441 if (!query.
exec(sql))
449 result.append(query.
value(0).toString());
470 return QString(
"%1-%2%3").arg(
prefix, time, extension);
487 if (!dirList.empty())
491 if (!QDir(directory).
exists())
493 LOG(VB_FILE, LOG_INFO,
"GetBackupDirectory() - ignoring " +
494 directory +
", using /tmp");
499 if (directory.isNull())
520 const QString &privateinfo, QString &
filename)
524 const QByteArray tmpfile =
filename.toLocal8Bit();
526 FILE *fp = fopen(tmpfile.constData(),
"w");
529 LOG(VB_GENERAL, LOG_ERR,
LOC +
530 QString(
"Unable to create temporary "
531 "configuration file for creating DB backup: %1")
532 .arg(tmpfile.constData()));
538 if (chmod(tmpfile.constData(), S_IRUSR))
540 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error changing permissions '%1'")
541 .arg(tmpfile.constData()) +
ENO);
544 QByteArray outarr = privateinfo.toLocal8Bit();
545 fprintf(fp,
"%s", outarr.constData());
549 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Error closing '%1'")
550 .arg(tmpfile.constData()) +
ENO);
564 bool disableRotation)
570 dbSchemaVer,
".sql");
575 if (!(scriptArgs.contains(
"rotate", Qt::CaseInsensitive)))
576 rotate =
"rotate=-1";
580 QString privateinfo =
581 QString(
"DBHostName=%1\nDBPort=%2\n"
582 "DBUserName=%3\nDBPassword=%4\n"
583 "DBName=%5\nDBSchemaVer=%6\n"
584 "DBBackupDirectory=%7\nDBBackupFilename=%8\n%9\n")
588 backupDirectory, backupFilename,
590 QString tempDatabaseConfFile;
593 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Attempting backup, anyway.");
595 LOG(VB_GENERAL, LOG_ERR, QString(
"Backing up database with script: '%1'")
598 QString command = backupScript +
" " + scriptArgs +
" " +
599 tempDatabaseConfFile;
604 QByteArray tmpfile = tempDatabaseConfFile.toLocal8Bit();
605 unlink(tmpfile.constData());
610 LOG(VB_GENERAL, LOG_ERR,
LOC +
611 QString(
"Error backing up database: %1 (%2)")
612 .arg(command).arg(status));
617 LOG(VB_GENERAL, LOG_CRIT,
"Database Backup complete.");
619 QDir dir(backupDirectory, backupFilename +
"*");
620 uint numfiles = dir.count();
627 LOG(VB_FILE, LOG_ERR,
LOC +
628 QString(
"No files beginning with the suggested database backup "
629 "filename '%1' were found in '%2'.")
630 .arg(backupFilename, backupDirectory));
634 filename = dir.path() +
"/" + dir[0];;
637 LOG(VB_FILE, LOG_ERR,
LOC +
638 QString(
"Multiple files beginning with the suggested database "
639 "backup filename '%1' were found in '%2'. "
640 "Assuming the first is the backup.")
641 .arg(backupFilename, backupDirectory));
647 LOG(VB_GENERAL, LOG_CRIT, QString(
"Backed up database to file: '%1'")
667 QString compressCommand(
"");
668 QString extension =
".sql";
670 compressCommand =
"/bin/gzip";
672 compressCommand =
"/usr/bin/gzip";
674 LOG(VB_GENERAL, LOG_CRIT,
"Neither /bin/gzip nor /usr/bin/gzip exist. "
675 "The database backup will be uncompressed.");
678 dbParams.
m_dbName +
"-" + dbSchemaVer, extension);
679 QString backupPathname = backupDirectory +
"/" + backupFilename;
681 QString privateinfo = QString(
682 "[client]\npassword=%1\n[mysqldump]\npassword=%2\n")
684 QString tempExtraConfFile;
688 QString portArg =
"";
690 portArg = QString(
" --port='%1'").arg(dbParams.
m_dbPort);
691 command = QString(
"mysqldump --defaults-extra-file='%1' --host='%2'%3"
692 " --user='%4' --add-drop-table --add-locks"
693 " --allow-keywords --complete-insert"
694 " --extended-insert --lock-tables --no-create-db --quick"
695 " '%5' > '%6' 2>/dev/null")
700 LOG(VB_FILE, LOG_INFO, QString(
"Backing up database with command: '%1'")
702 LOG(VB_GENERAL, LOG_CRIT, QString(
"Backing up database to file: '%1'")
703 .arg(backupPathname));
707 QByteArray tmpfile = tempExtraConfFile.toLocal8Bit();
708 unlink(tmpfile.constData());
712 LOG(VB_GENERAL, LOG_ERR,
LOC +
713 QString(
"Error backing up database: '%1' (%2)")
714 .arg(command).arg(status));
719 if (compressCommand !=
"")
721 LOG(VB_GENERAL, LOG_CRIT,
"Compressing database backup file.");
722 compressCommand +=
" " + backupPathname;
727 LOG(VB_GENERAL, LOG_CRIT,
728 "Compression failed, backup file will remain uncompressed.");
732 backupPathname +=
".gz";
734 LOG(VB_GENERAL, LOG_CRIT, QString(
"Database Backup filename: '%1'")
735 .arg(backupPathname));
739 LOG(VB_GENERAL, LOG_CRIT,
"Database Backup complete.");
756 if (dbmsVersion.isEmpty())
759 query.
prepare(
"SELECT VERSION();");
762 LOG(VB_GENERAL, LOG_ERR,
LOC +
763 "Unable to determine MySQL version.");
769 dbmsVersion = query.
value(0).toString();
786 static const QRegularExpression parseVersion
787 { R
"(^(\d+)(?:\.(\d+)(?:\.(\d+))?)?)" };
789 if (!match.hasMatch())
811 LOG(VB_GENERAL, LOG_DEBUG,
"Not connected to DB");
815 if (!query.
exec(
"SHOW PROCESSLIST;"))
821 QSqlRecord record = query.
record();
822 int db_index = record.indexOf(
"db");
823 QString dbName =
GetMythDB()->GetDatabaseName();
828 inUseDB = query.
value(db_index).toString();
829 if (inUseDB == dbName)
835 count = (count + 3)/4;
837 LOG(VB_GENERAL, LOG_DEBUG,
838 QString(
"DBUtil::CountClients() found %1").arg(count));
848 query.
prepare(
"SELECT GET_LOCK('schemaLock', :TIMEOUT)");
849 query.
bindValue(
":TIMEOUT", timeout_secs);
855 query.
prepare(
"SELECT RELEASE_LOCK('schemaLock')");
868 query.
prepare(
"SELECT CONVERT_TZ(NOW(), 'SYSTEM', 'Etc/UTC')");
871 LOG(VB_GENERAL, LOG_ERR,
"MySQL time zone support check failed");
875 return !query.
value(0).isNull();
895 QString sql = QString(
"SELECT COUNT(*) FROM information_schema.columns "
896 "WHERE table_schema = DATABASE() AND "
897 "table_name = '%1' AND column_name = '%2';")
898 .arg(tableName, columnName);
899 LOG(VB_GENERAL, LOG_DEBUG,
900 QString(
"DBUtil::CheckTableColumnExists() SQL: %1").arg(sql));
902 if (!query.
exec(sql))
911 result = (query.
value(0).toInt() > 0);
915 LOG(VB_GENERAL, LOG_ERR,
916 QString(
"DBUtil::CheckTableColumnExists() - Empty result set"));
919 LOG(VB_GENERAL, LOG_DEBUG,
920 QString(
"DBUtil::CheckTableColumnExists('%1', '%2') result: %3").arg(tableName,
921 columnName, QVariant(result).
toString()));