MDL-62781 question/privacy: fix tests with CodeRunner is installed
[moodle.git] / lib / dtl / database_importer.php
blobb14bad7141622437fb42be3844691615e940037b
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 = array('changedcolumns' => false); // Column types may be fixed by transfer.
114 if ($this->check_schema and $errors = $this->manager->check_database_schema($this->schema, $options)) {
115 $details = '';
116 foreach ($errors as $table=>$items) {
117 $details .= '<div>'.get_string('table').' '.$table.':';
118 $details .= '<ul>';
119 foreach ($items as $item) {
120 $details .= '<li>'.$item.'</li>';
122 $details .= '</ul></div>';
124 throw new dbtransfer_exception('importschemaexception', $details);
126 if ($this->transactionmode == 'allinone') {
127 $this->transaction = $this->mdb->start_delegated_transaction();
132 * Callback function. Should be called only once per table import operation,
133 * before any table changes are made. It will delete all table data.
135 * @throws dbtransfer_exception an unknown table import is attempted
136 * @throws ddl_table_missing_exception if the table is missing
138 * @param string $tablename - the name of the table that will be imported
139 * @param string $schemaHash - the hash of the xmldb_table schema of the table
140 * @return void
142 public function begin_table_import($tablename, $schemaHash) {
143 if ($this->transactionmode == 'pertable') {
144 $this->transaction = $this->mdb->start_delegated_transaction();
146 if (!$table = $this->schema->getTable($tablename)) {
147 throw new dbtransfer_exception('unknowntableexception', $tablename);
149 if ($schemaHash != $table->getHash()) {
150 throw new dbtransfer_exception('differenttableexception', $tablename);
152 // this should not happen, unless someone drops tables after import started
153 if (!$this->manager->table_exists($table)) {
154 throw new ddl_table_missing_exception($tablename);
156 $this->mdb->delete_records($tablename);
160 * Callback function. Should be called only once per table import operation,
161 * after all table changes are made. It will reset table sequences if any.
162 * @param string $tablename
163 * @return void
165 public function finish_table_import($tablename) {
166 $table = $this->schema->getTable($tablename);
167 $fields = $table->getFields();
168 foreach ($fields as $field) {
169 if ($field->getSequence()) {
170 $this->manager->reset_sequence($tablename);
171 return;
174 if ($this->transactionmode == 'pertable') {
175 $this->transaction->allow_commit();
180 * Callback function. Should be called only once database per import
181 * operation, after all database changes are made. It will commit changes.
182 * @return void
184 public function finish_database_import() {
185 if ($this->transactionmode == 'allinone') {
186 $this->transaction->allow_commit();
191 * Callback function. Should be called only once per record import operation, only
192 * between @see begin_table_import and @see finish_table_import calls.
193 * It will insert table data.
195 * @throws dml_exception if data insert operation failed
197 * @param string $tablename - the name of the table in which data will be
198 * imported
199 * @param object $data - data object (fields and values will be inserted
200 * into table)
201 * @return void
203 public function import_table_data($tablename, $data) {
204 $this->mdb->import_record($tablename, $data);
208 * Common import method
209 * @return void
211 public function import_database() {
212 // implement in subclasses