Bumping version for 1.8.9 release. Thinking we might have to go to x.x.10 for the...
[moodle.git] / lib / ddllib.php
blobf849aff54bc01fada4d5b9e26c61bafe07e22791
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 2001-3001 Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12 // //
13 // This program is free software; you can redistribute it and/or modify //
14 // it under the terms of the GNU General Public License as published by //
15 // the Free Software Foundation; either version 2 of the License, or //
16 // (at your option) any later version. //
17 // //
18 // This program is distributed in the hope that it will be useful, //
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21 // GNU General Public License for more details: //
22 // //
23 // http://www.gnu.org/copyleft/gpl.html //
24 // //
25 ///////////////////////////////////////////////////////////////////////////
27 // This library includes all the required functions used to handle the DB
28 // structure (DDL) independently of the underlying RDBMS in use. All the functions
29 // rely on the XMLDBDriver classes to be able to generate the correct SQL
30 // syntax needed by each DB.
32 // To define any structure to be created we'll use the schema defined
33 // by the XMLDB classes, for tables, fields, indexes, keys and other
34 // statements instead of direct handling of SQL sentences.
36 // This library should be used, exclusively, by the installation and
37 // upgrade process of Moodle.
39 // For further documentation, visit http://docs.moodle.org/en/DDL_functions
41 /// Add required XMLDB constants
42 require_once($CFG->libdir . '/xmldb/classes/XMLDBConstants.php');
44 /// Add main XMLDB Generator
45 require_once($CFG->libdir . '/xmldb/classes/generators/XMLDBGenerator.class.php');
47 /// Add required XMLDB DB classes
48 require_once($CFG->libdir . '/xmldb/classes/XMLDBObject.class.php');
49 require_once($CFG->libdir . '/xmldb/classes/XMLDBFile.class.php');
50 require_once($CFG->libdir . '/xmldb/classes/XMLDBStructure.class.php');
51 require_once($CFG->libdir . '/xmldb/classes/XMLDBTable.class.php');
52 require_once($CFG->libdir . '/xmldb/classes/XMLDBField.class.php');
53 require_once($CFG->libdir . '/xmldb/classes/XMLDBKey.class.php');
54 require_once($CFG->libdir . '/xmldb/classes/XMLDBIndex.class.php');
55 require_once($CFG->libdir . '/xmldb/classes/XMLDBStatement.class.php');
57 /// Based on $CFG->dbtype, add the proper generator class
58 if (!file_exists($CFG->libdir . '/xmldb/classes/generators/' . $CFG->dbtype . '/' . $CFG->dbtype . '.class.php')) {
59 error ('DB Type: ' . $CFG->dbtype . ' not supported by XMLDDB');
61 require_once($CFG->libdir . '/xmldb/classes/generators/' . $CFG->dbtype . '/' . $CFG->dbtype . '.class.php');
64 /// Add other libraries
65 require_once($CFG->libdir . '/xmlize.php');
66 /**
67 * Add a new field to a table, or modify an existing one (if oldfield is defined).
69 * WARNING: This function is deprecated and will be removed in future versions.
70 * Please use XMLDB (see http://docs.moodle.org/en/Development:DDL_functions ).
72 * Warning: Please be careful on primary keys, as this function will eat auto_increments
74 * @uses $CFG
75 * @uses $db
76 * @param string $table the name of the table to modify. (Without the prefix.)
77 * @param string $oldfield If changing an existing column, the name of that column.
78 * @param string $field The name of the column at the end of the operation.
79 * @param string $type The type of the column at the end of the operation. TEXT, VARCHAR, CHAR, INTEGER, REAL, or TINYINT
80 * @param string $size The size of that column type. As in VARCHAR($size), or INTEGER($size).
81 * @param string $signed For numeric column types, whether that column is 'signed' or 'unsigned'.
82 * @param string $default The new default value for the column.
83 * @param string $null 'not null', or '' to allow nulls.
84 * @param string $after Which column to insert this one after. Not supported on Postgres.
86 * @return boolean Wheter the operation succeeded.
88 function table_column($table, $oldfield, $field, $type='integer', $size='10',
89 $signed='unsigned', $default='0', $null='not null', $after='') {
90 global $CFG, $db, $empty_rs_cache;
92 if (!empty($empty_rs_cache[$table])) { // Clear the recordset cache because it's out of date
93 unset($empty_rs_cache[$table]);
96 switch (strtolower($CFG->dbtype)) {
98 case 'mysql':
99 case 'mysqlt':
101 switch (strtolower($type)) {
102 case 'text':
103 $type = 'TEXT';
104 $signed = '';
105 break;
106 case 'integer':
107 $type = 'INTEGER('. $size .')';
108 break;
109 case 'varchar':
110 $type = 'VARCHAR('. $size .')';
111 $signed = '';
112 break;
113 case 'char':
114 $type = 'CHAR('. $size .')';
115 $signed = '';
116 break;
119 if (!empty($oldfield)) {
120 $operation = 'CHANGE '. $oldfield .' '. $field;
121 } else {
122 $operation = 'ADD '. $field;
125 $default = 'DEFAULT \''. $default .'\'';
127 if (!empty($after)) {
128 $after = 'AFTER `'. $after .'`';
131 return execute_sql('ALTER TABLE '. $CFG->prefix . $table .' '. $operation .' '. $type .' '. $signed .' '. $default .' '. $null .' '. $after);
133 case 'postgres7': // From Petri Asikainen
134 //Check db-version
135 $dbinfo = $db->ServerInfo();
136 $dbver = substr($dbinfo['version'],0,3);
138 //to prevent conflicts with reserved words
139 $realfield = '"'. $field .'"';
140 $field = '"'. $field .'_alter_column_tmp"';
141 $oldfield = '"'. $oldfield .'"';
143 switch (strtolower($type)) {
144 case 'tinyint':
145 case 'integer':
146 if ($size <= 4) {
147 $type = 'INT2';
149 if ($size <= 10) {
150 $type = 'INT';
152 if ($size > 10) {
153 $type = 'INT8';
155 break;
156 case 'varchar':
157 $type = 'VARCHAR('. $size .')';
158 break;
159 case 'char':
160 $type = 'CHAR('. $size .')';
161 $signed = '';
162 break;
165 $default = '\''. $default .'\'';
167 //After is not implemented in postgesql
168 //if (!empty($after)) {
169 // $after = "AFTER '$after'";
172 //Use transactions
173 execute_sql('BEGIN');
175 //Always use temporary column
176 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ADD COLUMN '. $field .' '. $type);
177 //Add default values
178 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .'='. $default);
181 if ($dbver >= '7.3') {
182 // modifying 'not null' is posible before 7.3
183 //update default values to table
184 if (strtoupper($null) == 'NOT NULL') {
185 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .'='. $default .' WHERE '. $field .' IS NULL');
186 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $null);
187 } else {
188 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' DROP NOT NULL');
192 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET DEFAULT '. $default);
194 if ( $oldfield != '""' ) {
196 // We are changing the type of a column. This may require doing some casts...
197 $casting = '';
198 $oldtype = column_type($table, $oldfield);
199 $newtype = column_type($table, $field);
201 // Do we need a cast?
202 if($newtype == 'N' && $oldtype == 'C') {
203 $casting = 'CAST(CAST('.$oldfield.' AS TEXT) AS REAL)';
205 else if($newtype == 'I' && $oldtype == 'C') {
206 $casting = 'CAST(CAST('.$oldfield.' AS TEXT) AS INTEGER)';
208 else {
209 $casting = $oldfield;
212 // Run the update query, casting as necessary
213 execute_sql('UPDATE '. $CFG->prefix . $table .' SET '. $field .' = '. $casting);
214 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' DROP COLUMN '. $oldfield);
217 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' RENAME COLUMN '. $field .' TO '. $realfield);
219 return execute_sql('COMMIT');
221 default:
222 switch (strtolower($type)) {
223 case 'integer':
224 $type = 'INTEGER';
225 break;
226 case 'varchar':
227 $type = 'VARCHAR';
228 break;
231 $default = 'DEFAULT \''. $default .'\'';
233 if (!empty($after)) {
234 $after = 'AFTER '. $after;
237 if (!empty($oldfield)) {
238 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' RENAME COLUMN '. $oldfield .' '. $field);
239 } else {
240 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ADD COLUMN '. $field .' '. $type);
243 execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $null);
244 return execute_sql('ALTER TABLE '. $CFG->prefix . $table .' ALTER COLUMN '. $field .' SET '. $default);
249 * Given one XMLDBTable, check if it exists in DB (true/false)
251 * @param XMLDBTable table to be searched for
252 * @return boolean true/false
254 function table_exists($table) {
256 global $CFG, $db;
258 $exists = true;
260 /// Do this function silenty (to avoid output in install/upgrade process)
261 $olddbdebug = $db->debug;
262 $db->debug = false;
264 /// Load the needed generator
265 $classname = 'XMLDB' . $CFG->dbtype;
266 $generator = new $classname();
267 $generator->setPrefix($CFG->prefix);
268 /// Calculate the name of the table
269 $tablename = $generator->getTableName($table, false);
271 /// Search such tablename in DB
272 $metatables = $db->MetaTables();
273 $metatables = array_flip($metatables);
274 $metatables = array_change_key_case($metatables, CASE_LOWER);
275 if (!array_key_exists($tablename, $metatables)) {
276 $exists = false;
279 /// Re-set original debug
280 $db->debug = $olddbdebug;
282 return $exists;
286 * Given one XMLDBField, check if it exists in DB (true/false)
288 * @uses, $db
289 * @param XMLDBTable the table
290 * @param XMLDBField the field to be searched for
291 * @return boolean true/false
293 function field_exists($table, $field) {
295 global $CFG, $db;
297 $exists = true;
299 /// Do this function silenty (to avoid output in install/upgrade process)
300 $olddbdebug = $db->debug;
301 $db->debug = false;
303 /// Check the table exists
304 if (!table_exists($table)) {
305 $db->debug = $olddbdebug; //Re-set original $db->debug
306 return false;
309 /// Load the needed generator
310 $classname = 'XMLDB' . $CFG->dbtype;
311 $generator = new $classname();
312 $generator->setPrefix($CFG->prefix);
313 /// Calculate the name of the table
314 $tablename = $generator->getTableName($table, false);
316 /// Get list of fields in table
317 $fields = null;
318 if ($fields = $db->MetaColumns($tablename)) {
319 $fields = array_change_key_case($fields, CASE_LOWER);
322 if (!array_key_exists($field->getName(), $fields)) {
323 $exists = false;
326 /// Re-set original debug
327 $db->debug = $olddbdebug;
329 return $exists;
333 * Given one XMLDBIndex, check if it exists in DB (true/false)
335 * @uses, $db
336 * @param XMLDBTable the table
337 * @param XMLDBIndex the index to be searched for
338 * @return boolean true/false
340 function index_exists($table, $index) {
342 global $CFG, $db;
344 $exists = true;
346 /// Do this function silenty (to avoid output in install/upgrade process)
347 $olddbdebug = $db->debug;
348 $db->debug = false;
350 /// Wrap over find_index_name to see if the index exists
351 if (!find_index_name($table, $index)) {
352 $exists = false;
355 /// Re-set original debug
356 $db->debug = $olddbdebug;
358 return $exists;
362 * This function IS NOT IMPLEMENTED. ONCE WE'LL BE USING RELATIONAL
363 * INTEGRITY IT WILL BECOME MORE USEFUL. FOR NOW, JUST CALCULATE "OFFICIAL"
364 * KEY NAMES WITHOUT ACCESSING TO DB AT ALL.
365 * Given one XMLDBKey, the function returns the name of the key in DB (if exists)
366 * of false if it doesn't exist
368 * @uses, $db
369 * @param XMLDBTable the table to be searched
370 * @param XMLDBKey the key to be searched
371 * @return string key name of false
373 function find_key_name($table, $xmldb_key) {
375 global $CFG, $db;
377 /// Extract key columns
378 $keycolumns = $xmldb_key->getFields();
380 /// Get list of keys in table
381 /// first primaries (we aren't going to use this now, because the MetaPrimaryKeys is awful)
382 ///TODO: To implement when we advance in relational integrity
383 /// then uniques (note that Moodle, for now, shouldn't have any UNIQUE KEY for now, but unique indexes)
384 ///TODO: To implement when we advance in relational integrity (note that AdoDB hasn't any MetaXXX for this.
385 /// then foreign (note that Moodle, for now, shouldn't have any FOREIGN KEY for now, but indexes)
386 ///TODO: To implement when we advance in relational integrity (note that AdoDB has one MetaForeignKeys()
387 ///but it's far from perfect.
388 /// TODO: To create the proper functions inside each generator to retrieve all the needed KEY info (name
389 /// columns, reftable and refcolumns
391 /// So all we do is to return the official name of the requested key without any confirmation!)
392 $classname = 'XMLDB' . $CFG->dbtype;
393 $generator = new $classname();
394 $generator->setPrefix($CFG->prefix);
395 /// One exception, harcoded primary constraint names
396 if ($generator->primary_key_name && $xmldb_key->getType() == XMLDB_KEY_PRIMARY) {
397 return $generator->primary_key_name;
398 } else {
399 /// Calculate the name suffix
400 switch ($xmldb_key->getType()) {
401 case XMLDB_KEY_PRIMARY:
402 $suffix = 'pk';
403 break;
404 case XMLDB_KEY_UNIQUE:
405 $suffix = 'uk';
406 break;
407 case XMLDB_KEY_FOREIGN_UNIQUE:
408 case XMLDB_KEY_FOREIGN:
409 $suffix = 'fk';
410 break;
412 /// And simply, return the oficial name
413 return $generator->getNameForObject($table->getName(), implode(', ', $xmldb_key->getFields()), $suffix);
418 * Given one XMLDBIndex, the function returns the name of the index in DB (if exists)
419 * of false if it doesn't exist
421 * @uses, $db
422 * @param XMLDBTable the table to be searched
423 * @param XMLDBIndex the index to be searched
424 * @return string index name of false
426 function find_index_name($table, $index) {
428 global $CFG, $db;
430 /// Do this function silenty (to avoid output in install/upgrade process)
431 $olddbdebug = $db->debug;
432 $db->debug = false;
434 /// Extract index columns
435 $indcolumns = $index->getFields();
437 /// Check the table exists
438 if (!table_exists($table)) {
439 $db->debug = $olddbdebug; //Re-set original $db->debug
440 return false;
443 /// Load the needed generator
444 $classname = 'XMLDB' . $CFG->dbtype;
445 $generator = new $classname();
446 $generator->setPrefix($CFG->prefix);
447 /// Calculate the name of the table
448 $tablename = $generator->getTableName($table, false);
450 /// Get list of indexes in table
451 $indexes = null;
452 if ($indexes = $db->MetaIndexes($tablename)) {
453 $indexes = array_change_key_case($indexes, CASE_LOWER);
456 /// Iterate over them looking for columns coincidence
457 if ($indexes) {
458 foreach ($indexes as $indexname => $index) {
459 $columns = $index['columns'];
460 /// Lower case column names
461 $columns = array_flip($columns);
462 $columns = array_change_key_case($columns, CASE_LOWER);
463 $columns = array_flip($columns);
464 /// Check if index matchs queried index
465 $diferences = array_merge(array_diff($columns, $indcolumns), array_diff($indcolumns, $columns));
466 /// If no diferences, we have find the index
467 if (empty($diferences)) {
468 $db->debug = $olddbdebug; //Re-set original $db->debug
469 return $indexname;
473 /// Arriving here, index not found
474 $db->debug = $olddbdebug; //Re-set original $db->debug
475 return false;
479 * Given one XMLDBTable, the function returns the name of its sequence in DB (if exists)
480 * of false if it doesn't exist
482 * @param XMLDBTable the table to be searched
483 * @return string sequence name of false
485 function find_sequence_name($table) {
487 global $CFG, $db;
489 $sequencename = false;
491 /// Do this function silenty (to avoid output in install/upgrade process)
492 $olddbdebug = $db->debug;
493 $db->debug = false;
495 if (strtolower(get_class($table)) != 'xmldbtable') {
496 $db->debug = $olddbdebug; //Re-set original $db->debug
497 return false;
500 /// Check table exists
501 if (!table_exists($table)) {
502 debugging('Table ' . $table->getName() . ' do not exist. Sequence not found', DEBUG_DEVELOPER);
503 $db->debug = $olddbdebug; //Re-set original $db->debug
504 return false; //Table doesn't exist, nothing to do
507 $sequencename = $table->getSequenceFromDB($CFG->dbtype, $CFG->prefix);
509 $db->debug = $olddbdebug; //Re-set original $db->debug
510 return $sequencename;
514 * This function will load one entire XMLDB file, generating all the needed
515 * SQL statements, specific for each RDBMS ($CFG->dbtype) and, finally, it
516 * will execute all those statements against the DB.
518 * @uses $CFG, $db
519 * @param $file full path to the XML file to be used
520 * @return boolean (true on success, false on error)
522 function install_from_xmldb_file($file) {
524 global $CFG, $db;
526 $status = true;
529 $xmldb_file = new XMLDBFile($file);
531 if (!$xmldb_file->fileExists()) {
532 return false;
535 $loaded = $xmldb_file->loadXMLStructure();
536 if (!$loaded || !$xmldb_file->isLoaded()) {
537 /// Show info about the error if we can find it
538 if ($structure =& $xmldb_file->getStructure()) {
539 if ($errors = $structure->getAllErrors()) {
540 notify('Errors found in XMLDB file: '. implode (', ', $errors));
543 return false;
546 $structure = $xmldb_file->getStructure();
548 if (!$sqlarr = $structure->getCreateStructureSQL($CFG->dbtype, $CFG->prefix, false)) {
549 return true; //Empty array = nothing to do = no error
552 return execute_sql_arr($sqlarr);
556 * This function will create the table passed as argument with all its
557 * fields/keys/indexes/sequences, everything based in the XMLDB object
559 * @uses $CFG, $db
560 * @param XMLDBTable table object (full specs are required)
561 * @param boolean continue to specify if must continue on error (true) or stop (false)
562 * @param boolean feedback to specify to show status info (true) or not (false)
563 * @return boolean true on success, false on error
565 function create_table($table, $continue=true, $feedback=true) {
567 global $CFG, $db;
569 $status = true;
571 if (strtolower(get_class($table)) != 'xmldbtable') {
572 return false;
575 /// Check table doesn't exist
576 if (table_exists($table)) {
577 debugging('Table ' . $table->getName() . ' exists. Create skipped', DEBUG_DEVELOPER);
578 return true; //Table exists, nothing to do
581 if(!$sqlarr = $table->getCreateTableSQL($CFG->dbtype, $CFG->prefix, false)) {
582 return true; //Empty array = nothing to do = no error
585 return execute_sql_arr($sqlarr, $continue, $feedback);
589 * This function will drop the table passed as argument
590 * and all the associated objects (keys, indexes, constaints, sequences, triggers)
591 * will be dropped too.
593 * @uses $CFG, $db
594 * @param XMLDBTable table object (just the name is mandatory)
595 * @param boolean continue to specify if must continue on error (true) or stop (false)
596 * @param boolean feedback to specify to show status info (true) or not (false)
597 * @return boolean true on success, false on error
599 function drop_table($table, $continue=true, $feedback=true) {
601 global $CFG, $db;
603 $status = true;
605 if (strtolower(get_class($table)) != 'xmldbtable') {
606 return false;
609 /// Check table exists
610 if (!table_exists($table)) {
611 debugging('Table ' . $table->getName() . ' do not exist. Delete skipped', DEBUG_DEVELOPER);
612 return true; //Table don't exist, nothing to do
615 if(!$sqlarr = $table->getDropTableSQL($CFG->dbtype, $CFG->prefix, false)) {
616 return true; //Empty array = nothing to do = no error
619 return execute_sql_arr($sqlarr, $continue, $feedback);
623 * This function will rename the table passed as argument
624 * Before renaming the index, the function will check it exists
626 * @uses $CFG, $db
627 * @param XMLDBTable table object (just the name is mandatory)
628 * @param string new name of the index
629 * @param boolean continue to specify if must continue on error (true) or stop (false)
630 * @param boolean feedback to specify to show status info (true) or not (false)
631 * @return boolean true on success, false on error
633 function rename_table($table, $newname, $continue=true, $feedback=true) {
635 global $CFG, $db;
637 $status = true;
639 if (strtolower(get_class($table)) != 'xmldbtable') {
640 return false;
643 /// Check table exists
644 if (!table_exists($table)) {
645 debugging('Table ' . $table->getName() . ' do not exist. Rename skipped', DEBUG_DEVELOPER);
646 return true; //Table doesn't exist, nothing to do
649 /// Check new table doesn't exist
650 $check = new XMLDBTable($newname);
651 if (table_exists($check)) {
652 debugging('Table ' . $check->getName() . ' already exists. Rename skipped', DEBUG_DEVELOPER);
653 return true; //Table exists, nothing to do
656 /// Check newname isn't empty
657 if (!$newname) {
658 debugging('New name for table ' . $index->getName() . ' is empty! Rename skipped', DEBUG_DEVELOPER);
659 return true; //Table doesn't exist, nothing to do
662 if(!$sqlarr = $table->getRenameTableSQL($CFG->dbtype, $CFG->prefix, $newname, false)) {
663 return true; //Empty array = nothing to do = no error
666 return execute_sql_arr($sqlarr, $continue, $feedback);
670 * This function will add the field to the table passed as arguments
672 * @uses $CFG, $db
673 * @param XMLDBTable table object (just the name is mandatory)
674 * @param XMLDBField field object (full specs are required)
675 * @param boolean continue to specify if must continue on error (true) or stop (false)
676 * @param boolean feedback to specify to show status info (true) or not (false)
677 * @return boolean true on success, false on error
679 function add_field($table, $field, $continue=true, $feedback=true) {
681 global $CFG, $db;
683 $status = true;
685 if (strtolower(get_class($table)) != 'xmldbtable') {
686 return false;
688 if (strtolower(get_class($field)) != 'xmldbfield') {
689 return false;
692 /// Check the field doesn't exist
693 if (field_exists($table, $field)) {
694 debugging('Field ' . $field->getName() . ' exists. Create skipped', DEBUG_DEVELOPER);
695 return true;
698 /// If NOT NULL and no default given, check the table is empty
699 if ($field->getNotNull() && $field->getDefault() === NULL && count_records($table->getName())) {
700 debugging('Field ' . $field->getName() . ' cannot be added. Not null fields added to non empty tables require default value. Create skipped', DEBUG_DEVELOPER);
701 return true;
704 if(!$sqlarr = $table->getAddFieldSQL($CFG->dbtype, $CFG->prefix, $field, false)) {
705 return true; //Empty array = nothing to do = no error
708 return execute_sql_arr($sqlarr, $continue, $feedback);
712 * This function will drop the field from the table passed as arguments
714 * @uses $CFG, $db
715 * @param XMLDBTable table object (just the name is mandatory)
716 * @param XMLDBField field object (just the name is mandatory)
717 * @param boolean continue to specify if must continue on error (true) or stop (false)
718 * @param boolean feedback to specify to show status info (true) or not (false)
719 * @return boolean true on success, false on error
721 function drop_field($table, $field, $continue=true, $feedback=true) {
723 global $CFG, $db;
725 $status = true;
727 if (strtolower(get_class($table)) != 'xmldbtable') {
728 return false;
730 if (strtolower(get_class($field)) != 'xmldbfield') {
731 return false;
734 /// Check the field exists
735 if (!field_exists($table, $field)) {
736 debugging('Field ' . $field->getName() . ' do not exist. Delete skipped', DEBUG_DEVELOPER);
737 return true;
740 if(!$sqlarr = $table->getDropFieldSQL($CFG->dbtype, $CFG->prefix, $field, false)) {
741 return true; //Empty array = nothing to do = no error
744 return execute_sql_arr($sqlarr, $continue, $feedback);
748 * This function will change the type of the field in the table passed as arguments
750 * @uses $CFG, $db
751 * @param XMLDBTable table object (just the name is mandatory)
752 * @param XMLDBField field object (full specs are required)
753 * @param boolean continue to specify if must continue on error (true) or stop (false)
754 * @param boolean feedback to specify to show status info (true) or not (false)
755 * @return boolean true on success, false on error
757 function change_field_type($table, $field, $continue=true, $feedback=true) {
759 global $CFG, $db;
761 $status = true;
763 if (strtolower(get_class($table)) != 'xmldbtable') {
764 return false;
766 if (strtolower(get_class($field)) != 'xmldbfield') {
767 return false;
770 if(!$sqlarr = $table->getAlterFieldSQL($CFG->dbtype, $CFG->prefix, $field, false)) {
771 return true; //Empty array = nothing to do = no error
774 return execute_sql_arr($sqlarr, $continue, $feedback);
778 * This function will change the precision of the field in the table passed as arguments
780 * @uses $CFG, $db
781 * @param XMLDBTable table object (just the name is mandatory)
782 * @param XMLDBField field object (full specs are required)
783 * @param boolean continue to specify if must continue on error (true) or stop (false)
784 * @param boolean feedback to specify to show status info (true) or not (false)
785 * @return boolean true on success, false on error
787 function change_field_precision($table, $field, $continue=true, $feedback=true) {
789 /// Just a wrapper over change_field_type. Does exactly the same processing
790 return change_field_type($table, $field, $continue, $feedback);
794 * This function will change the unsigned/signed of the field in the table passed as arguments
796 * @uses $CFG, $db
797 * @param XMLDBTable table object (just the name is mandatory)
798 * @param XMLDBField field object (full specs are required)
799 * @param boolean continue to specify if must continue on error (true) or stop (false)
800 * @param boolean feedback to specify to show status info (true) or not (false)
801 * @return boolean true on success, false on error
803 function change_field_unsigned($table, $field, $continue=true, $feedback=true) {
805 /// Just a wrapper over change_field_type. Does exactly the same processing
806 return change_field_type($table, $field, $continue, $feedback);
810 * This function will change the nullability of the field in the table passed as arguments
812 * @uses $CFG, $db
813 * @param XMLDBTable table object (just the name is mandatory)
814 * @param XMLDBField field object (full specs are required)
815 * @param boolean continue to specify if must continue on error (true) or stop (false)
816 * @param boolean feedback to specify to show status info (true) or not (false)
817 * @return boolean true on success, false on error
819 function change_field_notnull($table, $field, $continue=true, $feedback=true) {
821 /// Just a wrapper over change_field_type. Does exactly the same processing
822 return change_field_type($table, $field, $continue, $feedback);
826 * This function will change the enum status of the field in the table passed as arguments
828 * @uses $CFG, $db
829 * @param XMLDBTable table object (just the name is mandatory)
830 * @param XMLDBField field object (full specs are required)
831 * @param boolean continue to specify if must continue on error (true) or stop (false)
832 * @param boolean feedback to specify to show status info (true) or not (false)
833 * @return boolean true on success, false on error
835 function change_field_enum($table, $field, $continue=true, $feedback=true) {
837 global $CFG, $db;
839 $status = true;
841 if (strtolower(get_class($table)) != 'xmldbtable') {
842 return false;
844 if (strtolower(get_class($field)) != 'xmldbfield') {
845 return false;
848 if(!$sqlarr = $table->getModifyEnumSQL($CFG->dbtype, $CFG->prefix, $field, false)) {
849 return true; //Empty array = nothing to do = no error
852 return execute_sql_arr($sqlarr, $continue, $feedback);
855 * This function will change the default of the field in the table passed as arguments
856 * One null value in the default field means delete the default
858 * @uses $CFG, $db
859 * @param XMLDBTable table object (just the name is mandatory)
860 * @param XMLDBField field object (full specs are required)
861 * @param boolean continue to specify if must continue on error (true) or stop (false)
862 * @param boolean feedback to specify to show status info (true) or not (false)
863 * @return boolean true on success, false on error
865 function change_field_default($table, $field, $continue=true, $feedback=true) {
867 global $CFG, $db;
869 $status = true;
871 if (strtolower(get_class($table)) != 'xmldbtable') {
872 return false;
874 if (strtolower(get_class($field)) != 'xmldbfield') {
875 return false;
878 if(!$sqlarr = $table->getModifyDefaultSQL($CFG->dbtype, $CFG->prefix, $field, false)) {
879 return true; //Empty array = nothing to do = no error
882 return execute_sql_arr($sqlarr, $continue, $feedback);
886 * This function will rename the field in the table passed as arguments
887 * Before renaming the field, the function will check it exists
889 * @uses $CFG, $db
890 * @param XMLDBTable table object (just the name is mandatory)
891 * @param XMLDBField index object (full specs are required)
892 * @param string new name of the field
893 * @param boolean continue to specify if must continue on error (true) or stop (false)
894 * @param boolean feedback to specify to show status info (true) or not (false)
895 * @return boolean true on success, false on error
897 function rename_field($table, $field, $newname, $continue=true, $feedback=true) {
899 global $CFG, $db;
901 $status = true;
903 if (strtolower(get_class($table)) != 'xmldbtable') {
904 return false;
906 if (strtolower(get_class($field)) != 'xmldbfield') {
907 return false;
910 /// Check we have included full field specs
911 if (!$field->getType()) {
912 debugging('Field ' . $field->getName() . ' must contain full specs. Rename skipped', DEBUG_DEVELOPER);
913 return false;
916 /// Check field isn't id. Renaming over that field is not allowed
917 if ($field->getName() == 'id') {
918 debugging('Field ' . $field->getName() . ' cannot be renamed. Rename skipped', DEBUG_DEVELOPER);
919 return true; //Field is "id", nothing to do
922 /// Check field exists
923 if (!field_exists($table, $field)) {
924 debugging('Field ' . $field->getName() . ' do not exist. Rename skipped', DEBUG_DEVELOPER);
925 return true; //Field doesn't exist, nothing to do
928 /// Check newname isn't empty
929 if (!$newname) {
930 debugging('New name for field ' . $field->getName() . ' is empty! Rename skipped', DEBUG_DEVELOPER);
931 return true; //Field doesn't exist, nothing to do
934 if(!$sqlarr = $table->getRenameFieldSQL($CFG->dbtype, $CFG->prefix, $field, $newname, false)) {
935 return true; //Empty array = nothing to do = no error
938 return execute_sql_arr($sqlarr, $continue, $feedback);
942 * This function will create the key in the table passed as arguments
944 * @uses $CFG, $db
945 * @param XMLDBTable table object (just the name is mandatory)
946 * @param XMLDBKey index object (full specs are required)
947 * @param boolean continue to specify if must continue on error (true) or stop (false)
948 * @param boolean feedback to specify to show status info (true) or not (false)
949 * @return boolean true on success, false on error
951 function add_key($table, $key, $continue=true, $feedback=true) {
953 global $CFG, $db;
955 $status = true;
957 if (strtolower(get_class($table)) != 'xmldbtable') {
958 return false;
960 if (strtolower(get_class($key)) != 'xmldbkey') {
961 return false;
963 if ($key->getType() == XMLDB_KEY_PRIMARY) { // Prevent PRIMARY to be added (only in create table, being serious :-P)
964 debugging('Primary Keys can be added at table create time only', DEBUG_DEVELOPER);
965 return true;
968 if(!$sqlarr = $table->getAddKeySQL($CFG->dbtype, $CFG->prefix, $key, false)) {
969 return true; //Empty array = nothing to do = no error
972 return execute_sql_arr($sqlarr, $continue, $feedback);
976 * This function will drop the key in the table passed as arguments
978 * @uses $CFG, $db
979 * @param XMLDBTable table object (just the name is mandatory)
980 * @param XMLDBKey key object (full specs are required)
981 * @param boolean continue to specify if must continue on error (true) or stop (false)
982 * @param boolean feedback to specify to show status info (true) or not (false)
983 * @return boolean true on success, false on error
985 function drop_key($table, $key, $continue=true, $feedback=true) {
987 global $CFG, $db;
989 $status = true;
991 if (strtolower(get_class($table)) != 'xmldbtable') {
992 return false;
994 if (strtolower(get_class($key)) != 'xmldbkey') {
995 return false;
997 if ($key->getType() == XMLDB_KEY_PRIMARY) { // Prevent PRIMARY to be dropped (only in drop table, being serious :-P)
998 debugging('Primary Keys can be deleted at table drop time only', DEBUG_DEVELOPER);
999 return true;
1002 if(!$sqlarr = $table->getDropKeySQL($CFG->dbtype, $CFG->prefix, $key, false)) {
1003 return true; //Empty array = nothing to do = no error
1006 return execute_sql_arr($sqlarr, $continue, $feedback);
1010 * This function will rename the key in the table passed as arguments
1011 * Experimental. Shouldn't be used at all in normal installation/upgrade!
1013 * @uses $CFG, $db
1014 * @param XMLDBTable table object (just the name is mandatory)
1015 * @param XMLDBKey key object (full specs are required)
1016 * @param string new name of the key
1017 * @param boolean continue to specify if must continue on error (true) or stop (false)
1018 * @param boolean feedback to specify to show status info (true) or not (false)
1019 * @return boolean true on success, false on error
1021 function rename_key($table, $key, $newname, $continue=true, $feedback=true) {
1023 global $CFG, $db;
1025 debugging('rename_key() is one experimental feature. You must not use it in production!', DEBUG_DEVELOPER);
1027 $status = true;
1029 if (strtolower(get_class($table)) != 'xmldbtable') {
1030 return false;
1032 if (strtolower(get_class($key)) != 'xmldbkey') {
1033 return false;
1036 /// Check newname isn't empty
1037 if (!$newname) {
1038 debugging('New name for key ' . $key->getName() . ' is empty! Rename skipped', DEBUG_DEVELOPER);
1039 return true; //Key doesn't exist, nothing to do
1042 if(!$sqlarr = $table->getRenameKeySQL($CFG->dbtype, $CFG->prefix, $key, $newname, false)) {
1043 debugging('Some DBs do not support key renaming (MySQL, PostgreSQL, MsSQL). Rename skipped', DEBUG_DEVELOPER);
1044 return true; //Empty array = nothing to do = no error
1047 return execute_sql_arr($sqlarr, $continue, $feedback);
1051 * This function will create the index in the table passed as arguments
1052 * Before creating the index, the function will check it doesn't exists
1054 * @uses $CFG, $db
1055 * @param XMLDBTable table object (just the name is mandatory)
1056 * @param XMLDBIndex index object (full specs are required)
1057 * @param boolean continue to specify if must continue on error (true) or stop (false)
1058 * @param boolean feedback to specify to show status info (true) or not (false)
1059 * @return boolean true on success, false on error
1061 function add_index($table, $index, $continue=true, $feedback=true) {
1063 global $CFG, $db;
1065 $status = true;
1067 if (strtolower(get_class($table)) != 'xmldbtable') {
1068 return false;
1070 if (strtolower(get_class($index)) != 'xmldbindex') {
1071 return false;
1074 /// Check index doesn't exist
1075 if (index_exists($table, $index)) {
1076 debugging('Index ' . $index->getName() . ' exists. Create skipped', DEBUG_DEVELOPER);
1077 return true; //Index exists, nothing to do
1080 if(!$sqlarr = $table->getAddIndexSQL($CFG->dbtype, $CFG->prefix, $index, false)) {
1081 return true; //Empty array = nothing to do = no error
1084 return execute_sql_arr($sqlarr, $continue, $feedback);
1088 * This function will drop the index in the table passed as arguments
1089 * Before dropping the index, the function will check it exists
1091 * @uses $CFG, $db
1092 * @param XMLDBTable table object (just the name is mandatory)
1093 * @param XMLDBIndex index object (full specs are required)
1094 * @param boolean continue to specify if must continue on error (true) or stop (false)
1095 * @param boolean feedback to specify to show status info (true) or not (false)
1096 * @return boolean true on success, false on error
1098 function drop_index($table, $index, $continue=true, $feedback=true) {
1100 global $CFG, $db;
1102 $status = true;
1104 if (strtolower(get_class($table)) != 'xmldbtable') {
1105 return false;
1107 if (strtolower(get_class($index)) != 'xmldbindex') {
1108 return false;
1111 /// Check index exists
1112 if (!index_exists($table, $index)) {
1113 debugging('Index ' . $index->getName() . ' do not exist. Delete skipped', DEBUG_DEVELOPER);
1114 return true; //Index doesn't exist, nothing to do
1117 if(!$sqlarr = $table->getDropIndexSQL($CFG->dbtype, $CFG->prefix, $index, false)) {
1118 return true; //Empty array = nothing to do = no error
1121 return execute_sql_arr($sqlarr, $continue, $feedback);
1125 * This function will rename the index in the table passed as arguments
1126 * Before renaming the index, the function will check it exists
1127 * Experimental. Shouldn't be used at all!
1129 * @uses $CFG, $db
1130 * @param XMLDBTable table object (just the name is mandatory)
1131 * @param XMLDBIndex index object (full specs are required)
1132 * @param string new name of the index
1133 * @param boolean continue to specify if must continue on error (true) or stop (false)
1134 * @param boolean feedback to specify to show status info (true) or not (false)
1135 * @return boolean true on success, false on error
1137 function rename_index($table, $index, $newname, $continue=true, $feedback=true) {
1139 global $CFG, $db;
1141 debugging('rename_index() is one experimental feature. You must not use it in production!', DEBUG_DEVELOPER);
1143 $status = true;
1145 if (strtolower(get_class($table)) != 'xmldbtable') {
1146 return false;
1148 if (strtolower(get_class($index)) != 'xmldbindex') {
1149 return false;
1152 /// Check index exists
1153 if (!index_exists($table, $index)) {
1154 debugging('Index ' . $index->getName() . ' do not exist. Rename skipped', DEBUG_DEVELOPER);
1155 return true; //Index doesn't exist, nothing to do
1158 /// Check newname isn't empty
1159 if (!$newname) {
1160 debugging('New name for index ' . $index->getName() . ' is empty! Rename skipped', DEBUG_DEVELOPER);
1161 return true; //Index doesn't exist, nothing to do
1164 if(!$sqlarr = $table->getRenameIndexSQL($CFG->dbtype, $CFG->prefix, $index, $newname, false)) {
1165 debugging('Some DBs do not support index renaming (MySQL). Rename skipped', DEBUG_DEVELOPER);
1166 return true; //Empty array = nothing to do = no error
1169 return execute_sql_arr($sqlarr, $continue, $feedback);
1172 /* trys to change default db encoding to utf8, if empty db
1174 function change_db_encoding() {
1175 global $CFG, $db;
1176 // try forcing utf8 collation, if mysql db and no tables present
1177 if (($CFG->dbfamily=='mysql') && !$db->Metatables()) {
1178 $SQL = 'ALTER DATABASE '.$CFG->dbname.' CHARACTER SET utf8';
1179 execute_sql($SQL, false); // silent, if it fails it fails
1180 if (setup_is_unicodedb()) {
1181 configure_dbconnection();