mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 17:02:03 +02:00
MDL-29314 enforce index size limits
This commit is contained in:
parent
9cfaebbd0e
commit
b2cfdcf697
@ -1487,6 +1487,115 @@ class ddl_test extends UnitTestCase {
|
||||
$this->assertTrue(count($reserved) > 1);
|
||||
}
|
||||
|
||||
public function test_index_max_bytes() {
|
||||
$DB = $this->tdb;
|
||||
$dbman = $DB->get_manager();
|
||||
|
||||
$maxstr = '';
|
||||
for($i=0; $i<255; $i++) {
|
||||
$maxstr .= '言'; // random long string that should fix exactly the limit for one char column
|
||||
}
|
||||
|
||||
$table = new xmldb_table('testtable');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
|
||||
|
||||
// Drop if exists
|
||||
if ($dbman->table_exists($table)) {
|
||||
$dbman->drop_table($table);
|
||||
}
|
||||
$dbman->create_table($table);
|
||||
$tablename = $table->getName();
|
||||
$this->tables[$tablename] = $table;
|
||||
|
||||
$rec = new stdClass();
|
||||
$rec->name = $maxstr;
|
||||
|
||||
$id = $DB->insert_record($tablename, $rec);
|
||||
$this->assertTrue(!empty($id));
|
||||
|
||||
$rec = $DB->get_record($tablename, array('id'=>$id));
|
||||
$this->assertIdentical($rec->name, $maxstr);
|
||||
|
||||
$dbman->drop_table($table);
|
||||
|
||||
|
||||
$table = new xmldb_table('testtable');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('name', XMLDB_TYPE_CHAR, 255+1, null, XMLDB_NOTNULL, null);
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
|
||||
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->assertTrue(false);
|
||||
} catch (Exception $e) {
|
||||
$this->assertTrue($e instanceof coding_exception);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_index_composed_max_bytes() {
|
||||
$DB = $this->tdb;
|
||||
$dbman = $DB->get_manager();
|
||||
|
||||
$maxstr = '';
|
||||
for($i=0; $i<200; $i++) {
|
||||
$maxstr .= '言';
|
||||
}
|
||||
$reststr = '';
|
||||
for($i=0; $i<133; $i++) {
|
||||
$reststr .= '言';
|
||||
}
|
||||
|
||||
$table = new xmldb_table('testtable');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('name1', XMLDB_TYPE_CHAR, 200, null, XMLDB_NOTNULL, null);
|
||||
$table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1','name2'));
|
||||
|
||||
// Drop if exists
|
||||
if ($dbman->table_exists($table)) {
|
||||
$dbman->drop_table($table);
|
||||
}
|
||||
$dbman->create_table($table);
|
||||
$tablename = $table->getName();
|
||||
$this->tables[$tablename] = $table;
|
||||
|
||||
$rec = new stdClass();
|
||||
$rec->name1 = $maxstr;
|
||||
$rec->name2 = $reststr;
|
||||
|
||||
$id = $DB->insert_record($tablename, $rec);
|
||||
$this->assertTrue(!empty($id));
|
||||
|
||||
$rec = $DB->get_record($tablename, array('id'=>$id));
|
||||
$this->assertIdentical($rec->name1, $maxstr);
|
||||
$this->assertIdentical($rec->name2, $reststr);
|
||||
|
||||
|
||||
$table = new xmldb_table('testtable');
|
||||
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
|
||||
$table->add_field('name1', XMLDB_TYPE_CHAR, 201, null, XMLDB_NOTNULL, null);
|
||||
$table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
|
||||
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
|
||||
$table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1','name2'));
|
||||
|
||||
// Drop if exists
|
||||
if ($dbman->table_exists($table)) {
|
||||
$dbman->drop_table($table);
|
||||
}
|
||||
|
||||
try {
|
||||
$dbman->create_table($table);
|
||||
$this->assertTrue(false);
|
||||
} catch (Exception $e) {
|
||||
$this->assertTrue($e instanceof coding_exception);
|
||||
}
|
||||
}
|
||||
|
||||
// Following methods are not supported == Do not test
|
||||
/*
|
||||
public function testRenameIndex() {
|
||||
|
@ -348,6 +348,9 @@ abstract class sql_generator {
|
||||
* needed to create it (in array)
|
||||
*/
|
||||
public function getCreateIndexSQL($xmldb_table, $xmldb_index) {
|
||||
if ($error = $xmldb_index->validateDefinition($xmldb_table)) {
|
||||
throw new coding_exception($error);
|
||||
}
|
||||
|
||||
$unique = '';
|
||||
$suffix = 'ix';
|
||||
|
@ -31,6 +31,22 @@ class xmldb_index extends xmldb_object {
|
||||
var $unique;
|
||||
var $fields;
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* - MySQL: MyISAM has a limit of 1000 bytes for any key including composed, InnoDB has limit 3500 bytes.
|
||||
*
|
||||
* @const max length of composed indexes, one utf-8 char is 3 bytes in the worst case
|
||||
*/
|
||||
const INDEX_COMPOSED_MAX_BYTES = 999;
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* - MySQL: InnoDB limits size of index on single column to 767bytes (256 chars)
|
||||
*
|
||||
* @const single column index length limit, one utf-8 char is 3 bytes in the worst case
|
||||
*/
|
||||
const INDEX_MAX_BYTES = 765;
|
||||
|
||||
/**
|
||||
* Creates one new xmldb_index
|
||||
*/
|
||||
@ -270,6 +286,75 @@ class xmldb_index extends xmldb_object {
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the index restrictions.
|
||||
*
|
||||
* The error message should not be localised because it is intended for developers,
|
||||
* end users and admins should never see these problems!
|
||||
*
|
||||
* @param xmldb_table $xmldb_table optional when object is table
|
||||
* @return string null if ok, error message if problem found
|
||||
*/
|
||||
function validateDefinition(xmldb_table $xmldb_table=null) {
|
||||
if (!$xmldb_table) {
|
||||
return 'Invalid xmldb_index->validateDefinition() call, $xmldb_table si required.';
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
foreach ($this->getFields() as $fieldname) {
|
||||
if (!$field = $xmldb_table->getField($fieldname)) {
|
||||
// argh, we do not have the fields loaded yet, this should not happen during install
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($field->getType()) {
|
||||
case XMLDB_TYPE_INTEGER:
|
||||
$total += 8; // big int
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_NUMBER:
|
||||
$total += 12; // this is just a guess
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_FLOAT:
|
||||
$total += 8; // double precision
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_CHAR:
|
||||
if ($field->getLength() > self::INDEX_MAX_BYTES / 3) {
|
||||
return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_CHAR field "'.$field->getName().'" can not be indexed because it is too long.'
|
||||
.' Limit is '.(self::INDEX_MAX_BYTES/3).' chars.';
|
||||
}
|
||||
$total += ($field->getLength() * 3); // the most complex utf-8 chars have 3 bytes
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_TEXT:
|
||||
return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_TEXT field "'.$field->getName().'" can not be indexed';
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_BINARY:
|
||||
return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: XMLDB_TYPE_BINARY field "'.$field->getName().'" can not be indexed';
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_DATETIME:
|
||||
$total += 8; // this is just a guess
|
||||
break;
|
||||
|
||||
case XMLDB_TYPE_TIMESTAMP:
|
||||
$total += 8; // this is just a guess
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($total > self::INDEX_COMPOSED_MAX_BYTES) {
|
||||
return 'Invalid index definition in table {'.$xmldb_table->getName(). '}: the composed index on fields "'.implode(',', $this->getFields()).'" is too long.'
|
||||
.' Limit is '.self::INDEX_COMPOSED_MAX_BYTES.' bytes / '.(self::INDEX_COMPOSED_MAX_BYTES/3).' chars.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// TODO: Delete for 2.1 (deprecated in 2.0).
|
||||
|
@ -478,4 +478,17 @@ class xmldb_object {
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the definition of objects and returns error message.
|
||||
*
|
||||
* The error message should not be localised because it is intended for developers,
|
||||
* end users and admins should never see these problems!
|
||||
*
|
||||
* @param xmldb_table $xmldb_table optional when object is table
|
||||
* @return string null if ok, error message if problem found
|
||||
*/
|
||||
function validateDefinition(xmldb_table $xmldb_table=null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user