diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index e2aa8a3833b..87b958723ed 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -3926,14 +3926,182 @@ privatefiles,moodle|/user/files.php'; } if ($oldversion < 2022020200.02) { - // Next, split question records into the new tables. - upgrade_migrate_question_table(); + // Define a new temporary field in the question_bank_entries tables. + // Creating temporary field questionid to populate the data in question version table. + // This will make sure the appropriate question id is inserted the version table without making any complex joins. + $table = new xmldb_table('question_bank_entries'); + $field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_TYPE_INTEGER); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + $transaction = $DB->start_delegated_transaction(); + upgrade_set_timeout(3600); + // Create the data for the question_bank_entries table with, including the new temporary field. + $sql = <<execute($sql); + + $transaction->allow_commit(); + // Main savepoint reached. upgrade_main_savepoint(true, 2022020200.02); } - // Finally, drop fields from question table. if ($oldversion < 2022020200.03) { + $transaction = $DB->start_delegated_transaction(); + upgrade_set_timeout(3600); + // Create the question_versions using that temporary field. + $sql = << 0 THEN 'hidden' + ELSE 'ready' + END + FROM {question_bank_entries} qbe + INNER JOIN {question} q ON qbe.questionid = q.id + EOF; + + // Inserting question_versions data. + $DB->execute($sql); + + $transaction->allow_commit(); + + // Dropping temporary field questionid. + $table = new xmldb_table('question_bank_entries'); + $field = new xmldb_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_TYPE_INTEGER); + if ($dbman->field_exists($table, $field)) { + $dbman->drop_field($table, $field); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2022020200.03); + } + + if ($oldversion < 2022020200.04) { + $transaction = $DB->start_delegated_transaction(); + upgrade_set_timeout(3600); + // Create the base data for the random questions in the set_references table. + // This covers most of the hard work in one go. + $concat = $DB->sql_concat("'{\"questioncategoryid\":\"'", 'q.category', "'\",\"includingsubcategories\":\"'", + 'qs.includingsubcategories', "'\"}'"); + $sql = <<execute($sql, [ + 'quizmoduleid' => $DB->get_field('modules', 'id', ['name' => 'quiz']), + 'contextmodule' => CONTEXT_MODULE, + 'random' => 'random', + ]); + + $transaction->allow_commit(); + + // Main savepoint reached. + upgrade_main_savepoint(true, 2022020200.04); + } + + if ($oldversion < 2022020200.05) { + $transaction = $DB->start_delegated_transaction(); + upgrade_set_timeout(3600); + + // Count all the slot tags to be migrated (for progress bar). + $total = $DB->count_records('quiz_slot_tags'); + $pbar = new progress_bar('migratequestiontags', 1000, true); + $i = 0; + // Updating slot_tags for random question tags. + // Now fetch any quiz slot tags and update those slot details into the question_set_references. + $slottags = $DB->get_recordset('quiz_slot_tags', [], 'slotid ASC'); + + $tagstrings = []; + $lastslot = null; + $runinsert = function (int $lastslot, array $tagstrings) use ($DB) { + $conditiondata = $DB->get_field('question_set_references', 'filtercondition', + ['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']); + $condition = json_decode($conditiondata); + $condition->tags = $tagstrings; + $DB->set_field('question_set_references', 'filtercondition', json_encode($condition), + ['itemid' => $lastslot, 'component' => 'mod_quiz', 'questionarea' => 'slot']); + }; + + foreach ($slottags as $tag) { + upgrade_set_timeout(3600); + if ($lastslot && $tag->slotid != $lastslot) { + if (!empty($tagstrings)) { + // Insert the data. + $runinsert($lastslot, $tagstrings); + } + // Prepare for the next slot id. + $tagstrings = []; + } + + $lastslot = $tag->slotid; + $tagstrings[] = "{$tag->tagid},{$tag->tagname}"; + // Update progress. + $i++; + $pbar->update($i, $total, "Migrating question tags - $i/$total."); + } + if ($tagstrings) { + $runinsert($lastslot, $tagstrings); + } + $slottags->close(); + + $transaction->allow_commit(); + // Main savepoint reached. + upgrade_main_savepoint(true, 2022020200.05); + } + + if ($oldversion < 2022020200.06) { + $transaction = $DB->start_delegated_transaction(); + upgrade_set_timeout(3600); + // Create question_references record for each question. + // Except if qtype is random. That case is handled by question_set_reference. + $sql = "INSERT INTO {question_references} + (usingcontextid, component, questionarea, itemid, questionbankentryid) + SELECT c.id, 'mod_quiz', 'slot', qs.id, qv.questionbankentryid + FROM {question} q + JOIN {question_versions} qv ON q.id = qv.questionid + JOIN {quiz_slots} qs ON q.id = qs.questionid + JOIN {modules} m ON m.name = 'quiz' + JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = qs.quizid + JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = " . CONTEXT_MODULE . " + WHERE q.qtype <> 'random'"; + + // Inserting question_references data. + $DB->execute($sql); + + $transaction->allow_commit(); + // Main savepoint reached. + upgrade_main_savepoint(true, 2022020200.06); + } + + // Finally, drop fields from question table. + if ($oldversion < 2022020200.07) { // Define fields to be dropped from questions. $table = new xmldb_table('question'); @@ -3976,7 +4144,7 @@ privatefiles,moodle|/user/files.php'; } // Main savepoint reached. - upgrade_main_savepoint(true, 2022020200.03); + upgrade_main_savepoint(true, 2022020200.07); } if ($oldversion < 2022021100.01) { diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index 958fcbec092..2ac6b755393 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -1275,140 +1275,6 @@ function upgrade_calendar_override_events_fix(stdClass $info, bool $output = tru return $return; } -/** - * Split question table in 2 new tables: - * - * question_bank_entries - * question_versions - * - * Move the random questions records to the following table: - * question_set_reference - * - * Move the question related records from quiz_slots table to: - * question_reference - * - * Move the tag related data from quiz_slot_tags to: - * question_references - * - * For more information: https://moodle.org/mod/forum/discuss.php?d=417599#p1688163 - */ -function upgrade_migrate_question_table(): void { - global $DB; - - // Maximum size of array. - $maxlength = 30000; - - // Array of question_versions objects. - $questionversions = []; - - // Array of question_set_references objects. - $questionsetreferences = []; - - // The actual update/insert done with multiple DB access, so we do it in a transaction. - $transaction = $DB->start_delegated_transaction(); - - // Count all questions to be migrated (for progress bar). - $total = $DB->count_records('question'); - $pbar = new progress_bar('migratequestions', 1000, true); - $i = 0; - // Get all records in question table, we dont need the subquestions, just regular questions and random questions. - $questions = $DB->get_recordset('question'); - foreach ($questions as $question) { - upgrade_set_timeout(60); - // Populate table question_bank_entries. - $questionbankentry = new \stdClass(); - $questionbankentry->questioncategoryid = $question->category; - $questionbankentry->idnumber = $question->idnumber; - $questionbankentry->ownerid = $question->createdby; - // Insert a question_bank_entries record here as the id is required to populate other tables. - $questionbankentry->id = $DB->insert_record('question_bank_entries', $questionbankentry); - - // Create question_versions records to be added. - $questionversion = new \stdClass(); - $questionversion->questionbankentryid = $questionbankentry->id; - $questionversion->questionid = $question->id; - $questionstatus = \core_question\local\bank\question_version_status::QUESTION_STATUS_READY; - if ((int)$question->hidden === 1) { - $questionstatus = \core_question\local\bank\question_version_status::QUESTION_STATUS_HIDDEN; - } - $questionversion->status = $questionstatus; - $questionversions[] = $questionversion; - - // Insert the records if the array limit is reached. - if (count($questionversions) >= $maxlength) { - $DB->insert_records('question_versions', $questionversions); - $questionversions = []; - } - - // Create question_set_references records to be added. - // Only if the question type is random and the question is used in a quiz. - if ($question->qtype === 'random') { - $quizslots = $DB->get_records('quiz_slots', ['questionid' => $question->id]); - foreach ($quizslots as $quizslot) { - $questionsetreference = new \stdClass(); - $cm = get_coursemodule_from_instance('quiz', $quizslot->quizid); - $questionsetreference->usingcontextid = context_module::instance($cm->id)->id; - $questionsetreference->component = 'mod_quiz'; - $questionsetreference->questionarea = 'slot'; - $questionsetreference->itemid = $quizslot->id; - $catcontext = $DB->get_field('question_categories', 'contextid', ['id' => $question->category]); - $questionsetreference->questionscontextid = $catcontext; - // Migration of the slot tags and filter identifiers from slot table to filtercondition. - $filtercondition = new stdClass(); - $filtercondition->questioncategoryid = $question->category; - $filtercondition->includingsubcategories = $quizslot->includingsubcategories; - $tags = $DB->get_records('quiz_slot_tags', ['slotid' => $quizslot->id]); - $tagstrings = []; - foreach ($tags as $tag) { - $tagstrings [] = "{$tag->id},{$tag->name}"; - } - if (!empty($tagstrings)) { - $filtercondition->tags = $tagstrings; - } - $questionsetreference->filtercondition = json_encode($filtercondition); - - $questionsetreferences[] = $questionsetreference; - - // Insert the records if the array limit is reached. - if (count($questionsetreferences) >= $maxlength) { - $DB->insert_records('question_set_references', $questionsetreferences); - $questionsetreferences = []; - } - } - } - // Update progress. - $i++; - $pbar->update($i, $total, "Migrating questions - $i/$total."); - } - $questions->close(); - - // Insert the remaining question_versions records. - if ($questionversions) { - $DB->insert_records('question_versions', $questionversions); - } - - // Insert the remaining question_set_references records. - if ($questionsetreferences) { - $DB->insert_records('question_set_references', $questionsetreferences); - } - - // Create question_references record for each question. - // Except if qtype is random. That case is handled by question_set_reference. - $sql = "INSERT INTO {question_references} - (usingcontextid, component, questionarea, itemid, questionbankentryid) - SELECT c.id, 'mod_quiz', 'slot', qs.id, qv.questionbankentryid - FROM {question} q - JOIN {question_versions} qv ON q.id = qv.questionid - JOIN {quiz_slots} qs ON q.id = qs.questionid - JOIN {modules} m ON m.name = 'quiz' - JOIN {course_modules} cm ON cm.module = m.id AND cm.instance = qs.quizid - JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = " . CONTEXT_MODULE . " - WHERE q.qtype <> 'random'"; - $DB->execute($sql); - - $transaction->allow_commit(); -} - /** * Add a new item at the end of the usermenu. *