c69c2a1c6f207afa579605a316565e01bd263c28
[moodle.git] / lib / xmldb / xmldb_table.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * This class represent one XMLDB table
19  *
20  * @package    core_xmldb
21  * @copyright  1999 onwards Martin Dougiamas     http://dougiamas.com
22  *             2001-3001 Eloy Lafuente (stronk7) http://contiento.com
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
29 class xmldb_table extends xmldb_object {
31     /** @var array table columns */
32     protected $fields;
34     /** @var array keys */
35     protected $keys;
37     /** @var array indexes */
38     protected $indexes;
40     /**
41      * Note:
42      *  - Oracle has 30 chars limit for all names,
43      *    2 chars are reserved for prefix.
44      *
45      * @const maximum length of field names
46      */
47     const NAME_MAX_LENGTH = 28;
49     /**
50      * Creates one new xmldb_table
51      * @param string $name
52      */
53     public function __construct($name) {
54         parent::__construct($name);
55         $this->fields = array();
56         $this->keys = array();
57         $this->indexes = array();
58     }
60     /**
61      * Add one field to the table, allowing to specify the desired  order
62      * If it's not specified, then the field is added at the end
63      * @param xmldb_field $field
64      * @param xmldb_object $after
65      * @return xmldb_field
66      */
67     public function addField($field, $after=null) {
69         // Detect duplicates first
70         if ($this->getField($field->getName())) {
71             throw new coding_exception('Duplicate field '.$field->getName().' specified in table '.$this->getName());
72         }
74         // Calculate the previous and next fields
75         $prevfield = null;
76         $nextfield = null;
78         if (!$after) {
79             $allfields = $this->getFields();
80             if (!empty($allfields)) {
81                 end($allfields);
82                 $prevfield = $allfields[key($allfields)];
83             }
84         } else {
85             $prevfield = $this->getField($after);
86         }
87         if ($prevfield && $prevfield->getNext()) {
88             $nextfield = $this->getField($prevfield->getNext());
89         }
91         // Set current field previous and next attributes
92         if ($prevfield) {
93             $field->setPrevious($prevfield->getName());
94             $prevfield->setNext($field->getName());
95         }
96         if ($nextfield) {
97             $field->setNext($nextfield->getName());
98             $nextfield->setPrevious($field->getName());
99         }
100         // Some more attributes
101         $field->setLoaded(true);
102         $field->setChanged(true);
103         // Add the new field
104         $this->fields[] = $field;
105         // Reorder the field
106         $this->orderFields($this->fields);
107         // Recalculate the hash
108         $this->calculateHash(true);
109         // We have one new field, so the table has changed
110         $this->setChanged(true);
112         return $field;
113     }
115     /**
116      * Add one key to the table, allowing to specify the desired  order
117      * If it's not specified, then the key is added at the end
118      * @param xmldb_key $key
119      * @param xmldb_object $after
120      */
121     public function addKey($key, $after=null) {
123         // Detect duplicates first
124         if ($this->getKey($key->getName())) {
125             throw new coding_exception('Duplicate key '.$key->getName().' specified in table '.$this->getName());
126         }
128         // Calculate the previous and next keys
129         $prevkey = null;
130         $nextkey = null;
132         if (!$after) {
133             $allkeys = $this->getKeys();
134             if (!empty($allkeys)) {
135                 end($allkeys);
136                 $prevkey = $allkeys[key($allkeys)];
137             }
138         } else {
139             $prevkey = $this->getKey($after);
140         }
141         if ($prevkey && $prevkey->getNext()) {
142             $nextkey = $this->getKey($prevkey->getNext());
143         }
145         // Set current key previous and next attributes
146         if ($prevkey) {
147             $key->setPrevious($prevkey->getName());
148             $prevkey->setNext($key->getName());
149         }
150         if ($nextkey) {
151             $key->setNext($nextkey->getName());
152             $nextkey->setPrevious($key->getName());
153         }
154         // Some more attributes
155         $key->setLoaded(true);
156         $key->setChanged(true);
157         // Add the new key
158         $this->keys[] = $key;
159         // Reorder the keys
160         $this->orderKeys($this->keys);
161         // Recalculate the hash
162         $this->calculateHash(true);
163         // We have one new field, so the table has changed
164         $this->setChanged(true);
165     }
167     /**
168      * Add one index to the table, allowing to specify the desired  order
169      * If it's not specified, then the index is added at the end
170      * @param xmldb_index $index
171      * @param xmldb_object $after
172      */
173     public function addIndex($index, $after=null) {
175         // Detect duplicates first
176         if ($this->getIndex($index->getName())) {
177             throw new coding_exception('Duplicate index '.$index->getName().' specified in table '.$this->getName());
178         }
180         // Calculate the previous and next indexes
181         $previndex = null;
182         $nextindex = null;
184         if (!$after) {
185             $allindexes = $this->getIndexes();
186             if (!empty($allindexes)) {
187                 end($allindexes);
188                 $previndex = $allindexes[key($allindexes)];
189             }
190         } else {
191             $previndex = $this->getIndex($after);
192         }
193         if ($previndex && $previndex->getNext()) {
194             $nextindex = $this->getIndex($previndex->getNext());
195         }
197         // Set current index previous and next attributes
198         if ($previndex) {
199             $index->setPrevious($previndex->getName());
200             $previndex->setNext($index->getName());
201         }
202         if ($nextindex) {
203             $index->setNext($nextindex->getName());
204             $nextindex->setPrevious($index->getName());
205         }
207         // Some more attributes
208         $index->setLoaded(true);
209         $index->setChanged(true);
210         // Add the new index
211         $this->indexes[] = $index;
212         // Reorder the indexes
213         $this->orderIndexes($this->indexes);
214         // Recalculate the hash
215         $this->calculateHash(true);
216         // We have one new index, so the table has changed
217         $this->setChanged(true);
218     }
220     /**
221      * This function will return the array of fields in the table
222      * @return array
223      */
224     public function getFields() {
225         return $this->fields;
226     }
228     /**
229      * This function will return the array of keys in the table
230      * @return array
231      */
232     public function getKeys() {
233         return $this->keys;
234     }
236     /**
237      * This function will return the array of indexes in the table
238      * @return array
239      */
240     public function getIndexes() {
241         return $this->indexes;
242     }
244     /**
245      * Returns one xmldb_field
246      * @param string $fieldname
247      * @return mixed
248      */
249     public function getField($fieldname) {
250         $i = $this->findFieldInArray($fieldname);
251         if ($i !== null) {
252             return $this->fields[$i];
253         }
254         return null;
255     }
257     /**
258      * Returns the position of one field in the array.
259      * @param string $fieldname
260      * @return mixed
261      */
262     public function findFieldInArray($fieldname) {
263         foreach ($this->fields as $i => $field) {
264             if ($fieldname == $field->getName()) {
265                 return $i;
266             }
267         }
268         return null;
269     }
271     /**
272      * This function will reorder the array of fields
273      * @return bool
274      */
275     public function orderFields() {
276         $result = $this->orderElements($this->fields);
277         if ($result) {
278             $this->setFields($result);
279             return true;
280         } else {
281             return false;
282         }
283     }
285     /**
286      * Returns one xmldb_key
287      * @param string $keyname
288      * @return mixed
289      */
290     public function getKey($keyname) {
291         $i = $this->findKeyInArray($keyname);
292         if ($i !== null) {
293             return $this->keys[$i];
294         }
295         return null;
296     }
298     /**
299      * Returns the position of one key in the array.
300      * @param string $keyname
301      * @return mixed
302      */
303     public function findKeyInArray($keyname) {
304         foreach ($this->keys as $i => $key) {
305             if ($keyname == $key->getName()) {
306                 return $i;
307             }
308         }
309         return null;
310     }
312     /**
313      * This function will reorder the array of keys
314      * @return bool
315      */
316     public function orderKeys() {
317         $result = $this->orderElements($this->keys);
318         if ($result) {
319             $this->setKeys($result);
320             return true;
321         } else {
322             return false;
323         }
324     }
326     /**
327      * Returns one xmldb_index
328      * @param string $indexname
329      * @return mixed
330      */
331     public function getIndex($indexname) {
332         $i = $this->findIndexInArray($indexname);
333         if ($i !== null) {
334             return $this->indexes[$i];
335         }
336         return null;
337     }
339     /**
340      * Returns the position of one index in the array.
341      * @param string $indexname
342      * @return mixed
343      */
344     public function findIndexInArray($indexname) {
345         foreach ($this->indexes as $i => $index) {
346             if ($indexname == $index->getName()) {
347                 return $i;
348             }
349         }
350         return null;
351     }
353     /**
354      * This function will reorder the array of indexes
355      * @return bool
356      */
357     public function orderIndexes() {
358         $result = $this->orderElements($this->indexes);
359         if ($result) {
360             $this->setIndexes($result);
361             return true;
362         } else {
363             return false;
364         }
365     }
367     /**
368      * This function will set the array of fields in the table
369      * @param array $fields
370      */
371     public function setFields($fields) {
372         $this->fields = $fields;
373     }
375     /**
376      * This function will set the array of keys in the table
377      * @param array $keys
378      */
379     public function setKeys($keys) {
380         $this->keys = $keys;
381     }
383     /**
384      * This function will set the array of indexes in the table
385      * @param array $indexes
386      */
387     public function setIndexes($indexes) {
388         $this->indexes = $indexes;
389     }
391     /**
392      * Delete one field from the table
393      * @param string $fieldname
394      */
395     public function deleteField($fieldname) {
397         $field = $this->getField($fieldname);
398         if ($field) {
399             $i = $this->findFieldInArray($fieldname);
400             // Look for prev and next field
401             $prevfield = $this->getField($field->getPrevious());
402             $nextfield = $this->getField($field->getNext());
403             // Change their previous and next attributes
404             if ($prevfield) {
405                 $prevfield->setNext($field->getNext());
406             }
407             if ($nextfield) {
408                 $nextfield->setPrevious($field->getPrevious());
409             }
410             // Delete the field
411             unset($this->fields[$i]);
412             // Reorder the whole structure
413             $this->orderFields($this->fields);
414             // Recalculate the hash
415             $this->calculateHash(true);
416             // We have one deleted field, so the table has changed
417             $this->setChanged(true);
418         }
419     }
421     /**
422      * Delete one key from the table
423      * @param string $keyname
424      */
425     public function deleteKey($keyname) {
427         $key = $this->getKey($keyname);
428         if ($key) {
429             $i = $this->findKeyInArray($keyname);
430             // Look for prev and next key
431             $prevkey = $this->getKey($key->getPrevious());
432             $nextkey = $this->getKey($key->getNext());
433             // Change their previous and next attributes
434             if ($prevkey) {
435                 $prevkey->setNext($key->getNext());
436             }
437             if ($nextkey) {
438                 $nextkey->setPrevious($key->getPrevious());
439             }
440             // Delete the key
441             unset($this->keys[$i]);
442             // Reorder the Keys
443             $this->orderKeys($this->keys);
444             // Recalculate the hash
445             $this->calculateHash(true);
446             // We have one deleted key, so the table has changed
447             $this->setChanged(true);
448         }
449     }
451     /**
452      * Delete one index from the table
453      * @param string $indexname
454      */
455     public function deleteIndex($indexname) {
457         $index = $this->getIndex($indexname);
458         if ($index) {
459             $i = $this->findIndexInArray($indexname);
460             // Look for prev and next index
461             $previndex = $this->getIndex($index->getPrevious());
462             $nextindex = $this->getIndex($index->getNext());
463             // Change their previous and next attributes
464             if ($previndex) {
465                 $previndex->setNext($index->getNext());
466             }
467             if ($nextindex) {
468                 $nextindex->setPrevious($index->getPrevious());
469             }
470             // Delete the index
471             unset($this->indexes[$i]);
472             // Reorder the indexes
473             $this->orderIndexes($this->indexes);
474             // Recalculate the hash
475             $this->calculateHash(true);
476             // We have one deleted index, so the table has changed
477             $this->setChanged(true);
478         }
479     }
481     /**
482      * Load data from XML to the table
483      * @param array $xmlarr
484      * @return bool success
485      */
486     public function arr2xmldb_table($xmlarr) {
488         global $CFG;
490         $result = true;
492         // Debug the table
493         // traverse_xmlize($xmlarr);                   //Debug
494         // print_object ($GLOBALS['traverse_array']);  //Debug
495         // $GLOBALS['traverse_array']="";              //Debug
497         // Process table attributes (name, comment, previoustable and nexttable)
498         if (isset($xmlarr['@']['NAME'])) {
499             $this->name = trim($xmlarr['@']['NAME']);
500         } else {
501             $this->errormsg = 'Missing NAME attribute';
502             $this->debug($this->errormsg);
503             $result = false;
504         }
505         if (isset($xmlarr['@']['COMMENT'])) {
506             $this->comment = trim($xmlarr['@']['COMMENT']);
507         } else if (!empty($CFG->xmldbdisablecommentchecking)) {
508             $this->comment = '';
509         } else {
510             $this->errormsg = 'Missing COMMENT attribute';
511             $this->debug($this->errormsg);
512             $result = false;
513         }
514         if (isset($xmlarr['@']['PREVIOUS'])) {
515             $this->previous = trim($xmlarr['@']['PREVIOUS']);
516         }
517         if (isset($xmlarr['@']['NEXT'])) {
518             $this->next = trim($xmlarr['@']['NEXT']);
519         }
521         // Iterate over fields
522         if (isset($xmlarr['#']['FIELDS']['0']['#']['FIELD'])) {
523             foreach ($xmlarr['#']['FIELDS']['0']['#']['FIELD'] as $xmlfield) {
524                 if (!$result) { //Skip on error
525                     continue;
526                 }
527                 $name = trim($xmlfield['@']['NAME']);
528                 $field = new xmldb_field($name);
529                 $field->arr2xmldb_field($xmlfield);
530                 $this->fields[] = $field;
531                 if (!$field->isLoaded()) {
532                     $this->errormsg = 'Problem loading field ' . $name;
533                     $this->debug($this->errormsg);
534                     $result = false;
535                 }
536             }
537         } else {
538             $this->errormsg = 'Missing FIELDS section';
539             $this->debug($this->errormsg);
540             $result = false;
541         }
543         // Perform some general checks over fields
544         if ($result && $this->fields) {
545             // Check field names are ok (lowercase, a-z _-)
546             if (!$this->checkNameValues($this->fields)) {
547                 $this->errormsg = 'Some FIELDS name values are incorrect';
548                 $this->debug($this->errormsg);
549                 $result = false;
550             }
551             // Check previous & next are ok (duplicates and existing fields)
552             $this->fixPrevNext($this->fields);
553             if ($result && !$this->checkPreviousNextValues($this->fields)) {
554                 $this->errormsg = 'Some FIELDS previous/next values are incorrect';
555                 $this->debug($this->errormsg);
556                 $result = false;
557             }
558             // Order fields
559             if ($result && !$this->orderFields($this->fields)) {
560                 $this->errormsg = 'Error ordering the fields';
561                 $this->debug($this->errormsg);
562                 $result = false;
563             }
564         }
566         // Iterate over keys
567         if (isset($xmlarr['#']['KEYS']['0']['#']['KEY'])) {
568             foreach ($xmlarr['#']['KEYS']['0']['#']['KEY'] as $xmlkey) {
569                 if (!$result) { //Skip on error
570                     continue;
571                 }
572                 $name = trim($xmlkey['@']['NAME']);
573                 $key = new xmldb_key($name);
574                 $key->arr2xmldb_key($xmlkey);
575                 $this->keys[] = $key;
576                 if (!$key->isLoaded()) {
577                     $this->errormsg = 'Problem loading key ' . $name;
578                     $this->debug($this->errormsg);
579                     $result = false;
580                 }
581             }
582         } else {
583             $this->errormsg = 'Missing KEYS section (at least one PK must exist)';
584             $this->debug($this->errormsg);
585             $result = false;
586         }
588         // Perform some general checks over keys
589         if ($result && $this->keys) {
590             // Check keys names are ok (lowercase, a-z _-)
591             if (!$this->checkNameValues($this->keys)) {
592                 $this->errormsg = 'Some KEYS name values are incorrect';
593                 $this->debug($this->errormsg);
594                 $result = false;
595             }
596             // Check previous & next are ok (duplicates and existing keys)
597             $this->fixPrevNext($this->keys);
598             if ($result && !$this->checkPreviousNextValues($this->keys)) {
599                 $this->errormsg = 'Some KEYS previous/next values are incorrect';
600                 $this->debug($this->errormsg);
601                 $result = false;
602             }
603             // Order keys
604             if ($result && !$this->orderKeys($this->keys)) {
605                 $this->errormsg = 'Error ordering the keys';
606                 $this->debug($this->errormsg);
607                 $result = false;
608             }
609             // TODO: Only one PK
610             // TODO: Not keys with repeated fields
611             // TODO: Check fields and reffieds exist in table
612         }
614         // Iterate over indexes
615         if (isset($xmlarr['#']['INDEXES']['0']['#']['INDEX'])) {
616             foreach ($xmlarr['#']['INDEXES']['0']['#']['INDEX'] as $xmlindex) {
617                 if (!$result) { //Skip on error
618                     continue;
619                 }
620                 $name = trim($xmlindex['@']['NAME']);
621                 $index = new xmldb_index($name);
622                 $index->arr2xmldb_index($xmlindex);
623                 $this->indexes[] = $index;
624                 if (!$index->isLoaded()) {
625                     $this->errormsg = 'Problem loading index ' . $name;
626                     $this->debug($this->errormsg);
627                     $result = false;
628                 }
629             }
630         }
632         // Perform some general checks over indexes
633         if ($result && $this->indexes) {
634             // Check field names are ok (lowercase, a-z _-)
635             if (!$this->checkNameValues($this->indexes)) {
636                 $this->errormsg = 'Some INDEXES name values are incorrect';
637                 $this->debug($this->errormsg);
638                 $result = false;
639             }
640             // Check previous & next are ok (duplicates and existing INDEXES)
641             $this->fixPrevNext($this->indexes);
642             if ($result && !$this->checkPreviousNextValues($this->indexes)) {
643                 $this->errormsg = 'Some INDEXES previous/next values are incorrect';
644                 $this->debug($this->errormsg);
645                 $result = false;
646             }
647             // Order indexes
648             if ($result && !$this->orderIndexes($this->indexes)) {
649                 $this->errormsg = 'Error ordering the indexes';
650                 $this->debug($this->errormsg);
651                 $result = false;
652             }
653             // TODO: Not indexes with repeated fields
654             // TODO: Check fields exist in table
655         }
657         // Set some attributes
658         if ($result) {
659             $this->loaded = true;
660         }
661         $this->calculateHash();
662         return $result;
663     }
665     /**
666      * This function calculate and set the hash of one xmldb_table
667      * @param bool $recursive
668      */
669      public function calculateHash($recursive = false) {
670         if (!$this->loaded) {
671             $this->hash = null;
672         } else {
673             $key = $this->name . $this->comment;
674             if ($this->fields) {
675                 foreach ($this->fields as $fie) {
676                     $field = $this->getField($fie->getName());
677                     if ($recursive) {
678                         $field->calculateHash($recursive);
679                     }
680                     $key .= $field->getHash();
681                 }
682             }
683             if ($this->keys) {
684                 foreach ($this->keys as $ke) {
685                     $k = $this->getKey($ke->getName());
686                     if ($recursive) {
687                         $k->calculateHash($recursive);
688                     }
689                     $key .= $k->getHash();
690                 }
691             }
692             if ($this->indexes) {
693                 foreach ($this->indexes as $in) {
694                     $index = $this->getIndex($in->getName());
695                     if ($recursive) {
696                         $index->calculateHash($recursive);
697                     }
698                     $key .= $index->getHash();
699                 }
700             }
701             $this->hash = md5($key);
702         }
703     }
705     /**
706      * Validates the table restrictions (does not validate child elements).
707      *
708      * The error message should not be localised because it is intended for developers,
709      * end users and admins should never see these problems!
710      *
711      * @param xmldb_table $xmldb_table optional when object is table
712      * @return string null if ok, error message if problem found
713      */
714     public function validateDefinition(xmldb_table $xmldb_table=null) {
715         // table parameter is ignored
716         $name = $this->getName();
717         if (strlen($name) > self::NAME_MAX_LENGTH) {
718             return 'Invalid table name {'.$name.'}: name is too long. Limit is 28 chars.';
719         }
720         if (!preg_match('/^[a-z][a-z0-9_]*$/', $name)) {
721             return 'Invalid table name {'.$name.'}: name includes invalid characters.';
722         }
724         return null;
725     }
727     /**
728      * This function will output the XML text for one table
729      * @return string
730      */
731     public function xmlOutput() {
732         $o = '';
733         $o.= '    <TABLE NAME="' . $this->name . '"';
734         if ($this->comment) {
735             $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
736         }
737         if ($this->previous) {
738             $o.= ' PREVIOUS="' . $this->previous . '"';
739         }
740         if ($this->next) {
741             $o.= ' NEXT="' . $this->next . '"';
742         }
743             $o.= '>' . "\n";
744         // Now the fields
745         if ($this->fields) {
746             $o.= '      <FIELDS>' . "\n";
747             foreach ($this->fields as $field) {
748                 $o.= $field->xmlOutput();
749             }
750             $o.= '      </FIELDS>' . "\n";
751         }
752         // Now the keys
753         if ($this->keys) {
754             $o.= '      <KEYS>' . "\n";
755             foreach ($this->keys as $key) {
756                 $o.= $key->xmlOutput();
757             }
758             $o.= '      </KEYS>' . "\n";
759         }
760         // Now the indexes
761         if ($this->indexes) {
762             $o.= '      <INDEXES>' . "\n";
763             foreach ($this->indexes as $index) {
764                 $o.= $index->xmlOutput();
765             }
766             $o.= '      </INDEXES>' . "\n";
767         }
768         $o.= '    </TABLE>' . "\n";
770         return $o;
771     }
773     /**
774      * This function will add one new field to the table with all
775      * its attributes defined
776      *
777      * @param string $name name of the field
778      * @param int $type XMLDB_TYPE_INTEGER, XMLDB_TYPE_NUMBER, XMLDB_TYPE_CHAR, XMLDB_TYPE_TEXT, XMLDB_TYPE_BINARY
779      * @param string $precision length for integers and chars, two-comma separated numbers for numbers
780      * @param bool $unsigned XMLDB_UNSIGNED or null (or false)
781      * @param bool $notnull XMLDB_NOTNULL or null (or false)
782      * @param bool $sequence XMLDB_SEQUENCE or null (or false)
783      * @param mixed $default meaningful default o null (or false)
784      * @param xmldb_object $previous name of the previous field in the table or null (or false)
785      * @return xmlddb_field
786      */
787     public function add_field($name, $type, $precision=null, $unsigned=null, $notnull=null, $sequence=null, $default=null, $previous=null) {
788         $field = new xmldb_field($name, $type, $precision, $unsigned, $notnull, $sequence, $default);
789         $this->addField($field, $previous);
791         return $field;
792     }
794     /**
795      * This function will add one new key to the table with all
796      * its attributes defined
797      *
798      * @param string $name name of the key
799      * @param int $type XMLDB_KEY_PRIMARY, XMLDB_KEY_UNIQUE, XMLDB_KEY_FOREIGN
800      * @param array $fields an array of fieldnames to build the key over
801      * @param string $reftable name of the table the FK points to or null
802      * @param array $reffields an array of fieldnames in the FK table or null
803      */
804     public function add_key($name, $type, $fields, $reftable=null, $reffields=null) {
805         $key = new xmldb_key($name, $type, $fields, $reftable, $reffields);
806         $this->addKey($key);
807     }
809     /**
810      * This function will add one new index to the table with all
811      * its attributes defined
812      *
813      * @param string $name name of the index
814      * @param int $type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
815      * @param array $fields an array of fieldnames to build the index over
816      * @param array $hints optional index type hints
817      */
818     public function add_index($name, $type, $fields, $hints = array()) {
819         $index = new xmldb_index($name, $type, $fields, $hints);
820         $this->addIndex($index);
821     }
823     /**
824      * This function will return all the errors found in one table
825      * looking recursively inside each field/key/index. Returns
826      * an array of errors or false
827      */
828     public function getAllErrors() {
830         $errors = array();
831         // First the table itself
832         if ($this->getError()) {
833             $errors[] = $this->getError();
834         }
835         // Delegate to fields
836         if ($fields = $this->getFields()) {
837             foreach ($fields as $field) {
838                 if ($field->getError()) {
839                     $errors[] = $field->getError();
840                 }
841             }
842         }
843         // Delegate to keys
844         if ($keys = $this->getKeys()) {
845             foreach ($keys as $key) {
846                 if ($key->getError()) {
847                     $errors[] = $key->getError();
848                 }
849             }
850         }
851         // Delegate to indexes
852         if ($indexes = $this->getIndexes()) {
853             foreach ($indexes as $index) {
854                 if ($index->getError()) {
855                     $errors[] = $index->getError();
856                 }
857             }
858         }
859         // Return decision
860         if (count($errors)) {
861             return $errors;
862         } else {
863             return false;
864         }
865     }