MDL-37726 stop using PREVIOUS/NEXT in install.xml files
[moodle.git] / lib / ddl / tests / ddl_test.php
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/>.
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  */
26 defined('MOODLE_INTERNAL') || die();
28 class ddl_testcase extends database_driver_testcase {
29     private $tables = array();
30     private $records= array();
32     protected function setUp() {
33         parent::setUp();
34         $dbman = $this->tdb->get_manager(); // loads DDL libs
36         $table = new xmldb_table('test_table0');
37         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
38         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, 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', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
44         $table->add_field('assesstimestart', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
45         $table->add_field('assesstimefinish', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, 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', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
48         $table->add_field('forcesubscribe', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
49         $table->add_field('trackingtype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1');
50         $table->add_field('rsstype', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
51         $table->add_field('rssarticles', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
52         $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
53         $table->add_field('grade', XMLDB_TYPE_NUMBER, '20,0', XMLDB_UNSIGNED, 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', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
56         $table->add_field('blockafter', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
57         $table->add_field('blockperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, 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");
64         $this->tables[$table->getName()] = $table;
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'));
79         // Second, smaller table
80         $table = new xmldb_table ('test_table1');
81         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
82         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, 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', XMLDB_UNSIGNED, 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', XMLDB_UNSIGNED, 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");
96         $this->tables[$table->getName()] = $table;
98         // Define 2 initial records for this table
99         $this->records[$table->getName()] = array(
100             (object)array(
101                 'course' => '1',
102                 'secondname'   => 'first record', // > 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', // > 10 cc, please don't modify. Some tests below depend of this
107                 'intro'  => 'second record'));
108     }
110     private function create_deftable($tablename) {
111         $dbman = $this->tdb->get_manager();
113         if (!isset($this->tables[$tablename])) {
114             return null;
115         }
117         $table = $this->tables[$tablename];
119         if ($dbman->table_exists($table)) {
120             $dbman->drop_table($table);
121         }
122         $dbman->create_table($table);
124         return $table;
125     }
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();
138         if (!isset($this->records[$tablename])) {
139             return null;
140         }
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         }
150         return count($this->records[$tablename]);
151     }
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();
160         // first make sure it returns false if table does not exist
161         $table = $this->tables['test_table0'];
163         try {
164             $result = $DB->get_records('test_table0');
165         } catch (dml_exception $e) {
166             $result = false;
167         }
168         $this->resetDebugging();
170         $this->assertFalse($result);
172         $this->assertFalse($dbman->table_exists('test_table0')); // by name
173         $this->assertFalse($dbman->table_exists($table));        // by xmldb_table
175         // create table and test again
176         $dbman->create_table($table);
178         $this->assertTrue($DB->get_records('test_table0') === array());
179         $this->assertTrue($dbman->table_exists('test_table0')); // by name
180         $this->assertTrue($dbman->table_exists($table));        // by xmldb_table
182         // drop table and test again
183         $dbman->drop_table($table);
185         try {
186             $result = $DB->get_records('test_table0');
187         } catch (dml_exception $e) {
188             $result = false;
189         }
190         $this->resetDebugging();
192         $this->assertFalse($result);
194         $this->assertFalse($dbman->table_exists('test_table0')); // by name
195         $this->assertFalse($dbman->table_exists($table));        // by xmldb_table
196     }
198     /**
199      * Test behaviour of create_table()
200      */
201     public function test_create_table() {
202         $DB = $this->tdb; // do not use global $DB!
203         $dbman = $this->tdb->get_manager();
205         // create table
206         $table = $this->tables['test_table1'];
208         $dbman->create_table($table);
209         $this->assertTrue($dbman->table_exists($table));
211         // basic get_tables() test
212         $tables = $DB->get_tables();
213         $this->assertTrue(array_key_exists('test_table1', $tables));
215         // basic get_columns() tests
216         $columns = $DB->get_columns('test_table1');
217         $this->assertEquals($columns['id']->meta_type, 'R');
218         $this->assertEquals($columns['course']->meta_type, 'I');
219         $this->assertEquals($columns['name']->meta_type, 'C');
220         $this->assertEquals($columns['secondname']->meta_type, 'C');
221         $this->assertEquals($columns['thirdname']->meta_type, 'C');
222         $this->assertEquals($columns['intro']->meta_type, 'X');
223         $this->assertEquals($columns['avatar']->meta_type, 'B');
224         $this->assertEquals($columns['grade']->meta_type, 'N');
225         $this->assertEquals($columns['percentfloat']->meta_type, 'N');
226         $this->assertEquals($columns['userid']->meta_type, 'I');
227         // some defaults
228         $this->assertTrue($columns['course']->has_default);
229         $this->assertEquals($columns['course']->default_value, 0);
230         $this->assertTrue($columns['name']->has_default);
231         $this->assertEquals($columns['name']->default_value, 'Moodle');
232         $this->assertTrue($columns['secondname']->has_default);
233         $this->assertEquals($columns['secondname']->default_value, '');
234         $this->assertTrue($columns['thirdname']->has_default);
235         $this->assertEquals($columns['thirdname']->default_value, '');
236         $this->assertTrue($columns['percentfloat']->has_default);
237         $this->assertEquals($columns['percentfloat']->default_value, 99.9);
238         $this->assertTrue($columns['userid']->has_default);
239         $this->assertEquals($columns['userid']->default_value, 0);
241         // basic get_indexes() test
242         $indexes = $DB->get_indexes('test_table1');
243         $courseindex = reset($indexes);
244         $this->assertEquals($courseindex['unique'], 1);
245         $this->assertEquals($courseindex['columns'][0], 'course');
247         // check sequence returns 1 for first insert
248         $rec = (object)array(
249             'course'     => 10,
250             'secondname' => 'not important',
251             'intro'      => 'not important');
252         $this->assertSame($DB->insert_record('test_table1', $rec), 1);
254         // check defined defaults are working ok
255         $dbrec = $DB->get_record('test_table1', array('id' => 1));
256         $this->assertEquals($dbrec->name, 'Moodle');
257         $this->assertEquals($dbrec->thirdname, '');
259         // check exceptions if multiple R columns
260         $table = new xmldb_table ('test_table2');
261         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
262         $table->add_field('rid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
263         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
264         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
265         $table->add_key('primaryx', XMLDB_KEY_PRIMARY, array('id'));
266         $table->setComment("This is a test'n drop table. You can drop it safely");
268         $this->tables[$table->getName()] = $table;
270         try {
271             $dbman->create_table($table);
272             $this->fail('Exception expected');
273         } catch (Exception $e) {
274             $this->assertTrue($e instanceof ddl_exception);
275         }
277         // check exceptions missing primary key on R column
278         $table = new xmldb_table ('test_table2');
279         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
280         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
281         $table->setComment("This is a test'n drop table. You can drop it safely");
283         $this->tables[$table->getName()] = $table;
285         try {
286             $dbman->create_table($table);
287             $this->fail('Exception expected');
288         } catch (Exception $e) {
289             $this->assertTrue($e instanceof ddl_exception);
290         }
292         // long table name names - the largest allowed
293         $table = new xmldb_table('test_table0123456789_____xyz');
294         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
295         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
296         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
297         $table->setComment("This is a test'n drop table. You can drop it safely");
299         $this->tables[$table->getName()] = $table;
301         $dbman->create_table($table);
302         $this->assertTrue($dbman->table_exists($table));
303         $dbman->drop_table($table);
305         // table name is too long
306         $table = new xmldb_table('test_table0123456789_____xyz9');
307         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
308         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
309         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
310         $table->setComment("This is a test'n drop table. You can drop it safely");
312         $this->tables[$table->getName()] = $table;
314         try {
315             $dbman->create_table($table);
316             $this->fail('Exception expected');
317         } catch (Exception $e) {
318             $this->assertSame(get_class($e), 'coding_exception');
319         }
321         // invalid table name
322         $table = new xmldb_table('test_tableCD');
323         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
324         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
325         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
326         $table->setComment("This is a test'n drop table. You can drop it safely");
328         $this->tables[$table->getName()] = $table;
330         try {
331             $dbman->create_table($table);
332             $this->fail('Exception expected');
333         } catch (Exception $e) {
334             $this->assertSame(get_class($e), 'coding_exception');
335         }
338         // weird column names - the largest allowed
339         $table = new xmldb_table('test_table3');
340         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
341         $table->add_field('abcdef____0123456789_______xyz', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
342         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
343         $table->setComment("This is a test'n drop table. You can drop it safely");
345         $this->tables[$table->getName()] = $table;
347         $dbman->create_table($table);
348         $this->assertTrue($dbman->table_exists($table));
349         $dbman->drop_table($table);
351         // Too long field name - max 30
352         $table = new xmldb_table('test_table4');
353         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
354         $table->add_field('abcdeabcdeabcdeabcdeabcdeabcdez', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
355         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
356         $table->setComment("This is a test'n drop table. You can drop it safely");
358         $this->tables[$table->getName()] = $table;
360         try {
361             $dbman->create_table($table);
362             $this->fail('Exception expected');
363         } catch (Exception $e) {
364             $this->assertSame(get_class($e), 'coding_exception');
365         }
367         // Invalid field name
368         $table = new xmldb_table('test_table4');
369         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
370         $table->add_field('abCD', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
371         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
372         $table->setComment("This is a test'n drop table. You can drop it safely");
374         $this->tables[$table->getName()] = $table;
376         try {
377             $dbman->create_table($table);
378             $this->fail('Exception expected');
379         } catch (Exception $e) {
380             $this->assertSame(get_class($e), 'coding_exception');
381         }
383         // Invalid integer length
384         $table = new xmldb_table('test_table4');
385         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
386         $table->add_field('course', XMLDB_TYPE_INTEGER, '21', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
387         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
388         $table->setComment("This is a test'n drop table. You can drop it safely");
390         $this->tables[$table->getName()] = $table;
392         try {
393             $dbman->create_table($table);
394             $this->fail('Exception expected');
395         } catch (Exception $e) {
396             $this->assertSame(get_class($e), 'coding_exception');
397         }
399         // Invalid integer default
400         $table = new xmldb_table('test_table4');
401         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
402         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'x');
403         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
404         $table->setComment("This is a test'n drop table. You can drop it safely");
406         $this->tables[$table->getName()] = $table;
408         try {
409             $dbman->create_table($table);
410             $this->fail('Exception expected');
411         } catch (Exception $e) {
412             $this->assertSame(get_class($e), 'coding_exception');
413         }
415         // Invalid decimal length
416         $table = new xmldb_table('test_table4');
417         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
418         $table->add_field('num', XMLDB_TYPE_NUMBER, '21,10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
419         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
420         $table->setComment("This is a test'n drop table. You can drop it safely");
422         $this->tables[$table->getName()] = $table;
424         try {
425             $dbman->create_table($table);
426             $this->fail('Exception expected');
427         } catch (Exception $e) {
428             $this->assertSame(get_class($e), 'coding_exception');
429         }
431         // Invalid decimal decimals
432         $table = new xmldb_table('test_table4');
433         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
434         $table->add_field('num', XMLDB_TYPE_NUMBER, '10,11', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
435         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
436         $table->setComment("This is a test'n drop table. You can drop it safely");
438         $this->tables[$table->getName()] = $table;
440         try {
441             $dbman->create_table($table);
442             $this->fail('Exception expected');
443         } catch (Exception $e) {
444             $this->assertSame(get_class($e), 'coding_exception');
445         }
447         // Invalid decimal default
448         $table = new xmldb_table('test_table4');
449         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
450         $table->add_field('num', XMLDB_TYPE_NUMBER, '10,5', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'x');
451         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
452         $table->setComment("This is a test'n drop table. You can drop it safely");
454         $this->tables[$table->getName()] = $table;
456         try {
457             $dbman->create_table($table);
458             $this->fail('Exception expected');
459         } catch (Exception $e) {
460             $this->assertSame(get_class($e), 'coding_exception');
461         }
463         // Invalid float length
464         $table = new xmldb_table('test_table4');
465         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
466         $table->add_field('num', XMLDB_TYPE_FLOAT, '21,10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
467         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
468         $table->setComment("This is a test'n drop table. You can drop it safely");
470         $this->tables[$table->getName()] = $table;
472         try {
473             $dbman->create_table($table);
474             $this->fail('Exception expected');
475         } catch (Exception $e) {
476             $this->assertSame(get_class($e), 'coding_exception');
477         }
479         // Invalid float decimals
480         $table = new xmldb_table('test_table4');
481         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
482         $table->add_field('num', XMLDB_TYPE_FLOAT, '10,11', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
483         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
484         $table->setComment("This is a test'n drop table. You can drop it safely");
486         $this->tables[$table->getName()] = $table;
488         try {
489             $dbman->create_table($table);
490             $this->fail('Exception expected');
491         } catch (Exception $e) {
492             $this->assertSame(get_class($e), 'coding_exception');
493         }
495         // Invalid float default
496         $table = new xmldb_table('test_table4');
497         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
498         $table->add_field('num', XMLDB_TYPE_FLOAT, '10,5', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'x');
499         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
500         $table->setComment("This is a test'n drop table. You can drop it safely");
502         $this->tables[$table->getName()] = $table;
504         try {
505             $dbman->create_table($table);
506             $this->fail('Exception expected');
507         } catch (Exception $e) {
508             $this->assertSame(get_class($e), 'coding_exception');
509         }
511     }
513     /**
514      * Test behaviour of drop_table()
515      */
516     public function test_drop_table() {
517         $DB = $this->tdb; // do not use global $DB!
518         $dbman = $this->tdb->get_manager();
520         // initially table doesn't exist
521         $this->assertFalse($dbman->table_exists('test_table0'));
523         // create table with contents
524         $table = $this->create_deftable('test_table0');
525         $this->assertTrue($dbman->table_exists('test_table0'));
527         // fill the table with some records before dropping it
528         $this->fill_deftable('test_table0');
530         // drop by xmldb_table object
531         $dbman->drop_table($table);
532         $this->assertFalse($dbman->table_exists('test_table0'));
534         // basic get_tables() test
535         $tables = $DB->get_tables();
536         $this->assertFalse(array_key_exists('test_table0', $tables));
538         // columns cache must be empty
539         $columns = $DB->get_columns('test_table0');
540         $this->assertEmpty($columns);
542         $indexes = $DB->get_indexes('test_table0');
543         $this->assertEmpty($indexes);
544     }
546     /**
547      * Test behaviour of rename_table()
548      */
549     public function test_rename_table() {
550         $DB = $this->tdb; // do not use global $DB!
551         $dbman = $this->tdb->get_manager();
553         $table = $this->create_deftable('test_table1');
555         // fill the table with some records before renaming it
556         $insertedrows = $this->fill_deftable('test_table1');
558         $this->assertFalse($dbman->table_exists('test_table_cust1'));
559         $dbman->rename_table($table, 'test_table_cust1');
560         $this->assertTrue($dbman->table_exists('test_table_cust1'));
562         // check sequence returns $insertedrows + 1 for this insert (after rename)
563         $rec = (object)array(
564             'course'     => 20,
565             'secondname' => 'not important',
566             'intro'      => 'not important');
567         $this->assertSame($DB->insert_record('test_table_cust1', $rec), $insertedrows + 1);
568     }
570     /**
571      * Test behaviour of field_exists()
572      */
573     public function test_field_exists() {
574         $dbman = $this->tdb->get_manager();
576         $table = $this->create_deftable('test_table0');
578         // String params
579         // Give a nonexistent table as first param (throw exception)
580         try {
581             $dbman->field_exists('nonexistenttable', 'id');
582             $this->assertTrue(false);
583         } catch (Exception $e) {
584             $this->assertTrue($e instanceof moodle_exception);
585         }
587         // Give a nonexistent field as second param (return false)
588         $this->assertFalse($dbman->field_exists('test_table0', 'nonexistentfield'));
590         // Correct string params
591         $this->assertTrue($dbman->field_exists('test_table0', 'id'));
593         // Object params
594         $realfield = $table->getField('id');
596         // Give a nonexistent table as first param (throw exception)
597         $nonexistenttable = new xmldb_table('nonexistenttable');
598         try {
599             $dbman->field_exists($nonexistenttable, $realfield);
600             $this->assertTrue(false);
601         } catch (Exception $e) {
602             $this->assertTrue($e instanceof moodle_exception);
603         }
605         // Give a nonexistent field as second param (return false)
606         $nonexistentfield = new xmldb_field('nonexistentfield');
607         $this->assertFalse($dbman->field_exists($table, $nonexistentfield));
609         // Correct object params
610         $this->assertTrue($dbman->field_exists($table, $realfield));
612         // Mix string and object params
613         // Correct ones
614         $this->assertTrue($dbman->field_exists($table, 'id'));
615         $this->assertTrue($dbman->field_exists('test_table0', $realfield));
616         // Non existing tables (throw exception)
617         try {
618             $this->assertFalse($dbman->field_exists($nonexistenttable, 'id'));
619             $this->assertTrue(false);
620         } catch (Exception $e) {
621             $this->assertTrue($e instanceof moodle_exception);
622         }
623         try {
624             $this->assertFalse($dbman->field_exists('nonexistenttable', $realfield));
625             $this->assertTrue(false);
626         } catch (Exception $e) {
627             $this->assertTrue($e instanceof moodle_exception);
628         }
629         // Non existing fields (return false)
630         $this->assertFalse($dbman->field_exists($table, 'nonexistentfield'));
631         $this->assertFalse($dbman->field_exists('test_table0', $nonexistentfield));
632     }
634     /**
635      * Test behaviour of add_field()
636      */
637     public function test_add_field() {
638         $DB = $this->tdb; // do not use global $DB!
639         $dbman = $this->tdb->get_manager();
641         $table = $this->create_deftable('test_table1');
643         // fill the table with some records before adding fields
644         $this->fill_deftable('test_table1');
646         // add one not null field without specifying default value (throws ddl_exception)
647         $field = new xmldb_field('onefield');
648         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
649         try {
650             $dbman->add_field($table, $field);
651             $this->assertTrue(false);
652         } catch (Exception $e) {
653             $this->assertTrue($e instanceof ddl_exception);
654         }
656         // add one existing field (throws ddl_exception)
657         $field = new xmldb_field('course');
658         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 2);
659         try {
660             $dbman->add_field($table, $field);
661             $this->assertTrue(false);
662         } catch (Exception $e) {
663             $this->assertTrue($e instanceof ddl_exception);
664         }
666         // TODO: add one field with invalid type, must throw exception
667         // TODO: add one text field with default, must throw exception
668         // TODO: add one binary field with default, must throw exception
670         // add one integer field and check it
671         $field = new xmldb_field('oneinteger');
672         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 2);
673         $dbman->add_field($table, $field);
674         $this->assertTrue($dbman->field_exists($table, 'oneinteger'));
675         $columns = $DB->get_columns('test_table1');
676         $this->assertEquals($columns['oneinteger']->name         ,'oneinteger');
677         $this->assertEquals($columns['oneinteger']->not_null     , true);
678         // max_length and scale cannot be checked under all DBs at all for integer fields
679         $this->assertEquals($columns['oneinteger']->primary_key  , false);
680         $this->assertEquals($columns['oneinteger']->binary       , false);
681         $this->assertEquals($columns['oneinteger']->has_default  , true);
682         $this->assertEquals($columns['oneinteger']->default_value, 2);
683         $this->assertEquals($columns['oneinteger']->meta_type    ,'I');
684         $this->assertEquals($DB->get_field('test_table1', 'oneinteger', array(), IGNORE_MULTIPLE), 2); //check default has been applied
686         // add one numeric field and check it
687         $field = new xmldb_field('onenumber');
688         $field->set_attributes(XMLDB_TYPE_NUMBER, '6,3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 2.55);
689         $dbman->add_field($table, $field);
690         $this->assertTrue($dbman->field_exists($table, 'onenumber'));
691         $columns = $DB->get_columns('test_table1');
692         $this->assertEquals($columns['onenumber']->name         ,'onenumber');
693         $this->assertEquals($columns['onenumber']->max_length   , 6);
694         $this->assertEquals($columns['onenumber']->scale        , 3);
695         $this->assertEquals($columns['onenumber']->not_null     , true);
696         $this->assertEquals($columns['onenumber']->primary_key  , false);
697         $this->assertEquals($columns['onenumber']->binary       , false);
698         $this->assertEquals($columns['onenumber']->has_default  , true);
699         $this->assertEquals($columns['onenumber']->default_value, 2.550);
700         $this->assertEquals($columns['onenumber']->meta_type    ,'N');
701         $this->assertEquals($DB->get_field('test_table1', 'onenumber', array(), IGNORE_MULTIPLE), 2.550); //check default has been applied
703         // add one float field and check it (not official type - must work as number)
704         $field = new xmldb_field('onefloat');
705         $field->set_attributes(XMLDB_TYPE_FLOAT, '6,3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 3.550);
706         $dbman->add_field($table, $field);
707         $this->assertTrue($dbman->field_exists($table, 'onefloat'));
708         $columns = $DB->get_columns('test_table1');
709         $this->assertEquals($columns['onefloat']->name         ,'onefloat');
710         $this->assertEquals($columns['onefloat']->not_null     , true);
711         // max_length and scale cannot be checked under all DBs at all for float fields
712         $this->assertEquals($columns['onefloat']->primary_key  , false);
713         $this->assertEquals($columns['onefloat']->binary       , false);
714         $this->assertEquals($columns['onefloat']->has_default  , true);
715         $this->assertEquals($columns['onefloat']->default_value, 3.550);
716         $this->assertEquals($columns['onefloat']->meta_type    ,'N');
717         // Just rounding DB information to 7 decimal digits. Fair enough to test 3.550 and avoids one nasty bug
718         // in MSSQL core returning wrong floats (http://social.msdn.microsoft.com/Forums/en-US/sqldataaccess/thread/5e08de63-16bb-4f24-b645-0cf8fc669de3)
719         // In any case, floats aren't officially supported by Moodle, with number/decimal type being the correct ones, so
720         // this isn't a real problem at all.
721         $this->assertEquals(round($DB->get_field('test_table1', 'onefloat', array(), IGNORE_MULTIPLE), 7), 3.550); //check default has been applied
723         // add one char field and check it
724         $field = new xmldb_field('onechar');
725         $field->set_attributes(XMLDB_TYPE_CHAR, '25', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'Nice dflt!');
726         $dbman->add_field($table, $field);
727         $this->assertTrue($dbman->field_exists($table, 'onechar'));
728         $columns = $DB->get_columns('test_table1');
729         $this->assertEquals($columns['onechar']->name         ,'onechar');
730         $this->assertEquals($columns['onechar']->max_length   , 25);
731         $this->assertEquals($columns['onechar']->scale        , null);
732         $this->assertEquals($columns['onechar']->not_null     , true);
733         $this->assertEquals($columns['onechar']->primary_key  , false);
734         $this->assertEquals($columns['onechar']->binary       , false);
735         $this->assertEquals($columns['onechar']->has_default  , true);
736         $this->assertEquals($columns['onechar']->default_value,'Nice dflt!');
737         $this->assertEquals($columns['onechar']->meta_type    ,'C');
738         $this->assertEquals($DB->get_field('test_table1', 'onechar', array(), IGNORE_MULTIPLE), 'Nice dflt!'); //check default has been applied
740         // add one big text field and check it
741         $field = new xmldb_field('onetext');
742         $field->set_attributes(XMLDB_TYPE_TEXT, 'big');
743         $dbman->add_field($table, $field);
744         $this->assertTrue($dbman->field_exists($table, 'onetext'));
745         $columns = $DB->get_columns('test_table1');
746         $this->assertEquals($columns['onetext']->name         ,'onetext');
747         $this->assertEquals($columns['onetext']->max_length   , -1); // -1 means unknown or big
748         $this->assertEquals($columns['onetext']->scale        , null);
749         $this->assertEquals($columns['onetext']->not_null     , false);
750         $this->assertEquals($columns['onetext']->primary_key  , false);
751         $this->assertEquals($columns['onetext']->binary       , false);
752         $this->assertEquals($columns['onetext']->has_default  , false);
753         $this->assertEquals($columns['onetext']->default_value, null);
754         $this->assertEquals($columns['onetext']->meta_type    ,'X');
756         // add one medium text field and check it
757         $field = new xmldb_field('mediumtext');
758         $field->set_attributes(XMLDB_TYPE_TEXT, 'medium');
759         $dbman->add_field($table, $field);
760         $columns = $DB->get_columns('test_table1');
761         $this->assertTrue(($columns['mediumtext']->max_length == -1) or ($columns['mediumtext']->max_length >= 16777215)); // -1 means unknown or big
763         // add one small text field and check it
764         $field = new xmldb_field('smalltext');
765         $field->set_attributes(XMLDB_TYPE_TEXT, 'small');
766         $dbman->add_field($table, $field);
767         $columns = $DB->get_columns('test_table1');
768         $this->assertTrue(($columns['smalltext']->max_length == -1) or ($columns['smalltext']->max_length >= 65535)); // -1 means unknown or big
770         // add one binary field and check it
771         $field = new xmldb_field('onebinary');
772         $field->set_attributes(XMLDB_TYPE_BINARY);
773         $dbman->add_field($table, $field);
774         $this->assertTrue($dbman->field_exists($table, 'onebinary'));
775         $columns = $DB->get_columns('test_table1');
776         $this->assertEquals($columns['onebinary']->name         ,'onebinary');
777         $this->assertEquals($columns['onebinary']->max_length   , -1);
778         $this->assertEquals($columns['onebinary']->scale        , null);
779         $this->assertEquals($columns['onebinary']->not_null     , false);
780         $this->assertEquals($columns['onebinary']->primary_key  , false);
781         $this->assertEquals($columns['onebinary']->binary       , true);
782         $this->assertEquals($columns['onebinary']->has_default  , false);
783         $this->assertEquals($columns['onebinary']->default_value, null);
784         $this->assertEquals($columns['onebinary']->meta_type    ,'B');
786         // TODO: check datetime type. Although unused should be fully supported.
787     }
789     /**
790      * Test behaviour of drop_field()
791      */
792     public function test_drop_field() {
793         $DB = $this->tdb; // do not use global $DB!
794         $dbman = $this->tdb->get_manager();
796         $table = $this->create_deftable('test_table0');
798         // fill the table with some records before dropping fields
799         $this->fill_deftable('test_table0');
801         // drop field with simple xmldb_field having indexes, must return exception
802         $field = new xmldb_field('type'); // Field has indexes and default clause
803         $this->assertTrue($dbman->field_exists($table, 'type'));
804         try {
805             $dbman->drop_field($table, $field);
806             $this->assertTrue(false);
807         } catch (Exception $e) {
808             $this->assertTrue($e instanceof ddl_dependency_exception);
809         }
810         $this->assertTrue($dbman->field_exists($table, 'type')); // continues existing, drop aborted
812         // drop field with complete xmldb_field object and related indexes, must return exception
813         $field = $table->getField('course'); // Field has indexes and default clause
814         $this->assertTrue($dbman->field_exists($table, $field));
815         try {
816             $dbman->drop_field($table, $field);
817             $this->assertTrue(false);
818         } catch (Exception $e) {
819             $this->assertTrue($e instanceof ddl_dependency_exception);
820         }
821         $this->assertTrue($dbman->field_exists($table, $field)); // continues existing, drop aborted
823         // drop one non-existing field, must return exception
824         $field = new xmldb_field('nonexistingfield');
825         $this->assertFalse($dbman->field_exists($table, $field));
826         try {
827             $dbman->drop_field($table, $field);
828             $this->assertTrue(false);
829         } catch (Exception $e) {
830             $this->assertTrue($e instanceof ddl_field_missing_exception);
831         }
833         // drop field with simple xmldb_field, not having related indexes
834         $field = new xmldb_field('forcesubscribe'); // Field has default clause
835         $this->assertTrue($dbman->field_exists($table, 'forcesubscribe'));
836         $dbman->drop_field($table, $field);
837         $this->assertFalse($dbman->field_exists($table, 'forcesubscribe'));
840         // drop field with complete xmldb_field object, not having related indexes
841         $field = new xmldb_field('trackingtype'); // Field has default clause
842         $this->assertTrue($dbman->field_exists($table, $field));
843         $dbman->drop_field($table, $field);
844         $this->assertFalse($dbman->field_exists($table, $field));
845     }
847     /**
848      * Test behaviour of change_field_type()
849      */
850     public function test_change_field_type() {
851         $DB = $this->tdb; // do not use global $DB!
852         $dbman = $this->tdb->get_manager();
854         // create table with indexed field and not indexed field to
855         // perform tests in both fields, both having defaults
856         $table = new xmldb_table('test_table_cust0');
857         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
858         $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2');
859         $table->add_field('anothernumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '4');
860         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
861         $table->add_index('onenumber', XMLDB_INDEX_NOTUNIQUE, array('onenumber'));
862         $dbman->create_table($table);
864         $record = new stdClass();
865         $record->onenumber = 2;
866         $record->anothernumber = 4;
867         $recoriginal = $DB->insert_record('test_table_cust0', $record);
869         // change column from integer to varchar. Must return exception because of dependent index
870         $field = new xmldb_field('onenumber');
871         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'test');
872         try {
873             $dbman->change_field_type($table, $field);
874             $this->assertTrue(false);
875         } catch (Exception $e) {
876             $this->assertTrue($e instanceof ddl_dependency_exception);
877         }
878         // column continues being integer 10 not null default 2
879         $columns = $DB->get_columns('test_table_cust0');
880         $this->assertEquals($columns['onenumber']->meta_type, 'I');
881         //TODO: check the rest of attributes
883         // change column from integer to varchar. Must work because column has no dependencies
884         $field = new xmldb_field('anothernumber');
885         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'test');
886         $dbman->change_field_type($table, $field);
887         // column is char 30 not null default 'test' now
888         $columns = $DB->get_columns('test_table_cust0');
889         $this->assertEquals($columns['anothernumber']->meta_type, 'C');
890         //TODO: check the rest of attributes
892         // change column back from char to integer
893         $field = new xmldb_field('anothernumber');
894         $field->set_attributes(XMLDB_TYPE_INTEGER, '8', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '5');
895         $dbman->change_field_type($table, $field);
896         // column is integer 8 not null default 5 now
897         $columns = $DB->get_columns('test_table_cust0');
898         $this->assertEquals($columns['anothernumber']->meta_type, 'I');
899         //TODO: check the rest of attributes
901         // change column once more from integer to char
902         $field = new xmldb_field('anothernumber');
903         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, "test'n drop");
904         $dbman->change_field_type($table, $field);
905         // column is char 30 not null default "test'n drop" now
906         $columns = $DB->get_columns('test_table_cust0');
907         $this->assertEquals($columns['anothernumber']->meta_type, 'C');
908         //TODO: check the rest of attributes
910         // insert one string value and try to convert to integer. Must throw exception
911         $record = new stdClass();
912         $record->onenumber = 7;
913         $record->anothernumber = 'string value';
914         $rectodrop = $DB->insert_record('test_table_cust0', $record);
915         $field = new xmldb_field('anothernumber');
916         $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '5');
917         try {
918             $dbman->change_field_type($table, $field);
919             $this->assertTrue(false);
920         } catch (Exception $e) {
921             $this->assertTrue($e instanceof ddl_change_structure_exception);
922         }
923         // column continues being char 30 not null default "test'n drop" now
924         $this->assertEquals($columns['anothernumber']->meta_type, 'C');
925         //TODO: check the rest of attributes
926         $DB->delete_records('test_table_cust0', array('id' => $rectodrop)); // Delete the string record
928         // change the column from varchar to float
929         $field = new xmldb_field('anothernumber');
930         $field->set_attributes(XMLDB_TYPE_FLOAT, '20,10', XMLDB_UNSIGNED, null, null, null);
931         $dbman->change_field_type($table, $field);
932         // column is float 20,10 null default null
933         $columns = $DB->get_columns('test_table_cust0');
934         $this->assertEquals($columns['anothernumber']->meta_type, 'N'); // floats are seen as number
935         //TODO: check the rest of attributes
937         // change the column back from float to varchar
938         $field = new xmldb_field('anothernumber');
939         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'test');
940         $dbman->change_field_type($table, $field);
941         // column is char 20 not null default "test" now
942         $columns = $DB->get_columns('test_table_cust0');
943         $this->assertEquals($columns['anothernumber']->meta_type, 'C');
944         //TODO: check the rest of attributes
946         // change the column from varchar to number
947         $field = new xmldb_field('anothernumber');
948         $field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', XMLDB_UNSIGNED, null, null, null);
949         $dbman->change_field_type($table, $field);
950         // column is number 20,10 null default null now
951         $columns = $DB->get_columns('test_table_cust0');
952         $this->assertEquals($columns['anothernumber']->meta_type, 'N');
953         //TODO: check the rest of attributes
955         // change the column from number to integer
956         $field = new xmldb_field('anothernumber');
957         $field->set_attributes(XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, null, null, null);
958         $dbman->change_field_type($table, $field);
959         // column is integer 2 null default null now
960         $columns = $DB->get_columns('test_table_cust0');
961         $this->assertEquals($columns['anothernumber']->meta_type, 'I');
962         //TODO: check the rest of attributes
964         // change the column from integer to text
965         $field = new xmldb_field('anothernumber');
966         $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
967         $dbman->change_field_type($table, $field);
968         // column is char text not null default null
969         $columns = $DB->get_columns('test_table_cust0');
970         $this->assertEquals($columns['anothernumber']->meta_type, 'X');
972         // change the column back from text to number
973         $field = new xmldb_field('anothernumber');
974         $field->set_attributes(XMLDB_TYPE_NUMBER, '20,10', XMLDB_UNSIGNED, null, null, null);
975         $dbman->change_field_type($table, $field);
976         // column is number 20,10 null default null now
977         $columns = $DB->get_columns('test_table_cust0');
978         $this->assertEquals($columns['anothernumber']->meta_type, 'N');
979         //TODO: check the rest of attributes
981         // change the column from number to text
982         $field = new xmldb_field('anothernumber');
983         $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
984         $dbman->change_field_type($table, $field);
985         // column is char text not null default "test" now
986         $columns = $DB->get_columns('test_table_cust0');
987         $this->assertEquals($columns['anothernumber']->meta_type, 'X');
988         //TODO: check the rest of attributes
990         // change the column back from text to integer
991         $field = new xmldb_field('anothernumber');
992         $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 10);
993         $dbman->change_field_type($table, $field);
994         // column is integer 10 not null default 10
995         $columns = $DB->get_columns('test_table_cust0');
996         $this->assertEquals($columns['anothernumber']->meta_type, 'I');
997         //TODO: check the rest of attributes
999         // check original value has survived to all the type changes
1000         $this->assertnotEmpty($rec = $DB->get_record('test_table_cust0', array('id' => $recoriginal)));
1001         $this->assertEquals($rec->anothernumber, 4);
1003         $dbman->drop_table($table);
1004         $this->assertFalse($dbman->table_exists($table));
1005     }
1007     /**
1008      * Test behaviour of test_change_field_precision()
1009      */
1010     public function test_change_field_precision() {
1011         $DB = $this->tdb; // do not use global $DB!
1012         $dbman = $this->tdb->get_manager();
1014         $table = $this->create_deftable('test_table1');
1016         // fill the table with some records before dropping fields
1017         $this->fill_deftable('test_table1');
1019         // change text field from medium to big
1020         $field = new xmldb_field('intro');
1021         $field->set_attributes(XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
1022         $dbman->change_field_precision($table, $field);
1023         $columns = $DB->get_columns('test_table1');
1024         // cannot check the text type, only the metatype
1025         $this->assertEquals($columns['intro']->meta_type, 'X');
1026         //TODO: check the rest of attributes
1028         // change char field from 30 to 20
1029         $field = new xmldb_field('secondname');
1030         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
1031         $dbman->change_field_precision($table, $field);
1032         $columns = $DB->get_columns('test_table1');
1033         $this->assertEquals($columns['secondname']->meta_type, 'C');
1034         //TODO: check the rest of attributes
1036         // change char field from 20 to 10, having contents > 10cc. Throw exception
1037         $field = new xmldb_field('secondname');
1038         $field->set_attributes(XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, null);
1039         try {
1040             $dbman->change_field_precision($table, $field);
1041             $this->assertTrue(false);
1042         } catch (Exception $e) {
1043             $this->assertTrue($e instanceof ddl_change_structure_exception);
1044         }
1045         // No changes in field specs at all
1046         $columns = $DB->get_columns('test_table1');
1047         $this->assertEquals($columns['secondname']->meta_type, 'C');
1048         //TODO: check the rest of attributes
1050         // change number field from 20,10 to 10,2
1051         $field = new xmldb_field('grade');
1052         $field->set_attributes(XMLDB_TYPE_NUMBER, '10,2', null, null, null, null);
1053         $dbman->change_field_precision($table, $field);
1054         $columns = $DB->get_columns('test_table1');
1055         $this->assertEquals($columns['grade']->meta_type, 'N');
1056         //TODO: check the rest of attributes
1058         // change integer field from 10 to 2
1059         $field = new xmldb_field('userid');
1060         $field->set_attributes(XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1061         $dbman->change_field_precision($table, $field);
1062         $columns = $DB->get_columns('test_table1');
1063         $this->assertEquals($columns['userid']->meta_type, 'I');
1064         //TODO: check the rest of attributes
1066         // change the column from integer (2) to integer (6) (forces change of type in some DBs)
1067         $field = new xmldb_field('userid');
1068         $field->set_attributes(XMLDB_TYPE_INTEGER, '6', XMLDB_UNSIGNED, null, null, null);
1069         $dbman->change_field_precision($table, $field);
1070         // column is integer 6 null default null now
1071         $columns = $DB->get_columns('test_table1');
1072         $this->assertEquals($columns['userid']->meta_type, 'I');
1073         //TODO: check the rest of attributes
1075         // insert one record with 6-digit field
1076         $record = new stdClass();
1077         $record->course = 10;
1078         $record->secondname  = 'third record';
1079         $record->intro  = 'third record';
1080         $record->userid = 123456;
1081         $DB->insert_record('test_table1', $record);
1082         // change integer field from 6 to 2, contents are bigger. must throw exception
1083         $field = new xmldb_field('userid');
1084         $field->set_attributes(XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1085         try {
1086             $dbman->change_field_precision($table, $field);
1087             $this->assertTrue(false);
1088         } catch (Exception $e) {
1089             $this->assertTrue($e instanceof ddl_change_structure_exception);
1090         }
1091         // No changes in field specs at all
1092         $columns = $DB->get_columns('test_table1');
1093         $this->assertEquals($columns['userid']->meta_type, 'I');
1094         //TODO: check the rest of attributes
1096         // change integer field from 10 to 3, in field used by index. must throw exception.
1097         $field = new xmldb_field('course');
1098         $field->set_attributes(XMLDB_TYPE_INTEGER, '3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1099         try {
1100             $dbman->change_field_precision($table, $field);
1101             $this->assertTrue(false);
1102         } catch (Exception $e) {
1103             $this->assertTrue($e instanceof ddl_dependency_exception);
1104         }
1105         // No changes in field specs at all
1106         $columns = $DB->get_columns('test_table1');
1107         $this->assertEquals($columns['course']->meta_type, 'I');
1108         //TODO: check the rest of attributes
1109     }
1111     public function testChangeFieldNullability() {
1112         $DB = $this->tdb; // do not use global $DB!
1113         $dbman = $this->tdb->get_manager();
1115         $table = new xmldb_table('test_table_cust0');
1116         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1117         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
1118         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1119         $dbman->create_table($table);
1121         $record = new stdClass();
1122         $record->name = NULL;
1124         try {
1125             $result = $DB->insert_record('test_table_cust0', $record, false);
1126         } catch (dml_exception $e) {
1127             $result = false;
1128         }
1129         $this->resetDebugging();
1130         $this->assertFalse($result);
1132         $field = new xmldb_field('name');
1133         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, null, null, null);
1134         $dbman->change_field_notnull($table, $field);
1136         $this->assertTrue($DB->insert_record('test_table_cust0', $record, false));
1138         // TODO: add some tests with existing data in table
1139         $DB->delete_records('test_table_cust0');
1141         $field = new xmldb_field('name');
1142         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
1143         $dbman->change_field_notnull($table, $field);
1145         try {
1146             $result = $DB->insert_record('test_table_cust0', $record, false);
1147         } catch (dml_exception $e) {
1148             $result = false;
1149         }
1150         $this->resetDebugging();
1151         $this->assertFalse($result);
1153         $dbman->drop_table($table);
1154     }
1156     public function testChangeFieldDefault() {
1157         $DB = $this->tdb; // do not use global $DB!
1158         $dbman = $this->tdb->get_manager();
1160         $table = new xmldb_table('test_table_cust0');
1161         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1162         $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1163         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1164         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1165         $dbman->create_table($table);
1167         $field = new xmldb_field('name');
1168         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle2');
1169         $dbman->change_field_default($table, $field);
1171         $record = new stdClass();
1172         $record->onenumber = 666;
1173         $id = $DB->insert_record('test_table_cust0', $record);
1175         $record = $DB->get_record('test_table_cust0', array('id'=>$id));
1176         $this->assertEquals($record->name, 'Moodle2');
1179         $field = new xmldb_field('onenumber');
1180         $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 666);
1181         $dbman->change_field_default($table, $field);
1183         $record = new stdClass();
1184         $record->name = 'something';
1185         $id = $DB->insert_record('test_table_cust0', $record);
1187         $record = $DB->get_record('test_table_cust0', array('id'=>$id));
1188         $this->assertEquals($record->onenumber, '666');
1190         $dbman->drop_table($table);
1191     }
1193     public function testAddUniqueIndex() {
1194         $DB = $this->tdb; // do not use global $DB!
1195         $dbman = $this->tdb->get_manager();
1197         $table = new xmldb_table('test_table_cust0');
1198         $table->add_field('id', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1199         $table->add_field('onenumber', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1200         $table->add_field('name', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, 'Moodle');
1201         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1202         $dbman->create_table($table);
1204         $record = new stdClass();
1205         $record->onenumber = 666;
1206         $record->name = 'something';
1207         $DB->insert_record('test_table_cust0', $record, false);
1209         $index = new xmldb_index('onenumber-name');
1210         $index->set_attributes(XMLDB_INDEX_UNIQUE, array('onenumber', 'name'));
1211         $dbman->add_index($table, $index);
1213         try {
1214             $result = $DB->insert_record('test_table_cust0', $record, false);
1215         } catch (dml_exception $e) {
1216             $result = false;
1217         }
1218         $this->resetDebugging();
1219         $this->assertFalse($result);
1221         $dbman->drop_table($table);
1222     }
1224     public function testAddNonUniqueIndex() {
1225         $dbman = $this->tdb->get_manager();
1227         $table = $this->create_deftable('test_table1');
1228         $index = new xmldb_index('secondname');
1229         $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1230         $dbman->add_index($table, $index);
1231         $this->assertTrue($dbman->index_exists($table, $index));
1232     }
1234     public function testFindIndexName() {
1235         $dbman = $this->tdb->get_manager();
1237         $table = $this->create_deftable('test_table1');
1238         $index = new xmldb_index('secondname');
1239         $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1240         $dbman->add_index($table, $index);
1242         //DBM Systems name their indices differently - do not test the actual index name
1243         $result = $dbman->find_index_name($table, $index);
1244         $this->assertTrue(!empty($result));
1246         $nonexistentindex = new xmldb_index('nonexistentindex');
1247         $nonexistentindex->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('name'));
1248         $this->assertFalse($dbman->find_index_name($table, $nonexistentindex));
1249     }
1251     public function testDropIndex() {
1252         $DB = $this->tdb; // do not use global $DB!
1254         $dbman = $this->tdb->get_manager();
1256         $table = $this->create_deftable('test_table1');
1257         $index = new xmldb_index('secondname');
1258         $index->set_attributes(XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1259         $dbman->add_index($table, $index);
1261         $dbman->drop_index($table, $index);
1262         $this->assertFalse($dbman->find_index_name($table, $index));
1264         // Test we are able to drop indexes having hyphens. MDL-22804
1265         // Create index with hyphens (by hand)
1266         $indexname = 'test-index-with-hyphens';
1267         switch ($DB->get_dbfamily()) {
1268             case 'mysql':
1269                 $indexname = '`' . $indexname . '`';
1270                 break;
1271             default:
1272                 $indexname = '"' . $indexname . '"';
1273         }
1274         $stmt = "CREATE INDEX {$indexname} ON {$DB->get_prefix()}test_table1 (course, name)";
1275         $DB->change_database_structure($stmt);
1276         $this->assertNotEmpty($dbman->find_index_name($table, $index));
1277         // Index created, let's drop it using db manager stuff
1278         $index = new xmldb_index('indexname', XMLDB_INDEX_NOTUNIQUE, array('course', 'name'));
1279         $dbman->drop_index($table, $index);
1280         $this->assertFalse($dbman->find_index_name($table, $index));
1281     }
1283     public function testAddUniqueKey() {
1284         $dbman = $this->tdb->get_manager();
1286         $table = $this->create_deftable('test_table1');
1287         $key = new xmldb_key('id-course-grade');
1288         $key->set_attributes(XMLDB_KEY_UNIQUE, array('id', 'course', 'grade'));
1289         $dbman->add_key($table, $key);
1291         // No easy way to test it, this just makes sure no errors are encountered.
1292         $this->assertTrue(true);
1293     }
1295     public function testAddForeignUniqueKey() {
1296         $dbman = $this->tdb->get_manager();
1298         $table = $this->create_deftable('test_table1');
1299         $this->create_deftable('test_table0');
1301         $key = new xmldb_key('course');
1302         $key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('id'));
1303         $dbman->add_key($table, $key);
1305         // No easy way to test it, this just makes sure no errors are encountered.
1306         $this->assertTrue(true);
1307     }
1309     public function testDropKey() {
1310         $dbman = $this->tdb->get_manager();
1312         $table = $this->create_deftable('test_table1');
1313         $this->create_deftable('test_table0');
1315         $key = new xmldb_key('course');
1316         $key->set_attributes(XMLDB_KEY_FOREIGN_UNIQUE, array('course'), 'test_table0', array('id'));
1317         $dbman->add_key($table, $key);
1319         $dbman->drop_key($table, $key);
1321         // No easy way to test it, this just makes sure no errors are encountered.
1322         $this->assertTrue(true);
1323     }
1325     public function testAddForeignKey() {
1326         $dbman = $this->tdb->get_manager();
1328         $table = $this->create_deftable('test_table1');
1329         $this->create_deftable('test_table0');
1331         $key = new xmldb_key('course');
1332         $key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'test_table0', array('id'));
1333         $dbman->add_key($table, $key);
1335         // No easy way to test it, this just makes sure no errors are encountered.
1336         $this->assertTrue(true);
1337     }
1339     public function testDropForeignKey() {
1340         $dbman = $this->tdb->get_manager();
1342         $table = $this->create_deftable('test_table1');
1343         $this->create_deftable('test_table0');
1345         $key = new xmldb_key('course');
1346         $key->set_attributes(XMLDB_KEY_FOREIGN, array('course'), 'test_table0', array('id'));
1347         $dbman->add_key($table, $key);
1349         $dbman->drop_key($table, $key);
1351         // No easy way to test it, this just makes sure no errors are encountered.
1352         $this->assertTrue(true);
1353     }
1355     public function testRenameField() {
1356         $DB = $this->tdb; // do not use global $DB!
1357         $dbman = $this->tdb->get_manager();
1359         $table = $this->create_deftable('test_table0');
1360         $field = new xmldb_field('type');
1361         $field->set_attributes(XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general', 'course');
1363         $dbman->rename_field($table, $field, 'newfieldname');
1365         $columns = $DB->get_columns('test_table0');
1367         $this->assertFalse(array_key_exists('type', $columns));
1368         $this->assertTrue(array_key_exists('newfieldname', $columns));
1369     }
1371     public function testIndexExists() {
1372         // Skipping: this is just a test of find_index_name
1373     }
1375     public function testFindKeyName() {
1376         $dbman = $this->tdb->get_manager();
1378         $table = $this->create_deftable('test_table0');
1379         $key = $table->getKey('primary');
1381         // With Mysql, the return value is actually "mdl_test_id_pk"
1382         $result = $dbman->find_key_name($table, $key);
1383         $this->assertTrue(!empty($result));
1384     }
1386     public function testDeleteTablesFromXmldbFile() {
1387         global $CFG;
1388         $dbman = $this->tdb->get_manager();
1390         $this->create_deftable('test_table1');
1392         $this->assertTrue($dbman->table_exists('test_table1'));
1394         // feed nonexistent file
1395         try {
1396             $dbman->delete_tables_from_xmldb_file('fpsoiudfposui');
1397             $this->assertTrue(false);
1398         } catch (Exception $e) {
1399             $this->resetDebugging();
1400             $this->assertTrue($e instanceof moodle_exception);
1401         }
1403         try {
1404             $dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
1405             $this->assertTrue(false);
1406         } catch (Exception $e) {
1407             $this->resetDebugging();
1408             $this->assertTrue($e instanceof moodle_exception);
1409         }
1411         // Check that the table has not been deleted from DB
1412         $this->assertTrue($dbman->table_exists('test_table1'));
1414         // Real and valid xml file
1415         //TODO: drop UNSINGED completely in Moodle 2.4
1416         $dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/xmldb_table.xml');
1418         // Check that the table has been deleted from DB
1419         $this->assertFalse($dbman->table_exists('test_table1'));
1420     }
1422     public function testInstallFromXmldbFile() {
1423         global $CFG;
1424         $dbman = $this->tdb->get_manager();
1426         // feed nonexistent file
1427         try {
1428             $dbman->install_from_xmldb_file('fpsoiudfposui');
1429             $this->assertTrue(false);
1430         } catch (Exception $e) {
1431             $this->resetDebugging();
1432             $this->assertTrue($e instanceof moodle_exception);
1433         }
1435         try {
1436             $dbman->install_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
1437             $this->assertTrue(false);
1438         } catch (Exception $e) {
1439             $this->resetDebugging();
1440             $this->assertTrue($e instanceof moodle_exception);
1441         }
1443         // Check that the table has not yet been created in DB
1444         $this->assertFalse($dbman->table_exists('test_table1'));
1446         // Real and valid xml file
1447         $dbman->install_from_xmldb_file(__DIR__ . '/fixtures/xmldb_table.xml');
1448         $this->assertTrue($dbman->table_exists('test_table1'));
1449     }
1451     public function test_temp_tables() {
1452         global $CFG;
1454         $DB = $this->tdb; // do not use global $DB!
1455         $dbman = $this->tdb->get_manager();
1457         // Create temp table0
1458         $table0 = $this->tables['test_table0'];
1459         $dbman->create_temp_table($table0);
1460         $this->assertTrue($dbman->table_exists('test_table0'));
1462         // Try to create temp table with same name, must throw exception
1463         $dupetable = $this->tables['test_table0'];
1464         try {
1465             $dbman->create_temp_table($dupetable);
1466             $this->assertTrue(false);
1467         } catch (Exception $e) {
1468             $this->assertTrue($e instanceof ddl_exception);
1469         }
1471         // Try to create table with same name, must throw exception
1472         $dupetable = $this->tables['test_table0'];
1473         try {
1474             $dbman->create_table($dupetable);
1475             $this->assertTrue(false);
1476         } catch (Exception $e) {
1477             $this->assertTrue($e instanceof ddl_exception);
1478         }
1480         // Create another temp table1
1481         $table1 = $this->tables['test_table1'];
1482         $dbman->create_temp_table($table1);
1483         $this->assertTrue($dbman->table_exists('test_table1'));
1485         // Get columns and perform some basic tests
1486         $columns = $DB->get_columns('test_table1');
1487         $this->assertEquals(count($columns), 11);
1488         $this->assertTrue($columns['name'] instanceof database_column_info);
1489         $this->assertEquals($columns['name']->max_length, 30);
1490         $this->assertTrue($columns['name']->has_default);
1491         $this->assertEquals($columns['name']->default_value, 'Moodle');
1493         // Insert some records
1494         $inserted = $this->fill_deftable('test_table1');
1495         $records = $DB->get_records('test_table1');
1496         $this->assertEquals(count($records), $inserted);
1497         $this->assertEquals($records[1]->course, $this->records['test_table1'][0]->course);
1498         $this->assertEquals($records[1]->secondname, $this->records['test_table1'][0]->secondname);
1499         $this->assertEquals($records[2]->intro, $this->records['test_table1'][1]->intro);
1501         // Drop table1
1502         $dbman->drop_table($table1);
1503         $this->assertFalse($dbman->table_exists('test_table1'));
1505         // Try to drop non-existing temp table, must throw exception
1506         $noetable = $this->tables['test_table1'];
1507         try {
1508             $dbman->drop_table($noetable);
1509             $this->assertTrue(false);
1510         } catch (Exception $e) {
1511             $this->assertTrue($e instanceof ddl_table_missing_exception);
1512         }
1514         // Fill/modify/delete a few table0 records
1515         // TODO: that's
1517         // Drop table0
1518         $dbman->drop_table($table0);
1519         $this->assertFalse($dbman->table_exists('test_table0'));
1521         // Create another temp table1
1522         $table1 = $this->tables['test_table1'];
1523         $dbman->create_temp_table($table1);
1524         $this->assertTrue($dbman->table_exists('test_table1'));
1526         // Make sure it can be dropped using deprecated drop_temp_table()
1527         $dbman->drop_temp_table($table1);
1528         $this->assertFalse($dbman->table_exists('test_table1'));
1529         $this->assertDebuggingCalled();
1530     }
1532     public function test_concurrent_temp_tables() {
1533         $DB = $this->tdb; // do not use global $DB!
1534         $dbman = $this->tdb->get_manager();
1536         // Define 2 records
1537         $record1 = (object)array(
1538             'course'     =>  1,
1539             'secondname' => '11 important',
1540             'intro'      => '111 important');
1541         $record2 = (object)array(
1542             'course'     =>  2,
1543             'secondname' => '22 important',
1544             'intro'      => '222 important');
1546         // Create temp table1 and insert 1 record (in DB)
1547         $table = $this->tables['test_table1'];
1548         $dbman->create_temp_table($table);
1549         $this->assertTrue($dbman->table_exists('test_table1'));
1550         $inserted = $DB->insert_record('test_table1', $record1);
1552         // Switch to new connection
1553         $cfg = $DB->export_dbconfig();
1554         if (!isset($cfg->dboptions)) {
1555             $cfg->dboptions = array();
1556         }
1557         $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
1558         $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
1559         $dbman2 = $DB2->get_manager();
1560         $this->assertFalse($dbman2->table_exists('test_table1')); // Temp table not exists in DB2
1562         // Create temp table1 and insert 1 record (in DB2)
1563         $table = $this->tables['test_table1'];
1564         $dbman2->create_temp_table($table);
1565         $this->assertTrue($dbman2->table_exists('test_table1'));
1566         $inserted = $DB2->insert_record('test_table1', $record2);
1568         $dbman2->drop_table($table); // Drop temp table before closing DB2
1569         $this->assertFalse($dbman2->table_exists('test_table1'));
1570         $DB2->dispose(); // Close DB2
1572         $this->assertTrue($dbman->table_exists('test_table1')); // Check table continues existing for DB
1573         $dbman->drop_table($table); // Drop temp table
1574         $this->assertFalse($dbman->table_exists('test_table1'));
1575     }
1577     public function test_reset_sequence() {
1578         $DB = $this->tdb;
1579         $dbman = $DB->get_manager();
1581         $table = new xmldb_table('testtable');
1582         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1583         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1584         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1586         // Drop if exists
1587         if ($dbman->table_exists($table)) {
1588             $dbman->drop_table($table);
1589         }
1590         $dbman->create_table($table);
1591         $tablename = $table->getName();
1592         $this->tables[$tablename] = $table;
1594         $record = (object)array('id'=>666, 'course'=>10);
1595         $DB->import_record('testtable', $record);
1596         $DB->delete_records('testtable'); // This delete performs one TRUNCATE
1598         $dbman->reset_sequence($table); // using xmldb object
1599         $this->assertEquals(1, $DB->insert_record('testtable', (object)array('course'=>13)));
1601         $record = (object)array('id'=>666, 'course'=>10);
1602         $DB->import_record('testtable', $record);
1603         $DB->delete_records('testtable', array()); // This delete performs one DELETE
1605         $dbman->reset_sequence($table); // using xmldb object
1606         $this->assertEquals(1, $DB->insert_record('testtable', (object)array('course'=>13)));
1608         $DB->import_record('testtable', $record);
1609         $dbman->reset_sequence($tablename); // using string
1610         $this->assertEquals(667, $DB->insert_record('testtable', (object)array('course'=>13)));
1612         $dbman->drop_table($table);
1613     }
1615     public function test_reserved_words() {
1616         $reserved = sql_generator::getAllReservedWords();
1617         $this->assertTrue(count($reserved) > 1);
1618     }
1620     public function test_index_hints() {
1621         $DB = $this->tdb;
1622         $dbman = $DB->get_manager();
1624         $table = new xmldb_table('testtable');
1625         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1626         $table->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1627         $table->add_field('path', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1628         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1629         $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'), array('xxxx,yyyy'));
1630         $table->add_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
1632         // Drop if exists
1633         if ($dbman->table_exists($table)) {
1634             $dbman->drop_table($table);
1635         }
1636         $dbman->create_table($table);
1637         $tablename = $table->getName();
1638         $this->tables[$tablename] = $table;
1640         $table = new xmldb_table('testtable');
1641         $index = new xmldb_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'), array('xxxx,yyyy'));
1642         $this->assertTrue($dbman->index_exists($table, $index));
1644         $table = new xmldb_table('testtable');
1645         $index = new xmldb_index('path', XMLDB_INDEX_NOTUNIQUE, array('path'), array('varchar_pattern_ops'));
1646         $this->assertTrue($dbman->index_exists($table, $index));
1648         // Try unique indexes too.
1649         $dbman->drop_table($this->tables[$tablename]);
1651         $table = new xmldb_table('testtable');
1652         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1653         $table->add_field('path', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1654         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1655         $table->add_index('path', XMLDB_INDEX_UNIQUE, array('path'), array('varchar_pattern_ops'));
1656         $dbman->create_table($table);
1657         $this->tables[$tablename] = $table;
1659         $table = new xmldb_table('testtable');
1660         $index = new xmldb_index('path', XMLDB_INDEX_UNIQUE, array('path'), array('varchar_pattern_ops'));
1661         $this->assertTrue($dbman->index_exists($table, $index));
1662     }
1664     public function test_index_max_bytes() {
1665         $DB = $this->tdb;
1666         $dbman = $DB->get_manager();
1668         $maxstr = '';
1669         for($i=0; $i<255; $i++) {
1670             $maxstr .= '言'; // random long string that should fix exactly the limit for one char column
1671         }
1673         $table = new xmldb_table('testtable');
1674         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1675         $table->add_field('name', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL, null);
1676         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1677         $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
1679         // Drop if exists
1680         if ($dbman->table_exists($table)) {
1681             $dbman->drop_table($table);
1682         }
1683         $dbman->create_table($table);
1684         $tablename = $table->getName();
1685         $this->tables[$tablename] = $table;
1687         $rec = new stdClass();
1688         $rec->name = $maxstr;
1690         $id = $DB->insert_record($tablename, $rec);
1691         $this->assertTrue(!empty($id));
1693         $rec = $DB->get_record($tablename, array('id'=>$id));
1694         $this->assertSame($rec->name, $maxstr);
1696         $dbman->drop_table($table);
1699         $table = new xmldb_table('testtable');
1700         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1701         $table->add_field('name', XMLDB_TYPE_CHAR, 255+1, null, XMLDB_NOTNULL, null);
1702         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1703         $table->add_index('name', XMLDB_INDEX_NOTUNIQUE, array('name'));
1705         try {
1706             $dbman->create_table($table);
1707             $this->assertTrue(false);
1708         } catch (Exception $e) {
1709             $this->assertTrue($e instanceof coding_exception);
1710         }
1711     }
1713     public function test_index_composed_max_bytes() {
1714         $DB = $this->tdb;
1715         $dbman = $DB->get_manager();
1717         $maxstr = '';
1718         for($i=0; $i<200; $i++) {
1719             $maxstr .= '言';
1720         }
1721         $reststr = '';
1722         for($i=0; $i<133; $i++) {
1723             $reststr .= '言';
1724         }
1726         $table = new xmldb_table('testtable');
1727         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1728         $table->add_field('name1', XMLDB_TYPE_CHAR, 200, null, XMLDB_NOTNULL, null);
1729         $table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
1730         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1731         $table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1','name2'));
1733         // Drop if exists
1734         if ($dbman->table_exists($table)) {
1735             $dbman->drop_table($table);
1736         }
1737         $dbman->create_table($table);
1738         $tablename = $table->getName();
1739         $this->tables[$tablename] = $table;
1741         $rec = new stdClass();
1742         $rec->name1 = $maxstr;
1743         $rec->name2 = $reststr;
1745         $id = $DB->insert_record($tablename, $rec);
1746         $this->assertTrue(!empty($id));
1748         $rec = $DB->get_record($tablename, array('id'=>$id));
1749         $this->assertSame($rec->name1, $maxstr);
1750         $this->assertSame($rec->name2, $reststr);
1753         $table = new xmldb_table('testtable');
1754         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1755         $table->add_field('name1', XMLDB_TYPE_CHAR, 201, null, XMLDB_NOTNULL, null);
1756         $table->add_field('name2', XMLDB_TYPE_CHAR, 133, null, XMLDB_NOTNULL, null);
1757         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1758         $table->add_index('name1-name2', XMLDB_INDEX_NOTUNIQUE, array('name1','name2'));
1760         // Drop if exists
1761         if ($dbman->table_exists($table)) {
1762             $dbman->drop_table($table);
1763         }
1765         try {
1766             $dbman->create_table($table);
1767             $this->assertTrue(false);
1768         } catch (Exception $e) {
1769             $this->assertTrue($e instanceof coding_exception);
1770         }
1771     }
1773     public function test_char_size_limit() {
1774         $DB = $this->tdb;
1775         $dbman = $DB->get_manager();
1777         $table = new xmldb_table('testtable');
1778         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1779         $table->add_field('name', XMLDB_TYPE_CHAR, xmldb_field::CHAR_MAX_LENGTH, null, XMLDB_NOTNULL, null);
1780         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1782         // Drop if exists
1783         if ($dbman->table_exists($table)) {
1784             $dbman->drop_table($table);
1785         }
1786         $dbman->create_table($table);
1787         $tablename = $table->getName();
1788         $this->tables[$tablename] = $table;
1790         // this has to work in all DBs
1791         $maxstr = '';
1792         for($i=0; $i<xmldb_field::CHAR_MAX_LENGTH; $i++) {
1793             $maxstr .= 'a'; // ascii only
1794         }
1796         $rec = new stdClass();
1797         $rec->name = $maxstr;
1799         $id = $DB->insert_record($tablename, $rec);
1800         $this->assertTrue(!empty($id));
1802         $rec = $DB->get_record($tablename, array('id'=>$id));
1803         $this->assertSame($rec->name, $maxstr);
1806         // Following test is supposed to fail in oracle
1807         $maxstr = '';
1808         for($i=0; $i<xmldb_field::CHAR_MAX_LENGTH; $i++) {
1809             $maxstr .= '言'; // random long string that should fix exactly the limit for one char column
1810         }
1812         $rec = new stdClass();
1813         $rec->name = $maxstr;
1815         try {
1816             $id = $DB->insert_record($tablename, $rec);
1817             $this->assertTrue(!empty($id));
1819             $rec = $DB->get_record($tablename, array('id'=>$id));
1820             $this->assertSame($rec->name, $maxstr);
1821         } catch (dml_exception $e) {
1822             if ($DB->get_dbfamily() === 'oracle') {
1823                 $this->fail('Oracle does not support text fields larger than 4000 bytes, this is not a big problem for mostly ascii based languages');
1824             } else {
1825                 throw $e;
1826             }
1827         }
1830         $table = new xmldb_table('testtable');
1831         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1832         $table->add_field('name', XMLDB_TYPE_CHAR, xmldb_field::CHAR_MAX_LENGTH+1, null, XMLDB_NOTNULL, null);
1833         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1835         // Drop if exists
1836         if ($dbman->table_exists($table)) {
1837             $dbman->drop_table($table);
1838         }
1839         $tablename = $table->getName();
1840         $this->tables[$tablename] = $table;
1842         try {
1843             $dbman->create_table($table);
1844             $this->assertTrue(false);
1845         } catch (Exception $e) {
1846             $this->assertTrue($e instanceof coding_exception);
1847         }
1848     }
1850     // Following methods are not supported == Do not test
1851     /*
1852         public function testRenameIndex() {
1853             // unsupported!
1854             $dbman = $this->tdb->get_manager();
1856             $table = $this->create_deftable('test_table0');
1857             $index = new xmldb_index('course');
1858             $index->set_attributes(XMLDB_INDEX_UNIQUE, array('course'));
1860             $this->assertTrue($dbman->rename_index($table, $index, 'newindexname'));
1861         }
1863         public function testRenameKey() {
1864             //unsupported
1865              $dbman = $this->tdb->get_manager();
1867             $table = $this->create_deftable('test_table0');
1868             $key = new xmldb_key('course');
1869             $key->set_attributes(XMLDB_KEY_UNIQUE, array('course'));
1871             $this->assertTrue($dbman->rename_key($table, $key, 'newkeyname'));
1872         }
1873     */