From: skodak Date: Sat, 7 Jun 2008 14:41:01 +0000 (+0000) Subject: MDL-15181 temp table support in ddl/dml X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=b922e86b7a04c474130f973d37867a66e636947f;p=moodle.git MDL-15181 temp table support in ddl/dml --- diff --git a/lib/ddl/database_manager.php b/lib/ddl/database_manager.php index 1b2d140be7..6382f41bfc 100644 --- a/lib/ddl/database_manager.php +++ b/lib/ddl/database_manager.php @@ -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 diff --git a/lib/ddl/mssql_sql_generator.php b/lib/ddl/mssql_sql_generator.php index 4616240d74..8a77157298 100644 --- a/lib/ddl/mssql_sql_generator.php +++ b/lib/ddl/mssql_sql_generator.php @@ -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 = ''; + public function getCreateTempTableSQL($xmldb_table) { + $sqlarr = $this->getCreateTableSQL($xmldb_table); + //ugly hack! + $this->mdb->temptables[trim($xmldb_table->getName(), '#')] = true; + return $sqlarr; + } - if (!empty($temporary)) { - $sqlarr = preg_replace('/^CREATE/', "CREATE $temporary", $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; + } - if (execute_sql_arr($sqlarr, $continue, $feedback)) { - return $xmldb_table->getName(); - } else { - return false; + /** + * 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; } /** diff --git a/lib/ddl/mysql_sql_generator.php b/lib/ddl/mysql_sql_generator.php index 4ccab7c65b..608e2c6db1 100644 --- a/lib/ddl/mysql_sql_generator.php +++ b/lib/ddl/mysql_sql_generator.php @@ -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 */ diff --git a/lib/ddl/oracle_sql_generator.php b/lib/ddl/oracle_sql_generator.php index 8f4723875c..a7287d84bc 100644 --- a/lib/ddl/oracle_sql_generator.php +++ b/lib/ddl/oracle_sql_generator.php @@ -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 */ diff --git a/lib/ddl/simpletest/testddllib.php b/lib/ddl/simpletest/testddllib.php index 7545d1608b..04cbf07178 100755 --- a/lib/ddl/simpletest/testddllib.php +++ b/lib/ddl/simpletest/testddllib.php @@ -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); + $table = $this->tables[1]; + $this->dbmanager->drop_table($table); $this->assertTrue($this->dbmanager->create_table($table)); - $this->assertTrue($this->dbmanager->table_exists("other_test_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)); } } ?> diff --git a/lib/ddl/sql_generator.php b/lib/ddl/sql_generator.php index c2b37404a9..bef24fda11 100644 --- a/lib/ddl/sql_generator.php +++ b/lib/ddl/sql_generator.php @@ -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 */ diff --git a/lib/dml/moodle_database.php b/lib/dml/moodle_database.php index 901d7819df..e170e7c773 100644 --- a/lib/dml/moodle_database.php +++ b/lib/dml/moodle_database.php @@ -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('/(?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