Commit | Line | Data |
---|---|---|
94b63295 | 1 | <?php |
f33e1ed4 | 2 | |
94b63295 | 3 | // This file is part of Moodle - http://moodle.org/ |
4 | // | |
5 | // Moodle is free software: you can redistribute it and/or modify | |
6 | // it under the terms of the GNU General Public License as published by | |
7 | // the Free Software Foundation, either version 3 of the License, or | |
8 | // (at your option) any later version. | |
9 | // | |
10 | // Moodle is distributed in the hope that it will be useful, | |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | // GNU General Public License for more details. | |
14 | // | |
15 | // You should have received a copy of the GNU General Public License | |
16 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
f33e1ed4 | 17 | |
f33e1ed4 | 18 | |
94b63295 | 19 | /** |
20 | * Dtabase manager instance is responsible for all database structure | |
21 | * modifications. | |
22 | * | |
23 | * @package moodlecore | |
24 | * @subpackage DDL | |
25 | * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com | |
26 | * 2001-3001 Eloy Lafuente (stronk7) http://contiento.com | |
27 | * 2008 Petr Skoda http://skodak.org | |
28 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
29 | */ | |
f33e1ed4 | 30 | |
94b63295 | 31 | /** |
32 | * Dtabase manager instance is responsible for all database structure | |
33 | * modifications. It is using db specific generators to find out | |
34 | * the correct SQL syntax to do that. | |
35 | */ | |
f33e1ed4 | 36 | class database_manager { |
37 | ||
38 | protected $mdb; | |
39 | public $generator; // public because XMLDB editor needs to access it | |
40 | ||
41 | /** | |
42 | * Creates new database manager | |
43 | * @param object moodle_database instance | |
44 | */ | |
45 | public function __construct($mdb, $generator) { | |
46 | global $CFG; | |
47 | ||
48 | $this->mdb = $mdb; | |
49 | $this->generator = $generator; | |
50 | } | |
51 | ||
25d854c6 | 52 | /** |
53 | * Release all resources | |
54 | */ | |
55 | public function dispose() { | |
56 | if ($this->generator) { | |
57 | $this->generator->dispose(); | |
58 | $this->generator = null; | |
59 | } | |
eee5d9bb | 60 | $this->mdb = null; |
25d854c6 | 61 | } |
62 | ||
f33e1ed4 | 63 | /** |
eee5d9bb | 64 | * This function will execute an array of SQL commands. |
65 | * | |
66 | * @exception ddl_exception if error found | |
f33e1ed4 | 67 | * |
68 | * @param array $sqlarr array of sql statements to execute | |
eee5d9bb | 69 | * @return void |
f33e1ed4 | 70 | */ |
eee5d9bb | 71 | protected function execute_sql_arr(array $sqlarr) { |
f33e1ed4 | 72 | foreach ($sqlarr as $sql) { |
eee5d9bb | 73 | $this->execute_sql($sql); |
f33e1ed4 | 74 | } |
f33e1ed4 | 75 | } |
76 | ||
77 | /** | |
eee5d9bb | 78 | * Execute a given sql command string |
f33e1ed4 | 79 | * |
eee5d9bb | 80 | * @exception ddl_exception if error found |
f33e1ed4 | 81 | * |
82 | * @param string $command The sql string you wish to be executed. | |
591ffe1a | 83 | * @return void |
f33e1ed4 | 84 | */ |
eee5d9bb | 85 | protected function execute_sql($sql) { |
86 | if (!$this->mdb->change_database_structure($sql)) { | |
591ffe1a | 87 | // in case driver does not throw exceptions yet ;-) |
88 | throw new ddl_change_structure_exception($this->mdb->get_last_error(), $sql); | |
f33e1ed4 | 89 | } |
f33e1ed4 | 90 | } |
91 | ||
92 | /** | |
a8cb94f6 | 93 | * Given one xmldb_table, check if it exists in DB (true/false) |
f33e1ed4 | 94 | * |
a8cb94f6 | 95 | * @param mixed the table to be searched (string name or xmldb_table instance) |
f33e1ed4 | 96 | * @return boolean true/false |
97 | */ | |
4ff402d6 | 98 | public function table_exists($table) { |
eee5d9bb | 99 | if (!is_string($table) and !($table instanceof xmldb_table)) { |
100 | throw new ddl_exception('ddlunknownerror', NULL, 'incorrect table parameter!'); | |
101 | } | |
4ff402d6 | 102 | return $this->generator->table_exists($table); |
f33e1ed4 | 103 | } |
104 | ||
be415e95 | 105 | /** |
106 | * Reset a sequence to the id field of a table. | |
107 | * @param string $table name of table | |
108 | * @return success | |
109 | */ | |
110 | public function reset_sequence($table) { | |
b1ca1387 | 111 | if (!is_string($table) and !($table instanceof xmldb_table)) { |
112 | throw new ddl_exception('ddlunknownerror', NULL, 'incorrect table parameter!'); | |
be415e95 | 113 | } |
114 | ||
115 | /// Check the table exists | |
116 | if (!$this->table_exists($table)) { | |
117 | throw new ddl_table_missing_exception($tablename); | |
118 | } | |
119 | ||
b1ca1387 | 120 | if (!$sqlarr = $this->generator->getResetSequenceSQL($table)) { |
121 | throw new ddl_exception('ddlunknownerror', null, 'table reset sequence sql not generated'); | |
122 | } | |
123 | ||
124 | $this->execute_sql_arr($sqlarr); | |
be415e95 | 125 | } |
126 | ||
f33e1ed4 | 127 | /** |
a8cb94f6 | 128 | * Given one xmldb_field, check if it exists in DB (true/false) |
f33e1ed4 | 129 | * |
a8cb94f6 | 130 | * @param mixed the table to be searched (string name or xmldb_table instance) |
131 | * @param mixed the field to be searched for (string name or xmldb_field instance) | |
f33e1ed4 | 132 | * @return boolean true/false |
133 | */ | |
134 | public function field_exists($table, $field) { | |
eee5d9bb | 135 | /// Calculate the name of the table |
136 | if (is_string($table)) { | |
137 | $tablename = $table; | |
138 | } else { | |
139 | $tablename = $table->getName(); | |
140 | } | |
f33e1ed4 | 141 | |
142 | /// Check the table exists | |
143 | if (!$this->table_exists($table)) { | |
eee5d9bb | 144 | throw new ddl_table_missing_exception($tablename); |
f33e1ed4 | 145 | } |
146 | ||
f33e1ed4 | 147 | if (is_string($field)) { |
148 | $fieldname = $field; | |
149 | } else { | |
150 | /// Calculate the name of the table | |
151 | $fieldname = $field->getName(); | |
152 | } | |
153 | ||
154 | /// Get list of fields in table | |
f713581b | 155 | $columns = $this->mdb->get_columns($tablename); |
f33e1ed4 | 156 | |
157 | $exists = array_key_exists($fieldname, $columns); | |
158 | ||
f33e1ed4 | 159 | return $exists; |
160 | } | |
161 | ||
162 | /** | |
eee5d9bb | 163 | * Given one xmldb_index, the function returns the name of the index in DB |
f33e1ed4 | 164 | * of false if it doesn't exist |
165 | * | |
eee5d9bb | 166 | * @param object $xmldb_table table to be searched |
167 | * @param object $xmldb_index the index to be searched | |
f33e1ed4 | 168 | * @return string index name of false |
169 | */ | |
eee5d9bb | 170 | public function find_index_name(xmldb_table $xmldb_table, xmldb_index $xmldb_index) { |
171 | /// Calculate the name of the table | |
172 | $tablename = $xmldb_table->getName(); | |
173 | ||
f33e1ed4 | 174 | /// Check the table exists |
eee5d9bb | 175 | if (!$this->table_exists($xmldb_table)) { |
176 | throw new ddl_table_missing_exception($tablename); | |
f33e1ed4 | 177 | } |
178 | ||
f33e1ed4 | 179 | /// Extract index columns |
180 | $indcolumns = $xmldb_index->getFields(); | |
181 | ||
f33e1ed4 | 182 | /// Get list of indexes in table |
183 | $indexes = $this->mdb->get_indexes($tablename); | |
184 | ||
185 | /// Iterate over them looking for columns coincidence | |
186 | foreach ($indexes as $indexname => $index) { | |
187 | $columns = $index['columns']; | |
188 | /// Check if index matchs queried index | |
189 | $diferences = array_merge(array_diff($columns, $indcolumns), array_diff($indcolumns, $columns)); | |
190 | /// If no diferences, we have find the index | |
191 | if (empty($diferences)) { | |
f33e1ed4 | 192 | return $indexname; |
193 | } | |
194 | } | |
195 | ||
196 | /// Arriving here, index not found | |
f33e1ed4 | 197 | return false; |
198 | } | |
199 | ||
200 | /** | |
a8cb94f6 | 201 | * Given one xmldb_index, check if it exists in DB (true/false) |
f33e1ed4 | 202 | * |
eee5d9bb | 203 | * @param object $xmldb_table the table to be searched |
204 | * @param object $xmldb_index the index to be searched for | |
f33e1ed4 | 205 | * @return boolean true/false |
206 | */ | |
eee5d9bb | 207 | public function index_exists(xmldb_table $xmldb_table, xmldb_index $xmldb_index) { |
208 | if (!$this->table_exists($xmldb_table)) { | |
209 | return false; | |
210 | } | |
211 | return ($this->find_index_name($xmldb_table, $xmldb_index) !== false); | |
f33e1ed4 | 212 | } |
213 | ||
214 | /** | |
a8cb94f6 | 215 | * Given one xmldb_field, the function returns the name of the check constraint in DB (if exists) |
f33e1ed4 | 216 | * of false if it doesn't exist. Note that XMLDB limits the number of check constrainst per field |
217 | * to 1 "enum-like" constraint. So, if more than one is returned, only the first one will be | |
218 | * retrieved by this funcion. | |
219 | * | |
2d2d79ef | 220 | * TODO: Moodle 2.1 - Drop find_check_constraint_name() |
221 | * | |
a8cb94f6 | 222 | * @param xmldb_table the table to be searched |
223 | * @param xmldb_field the field to be searched | |
6cef30f7 | 224 | * @return string check constraint name or false |
f33e1ed4 | 225 | */ |
eee5d9bb | 226 | public function find_check_constraint_name(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
f33e1ed4 | 227 | |
228 | /// Check the table exists | |
229 | if (!$this->table_exists($xmldb_table)) { | |
eee5d9bb | 230 | throw new ddl_table_missing_exception($xmldb_table->getName()); |
f33e1ed4 | 231 | } |
232 | ||
233 | /// Check the field exists | |
234 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 235 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); |
f33e1ed4 | 236 | } |
237 | ||
f33e1ed4 | 238 | /// Get list of check_constraints in table/field |
239 | $checks = false; | |
240 | if ($objchecks = $this->generator->getCheckConstraintsFromDB($xmldb_table, $xmldb_field)) { | |
241 | /// Get only the 1st element. Shouldn't be more than 1 under XMLDB | |
242 | $objcheck = array_shift($objchecks); | |
243 | if ($objcheck) { | |
244 | $checks = strtolower($objcheck->name); | |
245 | } | |
246 | } | |
247 | ||
248 | /// Arriving here, check not found | |
f33e1ed4 | 249 | return $checks; |
250 | } | |
251 | ||
252 | /** | |
a8cb94f6 | 253 | * Given one xmldb_field, check if it has a check constraint in DB |
f33e1ed4 | 254 | * |
5b49a67c | 255 | * TODO: Moodle 2.1 - Drop check_constraint_exists() |
256 | * | |
a8cb94f6 | 257 | * @param xmldb_table the table |
258 | * @param xmldb_field the field to be searched for any existing constraint | |
f33e1ed4 | 259 | * @return boolean true/false |
260 | */ | |
eee5d9bb | 261 | public function check_constraint_exists(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
f33e1ed4 | 262 | return ($this->find_check_constraint_name($xmldb_table, $xmldb_field) !== false); |
263 | } | |
264 | ||
265 | /** | |
266 | * This function IS NOT IMPLEMENTED. ONCE WE'LL BE USING RELATIONAL | |
267 | * INTEGRITY IT WILL BECOME MORE USEFUL. FOR NOW, JUST CALCULATE "OFFICIAL" | |
268 | * KEY NAMES WITHOUT ACCESSING TO DB AT ALL. | |
a8cb94f6 | 269 | * Given one xmldb_key, the function returns the name of the key in DB (if exists) |
f33e1ed4 | 270 | * of false if it doesn't exist |
271 | * | |
a8cb94f6 | 272 | * @param xmldb_table the table to be searched |
273 | * @param xmldb_key the key to be searched | |
f33e1ed4 | 274 | * @return string key name of false |
275 | */ | |
eee5d9bb | 276 | public function find_key_name(xmldb_table $xmldb_table, xmldb_key $xmldb_key) { |
6cef30f7 | 277 | |
f33e1ed4 | 278 | $keycolumns = $xmldb_key->getFields(); |
279 | ||
280 | /// Get list of keys in table | |
281 | /// first primaries (we aren't going to use this now, because the MetaPrimaryKeys is awful) | |
282 | ///TODO: To implement when we advance in relational integrity | |
283 | /// then uniques (note that Moodle, for now, shouldn't have any UNIQUE KEY for now, but unique indexes) | |
284 | ///TODO: To implement when we advance in relational integrity (note that AdoDB hasn't any MetaXXX for this. | |
285 | /// then foreign (note that Moodle, for now, shouldn't have any FOREIGN KEY for now, but indexes) | |
286 | ///TODO: To implement when we advance in relational integrity (note that AdoDB has one MetaForeignKeys() | |
287 | ///but it's far from perfect. | |
288 | /// TODO: To create the proper functions inside each generator to retrieve all the needed KEY info (name | |
289 | /// columns, reftable and refcolumns | |
290 | ||
291 | /// So all we do is to return the official name of the requested key without any confirmation!) | |
292 | /// One exception, harcoded primary constraint names | |
293 | if ($this->generator->primary_key_name && $xmldb_key->getType() == XMLDB_KEY_PRIMARY) { | |
294 | return $this->generator->primary_key_name; | |
295 | } else { | |
296 | /// Calculate the name suffix | |
297 | switch ($xmldb_key->getType()) { | |
298 | case XMLDB_KEY_PRIMARY: | |
299 | $suffix = 'pk'; | |
300 | break; | |
301 | case XMLDB_KEY_UNIQUE: | |
302 | $suffix = 'uk'; | |
303 | break; | |
304 | case XMLDB_KEY_FOREIGN_UNIQUE: | |
305 | case XMLDB_KEY_FOREIGN: | |
306 | $suffix = 'fk'; | |
307 | break; | |
308 | } | |
309 | /// And simply, return the oficial name | |
310 | return $this->generator->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), $suffix); | |
311 | } | |
312 | } | |
313 | ||
f33e1ed4 | 314 | /** |
4cacea3b | 315 | * This function will delete all tables found in XMLDB file from db |
f33e1ed4 | 316 | * |
317 | * @param $file full path to the XML file to be used | |
eee5d9bb | 318 | * @return void |
f33e1ed4 | 319 | */ |
eee5d9bb | 320 | public function delete_tables_from_xmldb_file($file) { |
f33e1ed4 | 321 | |
46293bd7 | 322 | $xmldb_file = new xmldb_file($file); |
f33e1ed4 | 323 | |
324 | if (!$xmldb_file->fileExists()) { | |
eee5d9bb | 325 | throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist'); |
f33e1ed4 | 326 | } |
327 | ||
328 | $loaded = $xmldb_file->loadXMLStructure(); | |
329 | $structure = $xmldb_file->getStructure(); | |
330 | ||
331 | if (!$loaded || !$xmldb_file->isLoaded()) { | |
332 | /// Show info about the error if we can find it | |
eee5d9bb | 333 | if ($structure) { |
f33e1ed4 | 334 | if ($errors = $structure->getAllErrors()) { |
eee5d9bb | 335 | throw new ddl_exception('ddlxmlfileerror', null, 'Errors found in XMLDB file: '. implode (', ', $errors)); |
f33e1ed4 | 336 | } |
337 | } | |
eee5d9bb | 338 | throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??'); |
f33e1ed4 | 339 | } |
340 | ||
341 | if ($xmldb_tables = $structure->getTables()) { | |
342 | foreach($xmldb_tables as $table) { | |
343 | if ($this->table_exists($table)) { | |
eee5d9bb | 344 | $this->drop_table($table); |
f33e1ed4 | 345 | } |
346 | } | |
347 | } | |
f33e1ed4 | 348 | } |
349 | ||
350 | /** | |
351 | * This function will drop the table passed as argument | |
352 | * and all the associated objects (keys, indexes, constaints, sequences, triggers) | |
353 | * will be dropped too. | |
354 | * | |
a8cb94f6 | 355 | * @param xmldb_table table object (just the name is mandatory) |
eee5d9bb | 356 | * @return void |
f33e1ed4 | 357 | */ |
eee5d9bb | 358 | public function drop_table(xmldb_table $xmldb_table) { |
f33e1ed4 | 359 | /// Check table exists |
360 | if (!$this->table_exists($xmldb_table)) { | |
eee5d9bb | 361 | throw new ddl_table_missing_exception($xmldb_table->getName()); |
f33e1ed4 | 362 | } |
363 | ||
364 | if (!$sqlarr = $this->generator->getDropTableSQL($xmldb_table)) { | |
eee5d9bb | 365 | throw new ddl_exception('ddlunknownerror', null, 'table drop sql not generated'); |
f33e1ed4 | 366 | } |
367 | ||
eee5d9bb | 368 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 369 | } |
370 | ||
371 | /** | |
67d5e9df | 372 | * Load an install.xml file, checking that it exists, and that the structure is OK. |
373 | * @param string $file the full path to the XMLDB file. | |
374 | * @return xmldb_file the loaded file. | |
f33e1ed4 | 375 | */ |
67d5e9df | 376 | private function load_xmldb_file($file) { |
46293bd7 | 377 | $xmldb_file = new xmldb_file($file); |
f33e1ed4 | 378 | |
379 | if (!$xmldb_file->fileExists()) { | |
eee5d9bb | 380 | throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist'); |
f33e1ed4 | 381 | } |
382 | ||
383 | $loaded = $xmldb_file->loadXMLStructure(); | |
384 | if (!$loaded || !$xmldb_file->isLoaded()) { | |
385 | /// Show info about the error if we can find it | |
386 | if ($structure =& $xmldb_file->getStructure()) { | |
387 | if ($errors = $structure->getAllErrors()) { | |
eee5d9bb | 388 | throw new ddl_exception('ddlxmlfileerror', null, 'Errors found in XMLDB file: '. implode (', ', $errors)); |
f33e1ed4 | 389 | } |
390 | } | |
eee5d9bb | 391 | throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??'); |
f33e1ed4 | 392 | } |
393 | ||
67d5e9df | 394 | return $xmldb_file; |
395 | } | |
396 | ||
397 | /** | |
398 | * This function will load one entire XMLDB file and call install_from_xmldb_structure. | |
399 | * | |
400 | * @param $file full path to the XML file to be used | |
401 | * @return void | |
402 | */ | |
403 | public function install_from_xmldb_file($file) { | |
404 | $xmldb_file = $this->load_xmldb_file($file); | |
405 | $xmldb_structure = $xmldb_file->getStructure(); | |
406 | $this->install_from_xmldb_structure($xmldb_structure); | |
407 | } | |
408 | ||
409 | /** | |
410 | * This function will load one entire XMLDB file and call install_from_xmldb_structure. | |
411 | * | |
412 | * @param $file full path to the XML file to be used | |
413 | * @param $tablename the name of the table. | |
a97c0dd5 EL |
414 | * @param bool $cachestructures boolean to decide if loaded xmldb structures can be safely cached |
415 | * useful for testunits loading the enormous main xml file hundred of times (100x) | |
67d5e9df | 416 | */ |
a97c0dd5 EL |
417 | public function install_one_table_from_xmldb_file($file, $tablename, $cachestructures = false) { |
418 | ||
419 | static $xmldbstructurecache = array(); // To store cached structures | |
420 | if (!empty($xmldbstructurecache) && array_key_exists($file, $xmldbstructurecache)) { | |
421 | $xmldb_structure = $xmldbstructurecache[$file]; | |
422 | } else { | |
423 | $xmldb_file = $this->load_xmldb_file($file); | |
424 | $xmldb_structure = $xmldb_file->getStructure(); | |
425 | if ($cachestructures) { | |
426 | $xmldbstructurecache[$file] = $xmldb_structure; | |
427 | } | |
428 | } | |
f33e1ed4 | 429 | |
67d5e9df | 430 | $targettable = $xmldb_structure->getTable($tablename); |
431 | if (is_null($targettable)) { | |
432 | throw new ddl_exception('ddlunknowntable', null, 'The table ' . $tablename . ' is not defined in file ' . $file); | |
433 | } | |
465d9695 | 434 | $targettable->setNext(NULL); |
435 | $targettable->setPrevious(NULL); | |
67d5e9df | 436 | |
437 | $tempstructure = new xmldb_structure('temp'); | |
438 | $tempstructure->addTable($targettable); | |
439 | $this->install_from_xmldb_structure($tempstructure); | |
0549c245 | 440 | } |
441 | ||
442 | /** | |
117bd748 | 443 | * This function will generate all the needed SQL statements, specific for each |
0549c245 | 444 | * RDBMS type and, finally, it will execute all those statements against the DB. |
445 | * | |
446 | * @param object $structure xmldb_structure object | |
447 | * @return void | |
448 | */ | |
449 | public function install_from_xmldb_structure($xmldb_structure) { | |
450 | ||
f33e1ed4 | 451 | if (!$sqlarr = $this->generator->getCreateStructureSQL($xmldb_structure)) { |
eee5d9bb | 452 | return; // nothing to do |
f33e1ed4 | 453 | } |
eee5d9bb | 454 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 455 | } |
456 | ||
457 | /** | |
458 | * This function will create the table passed as argument with all its | |
459 | * fields/keys/indexes/sequences, everything based in the XMLDB object | |
460 | * | |
a8cb94f6 | 461 | * @param xmldb_table table object (full specs are required) |
eee5d9bb | 462 | * @return void |
f33e1ed4 | 463 | */ |
eee5d9bb | 464 | public function create_table(xmldb_table $xmldb_table) { |
f33e1ed4 | 465 | /// Check table doesn't exist |
466 | if ($this->table_exists($xmldb_table)) { | |
eee5d9bb | 467 | throw new ddl_exception('ddltablealreadyexists', $xmldb_table->getName()); |
f33e1ed4 | 468 | } |
469 | ||
470 | if (!$sqlarr = $this->generator->getCreateTableSQL($xmldb_table)) { | |
eee5d9bb | 471 | throw new ddl_exception('ddlunknownerror', null, 'table create sql not generated'); |
f33e1ed4 | 472 | } |
eee5d9bb | 473 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 474 | } |
475 | ||
476 | /** | |
477 | * This function will create the temporary table passed as argument with all its | |
478 | * fields/keys/indexes/sequences, everything based in the XMLDB object | |
479 | * | |
f94f10ca | 480 | * If table already exists ddl_exception will be thrown, please make sure |
b922e86b | 481 | * the table name does not collide with existing normal table! |
4cacea3b | 482 | * |
a8cb94f6 | 483 | * @param xmldb_table table object (full specs are required) |
eee5d9bb | 484 | * @return void |
f33e1ed4 | 485 | */ |
eee5d9bb | 486 | public function create_temp_table(xmldb_table $xmldb_table) { |
b922e86b | 487 | |
f94f10ca | 488 | // Check table doesn't exist |
4ff402d6 | 489 | if ($this->table_exists($xmldb_table)) { |
f94f10ca | 490 | throw new ddl_exception('ddltablealreadyexists', $xmldb_table->getName()); |
f33e1ed4 | 491 | } |
492 | ||
b922e86b | 493 | if (!$sqlarr = $this->generator->getCreateTempTableSQL($xmldb_table)) { |
eee5d9bb | 494 | throw new ddl_exception('ddlunknownerror', null, 'temp table create sql not generated'); |
f33e1ed4 | 495 | } |
496 | ||
eee5d9bb | 497 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 498 | } |
499 | ||
b922e86b | 500 | /** |
501 | * This function will drop the temporary table passed as argument with all its | |
502 | * fields/keys/indexes/sequences, everything based in the XMLDB object | |
503 | * | |
504 | * It is recommended to drop temp table when not used anymore. | |
505 | * | |
506 | * @param xmldb_table table object | |
eee5d9bb | 507 | * @return void |
b922e86b | 508 | */ |
eee5d9bb | 509 | public function drop_temp_table(xmldb_table $xmldb_table) { |
b922e86b | 510 | |
511 | /// Check table doesn't exist | |
4ff402d6 | 512 | if (!$this->table_exists($xmldb_table)) { |
eee5d9bb | 513 | throw new ddl_table_missing_exception($xmldb_table->getName()); |
b922e86b | 514 | } |
515 | ||
516 | if (!$sqlarr = $this->generator->getDropTempTableSQL($xmldb_table)) { | |
eee5d9bb | 517 | throw new ddl_exception('ddlunknownerror', null, 'temp table drop sql not generated'); |
b922e86b | 518 | } |
519 | ||
eee5d9bb | 520 | $this->execute_sql_arr($sqlarr); |
b922e86b | 521 | } |
522 | ||
f33e1ed4 | 523 | /** |
524 | * This function will rename the table passed as argument | |
525 | * Before renaming the index, the function will check it exists | |
526 | * | |
a8cb94f6 | 527 | * @param xmldb_table table object (just the name is mandatory) |
f33e1ed4 | 528 | * @param string new name of the index |
eee5d9bb | 529 | * @return void |
f33e1ed4 | 530 | */ |
eee5d9bb | 531 | public function rename_table(xmldb_table $xmldb_table, $newname) { |
f33e1ed4 | 532 | /// Check newname isn't empty |
533 | if (!$newname) { | |
eee5d9bb | 534 | throw new ddl_exception('ddlunknownerror', null, 'newname can not be empty'); |
f33e1ed4 | 535 | } |
536 | ||
a8cb94f6 | 537 | $check = new xmldb_table($newname); |
f33e1ed4 | 538 | |
539 | /// Check table already renamed | |
f33e1ed4 | 540 | if (!$this->table_exists($xmldb_table)) { |
eee5d9bb | 541 | if ($this->table_exists($check)) { |
542 | throw new ddl_exception('ddlunknownerror', null, 'table probably already renamed'); | |
543 | } else { | |
544 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
545 | } | |
f33e1ed4 | 546 | } |
547 | ||
548 | /// Check new table doesn't exist | |
549 | if ($this->table_exists($check)) { | |
eee5d9bb | 550 | throw new ddl_exception('ddltablealreadyexists', $xmldb_table->getName(), 'can not rename table'); |
f33e1ed4 | 551 | } |
552 | ||
553 | if (!$sqlarr = $this->generator->getRenameTableSQL($xmldb_table, $newname)) { | |
eee5d9bb | 554 | throw new ddl_exception('ddlunknownerror', null, 'table rename sql not generated'); |
f33e1ed4 | 555 | } |
556 | ||
eee5d9bb | 557 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 558 | } |
559 | ||
560 | ||
561 | /** | |
562 | * This function will add the field to the table passed as arguments | |
563 | * | |
a8cb94f6 | 564 | * @param xmldb_table table object (just the name is mandatory) |
565 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 566 | * @return void |
f33e1ed4 | 567 | */ |
eee5d9bb | 568 | public function add_field(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
f33e1ed4 | 569 | /// Check the field doesn't exist |
570 | if ($this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 571 | throw new ddl_exception('ddlfieldalreadyexists', $xmldb_field->getName()); |
f33e1ed4 | 572 | } |
573 | ||
574 | /// If NOT NULL and no default given (we ask the generator about the | |
575 | /// *real* default that will be used) check the table is empty | |
576 | if ($xmldb_field->getNotNull() && $this->generator->getDefaultValue($xmldb_field) === NULL && $this->mdb->count_records($xmldb_table->getName())) { | |
eee5d9bb | 577 | throw new ddl_exception('ddlunknownerror', null, 'Field ' . $xmldb_table->getName() . '->' . $xmldb_field->getName() . |
578 | ' cannot be added. Not null fields added to non empty tables require default value. Create skipped'); | |
f33e1ed4 | 579 | } |
580 | ||
581 | if (!$sqlarr = $this->generator->getAddFieldSQL($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 582 | throw new ddl_exception('ddlunknownerror', null, 'addfield sql not generated'); |
f33e1ed4 | 583 | } |
eee5d9bb | 584 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 585 | } |
586 | ||
587 | /** | |
588 | * This function will drop the field from the table passed as arguments | |
589 | * | |
a8cb94f6 | 590 | * @param xmldb_table table object (just the name is mandatory) |
591 | * @param xmldb_field field object (just the name is mandatory) | |
eee5d9bb | 592 | * @return void |
f33e1ed4 | 593 | */ |
eee5d9bb | 594 | public function drop_field(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
595 | if (!$this->table_exists($xmldb_table)) { | |
596 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 597 | } |
f33e1ed4 | 598 | /// Check the field exists |
599 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 600 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); |
f33e1ed4 | 601 | } |
2baf1380 | 602 | /// Check for dependencies in the DB before performing any action |
603 | $this->check_field_dependencies($xmldb_table, $xmldb_field); | |
f33e1ed4 | 604 | |
605 | if (!$sqlarr = $this->generator->getDropFieldSQL($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 606 | throw new ddl_exception('ddlunknownerror', null, 'drop_field sql not generated'); |
f33e1ed4 | 607 | } |
608 | ||
eee5d9bb | 609 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 610 | } |
611 | ||
612 | /** | |
613 | * This function will change the type of the field in the table passed as arguments | |
614 | * | |
a8cb94f6 | 615 | * @param xmldb_table table object (just the name is mandatory) |
616 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 617 | * @return void |
f33e1ed4 | 618 | */ |
eee5d9bb | 619 | public function change_field_type(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
620 | if (!$this->table_exists($xmldb_table)) { | |
621 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 622 | } |
f33e1ed4 | 623 | /// Check the field exists |
624 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 625 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); |
f33e1ed4 | 626 | } |
d274f209 | 627 | /// Check for dependencies in the DB before performing any action |
628 | $this->check_field_dependencies($xmldb_table, $xmldb_field); | |
f33e1ed4 | 629 | |
630 | if (!$sqlarr = $this->generator->getAlterFieldSQL($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 631 | return; // probably nothing to do |
f33e1ed4 | 632 | } |
633 | ||
eee5d9bb | 634 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 635 | } |
636 | ||
637 | /** | |
638 | * This function will change the precision of the field in the table passed as arguments | |
639 | * | |
a8cb94f6 | 640 | * @param xmldb_table table object (just the name is mandatory) |
641 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 642 | * @return void |
f33e1ed4 | 643 | */ |
eee5d9bb | 644 | public function change_field_precision(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
f33e1ed4 | 645 | /// Just a wrapper over change_field_type. Does exactly the same processing |
eee5d9bb | 646 | $this->change_field_type($xmldb_table, $xmldb_field); |
f33e1ed4 | 647 | } |
648 | ||
649 | /** | |
650 | * This function will change the unsigned/signed of the field in the table passed as arguments | |
651 | * | |
a8cb94f6 | 652 | * @param xmldb_table table object (just the name is mandatory) |
653 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 654 | * @return void |
f33e1ed4 | 655 | */ |
eee5d9bb | 656 | public function change_field_unsigned(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
f33e1ed4 | 657 | /// Just a wrapper over change_field_type. Does exactly the same processing |
eee5d9bb | 658 | $this->change_field_type($xmldb_table, $xmldb_field); |
f33e1ed4 | 659 | } |
660 | ||
661 | /** | |
662 | * This function will change the nullability of the field in the table passed as arguments | |
663 | * | |
a8cb94f6 | 664 | * @param xmldb_table table object (just the name is mandatory) |
665 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 666 | * @return void |
f33e1ed4 | 667 | */ |
eee5d9bb | 668 | public function change_field_notnull(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
f33e1ed4 | 669 | /// Just a wrapper over change_field_type. Does exactly the same processing |
eee5d9bb | 670 | $this->change_field_type($xmldb_table, $xmldb_field); |
f33e1ed4 | 671 | } |
672 | ||
673 | /** | |
2baf1380 | 674 | * This function will change the default of the field in the table passed as arguments |
675 | * One null value in the default field means delete the default | |
f33e1ed4 | 676 | * |
a8cb94f6 | 677 | * @param xmldb_table table object (just the name is mandatory) |
678 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 679 | * @return void |
f33e1ed4 | 680 | */ |
2baf1380 | 681 | public function change_field_default(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
eee5d9bb | 682 | if (!$this->table_exists($xmldb_table)) { |
683 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 684 | } |
f33e1ed4 | 685 | /// Check the field exists |
686 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 687 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); |
f33e1ed4 | 688 | } |
d274f209 | 689 | /// Check for dependencies in the DB before performing any action |
690 | $this->check_field_dependencies($xmldb_table, $xmldb_field); | |
f33e1ed4 | 691 | |
2baf1380 | 692 | if (!$sqlarr = $this->generator->getModifyDefaultSQL($xmldb_table, $xmldb_field)) { |
eee5d9bb | 693 | return; //Empty array = nothing to do = no error |
f33e1ed4 | 694 | } |
695 | ||
eee5d9bb | 696 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 697 | } |
698 | ||
699 | /** | |
2baf1380 | 700 | * This function will drop the existing enum of the field in the table passed as arguments |
701 | * | |
702 | * TODO: Moodle 2.1 - Drop drop_enum_from_field() | |
f33e1ed4 | 703 | * |
a8cb94f6 | 704 | * @param xmldb_table table object (just the name is mandatory) |
705 | * @param xmldb_field field object (full specs are required) | |
eee5d9bb | 706 | * @return void |
f33e1ed4 | 707 | */ |
2baf1380 | 708 | public function drop_enum_from_field(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { |
eee5d9bb | 709 | if (!$this->table_exists($xmldb_table)) { |
710 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 711 | } |
f33e1ed4 | 712 | /// Check the field exists |
713 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 714 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); |
f33e1ed4 | 715 | } |
716 | ||
2baf1380 | 717 | if (!$this->check_constraint_exists($xmldb_table, $xmldb_field)) { |
718 | debugging('Enum for ' . $xmldb_table->getName() . '->' . $xmldb_field->getName() . | |
719 | ' does not exist. Delete skipped', DEBUG_DEVELOPER); | |
720 | return; //Enum does not exist, nothing to delete | |
721 | } | |
722 | ||
723 | if (!$sqlarr = $this->generator->getDropEnumSQL($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 724 | return; //Empty array = nothing to do = no error |
f33e1ed4 | 725 | } |
726 | ||
eee5d9bb | 727 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 728 | } |
729 | ||
730 | /** | |
731 | * This function will rename the field in the table passed as arguments | |
732 | * Before renaming the field, the function will check it exists | |
733 | * | |
a8cb94f6 | 734 | * @param xmldb_table table object (just the name is mandatory) |
735 | * @param xmldb_field index object (full specs are required) | |
f33e1ed4 | 736 | * @param string new name of the field |
eee5d9bb | 737 | * @return void |
f33e1ed4 | 738 | */ |
eee5d9bb | 739 | public function rename_field(xmldb_table $xmldb_table, xmldb_field $xmldb_field, $newname) { |
740 | if (empty($newname)) { | |
741 | throw new ddl_exception('ddlunknownerror', null, 'newname can not be empty'); | |
f33e1ed4 | 742 | } |
743 | ||
eee5d9bb | 744 | if (!$this->table_exists($xmldb_table)) { |
745 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 746 | } |
747 | ||
748 | /// Check the field exists | |
749 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
eee5d9bb | 750 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); |
f33e1ed4 | 751 | } |
752 | ||
753 | /// Check we have included full field specs | |
754 | if (!$xmldb_field->getType()) { | |
eee5d9bb | 755 | throw new ddl_exception('ddlunknownerror', null, |
756 | 'Field ' . $xmldb_table->getName() . '->' . $xmldb_field->getName() . | |
757 | ' must contain full specs. Rename skipped'); | |
f33e1ed4 | 758 | } |
759 | ||
760 | /// Check field isn't id. Renaming over that field is not allowed | |
761 | if ($xmldb_field->getName() == 'id') { | |
eee5d9bb | 762 | throw new ddl_exception('ddlunknownerror', null, |
763 | 'Field ' . $xmldb_table->getName() . '->' . $xmldb_field->getName() . | |
764 | ' cannot be renamed. Rename skipped'); | |
f33e1ed4 | 765 | } |
766 | ||
767 | if (!$sqlarr = $this->generator->getRenameFieldSQL($xmldb_table, $xmldb_field, $newname)) { | |
eee5d9bb | 768 | return; //Empty array = nothing to do = no error |
f33e1ed4 | 769 | } |
770 | ||
eee5d9bb | 771 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 772 | } |
773 | ||
2baf1380 | 774 | /** |
775 | * This function will check, for the given table and field, if there there is any dependency | |
776 | * preventing the field to be modified. It's used by all the public methods that perform any | |
777 | * DDL change on fields, throwing one ddl_dependency_exception if dependencies are found | |
778 | */ | |
779 | private function check_field_dependencies(xmldb_table $xmldb_table, xmldb_field $xmldb_field) { | |
780 | ||
781 | /// Check the table exists | |
782 | if (!$this->table_exists($xmldb_table)) { | |
783 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
784 | } | |
785 | ||
786 | /// Check the field exists | |
787 | if (!$this->field_exists($xmldb_table, $xmldb_field)) { | |
788 | throw new ddl_field_missing_exception($xmldb_field->getName(), $xmldb_table->getName()); | |
789 | } | |
790 | ||
791 | /// Check the field isn't in use by any index in the table | |
792 | if ($indexes = $this->mdb->get_indexes($xmldb_table->getName(), false)) { | |
793 | foreach ($indexes as $indexname => $index) { | |
794 | $columns = $index['columns']; | |
795 | if (in_array($xmldb_field->getName(), $columns)) { | |
796 | throw new ddl_dependency_exception('column', $xmldb_table->getName() . '->' . $xmldb_field->getName(), | |
797 | 'index', $indexname . ' (' . implode(', ', $columns) . ')'); | |
798 | } | |
799 | } | |
800 | } | |
801 | } | |
802 | ||
f33e1ed4 | 803 | /** |
804 | * This function will create the key in the table passed as arguments | |
805 | * | |
a8cb94f6 | 806 | * @param xmldb_table table object (just the name is mandatory) |
807 | * @param xmldb_key index object (full specs are required) | |
eee5d9bb | 808 | * @return void |
f33e1ed4 | 809 | */ |
eee5d9bb | 810 | public function add_key(xmldb_table $xmldb_table, xmldb_key $xmldb_key) { |
f33e1ed4 | 811 | |
812 | if ($xmldb_key->getType() == XMLDB_KEY_PRIMARY) { // Prevent PRIMARY to be added (only in create table, being serious :-P) | |
eee5d9bb | 813 | throw new ddl_exception('ddlunknownerror', null, 'Primary Keys can be added at table create time only'); |
f33e1ed4 | 814 | } |
815 | ||
816 | if (!$sqlarr = $this->generator->getAddKeySQL($xmldb_table, $xmldb_key)) { | |
eee5d9bb | 817 | return; //Empty array = nothing to do = no error |
f33e1ed4 | 818 | } |
819 | ||
eee5d9bb | 820 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 821 | } |
822 | ||
823 | /** | |
824 | * This function will drop the key in the table passed as arguments | |
825 | * | |
a8cb94f6 | 826 | * @param xmldb_table table object (just the name is mandatory) |
827 | * @param xmldb_key key object (full specs are required) | |
eee5d9bb | 828 | * @return void |
f33e1ed4 | 829 | */ |
eee5d9bb | 830 | public function drop_key(xmldb_table $xmldb_table, xmldb_key $xmldb_key) { |
f33e1ed4 | 831 | if ($xmldb_key->getType() == XMLDB_KEY_PRIMARY) { // Prevent PRIMARY to be dropped (only in drop table, being serious :-P) |
eee5d9bb | 832 | throw new ddl_exception('ddlunknownerror', null, 'Primary Keys can be deleted at table drop time only'); |
f33e1ed4 | 833 | } |
834 | ||
eee5d9bb | 835 | if (!$sqlarr = $this->generator->getDropKeySQL($xmldb_table, $xmldb_key)) { |
836 | return; //Empty array = nothing to do = no error | |
f33e1ed4 | 837 | } |
838 | ||
eee5d9bb | 839 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 840 | } |
841 | ||
842 | /** | |
843 | * This function will rename the key in the table passed as arguments | |
844 | * Experimental. Shouldn't be used at all in normal installation/upgrade! | |
845 | * | |
a8cb94f6 | 846 | * @param xmldb_table table object (just the name is mandatory) |
847 | * @param xmldb_key key object (full specs are required) | |
f33e1ed4 | 848 | * @param string new name of the key |
eee5d9bb | 849 | * @return void |
f33e1ed4 | 850 | */ |
eee5d9bb | 851 | public function rename_key(xmldb_table $xmldb_table, xmldb_key $xmldb_key, $newname) { |
f33e1ed4 | 852 | debugging('rename_key() is one experimental feature. You must not use it in production!', DEBUG_DEVELOPER); |
853 | ||
f33e1ed4 | 854 | /// Check newname isn't empty |
855 | if (!$newname) { | |
eee5d9bb | 856 | throw new ddl_exception('ddlunknownerror', null, 'newname can not be empty'); |
f33e1ed4 | 857 | } |
858 | ||
859 | if (!$sqlarr = $this->generator->getRenameKeySQL($xmldb_table, $xmldb_key, $newname)) { | |
eee5d9bb | 860 | throw new ddl_exception('ddlunknownerror', null, 'Some DBs do not support key renaming (MySQL, PostgreSQL, MsSQL). Rename skipped'); |
f33e1ed4 | 861 | } |
862 | ||
eee5d9bb | 863 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 864 | } |
865 | ||
866 | /** | |
867 | * This function will create the index in the table passed as arguments | |
868 | * Before creating the index, the function will check it doesn't exists | |
869 | * | |
a8cb94f6 | 870 | * @param xmldb_table table object (just the name is mandatory) |
871 | * @param xmldb_index index object (full specs are required) | |
eee5d9bb | 872 | * @return void |
f33e1ed4 | 873 | */ |
eee5d9bb | 874 | public function add_index($xmldb_table, $xmldb_intex) { |
875 | if (!$this->table_exists($xmldb_table)) { | |
876 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 877 | } |
878 | ||
879 | /// Check index doesn't exist | |
880 | if ($this->index_exists($xmldb_table, $xmldb_intex)) { | |
eee5d9bb | 881 | throw new ddl_exception('ddlunknownerror', null, |
882 | 'Index ' . $xmldb_table->getName() . '->' . $xmldb_intex->getName() . | |
883 | ' already exists. Create skipped'); | |
f33e1ed4 | 884 | } |
885 | ||
886 | if (!$sqlarr = $this->generator->getAddIndexSQL($xmldb_table, $xmldb_intex)) { | |
eee5d9bb | 887 | throw new ddl_exception('ddlunknownerror', null, 'add_index sql not generated'); |
f33e1ed4 | 888 | } |
889 | ||
eee5d9bb | 890 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 891 | } |
892 | ||
893 | /** | |
894 | * This function will drop the index in the table passed as arguments | |
895 | * Before dropping the index, the function will check it exists | |
896 | * | |
a8cb94f6 | 897 | * @param xmldb_table table object (just the name is mandatory) |
898 | * @param xmldb_index index object (full specs are required) | |
eee5d9bb | 899 | * @return void |
f33e1ed4 | 900 | */ |
eee5d9bb | 901 | public function drop_index($xmldb_table, $xmldb_intex) { |
902 | if (!$this->table_exists($xmldb_table)) { | |
903 | throw new ddl_table_missing_exception($xmldb_table->getName()); | |
f33e1ed4 | 904 | } |
905 | ||
906 | /// Check index exists | |
907 | if (!$this->index_exists($xmldb_table, $xmldb_intex)) { | |
eee5d9bb | 908 | throw new ddl_exception('ddlunknownerror', null, |
909 | 'Index ' . $xmldb_table->getName() . '->' . $xmldb_intex->getName() . | |
5cada363 | 910 | ' does not exist. Drop skipped'); |
f33e1ed4 | 911 | } |
912 | ||
913 | if (!$sqlarr = $this->generator->getDropIndexSQL($xmldb_table, $xmldb_intex)) { | |
eee5d9bb | 914 | throw new ddl_exception('ddlunknownerror', null, 'drop_index sql not generated'); |
f33e1ed4 | 915 | } |
916 | ||
eee5d9bb | 917 | $this->execute_sql_arr($sqlarr); |
f33e1ed4 | 918 | } |
919 | ||
920 | /** | |
921 | * This function will rename the index in the table passed as arguments | |
922 | * Before renaming the index, the function will check it exists | |
923 | * Experimental. Shouldn't be used at all! | |
924 | * | |
a8cb94f6 | 925 | * @param xmldb_table table object (just the name is mandatory) |
926 | * @param xmldb_index index object (full specs are required) | |
f33e1ed4 | 927 | * @param string new name of the index |
eee5d9bb | 928 | * @return void |
f33e1ed4 | 929 | */ |
eee5d9bb | 930 | public function rename_index($xmldb_table, $xmldb_intex, $newname) { |
f33e1ed4 | 931 | debugging('rename_index() is one experimental feature. You must not use it in production!', DEBUG_DEVELOPER); |
932 | ||
f33e1ed4 | 933 | /// Check newname isn't empty |
934 | if (!$newname) { | |
eee5d9bb | 935 | throw new ddl_exception('ddlunknownerror', null, 'newname can not be empty'); |
f33e1ed4 | 936 | } |
937 | ||
938 | /// Check index exists | |
939 | if (!$this->index_exists($xmldb_table, $xmldb_intex)) { | |
eee5d9bb | 940 | throw new ddl_exception('ddlunknownerror', null, |
941 | 'Index ' . $xmldb_table->getName() . '->' . $xmldb_intex->getName() . | |
942 | ' does not exist. Rename skipped'); | |
f33e1ed4 | 943 | } |
944 | ||
945 | if (!$sqlarr = $this->generator->getRenameIndexSQL($xmldb_table, $xmldb_intex, $newname)) { | |
eee5d9bb | 946 | throw new ddl_exception('ddlunknownerror', null, 'Some DBs do not support index renaming (MySQL). Rename skipped'); |
f33e1ed4 | 947 | } |
948 | ||
eee5d9bb | 949 | $this->execute_sql_arr($sqlarr); |
950 | } | |
f4caf0df | 951 | |
952 | /** | |
953 | * Reads the install.xml files for Moodle core and modules and returns an array of | |
954 | * xmldb_structure object with xmldb_table from these files. | |
955 | * @return xmldb_structure schema from install.xml files | |
956 | */ | |
957 | public function get_install_xml_schema() { | |
958 | global $CFG; | |
ebf20af0 | 959 | require_once($CFG->libdir.'/adminlib.php'); |
f4caf0df | 960 | |
961 | $schema = new xmldb_structure('export'); | |
962 | $schema->setVersion($CFG->version); | |
963 | $dbdirs = get_db_directories(); | |
964 | foreach ($dbdirs as $dbdir) { | |
965 | $xmldb_file = new xmldb_file($dbdir.'/install.xml'); | |
966 | if (!$xmldb_file->fileExists() or !$xmldb_file->loadXMLStructure()) { | |
967 | continue; | |
968 | } | |
969 | $structure = $xmldb_file->getStructure(); | |
970 | $tables = $structure->getTables(); | |
971 | foreach ($tables as $table) { | |
972 | $table->setPrevious(null); | |
973 | $table->setNext(null); | |
974 | $schema->addTable($table); | |
975 | } | |
976 | } | |
977 | return $schema; | |
978 | } | |
117bd748 | 979 | |
f4caf0df | 980 | /** |
981 | * Checks the database schema against a schema specified by an xmldb_structure object | |
982 | * @param xmldb_structure $schema export schema describing all known tables | |
117bd748 | 983 | * @return array keyed by table name with array of difference messages as values |
f4caf0df | 984 | */ |
985 | public function check_database_schema(xmldb_structure $schema) { | |
986 | $errors = array(); | |
987 | ||
988 | $dbtables = $this->mdb->get_tables(); | |
989 | $tables = $schema->getTables(); | |
990 | ||
991 | //TODO: maybe add several levels error/warning | |
992 | ||
993 | // make sure that current and schema tables match exactly | |
994 | foreach ($tables as $table) { | |
995 | $tablename = $table->getName(); | |
996 | if (empty($dbtables[$tablename])) { | |
997 | if (!isset($errors[$tablename])) { | |
998 | $errors[$tablename] = array(); | |
999 | } | |
1000 | $errors[$tablename][] = "Table $tablename is missing in database."; //TODO: localize | |
1001 | continue; | |
1002 | } | |
1003 | ||
1004 | // a) check for required fields | |
f713581b | 1005 | $dbfields = $this->mdb->get_columns($tablename); |
f4caf0df | 1006 | $fields = $table->getFields(); |
1007 | foreach ($fields as $field) { | |
1008 | $fieldname = $field->getName(); | |
1009 | if (empty($dbfields[$fieldname])) { | |
1010 | if (!isset($errors[$tablename])) { | |
1011 | $errors[$tablename] = array(); | |
1012 | } | |
1013 | $errors[$tablename][] = "Field $fieldname is missing in table $tablename."; //TODO: localize | |
1014 | } | |
1015 | unset($dbfields[$fieldname]); | |
1016 | } | |
1017 | ||
1018 | // b) check for extra fields (indicates unsupported hacks) - modify install.xml if you want the script to continue ;-) | |
1019 | foreach ($dbfields as $fieldname=>$info) { | |
1020 | if (!isset($errors[$tablename])) { | |
1021 | $errors[$tablename] = array(); | |
1022 | } | |
1023 | $errors[$tablename][] = "Field $fieldname is not expected in table $tablename."; //TODO: localize | |
1024 | } | |
1025 | unset($dbtables[$tablename]); | |
1026 | } | |
1027 | ||
17da2e6f | 1028 | // look for unsupported tables - local custom tables should be in /local/xxxx/db/install.xml ;-) |
f4caf0df | 1029 | // if there is no prefix, we can not say if tale is ours :-( |
1030 | if ($this->generator->prefix !== '') { | |
1031 | foreach ($dbtables as $tablename=>$unused) { | |
1032 | if (strpos($tablename, 'pma_') === 0) { | |
1033 | // ignore phpmyadmin tables for now | |
1034 | continue; | |
1035 | } | |
1036 | if (strpos($tablename, 'test') === 0) { | |
1037 | // ignore broken results of unit tests | |
1038 | continue; | |
1039 | } | |
1040 | if (!isset($errors[$tablename])) { | |
1041 | $errors[$tablename] = array(); | |
1042 | } | |
1043 | $errors[$tablename][] = "Table $tablename is not expected."; //TODO: localize | |
1044 | } | |
1045 | } | |
1046 | ||
1047 | return $errors; | |
1048 | } | |
eee5d9bb | 1049 | } |