Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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