3 ///////////////////////////////////////////////////////////////////////////
5 // NOTICE OF COPYRIGHT //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
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. //
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: //
23 // http://www.gnu.org/copyleft/gpl.html //
25 ///////////////////////////////////////////////////////////////////////////
27 /// This class represent one XMLDB table
29 class xmldb_table
extends xmldb_object
{
36 * Creates one new xmldb_table
38 function __construct($name) {
39 parent
::__construct($name);
40 $this->fields
= array();
41 $this->keys
= array();
42 $this->indexes
= array();
46 * Add one field to the table, allowing to specify the desired order
47 * If it's not specified, then the field is added at the end
49 function addField(&$field, $after=NULL) {
51 /// Detect duplicates first
52 if ($this->getField($field->getName())) {
53 throw new coding_exception('Duplicate field '.$field->getName().' specified in table '.$this->getName());
56 /// Calculate the previous and next fields
61 $allfields =& $this->getFields();
62 if (!empty($allfields)) {
64 $prevfield =& $allfields[key($allfields)];
67 $prevfield =& $this->getField($after);
69 if ($prevfield && $prevfield->getNext()) {
70 $nextfield =& $this->getField($prevfield->getNext());
73 /// Set current field previous and next attributes
75 $field->setPrevious($prevfield->getName());
76 $prevfield->setNext($field->getName());
79 $field->setNext($nextfield->getName());
80 $nextfield->setPrevious($field->getName());
82 /// Some more attributes
83 $field->setLoaded(true);
84 $field->setChanged(true);
86 $this->fields
[] = $field;
88 $this->orderFields($this->fields
);
89 /// Recalculate the hash
90 $this->calculateHash(true);
91 /// We have one new field, so the table has changed
92 $this->setChanged(true);
98 * Add one key to the table, allowing to specify the desired order
99 * If it's not specified, then the key is added at the end
101 function addKey(&$key, $after=NULL) {
103 /// Detect duplicates first
104 if ($this->getKey($key->getName())) {
105 throw new coding_exception('Duplicate key '.$key->getName().' specified in table '.$this->getName());
108 /// Calculate the previous and next keys
113 $allkeys =& $this->getKeys();
114 if (!empty($allkeys)) {
116 $prevkey =& $allkeys[key($allkeys)];
119 $prevkey =& $this->getKey($after);
121 if ($prevkey && $prevkey->getNext()) {
122 $nextkey =& $this->getKey($prevkey->getNext());
125 /// Set current key previous and next attributes
127 $key->setPrevious($prevkey->getName());
128 $prevkey->setNext($key->getName());
131 $key->setNext($nextkey->getName());
132 $nextkey->setPrevious($key->getName());
134 /// Some more attributes
135 $key->setLoaded(true);
136 $key->setChanged(true);
138 $this->keys
[] = $key;
140 $this->orderKeys($this->keys
);
141 /// Recalculate the hash
142 $this->calculateHash(true);
143 /// We have one new field, so the table has changed
144 $this->setChanged(true);
148 * Add one index to the table, allowing to specify the desired order
149 * If it's not specified, then the index is added at the end
151 function addIndex(&$index, $after=NULL) {
153 /// Detect duplicates first
154 if ($this->getIndex($index->getName())) {
155 throw new coding_exception('Duplicate index '.$index->getName().' specified in table '.$this->getName());
158 /// Calculate the previous and next indexes
163 $allindexes =& $this->getIndexes();
164 if (!empty($allindexes)) {
166 $previndex =& $allindexes[key($allindexes)];
169 $previndex =& $this->getIndex($after);
171 if ($previndex && $previndex->getNext()) {
172 $nextindex =& $this->getIndex($previndex->getNext());
175 /// Set current index previous and next attributes
177 $index->setPrevious($previndex->getName());
178 $previndex->setNext($index->getName());
181 $index->setNext($nextindex->getName());
182 $nextindex->setPrevious($index->getName());
185 /// Some more attributes
186 $index->setLoaded(true);
187 $index->setChanged(true);
188 /// Add the new index
189 $this->indexes
[] = $index;
190 /// Reorder the indexes
191 $this->orderIndexes($this->indexes
);
192 /// Recalculate the hash
193 $this->calculateHash(true);
194 /// We have one new index, so the table has changed
195 $this->setChanged(true);
199 * This function will return the array of fields in the table
201 function &getFields() {
202 return $this->fields
;
206 * This function will return the array of keys in the table
208 function &getKeys() {
213 * This function will return the array of indexes in the table
215 function &getIndexes() {
216 return $this->indexes
;
220 * Returns one xmldb_field
222 function &getField($fieldname) {
223 $i = $this->findFieldInArray($fieldname);
225 return $this->fields
[$i];
232 * Returns the position of one field in the array.
234 function &findFieldInArray($fieldname) {
235 foreach ($this->fields
as $i => $field) {
236 if ($fieldname == $field->getName()) {
245 * This function will reorder the array of fields
247 function orderFields() {
248 $result = $this->orderElements($this->fields
);
250 $this->setFields($result);
258 * Returns one xmldb_key
260 function &getKey($keyname) {
261 $i = $this->findKeyInArray($keyname);
263 return $this->keys
[$i];
270 * Returns the position of one key in the array.
272 function &findKeyInArray($keyname) {
273 foreach ($this->keys
as $i => $key) {
274 if ($keyname == $key->getName()) {
283 * This function will reorder the array of keys
285 function orderKeys() {
286 $result = $this->orderElements($this->keys
);
288 $this->setKeys($result);
296 * Returns one xmldb_index
298 function &getIndex($indexname) {
299 $i = $this->findIndexInArray($indexname);
301 return $this->indexes
[$i];
308 * Returns the position of one index in the array.
310 function &findIndexInArray($indexname) {
311 foreach ($this->indexes
as $i => $index) {
312 if ($indexname == $index->getName()) {
321 * This function will reorder the array of indexes
323 function orderIndexes() {
324 $result = $this->orderElements($this->indexes
);
326 $this->setIndexes($result);
334 * This function will set the array of fields in the table
336 function setFields($fields) {
337 $this->fields
= $fields;
341 * This function will set the array of keys in the table
343 function setKeys($keys) {
348 * This function will set the array of indexes in the table
350 function setIndexes($indexes) {
351 $this->indexes
= $indexes;
355 * Delete one field from the table
357 function deleteField($fieldname) {
359 $field =& $this->getField($fieldname);
361 $i = $this->findFieldInArray($fieldname);
362 /// Look for prev and next field
363 $prevfield =& $this->getField($field->getPrevious());
364 $nextfield =& $this->getField($field->getNext());
365 /// Change their previous and next attributes
367 $prevfield->setNext($field->getNext());
370 $nextfield->setPrevious($field->getPrevious());
373 unset($this->fields
[$i]);
374 /// Reorder the whole structure
375 $this->orderFields($this->fields
);
376 /// Recalculate the hash
377 $this->calculateHash(true);
378 /// We have one deleted field, so the table has changed
379 $this->setChanged(true);
384 * Delete one key from the table
386 function deleteKey($keyname) {
388 $key =& $this->getKey($keyname);
390 $i = $this->findKeyInArray($keyname);
391 /// Look for prev and next key
392 $prevkey =& $this->getKey($key->getPrevious());
393 $nextkey =& $this->getKey($key->getNext());
394 /// Change their previous and next attributes
396 $prevkey->setNext($key->getNext());
399 $nextkey->setPrevious($key->getPrevious());
402 unset($this->keys
[$i]);
404 $this->orderKeys($this->keys
);
405 /// Recalculate the hash
406 $this->calculateHash(true);
407 /// We have one deleted key, so the table has changed
408 $this->setChanged(true);
413 * Delete one index from the table
415 function deleteIndex($indexname) {
417 $index =& $this->getIndex($indexname);
419 $i = $this->findIndexInArray($indexname);
420 /// Look for prev and next index
421 $previndex =& $this->getIndex($index->getPrevious());
422 $nextindex =& $this->getIndex($index->getNext());
423 /// Change their previous and next attributes
425 $previndex->setNext($index->getNext());
428 $nextindex->setPrevious($index->getPrevious());
431 unset($this->indexes
[$i]);
432 /// Reorder the indexes
433 $this->orderIndexes($this->indexes
);
434 /// Recalculate the hash
435 $this->calculateHash(true);
436 /// We have one deleted index, so the table has changed
437 $this->setChanged(true);
442 * Load data from XML to the table
444 function arr2xmldb_table($xmlarr) {
451 /// traverse_xmlize($xmlarr); //Debug
452 /// print_object ($GLOBALS['traverse_array']); //Debug
453 /// $GLOBALS['traverse_array']=""; //Debug
455 /// Process table attributes (name, comment, previoustable and nexttable)
456 if (isset($xmlarr['@']['NAME'])) {
457 $this->name
= trim($xmlarr['@']['NAME']);
459 $this->errormsg
= 'Missing NAME attribute';
460 $this->debug($this->errormsg
);
463 if (isset($xmlarr['@']['COMMENT'])) {
464 $this->comment
= trim($xmlarr['@']['COMMENT']);
465 } else if (!empty($CFG->xmldbdisablecommentchecking
)) {
468 $this->errormsg
= 'Missing COMMENT attribute';
469 $this->debug($this->errormsg
);
472 if (isset($xmlarr['@']['PREVIOUS'])) {
473 $this->previous
= trim($xmlarr['@']['PREVIOUS']);
475 if (isset($xmlarr['@']['NEXT'])) {
476 $this->next
= trim($xmlarr['@']['NEXT']);
479 /// Iterate over fields
480 if (isset($xmlarr['#']['FIELDS']['0']['#']['FIELD'])) {
481 foreach ($xmlarr['#']['FIELDS']['0']['#']['FIELD'] as $xmlfield) {
482 if (!$result) { //Skip on error
485 $name = trim($xmlfield['@']['NAME']);
486 $field = new xmldb_field($name);
487 $field->arr2xmldb_field($xmlfield);
488 $this->fields
[] = $field;
489 if (!$field->isLoaded()) {
490 $this->errormsg
= 'Problem loading field ' . $name;
491 $this->debug($this->errormsg
);
496 $this->errormsg
= 'Missing FIELDS section';
497 $this->debug($this->errormsg
);
501 /// Perform some general checks over fields
502 if ($result && $this->fields
) {
503 /// Check field names are ok (lowercase, a-z _-)
504 if (!$this->checkNameValues($this->fields
)) {
505 $this->errormsg
= 'Some FIELDS name values are incorrect';
506 $this->debug($this->errormsg
);
509 /// Check previous & next are ok (duplicates and existing fields)
510 $this->fixPrevNext($this->fields
);
511 if ($result && !$this->checkPreviousNextValues($this->fields
)) {
512 $this->errormsg
= 'Some FIELDS previous/next values are incorrect';
513 $this->debug($this->errormsg
);
517 if ($result && !$this->orderFields($this->fields
)) {
518 $this->errormsg
= 'Error ordering the fields';
519 $this->debug($this->errormsg
);
524 /// Iterate over keys
525 if (isset($xmlarr['#']['KEYS']['0']['#']['KEY'])) {
526 foreach ($xmlarr['#']['KEYS']['0']['#']['KEY'] as $xmlkey) {
527 if (!$result) { //Skip on error
530 $name = trim($xmlkey['@']['NAME']);
531 $key = new xmldb_key($name);
532 $key->arr2xmldb_key($xmlkey);
533 $this->keys
[] = $key;
534 if (!$key->isLoaded()) {
535 $this->errormsg
= 'Problem loading key ' . $name;
536 $this->debug($this->errormsg
);
541 $this->errormsg
= 'Missing KEYS section (at least one PK must exist)';
542 $this->debug($this->errormsg
);
546 /// Perform some general checks over keys
547 if ($result && $this->keys
) {
548 /// Check keys names are ok (lowercase, a-z _-)
549 if (!$this->checkNameValues($this->keys
)) {
550 $this->errormsg
= 'Some KEYS name values are incorrect';
551 $this->debug($this->errormsg
);
554 /// Check previous & next are ok (duplicates and existing keys)
555 $this->fixPrevNext($this->keys
);
556 if ($result && !$this->checkPreviousNextValues($this->keys
)) {
557 $this->errormsg
= 'Some KEYS previous/next values are incorrect';
558 $this->debug($this->errormsg
);
562 if ($result && !$this->orderKeys($this->keys
)) {
563 $this->errormsg
= 'Error ordering the keys';
564 $this->debug($this->errormsg
);
567 /// TODO: Only one PK
568 /// TODO: Not keys with repeated fields
569 /// TODO: Check fields and reffieds exist in table
572 /// Iterate over indexes
573 if (isset($xmlarr['#']['INDEXES']['0']['#']['INDEX'])) {
574 foreach ($xmlarr['#']['INDEXES']['0']['#']['INDEX'] as $xmlindex) {
575 if (!$result) { //Skip on error
578 $name = trim($xmlindex['@']['NAME']);
579 $index = new xmldb_index($name);
580 $index->arr2xmldb_index($xmlindex);
581 $this->indexes
[] = $index;
582 if (!$index->isLoaded()) {
583 $this->errormsg
= 'Problem loading index ' . $name;
584 $this->debug($this->errormsg
);
590 /// Perform some general checks over indexes
591 if ($result && $this->indexes
) {
592 /// Check field names are ok (lowercase, a-z _-)
593 if (!$this->checkNameValues($this->indexes
)) {
594 $this->errormsg
= 'Some INDEXES name values are incorrect';
595 $this->debug($this->errormsg
);
598 /// Check previous & next are ok (duplicates and existing INDEXES)
599 $this->fixPrevNext($this->indexes
);
600 if ($result && !$this->checkPreviousNextValues($this->indexes
)) {
601 $this->errormsg
= 'Some INDEXES previous/next values are incorrect';
602 $this->debug($this->errormsg
);
606 if ($result && !$this->orderIndexes($this->indexes
)) {
607 $this->errormsg
= 'Error ordering the indexes';
608 $this->debug($this->errormsg
);
611 /// TODO: Not indexes with repeated fields
612 /// TODO: Check fields exist in table
615 /// Set some attributes
617 $this->loaded
= true;
619 $this->calculateHash();
624 * This function calculate and set the hash of one xmldb_table
626 function calculateHash($recursive = false) {
627 if (!$this->loaded
) {
630 $key = $this->name
. $this->comment
;
632 foreach ($this->fields
as $fie) {
633 $field =& $this->getField($fie->getName());
635 $field->calculateHash($recursive);
637 $key .= $field->getHash();
641 foreach ($this->keys
as $ke) {
642 $k =& $this->getKey($ke->getName());
644 $k->calculateHash($recursive);
646 $key .= $k->getHash();
649 if ($this->indexes
) {
650 foreach ($this->indexes
as $in) {
651 $index =& $this->getIndex($in->getName());
653 $index->calculateHash($recursive);
655 $key .= $index->getHash();
658 $this->hash
= md5($key);
663 * This function will output the XML text for one table
665 function xmlOutput() {
667 $o.= ' <TABLE NAME="' . $this->name
. '"';
668 if ($this->comment
) {
669 $o.= ' COMMENT="' . htmlspecialchars($this->comment
) . '"';
671 if ($this->previous
) {
672 $o.= ' PREVIOUS="' . $this->previous
. '"';
675 $o.= ' NEXT="' . $this->next
. '"';
680 $o.= ' <FIELDS>' . "\n";
681 foreach ($this->fields
as $field) {
682 $o.= $field->xmlOutput();
684 $o.= ' </FIELDS>' . "\n";
688 $o.= ' <KEYS>' . "\n";
689 foreach ($this->keys
as $key) {
690 $o.= $key->xmlOutput();
692 $o.= ' </KEYS>' . "\n";
695 if ($this->indexes
) {
696 $o.= ' <INDEXES>' . "\n";
697 foreach ($this->indexes
as $index) {
698 $o.= $index->xmlOutput();
700 $o.= ' </INDEXES>' . "\n";
702 $o.= ' </TABLE>' . "\n";
707 /// TODO: Delete for 2.1 (deprecated in 2.0).
708 /// Deprecated API starts here
709 function addFieldInfo($name, $type, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $enum=null, $enumvalues=null, $default=null, $previous=null) {
711 debugging('XMLDBTable->addFieldInfo() has been deprecated in Moodle 2.0. Will be out in Moodle 2.1. Please use xmldb_table->add_field() instead', DEBUG_DEVELOPER
);
713 debugging('Also, ENUMs support has been dropped in Moodle 2.0. Your fields specs are incorrect because you are trying to introduce one new ENUM. Created DB estructures will ignore that.');
716 return $this->add_field($name, $type, $precision, $unsigned, $notnull, $sequence, $default, $previous);
719 /// Deprecated API ends here
722 * This function will add one new field to the table with all
723 * its attributes defined
725 * @param string name name of the field
726 * @param string type XMLDB_TYPE_INTEGER, XMLDB_TYPE_NUMBER, XMLDB_TYPE_CHAR, XMLDB_TYPE_TEXT, XMLDB_TYPE_BINARY
727 * @param string precision length for integers and chars, two-comma separated numbers for numbers and 'small', 'medium', 'big' for texts and binaries
728 * @param string unsigned XMLDB_UNSIGNED or null (or false)
729 * @param string notnull XMLDB_NOTNULL or null (or false)
730 * @param string sequence XMLDB_SEQUENCE or null (or false)
731 * @param string default meaningful default o null (or false)
732 * @param string previous name of the previous field in the table or null (or false)
734 function add_field($name, $type, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $default=null, $previous=null) {
735 $field = new xmldb_field($name, $type, $precision, $unsigned, $notnull, $sequence, $default);
736 $this->addField($field, $previous);
741 /// TODO: Delete for 2.1 (deprecated in 2.0).
742 /// Deprecated API starts here
744 function addKeyInfo($name, $type, $fields, $reftable=null, $reffields=null) {
746 debugging('XMLDBTable->addKeyInfo() has been deprecated in Moodle 2.0. Will be out in Moodle 2.1. Please use xmldb_table->add_key() instead', DEBUG_DEVELOPER
);
748 return $this->add_key($name, $type, $fields, $reftable, $reffields);
751 /// Deprecated API ends here
754 * This function will add one new key to the table with all
755 * its attributes defined
757 * @param string name name of the key
758 * @param string type XMLDB_KEY_PRIMARY, XMLDB_KEY_UNIQUE, XMLDB_KEY_FOREIGN
759 * @param array fields an array of fieldnames to build the key over
760 * @param string reftable name of the table the FK points to or null
761 * @param array reffields an array of fieldnames in the FK table or null
763 function add_key($name, $type, $fields, $reftable=null, $reffields=null) {
764 $key = new xmldb_key($name, $type, $fields, $reftable, $reffields);
768 /// TODO: Delete for 2.1 (deprecated in 2.0).
769 /// Deprecated API starts here
770 function addIndexInfo($name, $type, $fields) {
772 debugging('XMLDBTable->addIndexInfo() has been deprecated in Moodle 2.0. Will be out in Moodle 2.1. Please use xmldb_table->add_index() instead', DEBUG_DEVELOPER
);
774 return $this->add_index($name, $type, $fields);
777 /// Deprecated API ends here
780 * This function will add one new index to the table with all
781 * its attributes defined
783 * @param string name name of the index
784 * @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
785 * @param array fields an array of fieldnames to build the index over
787 function add_index($name, $type, $fields) {
788 $index = new xmldb_index($name, $type, $fields);
789 $this->addIndex($index);
793 * This function will return all the errors found in one table
794 * looking recursively inside each field/key/index. Returns
795 * an array of errors or false
797 function getAllErrors() {
800 /// First the table itself
801 if ($this->getError()) {
802 $errors[] = $this->getError();
804 /// Delegate to fields
805 if ($fields = $this->getFields()) {
806 foreach ($fields as $field) {
807 if ($field->getError()) {
808 $errors[] = $field->getError();
813 if ($keys = $this->getKeys()) {
814 foreach ($keys as $key) {
815 if ($key->getError()) {
816 $errors[] = $key->getError();
820 /// Delegate to indexes
821 if ($indexes = $this->getIndexes()) {
822 foreach ($indexes as $index) {
823 if ($index->getError()) {
824 $errors[] = $index->getError();
829 if (count($errors)) {
837 /// TODO: Delete for 2.1 (deeprecated in 2.0).
838 /// Deprecated API starts here
839 class XMLDBTable
extends xmldb_table
{
841 function __construct($name) {
842 parent
::__construct($name);
846 /// Deprecated API ends here