2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * This class represent one XMLDB Index
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
26 defined('MOODLE_INTERNAL') ||
die();
29 class xmldb_index
extends xmldb_object
{
31 /** @var bool is unique? */
34 /** @var array index fields */
39 * - MySQL: MyISAM has a limit of 1000 bytes for any key including composed, InnoDB has limit 3500 bytes.
41 * @const max length of composed indexes, one utf-8 char is 3 bytes in the worst case
43 const INDEX_COMPOSED_MAX_BYTES
= 999;
47 * - MySQL: InnoDB limits size of index on single column to 767bytes (256 chars)
49 * @const single column index length limit, one utf-8 char is 3 bytes in the worst case
51 const INDEX_MAX_BYTES
= 765;
54 * Creates one new xmldb_index
57 * @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
58 * @param array fields an array of fieldnames to build the index over
60 function __construct($name, $type=null, $fields=array()) {
61 $this->unique
= false;
62 $this->fields
= array();
63 parent
::__construct($name);
64 $this->set_attributes($type, $fields);
68 * Set all the attributes of one xmldb_index
70 * @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
71 * @param array fields an array of fieldnames to build the index over
73 function set_attributes($type, $fields) {
74 $this->unique
= !empty($type) ?
true : false;
75 $this->fields
= $fields;
79 * Get the index unique
82 function getUnique() {
87 * Set the index unique
90 function setUnique($unique = true) {
91 $this->unique
= $unique;
95 * Set the index fields
96 * @param array $fields
98 function setFields($fields) {
99 $this->fields
= $fields;
103 * Get the index fields
104 * @return array reference to fields array
106 function &getFields() {
107 return $this->fields
;
111 * Load data from XML to the index
112 * @param $xmlarr array
115 function arr2xmldb_index($xmlarr) {
120 // traverse_xmlize($xmlarr); //Debug
121 // print_object ($GLOBALS['traverse_array']); //Debug
122 // $GLOBALS['traverse_array']=""; //Debug
124 // Process key attributes (name, unique, fields, comment, previous, next)
125 if (isset($xmlarr['@']['NAME'])) {
126 $this->name
= trim($xmlarr['@']['NAME']);
128 $this->errormsg
= 'Missing NAME attribute';
129 $this->debug($this->errormsg
);
133 if (isset($xmlarr['@']['UNIQUE'])) {
134 $unique = strtolower(trim($xmlarr['@']['UNIQUE']));
135 if ($unique == 'true') {
136 $this->unique
= true;
137 } else if ($unique == 'false') {
138 $this->unique
= false;
140 $this->errormsg
= 'Incorrect UNIQUE attribute (true/false allowed)';
141 $this->debug($this->errormsg
);
145 $this->errormsg
= 'Undefined UNIQUE attribute';
146 $this->debug($this->errormsg
);
150 if (isset($xmlarr['@']['FIELDS'])) {
151 $fields = strtolower(trim($xmlarr['@']['FIELDS']));
153 $fieldsarr = explode(',',$fields);
155 foreach ($fieldsarr as $key => $element) {
156 $fieldsarr [$key] = trim($element);
159 $this->errormsg
= 'Incorrect FIELDS attribute (comma separated of fields)';
160 $this->debug($this->errormsg
);
164 $this->errormsg
= 'Empty FIELDS attribute';
165 $this->debug($this->errormsg
);
169 $this->errormsg
= 'Missing FIELDS attribute';
170 $this->debug($this->errormsg
);
173 // Finally, set the array of fields
174 $this->fields
= $fieldsarr;
176 if (isset($xmlarr['@']['COMMENT'])) {
177 $this->comment
= trim($xmlarr['@']['COMMENT']);
180 if (isset($xmlarr['@']['PREVIOUS'])) {
181 $this->previous
= trim($xmlarr['@']['PREVIOUS']);
184 if (isset($xmlarr['@']['NEXT'])) {
185 $this->next
= trim($xmlarr['@']['NEXT']);
188 // Set some attributes
190 $this->loaded
= true;
192 $this->calculateHash();
197 * This function calculate and set the hash of one xmldb_index
198 * @retur nvoid, changes $this->hash
200 function calculateHash($recursive = false) {
201 if (!$this->loaded
) {
204 $key = $this->unique
. implode (', ', $this->fields
);
205 $this->hash
= md5($key);
210 *This function will output the XML text for one index
213 function xmlOutput() {
215 $o.= ' <INDEX NAME="' . $this->name
. '"';
221 $o.= ' UNIQUE="' . $unique . '"';
222 $o.= ' FIELDS="' . implode(', ', $this->fields
) . '"';
223 if ($this->comment
) {
224 $o.= ' COMMENT="' . htmlspecialchars($this->comment
) . '"';
226 if ($this->previous
) {
227 $o.= ' PREVIOUS="' . $this->previous
. '"';
230 $o.= ' NEXT="' . $this->next
. '"';
238 * This function will set all the attributes of the xmldb_index object
239 * based on information passed in one ADOindex
243 function setFromADOIndex($adoindex) {
245 // Set the unique field
246 $this->unique
= false;
247 // Set the fields, converting all them to lowercase
248 $fields = array_flip(array_change_key_case(array_flip($adoindex['columns'])));
249 $this->fields
= $fields;
251 $this->loaded
= true;
252 $this->changed
= true;
256 * Returns the PHP code needed to define one xmldb_index
264 $unique = $this->getUnique();
265 if (!empty($unique)) {
266 $result .= 'XMLDB_INDEX_UNIQUE, ';
268 $result .= 'XMLDB_INDEX_NOTUNIQUE, ';
271 $indexfields = $this->getFields();
272 if (!empty($indexfields)) {
273 $result .= 'array(' . "'". implode("', '", $indexfields) . "')";
282 * Shows info in a readable format
285 function readableInfo() {
294 $o .= ' (' . implode(', ', $this->fields
) . ')';
300 * Validates the index restrictions.
302 * The error message should not be localised because it is intended for developers,
303 * end users and admins should never see these problems!
305 * @param xmldb_table $xmldb_table optional when object is table
306 * @return string null if ok, error message if problem found
308 function validateDefinition(xmldb_table
$xmldb_table=null) {
310 return 'Invalid xmldb_index->validateDefinition() call, $xmldb_table si required.';
314 foreach ($this->getFields() as $fieldname) {
315 if (!$field = $xmldb_table->getField($fieldname)) {
316 // argh, we do not have the fields loaded yet, this should not happen during install
320 switch ($field->getType()) {
321 case XMLDB_TYPE_INTEGER
:
322 $total +
= 8; // big int
325 case XMLDB_TYPE_NUMBER
:
326 $total +
= 12; // this is just a guess
329 case XMLDB_TYPE_FLOAT
:
330 $total +
= 8; // double precision
333 case XMLDB_TYPE_CHAR
:
334 if ($field->getLength() > self
::INDEX_MAX_BYTES
/ 3) {
335 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$field->getName().'" can not be indexed because it is too long.'
336 .' Limit is '.(self
::INDEX_MAX_BYTES
/3).' chars.';
338 $total +
= ($field->getLength() * 3); // the most complex utf-8 chars have 3 bytes
341 case XMLDB_TYPE_TEXT
:
342 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_TEXT field "'.$field->getName().'" can not be indexed';
345 case XMLDB_TYPE_BINARY
:
346 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_BINARY field "'.$field->getName().'" can not be indexed';
349 case XMLDB_TYPE_DATETIME
:
350 $total +
= 8; // this is just a guess
353 case XMLDB_TYPE_TIMESTAMP
:
354 $total +
= 8; // this is just a guess
359 if ($total > self
::INDEX_COMPOSED_MAX_BYTES
) {
360 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: the composed index on fields "'.implode(',', $this->getFields()).'" is too long.'
361 .' Limit is '.self
::INDEX_COMPOSED_MAX_BYTES
.' bytes / '.(self
::INDEX_COMPOSED_MAX_BYTES
/3).' chars.';