Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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.
13
//
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/>.
16
 
17
/**
18
 * General database importer class
19
 *
20
 * @package    core_dtl
21
 * @copyright  2008 Andrei Bautu
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
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.
41
 */
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
54
     */
55
    protected $check_schema;
56
    /** @var string How to use transactions. */
57
    protected $transactionmode = 'allinone';
58
    /** @var moodle_transaction Transaction object */
59
    protected $transaction;
60
 
61
    /**
62
     * Object constructor.
63
     *
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).
69
     */
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;
75
    }
76
 
77
    /**
78
     * How to use transactions during the import.
79
     * @param string $mode 'pertable', 'allinone' or 'none'.
80
     */
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);
84
        }
85
        $this->transactionmode = $mode;
86
    }
87
 
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
92
     *
93
     * @throws dbtransfer_exception if any checking (e.g. database schema, Moodle
94
     * version) fails
95
     *
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
99
     */
100
    public function begin_database_import($version, $timestamp) {
101
        global $CFG;
102
 
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);
106
        }
107
 
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);
111
        }
112
 
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
117
        ];
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>';
125
                }
126
                $details .= '</ul></div>';
127
            }
128
            throw new dbtransfer_exception('importschemaexception', $details);
129
        }
130
        if ($this->transactionmode == 'allinone') {
131
            $this->transaction = $this->mdb->start_delegated_transaction();
132
        }
133
    }
134
 
135
    /**
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.
138
     *
139
     * @throws dbtransfer_exception an unknown table import is attempted
140
     * @throws ddl_table_missing_exception if the table is missing
141
     *
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
145
     */
146
    public function begin_table_import($tablename, $schemaHash) {
147
        if ($this->transactionmode == 'pertable') {
148
            $this->transaction = $this->mdb->start_delegated_transaction();
149
        }
150
        if (!$table = $this->schema->getTable($tablename)) {
151
            throw new dbtransfer_exception('unknowntableexception', $tablename);
152
        }
153
        if ($schemaHash != $table->getHash()) {
154
            throw new dbtransfer_exception('differenttableexception', $tablename);
155
        }
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);
159
        }
160
        $this->mdb->delete_records($tablename);
161
    }
162
 
163
    /**
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
168
     */
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;
176
            }
177
        }
178
        if ($this->transactionmode == 'pertable') {
179
            $this->transaction->allow_commit();
180
        }
181
    }
182
 
183
    /**
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
187
     */
188
    public function finish_database_import() {
189
        if ($this->transactionmode == 'allinone') {
190
            $this->transaction->allow_commit();
191
        }
192
    }
193
 
194
    /**
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.
198
     *
199
     * @throws dml_exception if data insert operation failed
200
     *
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
206
     */
207
    public function import_table_data($tablename, $data) {
208
        $this->mdb->import_record($tablename, $data);
209
    }
210
 
211
    /**
212
     * Common import method
213
     * @return void
214
     */
215
    public function import_database() {
216
        // implement in subclasses
217
    }
218
}