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 Index
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_index extends xmldb_object {
30
 
31
    /** @var bool is unique? */
32
    protected $unique;
33
 
34
    /** @var array index fields */
35
    protected $fields;
36
 
37
    /** @var array index hints */
38
    protected $hints;
39
 
40
    /**
41
     * Note:
42
     *  - MySQL: MyISAM has a limit of 1000 bytes for any key including composed, InnoDB has limit 3500 bytes.
43
     *
44
     * @var max length of composed indexes, one utf-8 char is 3 bytes in the worst case
45
     */
46
    const INDEX_COMPOSED_MAX_BYTES = 999;
47
 
48
    /**
49
     * Note:
50
     *  - MySQL: InnoDB limits size of index on single column to 767bytes (256 chars)
51
     *
52
     * @var single column index length limit, one utf-8 char is 3 bytes in the worst case
53
     */
54
    const INDEX_MAX_BYTES = 765;
55
 
56
    /**
57
     * Creates one new xmldb_index
58
     *
59
     * @param string $name
60
     * @param string $type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
61
     * @param array $fields an array of fieldnames to build the index over
62
     * @param array $hints an array of optional hints
63
     */
64
    public function __construct($name, $type=null, $fields=array(), $hints=array()) {
65
        $this->unique = false;
66
        $this->fields = array();
67
        $this->hints = array();
68
        parent::__construct($name);
69
        $this->set_attributes($type, $fields, $hints);
70
    }
71
 
72
    /**
73
     * Set all the attributes of one xmldb_index
74
     *
75
     * @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
76
     * @param array fields an array of fieldnames to build the index over
77
     * @param array $hints array of optional hints
78
     */
79
    public function set_attributes($type, $fields, $hints = array()) {
80
        $this->unique = !empty($type) ? true : false;
81
        $this->fields = $fields;
82
        $this->hints = $hints;
83
    }
84
 
85
    /**
86
     * Get the index unique
87
     * @return bool
88
     */
89
    public function getUnique() {
90
        return $this->unique;
91
    }
92
 
93
    /**
94
     * Set the index unique
95
     * @param bool $unique
96
     */
97
    public function setUnique($unique = true) {
98
        $this->unique = $unique;
99
    }
100
 
101
    /**
102
     * Set the index fields
103
     * @param array $fields
104
     */
105
    public function setFields($fields) {
106
        $this->fields = $fields;
107
    }
108
 
109
    /**
110
     * Get the index fields
111
     * @return array
112
     */
113
    public function getFields() {
114
        return $this->fields;
115
    }
116
 
117
    /**
118
     * Set optional index hints.
119
     * @param array $hints
120
     */
121
    public function setHints($hints) {
122
        $this->hints = $hints;
123
    }
124
 
125
    /**
126
     * Returns optional index hints.
127
     * @return array
128
     */
129
    public function getHints() {
130
        return $this->hints;
131
    }
132
 
133
    /**
134
     * Load data from XML to the index
135
     * @param $xmlarr array
136
     * @return bool
137
     */
138
    public function arr2xmldb_index($xmlarr) {
139
 
140
        $result = true;
141
 
142
        // Debug the table
143
        // traverse_xmlize($xmlarr);                   //Debug
144
        // print_object ($GLOBALS['traverse_array']);  //Debug
145
        // $GLOBALS['traverse_array']="";              //Debug
146
 
147
        // Process key attributes (name, unique, fields, comment, previous, next)
148
        if (isset($xmlarr['@']['NAME'])) {
149
            $this->name = trim($xmlarr['@']['NAME']);
150
        } else {
151
            $this->errormsg = 'Missing NAME attribute';
152
            $this->debug($this->errormsg);
153
            $result = false;
154
        }
155
 
156
        if (isset($xmlarr['@']['UNIQUE'])) {
157
            $unique = strtolower(trim($xmlarr['@']['UNIQUE']));
158
            if ($unique == 'true') {
159
                $this->unique = true;
160
            } else if ($unique == 'false') {
161
                $this->unique = false;
162
            } else {
163
                $this->errormsg = 'Incorrect UNIQUE attribute (true/false allowed)';
164
                $this->debug($this->errormsg);
165
                $result = false;
166
            }
167
        } else {
168
                $this->errormsg = 'Undefined UNIQUE attribute';
169
                $this->debug($this->errormsg);
170
                $result = false;
171
        }
172
 
173
        if (isset($xmlarr['@']['FIELDS'])) {
174
            $fields = strtolower(trim($xmlarr['@']['FIELDS']));
175
            if ($fields) {
176
                $fieldsarr = explode(',',$fields);
177
                if ($fieldsarr) {
178
                    foreach ($fieldsarr as $key => $element) {
179
                        $fieldsarr [$key] = trim($element);
180
                    }
181
                } else {
182
                    $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
183
                    $this->debug($this->errormsg);
184
                    $result = false;
185
                }
186
            } else {
187
                $this->errormsg = 'Empty FIELDS attribute';
188
                $this->debug($this->errormsg);
189
                $result = false;
190
            }
191
        } else {
192
            $this->errormsg = 'Missing FIELDS attribute';
193
            $this->debug($this->errormsg);
194
            $result = false;
195
        }
196
        // Finally, set the array of fields
197
        $this->fields = $fieldsarr;
198
 
199
        if (isset($xmlarr['@']['HINTS'])) {
200
            $this->hints = array();
201
            $hints = strtolower(trim($xmlarr['@']['HINTS']));
202
            if ($hints !== '') {
203
                $hints = explode(',', $hints);
204
                $this->hints = array_map('trim', $hints);
205
            }
206
        }
207
 
208
        if (isset($xmlarr['@']['COMMENT'])) {
209
            $this->comment = trim($xmlarr['@']['COMMENT']);
210
        }
211
 
212
        // Set some attributes
213
        if ($result) {
214
            $this->loaded = true;
215
        }
216
        $this->calculateHash();
217
        return $result;
218
    }
219
 
220
    /**
221
     * This function calculate and set the hash of one xmldb_index
222
     * @retur nvoid, changes $this->hash
223
     */
224
     public function calculateHash($recursive = false) {
225
        if (!$this->loaded) {
226
            $this->hash = null;
227
        } else {
228
            $key = $this->unique . implode (', ', $this->fields) . implode (', ', $this->hints);
229
            $this->hash = md5($key);
230
        }
231
    }
232
 
233
    /**
234
     *This function will output the XML text for one index
235
     * @return string
236
     */
237
    public function xmlOutput() {
238
        $o = '';
239
        $o.= '        <INDEX NAME="' . $this->name . '"';
240
        if ($this->unique) {
241
            $unique = 'true';
242
        } else {
243
            $unique = 'false';
244
        }
245
        $o.= ' UNIQUE="' . $unique . '"';
246
        $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
247
        if ($this->hints) {
248
            $o.= ' HINTS="' . implode(', ', $this->hints) . '"';
249
        }
250
        if ($this->comment) {
251
            $o.= ' COMMENT="' . htmlspecialchars($this->comment, ENT_COMPAT) . '"';
252
        }
253
        $o.= '/>' . "\n";
254
 
255
        return $o;
256
    }
257
 
258
    /**
259
     * This function will set all the attributes of the xmldb_index object
260
     * based on information passed in one ADOindex
261
     * @param array
262
     * @return void
263
     */
264
    public function setFromADOIndex($adoindex) {
265
 
266
        // Set the unique field
267
        $this->unique = false;
268
        // Set the fields, converting all them to lowercase
269
        $fields = array_flip(array_change_key_case(array_flip($adoindex['columns'])));
270
        $this->fields = $fields;
271
        // Some more fields
272
        $this->loaded = true;
273
        $this->changed = true;
274
    }
275
 
276
    /**
277
     * Returns the PHP code needed to define one xmldb_index
278
     * @return string
279
     */
280
    public function getPHP() {
281
 
282
        $result = '';
283
 
284
        // The type
285
        $unique = $this->getUnique();
286
        if (!empty($unique)) {
287
            $result .= 'XMLDB_INDEX_UNIQUE, ';
288
        } else {
289
            $result .= 'XMLDB_INDEX_NOTUNIQUE, ';
290
        }
291
        // The fields
292
        $indexfields = $this->getFields();
293
        if (!empty($indexfields)) {
294
            $result .= "['".  implode("', '", $indexfields) . "']";
295
        } else {
296
            $result .= 'null';
297
        }
298
        // Hints
299
        $hints = $this->getHints();
300
        if (!empty($hints)) {
301
            $result .= ", ['".  implode("', '", $hints) . "']";
302
        }
303
 
304
        // Return result
305
        return $result;
306
    }
307
 
308
    /**
309
     * Shows info in a readable format
310
     * @return string
311
     */
312
    public function readableInfo() {
313
        $o = '';
314
        // unique
315
        if ($this->unique) {
316
            $o .= 'unique';
317
        } else {
318
            $o .= 'not unique';
319
        }
320
        // fields
321
        $o .= ' (' . implode(', ', $this->fields) . ')';
322
 
323
        if ($this->hints) {
324
            $o .= ' [' . implode(', ', $this->hints) . ']';
325
        }
326
 
327
        return $o;
328
    }
329
 
330
    /**
331
     * Validates the index restrictions.
332
     *
333
     * The error message should not be localised because it is intended for developers,
334
     * end users and admins should never see these problems!
335
     *
336
     * @param xmldb_table $xmldb_table optional when object is table
337
     * @return string null if ok, error message if problem found
338
     */
339
    public function validateDefinition(xmldb_table $xmldb_table=null) {
340
        if (!$xmldb_table) {
341
            return 'Invalid xmldb_index->validateDefinition() call, $xmldb_table is required.';
342
        }
343
 
344
        $total = 0;
345
        foreach ($this->getFields() as $fieldname) {
346
            if (!$field = $xmldb_table->getField($fieldname)) {
347
                // argh, we do not have the fields loaded yet, this should not happen during install
348
                continue;
349
            }
350
 
351
            switch ($field->getType()) {
352
                case XMLDB_TYPE_INTEGER:
353
                    $total += 8; // big int
354
                    break;
355
 
356
                case XMLDB_TYPE_NUMBER:
357
                    $total += 12; // this is just a guess
358
                    break;
359
 
360
                case XMLDB_TYPE_FLOAT:
361
                    $total += 8; // double precision
362
                    break;
363
 
364
                case XMLDB_TYPE_CHAR:
365
                    if ($field->getLength() > self::INDEX_MAX_BYTES / 3) {
366
                        return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$field->getName().'" can not be indexed because it is too long.'
367
                                .' Limit is '.(self::INDEX_MAX_BYTES/3).' chars.';
368
                    }
369
                    $total += ($field->getLength() * 3); // the most complex utf-8 chars have 3 bytes
370
                    break;
371
 
372
                case XMLDB_TYPE_TEXT:
373
                    return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_TEXT field "'.$field->getName().'" can not be indexed';
374
                    break;
375
 
376
                case XMLDB_TYPE_BINARY:
377
                    return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_BINARY field "'.$field->getName().'" can not be indexed';
378
                    break;
379
 
380
                case XMLDB_TYPE_DATETIME:
381
                    $total += 8; // this is just a guess
382
                    break;
383
 
384
                case XMLDB_TYPE_TIMESTAMP:
385
                    $total += 8; // this is just a guess
386
                    break;
387
            }
388
        }
389
 
390
        if ($total > self::INDEX_COMPOSED_MAX_BYTES) {
391
            return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: the composed index on fields "'.implode(',', $this->getFields()).'" is too long.'
392
                    .' Limit is '.self::INDEX_COMPOSED_MAX_BYTES.' bytes / '.(self::INDEX_COMPOSED_MAX_BYTES/3).' chars.';
393
        }
394
 
395
        return null;
396
    }
397
}