Merge branch 'w46_MDL-30147_m22_tablenotexists' of git://github.com/skodak/moodle
[moodle.git] / lib / ddl / simpletest / testddl.php
1 <?php
2 /**
3  * Unit tests for (some of) ddl lib.
4  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
5  */
7 if (!defined('MOODLE_INTERNAL')) {
8     die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
9 }
11 require_once($CFG->libdir . '/adminlib.php');
13 class ddl_test extends UnitTestCase {
14     private $tables = array();
15     private $records= array();
16     private $tdb;
17     public  static $includecoverage = array('lib/ddl');
18     public  static $excludecoverage = array('lib/ddl/simpletest');
20     public function setUp() {
21         global $CFG, $DB, $UNITTEST;
23         if (isset($UNITTEST->func_test_db)) {
24             $this->tdb = $UNITTEST->func_test_db;
25         } else {
26             $this->tdb = $DB;
27         }
29         unset($CFG->xmldbreconstructprevnext); // remove this unhack ;-)
31         $dbman = $this->tdb->get_manager();
33         $table = new xmldb_table('test_table0');
34         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
35         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
36         $table->add_field('type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general');
37         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null);
38         $table->add_field('intro', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null);
39         $table->add_field('logo', XMLDB_TYPE_BINARY, 'big', null, null, null);
40         $table->add_field('assessed', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
41         $table->add_field('assesstimestart', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
42         $table->add_field('assesstimefinish', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
43         $table->add_field('scale', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
44         $table->add_field('maxbytes', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
45         $table->add_field('forcesubscribe', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
46         $table->add_field('trackingtype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1');
47         $table->add_field('rsstype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
48         $table->add_field('rssarticles', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
49         $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
50         $table->add_field('grade', XMLDB_TYPE_NUMBER, '20,0', XMLDB_UNSIGNED, null, null, null);
51         $table->add_field('percent', XMLDB_TYPE_NUMBER, '5,2', null, null, null, 66.6);
52         $table->add_field('warnafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
53         $table->add_field('blockafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
54         $table->add_field('blockperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
55         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
56         $table->add_key('course', XMLDB_KEY_UNIQUE, array('course'));
57         $table->add_index('type-name', XMLDB_INDEX_UNIQUE, array('type', 'name'));
58         $table->add_index('rsstype', XMLDB_INDEX_NOTUNIQUE, array('rsstype'));
59         $table->setComment("This is a test'n drop table. You can drop it safely");
61         $this->tables[$table->getName()] = $table;
63         // Define 2 initial records for this table
64         $this->records[$table->getName()] = array(
65                 (object)array(
66                         'course' => '1',
67                         'type'   => 'general',
68                         'name'   => 'record',
69                         'intro'  => 'first record'),
70                 (object)array(
71                         'course' => '2',
72                         'type'   => 'social',
73                         'name'   => 'record',
74                         'intro'  => 'second record'));
76         // Second, smaller table
77         $table = new xmldb_table ('test_table1');
78         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
79         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
80         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, null, null, 'Moodle');
81         $table->add_field('secondname', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
82         $table->add_field('thirdname', XMLDB_TYPE_CHAR, '30', null, null, null, ''); // nullable column with empty default
83         $table->add_field('intro', XMLDB_TYPE_TEXT, 'medium', null, XMLDB_NOTNULL, null, null);
84         $table->add_field('avatar', XMLDB_TYPE_BINARY, 'medium', null, null, null, null);
85         $table->add_field('grade', XMLDB_TYPE_NUMBER, '20,10', null, null, null);
86         $table->add_field('gradefloat', XMLDB_TYPE_FLOAT, '20,0', XMLDB_UNSIGNED, null, null, null);
87         $table->add_field('percentfloat', XMLDB_TYPE_FLOAT, '5,2', null, null, null, 99.9);
88         $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
89         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
90         $table->add_key('course', XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('course'));
91         $table->setComment("This is a test'n drop table. You can drop it safely");
93         $this->tables[$table->getName()] = $table;
95         // Define 2 initial records for this table
96         $this->records[$table->getName()] = array(
97                 (object)array(
98                         'course' => '1',
99                         'secondname'   => 'first record', // > 10 cc, please don't modify. Some tests below depend of this
100                         'intro'  => 'first record'),
101                 (object)array(
102                         'course'       => '2',
103                         'secondname'   => 'second record', // > 10 cc, please don't modify. Some tests below depend of this
104                         'intro'  => 'second record'));
106         // make sure no tables are present!
107         $this->tearDown();
108     }
110     public function tearDown() {
111         $dbman = $this->tdb->get_manager();
113         // drop custom test tables
114         for ($i=0; $i<3; $i++) {
115             $table = new xmldb_table('test_table_cust'.$i);
116             if ($dbman->table_exists($table)) {
117                 $dbman->drop_table($table);
118             }
119         }
121         // drop default tables
122         foreach ($this->tables as $table) {
123             if ($dbman->table_exists($table)) {
124                 $dbman->drop_table($table);
125             }
126         }
127     }
129     private function create_deftable($tablename) {
130         $dbman = $this->tdb->get_manager();
132         if (!isset($this->tables[$tablename])) {
133             return null;
134         }
136         $table = $this->tables[$tablename];
138         if ($dbman->table_exists($table)) {
139             $dbman->drop_table($table);
140         }
141         $dbman->create_table($table);
143         return $table;
144     }
146     /**
147      * Fill the given test table with some records, as far as
148      * DDL behaviour must be tested both with real data and
149      * with empty tables
150      */
151     private function fill_deftable($tablename) {
152         $DB = $this->tdb; // do not use global $DB!
153         $dbman = $this->tdb->get_manager();
155         if (!isset($this->records[$tablename])) {
156             return null;
157         }
159         if ($dbman->table_exists($tablename)) {
160             foreach ($this->records[$tablename] as $row) {
161                 $DB->insert_record($tablename, $row);
162             }
163         } else {
164             return null;
165         }
167         return count($this->records[$tablename]);
168     }
170     /**
171      * Test behaviour of table_exists()
172      */
173     public function test_table_exists() {
174         $DB = $this->tdb; // do not use global $DB!
175         $dbman = $this->tdb->get_manager();
177         // first make sure it returns false if table does not exist
178         $table = $this->tables['test_table0'];
180         ob_start(); // hide debug warning
181         try {
182             $result = $DB->get_records('test_table0');
183         } catch (dml_exception $e) {
184             $result = false;
185         }
186         ob_end_clean();
188         $this->assertFalse($result);
190         $this->assertFalse($dbman->table_exists('test_table0')); // by name
191         $this->assertFalse($dbman->table_exists($table));        // by xmldb_table
193         // create table and test again
194         $dbman->create_table($table);
196         $this->assertTrue($DB->get_records('test_table0') === array());
197         $this->assertTrue($dbman->table_exists('test_table0')); // by name
198         $this->assertTrue($dbman->table_exists($table));        // by xmldb_table
200         // drop table and test again
201         $dbman->drop_table($table);
203         ob_start(); // hide debug warning
204         try {
205             $result = $DB->get_records('test_table0');
206         } catch (dml_exception $e) {
207             $result = false;
208         }
209         ob_end_clean();
211         $this->assertFalse($result);
213         $this->assertFalse($dbman->table_exists('test_table0')); // by name
214         $this->assertFalse($dbman->table_exists($table));        // by xmldb_table
215     }
217     /**
218      * Test behaviour of create_table()
219      */
220     public function test_create_table() {
221         $DB = $this->tdb; // do not use global $DB!
222         $dbman = $this->tdb->get_manager();
224         // create table
225         $table = $this->tables['test_table1'];
227         $dbman->create_table($table);
228         $this->assertTrue($dbman->table_exists($table));
230         // basic get_tables() test
231         $tables = $DB->get_tables();
232         $this->assertTrue(array_key_exists('test_table1', $tables));
234         // basic get_columns() tests
235         $columns = $DB->get_columns('test_table1');
236         $this->assertEqual($columns['id']->meta_type, 'R');
237         $this->assertEqual($columns['course']->meta_type, 'I');
238         $this->assertEqual($columns['name']->meta_type, 'C');
239         $this->assertEqual($columns['secondname']->meta_type, 'C');
240         $this->assertEqual($columns['thirdname']->meta_type, 'C');
241         $this->assertEqual($columns['intro']->meta_type, 'X');
242         $this->assertEqual($columns['avatar']->meta_type, 'B');
243         $this->assertEqual($columns['grade']->meta_type, 'N');
244         $this->assertEqual($columns['percentfloat']->meta_type, 'N');
245         $this->assertEqual($columns['userid']->meta_type, 'I');
246         // some defaults
247         $this->assertTrue($columns['course']->has_default);
248         $this->assertEqual($columns['course']->default_value, 0);
249         $this->assertTrue($columns['name']->has_default);
250         $this->assertEqual($columns['name']->default_value, 'Moodle');
251         $this->assertTrue($columns['secondname']->has_default);
252         $this->assertEqual($columns['secondname']->default_value, '');
253         $this->assertTrue($columns['thirdname']->has_default);
254         $this->assertEqual($columns['thirdname']->default_value, '');
255         $this->assertTrue($columns['percentfloat']->has_default);
256         $this->assertEqual($columns['percentfloat']->default_value, 99.9);
257         $this->assertTrue($columns['userid']->has_default);
258         $this->assertEqual($columns['userid']->default_value, 0);
260         // basic get_indexes() test
261         $indexes = $DB->get_indexes('test_table1');
262         $courseindex = reset($indexes);
263         $this->assertEqual($courseindex['unique'], 1);
264         $this->assertEqual($courseindex['columns'][0], 'course');
266         // check sequence returns 1 for first insert
267         $rec = (object)array(
268                 'course'     => 10,
269                 'secondname' => 'not important',
270                 'intro'      => 'not important');
271         $this->assertIdentical($DB->insert_record('test_table1', $rec), 1);
273         // check defined defaults are working ok
274         $dbrec = $DB->get_record('test_table1', array('id' => 1));
275         $this->assertEqual($dbrec->name, 'Moodle');
276         $this->assertEqual($dbrec->thirdname, '');
278         // check exceptions if multiple R columns
279         $table = new xmldb_table ('test_table2');
280         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
281         $table->add_field('rid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
282         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
283         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
284         $table->add_key('primaryx', XMLDB_KEY_PRIMARY, array('id'));
285         $table->setComment("This is a test'n drop table. You can drop it safely");
287         $this->tables[$table->getName()] = $table;
289         try {
290             $dbman->create_table($table);
291             $this->fail('Exception expected');
292         } catch (Exception $e) {
293             $this->assertTrue($e instanceof ddl_exception);
294         }
296         // check exceptions missing primary key on R column
297         $table = new xmldb_table ('test_table2');
298         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
299         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
300         $table->setComment("This is a test'n drop table. You can drop it safely");
302         $this->tables[$table->getName()] = $table;
304         try {
305             $dbman->create_table($table);
306             $this->fail('Exception expected');
307         } catch (Exception $e) {
308             $this->assertTrue($e instanceof ddl_exception);
309         }
311     }
313     /**
314      * Test behaviour of drop_table()
315      */
316     public function test_drop_table() {
317         $DB = $this->tdb; // do not use global $DB!
318         $dbman = $this->tdb->get_manager();
320         // initially table doesn't exist
321         $this->assertFalse($dbman->table_exists('test_table0'));
323         // create table with contents
324         $table = $this->create_deftable('test_table0');
325         $this->assertTrue($dbman->table_exists('test_table0'));
327         // fill the table with some records before dropping it
328         $this->fill_deftable('test_table0');
330         // drop by xmldb_table object
331         $dbman->drop_table($table);
332         $this->assertFalse($dbman->table_exists('test_table0'));
334         // basic get_tables() test
335         $tables = $DB->get_tables();
336         $this->assertFalse(array_key_exists('test_table0', $tables));
338         try { // columns cache must be empty, so sentence throw exception
339             $columns = $DB->get_columns('test_table0');
340         } catch (dml_exception $e) {
341             $columns = false;
342         }
343         $this->assertFalse($columns);
345         try { /// throw exception
346             $indexes = $DB->get_indexes('test_table0');
347         } catch (dml_exception $e) {
348             $indexes = false;
349         }
350         $this->assertFalse($indexes);
351     }
353     /**
354      * Test behaviour of rename_table()
355      */
356     public function test_rename_table() {
357         $DB = $this->tdb; // do not use global $DB!
358         $dbman = $this->tdb->get_manager();
360         $table = $this->create_deftable('test_table1');
362         // fill the table with some records before renaming it
363         $insertedrows = $this->fill_deftable('test_table1');
365         $this->assertFalse($dbman->table_exists('test_table_cust1'));
366         $dbman->rename_table($table, 'test_table_cust1');
367         $this->assertTrue($dbman->table_exists('test_table_cust1'));
369         // check sequence returns $insertedrows + 1 for this insert (after rename)
370         $rec = (object)array(
371                 'course'     => 20,
372                 'secondname' => 'not important',
373                 'intro'      => 'not important');
374         $this->assertIdentical($DB->insert_record('test_table_cust1', $rec), $insertedrows + 1);
375     }
377     /**
378      * Test behaviour of field_exists()
379      */
380     public function test_field_exists() {
381         $dbman = $this->tdb->get_manager();
383         $table = $this->create_deftable('test_table0');
385         // String params
386         // Give a nonexistent table as first param (throw exception)
387         try {
388             $dbman->field_exists('nonexistenttable', 'id');
389             $this->assertTrue(false);
390         } catch (Exception $e) {
391             $this->assertTrue($e instanceof moodle_exception);
392         }
394         // Give a nonexistent field as second param (return false)
395         $this->assertFalse($dbman->field_exists('test_table0', 'nonexistentfield'));
397         // Correct string params
398         $this->assertTrue($dbman->field_exists('test_table0', 'id'));
400         // Object params
401         $realfield = $table->getField('id');
403         // Give a nonexistent table as first param (throw exception)
404         $nonexistenttable = new xmldb_table('nonexistenttable');
405         try {
406             $dbman->field_exists($nonexistenttable, $realfield);
407             $this->assertTrue(false);
408         } catch (Exception $e) {
409             $this->assertTrue($e instanceof moodle_exception);
410         }
412         // Give a nonexistent field as second param (return false)
413         $nonexistentfield = new xmldb_field('nonexistentfield');
414         $this->assertFalse($dbman->field_exists($table, $nonexistentfield));
416         // Correct object params
417         $this->assertTrue($dbman->field_exists($table, $realfield));
419         // Mix string and object params
420         // Correct ones
421         $this->assertTrue($dbman->field_exists($table, 'id'));
422         $this->assertTrue($dbman->field_exists('test_table0', $realfield));
423         // Non existing tables (throw exception)
424         try {
425             $this->assertFalse($dbman->field_exists($nonexistenttable, 'id'));
426             $this->assertTrue(false);
427         } catch (Exception $e) {
428             $this->assertTrue($e instanceof moodle_exception);
429         }
430         try {
431             $this->assertFalse($dbman->field_exists('nonexistenttable', $realfield));
432             $this->assertTrue(false);
433         } catch (Exception $e) {
434             $this->assertTrue($e instanceof moodle_exception);
435         }
436         // Non existing fields (return false)
437         $this->assertFalse($dbman->field_exists($table, 'nonexistentfield'));
438         $this->assertFalse($dbman->field_exists('test_table0', $nonexistentfield));
439     }
441     /**
442      * Test behaviour of add_field()
443      */
444     public function test_add_field() {
445         $DB = $this->tdb; // do not use global $DB!
446         $dbman = $this->tdb->get_manager();
448         $table = $this->create_deftable('test_table1');
450         // fill the table with some records before adding fields
451         $this->fill_deftable('test_table1');
453         /// add one not null field without specifying default value (throws ddl_exception)
454         $field = new xmldb_field('onefield');
455         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
456         try {
457             $dbman->add_field($table, $field);
458             $this->assertTrue(false);
459         } catch (Exception $e) {
460             $this->assertTrue($e instanceof ddl_exception);
461         }
463         /// add one existing field (throws ddl_exception)
464         $field = new xmldb_field('course');
465         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 2);
466         try {
467             $dbman->add_field($table, $field);
468             $this->assertTrue(false);
469         } catch (Exception $e) {
470             $this->assertTrue($e instanceof ddl_exception);
471         }
473         // TODO: add one field with invalid type, must throw exception
474         // TODO: add one text field with default, must throw exception
475         // TODO: add one binary field with default, must throw exception
477         /// add one integer field and check it
478         $field = new xmldb_field('oneinteger');
479         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 2);
480         $dbman->add_field($table, $field);
481         $this->assertTrue($dbman->field_exists($table, 'oneinteger'));
482         $columns = $DB->get_columns('test_table1');
483         $this->assertEqual($columns['oneinteger']->name         ,'oneinteger');
484         $this->assertEqual($columns['oneinteger']->not_null     , true);
485         // max_length and scale cannot be checked under all DBs at all for integer fields
486         $this->assertEqual($columns['oneinteger']->primary_key  , false);
487         $this->assertEqual($columns['oneinteger']->binary       , false);
488         $this->assertEqual($columns['oneinteger']->has_default  , true);
489         $this->assertEqual($columns['oneinteger']->default_value, 2);
490         $this->assertEqual($columns['oneinteger']->meta_type    ,'I');
491         $this->assertEqual($DB->get_field('test_table1', 'oneinteger', array(), IGNORE_MULTIPLE), 2); //check default has been applied
493         /// add one numeric field and check it
494         $field = new xmldb_field('onenumber');
495         $field->set_attributes(XMLDB_TYPE_NUMBER, '6,3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 2.55);
496         $dbman->add_field($table, $field);
497         $this->assertTrue($dbman->field_exists($table, 'onenumber'));
498         $columns = $DB->get_columns('test_table1');
499         $this->assertEqual($columns['onenumber']->name         ,'onenumber');
500         $this->assertEqual($columns['onenumber']->max_length   , 6);
501         $this->assertEqual($columns['onenumber']->scale        , 3);
502         $this->assertEqual($columns['onenumber']->not_null     , true);
503         $this->assertEqual($columns['onenumber']->primary_key  , false);
504         $this->assertEqual($columns['onenumber']->binary       , false);
505         $this->assertEqual($columns['onenumber']->has_default  , true);
506         $this->assertEqual($columns['onenumber']->default_value, 2.550);
507         $this->assertEqual($columns['onenumber']->meta_type    ,'N');
508         $this->assertEqual($DB->get_field('test_table1', 'onenumber', array(), IGNORE_MULTIPLE), 2.550); //check default has been applied
510         /// add one float field and check it (not official type - must work as number)
511         $field = new xmldb_field('onefloat');
512         $field->set_attributes(XMLDB_TYPE_FLOAT, '6,3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 3.550);
513         $dbman->add_field($table, $field);
514         $this->assertTrue($dbman->field_exists($table, 'onefloat'));
515         $columns = $DB->get_columns('test_table1');
516         $this->assertEqual($columns['onefloat']->name         ,'onefloat');
517         $this->assertEqual($columns['onefloat']->not_null     , true);
518         // max_length and scale cannot be checked under all DBs at all for float fields
519         $this->assertEqual($columns['onefloat']->primary_key  , false);
520         $this->assertEqual($columns['onefloat']->binary       , false);
521         $this->assertEqual($columns['onefloat']->has_default  , true);
522         $this->assertEqual($columns['onefloat']->default_value, 3.550);
523         $this->assertEqual($columns['onefloat']->meta_type    ,'N');
524         // Just rounding DB information to 7 decimal digits. Fair enough to test 3.550 and avoids one nasty bug
525         // in MSSQL core returning wrong floats (http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/5e08de63-16bb-4f24-b645-0cf8fc669de3)
526         // In any case, floats aren't officially supported by Moodle, with number/decimal type being the correct ones, so
527         // this isn't a real problem at all.
528         $this->assertEqual(round($DB->get_field('test_table1', 'onefloat', array(), IGNORE_MULTIPLE), 7), 3.550); //check default has been applied
530         /// add one char field and check it
531         $field = new xmldb_field('onechar');
532         $field->set_attributes(XMLDB_TYPE_CHAR, '25', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'Nice dflt!');
533         $dbman->add_field($table, $field);
534         $this->assertTrue($dbman->field_exists($table, 'onechar'));
535         $columns = $DB->get_columns('test_table1');
536         $this->assertEqual($columns['onechar']->name         ,'onechar');
537         $this->assertEqual($columns['onechar']->max_length   , 25);
538         $this->assertEqual($columns['onechar']->scale        , null);
539         $this->assertEqual($columns['onechar']->not_null     , true);
540         $this->assertEqual($columns['onechar']->primary_key  , false);
541         $this->assertEqual($columns['onechar']->binary       , false);
542         $this->assertEqual($columns['onechar']->has_default  , true);
543         $this->assertEqual($columns['onechar']->default_value,'Nice dflt!');
544         $this->assertEqual($columns['onechar']->meta_type    ,'C');
545         $this->assertEqual($DB->get_field('test_table1', 'onechar', array(), IGNORE_MULTIPLE), 'Nice dflt!'); //check default has been applied
547         /// add one text field and check it
548         $field = new xmldb_field('onetext');
549         $field->set_attributes(XMLDB_TYPE_TEXT);
550         $dbman->add_field($table, $field);
551         $this->assertTrue($dbman->field_exists($table, 'onetext'));
552         $columns = $DB->get_columns('test_table1');
553         $this->assertEqual($columns['onetext']->name         ,'onetext');
554         $this->assertEqual($columns['onetext']->max_length   , -1);
555         $this->assertEqual($columns['onetext']->scale        , null);
556         $this->assertEqual($columns['onetext']->not_null     , false);
557         $this->assertEqual($columns['onetext']->primary_key  , false);
558         $this->assertEqual($columns['onetext']->binary       , false);
559         $this->assertEqual($columns['onetext']->has_default  , false);
560         $this->assertEqual($columns['onetext']->default_value, null);
561         $this->assertEqual($columns['onetext']->meta_type    ,'X');
563         /// add one binary field and check it
564         $field = new xmldb_field('onebinary');
565         $field->set_attributes(XMLDB_TYPE_BINARY);
566         $dbman->add_field($table, $field);
567         $this->assertTrue($dbman->field_exists($table, 'onebinary'));
568         $columns = $DB->get_columns('test_table1');
569         $this->assertEqual($columns['onebinary']->name         ,'onebinary');
570         $this->assertEqual($columns['onebinary']->max_length   , -1);
571         $this->assertEqual($columns['onebinary']->scale        , null);
572         $this->assertEqual($columns['onebinary']->not_null     , false);
573         $this->assertEqual($columns['onebinary']->primary_key  , false);
574         $this->assertEqual($columns['onebinary']->binary       , true);
575         $this->assertEqual($columns['onebinary']->has_default  , false);
576         $this->assertEqual($columns['onebinary']->default_value, null);
577         $this->assertEqual($columns['onebinary']->meta_type    ,'B');
579         // TODO: check datetime type. Although unused should be fully supported.
580     }
582     /**
583      * Test behaviour of drop_field()
584      */
585     public function test_drop_field() {
586         $DB = $this->tdb; // do not use global $DB!
587         $dbman = $this->tdb->get_manager();
589         $table = $this->create_deftable('test_table0');
591         // fill the table with some records before dropping fields
592         $this->fill_deftable('test_table0');
594         // drop field with simple xmldb_field having indexes, must return exception
595         $field = new xmldb_field('type'); // Field has indexes and default clause
596         $this->assertTrue($dbman->field_exists($table, 'type'));
597         try {
598             $dbman->drop_field($table, $field);
599             $this->assertTrue(false);
600         } catch (Exception $e) {
601             $this->assertTrue($e instanceof ddl_dependency_exception);
602         }
603         $this->assertTrue($dbman->field_exists($table, 'type')); // continues existing, drop aborted
605         // drop field with complete xmldb_field object and related indexes, must return exception
606         $field = $table->getField('course'); // Field has indexes and default clause
607         $this->assertTrue($dbman->field_exists($table, $field));
608         try {
609             $dbman->drop_field($table, $field);
610             $this->assertTrue(false);
611         } catch (Exception $e) {
612             $this->assertTrue($e instanceof ddl_dependency_exception);
613         }
614         $this->assertTrue($dbman->field_exists($table, $field)); // continues existing, drop aborted
616         // drop one non-existing field, must return exception
617         $field = new xmldb_field('nonexistingfield');
618         $this->assertFalse($dbman->field_exists($table, $field));
619         try {
620             $dbman->drop_field($table, $field);
621             $this->assertTrue(false);
622         } catch (Exception $e) {
623             $this->assertTrue($e instanceof ddl_field_missing_exception);
624         }
626         // drop field with simple xmldb_field, not having related indexes
627         $field = new xmldb_field('forcesubscribe'); // Field has default clause
628         $this->assertTrue($dbman->field_exists($table, 'forcesubscribe'));
629         $dbman->drop_field($table, $field);
630         $this->assertFalse($dbman->field_exists($table, 'forcesubscribe'));
633         // drop field with complete xmldb_field object, not having related indexes
634         $field = new xmldb_field('trackingtype'); // Field has default clause
635         $this->assertTrue($dbman->field_exists($table, $field));
636         $dbman->drop_field($table, $field);
637         $this->assertFalse($dbman->field_exists($table, $field));
638     }
640     /**
641      * Test behaviour of change_field_type()
642      */
643     public function test_change_field_type() {
644         $DB = $this->tdb; // do not use global $DB!
645         $dbman = $this->tdb->get_manager();
647         // create table with indexed field and not indexed field to
648         // perform tests in both fields, both having defaults
649         $table = new xmldb_table('test_table_cust0');
650         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
651         $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
652         $table->add_field('anothernumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '4');
653         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
654         $table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
655         $dbman->create_table($table);
657         $record = new stdClass();
658         $record->onenumber = 2;
659         $record->anothernumber = 4;
660         $recoriginal = $DB->insert_record('test_table_cust0', $record);
662         // change column from integer to varchar. Must return exception because of dependent index
663         $field = new xmldb_field('onenumber');
664         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'test');
665         try {
666             $dbman->change_field_type($table, $field);
667             $this->assertTrue(false);
668         } catch (Exception $e) {
669             $this->assertTrue($e instanceof ddl_dependency_exception);
670         }
671         // column continues being integer 10 not null default 2
672         $columns = $DB->get_columns('test_table_cust0');
673         $this->assertEqual($columns['onenumber']->meta_type, 'I');
674         //TODO: check the rest of attributes
676         // change column from integer to varchar. Must work because column has no dependencies
677         $field = new xmldb_field('anothernumber');
678         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'test');
679         $dbman->change_field_type($table, $field);
680         // column is char 30 not null default 'test' now
681         $columns = $DB->get_columns('test_table_cust0');
682         $this->assertEqual($columns['anothernumber']->meta_type, 'C');
683         //TODO: check the rest of attributes
685         // change column back from char to integer
686         $field = new xmldb_field('anothernumber');
687         $field->set_attributes(XMLDB_TYPE_INTEGER, '8', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '5');
688         $dbman->change_field_type($table, $field);
689         // column is integer 8 not null default 5 now
690         $columns = $DB->get_columns('test_table_cust0');
691         $this->assertEqual($columns['anothernumber']->meta_type, 'I');
692         //TODO: check the rest of attributes
694         // change column once more from integer to char
695         $field = new xmldb_field('anothernumber');
696         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, "test'n drop");
697         $dbman->change_field_type($table, $field);
698         // column is char 30 not null default "test'n drop" now
699         $columns = $DB->get_columns('test_table_cust0');
700         $this->assertEqual($columns['anothernumber']->meta_type, 'C');
701         //TODO: check the rest of attributes
703         // insert one string value and try to convert to integer. Must throw exception
704         $record = new stdClass();
705         $record->onenumber = 7;
706         $record->anothernumber = 'string value';
707         $rectodrop = $DB->insert_record('test_table_cust0', $record);
708         $field = new xmldb_field('anothernumber');
709         $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '5');
710         try {
711             $dbman->change_field_type($table, $field);
712             $this->assertTrue(false);
713         } catch (Exception $e) {
714             $this->assertTrue($e instanceof ddl_change_structure_exception);
715         }
716         // column continues being char 30 not null default "test'n drop" now
717         $this->assertEqual($columns['anothernumber']->meta_type, 'C');
718         //TODO: check the rest of attributes
719         $DB->delete_records('test_table_cust0', array('id' => $rectodrop)); // Delete the string record
721         // change the column from varchar to float
722         $field = new xmldb_field('anothernumber');
723         $field->set_attributes(XMLDB_TYPE_FLOAT, '20,10', XMLDB_UNSIGNED, null, null, null);
724         $dbman->change_field_type($table, $field);
725         // column is float 20,10 null default null
726         $columns = $DB->get_columns('test_table_cust0');
727         $this->assertEqual($columns['anothernumber']->meta_type, 'N'); // floats are seen as number
728         //TODO: check the rest of attributes
730         // change the column back from float to varchar
731         $field = new xmldb_field('anothernumber');
732         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'test');
733         $dbman->change_field_type($table, $field);
734         // column is char 20 not null default "test" now
735         $columns = $DB->get_columns('test_table_cust0');
736         $this->assertEqual($columns['anothernumber']->meta_type, 'C');
737         //TODO: check the rest of attributes
739         // change the column from varchar to number
740         $field = new xmldb_field('anothernumber');
741         $field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', XMLDB_UNSIGNED, null, null, null);
742         $dbman->change_field_type($table, $field);
743         // column is number 20,10 null default null now
744         $columns = $DB->get_columns('test_table_cust0');
745         $this->assertEqual($columns['anothernumber']->meta_type, 'N');
746         //TODO: check the rest of attributes
748         // change the column from number to integer
749         $field = new xmldb_field('anothernumber');
750         $field->set_attributes(XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, null, null, null);
751         $dbman->change_field_type($table, $field);
752         // column is integer 2 null default null now
753         $columns = $DB->get_columns('test_table_cust0');
754         $this->assertEqual($columns['anothernumber']->meta_type, 'I');
755         //TODO: check the rest of attributes
757         // change the column from integer to text
758         $field = new xmldb_field('anothernumber');
759         $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
760         $dbman->change_field_type($table, $field);
761         // column is char text not null default null
762         $columns = $DB->get_columns('test_table_cust0');
763         $this->assertEqual($columns['anothernumber']->meta_type, 'X');
765         // change the column back from text to number
766         $field = new xmldb_field('anothernumber');
767         $field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', XMLDB_UNSIGNED, null, null, null);
768         $dbman->change_field_type($table, $field);
769         // column is number 20,10 null default null now
770         $columns = $DB->get_columns('test_table_cust0');
771         $this->assertEqual($columns['anothernumber']->meta_type, 'N');
772         //TODO: check the rest of attributes
774         // change the column from number to text
775         $field = new xmldb_field('anothernumber');
776         $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
777         $dbman->change_field_type($table, $field);
778         // column is char text not null default "test" now
779         $columns = $DB->get_columns('test_table_cust0');
780         $this->assertEqual($columns['anothernumber']->meta_type, 'X');
781         //TODO: check the rest of attributes
783         // change the column back from text to integer
784         $field = new xmldb_field('anothernumber');
785         $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 10);
786         $dbman->change_field_type($table, $field);
787         // column is integer 10 not null default 10
788         $columns = $DB->get_columns('test_table_cust0');
789         $this->assertEqual($columns['anothernumber']->meta_type, 'I');
790         //TODO: check the rest of attributes
792         // check original value has survived to all the type changes
793         $this->assertTrue($rec = $DB->get_record('test_table_cust0', array('id' => $recoriginal)));
794         $this->assertEqual($rec->anothernumber, 4);
796         $dbman->drop_table($table);
797         $this->assertFalse($dbman->table_exists($table));
798     }
800     /**
801      * Test behaviour of test_change_field_precision()
802      */
803     public function test_change_field_precision() {
804         $DB = $this->tdb; // do not use global $DB!
805         $dbman = $this->tdb->get_manager();
807         $table = $this->create_deftable('test_table1');
809         // fill the table with some records before dropping fields
810         $this->fill_deftable('test_table1');
812         // change text field from medium to big
813         $field = new xmldb_field('intro');
814         $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
815         $dbman->change_field_precision($table, $field);
816         $columns = $DB->get_columns('test_table1');
817         // cannot check the text type, only the metatype
818         $this->assertEqual($columns['intro']->meta_type, 'X');
819         //TODO: check the rest of attributes
821         // change char field from 30 to 20
822         $field = new xmldb_field('secondname');
823         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
824         $dbman->change_field_precision($table, $field);
825         $columns = $DB->get_columns('test_table1');
826         $this->assertEqual($columns['secondname']->meta_type, 'C');
827         //TODO: check the rest of attributes
829         // change char field from 20 to 10, having contents > 10cc. Throw exception
830         $field = new xmldb_field('secondname');
831         $field->set_attributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null);
832         try {
833             $dbman->change_field_precision($table, $field);
834             $this->assertTrue(false);
835         } catch (Exception $e) {
836             $this->assertTrue($e instanceof ddl_change_structure_exception);
837         }
838         // No changes in field specs at all
839         $columns = $DB->get_columns('test_table1');
840         $this->assertEqual($columns['secondname']->meta_type, 'C');
841         //TODO: check the rest of attributes
843         // change number field from 20,10 to 10,2
844         $field = new xmldb_field('grade');
845         $field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null);
846         $dbman->change_field_precision($table, $field);
847         $columns = $DB->get_columns('test_table1');
848         $this->assertEqual($columns['grade']->meta_type, 'N');
849         //TODO: check the rest of attributes
851         // change integer field from 10 to 2
852         $field = new xmldb_field('userid');
853         $field->set_attributes(XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
854         $dbman->change_field_precision($table, $field);
855         $columns = $DB->get_columns('test_table1');
856         $this->assertEqual($columns['userid']->meta_type, 'I');
857         //TODO: check the rest of attributes
859         // change the column from integer (2) to integer (6) (forces change of type in some DBs)
860         $field = new xmldb_field('userid');
861         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, null, null, null);
862         $dbman->change_field_precision($table, $field);
863         // column is integer 6 null default null now
864         $columns = $DB->get_columns('test_table1');
865         $this->assertEqual($columns['userid']->meta_type, 'I');
866         //TODO: check the rest of attributes
868         // insert one record with 6-digit field
869         $record = new stdClass();
870         $record->course = 10;
871         $record->secondname  = 'third record';
872         $record->intro  = 'third record';
873         $record->userid = 123456;
874         $DB->insert_record('test_table1', $record);
875         // change integer field from 6 to 2, contents are bigger. must throw exception
876         $field = new xmldb_field('userid');
877         $field->set_attributes(XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
878         try {
879             $dbman->change_field_precision($table, $field);
880             $this->assertTrue(false);
881         } catch (Exception $e) {
882             $this->assertTrue($e instanceof ddl_change_structure_exception);
883         }
884         // No changes in field specs at all
885         $columns = $DB->get_columns('test_table1');
886         $this->assertEqual($columns['userid']->meta_type, 'I');
887         //TODO: check the rest of attributes
889         // change integer field from 10 to 3, in field used by index. must throw exception.
890         $field = new xmldb_field('course');
891         $field->set_attributes(XMLDB_TYPE_INTEGER, '3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
892         try {
893             $dbman->change_field_precision($table, $field);
894             $this->assertTrue(false);
895         } catch (Exception $e) {
896             $this->assertTrue($e instanceof ddl_dependency_exception);
897         }
898         // No changes in field specs at all
899         $columns = $DB->get_columns('test_table1');
900         $this->assertEqual($columns['course']->meta_type, 'I');
901         //TODO: check the rest of attributes
902     }
904     public function testChangeFieldSign() {
905         $dbman = $this->tdb->get_manager();
906 // TODO: verify the signed is changed in db
908         $table = $this->create_deftable('test_table1');
909         $field = new xmldb_field('grade');
910         $field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', XMLDB_UNSIGNED, null, null, null);
911         $dbman->change_field_unsigned($table, $field);
913         $field = new xmldb_field('grade');
914         $field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null);
915         $dbman->change_field_unsigned($table, $field);
916     }
918     public function testChangeFieldNullability() {
919         $DB = $this->tdb; // do not use global $DB!
920         $dbman = $this->tdb->get_manager();
922         $table = new xmldb_table('test_table_cust0');
923         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
924         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
925         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
926         $dbman->create_table($table);
928         $record = new stdClass();
929         $record->name = NULL;
931         ob_start(); // hide debug warning
932         try {
933             $result = $DB->insert_record('test_table_cust0', $record, false);
934         } catch (dml_exception $e) {
935             $result = false;
936         }
937         ob_end_clean();
938         $this->assertFalse($result);
940         $field = new xmldb_field('name');
941         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, null, null, null);
942         $dbman->change_field_notnull($table, $field);
944         $this->assertTrue($DB->insert_record('test_table_cust0', $record, false));
946     // TODO: add some tests with existing data in table
947         $DB->delete_records('test_table_cust0');
949         $field = new xmldb_field('name');
950         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
951         $dbman->change_field_notnull($table, $field);
953         ob_start(); // hide debug warning
954         try {
955             $result = $DB->insert_record('test_table_cust0', $record, false);
956         } catch (dml_exception $e) {
957             $result = false;
958         }
959         ob_end_clean();
960         $this->assertFalse($result);
962         $dbman->drop_table($table);
963     }
965     public function testChangeFieldDefault() {
966         $DB = $this->tdb; // do not use global $DB!
967         $dbman = $this->tdb->get_manager();
969         $table = new xmldb_table('test_table_cust0');
970         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
971         $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
972         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
973         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
974         $dbman->create_table($table);
976         $field = new xmldb_field('name');
977         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle2');
978         $dbman->change_field_default($table, $field);
980         $record = new stdClass();
981         $record->onenumber = 666;
982         $id = $DB->insert_record('test_table_cust0', $record);
984         $record = $DB->get_record('test_table_cust0', array('id'=>$id));
985         $this->assertEqual($record->name, 'Moodle2');
988         $field = new xmldb_field('onenumber');
989         $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 666);
990         $dbman->change_field_default($table, $field);
992         $record = new stdClass();
993         $record->name = 'something';
994         $id = $DB->insert_record('test_table_cust0', $record);
996         $record = $DB->get_record('test_table_cust0', array('id'=>$id));
997         $this->assertEqual($record->onenumber, '666');
999         $dbman->drop_table($table);
1000     }
1002     public function testAddUniqueIndex() {
1003         $DB = $this->tdb; // do not use global $DB!
1004         $dbman = $this->tdb->get_manager();
1006         $table = new xmldb_table('test_table_cust0');
1007         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1008         $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1009         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1010         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1011         $dbman->create_table($table);
1013         $record = new stdClass();
1014         $record->onenumber = 666;
1015         $record->name = 'something';
1016         $DB->insert_record('test_table_cust0', $record, false);
1018         $index = new xmldb_index('onenumber-name');
1019         $index->set_attributes(XMLDB_INDEX_UNIQUE, array('onenumber', 'name'));
1020         $dbman->add_index($table, $index);
1022         ob_start(); // hide debug warning
1023         try {
1024             $result = $DB->insert_record('test_table_cust0', $record, false);
1025         } catch (dml_exception $e) {
1026             $result = false;;
1027         }
1028         ob_end_clean();
1029         $this->assertFalse($result);
1031         $dbman->drop_table($table);
1032     }
1034     public function testAddNonUniqueIndex() {
1035         $dbman = $this->tdb->get_manager();
1037         $table = $this->create_deftable('test_table1');
1038         $index = new xmldb_index('secondname');
1039         $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1040         $dbman->add_index($table, $index);
1041     }
1043     public function testFindIndexName() {
1044         $dbman = $this->tdb->get_manager();
1046         $table = $this->create_deftable('test_table1');
1047         $index = new xmldb_index('secondname');
1048         $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1049         $dbman->add_index($table, $index);
1051         //DBM Systems name their indices differently - do not test the actual index name
1052         $result = $dbman->find_index_name($table, $index);
1053         $this->assertTrue(!empty($result));
1055         $nonexistentindex = new xmldb_index('nonexistentindex');
1056         $nonexistentindex->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('name'));
1057         $this->assertFalse($dbman->find_index_name($table, $nonexistentindex));
1058     }
1060     public function testDropIndex() {
1061         $DB = $this->tdb; // do not use global $DB!
1063         $dbman = $this->tdb->get_manager();
1065         $table = $this->create_deftable('test_table1');
1066         $index = new xmldb_index('secondname');
1067         $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1068         $dbman->add_index($table, $index);
1070         $dbman->drop_index($table, $index);
1071         $this->assertFalse($dbman->find_index_name($table, $index));
1073         // Test we are able to drop indexes having hyphens. MDL-22804
1074         // Create index with hyphens (by hand)
1075         $indexname = 'test-index-with-hyphens';
1076         switch ($DB->get_dbfamily()) {
1077             case 'mysql':
1078                 $indexname = '`' . $indexname . '`';
1079                 break;
1080             default:
1081                 $indexname = '"' . $indexname . '"';
1082         }
1083         $stmt = "CREATE INDEX {$indexname} ON {$DB->get_prefix()}test_table1 (course, name)";
1084         $DB->change_database_structure($stmt);
1085         $this->assertTrue($dbman->find_index_name($table, $index));
1086         // Index created, let's drop it using db manager stuff
1087         $index = new xmldb_index('indexname', XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1088         $dbman->drop_index($table, $index);
1089         $this->assertFalse($dbman->find_index_name($table, $index));
1090     }
1092     public function testAddUniqueKey() {
1093         $dbman = $this->tdb->get_manager();
1095         $table = $this->create_deftable('test_table1');
1096         $key = new xmldb_key('id-course-grade');
1097         $key->set_attributes(XMLDB_KEY_UNIQUE, array('id', 'course', 'grade'));
1098         $dbman->add_key($table, $key);
1099     }
1101     public function testAddForeignUniqueKey() {
1102         $dbman = $this->tdb->get_manager();
1104         $table = $this->create_deftable('test_table1');
1105         $this->create_deftable('test_table0');
1107         $key = new xmldb_key('course');
1108         $key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('id'));
1109         $dbman->add_key($table, $key);
1110     }
1112     public function testDropKey() {
1113         $dbman = $this->tdb->get_manager();
1115         $table = $this->create_deftable('test_table1');
1116         $this->create_deftable('test_table0');
1118         $key = new xmldb_key('course');
1119         $key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('id'));
1120         $dbman->add_key($table, $key);
1122         $dbman->drop_key($table, $key);
1123     }
1125     public function testAddForeignKey() {
1126         $dbman = $this->tdb->get_manager();
1128         $table = $this->create_deftable('test_table1');
1129         $this->create_deftable('test_table0');
1131         $key = new xmldb_key('course');
1132         $key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'test_table0', array('id'));
1133         $dbman->add_key($table, $key);
1134     }
1136     public function testDropForeignKey() {
1137         $dbman = $this->tdb->get_manager();
1139         $table = $this->create_deftable('test_table1');
1140         $this->create_deftable('test_table0');
1142         $key = new xmldb_key('course');
1143         $key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'test_table0', array('id'));
1144         $dbman->add_key($table, $key);
1146         $dbman->drop_key($table, $key);
1147     }
1149     /**
1150      * Test behaviour of drop_enum_from_field() and related functions (find_check_constraint_name
1151      * and check_constraint_exists). Needed to be able to drop existing "enum" fields in the upgrade
1152      * from 1.9 to 2.0, will be completely deleted for Moodle 2.1
1153      *
1154      * Because we already have dropped support for creation of enum fields in 2.0, we are going to
1155      * create them here "manually" (hardcoded DB-dependent SQL). Just to be able to test the
1156      * find and drop functions properly.
1157      *
1158      * TODO: Drop this tests completely from Moodle 2.1
1159      */
1160     public function test_drop_enum_from_field() {
1161         $DB = $this->tdb; // do not use global $DB!
1162         $dbman = $this->tdb->get_manager();
1164         // Create normal table, no enums.
1165         $table = new xmldb_table('test_table_cust0');
1166         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1167         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1168         $field = new xmldb_field('type');
1169         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general');
1170         $table->addField($field);
1171         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1172         $dbman->create_table($table);
1174         $this->assertTrue($dbman->table_exists($table));
1175         $this->assertTrue($dbman->field_exists($table, $field));
1177         // Check table hasn't enums at all
1178         $this->assertFalse($dbman->check_constraint_exists($table, $field));
1179         $this->assertFalse($dbman->find_check_constraint_name($table, $field));
1180         ob_start();
1181         $this->assertFalse($dbman->drop_enum_from_field($table, $field)); // This just outputs debug warning if field hasn't enums
1182         ob_end_clean();
1184         // Insert some info
1185         $record = new stdClass();
1186         $record->course = 666;
1187         $record->type = 'qanda';
1188         $this->assertTrue($DB->insert_record('test_table_cust0', $record, false));
1190         // Hackery starts here, depending of the db family we are testing... execute
1191         // the needed SQL statements to get the "type" field defined as enum
1192         $stmt = '';
1193         switch ($DB->get_dbfamily()) {
1194             case 'mysql': // It's ENUM field for mysql
1195                 $stmt = "ALTER TABLE {$DB->get_prefix()}test_table_cust0 MODIFY type ENUM ('general', 'qanda', 'moodle') NOT NULL DEFAULT 'general'";
1196                 break;
1197             default: // It's check constraint for "normal" DBs
1198                 $stmt = "ALTER TABLE {$DB->get_prefix()}test_table_cust0 ADD CONSTRAINT ttcu0_ck CHECK (type IN ('general', 'qanda', 'moodle'))";
1199         }
1200         $DB->change_database_structure($stmt);
1202         // Check table has enums now
1203         $this->assertTrue($dbman->check_constraint_exists($table, $field));
1204         $this->assertTrue($dbman->find_check_constraint_name($table, $field));
1206         // Removing an enum value
1207         $dbman->drop_enum_from_field($table, $field);
1209         // Chech table hasn't enum anymore
1210         $this->assertFalse($dbman->check_constraint_exists($table, $field));
1211         $this->assertFalse($dbman->find_check_constraint_name($table, $field));
1213         $dbman->drop_table($table);
1214     }
1216     public function testRenameField() {
1217         $DB = $this->tdb; // do not use global $DB!
1218         $dbman = $this->tdb->get_manager();
1220         $table = $this->create_deftable('test_table0');
1221         $field = new xmldb_field('type');
1222         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general', 'course');
1224         $dbman->rename_field($table, $field, 'newfieldname');
1226         $columns = $DB->get_columns('test_table0');
1228         $this->assertFalse(array_key_exists('type', $columns));
1229         $this->assertTrue(array_key_exists('newfieldname', $columns));
1230     }
1233     public function testIndexExists() {
1234         // Skipping: this is just a test of find_index_name
1235     }
1237     public function testFindKeyName() {
1238         $dbman = $this->tdb->get_manager();
1240         $table = $this->create_deftable('test_table0');
1241         $key = $table->getKey('primary');
1243         // With Mysql, the return value is actually "mdl_test_id_pk"
1244         $result = $dbman->find_key_name($table, $key);
1245         $this->assertTrue(!empty($result));
1246     }
1248     public function testDeleteTablesFromXmldbFile() {
1249         global $CFG;
1250         $dbman = $this->tdb->get_manager();
1252         $this->create_deftable('test_table1');
1254         $this->assertTrue($dbman->table_exists('test_table1'));
1256         // feed nonexistent file
1257         try {
1258             ob_start(); // hide debug warning
1259             $dbman->delete_tables_from_xmldb_file('fpsoiudfposui');
1260             ob_end_clean();
1261             $this->assertTrue(false);
1262         } catch (Exception $e) {
1263             ob_end_clean();
1264             $this->assertTrue($e instanceof moodle_exception);
1265         }
1267         // Real file but invalid xml file
1268         $devhack = false;
1269         if (!empty($CFG->xmldbdisablenextprevchecking)) {
1270             $CFG->xmldbdisablenextprevchecking = false;
1271             $devhack = true;
1272         }
1273         try {
1274             ob_start(); // hide debug warning
1275             $dbman->delete_tables_from_xmldb_file($CFG->libdir . '/ddl/simpletest/fixtures/invalid.xml');
1276             $this->assertTrue(false);
1277             ob_end_clean();
1278         } catch (Exception $e) {
1279             ob_end_clean();
1280             $this->assertTrue($e instanceof moodle_exception);
1281         }
1282         if ($devhack) {
1283             $CFG->xmldbdisablenextprevchecking = true;
1284         }
1286         // Check that the table has not been deleted from DB
1287         $this->assertTrue($dbman->table_exists('test_table1'));
1289         // Real and valid xml file
1290         $dbman->delete_tables_from_xmldb_file($CFG->libdir . '/ddl/simpletest/fixtures/xmldb_table.xml');
1292         // Check that the table has been deleted from DB
1293         $this->assertFalse($dbman->table_exists('test_table1'));
1294     }
1296     public function testInstallFromXmldbFile() {
1297         global $CFG;
1298         $dbman = $this->tdb->get_manager();
1300         // feed nonexistent file
1301         try {
1302             ob_start(); // hide debug warning
1303             $dbman->install_from_xmldb_file('fpsoiudfposui');
1304             ob_end_clean();
1305             $this->assertTrue(false);
1306         } catch (Exception $e) {
1307             ob_end_clean();
1308             $this->assertTrue($e instanceof moodle_exception);
1309         }
1311         // Real but invalid xml file
1312         $devhack = false;
1313         if (!empty($CFG->xmldbdisablenextprevchecking)) {
1314             $CFG->xmldbdisablenextprevchecking = false;
1315             $devhack = true;
1316         }
1317         try {
1318             ob_start(); // hide debug warning
1319             $dbman->install_from_xmldb_file($CFG->libdir.'/ddl/simpletest/fixtures/invalid.xml');
1320             ob_end_clean();
1321             $this->assertTrue(false);
1322         } catch (Exception $e) {
1323             ob_end_clean();
1324             $this->assertTrue($e instanceof moodle_exception);
1325         }
1326         if ($devhack) {
1327             $CFG->xmldbdisablenextprevchecking = true;
1328         }
1330         // Check that the table has not yet been created in DB
1331         $this->assertFalse($dbman->table_exists('test_table1'));
1333         // Real and valid xml file
1334         $dbman->install_from_xmldb_file($CFG->libdir.'/ddl/simpletest/fixtures/xmldb_table.xml');
1335         $this->assertTrue($dbman->table_exists('test_table1'));
1336     }
1338     public function test_temp_tables() {
1339         $DB = $this->tdb; // do not use global $DB!
1340         $dbman = $this->tdb->get_manager();
1342         // Create temp table0
1343         $table0 = $this->tables['test_table0'];
1344         $dbman->create_temp_table($table0);
1345         $this->assertTrue($dbman->table_exists('test_table0'));
1347         // Try to create temp table with same name, must throw exception
1348         $dupetable = $this->tables['test_table0'];
1349         try {
1350             $dbman->create_temp_table($dupetable);
1351             $this->assertTrue(false);
1352         } catch (Exception $e) {
1353             $this->assertTrue($e instanceof ddl_exception);
1354         }
1356         // Try to create table with same name, must throw exception
1357         $dupetable = $this->tables['test_table0'];
1358         try {
1359             $dbman->create_table($dupetable);
1360             $this->assertTrue(false);
1361         } catch (Exception $e) {
1362             $this->assertTrue($e instanceof ddl_exception);
1363         }
1365         // Create another temp table1
1366         $table1 = $this->tables['test_table1'];
1367         $dbman->create_temp_table($table1);
1368         $this->assertTrue($dbman->table_exists('test_table1'));
1370         // Get columns and perform some basic tests
1371         $columns = $DB->get_columns('test_table1');
1372         $this->assertEqual(count($columns), 11);
1373         $this->assertTrue($columns['name'] instanceof database_column_info);
1374         $this->assertEqual($columns['name']->max_length, 30);
1375         $this->assertTrue($columns['name']->has_default);
1376         $this->assertEqual($columns['name']->default_value, 'Moodle');
1378         // Insert some records
1379         $inserted = $this->fill_deftable('test_table1');
1380         $records = $DB->get_records('test_table1');
1381         $this->assertEqual(count($records), $inserted);
1382         $this->assertEqual($records[1]->course, $this->records['test_table1'][0]->course);
1383         $this->assertEqual($records[1]->secondname, $this->records['test_table1'][0]->secondname);
1384         $this->assertEqual($records[2]->intro, $this->records['test_table1'][1]->intro);
1386         // Drop table1
1387         $dbman->drop_temp_table($table1);
1388         $this->assertFalse($dbman->table_exists('test_table1'));
1390         // Try to drop non-existing temp table, must throw exception
1391         $noetable = $this->tables['test_table1'];
1392         try {
1393             $dbman->drop_temp_table($noetable);
1394             $this->assertTrue(false);
1395         } catch (Exception $e) {
1396             $this->assertTrue($e instanceof ddl_table_missing_exception);
1397         }
1399         // Fill/modify/delete a few table0 records
1400         // TODO: that's
1402         // Drop table0
1403         $dbman->drop_temp_table($table0);
1404         $this->assertFalse($dbman->table_exists('test_table0'));
1406         // Have dropped all these temp tables here, to avoid conflicts with other (normal tables) tests!
1407     }
1409     public function test_concurrent_temp_tables() {
1410         $DB = $this->tdb; // do not use global $DB!
1411         $dbman = $this->tdb->get_manager();
1413         // Define 2 records
1414         $record1 = (object)array(
1415                         'course'     =>  1,
1416                         'secondname' => '11 important',
1417                         'intro'      => '111 important');
1418         $record2 = (object)array(
1419                         'course'     =>  2,
1420                         'secondname' => '22 important',
1421                         'intro'      => '222 important');
1423         // Create temp table1 and insert 1 record (in DB)
1424         $table = $this->tables['test_table1'];
1425         $dbman->create_temp_table($table);
1426         $this->assertTrue($dbman->table_exists('test_table1'));
1427         $inserted = $DB->insert_record('test_table1', $record1);
1429         // Switch to new connection
1430         $cfg = $DB->export_dbconfig();
1431         if (!isset($cfg->dboptions)) {
1432             $cfg->dboptions = array();
1433         }
1434         $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
1435         $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
1436         $dbman2 = $DB2->get_manager();
1437         $this->assertFalse($dbman2->table_exists('test_table1')); // Temp table not exists in DB2
1439         // Create temp table1 and insert 1 record (in DB2)
1440         $table = $this->tables['test_table1'];
1441         $dbman2->create_temp_table($table);
1442         $this->assertTrue($dbman2->table_exists('test_table1'));
1443         $inserted = $DB2->insert_record('test_table1', $record2);
1445         $dbman2->drop_temp_table($table); // Drop temp table before closing DB2
1446         $this->assertFalse($dbman2->table_exists('test_table1'));
1447         $DB2->dispose(); // Close DB2
1449         $this->assertTrue($dbman->table_exists('test_table1')); // Check table continues existing for DB
1450         $dbman->drop_temp_table($table); // Drop temp table
1451         $this->assertFalse($dbman->table_exists('test_table1'));
1452     }
1454     public function test_reset_sequence() {
1455         $DB = $this->tdb;
1456         $dbman = $DB->get_manager();
1458         $table = new xmldb_table('testtable');
1459         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1460         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1461         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1463         // Drop if exists
1464         if ($dbman->table_exists($table)) {
1465             $dbman->drop_table($table);
1466         }
1467         $dbman->create_table($table);
1468         $tablename = $table->getName();
1469         $this->tables[$tablename] = $table;
1471         $record = (object)array('id'=>666, 'course'=>10);
1472         $DB->import_record('testtable', $record);
1473         $DB->delete_records('testtable');
1475         $dbman->reset_sequence($table); // using xmldb object
1476         $this->assertEqual(1, $DB->insert_record('testtable', (object)array('course'=>13)));
1478         $DB->import_record('testtable', $record);
1479         $dbman->reset_sequence($tablename); // using string
1480         $this->assertEqual(667, $DB->insert_record('testtable', (object)array('course'=>13)));
1482         $dbman->drop_table($table);
1483     }
1485     public function test_reserved_words() {
1486         $reserved = sql_generator::getAllReservedWords();
1487         $this->assertTrue(count($reserved) > 1);
1488     }
1490     public function test_index_max_bytes() {
1491         $DB = $this->tdb;
1492         $dbman = $DB->get_manager();
1494         $maxstr = '';
1495         for($i=0; $i<255; $i++) {
1496             $maxstr .= '言'; // random long string that should fix exactly the limit for one char column
1497         }
1499         $table = new xmldb_table('testtable');
1500         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1501         $table->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1502         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1503         $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
1505         // Drop if exists
1506         if ($dbman->table_exists($table)) {
1507             $dbman->drop_table($table);
1508         }
1509         $dbman->create_table($table);
1510         $tablename = $table->getName();
1511         $this->tables[$tablename] = $table;
1513         $rec = new stdClass();
1514         $rec->name = $maxstr;
1516         $id = $DB->insert_record($tablename, $rec);
1517         $this->assertTrue(!empty($id));
1519         $rec = $DB->get_record($tablename, array('id'=>$id));
1520         $this->assertIdentical($rec->name, $maxstr);
1522         $dbman->drop_table($table);
1525         $table = new xmldb_table('testtable');
1526         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1527         $table->add_field('name', XMLDB_TYPE_CHAR, 255+1, null, XMLDB_NOTNULL, null);
1528         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1529         $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
1531         try {
1532             $dbman->create_table($table);
1533             $this->assertTrue(false);
1534         } catch (Exception $e) {
1535             $this->assertTrue($e instanceof coding_exception);
1536         }
1537     }
1539     public function test_index_composed_max_bytes() {
1540         $DB = $this->tdb;
1541         $dbman = $DB->get_manager();
1543         $maxstr = '';
1544         for($i=0; $i<200; $i++) {
1545             $maxstr .= '言';
1546         }
1547         $reststr = '';
1548         for($i=0; $i<133; $i++) {
1549             $reststr .= '言';
1550         }
1552         $table = new xmldb_table('testtable');
1553         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1554         $table->add_field('name1', XMLDB_TYPE_CHAR, 200, null, XMLDB_NOTNULL, null);
1555         $table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
1556         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1557         $table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1','name2'));
1559         // Drop if exists
1560         if ($dbman->table_exists($table)) {
1561             $dbman->drop_table($table);
1562         }
1563         $dbman->create_table($table);
1564         $tablename = $table->getName();
1565         $this->tables[$tablename] = $table;
1567         $rec = new stdClass();
1568         $rec->name1 = $maxstr;
1569         $rec->name2 = $reststr;
1571         $id = $DB->insert_record($tablename, $rec);
1572         $this->assertTrue(!empty($id));
1574         $rec = $DB->get_record($tablename, array('id'=>$id));
1575         $this->assertIdentical($rec->name1, $maxstr);
1576         $this->assertIdentical($rec->name2, $reststr);
1579         $table = new xmldb_table('testtable');
1580         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1581         $table->add_field('name1', XMLDB_TYPE_CHAR, 201, null, XMLDB_NOTNULL, null);
1582         $table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
1583         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1584         $table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1','name2'));
1586         // Drop if exists
1587         if ($dbman->table_exists($table)) {
1588             $dbman->drop_table($table);
1589         }
1591         try {
1592             $dbman->create_table($table);
1593             $this->assertTrue(false);
1594         } catch (Exception $e) {
1595             $this->assertTrue($e instanceof coding_exception);
1596         }
1597     }
1599     public function test_char_size_limit() {
1600         $DB = $this->tdb;
1601         $dbman = $DB->get_manager();
1603         $table = new xmldb_table('testtable');
1604         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1605         $table->add_field('name', XMLDB_TYPE_CHAR, xmldb_field::CHAR_MAX_LENGTH, null, XMLDB_NOTNULL, null);
1606         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1608         // Drop if exists
1609         if ($dbman->table_exists($table)) {
1610             $dbman->drop_table($table);
1611         }
1612         $dbman->create_table($table);
1613         $tablename = $table->getName();
1614         $this->tables[$tablename] = $table;
1616         // this has to work in all DBs
1617         $maxstr = '';
1618         for($i=0; $i<xmldb_field::CHAR_MAX_LENGTH; $i++) {
1619             $maxstr .= 'a'; // ascii only
1620         }
1622         $rec = new stdClass();
1623         $rec->name = $maxstr;
1625         $id = $DB->insert_record($tablename, $rec);
1626         $this->assertTrue(!empty($id));
1628         $rec = $DB->get_record($tablename, array('id'=>$id));
1629         $this->assertIdentical($rec->name, $maxstr);
1632         // Following test is supposed to fail in oracle
1633         $maxstr = '';
1634         for($i=0; $i<xmldb_field::CHAR_MAX_LENGTH; $i++) {
1635             $maxstr .= '言'; // random long string that should fix exactly the limit for one char column
1636         }
1638         $rec = new stdClass();
1639         $rec->name = $maxstr;
1641         try {
1642             $id = $DB->insert_record($tablename, $rec);
1643             $this->assertTrue(!empty($id));
1645             $rec = $DB->get_record($tablename, array('id'=>$id));
1646             $this->assertIdentical($rec->name, $maxstr);
1647         } catch (dml_exception $e) {
1648             if ($DB->get_dbfamily() === 'oracle') {
1649                 $this->fail('Oracle does not support text fields larger than 4000 bytes, this is not a big problem for mostly ascii based languages');
1650             } else {
1651                 throw $e;
1652             }
1653         }
1656         $table = new xmldb_table('testtable');
1657         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1658         $table->add_field('name', XMLDB_TYPE_CHAR, xmldb_field::CHAR_MAX_LENGTH+1, null, XMLDB_NOTNULL, null);
1659         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1661         // Drop if exists
1662         if ($dbman->table_exists($table)) {
1663             $dbman->drop_table($table);
1664         }
1665         $tablename = $table->getName();
1666         $this->tables[$tablename] = $table;
1668         try {
1669             $dbman->create_table($table);
1670             $this->assertTrue(false);
1671         } catch (Exception $e) {
1672             $this->assertTrue($e instanceof coding_exception);
1673         }
1674     }
1676  // Following methods are not supported == Do not test
1677 /*
1678     public function testRenameIndex() {
1679         // unsupported!
1680         $dbman = $this->tdb->get_manager();
1682         $table = $this->create_deftable('test_table0');
1683         $index = new xmldb_index('course');
1684         $index->set_attributes(XMLDB_INDEX_UNIQUE, array('course'));
1686         $this->assertTrue($dbman->rename_index($table, $index, 'newindexname'));
1687     }
1689     public function testRenameKey() {
1690         //unsupported
1691          $dbman = $this->tdb->get_manager();
1693         $table = $this->create_deftable('test_table0');
1694         $key = new xmldb_key('course');
1695         $key->set_attributes(XMLDB_KEY_UNIQUE, array('course'));
1697         $this->assertTrue($dbman->rename_key($table, $key, 'newkeyname'));
1698     }
1699 */