MDL-15181 temp table support in ddl/dml

This commit is contained in:
skodak 2008-06-07 14:41:01 +00:00
parent 9575899294
commit b922e86b7a
8 changed files with 305 additions and 217 deletions

View File

@ -102,27 +102,11 @@ class database_manager {
* Given one xmldb_table, check if it exists in DB (true/false)
*
* @param mixed the table to be searched (string name or xmldb_table instance)
* @param bool temp table (might need different checks)
* @return boolean true/false
*/
public function table_exists($table) {
/// Do this function silenty (to avoid output in install/upgrade process)
$olddbdebug = $this->mdb->get_debug();
$this->mdb->set_debug(false);
if (is_string($table)) {
$tablename = $table;
} else {
/// Calculate the name of the table
$tablename = $table->getName();
}
/// get all tables in moodle database
$tables = $this->mdb->get_tables();
$exists = in_array($tablename, $tables);
/// Re-set original debug
$this->mdb->set_debug($olddbdebug);
return $exists;
public function table_exists($table, $temptable=false) {
return $this->generator->table_exists($table, $temptable);
}
/**
@ -523,37 +507,33 @@ class database_manager {
* This function will create the temporary table passed as argument with all its
* fields/keys/indexes/sequences, everything based in the XMLDB object
*
* TRUNCATE the table immediately after creation. A previous process using
* the same persistent connection may have created the temp table and failed to
* drop it. In that case, the table will exist, and create_temp_table() will
* will succeed.
*
* NOTE: The return value is the tablename - some DBs (MSSQL at least) use special
* names for temp tables.
*
* @TODO There is no way to know, from the return value alone, whether a table was actually created
* or not: if an existing table is given as param, its name will be returned, but no DB action
* will have occurred. This should be remedied using an Exception
* If table already exists it will be dropped and recreated, please make sure
* the table name does not collide with existing normal table!
*
* @param xmldb_table table object (full specs are required)
* @param boolean continue to specify if must continue on error (true) or stop (false)
* @param boolean feedback to specify to show status info (true) or not (false)
* @return string tablename on success, false on error
*/
function create_temp_table($xmldb_table, $continue=true, $feedback=true) {
public function create_temp_table($xmldb_table, $continue=true, $feedback=true) {
if (!($xmldb_table instanceof xmldb_table)) {
debugging('Incorrect create_table() $xmldb_table parameter');
return false;
}
/// hack for mssql - it requires names to start with #
$xmldb_table = $this->generator->tweakTempTable($xmldb_table);
/// Check table doesn't exist
if ($this->table_exists($xmldb_table)) {
debugging('Table ' . $xmldb_table->getName() .
' already exists. Create skipped', DEBUG_DEVELOPER);
return $xmldb_table->getName(); //Table exists, nothing to do
if ($this->table_exists($xmldb_table, true)) {
debugging('Temporary table ' . $xmldb_table->getName() .
' already exists, dropping and recreating it.', DEBUG_DEVELOPER);
if (!$this->drop_temp_table($xmldb_table, $continue, $feedback)) {
return false;
}
}
if (!$sqlarr = $this->generator->getCreateTableSQL($xmldb_table)) {
if (!$sqlarr = $this->generator->getCreateTempTableSQL($xmldb_table)) {
return $xmldb_table->getName(); //Empty array = nothing to do = no error
}
@ -564,6 +544,38 @@ class database_manager {
}
}
/**
* This function will drop the temporary table passed as argument with all its
* fields/keys/indexes/sequences, everything based in the XMLDB object
*
* It is recommended to drop temp table when not used anymore.
*
* @param xmldb_table table object
* @param boolean continue to specify if must continue on error (true) or stop (false)
* @param boolean feedback to specify to show status info (true) or not (false)
* @return string tablename on success, false on error
*/
public function drop_temp_table($xmldb_table, $continue=true, $feedback=true) {
if (!($xmldb_table instanceof xmldb_table)) {
debugging('Incorrect create_table() $xmldb_table parameter');
return false;
}
/// mssql requires names to start with #
$xmldb_table = $this->generator->tweakTempTable($xmldb_table);
/// Check table doesn't exist
if (!$this->table_exists($xmldb_table, true)) {
return true;
}
if (!$sqlarr = $this->generator->getDropTempTableSQL($xmldb_table)) {
return false; // error
}
return $this->execute_sql_arr($sqlarr, $continue, $feedback);
}
/**
* This function will rename the table passed as argument
* Before renaming the index, the function will check it exists

View File

@ -78,52 +78,37 @@ class mssql_sql_generator extends sql_generator {
}
/**
* This function will create the temporary table passed as argument with all its
* fields/keys/indexes/sequences, everything based in the XMLDB object
*
* TRUNCATE the table immediately after creation. A previous process using
* the same persistent connection may have created the temp table and failed to
* drop it. In that case, the table will exist, and create_temp_table() will
* will succeed.
*
* NOTE: The return value is the tablename - some DBs (MSSQL at least) use special
* names for temp tables.
*
* @uses $CFG, $db
* @param xmldb_table table object (full specs are required)
* @param boolean continue to specify if must continue on error (true) or stop (false)
* @param boolean feedback to specify to show status info (true) or not (false)
* @return string tablename on success, false on error
* Given one correct xmldb_table, returns the SQL statements
* to create temporary table (inside one array)
*/
function create_temp_table($xmldb_table, $continue=true, $feedback=true) {
if (!($xmldb_table instanceof xmldb_table)) {
debugging('Incorrect create_table() $xmldb_table parameter');
return false;
}
/// Check table doesn't exist
if ($this->table_exists($xmldb_table)) {
debugging('Table ' . $xmldb_table->getName() .
' already exists. Create skipped', DEBUG_DEVELOPER);
return $xmldb_table->getName(); //Table exists, nothing to do
}
if (!$sqlarr = $this->getCreateTableSQL($xmldb_table)) {
return $xmldb_table->getName(); //Empty array = nothing to do = no error
}
// TODO: somehow change the name to have a #
/*$temporary = '';
if (!empty($temporary)) {
$sqlarr = preg_replace('/^CREATE/', "CREATE $temporary", $sqlarr);
}*/
if (execute_sql_arr($sqlarr, $continue, $feedback)) {
return $xmldb_table->getName();
} else {
return false;
public function getCreateTempTableSQL($xmldb_table) {
$sqlarr = $this->getCreateTableSQL($xmldb_table);
//ugly hack!
$this->mdb->temptables[trim($xmldb_table->getName(), '#')] = true;
return $sqlarr;
}
/**
* Given one correct xmldb_table and the new name, returns the SQL statements
* to drop it (inside one array)
*/
public function getDropTempTableSQL($xmldb_table) {
$sqlarr = $this->getDropTableSQL($xmldb_table);
$tablename = $xmldb_table->getName();
array_unshift($sqlarr, "TRUNCATE TABLE {".$tablename."}"); // oracle requires truncate before being able to drop a temp table
//ugly hack!
unset($this->mdb->temptables[trim($xmldb_table->getName(), '#')]);
return $sqlarr;
}
/**
* Tweaks the temp table instance - required for mssql # naming
*/
public function tweakTempTable($xmldb_table) {
if (strpos($xmldb_table->getName(), '#') !== 0) {
$xmldb_table->setName('#'.$xmldb_table->getName()); // MSSQL requires temp table names to start with #
}
return $xmldb_table;
}
/**

View File

@ -80,6 +80,45 @@ class mysql_sql_generator extends sql_generator {
parent::__construct($mdb);
}
/**
* Given one xmldb_table, check if it exists in DB (true/false)
*
* @param mixed the table to be searched (string name or xmldb_table instance)
* @param bool temp table (might need different checks)
* @return boolean true/false
*/
public function table_exists($table, $temptable=false) {
if (!$temptable) {
return parent::table_exists($table, $temptable);
}
if (is_string($table)) {
$tablename = $table;
} else {
/// Calculate the name of the table
$tablename = $table->getName();
}
// ugly hack - mysql does not list temporary tables :-(
if ($this->mdb->execute("DESCRIBE {".$tablename."}") === false) {
$exists = false;
} else {
$exists = true;
}
return $exists;
}
/**
* Given one correct xmldb_table and the new name, returns the SQL statements
* to drop it (inside one array)
*/
public function getDropTableSQL($xmldb_table) {
$sqlarr = parent::getDropTableSQL($xmldb_table);
$sqlarr = preg_replace('/^DROP TABLE/', "DROP TEMPORARY TABLE", $sqlarr);
return $sqlarr;
}
/**
* Given one XMLDB Type, lenght and decimals, returns the DB proper SQL type
*/

View File

@ -62,6 +62,27 @@ class oracle_sql_generator extends sql_generator {
parent::__construct($mdb);
}
/**
* Given one correct xmldb_table, returns the SQL statements
* to create temporary table (inside one array)
*/
public function getCreateTempTableSQL($xmldb_table) {
$sqlarr = $this->getCreateTableSQL($xmldb_table);
$sqlarr = preg_replace('/^CREATE TABLE/', "CREATE GLOBAL TEMPORARY TABLE", $sqlarr);
return $sqlarr;
}
/**
* Given one correct xmldb_table and the new name, returns the SQL statements
* to drop it (inside one array)
*/
public function getDropTempTableSQL($xmldb_table) {
$sqlarr = $this->getDropTableSQL($xmldb_table);
$tablename = $xmldb_table->getName();
array_unshift($sqlarr, "TRUNCATE TABLE {".$tablename."}"); // oracle requires truncate before being able to drop a temp table
return $sqlarr;
}
/**
* Given one XMLDB Type, lenght and decimals, returns the DB proper SQL type
*/
@ -111,50 +132,6 @@ class oracle_sql_generator extends sql_generator {
return $dbtype;
}
/**
* This function will create the temporary table passed as argument with all its
* fields/keys/indexes/sequences, everything based in the XMLDB object
*
* TRUNCATE the table immediately after creation. A previous process using
* the same persistent connection may have created the temp table and failed to
* drop it. In that case, the table will exist, and create_temp_table() will
* will succeed.
*
* NOTE: The return value is the tablename - some DBs (MSSQL at least) use special
* names for temp tables.
*
* @uses $CFG, $db
* @param xmldb_table table object (full specs are required)
* @param boolean continue to specify if must continue on error (true) or stop (false)
* @param boolean feedback to specify to show status info (true) or not (false)
* @return string tablename on success, false on error
*/
function create_temp_table($xmldb_table, $continue=true, $feedback=true) {
if (!($xmldb_table instanceof xmldb_table)) {
debugging('Incorrect create_table() $xmldb_table parameter');
return false;
}
/// Check table doesn't exist
if ($this->table_exists($xmldb_table)) {
debugging('Table ' . $xmldb_table->getName() .
' already exists. Create skipped', DEBUG_DEVELOPER);
return $xmldb_table->getName(); //Table exists, nothing to do
}
if (!$sqlarr = $this->getCreateTableSQL($xmldb_table)) {
return $xmldb_table->getName(); //Empty array = nothing to do = no error
}
$sqlarr = preg_replace('/^CREATE/', "CREATE GLOBAL TEMPORARY", $sqlarr);
if (execute_sql_arr($sqlarr, $continue, $feedback)) {
return $xmldb_table->getName();
} else {
return false;
}
}
/**
* Returns the code needed to create one enum for the xmldb_table and xmldb_field passes
*/

View File

@ -17,39 +17,37 @@ class ddllib_test extends UnitTestCase {
private $dbmanager;
public function setUp() {
global $CFG;
global $CFG, $DB;
$db = new mysqli_adodb_moodle_database();
$db->connect($CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->dbpersist, $CFG->prefix);
$this->dbmanager = $db->get_manager();
$this->dbmanager = $DB->get_manager();
$table = new xmldb_table("testtable");
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->addFieldInfo('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('single', 'news', 'general', 'social', 'eachuser', 'teacher', 'qanda'), 'general');
$table->addFieldInfo('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null);
$table->addFieldInfo('intro', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null, null, null);
$table->addFieldInfo('logo', XMLDB_TYPE_BINARY, 'big', null, XMLDB_NOTNULL, null, null, null);
$table->addFieldInfo('assessed', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('assesstimestart', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('assesstimefinish', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('scale', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('maxbytes', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('forcesubscribe', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('trackingtype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '1');
$table->addFieldInfo('rsstype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('rssarticles', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('grade', XMLDB_TYPE_NUMBER, '20,0', XMLDB_UNSIGNED, null, null, null, null, null);
$table->addFieldInfo('percent', XMLDB_TYPE_NUMBER, '5,2', null, null, null, null, null, null);
$table->addFieldInfo('warnafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('blockafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('blockperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->addKeyInfo('type-name', XMLDB_KEY_UNIQUE, array('type', 'name'));
$table->addIndexInfo('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
$table->addIndexInfo('rsstype', XMLDB_INDEX_UNIQUE, array('rsstype'));
$table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, null);
$table->add_field('intro', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null, null, null);
$table->add_field('logo', XMLDB_TYPE_BINARY, 'big', null, XMLDB_NOTNULL, null, null, null);
$table->add_field('assessed', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('assesstimestart', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('assesstimefinish', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('scale', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('maxbytes', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('forcesubscribe', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('trackingtype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '1');
$table->add_field('rsstype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('rssarticles', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('grade', XMLDB_TYPE_NUMBER, '20,0', XMLDB_UNSIGNED, null, null, null, null, null);
$table->add_field('percent', XMLDB_TYPE_NUMBER, '5,2', null, null, null, null, null, null);
$table->add_field('warnafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('blockafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('blockperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('type-name', XMLDB_KEY_UNIQUE, array('type', 'name'));
$table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
$table->add_index('rsstype', XMLDB_INDEX_UNIQUE, array('rsstype'));
$table->setComment("This is a test'n drop table. You can drop it safely");
$this->dbmanager->create_table($table);
@ -57,14 +55,14 @@ class ddllib_test extends UnitTestCase {
// Second, smaller table
$table = new xmldb_table ('anothertest');
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->addFieldInfo('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->addFieldInfo('name', XMLDB_TYPE_CHAR, '30', null, null, null, null, null, 'Moodle');
$table->addFieldInfo('secondname', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, null, null);
$table->addFieldInfo('intro', XMLDB_TYPE_TEXT, 'medium', null, XMLDB_NOTNULL, null, null, null, null);
$table->addFieldInfo('avatar', XMLDB_TYPE_BINARY, 'medium', null, null, null, null, null, null);
$table->addFieldInfo('grade', XMLDB_TYPE_NUMBER, '20,10', null, null, null, null, null);
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$table->add_field('name', XMLDB_TYPE_CHAR, '30', null, null, null, null, null, 'Moodle');
$table->add_field('secondname', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, null, null);
$table->add_field('intro', XMLDB_TYPE_TEXT, 'medium', null, XMLDB_NOTNULL, null, null, null, null);
$table->add_field('avatar', XMLDB_TYPE_BINARY, 'medium', null, null, null, null, null, null);
$table->add_field('grade', XMLDB_TYPE_NUMBER, '20,10', null, null, null, null, null);
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$this->dbmanager->create_table($table);
$this->tables[] = $table;
@ -77,28 +75,21 @@ class ddllib_test extends UnitTestCase {
}
}
unset($this->tables);
setup_DB();
}
public function testCreateTable() {
$table = new xmldb_table("other_test_table");
$field = new xmldb_field('id');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, true);
$table->addField($field);
$key = new xmldb_key('PRIMARY');
$key->setAttributes(XMLDB_KEY_PRIMARY, array('id'));
$table->addKey($key);
$this->assertTrue($this->dbmanager->create_table($table));
$this->assertTrue($this->dbmanager->table_exists("other_test_table"));
$table = $this->tables[1];
$this->dbmanager->drop_table($table);
$this->assertTrue($this->dbmanager->create_table($table));
$this->assertTrue($this->dbmanager->table_exists("anothertest"));
$this->dbmanager->drop_table($table);
$this->assertFalse($this->dbmanager->table_exists("anothertest"));
// Give existing table as argument
$table = $this->tables[1];
$this->assertFalse($this->dbmanager->create_table($table));
// Give a wrong table param
// Give a wrong table param (expect a debugging message)
$table = 'string';
$this->assertFalse($this->dbmanager->create_table($table));
@ -122,7 +113,7 @@ class ddllib_test extends UnitTestCase {
$table = $this->tables[0];
/// Create a new field with complex specs (enums are good candidates)
$field = new xmldb_field('type2');
$field->setAttributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
$field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('single', 'news', 'general', 'social', 'eachuser', 'teacher', 'qanda'), 'general', 'course');
$this->assertTrue($this->dbmanager->add_field($table, $field));
$this->assertTrue($this->dbmanager->field_exists($table, 'type2'));
@ -134,7 +125,7 @@ class ddllib_test extends UnitTestCase {
$table = $this->tables[0];
/// Create a new field with complex specs (enums are good candidates)
$field = new xmldb_field('onenumber');
$field->setAttributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, 0, 'type');
$field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, 0, 'type');
$this->assertTrue($this->dbmanager->add_field($table, $field));
$this->assertTrue($this->dbmanager->field_exists($table, 'onenumber'));
@ -153,122 +144,122 @@ class ddllib_test extends UnitTestCase {
public function testChangeFieldType() {
$table = $this->tables[1];
$field = new xmldb_field('course');
$field->setAttributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, null, '0');
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, null, '0');
$this->assertTrue($this->dbmanager->change_field_type($this->tables[1], $field));
$field = new xmldb_field('course');
$field->setAttributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$this->assertTrue($this->dbmanager->change_field_type($this->tables[1], $field));
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, null, "test'n drop");
$field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, null, "test'n drop");
$this->assertTrue($this->dbmanager->change_field_type($this->tables[1], $field));
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_FLOAT, '20,10', XMLDB_UNSIGNED, null, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_FLOAT, '20,10', XMLDB_UNSIGNED, null, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_type($this->tables[1], $field));
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, null, 'test');
$field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, null, 'test');
$this->assertTrue($this->dbmanager->change_field_type($this->tables[1], $field));
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_NUMBER, '20,10', XMLDB_UNSIGNED, null, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', XMLDB_UNSIGNED, null, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_type($this->tables[1], $field));
}
public function testChangeFieldPrecision() {
$table = $this->tables[1];
$field = new xmldb_field('intro');
$field->setAttributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_precision($this->tables[1], $field));
$field = new xmldb_field('secondname');
$field->setAttributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_precision($this->tables[1], $field));
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_precision($this->tables[1], $field));
$field = new xmldb_field('course');
$field->setAttributes(XMLDB_TYPE_INTEGER, '5', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$field->set_attributes(XMLDB_TYPE_INTEGER, '5', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
$this->assertTrue($this->dbmanager->change_field_precision($this->tables[1], $field));
}
public function testChangeFieldSign() {
$table = $this->tables[1];
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_NUMBER, '10,2', XMLDB_UNSIGNED, null, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', XMLDB_UNSIGNED, null, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_unsigned($this->tables[1], $field));
$field = new xmldb_field('grade');
$field->setAttributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_unsigned($this->tables[1], $field));
}
public function testChangeFieldNullability() {
$table = $this->tables[1];
$field = new xmldb_field('name');
$field->setAttributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, null, 'Moodle');
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null, null, 'Moodle');
$this->assertTrue($this->dbmanager->change_field_notnull($this->tables[1], $field));
$field = new xmldb_field('name');
$field->setAttributes(XMLDB_TYPE_CHAR, '30', null, null, null, null, null, 'Moodle');
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, null, null, null, null, 'Moodle');
$this->assertTrue($this->dbmanager->change_field_notnull($this->tables[1], $field));
}
public function testChangeFieldDefault() {
$table = $this->tables[1];
$field = new xmldb_field('name');
$field->setAttributes(XMLDB_TYPE_CHAR, '30', null, null, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, null, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_default($this->tables[1], $field));
$field = new xmldb_field('name');
$field->setAttributes(XMLDB_TYPE_CHAR, '30', null, null, null, null, null, 'Moodle');
$field->set_attributes(XMLDB_TYPE_CHAR, '30', null, null, null, null, null, 'Moodle');
$this->assertTrue($this->dbmanager->change_field_default($this->tables[1], $field));
$field = new xmldb_field('secondname');
$field->setAttributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null, null, 'Moodle2');
$field->set_attributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null, null, 'Moodle2');
$this->assertTrue($this->dbmanager->change_field_default($this->tables[1], $field));
$field = new xmldb_field('secondname');
$field->setAttributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null, null, null);
$field->set_attributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null, null, null);
$this->assertTrue($this->dbmanager->change_field_default($this->tables[1], $field));
}
public function testAddUniqueIndex() {
$table = $this->tables[1];
$index = new xmldb_index('secondname');
$index->setAttributes(XMLDB_INDEX_UNIQUE, array('name', 'secondname', 'grade'));
$index->set_attributes(XMLDB_INDEX_UNIQUE, array('name', 'secondname', 'grade'));
$this->assertTrue($this->dbmanager->add_index($this->tables[1], $index));
}
public function testAddNonUniqueIndex() {
$table = $this->tables[1];
$index = new xmldb_index('secondname');
$index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
$index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
$this->assertTrue($this->dbmanager->add_index($this->tables[1], $index));
}
public function testFindIndexName() {
$table = $this->tables[1];
$index = new xmldb_index('secondname');
$index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
$index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
$this->dbmanager->add_index($this->tables[1], $index);
// TODO DBM Systems name their indices differently. Maybe just test for non-false (or simply true)
$this->assertEqual($this->dbmanager->find_index_name($this->tables[1], $index), 'mdl_anot_counam_ix');
$nonexistentindex = new xmldb_index('nonexistentindex');
$nonexistentindex->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('name'));
$nonexistentindex->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('name'));
$this->assertFalse($this->dbmanager->find_index_name($this->tables[1], $nonexistentindex));
}
public function testDropIndex() {
$table = $this->tables[1];
$index = new xmldb_index('secondname');
$index->setAttributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
$index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
$this->dbmanager->add_index($this->tables[1], $index);
$this->assertTrue($this->dbmanager->drop_index($this->tables[1], $index));
@ -278,21 +269,21 @@ class ddllib_test extends UnitTestCase {
public function testAddUniqueKey() {
$table = $this->tables[1];
$key = new xmldb_key('id-course-grade');
$key->setAttributes(XMLDB_KEY_UNIQUE, array('id', 'course', 'grade'));
$key->set_attributes(XMLDB_KEY_UNIQUE, array('id', 'course', 'grade'));
$this->assertTrue($this->dbmanager->add_key($this->tables[1], $key));
}
public function testAddForeignUniqueKey() {
$table = $this->tables[1];
$key = new xmldb_key('course');
$key->setAttributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'anothertest', array('id'));
$key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'anothertest', array('id'));
$this->assertTrue($this->dbmanager->add_key($this->tables[1], $key));
}
public function testDropKey() {
$table = $this->tables[1];
$key = new xmldb_key('course');
$key->setAttributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'anothertest', array('id'));
$key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'anothertest', array('id'));
$this->dbmanager->add_key($this->tables[1], $key);
$this->assertTrue($this->dbmanager->drop_key($this->tables[1], $key));
@ -301,14 +292,14 @@ class ddllib_test extends UnitTestCase {
public function testAddForeignKey() {
$table = $this->tables[1];
$key = new xmldb_key('course');
$key->setAttributes(XMLDB_KEY_FOREIGN, array('course'), 'anothertest', array('id'));
$key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'anothertest', array('id'));
$this->assertTrue($this->dbmanager->add_key($this->tables[1], $key));
}
public function testDropForeignKey() {
$table = $this->tables[1];
$key = new xmldb_key('course');
$key->setAttributes(XMLDB_KEY_FOREIGN, array('course'), 'anothertest', array('id'));
$key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'anothertest', array('id'));
$this->dbmanager->add_key($this->tables[1], $key);
$this->assertTrue($this->dbmanager->drop_key($this->tables[1], $key));
@ -318,14 +309,14 @@ class ddllib_test extends UnitTestCase {
$table = $this->tables[0];
// Removing an enum value
$field = new xmldb_field('type');
$field->setAttributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
$field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('news', 'general', 'social', 'eachuser', 'teacher', 'qanda'), 'general', 'course');
$this->assertTrue($this->dbmanager->change_field_enum($table, $field));
// Adding an enum value
$field = new xmldb_field('type');
$field->setAttributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
$field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('single', 'news', 'general', 'social', 'eachuser', 'teacher', 'qanda'), 'general', 'course');
$this->assertTrue($this->dbmanager->change_field_enum($table, $field));
}
@ -333,7 +324,7 @@ class ddllib_test extends UnitTestCase {
public function testRenameIndex() {
$table = $this->tables[0];
$index = new xmldb_index('course');
$index->setAttributes(XMLDB_INDEX_UNIQUE, array('course'));
$index->set_attributes(XMLDB_INDEX_UNIQUE, array('course'));
$this->assertTrue($this->dbmanager->rename_index($table, $index, 'newindexname'));
}
@ -341,7 +332,7 @@ class ddllib_test extends UnitTestCase {
public function testRenameKey() {
$table = $this->tables[0];
$key = new xmldb_key('course');
$key->setAttributes(XMLDB_KEY_UNIQUE, array('course'));
$key->set_attributes(XMLDB_KEY_UNIQUE, array('course'));
$this->assertTrue($this->dbmanager->rename_key($table, $key, 'newkeyname'));
@ -350,7 +341,7 @@ class ddllib_test extends UnitTestCase {
public function testRenameField() {
$table = $this->tables[0];
$field = new xmldb_field('type');
$field->setAttributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
$field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('single', 'news', 'general', 'social', 'eachuser', 'teacher', 'qanda'), 'general', 'course');
$this->assertTrue($this->dbmanager->rename_field($table, $field, 'newfieldname'));
@ -361,6 +352,7 @@ class ddllib_test extends UnitTestCase {
$rand = round(rand() * 100);
$this->assertFalse($this->dbmanager->table_exists('newtablename'. $rand));
$this->assertTrue($this->dbmanager->rename_table($table, 'newtablename'. $rand));
$this->dbmanager->drop_table('newtablename' . $rand);
}
public function testTableExists() {
@ -489,14 +481,11 @@ class ddllib_test extends UnitTestCase {
// Feed incorrect table param
$this->assertFalse($this->dbmanager->create_temp_table('anothertest'));
// Correct table but with existing name
$table = $this->tables[0];
$this->assertEqual('testtable', $this->dbmanager->create_temp_table($table));
$table = $this->tables[1];
// New table
$this->dbmanager->drop_table($this->tables[0]);
$this->assertEqual('testtable', $this->dbmanager->create_temp_table($table));
$this->assertTrue($this->dbmanager->create_temp_table($table));
$this->assertTrue($this->dbmanager->drop_temp_table($table));
}
}
?>

View File

@ -158,6 +158,34 @@ abstract class sql_generator {
}
}
/**
* Given one xmldb_table, check if it exists in DB (true/false)
*
* @param mixed the table to be searched (string name or xmldb_table instance)
* @param bool temp table (might need different checks)
* @return boolean true/false
*/
public function table_exists($table, $temptable=false) {
/// Do this function silenty (to avoid output in install/upgrade process)
$olddbdebug = $this->mdb->get_debug();
$this->mdb->set_debug(false);
if (is_string($table)) {
$tablename = $table;
} else {
/// Calculate the name of the table
$tablename = $table->getName();
}
/// get all tables in moodle database
$tables = $this->mdb->get_tables();
$exists = in_array($tablename, $tables);
/// Re-set original debug
$this->mdb->set_debug($olddbdebug);
return $exists;
}
/**
* This function will return the SQL code needed to create db tables and statements
*/
@ -344,6 +372,23 @@ abstract class sql_generator {
return $results;
}
/**
* Given one correct xmldb_table, returns the SQL statements
* to create temporary table (inside one array)
*/
public function getCreateTempTableSQL($xmldb_table) {
$sqlarr = $this->getCreateTableSQL($xmldb_table);
$sqlarr = preg_replace('/^CREATE TABLE/', "CREATE TEMPORARY TABLE", $sqlarr);
return $sqlarr;
}
/**
* Tweaks the temp table instance - required for mssql # naming
*/
public function tweakTempTable($xmldb_table) {
return $xmldb_table;
}
/**
* Given one correct xmldb_index, returns the SQL statements
* needed to create it (in array)
@ -576,6 +621,14 @@ abstract class sql_generator {
return $results;
}
/**
* Given one correct xmldb_table and the new name, returns the SQL statements
* to drop it (inside one array)
*/
public function getDropTempTableSQL($xmldb_table) {
return $this->getDropTableSQL($xmldb_table);
}
/**
* Given one xmldb_table and one xmldb_field, return the SQL statements needded to add the field to the table
*/

View File

@ -200,6 +200,15 @@ abstract class moodle_database {
return array($sql, $params);
}
/**
* Converts short table name {tablename} to real table name
* @param string sql
* @return string sql
*/
protected function fix_table_names($sql) {
return preg_replace('/\{([a-z][a-z0-9_]*)\}/', $this->prefix.'$1', $sql);
}
/**
* Normalizes sql query parameters and verifies parameters.
* @param string $sql query or part of it
@ -210,7 +219,7 @@ abstract class moodle_database {
$allowed_types = $this->allowed_param_types();
// convert table names
$sql = preg_replace('/\{([a-z][a-z0-9_]*)\}/', $this->prefix.'$1', $sql);
$sql = $this->fix_table_names($sql);
// NICOLAS C: Fixed regexp for negative backwards lookahead of double colons. Thanks for Sam Marshall's help
$named_count = preg_match_all('/(?<!:):[a-z][a-z0-9_]*/', $sql, $named_matches); // :: used in pgsql casts

View File

@ -8,6 +8,10 @@ require_once($CFG->libdir.'/dml/adodb_moodle_database.php');
* @package dmlib
*/
class mssql_adodb_moodle_database extends adodb_moodle_database {
/**
* Ungly mssql hack needed for temp table names starting with '#'
*/
public $temptables;
public function connect($dbhost, $dbuser, $dbpass, $dbname, $dbpersist, $prefix, array $dboptions=null) {
if ($prefix == '' and !$this->external) {
@ -84,6 +88,26 @@ class mssql_adodb_moodle_database extends adodb_moodle_database {
return $str;
}
/**
* Converts short table name {tablename} to real table name
* @param string sql
* @return string sql
*/
protected function fix_table_names($sql) {
// look for temporary tables, they must start with #
if (preg_match_all('/\{([a-z][a-z0-9_]*)\}/', $sql, $matches)) {
foreach($matches[0] as $key=>$match) {
$name = $matches[1][$key];
if (empty($this->temptables[$name])) {
$sql = str_replace($match, $this->prefix.$name, $sql);
} else {
$sql = str_replace($match, '#'.$this->prefix.$name, $sql);
}
}
}
return $sql;
}
/**
* Returns supported query parameter types
* @return bitmask