Merge branch 'w28_MDL-33018_m24_pgindex2' of git://github.com/skodak/moodle

Conflicts:
	lib/db/upgrade.php
	version.php
This commit is contained in:
Dan Poltawski 2012-07-10 09:17:23 +08:00
commit b40eca11d9
15 changed files with 205 additions and 22 deletions

View File

@ -125,6 +125,9 @@ class edit_index extends XMLDBAction {
// xmldb_index Fields
$o.= ' <tr valign="top"><td><label for="fields" accesskey="f">Fields:</label></td>';
$o.= ' <td colspan="2"><input name="fields" type="text" size="40" maxlength="80" id="fields" value="' . s(implode(', ', $index->getFields())) . '" /></td></tr>';
// xmldb_index hints
$o.= ' <tr valign="top"><td><label for="hints" accesskey="h">Hints:</label></td>';
$o.= ' <td colspan="2"><input name="hints" type="text" size="40" maxlength="80" id="hints" value="' . s(implode(', ', $index->getHints())) . '" /></td></tr>';
// Change button
$o.= ' <tr valign="top"><td>&nbsp;</td><td colspan="2"><input type="submit" value="' .$this->str['change'] . '" /></td></tr>';
$o.= ' </table>';

View File

@ -90,6 +90,8 @@ class edit_index_save extends XMLDBAction {
$unique = required_param('unique', PARAM_INT);
$fields = required_param('fields', PARAM_CLEAN);
$fields = str_replace(' ', '', trim(strtolower($fields)));
$hints = required_param('hints', PARAM_CLEAN);
$hints = str_replace(' ', '', trim(strtolower($hints)));
$editeddir = $XMLDB->editeddirs[$dirpath];
$structure = $editeddir->xml_file->getStructure();
@ -160,11 +162,20 @@ class edit_index_save extends XMLDBAction {
}
}
}
$hintsarr = array();
foreach (explode(',', $hints) as $hint) {
$hint = preg_replace('/[^a-z]/', '', $hint);
if ($hint === '') {
continue;
}
$hintsarr[] = $hint;
}
if (!empty($errors)) {
$tempindex = new xmldb_index($name);
$tempindex->setUnique($unique);
$tempindex->setFields($fieldsarr);
$tempindex->setHints($hintsarr);
// Prepare the output
$o = '<p>' .implode(', ', $errors) . '</p>
<p>' . $tempindex->readableInfo() . '</p>';
@ -197,6 +208,7 @@ class edit_index_save extends XMLDBAction {
// Set the rest of fields
$index->setUnique($unique);
$index->setFields($fieldsarr);
$index->setHints($hintsarr);
// If the hash has changed from the old one, change the version
// and mark the structure as changed

View File

@ -1047,7 +1047,7 @@
<INDEXES>
<INDEX NAME="contextlevel-instanceid" UNIQUE="true" FIELDS="contextlevel, instanceid" NEXT="instanceid"/>
<INDEX NAME="instanceid" UNIQUE="false" FIELDS="instanceid" PREVIOUS="contextlevel-instanceid" NEXT="path"/>
<INDEX NAME="path" UNIQUE="false" FIELDS="path" PREVIOUS="instanceid"/>
<INDEX NAME="path" UNIQUE="false" FIELDS="path" HINTS="varchar_pattern_ops" PREVIOUS="instanceid"/>
</INDEXES>
</TABLE>
<TABLE NAME="context_temp" COMMENT="Used by build_context_path() in upgrade and cron to keep context depths and paths in sync." PREVIOUS="context" NEXT="capabilities">

View File

@ -997,5 +997,23 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2012062500.08);
}
if ($oldversion < 2012062500.09) {
// Define index path (not unique) to be added to context
$table = new xmldb_table('context');
$index = new xmldb_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
// Recreate index with new pattern hint
if ($DB->get_dbfamily() === 'postgres') {
if ($dbman->index_exists($table, $index)) {
$dbman->drop_index($table, $index);
}
$dbman->add_index($table, $index);
}
// Main savepoint reached
upgrade_main_savepoint(true, 2012062500.09);
}
return true;
}

View File

@ -165,10 +165,11 @@ class database_manager {
*
* @param xmldb_table $xmldb_table table to be searched
* @param xmldb_index $xmldb_index the index to be searched
* @return string|bool Index name or false if no indexes are found.
* @param bool $returnall true means return array of all indexes, false means first index only as string
* @return array|string|bool Index name, array of index names or false if no indexes are found.
* @throws ddl_table_missing_exception Thrown when table is not found.
*/
public function find_index_name(xmldb_table $xmldb_table, xmldb_index $xmldb_index) {
public function find_index_name(xmldb_table $xmldb_table, xmldb_index $xmldb_index, $returnall = false) {
// Calculate the name of the table
$tablename = $xmldb_table->getName();
@ -183,6 +184,8 @@ class database_manager {
// Get list of indexes in table
$indexes = $this->mdb->get_indexes($tablename);
$return = array();
// Iterate over them looking for columns coincidence
foreach ($indexes as $indexname => $index) {
$columns = $index['columns'];
@ -190,10 +193,18 @@ class database_manager {
$diferences = array_merge(array_diff($columns, $indcolumns), array_diff($indcolumns, $columns));
// If no differences, we have find the index
if (empty($diferences)) {
return $indexname;
if ($returnall) {
$return[] = $indexname;
} else {
return $indexname;
}
}
}
if ($return and $returnall) {
return $return;
}
// Arriving here, index not found
return false;
}

View File

@ -118,6 +118,37 @@ class postgres_sql_generator extends sql_generator {
return $sqlarr;
}
/**
* Given one correct xmldb_index, returns the SQL statements
* needed to create it (in array).
*
* @param xmldb_table $xmldb_table The xmldb_table instance to create the index on.
* @param xmldb_index $xmldb_index The xmldb_index to create.
* @return array An array of SQL statements to create the index.
* @throws coding_exception Thrown if the xmldb_index does not validate with the xmldb_table.
*/
public function getCreateIndexSQL($xmldb_table, $xmldb_index) {
$sqls = parent::getCreateIndexSQL($xmldb_table, $xmldb_index);
$hints = $xmldb_index->getHints();
$fields = $xmldb_index->getFields();
if (in_array('varchar_pattern_ops', $hints) and count($fields) == 1) {
// Add the pattern index and keep the normal one, keep unique only the standard index to improve perf.
foreach ($sqls as $sql) {
$field = reset($fields);
$count = 0;
$newindex = preg_replace("/^CREATE( UNIQUE)? INDEX ([a-z0-9_]+) ON ([a-z0-9_]+) \($field\)$/", "CREATE INDEX \\2_pattern ON \\3 USING btree ($field varchar_pattern_ops)", $sql, -1, $count);
if ($count != 1) {
debugging('Unexpected getCreateIndexSQL() structure.');
continue;
}
$sqls[] = $newindex;
}
}
return $sqls;
}
/**
* Given one XMLDB Type, length and decimals, returns the DB proper SQL type.
*
@ -306,7 +337,7 @@ class postgres_sql_generator extends sql_generator {
$results[] = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $fieldname . ' DROP DEFAULT'; // Drop default clause
}
$alterstmt = 'ALTER TABLE ' . $tablename . ' ALTER COLUMN ' . $this->getEncQuoted($xmldb_field->getName()) .
' TYPE' . $this->getFieldSQL($xmldb_table, $xmldb_field, null, true, true, null, false);
' TYPE' . $this->getFieldSQL($xmldb_table, $xmldb_field, null, true, true, null, false);
// Some castings must be performed explicitly (mainly from text|char to numeric|integer)
if (($oldmetatype == 'C' || $oldmetatype == 'X') &&
($xmldb_field->getType() == XMLDB_TYPE_NUMBER || $xmldb_field->getType() == XMLDB_TYPE_FLOAT)) {
@ -417,7 +448,7 @@ class postgres_sql_generator extends sql_generator {
if (!$this->mdb->get_record_sql("SELECT *
FROM pg_class
WHERE relname = ? AND relkind = 'S'",
array($sequencename))) {
array($sequencename))) {
$sequencename = false;
}
@ -475,6 +506,7 @@ class postgres_sql_generator extends sql_generator {
*/
public static function getReservedWords() {
// This file contains the reserved words for PostgreSQL databases
// This file contains the reserved words for PostgreSQL databases
// http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html
$reserved_words = array (
'all', 'analyse', 'analyze', 'and', 'any', 'array', 'as', 'asc',

View File

@ -1013,13 +1013,16 @@ abstract class sql_generator {
$results = array();
// Get the real index name
$dbindexname = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index);
$dbindexnames = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index, true);
// Replace TABLENAME and INDEXNAME as needed
$dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_index_sql);
$dropsql = str_replace('INDEXNAME', $this->getEncQuoted($dbindexname), $dropsql);
$results[] = $dropsql;
if ($dbindexnames) {
foreach ($dbindexnames as $dbindexname) {
$dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_index_sql);
$dropsql = str_replace('INDEXNAME', $this->getEncQuoted($dbindexname), $dropsql);
$results[] = $dropsql;
}
}
return $results;
}

View File

@ -1649,6 +1649,50 @@ class ddl_testcase extends database_driver_testcase {
$this->assertTrue(count($reserved) > 1);
}
public function test_index_hints() {
$DB = $this->tdb;
$dbman = $DB->get_manager();
$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_field('path', 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'), array('xxxx,yyyy'));
$table->add_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
// Drop if exists
if ($dbman->table_exists($table)) {
$dbman->drop_table($table);
}
$dbman->create_table($table);
$tablename = $table->getName();
$this->tables[$tablename] = $table;
$table = new xmldb_table('testtable');
$index = new xmldb_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'), array('xxxx,yyyy'));
$this->assertTrue($dbman->index_exists($table, $index));
$table = new xmldb_table('testtable');
$index = new xmldb_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
$this->assertTrue($dbman->index_exists($table, $index));
// Try unique indexes too.
$dbman->drop_table($this->tables[$tablename]);
$table = new xmldb_table('testtable');
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('path', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_index('path', XMLDB_INDEX_UNIQUE, array('path'), array('varchar_pattern_ops'));
$dbman->create_table($table);
$this->tables[$tablename] = $table;
$table = new xmldb_table('testtable');
$index = new xmldb_index('path', XMLDB_INDEX_UNIQUE, array('path'), array('varchar_pattern_ops'));
$this->assertTrue($dbman->index_exists($table, $index));
}
public function test_index_max_bytes() {
$DB = $this->tdb;
$dbman = $DB->get_manager();

View File

@ -9,11 +9,16 @@
<FIELD NAME="secondname" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="name" NEXT="intro"/>
<FIELD NAME="intro" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" PREVIOUS="secondname" NEXT="avatar"/>
<FIELD NAME="avatar" TYPE="binary" LENGTH="medium" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" PREVIOUS="intro" NEXT="grade"/>
<FIELD NAME="grade" TYPE="number" LENGTH="20" DECIMALS="10" NOTNULL="false" SEQUENCE="false" PREVIOUS="avatar"/>
<FIELD NAME="grade" TYPE="number" LENGTH="20" DECIMALS="10" NOTNULL="false" SEQUENCE="false" PREVIOUS="avatar" NEXT="path"/>
<FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true" PREVIOUS="grade"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
</KEYS>
<INDEXES>
<INDEX NAME="course" UNIQUE="false" FIELDS="course" NEXT="path"/>
<INDEX NAME="path" UNIQUE="false" FIELDS="path" HINTS="varchar_pattern_ops,unknownxyz" PREVIOUS="course"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>

View File

@ -353,7 +353,14 @@ class pgsql_native_moodle_database extends moodle_database {
continue;
}
$columns = explode(',', $matches[4]);
$columns = array_map(array($this, 'trim_quotes'), $columns);
foreach ($columns as $k=>$column) {
$column = trim($column);
if ($pos = strpos($column, ' ')) {
// index type is separated by space
$column = substr($column, 0, $pos);
}
$columns[$k] = $this->trim_quotes($column);
}
$indexes[$row['indexname']] = array('unique'=>!empty($matches[1]),
'columns'=>$columns);
}

View File

@ -14,6 +14,7 @@
<!ELEMENT INDEX EMPTY >
<!ATTLIST INDEX COMMENT CDATA #IMPLIED >
<!ATTLIST INDEX HINTS CDATA #IMPLIED >
<!ATTLIST INDEX FIELDS CDATA #REQUIRED >
<!ATTLIST INDEX NAME NMTOKEN #REQUIRED >
<!ATTLIST INDEX NEXT NMTOKEN #IMPLIED >

View File

@ -85,6 +85,7 @@
<xs:attribute name="NAME" type="xs:NMTOKEN" use="required" />
<xs:attribute name="UNIQUE" type="trueFalse" use="required" />
<xs:attribute name="FIELDS" type="fieldsList" use="required" />
<xs:attribute name="HINTS" type="xs:string" use="optional" />
<xs:attribute name="COMMENT" type="xs:string" use="optional" />
<xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
<xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />

View File

@ -34,6 +34,9 @@ class xmldb_index extends xmldb_object {
/** @var array index fields */
protected $fields;
/** @var array index hints */
protected $hints;
/**
* Note:
* - MySQL: MyISAM has a limit of 1000 bytes for any key including composed, InnoDB has limit 3500 bytes.
@ -54,14 +57,16 @@ class xmldb_index extends xmldb_object {
* Creates one new xmldb_index
*
* @param string $name
* @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
* @param array fields an array of fieldnames to build the index over
* @param string $type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
* @param array $fields an array of fieldnames to build the index over
* @param array $hints an array of optional hints
*/
public function __construct($name, $type=null, $fields=array()) {
public function __construct($name, $type=null, $fields=array(), $hints=array()) {
$this->unique = false;
$this->fields = array();
$this->hints = array();
parent::__construct($name);
$this->set_attributes($type, $fields);
$this->set_attributes($type, $fields, $hints);
}
/**
@ -69,10 +74,12 @@ class xmldb_index extends xmldb_object {
*
* @param string type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
* @param array fields an array of fieldnames to build the index over
* @param array $hints array of optional hints
*/
public function set_attributes($type, $fields) {
public function set_attributes($type, $fields, $hints = array()) {
$this->unique = !empty($type) ? true : false;
$this->fields = $fields;
$this->hints = $hints;
}
/**
@ -107,6 +114,22 @@ class xmldb_index extends xmldb_object {
return $this->fields;
}
/**
* Set optional index hints.
* @param array $hints
*/
public function setHints($hints) {
$this->hints = $hints;
}
/**
* Returns optional index hints.
* @return array
*/
public function getHints() {
return $this->hints;
}
/**
* Load data from XML to the index
* @param $xmlarr array
@ -173,6 +196,15 @@ class xmldb_index extends xmldb_object {
// Finally, set the array of fields
$this->fields = $fieldsarr;
if (isset($xmlarr['@']['HINTS'])) {
$this->hints = array();
$hints = strtolower(trim($xmlarr['@']['HINTS']));
if ($hints !== '') {
$hints = explode(',', $hints);
$this->hints = array_map('trim', $hints);
}
}
if (isset($xmlarr['@']['COMMENT'])) {
$this->comment = trim($xmlarr['@']['COMMENT']);
}
@ -201,7 +233,7 @@ class xmldb_index extends xmldb_object {
if (!$this->loaded) {
$this->hash = null;
} else {
$key = $this->unique . implode (', ', $this->fields);
$key = $this->unique . implode (', ', $this->fields) . implode (', ', $this->hints);
$this->hash = md5($key);
}
}
@ -220,6 +252,9 @@ class xmldb_index extends xmldb_object {
}
$o.= ' UNIQUE="' . $unique . '"';
$o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
if ($this->hints) {
$o.= ' HINTS="' . implode(', ', $this->hints) . '"';
}
if ($this->comment) {
$o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
}
@ -274,6 +309,12 @@ class xmldb_index extends xmldb_object {
} else {
$result .= 'null';
}
// Hints
$hints = $this->getHints();
if (!empty($hints)) {
$result .= ', array(' . "'". implode("', '", $hints) . "')";
}
// Return result
return $result;
}
@ -293,6 +334,10 @@ class xmldb_index extends xmldb_object {
// fields
$o .= ' (' . implode(', ', $this->fields) . ')';
if ($this->hints) {
$o .= ' [' . implode(', ', $this->hints) . ']';
}
return $o;
}

View File

@ -813,9 +813,10 @@ class xmldb_table extends xmldb_object {
* @param string $name name of the index
* @param int $type XMLDB_INDEX_UNIQUE, XMLDB_INDEX_NOTUNIQUE
* @param array $fields an array of fieldnames to build the index over
* @param array $hints optional index type hints
*/
public function add_index($name, $type, $fields) {
$index = new xmldb_index($name, $type, $fields);
public function add_index($name, $type, $fields, $hints = array()) {
$index = new xmldb_index($name, $type, $fields, $hints);
$this->addIndex($index);
}

View File

@ -30,7 +30,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2012062500.08; // YYYYMMDD = weekly release date of this DEV branch
$version = 2012062500.09; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes