[ Index ]

PHP Cross Reference of Unnamed Project

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1