See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 403]
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 * @const 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 * @const 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body