Merge branch 'MDL-77272-main' of https://github.com/SergioComeron/moodle
[moodle.git] / lib / dtl / database_importer.php
blob0306461d335f520c20c0e709de82007f2ef66575
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * General database importer class
20 * @package core_dtl
21 * @copyright 2008 Andrei Bautu
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') || die();
27 /**
28 * Base class for database import operations. This class implements
29 * basic callbacks for import operations and defines the @see import_database
30 * method as a common method for all importers. In general, subclasses will
31 * override import_database and call other methods in appropriate moments.
32 * Between a single pair of calls to @see begin_database_import and
33 * @see finish_database_import, multiple non-overlapping pairs of calls may
34 * be made to @see begin_table_import and @see finish_database_import for
35 * different tables.
36 * Between one pair of calls to @see begin_table_import and
37 * @see finish_database_import multiple calls may be made to
38 * @see import_table_data for the same table.
39 * This class can be used directly, if the standard control flow (defined above)
40 * is respected.
42 class database_importer {
43 /** @var moodle_database Connection to the target database (a @see moodle_database object). */
44 protected $mdb;
45 /** @var database_manager Database manager of the target database (a @see database_manager object). */
46 protected $manager;
47 /** @var xmldb_structure Target database schema in XMLDB format (a @see xmldb_structure object). */
48 protected $schema;
49 /**
50 * Boolean flag - whether or not to check that XML database schema matches
51 * the RDBMS database schema before importing (used by
52 * @see begin_database_import).
53 * @var bool
55 protected $check_schema;
56 /** @var string How to use transactions. */
57 protected $transactionmode = 'allinone';
58 /** @var moodle_transaction Transaction object */
59 protected $transaction;
61 /**
62 * Object constructor.
64 * @param moodle_database $mdb Connection to the target database (a
65 * @see moodle_database object). Use null to use the current $DB connection.
66 * @param boolean $check_schema - whether or not to check that XML database
67 * schema matches the RDBMS database schema before importing (inside
68 * @see begin_database_import).
70 public function __construct(moodle_database $mdb, $check_schema=true) {
71 $this->mdb = $mdb;
72 $this->manager = $mdb->get_manager();
73 $this->schema = $this->manager->get_install_xml_schema();
74 $this->check_schema = $check_schema;
77 /**
78 * How to use transactions during the import.
79 * @param string $mode 'pertable', 'allinone' or 'none'.
81 public function set_transaction_mode($mode) {
82 if (!in_array($mode, array('pertable', 'allinone', 'none'))) {
83 throw new coding_exception('Unknown transaction mode', $mode);
85 $this->transactionmode = $mode;
88 /**
89 * Callback function. Should be called only once database per import
90 * operation, before any database changes are made. It will check the database
91 * schema if @see check_schema is true
93 * @throws dbtransfer_exception if any checking (e.g. database schema, Moodle
94 * version) fails
96 * @param float $version the version of the system which generated the data
97 * @param string $timestamp the timestamp of the data (in ISO 8601) format.
98 * @return void
100 public function begin_database_import($version, $timestamp) {
101 global $CFG;
103 if (!$this->mdb->get_tables()) {
104 // No tables present yet, time to create all tables.
105 $this->manager->install_from_xmldb_structure($this->schema);
108 if (round($version, 2) !== round($CFG->version, 2)) { // version might be in decimal format too
109 $a = (object)array('schemaver'=>$version, 'currentver'=>$CFG->version);
110 throw new dbtransfer_exception('importversionmismatchexception', $a);
113 $options = [
114 'changedcolumns' => false, // Column types may be fixed by transfer.
115 'missingindexes' => false, // No need to worry about indexes for transfering data.
116 'extraindexes' => false
118 if ($this->check_schema and $errors = $this->manager->check_database_schema($this->schema, $options)) {
119 $details = '';
120 foreach ($errors as $table=>$items) {
121 $details .= '<div>'.get_string('table').' '.$table.':';
122 $details .= '<ul>';
123 foreach ($items as $item) {
124 $details .= '<li>'.$item.'</li>';
126 $details .= '</ul></div>';
128 throw new dbtransfer_exception('importschemaexception', $details);
130 if ($this->transactionmode == 'allinone') {
131 $this->transaction = $this->mdb->start_delegated_transaction();
136 * Callback function. Should be called only once per table import operation,
137 * before any table changes are made. It will delete all table data.
139 * @throws dbtransfer_exception an unknown table import is attempted
140 * @throws ddl_table_missing_exception if the table is missing
142 * @param string $tablename - the name of the table that will be imported
143 * @param string $schemaHash - the hash of the xmldb_table schema of the table
144 * @return void
146 public function begin_table_import($tablename, $schemaHash) {
147 if ($this->transactionmode == 'pertable') {
148 $this->transaction = $this->mdb->start_delegated_transaction();
150 if (!$table = $this->schema->getTable($tablename)) {
151 throw new dbtransfer_exception('unknowntableexception', $tablename);
153 if ($schemaHash != $table->getHash()) {
154 throw new dbtransfer_exception('differenttableexception', $tablename);
156 // this should not happen, unless someone drops tables after import started
157 if (!$this->manager->table_exists($table)) {
158 throw new ddl_table_missing_exception($tablename);
160 $this->mdb->delete_records($tablename);
164 * Callback function. Should be called only once per table import operation,
165 * after all table changes are made. It will reset table sequences if any.
166 * @param string $tablename
167 * @return void
169 public function finish_table_import($tablename) {
170 $table = $this->schema->getTable($tablename);
171 $fields = $table->getFields();
172 foreach ($fields as $field) {
173 if ($field->getSequence()) {
174 $this->manager->reset_sequence($tablename);
175 return;
178 if ($this->transactionmode == 'pertable') {
179 $this->transaction->allow_commit();
184 * Callback function. Should be called only once database per import
185 * operation, after all database changes are made. It will commit changes.
186 * @return void
188 public function finish_database_import() {
189 if ($this->transactionmode == 'allinone') {
190 $this->transaction->allow_commit();
195 * Callback function. Should be called only once per record import operation, only
196 * between @see begin_table_import and @see finish_table_import calls.
197 * It will insert table data.
199 * @throws dml_exception if data insert operation failed
201 * @param string $tablename - the name of the table in which data will be
202 * imported
203 * @param object $data - data object (fields and values will be inserted
204 * into table)
205 * @return void
207 public function import_table_data($tablename, $data) {
208 $this->mdb->import_record($tablename, $data);
212 * Common import method
213 * @return void
215 public function import_database() {
216 // implement in subclasses