[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/ddl/tests/ -> ddl_test.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * DDL layer tests.
  19   *
  20   * @package    core_ddl
  21   * @category   phpunit
  22   * @copyright  2008 Nicolas Connault
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  class core_ddl_testcase extends database_driver_testcase {
  29      private $tables = array();
  30      private $records= array();
  31  
  32      protected function setUp() {
  33          parent::setUp();
  34          $dbman = $this->tdb->get_manager(); // Loads DDL libs.
  35  
  36          $table = new xmldb_table('test_table0');
  37          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  38          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  39          $table->add_field('type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general');
  40          $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null);
  41          $table->add_field('intro', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null);
  42          $table->add_field('logo', XMLDB_TYPE_BINARY, 'big', null, null, null);
  43          $table->add_field('assessed', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  44          $table->add_field('assesstimestart', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  45          $table->add_field('assesstimefinish', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  46          $table->add_field('scale', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  47          $table->add_field('maxbytes', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  48          $table->add_field('forcesubscribe', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0');
  49          $table->add_field('trackingtype', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1');
  50          $table->add_field('rsstype', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
  51          $table->add_field('rssarticles', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
  52          $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  53          $table->add_field('grade', XMLDB_TYPE_NUMBER, '20,0', null, null, null, null);
  54          $table->add_field('percent', XMLDB_TYPE_NUMBER, '5,2', null, null, null, 66.6);
  55          $table->add_field('warnafter', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  56          $table->add_field('blockafter', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  57          $table->add_field('blockperiod', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  58          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  59          $table->add_key('course', XMLDB_KEY_UNIQUE, array('course'));
  60          $table->add_index('type-name', XMLDB_INDEX_UNIQUE, array('type', 'name'));
  61          $table->add_index('rsstype', XMLDB_INDEX_NOTUNIQUE, array('rsstype'));
  62          $table->setComment("This is a test'n drop table. You can drop it safely");
  63  
  64          $this->tables[$table->getName()] = $table;
  65  
  66          // Define 2 initial records for this table.
  67          $this->records[$table->getName()] = array(
  68              (object)array(
  69                  'course' => '1',
  70                  'type'   => 'general',
  71                  'name'   => 'record',
  72                  'intro'  => 'first record'),
  73              (object)array(
  74                  'course' => '2',
  75                  'type'   => 'social',
  76                  'name'   => 'record',
  77                  'intro'  => 'second record'));
  78  
  79          // Second, smaller table.
  80          $table = new xmldb_table ('test_table1');
  81          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
  82          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  83          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, null, null, 'Moodle');
  84          $table->add_field('secondname', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
  85          $table->add_field('thirdname', XMLDB_TYPE_CHAR, '30', null, null, null, ''); // Nullable column with empty default.
  86          $table->add_field('intro', XMLDB_TYPE_TEXT, 'medium', null, XMLDB_NOTNULL, null, null);
  87          $table->add_field('avatar', XMLDB_TYPE_BINARY, 'medium', null, null, null, null);
  88          $table->add_field('grade', XMLDB_TYPE_NUMBER, '20,10', null, null, null);
  89          $table->add_field('gradefloat', XMLDB_TYPE_FLOAT, '20,0', null, null, null, null);
  90          $table->add_field('percentfloat', XMLDB_TYPE_FLOAT, '5,2', null, null, null, 99.9);
  91          $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
  92          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
  93          $table->add_key('course', XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('course'));
  94          $table->setComment("This is a test'n drop table. You can drop it safely");
  95  
  96          $this->tables[$table->getName()] = $table;
  97  
  98          // Define 2 initial records for this table.
  99          $this->records[$table->getName()] = array(
 100              (object)array(
 101                  'course' => '1',
 102                  'secondname'   => 'first record', // Less than 10 cc, please don't modify. Some tests below depend of this.
 103                  'intro'  => 'first record'),
 104              (object)array(
 105                  'course'       => '2',
 106                  'secondname'   => 'second record', // More than 10 cc, please don't modify. Some tests below depend of this.
 107                  'intro'  => 'second record'));
 108      }
 109  
 110      private function create_deftable($tablename) {
 111          $dbman = $this->tdb->get_manager();
 112  
 113          if (!isset($this->tables[$tablename])) {
 114              return null;
 115          }
 116  
 117          $table = $this->tables[$tablename];
 118  
 119          if ($dbman->table_exists($table)) {
 120              $dbman->drop_table($table);
 121          }
 122          $dbman->create_table($table);
 123  
 124          return $table;
 125      }
 126  
 127      /**
 128       * Fill the given test table with some records, as far as
 129       * DDL behaviour must be tested both with real data and
 130       * with empty tables
 131       * @param string $tablename
 132       * @return int count of records
 133       */
 134      private function fill_deftable($tablename) {
 135          $DB = $this->tdb; // Do not use global $DB!
 136          $dbman = $this->tdb->get_manager();
 137  
 138          if (!isset($this->records[$tablename])) {
 139              return null;
 140          }
 141  
 142          if ($dbman->table_exists($tablename)) {
 143              foreach ($this->records[$tablename] as $row) {
 144                  $DB->insert_record($tablename, $row);
 145              }
 146          } else {
 147              return null;
 148          }
 149  
 150          return count($this->records[$tablename]);
 151      }
 152  
 153      /**
 154       * Test behaviour of table_exists()
 155       */
 156      public function test_table_exists() {
 157          $DB = $this->tdb; // Do not use global $DB!
 158          $dbman = $this->tdb->get_manager();
 159  
 160          // First make sure it returns false if table does not exist.
 161          $table = $this->tables['test_table0'];
 162  
 163          try {
 164              $result = $DB->get_records('test_table0');
 165          } catch (dml_exception $e) {
 166              $result = false;
 167          }
 168          $this->resetDebugging();
 169  
 170          $this->assertFalse($result);
 171  
 172          $this->assertFalse($dbman->table_exists('test_table0')); // By name..
 173          $this->assertFalse($dbman->table_exists($table));        // By xmldb_table..
 174  
 175          // Create table and test again.
 176          $dbman->create_table($table);
 177  
 178          $this->assertSame(array(), $DB->get_records('test_table0'));
 179          $this->assertTrue($dbman->table_exists('test_table0')); // By name.
 180          $this->assertTrue($dbman->table_exists($table));        // By xmldb_table.
 181  
 182          // Drop table and test again.
 183          $dbman->drop_table($table);
 184  
 185          try {
 186              $result = $DB->get_records('test_table0');
 187          } catch (dml_exception $e) {
 188              $result = false;
 189          }
 190          $this->resetDebugging();
 191  
 192          $this->assertFalse($result);
 193  
 194          $this->assertFalse($dbman->table_exists('test_table0')); // By name.
 195          $this->assertFalse($dbman->table_exists($table));        // By xmldb_table.
 196      }
 197  
 198      /**
 199       * Test behaviour of create_table()
 200       */
 201      public function test_create_table() {
 202          $DB = $this->tdb; // Do not use global $DB!
 203          $dbman = $this->tdb->get_manager();
 204  
 205          // Create table.
 206          $table = $this->tables['test_table1'];
 207  
 208          $dbman->create_table($table);
 209          $this->assertTrue($dbman->table_exists($table));
 210  
 211          // Basic get_tables() test.
 212          $tables = $DB->get_tables();
 213          $this->assertArrayHasKey('test_table1', $tables);
 214  
 215          // Basic get_columns() tests.
 216          $columns = $DB->get_columns('test_table1');
 217          $this->assertSame('R', $columns['id']->meta_type);
 218          $this->assertSame('I', $columns['course']->meta_type);
 219          $this->assertSame('C', $columns['name']->meta_type);
 220          $this->assertSame('C', $columns['secondname']->meta_type);
 221          $this->assertSame('C', $columns['thirdname']->meta_type);
 222          $this->assertSame('X', $columns['intro']->meta_type);
 223          $this->assertSame('B', $columns['avatar']->meta_type);
 224          $this->assertSame('N', $columns['grade']->meta_type);
 225          $this->assertSame('N', $columns['percentfloat']->meta_type);
 226          $this->assertSame('I', $columns['userid']->meta_type);
 227          // Some defaults.
 228          $this->assertTrue($columns['course']->has_default);
 229          $this->assertEquals(0, $columns['course']->default_value);
 230          $this->assertTrue($columns['name']->has_default);
 231          $this->assertSame('Moodle', $columns['name']->default_value);
 232          $this->assertTrue($columns['secondname']->has_default);
 233          $this->assertSame('', $columns['secondname']->default_value);
 234          $this->assertTrue($columns['thirdname']->has_default);
 235          $this->assertSame('', $columns['thirdname']->default_value);
 236          $this->assertTrue($columns['percentfloat']->has_default);
 237          $this->assertEquals(99.9, $columns['percentfloat']->default_value);
 238          $this->assertTrue($columns['userid']->has_default);
 239          $this->assertEquals(0, $columns['userid']->default_value);
 240  
 241          // Basic get_indexes() test.
 242          $indexes = $DB->get_indexes('test_table1');
 243          $courseindex = reset($indexes);
 244          $this->assertEquals(1, $courseindex['unique']);
 245          $this->assertSame('course', $courseindex['columns'][0]);
 246  
 247          // Check sequence returns 1 for first insert.
 248          $rec = (object)array(
 249              'course'     => 10,
 250              'secondname' => 'not important',
 251              'intro'      => 'not important');
 252          $this->assertSame(1, $DB->insert_record('test_table1', $rec));
 253  
 254          // Check defined defaults are working ok.
 255          $dbrec = $DB->get_record('test_table1', array('id' => 1));
 256          $this->assertSame('Moodle', $dbrec->name);
 257          $this->assertSame('', $dbrec->thirdname);
 258  
 259          // Check exceptions if multiple R columns.
 260          $table = new xmldb_table ('test_table2');
 261          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 262          $table->add_field('rid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 263          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 264          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 265          $table->add_key('primaryx', XMLDB_KEY_PRIMARY, array('id'));
 266          $table->setComment("This is a test'n drop table. You can drop it safely");
 267  
 268          $this->tables[$table->getName()] = $table;
 269  
 270          try {
 271              $dbman->create_table($table);
 272              $this->fail('Exception expected');
 273          } catch (moodle_exception $e) {
 274              $this->assertInstanceOf('ddl_exception', $e);
 275          }
 276  
 277          // Check exceptions missing primary key on R column.
 278          $table = new xmldb_table ('test_table2');
 279          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 280          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
 281          $table->setComment("This is a test'n drop table. You can drop it safely");
 282  
 283          $this->tables[$table->getName()] = $table;
 284  
 285          try {
 286              $dbman->create_table($table);
 287              $this->fail('Exception expected');
 288          } catch (moodle_exception $e) {
 289              $this->assertInstanceOf('ddl_exception', $e);
 290          }
 291  
 292          // Long table name names - the largest allowed.
 293          $table = new xmldb_table('test_table0123456789_____xyz');
 294          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 295          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 296          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 297          $table->setComment("This is a test'n drop table. You can drop it safely");
 298  
 299          $this->tables[$table->getName()] = $table;
 300  
 301          $dbman->create_table($table);
 302          $this->assertTrue($dbman->table_exists($table));
 303          $dbman->drop_table($table);
 304  
 305          // Table name is too long.
 306          $table = new xmldb_table('test_table0123456789_____xyz9');
 307          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 308          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 309          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 310          $table->setComment("This is a test'n drop table. You can drop it safely");
 311  
 312          $this->tables[$table->getName()] = $table;
 313  
 314          try {
 315              $dbman->create_table($table);
 316              $this->fail('Exception expected');
 317          } catch (moodle_exception $e) {
 318              $this->assertInstanceOf('coding_exception', $e);
 319          }
 320  
 321          // Invalid table name.
 322          $table = new xmldb_table('test_tableCD');
 323          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 324          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 325          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 326          $table->setComment("This is a test'n drop table. You can drop it safely");
 327  
 328          $this->tables[$table->getName()] = $table;
 329  
 330          try {
 331              $dbman->create_table($table);
 332              $this->fail('Exception expected');
 333          } catch (moodle_exception $e) {
 334              $this->assertInstanceOf('coding_exception', $e);
 335          }
 336  
 337          // Weird column names - the largest allowed.
 338          $table = new xmldb_table('test_table3');
 339          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 340          $table->add_field('abcdef____0123456789_______xyz', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 341          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 342          $table->setComment("This is a test'n drop table. You can drop it safely");
 343  
 344          $this->tables[$table->getName()] = $table;
 345  
 346          $dbman->create_table($table);
 347          $this->assertTrue($dbman->table_exists($table));
 348          $dbman->drop_table($table);
 349  
 350          // Too long field name - max 30.
 351          $table = new xmldb_table('test_table4');
 352          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 353          $table->add_field('abcdeabcdeabcdeabcdeabcdeabcdez', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 354          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 355          $table->setComment("This is a test'n drop table. You can drop it safely");
 356  
 357          $this->tables[$table->getName()] = $table;
 358  
 359          try {
 360              $dbman->create_table($table);
 361              $this->fail('Exception expected');
 362          } catch (moodle_exception $e) {
 363              $this->assertInstanceOf('coding_exception', $e);
 364          }
 365  
 366          // Invalid field name.
 367          $table = new xmldb_table('test_table4');
 368          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 369          $table->add_field('abCD', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 370          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 371          $table->setComment("This is a test'n drop table. You can drop it safely");
 372  
 373          $this->tables[$table->getName()] = $table;
 374  
 375          try {
 376              $dbman->create_table($table);
 377              $this->fail('Exception expected');
 378          } catch (moodle_exception $e) {
 379              $this->assertInstanceOf('coding_exception', $e);
 380          }
 381  
 382          // Invalid integer length.
 383          $table = new xmldb_table('test_table4');
 384          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 385          $table->add_field('course', XMLDB_TYPE_INTEGER, '21', null, XMLDB_NOTNULL, null, '2');
 386          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 387          $table->setComment("This is a test'n drop table. You can drop it safely");
 388  
 389          $this->tables[$table->getName()] = $table;
 390  
 391          try {
 392              $dbman->create_table($table);
 393              $this->fail('Exception expected');
 394          } catch (moodle_exception $e) {
 395              $this->assertInstanceOf('coding_exception', $e);
 396          }
 397  
 398          // Invalid integer default.
 399          $table = new xmldb_table('test_table4');
 400          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 401          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 'x');
 402          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 403          $table->setComment("This is a test'n drop table. You can drop it safely");
 404  
 405          $this->tables[$table->getName()] = $table;
 406  
 407          try {
 408              $dbman->create_table($table);
 409              $this->fail('Exception expected');
 410          } catch (moodle_exception $e) {
 411              $this->assertInstanceOf('coding_exception', $e);
 412          }
 413  
 414          // Invalid decimal length.
 415          $table = new xmldb_table('test_table4');
 416          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 417          $table->add_field('num', XMLDB_TYPE_NUMBER, '21,10', null, XMLDB_NOTNULL, null, null);
 418          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 419          $table->setComment("This is a test'n drop table. You can drop it safely");
 420  
 421          $this->tables[$table->getName()] = $table;
 422  
 423          try {
 424              $dbman->create_table($table);
 425              $this->fail('Exception expected');
 426          } catch (moodle_exception $e) {
 427              $this->assertInstanceOf('coding_exception', $e);
 428          }
 429  
 430          // Invalid decimal decimals.
 431          $table = new xmldb_table('test_table4');
 432          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 433          $table->add_field('num', XMLDB_TYPE_NUMBER, '10,11', null, XMLDB_NOTNULL, null, null);
 434          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 435          $table->setComment("This is a test'n drop table. You can drop it safely");
 436  
 437          $this->tables[$table->getName()] = $table;
 438  
 439          try {
 440              $dbman->create_table($table);
 441              $this->fail('Exception expected');
 442          } catch (moodle_exception $e) {
 443              $this->assertInstanceOf('coding_exception', $e);
 444          }
 445  
 446          // Invalid decimal default.
 447          $table = new xmldb_table('test_table4');
 448          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 449          $table->add_field('num', XMLDB_TYPE_NUMBER, '10,5', null, XMLDB_NOTNULL, null, 'x');
 450          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 451          $table->setComment("This is a test'n drop table. You can drop it safely");
 452  
 453          $this->tables[$table->getName()] = $table;
 454  
 455          try {
 456              $dbman->create_table($table);
 457              $this->fail('Exception expected');
 458          } catch (moodle_exception $e) {
 459              $this->assertInstanceOf('coding_exception', $e);
 460          }
 461  
 462          // Invalid float length.
 463          $table = new xmldb_table('test_table4');
 464          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 465          $table->add_field('num', XMLDB_TYPE_FLOAT, '21,10', null, XMLDB_NOTNULL, null, null);
 466          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 467          $table->setComment("This is a test'n drop table. You can drop it safely");
 468  
 469          $this->tables[$table->getName()] = $table;
 470  
 471          try {
 472              $dbman->create_table($table);
 473              $this->fail('Exception expected');
 474          } catch (moodle_exception $e) {
 475              $this->assertInstanceOf('coding_exception', $e);
 476          }
 477  
 478          // Invalid float decimals.
 479          $table = new xmldb_table('test_table4');
 480          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 481          $table->add_field('num', XMLDB_TYPE_FLOAT, '10,11', null, XMLDB_NOTNULL, null, null);
 482          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 483          $table->setComment("This is a test'n drop table. You can drop it safely");
 484  
 485          $this->tables[$table->getName()] = $table;
 486  
 487          try {
 488              $dbman->create_table($table);
 489              $this->fail('Exception expected');
 490          } catch (moodle_exception $e) {
 491              $this->assertInstanceOf('coding_exception', $e);
 492          }
 493  
 494          // Invalid float default.
 495          $table = new xmldb_table('test_table4');
 496          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 497          $table->add_field('num', XMLDB_TYPE_FLOAT, '10,5', null, XMLDB_NOTNULL, null, 'x');
 498          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 499          $table->setComment("This is a test'n drop table. You can drop it safely");
 500  
 501          $this->tables[$table->getName()] = $table;
 502  
 503          try {
 504              $dbman->create_table($table);
 505              $this->fail('Exception expected');
 506          } catch (moodle_exception $e) {
 507              $this->assertInstanceOf('coding_exception', $e);
 508          }
 509      }
 510  
 511      /**
 512       * Test if database supports tables with many TEXT fields,
 513       * InnoDB is known to failed during data insertion instead
 514       * of table creation when text fields contain actual data.
 515       */
 516      public function test_row_size_limits() {
 517  
 518          $DB = $this->tdb; // Do not use global $DB!
 519          $dbman = $this->tdb->get_manager();
 520  
 521          $text = str_repeat('Å¡', 1333);
 522  
 523          $data = new stdClass();
 524          $data->name = 'test';
 525          $table = new xmldb_table('test_innodb');
 526          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 527          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, null, null, null);
 528          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 529          for ($i = 0; $i < 20; $i++) {
 530              $table->add_field('text'.$i, XMLDB_TYPE_TEXT, null, null, null, null, null);
 531              $data->{'text'.$i} = $text;
 532          }
 533          $dbman->create_table($table);
 534  
 535          try {
 536              $id = $DB->insert_record('test_innodb', $data);
 537              $expected = (array)$data;
 538              $expected['id'] = (string)$id;
 539              $this->assertEquals($expected, (array)$DB->get_record('test_innodb', array('id' => $id)), '', 0, 10, true);
 540          } catch (dml_exception $e) {
 541              // Give some nice error message when known problematic MySQL with InnoDB detected.
 542              if ($DB->get_dbfamily() === 'mysql') {
 543                  $engine = strtolower($DB->get_dbengine());
 544                  if ($engine === 'innodb' or $engine === 'xtradb') {
 545                      if (!$DB->is_compressed_row_format_supported()) {
 546                          $this->fail("Row size limit reached in MySQL using InnoDB, configure server to use innodb_file_format=Barracuda and innodb_file_per_table=1");
 547                      }
 548                  }
 549              }
 550              throw $e;
 551          }
 552  
 553          $dbman->drop_table($table);
 554  
 555          $data = new stdClass();
 556          $data->name = 'test';
 557          $table = new xmldb_table('test_innodb');
 558          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 559          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, null, null, null);
 560          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 561          $dbman->create_table($table);
 562          $DB->insert_record('test_innodb', array('name' => 'test'));
 563  
 564          for ($i = 0; $i < 20; $i++) {
 565              $field = new xmldb_field('text'.$i, XMLDB_TYPE_TEXT, null, null, null, null, null);
 566              $dbman->add_field($table, $field);
 567              $data->{'text'.$i} = $text;
 568  
 569              $id = $DB->insert_record('test_innodb', $data);
 570              $expected = (array)$data;
 571              $expected['id'] = (string)$id;
 572              $this->assertEquals($expected, (array)$DB->get_record('test_innodb', array('id' => $id)), '', 0, 10, true);
 573          }
 574  
 575          $dbman->drop_table($table);
 576  
 577          // MySQL VARCHAR fields may hit a different 65535 row size limit when creating tables.
 578          $data = new stdClass();
 579          $data->name = 'test';
 580          $table = new xmldb_table('test_innodb');
 581          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 582          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, null, null, null);
 583          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 584          for ($i = 0; $i < 15; $i++) {
 585              $table->add_field('text'.$i, XMLDB_TYPE_CHAR, '1333', null, null, null, null);
 586              $data->{'text'.$i} = $text;
 587          }
 588          $dbman->create_table($table);
 589  
 590          $id = $DB->insert_record('test_innodb', $data);
 591          $expected = (array)$data;
 592          $expected['id'] = (string)$id;
 593          $this->assertEquals($expected, (array)$DB->get_record('test_innodb', array('id' => $id)), '', 0, 10, true);
 594  
 595          $dbman->drop_table($table);
 596      }
 597  
 598      /**
 599       * Test behaviour of drop_table()
 600       */
 601      public function test_drop_table() {
 602          $DB = $this->tdb; // Do not use global $DB!
 603          $dbman = $this->tdb->get_manager();
 604  
 605          // Initially table doesn't exist.
 606          $this->assertFalse($dbman->table_exists('test_table0'));
 607  
 608          // Create table with contents.
 609          $table = $this->create_deftable('test_table0');
 610          $this->assertTrue($dbman->table_exists('test_table0'));
 611  
 612          // Fill the table with some records before dropping it.
 613          $this->fill_deftable('test_table0');
 614  
 615          // Drop by xmldb_table object.
 616          $dbman->drop_table($table);
 617          $this->assertFalse($dbman->table_exists('test_table0'));
 618  
 619          // Basic get_tables() test.
 620          $tables = $DB->get_tables();
 621          $this->assertArrayNotHasKey('test_table0', $tables);
 622  
 623          // Columns cache must be empty.
 624          $columns = $DB->get_columns('test_table0');
 625          $this->assertEmpty($columns);
 626  
 627          $indexes = $DB->get_indexes('test_table0');
 628          $this->assertEmpty($indexes);
 629      }
 630  
 631      /**
 632       * Test behaviour of rename_table()
 633       */
 634      public function test_rename_table() {
 635          $DB = $this->tdb; // Do not use global $DB!
 636          $dbman = $this->tdb->get_manager();
 637  
 638          $table = $this->create_deftable('test_table1');
 639  
 640          // Fill the table with some records before renaming it.
 641          $insertedrows = $this->fill_deftable('test_table1');
 642  
 643          $this->assertFalse($dbman->table_exists('test_table_cust1'));
 644          $dbman->rename_table($table, 'test_table_cust1');
 645          $this->assertTrue($dbman->table_exists('test_table_cust1'));
 646  
 647          // Check sequence returns $insertedrows + 1 for this insert (after rename).
 648          $rec = (object)array(
 649              'course'     => 20,
 650              'secondname' => 'not important',
 651              'intro'      => 'not important');
 652          $this->assertSame($insertedrows+1, $DB->insert_record('test_table_cust1', $rec));
 653  
 654          // Verify behavior when target table already exists.
 655          $sourcetable = $this->create_deftable('test_table0');
 656          $targettable = $this->create_deftable('test_table1');
 657          try {
 658              $dbman->rename_table($sourcetable, $targettable->getName());
 659              $this->fail('Exception expected');
 660          } catch (moodle_exception $e) {
 661              $this->assertInstanceOf('ddl_exception', $e);
 662              $this->assertEquals('Table "test_table1" already exists (can not rename table)', $e->getMessage());
 663          }
 664      }
 665  
 666      /**
 667       * Test behaviour of field_exists()
 668       */
 669      public function test_field_exists() {
 670          $dbman = $this->tdb->get_manager();
 671  
 672          $table = $this->create_deftable('test_table0');
 673  
 674          // String params.
 675          // Give a nonexistent table as first param (throw exception).
 676          try {
 677              $dbman->field_exists('nonexistenttable', 'id');
 678              $this->fail('Exception expected');
 679          } catch (moodle_exception $e) {
 680              $this->assertInstanceOf('moodle_exception', $e);
 681          }
 682  
 683          // Give a nonexistent field as second param (return false).
 684          $this->assertFalse($dbman->field_exists('test_table0', 'nonexistentfield'));
 685  
 686          // Correct string params.
 687          $this->assertTrue($dbman->field_exists('test_table0', 'id'));
 688  
 689          // Object params.
 690          $realfield = $table->getField('id');
 691  
 692          // Give a nonexistent table as first param (throw exception).
 693          $nonexistenttable = new xmldb_table('nonexistenttable');
 694          try {
 695              $dbman->field_exists($nonexistenttable, $realfield);
 696              $this->fail('Exception expected');
 697          } catch (moodle_exception $e) {
 698              $this->assertInstanceOf('moodle_exception', $e);
 699          }
 700  
 701          // Give a nonexistent field as second param (return false).
 702          $nonexistentfield = new xmldb_field('nonexistentfield');
 703          $this->assertFalse($dbman->field_exists($table, $nonexistentfield));
 704  
 705          // Correct object params.
 706          $this->assertTrue($dbman->field_exists($table, $realfield));
 707  
 708          // Mix string and object params.
 709          // Correct ones.
 710          $this->assertTrue($dbman->field_exists($table, 'id'));
 711          $this->assertTrue($dbman->field_exists('test_table0', $realfield));
 712          // Non existing tables (throw exception).
 713          try {
 714              $this->assertFalse($dbman->field_exists($nonexistenttable, 'id'));
 715              $this->fail('Exception expected');
 716          } catch (moodle_exception $e) {
 717              $this->assertInstanceOf('moodle_exception', $e);
 718          }
 719          try {
 720              $this->assertFalse($dbman->field_exists('nonexistenttable', $realfield));
 721              $this->fail('Exception expected');
 722          } catch (moodle_exception $e) {
 723              $this->assertInstanceOf('moodle_exception', $e);
 724          }
 725          // Non existing fields (return false).
 726          $this->assertFalse($dbman->field_exists($table, 'nonexistentfield'));
 727          $this->assertFalse($dbman->field_exists('test_table0', $nonexistentfield));
 728      }
 729  
 730      /**
 731       * Test behaviour of add_field()
 732       */
 733      public function test_add_field() {
 734          $DB = $this->tdb; // Do not use global $DB!
 735          $dbman = $this->tdb->get_manager();
 736  
 737          $table = $this->create_deftable('test_table1');
 738  
 739          // Fill the table with some records before adding fields.
 740          $this->fill_deftable('test_table1');
 741  
 742          // Add one not null field without specifying default value (throws ddl_exception).
 743          $field = new xmldb_field('onefield');
 744          $field->set_attributes(XMLDB_TYPE_INTEGER, '6', null, XMLDB_NOTNULL, null, null);
 745          try {
 746              $dbman->add_field($table, $field);
 747              $this->fail('Exception expected');
 748          } catch (moodle_exception $e) {
 749              $this->assertInstanceOf('ddl_exception', $e);
 750          }
 751  
 752          // Add one existing field (throws ddl_exception).
 753          $field = new xmldb_field('course');
 754          $field->set_attributes(XMLDB_TYPE_INTEGER, '6', null, XMLDB_NOTNULL, null, 2);
 755          try {
 756              $dbman->add_field($table, $field);
 757              $this->fail('Exception expected');
 758          } catch (moodle_exception $e) {
 759              $this->assertInstanceOf('ddl_exception', $e);
 760          }
 761  
 762          // TODO: add one field with invalid type, must throw exception.
 763          // TODO: add one text field with default, must throw exception.
 764          // TODO: add one binary field with default, must throw exception.
 765  
 766          // Add one integer field and check it.
 767          $field = new xmldb_field('oneinteger');
 768          $field->set_attributes(XMLDB_TYPE_INTEGER, '6', null, XMLDB_NOTNULL, null, 2);
 769          $dbman->add_field($table, $field);
 770          $this->assertTrue($dbman->field_exists($table, 'oneinteger'));
 771          $columns = $DB->get_columns('test_table1');
 772          $this->assertEquals('oneinteger', $columns['oneinteger']->name);
 773          $this->assertTrue($columns['oneinteger']->not_null);
 774          // Max_length and scale cannot be checked under all DBs at all for integer fields.
 775          $this->assertFalse($columns['oneinteger']->primary_key);
 776          $this->assertFalse($columns['oneinteger']->binary);
 777          $this->assertTrue($columns['oneinteger']->has_default);
 778          $this->assertEquals(2, $columns['oneinteger']->default_value);
 779          $this->assertSame('I', $columns['oneinteger']->meta_type);
 780          $this->assertEquals(2, $DB->get_field('test_table1', 'oneinteger', array(), IGNORE_MULTIPLE)); // Check default has been applied.
 781  
 782          // Add one numeric field and check it.
 783          $field = new xmldb_field('onenumber');
 784          $field->set_attributes(XMLDB_TYPE_NUMBER, '6,3', null, XMLDB_NOTNULL, null, 2.55);
 785          $dbman->add_field($table, $field);
 786          $this->assertTrue($dbman->field_exists($table, 'onenumber'));
 787          $columns = $DB->get_columns('test_table1');
 788          $this->assertSame('onenumber', $columns['onenumber']->name);
 789          $this->assertEquals(6, $columns['onenumber']->max_length);
 790          $this->assertEquals(3, $columns['onenumber']->scale);
 791          $this->assertTrue($columns['onenumber']->not_null);
 792          $this->assertFalse($columns['onenumber']->primary_key);
 793          $this->assertFalse($columns['onenumber']->binary);
 794          $this->assertTrue($columns['onenumber']->has_default);
 795          $this->assertEquals(2.550, $columns['onenumber']->default_value);
 796          $this->assertSame('N', $columns['onenumber']->meta_type);
 797          $this->assertEquals(2.550, $DB->get_field('test_table1', 'onenumber', array(), IGNORE_MULTIPLE)); // Check default has been applied.
 798  
 799          // Add one numeric field with scale of 0 and check it.
 800          $field = new xmldb_field('onenumberwith0scale');
 801          $field->set_attributes(XMLDB_TYPE_NUMBER, '6,0', null, XMLDB_NOTNULL, null, 2);
 802          $dbman->add_field($table, $field);
 803          $this->assertTrue($dbman->field_exists($table, 'onenumberwith0scale'));
 804          $columns = $DB->get_columns('test_table1');
 805          $this->assertEquals(6, $columns['onenumberwith0scale']->max_length);
 806          // We can not use assertEquals as that accepts null/false as a valid value.
 807          $this->assertSame('0', strval($columns['onenumberwith0scale']->scale));
 808  
 809          // Add one float field and check it (not official type - must work as number).
 810          $field = new xmldb_field('onefloat');
 811          $field->set_attributes(XMLDB_TYPE_FLOAT, '6,3', null, XMLDB_NOTNULL, null, 3.550);
 812          $dbman->add_field($table, $field);
 813          $this->assertTrue($dbman->field_exists($table, 'onefloat'));
 814          $columns = $DB->get_columns('test_table1');
 815          $this->assertSame('onefloat', $columns['onefloat']->name);
 816          $this->assertTrue($columns['onefloat']->not_null);
 817          // Max_length and scale cannot be checked under all DBs at all for float fields.
 818          $this->assertFalse($columns['onefloat']->primary_key);
 819          $this->assertFalse($columns['onefloat']->binary);
 820          $this->assertTrue($columns['onefloat']->has_default);
 821          $this->assertEquals(3.550, $columns['onefloat']->default_value);
 822          $this->assertSame('N', $columns['onefloat']->meta_type);
 823          // Just rounding DB information to 7 decimal digits. Fair enough to test 3.550 and avoids one nasty bug
 824          // in MSSQL core returning wrong floats (http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/5e08de63-16bb-4f24-b645-0cf8fc669de3)
 825          // In any case, floats aren't officially supported by Moodle, with number/decimal type being the correct ones, so
 826          // this isn't a real problem at all.
 827          $this->assertEquals(3.550, round($DB->get_field('test_table1', 'onefloat', array(), IGNORE_MULTIPLE), 7)); // Check default has been applied.
 828  
 829          // Add one char field and check it.
 830          $field = new xmldb_field('onechar');
 831          $field->set_attributes(XMLDB_TYPE_CHAR, '25', null, XMLDB_NOTNULL, null, 'Nice dflt!');
 832          $dbman->add_field($table, $field);
 833          $this->assertTrue($dbman->field_exists($table, 'onechar'));
 834          $columns = $DB->get_columns('test_table1');
 835          $this->assertSame('onechar', $columns['onechar']->name);
 836          $this->assertEquals(25, $columns['onechar']->max_length);
 837          $this->assertNull($columns['onechar']->scale);
 838          $this->assertTrue($columns['onechar']->not_null);
 839          $this->assertFalse($columns['onechar']->primary_key);
 840          $this->assertFalse($columns['onechar']->binary);
 841          $this->assertTrue($columns['onechar']->has_default);
 842          $this->assertSame('Nice dflt!', $columns['onechar']->default_value);
 843          $this->assertSame('C', $columns['onechar']->meta_type);
 844          $this->assertEquals('Nice dflt!', $DB->get_field('test_table1', 'onechar', array(), IGNORE_MULTIPLE)); // Check default has been applied.
 845  
 846          // Add one big text field and check it.
 847          $field = new xmldb_field('onetext');
 848          $field->set_attributes(XMLDB_TYPE_TEXT, 'big');
 849          $dbman->add_field($table, $field);
 850          $this->assertTrue($dbman->field_exists($table, 'onetext'));
 851          $columns = $DB->get_columns('test_table1');
 852          $this->assertSame('onetext', $columns['onetext']->name);
 853          $this->assertEquals(-1, $columns['onetext']->max_length); // -1 means unknown or big.
 854          $this->assertNull($columns['onetext']->scale);
 855          $this->assertFalse($columns['onetext']->not_null);
 856          $this->assertFalse($columns['onetext']->primary_key);
 857          $this->assertFalse($columns['onetext']->binary);
 858          $this->assertFalse($columns['onetext']->has_default);
 859          $this->assertNull($columns['onetext']->default_value);
 860          $this->assertSame('X', $columns['onetext']->meta_type);
 861  
 862          // Add one medium text field and check it.
 863          $field = new xmldb_field('mediumtext');
 864          $field->set_attributes(XMLDB_TYPE_TEXT, 'medium');
 865          $dbman->add_field($table, $field);
 866          $columns = $DB->get_columns('test_table1');
 867          $this->assertTrue(($columns['mediumtext']->max_length == -1) or ($columns['mediumtext']->max_length >= 16777215)); // -1 means unknown or big.
 868  
 869          // Add one small text field and check it.
 870          $field = new xmldb_field('smalltext');
 871          $field->set_attributes(XMLDB_TYPE_TEXT, 'small');
 872          $dbman->add_field($table, $field);
 873          $columns = $DB->get_columns('test_table1');
 874          $this->assertTrue(($columns['smalltext']->max_length == -1) or ($columns['smalltext']->max_length >= 65535)); // -1 means unknown or big.
 875  
 876          // Add one binary field and check it.
 877          $field = new xmldb_field('onebinary');
 878          $field->set_attributes(XMLDB_TYPE_BINARY);
 879          $dbman->add_field($table, $field);
 880          $this->assertTrue($dbman->field_exists($table, 'onebinary'));
 881          $columns = $DB->get_columns('test_table1');
 882          $this->assertSame('onebinary', $columns['onebinary']->name);
 883          $this->assertEquals(-1, $columns['onebinary']->max_length);
 884          $this->assertNull($columns['onebinary']->scale);
 885          $this->assertFalse($columns['onebinary']->not_null);
 886          $this->assertFalse($columns['onebinary']->primary_key);
 887          $this->assertTrue($columns['onebinary']->binary);
 888          $this->assertFalse($columns['onebinary']->has_default);
 889          $this->assertNull($columns['onebinary']->default_value);
 890          $this->assertSame('B', $columns['onebinary']->meta_type);
 891  
 892          // TODO: check datetime type. Although unused should be fully supported.
 893      }
 894  
 895      /**
 896       * Test behaviour of drop_field()
 897       */
 898      public function test_drop_field() {
 899          $DB = $this->tdb; // Do not use global $DB!
 900          $dbman = $this->tdb->get_manager();
 901  
 902          $table = $this->create_deftable('test_table0');
 903  
 904          // Fill the table with some records before dropping fields.
 905          $this->fill_deftable('test_table0');
 906  
 907          // Drop field with simple xmldb_field having indexes, must return exception.
 908          $field = new xmldb_field('type'); // Field has indexes and default clause.
 909          $this->assertTrue($dbman->field_exists($table, 'type'));
 910          try {
 911              $dbman->drop_field($table, $field);
 912              $this->fail('Exception expected');
 913          } catch (moodle_exception $e) {
 914              $this->assertInstanceOf('ddl_dependency_exception', $e);
 915          }
 916          $this->assertTrue($dbman->field_exists($table, 'type')); // Continues existing, drop aborted.
 917  
 918          // Drop field with complete xmldb_field object and related indexes, must return exception.
 919          $field = $table->getField('course'); // Field has indexes and default clause.
 920          $this->assertTrue($dbman->field_exists($table, $field));
 921          try {
 922              $dbman->drop_field($table, $field);
 923              $this->fail('Exception expected');
 924          } catch (moodle_exception $e) {
 925              $this->assertInstanceOf('ddl_dependency_exception', $e);
 926          }
 927          $this->assertTrue($dbman->field_exists($table, $field)); // Continues existing, drop aborted.
 928  
 929          // Drop one non-existing field, must return exception.
 930          $field = new xmldb_field('nonexistingfield');
 931          $this->assertFalse($dbman->field_exists($table, $field));
 932          try {
 933              $dbman->drop_field($table, $field);
 934              $this->fail('Exception expected');
 935          } catch (moodle_exception $e) {
 936              $this->assertInstanceOf('ddl_field_missing_exception', $e);
 937          }
 938  
 939          // Drop field with simple xmldb_field, not having related indexes.
 940          $field = new xmldb_field('forcesubscribe'); // Field has default clause.
 941          $this->assertTrue($dbman->field_exists($table, 'forcesubscribe'));
 942          $dbman->drop_field($table, $field);
 943          $this->assertFalse($dbman->field_exists($table, 'forcesubscribe'));
 944  
 945          // Drop field with complete xmldb_field object, not having related indexes.
 946          $field = new xmldb_field('trackingtype'); // Field has default clause.
 947          $this->assertTrue($dbman->field_exists($table, $field));
 948          $dbman->drop_field($table, $field);
 949          $this->assertFalse($dbman->field_exists($table, $field));
 950      }
 951  
 952      /**
 953       * Test behaviour of change_field_type()
 954       */
 955      public function test_change_field_type() {
 956          $DB = $this->tdb; // Do not use global $DB!
 957          $dbman = $this->tdb->get_manager();
 958  
 959          // Create table with indexed field and not indexed field to
 960          // perform tests in both fields, both having defaults.
 961          $table = new xmldb_table('test_table_cust0');
 962          $table->add_field('id', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
 963          $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '2');
 964          $table->add_field('anothernumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '4');
 965          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
 966          $table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
 967          $dbman->create_table($table);
 968  
 969          $record = new stdClass();
 970          $record->onenumber = 2;
 971          $record->anothernumber = 4;
 972          $recoriginal = $DB->insert_record('test_table_cust0', $record);
 973  
 974          // Change column from integer to varchar. Must return exception because of dependent index.
 975          $field = new xmldb_field('onenumber');
 976          $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'test');
 977          try {
 978              $dbman->change_field_type($table, $field);
 979              $this->fail('Exception expected');
 980          } catch (moodle_exception $e) {
 981              $this->assertInstanceOf('ddl_dependency_exception', $e);
 982          }
 983          // Column continues being integer 10 not null default 2.
 984          $columns = $DB->get_columns('test_table_cust0');
 985          $this->assertSame('I', $columns['onenumber']->meta_type);
 986          // TODO: check the rest of attributes.
 987  
 988          // Change column from integer to varchar. Must work because column has no dependencies.
 989          $field = new xmldb_field('anothernumber');
 990          $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'test');
 991          $dbman->change_field_type($table, $field);
 992          // Column is char 30 not null default 'test' now.
 993          $columns = $DB->get_columns('test_table_cust0');
 994          $this->assertSame('C', $columns['anothernumber']->meta_type);
 995          // TODO: check the rest of attributes.
 996  
 997          // Change column back from char to integer.
 998          $field = new xmldb_field('anothernumber');
 999          $field->set_attributes(XMLDB_TYPE_INTEGER, '8', null, XMLDB_NOTNULL, null, '5');
1000          $dbman->change_field_type($table, $field);
1001          // Column is integer 8 not null default 5 now.
1002          $columns = $DB->get_columns('test_table_cust0');
1003          $this->assertSame('I', $columns['anothernumber']->meta_type);
1004          // TODO: check the rest of attributes.
1005  
1006          // Change column once more from integer to char.
1007          $field = new xmldb_field('anothernumber');
1008          $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, "test'n drop");
1009          $dbman->change_field_type($table, $field);
1010          // Column is char 30 not null default "test'n drop" now.
1011          $columns = $DB->get_columns('test_table_cust0');
1012          $this->assertSame('C', $columns['anothernumber']->meta_type);
1013          // TODO: check the rest of attributes.
1014  
1015          // Insert one string value and try to convert to integer. Must throw exception.
1016          $record = new stdClass();
1017          $record->onenumber = 7;
1018          $record->anothernumber = 'string value';
1019          $rectodrop = $DB->insert_record('test_table_cust0', $record);
1020          $field = new xmldb_field('anothernumber');
1021          $field->set_attributes(XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '5');
1022          try {
1023              $dbman->change_field_type($table, $field);
1024              $this->fail('Exception expected');
1025          } catch (moodle_exception $e) {
1026              $this->assertInstanceOf('ddl_change_structure_exception', $e);
1027          }
1028          // Column continues being char 30 not null default "test'n drop" now.
1029          $this->assertSame('C', $columns['anothernumber']->meta_type);
1030          // TODO: check the rest of attributes.
1031          $DB->delete_records('test_table_cust0', array('id' => $rectodrop)); // Delete the string record.
1032  
1033          // Change the column from varchar to float.
1034          $field = new xmldb_field('anothernumber');
1035          $field->set_attributes(XMLDB_TYPE_FLOAT, '20,10', null, null, null, null);
1036          $dbman->change_field_type($table, $field);
1037          // Column is float 20,10 null default null.
1038          $columns = $DB->get_columns('test_table_cust0');
1039          $this->assertSame('N', $columns['anothernumber']->meta_type); // Floats are seen as number.
1040          // TODO: check the rest of attributes.
1041  
1042          // Change the column back from float to varchar.
1043          $field = new xmldb_field('anothernumber');
1044          $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'test');
1045          $dbman->change_field_type($table, $field);
1046          // Column is char 20 not null default "test" now.
1047          $columns = $DB->get_columns('test_table_cust0');
1048          $this->assertSame('C', $columns['anothernumber']->meta_type);
1049          // TODO: check the rest of attributes.
1050  
1051          // Change the column from varchar to number.
1052          $field = new xmldb_field('anothernumber');
1053          $field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', null, null, null, null);
1054          $dbman->change_field_type($table, $field);
1055          // Column is number 20,10 null default null now.
1056          $columns = $DB->get_columns('test_table_cust0');
1057          $this->assertSame('N', $columns['anothernumber']->meta_type);
1058          // TODO: check the rest of attributes.
1059  
1060          // Change the column from number to integer.
1061          $field = new xmldb_field('anothernumber');
1062          $field->set_attributes(XMLDB_TYPE_INTEGER, '2', null, null, null, null);
1063          $dbman->change_field_type($table, $field);
1064          // Column is integer 2 null default null now.
1065          $columns = $DB->get_columns('test_table_cust0');
1066          $this->assertSame('I', $columns['anothernumber']->meta_type);
1067          // TODO: check the rest of attributes.
1068  
1069          // Change the column from integer to text.
1070          $field = new xmldb_field('anothernumber');
1071          $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
1072          $dbman->change_field_type($table, $field);
1073          // Column is char text not null default null.
1074          $columns = $DB->get_columns('test_table_cust0');
1075          $this->assertSame('X', $columns['anothernumber']->meta_type);
1076  
1077          // Change the column back from text to number.
1078          $field = new xmldb_field('anothernumber');
1079          $field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', null, null, null, null);
1080          $dbman->change_field_type($table, $field);
1081          // Column is number 20,10 null default null now.
1082          $columns = $DB->get_columns('test_table_cust0');
1083          $this->assertSame('N', $columns['anothernumber']->meta_type);
1084          // TODO: check the rest of attributes.
1085  
1086          // Change the column from number to text.
1087          $field = new xmldb_field('anothernumber');
1088          $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
1089          $dbman->change_field_type($table, $field);
1090          // Column is char text not null default "test" now.
1091          $columns = $DB->get_columns('test_table_cust0');
1092          $this->assertSame('X', $columns['anothernumber']->meta_type);
1093          // TODO: check the rest of attributes.
1094  
1095          // Change the column back from text to integer.
1096          $field = new xmldb_field('anothernumber');
1097          $field->set_attributes(XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 10);
1098          $dbman->change_field_type($table, $field);
1099          // Column is integer 10 not null default 10.
1100          $columns = $DB->get_columns('test_table_cust0');
1101          $this->assertSame('I', $columns['anothernumber']->meta_type);
1102          // TODO: check the rest of attributes.
1103  
1104          // Check original value has survived to all the type changes.
1105          $this->assertnotEmpty($rec = $DB->get_record('test_table_cust0', array('id' => $recoriginal)));
1106          $this->assertEquals(4, $rec->anothernumber);
1107  
1108          $dbman->drop_table($table);
1109          $this->assertFalse($dbman->table_exists($table));
1110      }
1111  
1112      /**
1113       * Test behaviour of test_change_field_precision()
1114       */
1115      public function test_change_field_precision() {
1116          $DB = $this->tdb; // Do not use global $DB!
1117          $dbman = $this->tdb->get_manager();
1118  
1119          $table = $this->create_deftable('test_table1');
1120  
1121          // Fill the table with some records before dropping fields.
1122          $this->fill_deftable('test_table1');
1123  
1124          // Change text field from medium to big.
1125          $field = new xmldb_field('intro');
1126          $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
1127          $dbman->change_field_precision($table, $field);
1128          $columns = $DB->get_columns('test_table1');
1129          // Cannot check the text type, only the metatype.
1130          $this->assertSame('X', $columns['intro']->meta_type);
1131          // TODO: check the rest of attributes.
1132  
1133          // Change char field from 30 to 20.
1134          $field = new xmldb_field('secondname');
1135          $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
1136          $dbman->change_field_precision($table, $field);
1137          $columns = $DB->get_columns('test_table1');
1138          $this->assertSame('C', $columns['secondname']->meta_type);
1139          // TODO: check the rest of attributes.
1140  
1141          // Change char field from 20 to 10, having contents > 10cc. Throw exception.
1142          $field = new xmldb_field('secondname');
1143          $field->set_attributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null);
1144          try {
1145              $dbman->change_field_precision($table, $field);
1146              $this->fail('Exception expected');
1147          } catch (moodle_exception $e) {
1148              $this->assertInstanceOf('ddl_change_structure_exception', $e);
1149          }
1150          // No changes in field specs at all.
1151          $columns = $DB->get_columns('test_table1');
1152          $this->assertSame('C', $columns['secondname']->meta_type);
1153          // TODO: check the rest of attributes.
1154  
1155          // Change number field from 20,10 to 10,2.
1156          $field = new xmldb_field('grade');
1157          $field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null);
1158          $dbman->change_field_precision($table, $field);
1159          $columns = $DB->get_columns('test_table1');
1160          $this->assertSame('N', $columns['grade']->meta_type);
1161          // TODO: check the rest of attributes.
1162  
1163          // Change integer field from 10 to 2.
1164          $field = new xmldb_field('userid');
1165          $field->set_attributes(XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
1166          $dbman->change_field_precision($table, $field);
1167          $columns = $DB->get_columns('test_table1');
1168          $this->assertSame('I', $columns['userid']->meta_type);
1169          // TODO: check the rest of attributes.
1170  
1171          // Change the column from integer (2) to integer (6) (forces change of type in some DBs).
1172          $field = new xmldb_field('userid');
1173          $field->set_attributes(XMLDB_TYPE_INTEGER, '6', null, null, null, null);
1174          $dbman->change_field_precision($table, $field);
1175          // Column is integer 6 null default null now.
1176          $columns = $DB->get_columns('test_table1');
1177          $this->assertSame('I', $columns['userid']->meta_type);
1178          // TODO: check the rest of attributes.
1179  
1180          // Insert one record with 6-digit field.
1181          $record = new stdClass();
1182          $record->course = 10;
1183          $record->secondname  = 'third record';
1184          $record->intro  = 'third record';
1185          $record->userid = 123456;
1186          $DB->insert_record('test_table1', $record);
1187          // Change integer field from 6 to 2, contents are bigger, must throw exception.
1188          $field = new xmldb_field('userid');
1189          $field->set_attributes(XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0');
1190          try {
1191              $dbman->change_field_precision($table, $field);
1192              $this->fail('Exception expected');
1193          } catch (moodle_exception $e) {
1194              $this->assertInstanceOf('ddl_change_structure_exception', $e);
1195          }
1196          // No changes in field specs at all.
1197          $columns = $DB->get_columns('test_table1');
1198          $this->assertSame('I', $columns['userid']->meta_type);
1199          // TODO: check the rest of attributes.
1200  
1201          // Change integer field from 10 to 3, in field used by index. must throw exception.
1202          $field = new xmldb_field('course');
1203          $field->set_attributes(XMLDB_TYPE_INTEGER, '3', null, XMLDB_NOTNULL, null, '0');
1204          try {
1205              $dbman->change_field_precision($table, $field);
1206              $this->fail('Exception expected');
1207          } catch (moodle_exception $e) {
1208              $this->assertInstanceOf('ddl_dependency_exception', $e);
1209          }
1210          // No changes in field specs at all.
1211          $columns = $DB->get_columns('test_table1');
1212          $this->assertSame('I', $columns['course']->meta_type);
1213          // TODO: check the rest of attributes.
1214      }
1215  
1216      public function testChangeFieldNullability() {
1217          $DB = $this->tdb; // Do not use global $DB!
1218          $dbman = $this->tdb->get_manager();
1219  
1220          $table = new xmldb_table('test_table_cust0');
1221          $table->add_field('id', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1222          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
1223          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1224          $dbman->create_table($table);
1225  
1226          $record = new stdClass();
1227          $record->name = null;
1228  
1229          try {
1230              $result = $DB->insert_record('test_table_cust0', $record, false);
1231          } catch (dml_exception $e) {
1232              $result = false;
1233          }
1234          $this->resetDebugging();
1235          $this->assertFalse($result);
1236  
1237          $field = new xmldb_field('name');
1238          $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, null, null, null);
1239          $dbman->change_field_notnull($table, $field);
1240  
1241          $this->assertTrue($DB->insert_record('test_table_cust0', $record, false));
1242  
1243          // TODO: add some tests with existing data in table.
1244          $DB->delete_records('test_table_cust0');
1245  
1246          $field = new xmldb_field('name');
1247          $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
1248          $dbman->change_field_notnull($table, $field);
1249  
1250          try {
1251              $result = $DB->insert_record('test_table_cust0', $record, false);
1252          } catch (dml_exception $e) {
1253              $result = false;
1254          }
1255          $this->resetDebugging();
1256          $this->assertFalse($result);
1257  
1258          $dbman->drop_table($table);
1259      }
1260  
1261      public function testChangeFieldDefault() {
1262          $DB = $this->tdb; // Do not use global $DB!
1263          $dbman = $this->tdb->get_manager();
1264  
1265          $table = new xmldb_table('test_table_cust0');
1266          $table->add_field('id', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1267          $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1268          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1269          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1270          $dbman->create_table($table);
1271  
1272          $field = new xmldb_field('name');
1273          $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle2');
1274          $dbman->change_field_default($table, $field);
1275  
1276          $record = new stdClass();
1277          $record->onenumber = 666;
1278          $id = $DB->insert_record('test_table_cust0', $record);
1279  
1280          $record = $DB->get_record('test_table_cust0', array('id'=>$id));
1281          $this->assertSame('Moodle2', $record->name);
1282  
1283          $field = new xmldb_field('onenumber');
1284          $field->set_attributes(XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 666);
1285          $dbman->change_field_default($table, $field);
1286  
1287          $record = new stdClass();
1288          $record->name = 'something';
1289          $id = $DB->insert_record('test_table_cust0', $record);
1290  
1291          $record = $DB->get_record('test_table_cust0', array('id'=>$id));
1292          $this->assertSame('666', $record->onenumber);
1293  
1294          $dbman->drop_table($table);
1295      }
1296  
1297      public function testAddUniqueIndex() {
1298          $DB = $this->tdb; // Do not use global $DB!
1299          $dbman = $this->tdb->get_manager();
1300  
1301          $table = new xmldb_table('test_table_cust0');
1302          $table->add_field('id', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1303          $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1304          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1305          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1306          $dbman->create_table($table);
1307  
1308          $record = new stdClass();
1309          $record->onenumber = 666;
1310          $record->name = 'something';
1311          $DB->insert_record('test_table_cust0', $record, false);
1312  
1313          $index = new xmldb_index('onenumber-name');
1314          $index->set_attributes(XMLDB_INDEX_UNIQUE, array('onenumber', 'name'));
1315          $dbman->add_index($table, $index);
1316  
1317          try {
1318              $result = $DB->insert_record('test_table_cust0', $record, false);
1319          } catch (dml_exception $e) {
1320              $result = false;
1321          }
1322          $this->resetDebugging();
1323          $this->assertFalse($result);
1324  
1325          $dbman->drop_table($table);
1326      }
1327  
1328      public function testAddNonUniqueIndex() {
1329          $dbman = $this->tdb->get_manager();
1330  
1331          $table = $this->create_deftable('test_table1');
1332          $index = new xmldb_index('secondname');
1333          $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1334          $dbman->add_index($table, $index);
1335          $this->assertTrue($dbman->index_exists($table, $index));
1336  
1337          try {
1338              $dbman->add_index($table, $index);
1339              $this->fail('Exception expected for duplicate indexes');
1340          } catch (moodle_exception $e) {
1341              $this->assertInstanceOf('ddl_exception', $e);
1342          }
1343  
1344          $index = new xmldb_index('third');
1345          $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course'));
1346          try {
1347              $dbman->add_index($table, $index);
1348              $this->fail('Exception expected for duplicate indexes');
1349          } catch (moodle_exception $e) {
1350              $this->assertInstanceOf('ddl_exception', $e);
1351          }
1352  
1353          $table = new xmldb_table('test_table_cust0');
1354          $table->add_field('id', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1355          $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1356          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1357          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1358          $table->add_key('onenumber', XMLDB_KEY_FOREIGN, array('onenumber'));
1359  
1360          try {
1361              $table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
1362              $this->fail('Coding exception expected');
1363          } catch (moodle_exception $e) {
1364              $this->assertInstanceOf('coding_exception', $e);
1365          }
1366  
1367          $table = new xmldb_table('test_table_cust0');
1368          $table->add_field('id', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1369          $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1370          $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1371          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1372          $table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
1373  
1374          try {
1375              $table->add_key('onenumber', XMLDB_KEY_FOREIGN, array('onenumber'));
1376              $this->fail('Coding exception expected');
1377          } catch (moodle_exception $e) {
1378              $this->assertInstanceOf('coding_exception', $e);
1379          }
1380  
1381      }
1382  
1383      public function testFindIndexName() {
1384          $dbman = $this->tdb->get_manager();
1385  
1386          $table = $this->create_deftable('test_table1');
1387          $index = new xmldb_index('secondname');
1388          $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1389          $dbman->add_index($table, $index);
1390  
1391          // DBM Systems name their indices differently - do not test the actual index name.
1392          $result = $dbman->find_index_name($table, $index);
1393          $this->assertTrue(!empty($result));
1394  
1395          $nonexistentindex = new xmldb_index('nonexistentindex');
1396          $nonexistentindex->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('name'));
1397          $this->assertFalse($dbman->find_index_name($table, $nonexistentindex));
1398      }
1399  
1400      public function testDropIndex() {
1401          $DB = $this->tdb; // Do not use global $DB!
1402  
1403          $dbman = $this->tdb->get_manager();
1404  
1405          $table = $this->create_deftable('test_table1');
1406          $index = new xmldb_index('secondname');
1407          $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1408          $dbman->add_index($table, $index);
1409  
1410          $dbman->drop_index($table, $index);
1411          $this->assertFalse($dbman->find_index_name($table, $index));
1412  
1413          // Test we are able to drop indexes having hyphens MDL-22804.
1414          // Create index with hyphens (by hand).
1415          $indexname = 'test-index-with-hyphens';
1416          switch ($DB->get_dbfamily()) {
1417              case 'mysql':
1418                  $indexname = '`' . $indexname . '`';
1419                  break;
1420              default:
1421                  $indexname = '"' . $indexname . '"';
1422          }
1423          $stmt = "CREATE INDEX {$indexname} ON {$DB->get_prefix()}test_table1 (course, name)";
1424          $DB->change_database_structure($stmt);
1425          $this->assertNotEmpty($dbman->find_index_name($table, $index));
1426          // Index created, let's drop it using db manager stuff.
1427          $index = new xmldb_index('indexname', XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1428          $dbman->drop_index($table, $index);
1429          $this->assertFalse($dbman->find_index_name($table, $index));
1430      }
1431  
1432      public function testAddUniqueKey() {
1433          $dbman = $this->tdb->get_manager();
1434  
1435          $table = $this->create_deftable('test_table1');
1436          $key = new xmldb_key('id-course-grade');
1437          $key->set_attributes(XMLDB_KEY_UNIQUE, array('id', 'course', 'grade'));
1438          $dbman->add_key($table, $key);
1439  
1440          // No easy way to test it, this just makes sure no errors are encountered.
1441          $this->assertTrue(true);
1442      }
1443  
1444      public function testAddForeignUniqueKey() {
1445          $dbman = $this->tdb->get_manager();
1446  
1447          $table = $this->create_deftable('test_table1');
1448          $this->create_deftable('test_table0');
1449  
1450          $key = new xmldb_key('course');
1451          $key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('id'));
1452          $dbman->add_key($table, $key);
1453  
1454          // No easy way to test it, this just makes sure no errors are encountered.
1455          $this->assertTrue(true);
1456      }
1457  
1458      public function testDropKey() {
1459          $dbman = $this->tdb->get_manager();
1460  
1461          $table = $this->create_deftable('test_table1');
1462          $this->create_deftable('test_table0');
1463  
1464          $key = new xmldb_key('course');
1465          $key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('id'));
1466          $dbman->add_key($table, $key);
1467  
1468          $dbman->drop_key($table, $key);
1469  
1470          // No easy way to test it, this just makes sure no errors are encountered.
1471          $this->assertTrue(true);
1472      }
1473  
1474      public function testAddForeignKey() {
1475          $dbman = $this->tdb->get_manager();
1476  
1477          $table = $this->create_deftable('test_table1');
1478          $this->create_deftable('test_table0');
1479  
1480          $key = new xmldb_key('course');
1481          $key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'test_table0', array('id'));
1482          $dbman->add_key($table, $key);
1483  
1484          // No easy way to test it, this just makes sure no errors are encountered.
1485          $this->assertTrue(true);
1486      }
1487  
1488      public function testDropForeignKey() {
1489          $dbman = $this->tdb->get_manager();
1490  
1491          $table = $this->create_deftable('test_table1');
1492          $this->create_deftable('test_table0');
1493  
1494          $key = new xmldb_key('course');
1495          $key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'test_table0', array('id'));
1496          $dbman->add_key($table, $key);
1497  
1498          $dbman->drop_key($table, $key);
1499  
1500          // No easy way to test it, this just makes sure no errors are encountered.
1501          $this->assertTrue(true);
1502      }
1503  
1504      public function testRenameField() {
1505          $DB = $this->tdb; // Do not use global $DB!
1506          $dbman = $this->tdb->get_manager();
1507  
1508          $table = $this->create_deftable('test_table0');
1509          $field = new xmldb_field('type');
1510          $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general', 'course');
1511  
1512          $dbman->rename_field($table, $field, 'newfieldname');
1513  
1514          $columns = $DB->get_columns('test_table0');
1515  
1516          $this->assertArrayNotHasKey('type', $columns);
1517          $this->assertArrayHasKey('newfieldname', $columns);
1518      }
1519  
1520      public function testIndexExists() {
1521          // Skipping: this is just a test of find_index_name.
1522      }
1523  
1524      public function testFindKeyName() {
1525          $dbman = $this->tdb->get_manager();
1526  
1527          $table = $this->create_deftable('test_table0');
1528          $key = $table->getKey('primary');
1529  
1530          // With Mysql, the return value is actually "mdl_test_id_pk".
1531          $result = $dbman->find_key_name($table, $key);
1532          $this->assertTrue(!empty($result));
1533      }
1534  
1535      public function testDeleteTablesFromXmldbFile() {
1536          $dbman = $this->tdb->get_manager();
1537  
1538          $this->create_deftable('test_table1');
1539  
1540          $this->assertTrue($dbman->table_exists('test_table1'));
1541  
1542          // Feed nonexistent file.
1543          try {
1544              $dbman->delete_tables_from_xmldb_file('fpsoiudfposui');
1545              $this->fail('Exception expected');
1546          } catch (moodle_exception $e) {
1547              $this->resetDebugging();
1548              $this->assertInstanceOf('moodle_exception', $e);
1549          }
1550  
1551          try {
1552              $dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
1553              $this->fail('Exception expected');
1554          } catch (moodle_exception $e) {
1555              $this->resetDebugging();
1556              $this->assertInstanceOf('moodle_exception', $e);
1557          }
1558  
1559          // Check that the table has not been deleted from DB.
1560          $this->assertTrue($dbman->table_exists('test_table1'));
1561  
1562          // Real and valid xml file.
1563          // TODO: drop UNSINGED completely in Moodle 2.4.
1564          $dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/xmldb_table.xml');
1565  
1566          // Check that the table has been deleted from DB.
1567          $this->assertFalse($dbman->table_exists('test_table1'));
1568      }
1569  
1570      public function testInstallFromXmldbFile() {
1571          $dbman = $this->tdb->get_manager();
1572  
1573          // Feed nonexistent file.
1574          try {
1575              $dbman->install_from_xmldb_file('fpsoiudfposui');
1576              $this->fail('Exception expected');
1577          } catch (moodle_exception $e) {
1578              $this->resetDebugging();
1579              $this->assertInstanceOf('moodle_exception', $e);
1580          }
1581  
1582          try {
1583              $dbman->install_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
1584              $this->fail('Exception expected');
1585          } catch (moodle_exception $e) {
1586              $this->resetDebugging();
1587              $this->assertInstanceOf('moodle_exception', $e);
1588          }
1589  
1590          // Check that the table has not yet been created in DB.
1591          $this->assertFalse($dbman->table_exists('test_table1'));
1592  
1593          // Real and valid xml file.
1594          $dbman->install_from_xmldb_file(__DIR__ . '/fixtures/xmldb_table.xml');
1595          $this->assertTrue($dbman->table_exists('test_table1'));
1596      }
1597  
1598      public function test_temp_tables() {
1599          $DB = $this->tdb; // Do not use global $DB!
1600          $dbman = $this->tdb->get_manager();
1601  
1602          // Create temp table0.
1603          $table0 = $this->tables['test_table0'];
1604          $dbman->create_temp_table($table0);
1605          $this->assertTrue($dbman->table_exists('test_table0'));
1606  
1607          // Try to create temp table with same name, must throw exception.
1608          $dupetable = $this->tables['test_table0'];
1609          try {
1610              $dbman->create_temp_table($dupetable);
1611              $this->fail('Exception expected');
1612          } catch (moodle_exception $e) {
1613              $this->assertInstanceOf('ddl_exception', $e);
1614          }
1615  
1616          // Try to create table with same name, must throw exception.
1617          $dupetable = $this->tables['test_table0'];
1618          try {
1619              $dbman->create_table($dupetable);
1620              $this->fail('Exception expected');
1621          } catch (moodle_exception $e) {
1622              $this->assertInstanceOf('ddl_exception', $e);
1623          }
1624  
1625          // Create another temp table1.
1626          $table1 = $this->tables['test_table1'];
1627          $dbman->create_temp_table($table1);
1628          $this->assertTrue($dbman->table_exists('test_table1'));
1629  
1630          // Get columns and perform some basic tests.
1631          $columns = $DB->get_columns('test_table1');
1632          $this->assertCount(11, $columns);
1633          $this->assertTrue($columns['name'] instanceof database_column_info);
1634          $this->assertEquals(30, $columns['name']->max_length);
1635          $this->assertTrue($columns['name']->has_default);
1636          $this->assertEquals('Moodle', $columns['name']->default_value);
1637  
1638          // Insert some records.
1639          $inserted = $this->fill_deftable('test_table1');
1640          $records = $DB->get_records('test_table1');
1641          $this->assertCount($inserted, $records);
1642          $this->assertSame($records[1]->course, $this->records['test_table1'][0]->course);
1643          $this->assertSame($records[1]->secondname, $this->records['test_table1'][0]->secondname);
1644          $this->assertSame($records[2]->intro, $this->records['test_table1'][1]->intro);
1645  
1646          // Collect statistics about the data in the temp table.
1647          $DB->update_temp_table_stats();
1648  
1649          // Drop table1.
1650          $dbman->drop_table($table1);
1651          $this->assertFalse($dbman->table_exists('test_table1'));
1652  
1653          // Try to drop non-existing temp table, must throw exception.
1654          $noetable = $this->tables['test_table1'];
1655          try {
1656              $dbman->drop_table($noetable);
1657              $this->fail('Exception expected');
1658          } catch (moodle_exception $e) {
1659              $this->assertInstanceOf('ddl_table_missing_exception', $e);
1660          }
1661  
1662          // Collect statistics about the data in the temp table with less tables.
1663          $DB->update_temp_table_stats();
1664  
1665          // Fill/modify/delete a few table0 records.
1666  
1667          // Drop table0.
1668          $dbman->drop_table($table0);
1669          $this->assertFalse($dbman->table_exists('test_table0'));
1670  
1671          // Create another temp table1.
1672          $table1 = $this->tables['test_table1'];
1673          $dbman->create_temp_table($table1);
1674          $this->assertTrue($dbman->table_exists('test_table1'));
1675  
1676          // Make sure it can be dropped using deprecated drop_temp_table().
1677          $dbman->drop_temp_table($table1);
1678          $this->assertFalse($dbman->table_exists('test_table1'));
1679          $this->assertDebuggingCalled();
1680  
1681          // Try join with normal tables - MS SQL may use incompatible collation.
1682          $table1 = new xmldb_table('test_table');
1683          $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1684          $table1->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1685          $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1686          $dbman->create_table($table1);
1687  
1688          $table2 = new xmldb_table('test_temp');
1689          $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1690          $table2->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1691          $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1692          $dbman->create_temp_table($table2);
1693  
1694          $record = array('name' => 'a');
1695          $DB->insert_record('test_table', $record);
1696          $DB->insert_record('test_temp', $record);
1697  
1698          $record = array('name' => 'b');
1699          $DB->insert_record('test_table', $record);
1700  
1701          $record = array('name' => 'c');
1702          $DB->insert_record('test_temp', $record);
1703  
1704          $sql = "SELECT *
1705                    FROM {test_table} n
1706                    JOIN {test_temp} t ON t.name = n.name";
1707          $records = $DB->get_records_sql($sql);
1708          $this->assertCount(1, $records);
1709  
1710          // Drop temp table.
1711          $dbman->drop_table($table2);
1712          $this->assertFalse($dbman->table_exists('test_temp'));
1713      }
1714  
1715      public function test_concurrent_temp_tables() {
1716          $DB = $this->tdb; // Do not use global $DB!
1717          $dbman = $this->tdb->get_manager();
1718  
1719          // Define 2 records.
1720          $record1 = (object)array(
1721              'course'     =>  1,
1722              'secondname' => '11 important',
1723              'intro'      => '111 important');
1724          $record2 = (object)array(
1725              'course'     =>  2,
1726              'secondname' => '22 important',
1727              'intro'      => '222 important');
1728  
1729          // Create temp table1 and insert 1 record (in DB).
1730          $table = $this->tables['test_table1'];
1731          $dbman->create_temp_table($table);
1732          $this->assertTrue($dbman->table_exists('test_table1'));
1733          $inserted = $DB->insert_record('test_table1', $record1);
1734  
1735          // Switch to new connection.
1736          $cfg = $DB->export_dbconfig();
1737          if (!isset($cfg->dboptions)) {
1738              $cfg->dboptions = array();
1739          }
1740          $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
1741          $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
1742          $dbman2 = $DB2->get_manager();
1743          $this->assertFalse($dbman2->table_exists('test_table1')); // Temp table not exists in DB2.
1744  
1745          // Create temp table1 and insert 1 record (in DB2).
1746          $table = $this->tables['test_table1'];
1747          $dbman2->create_temp_table($table);
1748          $this->assertTrue($dbman2->table_exists('test_table1'));
1749          $inserted = $DB2->insert_record('test_table1', $record2);
1750  
1751          $dbman2->drop_table($table); // Drop temp table before closing DB2.
1752          $this->assertFalse($dbman2->table_exists('test_table1'));
1753          $DB2->dispose(); // Close DB2.
1754  
1755          $this->assertTrue($dbman->table_exists('test_table1')); // Check table continues existing for DB.
1756          $dbman->drop_table($table); // Drop temp table.
1757          $this->assertFalse($dbman->table_exists('test_table1'));
1758      }
1759  
1760      public function test_reset_sequence() {
1761          $DB = $this->tdb;
1762          $dbman = $DB->get_manager();
1763  
1764          $table = new xmldb_table('testtable');
1765          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1766          $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1767          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1768  
1769          // Drop if exists.
1770          if ($dbman->table_exists($table)) {
1771              $dbman->drop_table($table);
1772          }
1773          $dbman->create_table($table);
1774          $tablename = $table->getName();
1775          $this->tables[$tablename] = $table;
1776  
1777          $record = (object)array('id'=>666, 'course'=>10);
1778          $DB->import_record('testtable', $record);
1779          $DB->delete_records('testtable'); // This delete performs one TRUNCATE.
1780  
1781          $dbman->reset_sequence($table); // Using xmldb object.
1782          $this->assertEquals(1, $DB->insert_record('testtable', (object)array('course'=>13)));
1783  
1784          $record = (object)array('id'=>666, 'course'=>10);
1785          $DB->import_record('testtable', $record);
1786          $DB->delete_records('testtable', array()); // This delete performs one DELETE.
1787  
1788          $dbman->reset_sequence($table); // Using xmldb object.
1789          $this->assertEquals(1, $DB->insert_record('testtable', (object)array('course'=>13)),
1790              'Some versions of MySQL 5.6.x are known to not support lowering of auto-increment numbers.');
1791  
1792          $DB->import_record('testtable', $record);
1793          $dbman->reset_sequence($tablename); // Using string.
1794          $this->assertEquals(667, $DB->insert_record('testtable', (object)array('course'=>13)));
1795  
1796          $dbman->drop_table($table);
1797      }
1798  
1799      public function test_reserved_words() {
1800          $reserved = sql_generator::getAllReservedWords();
1801          $this->assertTrue(count($reserved) > 1);
1802      }
1803  
1804      public function test_index_hints() {
1805          $DB = $this->tdb;
1806          $dbman = $DB->get_manager();
1807  
1808          $table = new xmldb_table('testtable');
1809          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1810          $table->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1811          $table->add_field('path', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1812          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1813          $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'), array('xxxx,yyyy'));
1814          $table->add_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
1815  
1816          // Drop if exists.
1817          if ($dbman->table_exists($table)) {
1818              $dbman->drop_table($table);
1819          }
1820          $dbman->create_table($table);
1821          $tablename = $table->getName();
1822          $this->tables[$tablename] = $table;
1823  
1824          $table = new xmldb_table('testtable');
1825          $index = new xmldb_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'), array('xxxx,yyyy'));
1826          $this->assertTrue($dbman->index_exists($table, $index));
1827  
1828          $table = new xmldb_table('testtable');
1829          $index = new xmldb_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
1830          $this->assertTrue($dbman->index_exists($table, $index));
1831  
1832          // Try unique indexes too.
1833          $dbman->drop_table($this->tables[$tablename]);
1834  
1835          $table = new xmldb_table('testtable');
1836          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1837          $table->add_field('path', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1838          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1839          $table->add_index('path', XMLDB_INDEX_UNIQUE, array('path'), array('varchar_pattern_ops'));
1840          $dbman->create_table($table);
1841          $this->tables[$tablename] = $table;
1842  
1843          $table = new xmldb_table('testtable');
1844          $index = new xmldb_index('path', XMLDB_INDEX_UNIQUE, array('path'), array('varchar_pattern_ops'));
1845          $this->assertTrue($dbman->index_exists($table, $index));
1846      }
1847  
1848      public function test_index_max_bytes() {
1849          $DB = $this->tdb;
1850          $dbman = $DB->get_manager();
1851  
1852          $maxstr = '';
1853          for ($i=0; $i<255; $i++) {
1854              $maxstr .= '言'; // Random long string that should fix exactly the limit for one char column.
1855          }
1856  
1857          $table = new xmldb_table('testtable');
1858          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1859          $table->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1860          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1861          $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
1862  
1863          // Drop if exists.
1864          if ($dbman->table_exists($table)) {
1865              $dbman->drop_table($table);
1866          }
1867          $dbman->create_table($table);
1868          $tablename = $table->getName();
1869          $this->tables[$tablename] = $table;
1870  
1871          $rec = new stdClass();
1872          $rec->name = $maxstr;
1873  
1874          $id = $DB->insert_record($tablename, $rec);
1875          $this->assertTrue(!empty($id));
1876  
1877          $rec = $DB->get_record($tablename, array('id'=>$id));
1878          $this->assertSame($maxstr, $rec->name);
1879  
1880          $dbman->drop_table($table);
1881  
1882          $table = new xmldb_table('testtable');
1883          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1884          $table->add_field('name', XMLDB_TYPE_CHAR, 255+1, null, XMLDB_NOTNULL, null);
1885          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1886          $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
1887  
1888          try {
1889              $dbman->create_table($table);
1890              $this->fail('Exception expected');
1891          } catch (moodle_exception $e) {
1892              $this->assertInstanceOf('coding_exception', $e);
1893          }
1894      }
1895  
1896      public function test_index_composed_max_bytes() {
1897          $DB = $this->tdb;
1898          $dbman = $DB->get_manager();
1899  
1900          $maxstr = '';
1901          for ($i=0; $i<200; $i++) {
1902              $maxstr .= '言';
1903          }
1904          $reststr = '';
1905          for ($i=0; $i<133; $i++) {
1906              $reststr .= '言';
1907          }
1908  
1909          $table = new xmldb_table('testtable');
1910          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1911          $table->add_field('name1', XMLDB_TYPE_CHAR, 200, null, XMLDB_NOTNULL, null);
1912          $table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
1913          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1914          $table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1', 'name2'));
1915  
1916          // Drop if exists.
1917          if ($dbman->table_exists($table)) {
1918              $dbman->drop_table($table);
1919          }
1920          $dbman->create_table($table);
1921          $tablename = $table->getName();
1922          $this->tables[$tablename] = $table;
1923  
1924          $rec = new stdClass();
1925          $rec->name1 = $maxstr;
1926          $rec->name2 = $reststr;
1927  
1928          $id = $DB->insert_record($tablename, $rec);
1929          $this->assertTrue(!empty($id));
1930  
1931          $rec = $DB->get_record($tablename, array('id'=>$id));
1932          $this->assertSame($maxstr, $rec->name1);
1933          $this->assertSame($reststr, $rec->name2);
1934  
1935          $table = new xmldb_table('testtable');
1936          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1937          $table->add_field('name1', XMLDB_TYPE_CHAR, 201, null, XMLDB_NOTNULL, null);
1938          $table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
1939          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1940          $table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1', 'name2'));
1941  
1942          // Drop if exists.
1943          if ($dbman->table_exists($table)) {
1944              $dbman->drop_table($table);
1945          }
1946  
1947          try {
1948              $dbman->create_table($table);
1949              $this->fail('Exception expected');
1950          } catch (moodle_exception $e) {
1951              $this->assertInstanceOf('coding_exception', $e);
1952          }
1953      }
1954  
1955      public function test_char_size_limit() {
1956          $DB = $this->tdb;
1957          $dbman = $DB->get_manager();
1958  
1959          $table = new xmldb_table('testtable');
1960          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1961          $table->add_field('name', XMLDB_TYPE_CHAR, xmldb_field::CHAR_MAX_LENGTH, null, XMLDB_NOTNULL, null);
1962          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1963  
1964          // Drop if exists.
1965          if ($dbman->table_exists($table)) {
1966              $dbman->drop_table($table);
1967          }
1968          $dbman->create_table($table);
1969          $tablename = $table->getName();
1970          $this->tables[$tablename] = $table;
1971  
1972          // This has to work in all DBs.
1973          $maxstr = '';
1974          for ($i=0; $i<xmldb_field::CHAR_MAX_LENGTH; $i++) {
1975              $maxstr .= 'a'; // Ascii only.
1976          }
1977  
1978          $rec = new stdClass();
1979          $rec->name = $maxstr;
1980  
1981          $id = $DB->insert_record($tablename, $rec);
1982          $this->assertTrue(!empty($id));
1983  
1984          $rec = $DB->get_record($tablename, array('id'=>$id));
1985          $this->assertSame($maxstr, $rec->name);
1986  
1987          // Following test is supposed to fail in oracle.
1988          $maxstr = '';
1989          for ($i=0; $i<xmldb_field::CHAR_MAX_LENGTH; $i++) {
1990              $maxstr .= '言'; // Random long string that should fix exactly the limit for one char column.
1991          }
1992  
1993          $rec = new stdClass();
1994          $rec->name = $maxstr;
1995  
1996          try {
1997              $id = $DB->insert_record($tablename, $rec);
1998              $this->assertTrue(!empty($id));
1999  
2000              $rec = $DB->get_record($tablename, array('id'=>$id));
2001              $this->assertSame($maxstr, $rec->name);
2002          } catch (dml_exception $e) {
2003              if ($DB->get_dbfamily() === 'oracle') {
2004                  $this->fail('Oracle does not support text fields larger than 4000 bytes, this is not a big problem for mostly ascii based languages');
2005              } else {
2006                  throw $e;
2007              }
2008          }
2009  
2010          $table = new xmldb_table('testtable');
2011          $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2012          $table->add_field('name', XMLDB_TYPE_CHAR, xmldb_field::CHAR_MAX_LENGTH+1, null, XMLDB_NOTNULL, null);
2013          $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2014  
2015          // Drop if exists.
2016          if ($dbman->table_exists($table)) {
2017              $dbman->drop_table($table);
2018          }
2019          $tablename = $table->getName();
2020          $this->tables[$tablename] = $table;
2021  
2022          try {
2023              $dbman->create_table($table);
2024              $this->fail('Exception expected');
2025          } catch (moodle_exception $e) {
2026              $this->assertInstanceOf('coding_exception', $e);
2027          }
2028      }
2029  
2030      public function test_object_name() {
2031          $gen = $this->tdb->get_manager()->generator;
2032  
2033          // This will form short object name and max length should not be exceeded.
2034          $table = 'tablename';
2035          $fields = 'id';
2036          $suffix = 'pk';
2037          for ($i=0; $i<12; $i++) {
2038              $this->assertLessThanOrEqual($gen->names_max_length,
2039                      strlen($gen->getNameForObject($table, $fields, $suffix)),
2040                      'Generated object name is too long. $i = '.$i);
2041          }
2042  
2043          // This will form too long object name always and it must be trimmed to exactly 30 chars.
2044          $table = 'aaaa_bbbb_cccc_dddd_eeee_ffff_gggg';
2045          $fields = 'aaaaa,bbbbb,ccccc,ddddd';
2046          $suffix = 'idx';
2047          for ($i=0; $i<12; $i++) {
2048              $this->assertEquals($gen->names_max_length,
2049                      strlen($gen->getNameForObject($table, $fields, $suffix)),
2050                      'Generated object name is too long. $i = '.$i);
2051          }
2052  
2053          // Same test without suffix.
2054          $table = 'bbbb_cccc_dddd_eeee_ffff_gggg_hhhh';
2055          $fields = 'aaaaa,bbbbb,ccccc,ddddd';
2056          $suffix = '';
2057          for ($i=0; $i<12; $i++) {
2058              $this->assertEquals($gen->names_max_length,
2059                      strlen($gen->getNameForObject($table, $fields, $suffix)),
2060                      'Generated object name is too long. $i = '.$i);
2061          }
2062  
2063          // This must only trim name when counter is 10 or more.
2064          $table = 'cccc_dddd_eeee_ffff_gggg_hhhh_iiii';
2065          $fields = 'id';
2066          $suffix = 'idx';
2067          // Since we don't know how long prefix is, loop to generate tablename that gives exactly maxlengh-1 length.
2068          // Skip this test if prefix is too long.
2069          while (strlen($table) && strlen($gen->prefix.preg_replace('/_/','',$table).'_id_'.$suffix) >= $gen->names_max_length) {
2070              $table = rtrim(substr($table, 0, strlen($table) - 1), '_');
2071          }
2072          if (strlen($table)) {
2073              $this->assertEquals($gen->names_max_length - 1,
2074                          strlen($gen->getNameForObject($table, $fields, $suffix)));
2075              for ($i=0; $i<12; $i++) {
2076                  $this->assertEquals($gen->names_max_length,
2077                          strlen($gen->getNameForObject($table, $fields, $suffix)),
2078                          'Generated object name is too long. $i = '.$i);
2079              }
2080          }
2081      }
2082  
2083      // Following methods are not supported == Do not test.
2084      /*
2085          public function testRenameIndex() {
2086              // Unsupported!
2087              $dbman = $this->tdb->get_manager();
2088  
2089              $table = $this->create_deftable('test_table0');
2090              $index = new xmldb_index('course');
2091              $index->set_attributes(XMLDB_INDEX_UNIQUE, array('course'));
2092  
2093              $this->assertTrue($dbman->rename_index($table, $index, 'newindexname'));
2094          }
2095  
2096          public function testRenameKey() {
2097              // Unsupported!
2098               $dbman = $this->tdb->get_manager();
2099  
2100              $table = $this->create_deftable('test_table0');
2101              $key = new xmldb_key('course');
2102              $key->set_attributes(XMLDB_KEY_UNIQUE, array('course'));
2103  
2104              $this->assertTrue($dbman->rename_key($table, $key, 'newkeyname'));
2105          }
2106      */
2107  
2108  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1