Merge branch 'MDL-29276' of git://github.com/mouneyrac/moodle
[moodle.git] / lib / xmldb / xmldb_index.php
blob5763a43440df03322a9a439aae010c6d52d8b0fe
1 <?php
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12 // //
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. //
17 // //
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: //
22 // //
23 // http://www.gnu.org/copyleft/gpl.html //
24 // //
25 ///////////////////////////////////////////////////////////////////////////
27 /// This class represent one XMLDB Index
29 class xmldb_index extends xmldb_object {
31 var $unique;
32 var $fields;
34 /**
35 * Note:
36 * - MySQL: MyISAM has a limit of 1000 bytes for any key including composed, InnoDB has limit 3500 bytes.
38 * @const max length of composed indexes, one utf-8 char is 3 bytes in the worst case
40 const INDEX_COMPOSED_MAX_BYTES = 999;
42 /**
43 * Note:
44 * - MySQL: InnoDB limits size of index on single column to 767bytes (256 chars)
46 * @const single column index length limit, one utf-8 char is 3 bytes in the worst case
48 const INDEX_MAX_BYTES = 765;
50 /**
51 * Creates one new xmldb_index
53 function __construct($name, $type=null, $fields=array()) {
54 $this->unique = false;
55 $this->fields = array();
56 parent::__construct($name);
57 return $this->set_attributes($type, $fields);
60 /// TODO: Delete for 2.1 (deprecated in 2.0).
61 /// Deprecated API starts here
62 function setAttributes($type, $fields) {
64 debugging('XMLDBIndex->setAttributes() has been deprecated in Moodle 2.0. Will be out in Moodle 2.1. Please use xmldb_index->set_attributes() instead.', DEBUG_DEVELOPER);
66 return $this->set_attributes($type, $fields);
68 /// Deprecated API ends here
70 /**
71 * Set all the attributes of one xmldb_index
73 * @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
74 * @param array fields an array of fieldnames to build the index over
76 function set_attributes($type, $fields) {
77 $this->unique = !empty($type) ? true : false;
78 $this->fields = $fields;
81 /**
82 * Get the index unique
84 function getUnique() {
85 return $this->unique;
88 /**
89 * Set the index unique
91 function setUnique($unique = true) {
92 $this->unique = $unique;
95 /**
96 * Set the index fields
98 function setFields($fields) {
99 $this->fields = $fields;
103 * Get the index fields
105 function &getFields() {
106 return $this->fields;
110 * Load data from XML to the index
112 function arr2xmldb_index($xmlarr) {
114 $result = true;
116 /// Debug the table
117 /// traverse_xmlize($xmlarr); //Debug
118 /// print_object ($GLOBALS['traverse_array']); //Debug
119 /// $GLOBALS['traverse_array']=""; //Debug
121 /// Process key attributes (name, unique, fields, comment, previous, next)
122 if (isset($xmlarr['@']['NAME'])) {
123 $this->name = trim($xmlarr['@']['NAME']);
124 } else {
125 $this->errormsg = 'Missing NAME attribute';
126 $this->debug($this->errormsg);
127 $result = false;
130 if (isset($xmlarr['@']['UNIQUE'])) {
131 $unique = strtolower(trim($xmlarr['@']['UNIQUE']));
132 if ($unique == 'true') {
133 $this->unique = true;
134 } else if ($unique == 'false') {
135 $this->unique = false;
136 } else {
137 $this->errormsg = 'Incorrect UNIQUE attribute (true/false allowed)';
138 $this->debug($this->errormsg);
139 $result = false;
141 } else {
142 $this->errormsg = 'Undefined UNIQUE attribute';
143 $this->debug($this->errormsg);
144 $result = false;
147 if (isset($xmlarr['@']['FIELDS'])) {
148 $fields = strtolower(trim($xmlarr['@']['FIELDS']));
149 if ($fields) {
150 $fieldsarr = explode(',',$fields);
151 if ($fieldsarr) {
152 foreach ($fieldsarr as $key => $element) {
153 $fieldsarr [$key] = trim($element);
155 } else {
156 $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
157 $this->debug($this->errormsg);
158 $result = false;
160 } else {
161 $this->errormsg = 'Empty FIELDS attribute';
162 $this->debug($this->errormsg);
163 $result = false;
165 } else {
166 $this->errormsg = 'Missing FIELDS attribute';
167 $this->debug($this->errormsg);
168 $result = false;
170 /// Finally, set the array of fields
171 $this->fields = $fieldsarr;
173 if (isset($xmlarr['@']['COMMENT'])) {
174 $this->comment = trim($xmlarr['@']['COMMENT']);
177 if (isset($xmlarr['@']['PREVIOUS'])) {
178 $this->previous = trim($xmlarr['@']['PREVIOUS']);
181 if (isset($xmlarr['@']['NEXT'])) {
182 $this->next = trim($xmlarr['@']['NEXT']);
185 /// Set some attributes
186 if ($result) {
187 $this->loaded = true;
189 $this->calculateHash();
190 return $result;
194 * This function calculate and set the hash of one xmldb_index
196 function calculateHash($recursive = false) {
197 if (!$this->loaded) {
198 $this->hash = NULL;
199 } else {
200 $key = $this->unique . implode (', ', $this->fields);
201 $this->hash = md5($key);
206 *This function will output the XML text for one index
208 function xmlOutput() {
209 $o = '';
210 $o.= ' <INDEX NAME="' . $this->name . '"';
211 if ($this->unique) {
212 $unique = 'true';
213 } else {
214 $unique = 'false';
216 $o.= ' UNIQUE="' . $unique . '"';
217 $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
218 if ($this->comment) {
219 $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
221 if ($this->previous) {
222 $o.= ' PREVIOUS="' . $this->previous . '"';
224 if ($this->next) {
225 $o.= ' NEXT="' . $this->next . '"';
227 $o.= '/>' . "\n";
229 return $o;
233 * This function will set all the attributes of the xmldb_index object
234 * based on information passed in one ADOindex
236 function setFromADOIndex($adoindex) {
238 /// Set the unique field
239 $this->unique = false;
240 /// Set the fields, converting all them to lowercase
241 $fields = array_flip(array_change_key_case(array_flip($adoindex['columns'])));
242 $this->fields = $fields;
243 /// Some more fields
244 $this->loaded = true;
245 $this->changed = true;
249 * Returns the PHP code needed to define one xmldb_index
251 function getPHP() {
253 $result = '';
255 /// The type
256 $unique = $this->getUnique();
257 if (!empty($unique)) {
258 $result .= 'XMLDB_INDEX_UNIQUE, ';
259 } else {
260 $result .= 'XMLDB_INDEX_NOTUNIQUE, ';
262 /// The fields
263 $indexfields = $this->getFields();
264 if (!empty($indexfields)) {
265 $result .= 'array(' . "'". implode("', '", $indexfields) . "')";
266 } else {
267 $result .= 'null';
269 /// Return result
270 return $result;
274 * Shows info in a readable format
276 function readableInfo() {
277 $o = '';
278 /// unique
279 if ($this->unique) {
280 $o .= 'unique';
281 } else {
282 $o .= 'not unique';
284 /// fields
285 $o .= ' (' . implode(', ', $this->fields) . ')';
287 return $o;
291 * Validates the index restrictions.
293 * The error message should not be localised because it is intended for developers,
294 * end users and admins should never see these problems!
296 * @param xmldb_table $xmldb_table optional when object is table
297 * @return string null if ok, error message if problem found
299 function validateDefinition(xmldb_table $xmldb_table=null) {
300 if (!$xmldb_table) {
301 return 'Invalid xmldb_index->validateDefinition() call, $xmldb_table si required.';
304 $total = 0;
305 foreach ($this->getFields() as $fieldname) {
306 if (!$field = $xmldb_table->getField($fieldname)) {
307 // argh, we do not have the fields loaded yet, this should not happen during install
308 continue;
311 switch ($field->getType()) {
312 case XMLDB_TYPE_INTEGER:
313 $total += 8; // big int
314 break;
316 case XMLDB_TYPE_NUMBER:
317 $total += 12; // this is just a guess
318 break;
320 case XMLDB_TYPE_FLOAT:
321 $total += 8; // double precision
322 break;
324 case XMLDB_TYPE_CHAR:
325 if ($field->getLength() > self::INDEX_MAX_BYTES / 3) {
326 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$field->getName().'" can not be indexed because it is too long.'
327 .' Limit is '.(self::INDEX_MAX_BYTES/3).' chars.';
329 $total += ($field->getLength() * 3); // the most complex utf-8 chars have 3 bytes
330 break;
332 case XMLDB_TYPE_TEXT:
333 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_TEXT field "'.$field->getName().'" can not be indexed';
334 break;
336 case XMLDB_TYPE_BINARY:
337 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_BINARY field "'.$field->getName().'" can not be indexed';
338 break;
340 case XMLDB_TYPE_DATETIME:
341 $total += 8; // this is just a guess
342 break;
344 case XMLDB_TYPE_TIMESTAMP:
345 $total += 8; // this is just a guess
346 break;
350 if ($total > self::INDEX_COMPOSED_MAX_BYTES) {
351 return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: the composed index on fields "'.implode(',', $this->getFields()).'" is too long.'
352 .' Limit is '.self::INDEX_COMPOSED_MAX_BYTES.' bytes / '.(self::INDEX_COMPOSED_MAX_BYTES/3).' chars.';
355 return null;
360 /// TODO: Delete for 2.1 (deprecated in 2.0).
361 /// Deprecated API starts here
362 class XMLDBIndex extends xmldb_index {
364 function __construct($name) {
365 parent::__construct($name);
369 /// Deprecated API ends here