MDL-41924 backup: Reduce questions included in backups

Historically, backups of an activity using questions would include all
questions for the entire context, where any question had been used. This
could lead to very large backup files, and exacerbated problems with
question duplication.

This changes the process for annotating categories to include in the
backup to find specific categories, rather than whole contexts. It
includes 3 groups of categories:
1. Those that belong to the backed up activity's context.
2. Those that contain a question used directly in a question_reference
belonging to the activity.
3. Those that contain a question used via a question_set_reference
belonging to the activity.
It also adds in any parent categories that are not included by the
above, so we have a complete hierarchy.

For group 1, the backup will include all questions belonging to these
categories, as before. For groups 2 and 3, it will only include
questions for question bank entries that have been annotated by the
activity.
This commit is contained in:
Mark Johnson
2025-07-03 14:50:52 +01:00
parent 3e6d669b34
commit 006568435d
5 changed files with 208 additions and 49 deletions
@@ -161,6 +161,28 @@ abstract class backup_controller_dbops extends backup_dbops {
$dbman->create_temp_table($xmldb_table); // And create it
}
/**
* Create temporary tables to store a partial copies of question category data from the backup_ids_temp table.
*
* This is to work around MySQL's restruction on joining the same temporary table multiple times in the same query. It isn't
* necessary on PostgreSQL (which can join temporary tables multiple times) or MSSQL (which doesn't really use temporary tables)
* but doing it this way keeps things database-agnostic.
*/
public static function create_question_category_temp_tables(): void {
global $DB;
$dbman = $DB->get_manager();
foreach (['question_category_complete_temp', 'question_category_partial_temp'] as $tablename) {
$xmldbtable = new xmldb_table($tablename);
$xmldbtable->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
$xmldbtable->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL);
$xmldbtable->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL);
$xmldbtable->add_key('primary', XMLDB_KEY_PRIMARY, ['id']);
$dbman->create_temp_table($xmldbtable);
}
}
public static function drop_backup_ids_temp_table($backupid) {
global $DB;
$dbman = $DB->get_manager(); // We are going to use database_manager services
@@ -172,6 +194,21 @@ abstract class backup_controller_dbops extends backup_dbops {
}
}
/**
* Dispose of the additional temporary tables.
*/
public static function drop_question_category_temp_tables(): void {
global $DB;
$dbman = $DB->get_manager();
foreach (['question_category_complete_temp', 'question_category_partial_temp'] as $tablename) {
if ($dbman->table_exists($tablename)) {
$xmldbtable = new xmldb_table($tablename);
$dbman->drop_table($xmldbtable);
}
}
}
/**
* Decode the info field from backup_ids_temp or backup_files_temp.
*