29c010bc60c2387aec9e67e7e05d2b00a4850989
[moodle.git] / lib / dml / simpletest / testdml.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package    core
20  * @subpackage dml
21  * @copyright  2008 Petr Skoda (http://skodak.org)
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 class dml_test extends UnitTestCase {
28     private $tables = array();
29     private $tdb;
30     private $data;
31     public  static $includecoverage = array('lib/dml');
32     public  static $excludecoverage = array('lib/dml/simpletest');
34     protected $olddebug;
35     protected $olddisplay;
37     function setUp() {
38         global $DB, $UNITTEST;
40         if (isset($UNITTEST->func_test_db)) {
41             $this->tdb = $UNITTEST->func_test_db;
42         } else {
43             $this->tdb = $DB;
44         }
45     }
47     function tearDown() {
48         $dbman = $this->tdb->get_manager();
50         foreach ($this->tables as $tablename) {
51             if ($dbman->table_exists($tablename)) {
52                 $table = new xmldb_table($tablename);
53                 $dbman->drop_table($table);
54             }
55         }
56         $this->tables = array();
57     }
59     /**
60      * Get a xmldb_table object for testing, deleting any existing table
61      * of the same name, for example if one was left over from a previous test
62      * run that crashed.
63      *
64      * @param database_manager $dbman the database_manager to use.
65      * @param string $suffix table name suffix, use if you need more test tables
66      * @return xmldb_table the table object.
67      */
68     private function get_test_table($suffix = '') {
69         $dbman = $this->tdb->get_manager();
71         $tablename = "unit_table";
72         if ($suffix !== '') {
73             $tablename .= $suffix;
74         }
76         $table = new xmldb_table($tablename);
77         if ($dbman->table_exists($table)) {
78             $dbman->drop_table($table);
79         }
80         $table->setComment("This is a test'n drop table. You can drop it safely");
81         $this->tables[$tablename] = $tablename;
82         return new xmldb_table($tablename);
83     }
85     protected function enable_debugging() {
86         global $CFG;
88         $this->olddebug   = $CFG->debug;       // Save current debug settings
89         $this->olddisplay = $CFG->debugdisplay;
90         $CFG->debug = DEBUG_DEVELOPER;
91         $CFG->debugdisplay = true;
92         ob_start(); // hide debug warning
94     }
96     protected function get_debugging() {
97         global $CFG;
99         $debuginfo = ob_get_contents();
100         ob_end_clean();
101         $CFG->debug = $this->olddebug;         // Restore original debug settings
102         $CFG->debugdisplay = $this->olddisplay;
104         return $debuginfo;
105     }
107     // NOTE: please keep order of test methods here matching the order of moodle_database class methods
109     function test_diagnose() {
110         $DB = $this->tdb;
111         $result = $DB->diagnose();
112         $this->assertNull($result, 'Database self diagnostics failed %s');
113     }
115     function test_get_server_info() {
116         $DB = $this->tdb;
117         $result = $DB->get_server_info();
118         $this->assertTrue(is_array($result));
119         $this->assertTrue(array_key_exists('description', $result));
120         $this->assertTrue(array_key_exists('version', $result));
121     }
123     public function test_get_in_or_equal() {
124         $DB = $this->tdb;
126         // SQL_PARAMS_QM - IN or =
128         // Correct usage of multiple values
129         $in_values = array('value1', 'value2', '3', 4, null, false, true);
130         list($usql, $params) = $DB->get_in_or_equal($in_values);
131         $this->assertEqual('IN ('.implode(',',array_fill(0, count($in_values), '?')).')', $usql);
132         $this->assertEqual(count($in_values), count($params));
133         foreach ($params as $key => $value) {
134             $this->assertIdentical($in_values[$key], $value);
135         }
137         // Correct usage of single value (in an array)
138         $in_values = array('value1');
139         list($usql, $params) = $DB->get_in_or_equal($in_values);
140         $this->assertEqual("= ?", $usql);
141         $this->assertEqual(1, count($params));
142         $this->assertEqual($in_values[0], $params[0]);
144         // Correct usage of single value
145         $in_value = 'value1';
146         list($usql, $params) = $DB->get_in_or_equal($in_values);
147         $this->assertEqual("= ?", $usql);
148         $this->assertEqual(1, count($params));
149         $this->assertEqual($in_value, $params[0]);
151         // SQL_PARAMS_QM - NOT IN or <>
153         // Correct usage of multiple values
154         $in_values = array('value1', 'value2', 'value3', 'value4');
155         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
156         $this->assertEqual("NOT IN (?,?,?,?)", $usql);
157         $this->assertEqual(4, count($params));
158         foreach ($params as $key => $value) {
159             $this->assertEqual($in_values[$key], $value);
160         }
162         // Correct usage of single value (in array()
163         $in_values = array('value1');
164         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
165         $this->assertEqual("<> ?", $usql);
166         $this->assertEqual(1, count($params));
167         $this->assertEqual($in_values[0], $params[0]);
169         // Correct usage of single value
170         $in_value = 'value1';
171         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
172         $this->assertEqual("<> ?", $usql);
173         $this->assertEqual(1, count($params));
174         $this->assertEqual($in_value, $params[0]);
176         // SQL_PARAMS_NAMED - IN or =
178         // Correct usage of multiple values
179         $in_values = array('value1', 'value2', 'value3', 'value4');
180         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', true);
181         $this->assertEqual("IN (:param01,:param02,:param03,:param04)", $usql);
182         $this->assertEqual(4, count($params));
183         reset($in_values);
184         foreach ($params as $key => $value) {
185             $this->assertEqual(current($in_values), $value);
186             next($in_values);
187         }
189         // Correct usage of single values (in array)
190         $in_values = array('value1');
191         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', true);
192         $this->assertEqual("= :param01", $usql);
193         $this->assertEqual(1, count($params));
194         $this->assertEqual($in_values[0], $params['param01']);
196         // Correct usage of single value
197         $in_value = 'value1';
198         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', true);
199         $this->assertEqual("= :param01", $usql);
200         $this->assertEqual(1, count($params));
201         $this->assertEqual($in_value, $params['param01']);
203         // SQL_PARAMS_NAMED - NOT IN or <>
205         // Correct usage of multiple values
206         $in_values = array('value1', 'value2', 'value3', 'value4');
207         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false);
208         $this->assertEqual("NOT IN (:param01,:param02,:param03,:param04)", $usql);
209         $this->assertEqual(4, count($params));
210         reset($in_values);
211         foreach ($params as $key => $value) {
212             $this->assertEqual(current($in_values), $value);
213             next($in_values);
214         }
216         // Correct usage of single values (in array)
217         $in_values = array('value1');
218         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false);
219         $this->assertEqual("<> :param01", $usql);
220         $this->assertEqual(1, count($params));
221         $this->assertEqual($in_values[0], $params['param01']);
223         // Correct usage of single value
224         $in_value = 'value1';
225         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false);
226         $this->assertEqual("<> :param01", $usql);
227         $this->assertEqual(1, count($params));
228         $this->assertEqual($in_value, $params['param01']);
230     }
232     public function test_fix_table_names() {
233         $DB = new moodle_database_for_testing();
234         $prefix = $DB->get_prefix();
236         // Simple placeholder
237         $placeholder = "{user_123}";
238         $this->assertIdentical($prefix."user_123", $DB->public_fix_table_names($placeholder));
240         // wrong table name
241         $placeholder = "{user-a}";
242         $this->assertIdentical($placeholder, $DB->public_fix_table_names($placeholder));
244         // wrong table name
245         $placeholder = "{123user}";
246         $this->assertIdentical($placeholder, $DB->public_fix_table_names($placeholder));
248         // Full SQL
249         $sql = "SELECT * FROM {user}, {funny_table_name}, {mdl_stupid_table} WHERE {user}.id = {funny_table_name}.userid";
250         $expected = "SELECT * FROM {$prefix}user, {$prefix}funny_table_name, {$prefix}mdl_stupid_table WHERE {$prefix}user.id = {$prefix}funny_table_name.userid";
251         $this->assertIdentical($expected, $DB->public_fix_table_names($sql));
252     }
254     function test_fix_sql_params() {
255         $DB = $this->tdb;
257         $table = $this->get_test_table();
258         $tablename = $table->getName();
260         // Correct table placeholder substitution
261         $sql = "SELECT * FROM {{$tablename}}";
262         $sqlarray = $DB->fix_sql_params($sql);
263         $this->assertEqual("SELECT * FROM {$DB->get_prefix()}".$tablename, $sqlarray[0]);
265         // Conversions of all param types
266         $sql = array();
267         $sql[SQL_PARAMS_NAMED]  = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = :param1, course = :param2";
268         $sql[SQL_PARAMS_QM]     = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = ?, course = ?";
269         $sql[SQL_PARAMS_DOLLAR] = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = \$1, course = \$2";
271         $params = array();
272         $params[SQL_PARAMS_NAMED]  = array('param1'=>'first record', 'param2'=>1);
273         $params[SQL_PARAMS_QM]     = array('first record', 1);
274         $params[SQL_PARAMS_DOLLAR] = array('first record', 1);
276         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_NAMED], $params[SQL_PARAMS_NAMED]);
277         $this->assertIdentical($rsql, $sql[$rtype]);
278         $this->assertIdentical($rparams, $params[$rtype]);
280         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_QM], $params[SQL_PARAMS_QM]);
281         $this->assertIdentical($rsql, $sql[$rtype]);
282         $this->assertIdentical($rparams, $params[$rtype]);
284         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_DOLLAR], $params[SQL_PARAMS_DOLLAR]);
285         $this->assertIdentical($rsql, $sql[$rtype]);
286         $this->assertIdentical($rparams, $params[$rtype]);
289         // Malformed table placeholder
290         $sql = "SELECT * FROM [testtable]";
291         $sqlarray = $DB->fix_sql_params($sql);
292         $this->assertIdentical($sql, $sqlarray[0]);
295         // Mixed param types (colon and dollar)
296         $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1";
297         $params = array('param1' => 'record1', 'param2' => 3);
298         try {
299             $DB->fix_sql_params($sql, $params);
300             $this->fail("Expecting an exception, none occurred");
301         } catch (Exception $e) {
302             $this->assertTrue($e instanceof dml_exception);
303         }
305         // Mixed param types (question and dollar)
306         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1";
307         $params = array('param1' => 'record2', 'param2' => 5);
308         try {
309             $DB->fix_sql_params($sql, $params);
310             $this->fail("Expecting an exception, none occurred");
311         } catch (Exception $e) {
312             $this->assertTrue($e instanceof dml_exception);
313         }
315         // Too few params in sql
316         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?";
317         $params = array('record2', 3);
318         try {
319             $DB->fix_sql_params($sql, $params);
320             $this->fail("Expecting an exception, none occurred");
321         } catch (Exception $e) {
322             $this->assertTrue($e instanceof dml_exception);
323         }
325         // Too many params in array: no error, just use what is necessary
326         $params[] = 1;
327         $params[] = time();
328         try {
329             $sqlarray = $DB->fix_sql_params($sql, $params);
330             $this->assertTrue(is_array($sqlarray));
331             $this->assertEqual(count($sqlarray[1]), 3);
332         } catch (Exception $e) {
333             $this->fail("Unexpected ".get_class($e)." exception");
334         }
336         // Named params missing from array
337         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
338         $params = array('wrongname' => 'record1', 'course' => 1);
339         try {
340             $DB->fix_sql_params($sql, $params);
341             $this->fail("Expecting an exception, none occurred");
342         } catch (Exception $e) {
343             $this->assertTrue($e instanceof dml_exception);
344         }
346         // Duplicate named param in query - this is a very important feature!!
347         // it helps with debugging of sloppy code
348         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name";
349         $params = array('name' => 'record2', 'course' => 3);
350         try {
351             $DB->fix_sql_params($sql, $params);
352             $this->fail("Expecting an exception, none occurred");
353         } catch (Exception $e) {
354             $this->assertTrue($e instanceof dml_exception);
355         }
357         // Extra named param is ignored
358         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
359         $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha');
360         try {
361             $sqlarray = $DB->fix_sql_params($sql, $params);
362             $this->assertTrue(is_array($sqlarray));
363             $this->assertEqual(count($sqlarray[1]), 2);
364         } catch (Exception $e) {
365             $this->fail("Unexpected ".get_class($e)." exception");
366         }
368         // Booleans in NAMED params are casting to 1/0 int
369         $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?";
370         $params = array(true, false);
371         list($sql, $params) = $DB->fix_sql_params($sql, $params);
372         $this->assertTrue(reset($params) === 1);
373         $this->assertTrue(next($params) === 0);
375         // Booleans in QM params are casting to 1/0 int
376         $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2";
377         $params = array('course1' => true, 'course2' => false);
378         list($sql, $params) = $DB->fix_sql_params($sql, $params);
379         $this->assertTrue(reset($params) === 1);
380         $this->assertTrue(next($params) === 0);
382         // Booleans in DOLLAR params are casting to 1/0 int
383         $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2";
384         $params = array(true, false);
385         list($sql, $params) = $DB->fix_sql_params($sql, $params);
386         $this->assertTrue(reset($params) === 1);
387         $this->assertTrue(next($params) === 0);
389         // No data types are touched except bool
390         $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)";
391         $inparams = array('abc', 'ABC', NULL, '1', 1, 1.4);
392         list($sql, $params) = $DB->fix_sql_params($sql, $inparams);
393         $this->assertIdentical(array_values($params), array_values($inparams));
394     }
396     public function test_get_tables() {
397         $DB = $this->tdb;
398         $dbman = $this->tdb->get_manager();
400         // Need to test with multiple DBs
401         $table = $this->get_test_table();
402         $tablename = $table->getName();
404         $original_count = count($DB->get_tables());
406         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
407         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
409         $dbman->create_table($table);
410         $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
412         $dbman->drop_table($table);
413         $this->assertTrue(count($DB->get_tables()) == $original_count);
414     }
416     public function test_get_indexes() {
417         $DB = $this->tdb;
418         $dbman = $this->tdb->get_manager();
420         $table = $this->get_test_table();
421         $tablename = $table->getName();
423         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
424         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
425         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
426         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
427         $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
428         $dbman->create_table($table);
430         $indices = $DB->get_indexes($tablename);
431         $this->assertTrue(is_array($indices));
432         $this->assertEqual(count($indices), 2);
433         // we do not care about index names for now
434         $first = array_shift($indices);
435         $second = array_shift($indices);
436         if (count($first['columns']) == 2) {
437             $composed = $first;
438             $single   = $second;
439         } else {
440             $composed = $second;
441             $single   = $first;
442         }
443         $this->assertFalse($single['unique']);
444         $this->assertTrue($composed['unique']);
445         $this->assertEqual(1, count($single['columns']));
446         $this->assertEqual(2, count($composed['columns']));
447         $this->assertEqual('course', $single['columns'][0]);
448         $this->assertEqual('course', $composed['columns'][0]);
449         $this->assertEqual('id', $composed['columns'][1]);
450     }
452     public function test_get_columns() {
453         $DB = $this->tdb;
454         $dbman = $this->tdb->get_manager();
456         $table = $this->get_test_table();
457         $tablename = $table->getName();
459         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
460         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
461         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
462         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
463         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
464         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
465         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
466         $dbman->create_table($table);
468         $columns = $DB->get_columns($tablename);
469         $this->assertTrue(is_array($columns));
471         $fields = $table->getFields();
472         $this->assertEqual(count($columns), count($fields));
474         $field = $columns['id'];
475         $this->assertEqual('R', $field->meta_type);
476         $this->assertTrue($field->auto_increment);
477         $this->assertTrue($field->unique);
479         $field = $columns['course'];
480         $this->assertEqual('I', $field->meta_type);
481         $this->assertFalse($field->auto_increment);
482         $this->assertTrue($field->has_default);
483         $this->assertEqual(0, $field->default_value);
484         $this->assertTrue($field->not_null);
486         $field = $columns['name'];
487         $this->assertEqual('C', $field->meta_type);
488         $this->assertFalse($field->auto_increment);
489         $this->assertTrue($field->has_default);
490         $this->assertIdentical('lala', $field->default_value);
491         $this->assertFalse($field->not_null);
493         $field = $columns['description'];
494         $this->assertEqual('X', $field->meta_type);
495         $this->assertFalse($field->auto_increment);
496         $this->assertFalse($field->has_default);
497         $this->assertIdentical(null, $field->default_value);
498         $this->assertFalse($field->not_null);
500         $field = $columns['enumfield'];
501         $this->assertEqual('C', $field->meta_type);
502         $this->assertFalse($field->auto_increment);
503         $this->assertIdentical('test2', $field->default_value);
504         $this->assertTrue($field->not_null);
506         $field = $columns['onenum'];
507         $this->assertEqual('N', $field->meta_type);
508         $this->assertFalse($field->auto_increment);
509         $this->assertTrue($field->has_default);
510         $this->assertEqual(200.0, $field->default_value);
511         $this->assertFalse($field->not_null);
513         for ($i = 0; $i < count($columns); $i++) {
514             if ($i == 0) {
515                 $next_column = reset($columns);
516                 $next_field  = reset($fields);
517             } else {
518                 $next_column = next($columns);
519                 $next_field  = next($fields);
520             }
522             $this->assertEqual($next_column->name, $next_field->name);
523         }
524     }
526     public function test_get_manager() {
527         $DB = $this->tdb;
528         $dbman = $this->tdb->get_manager();
530         $this->assertTrue($dbman instanceof database_manager);
531     }
533     public function test_setup_is_unicodedb() {
534         $DB = $this->tdb;
535         $this->assertTrue($DB->setup_is_unicodedb());
536     }
538     public function test_set_debug() { //tests get_debug() too
539         $DB = $this->tdb;
540         $dbman = $this->tdb->get_manager();
542         $table = $this->get_test_table();
543         $tablename = $table->getName();
545         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
546         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
547         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
548         $dbman->create_table($table);
550         $sql = "SELECT * FROM {{$tablename}}";
552         $prevdebug = $DB->get_debug();
554         ob_start();
555         $DB->set_debug(true);
556         $this->assertTrue($DB->get_debug());
557         $DB->execute($sql);
558         $DB->set_debug(false);
559         $this->assertFalse($DB->get_debug());
560         $debuginfo = ob_get_contents();
561         ob_end_clean();
562         $this->assertFalse($debuginfo === '');
564         ob_start();
565         $DB->execute($sql);
566         $debuginfo = ob_get_contents();
567         ob_end_clean();
568         $this->assertTrue($debuginfo === '');
570         $DB->set_debug($prevdebug);
571     }
573     public function test_execute() {
574         $DB = $this->tdb;
575         $dbman = $this->tdb->get_manager();
577         $table1 = $this->get_test_table('1');
578         $tablename1 = $table1->getName();
579         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
580         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
581         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
582         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
583         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
584         $dbman->create_table($table1);
586         $table2 = $this->get_test_table('2');
587         $tablename2 = $table2->getName();
588         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
589         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
590         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
591         $dbman->create_table($table2);
593         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
594         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
595         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
596         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
598         // select results are ignored
599         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
600         $this->assertTrue($DB->execute($sql, array('course'=>3)));
602         // throw exception on error
603         $sql = "XXUPDATE SET XSSD";
604         try {
605             $DB->execute($sql);
606             $this->fail("Expecting an exception, none occurred");
607         } catch (Exception $e) {
608             $this->assertTrue($e instanceof dml_write_exception);
609         }
611         // update records
612         $sql = "UPDATE {{$tablename1}}
613                    SET course = 6
614                  WHERE course = ?";
615         $this->assertTrue($DB->execute($sql, array('3')));
616         $this->assertEqual($DB->count_records($tablename1, array('course' => 6)), 2);
618         // insert from one into second table
619         $sql = "INSERT INTO {{$tablename2}} (course)
621                 SELECT course
622                   FROM {{$tablename1}}";
623         $this->assertTrue($DB->execute($sql));
624         $this->assertEqual($DB->count_records($tablename2), 4);
625     }
627     public function test_get_recordset() {
628         $DB = $this->tdb;
629         $dbman = $DB->get_manager();
631         $table = $this->get_test_table();
632         $tablename = $table->getName();
634         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
635         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
636         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
637         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
638         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
639         $dbman->create_table($table);
641         $data = array(array('id' => 1, 'course' => 3, 'name' => 'record1'),
642                       array('id' => 2, 'course' => 3, 'name' => 'record2'),
643                       array('id' => 3, 'course' => 5, 'name' => 'record3'));
645         foreach ($data as $record) {
646             $DB->insert_record($tablename, $record);
647         }
649         // standard recordset iteration
650         $rs = $DB->get_recordset($tablename);
651         $this->assertTrue($rs instanceof moodle_recordset);
652         reset($data);
653         foreach($rs as $record) {
654             $data_record = current($data);
655             foreach ($record as $k => $v) {
656                 $this->assertEqual($data_record[$k], $v);
657             }
658             next($data);
659         }
660         $rs->close();
662         // iterator style usage
663         $rs = $DB->get_recordset($tablename);
664         $this->assertTrue($rs instanceof moodle_recordset);
665         reset($data);
666         while ($rs->valid()) {
667             $record = $rs->current();
668             $data_record = current($data);
669             foreach ($record as $k => $v) {
670                 $this->assertEqual($data_record[$k], $v);
671             }
672             next($data);
673             $rs->next();
674         }
675         $rs->close();
677         // make sure rewind is ignored
678         $rs = $DB->get_recordset($tablename);
679         $this->assertTrue($rs instanceof moodle_recordset);
680         reset($data);
681         $i = 0;
682         foreach($rs as $record) {
683             $i++;
684             $rs->rewind();
685             if ($i > 10) {
686                 $this->fail('revind not ignored in recordsets');
687                 break;
688             }
689             $data_record = current($data);
690             foreach ($record as $k => $v) {
691                 $this->assertEqual($data_record[$k], $v);
692             }
693             next($data);
694         }
695         $rs->close();
697         // notes:
698         //  * limits are tested in test_get_recordset_sql()
699         //  * where_clause() is used internally and is tested in test_get_records()
700     }
702     public function test_get_recordset_iterator_keys() {
703         $DB = $this->tdb;
704         $dbman = $DB->get_manager();
706         $table = $this->get_test_table();
707         $tablename = $table->getName();
709         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
710         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
711         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
712         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
713         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
714         $dbman->create_table($table);
716         $data = array(array('id'=> 1, 'course' => 3, 'name' => 'record1'),
717                       array('id'=> 2, 'course' => 3, 'name' => 'record2'),
718                       array('id'=> 3, 'course' => 5, 'name' => 'record3'));
719         foreach ($data as $record) {
720             $DB->insert_record($tablename, $record);
721         }
723         // Test repeated numeric keys are returned ok
724         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
726         reset($data);
727         $count = 0;
728         foreach($rs as $key => $record) {
729             $data_record = current($data);
730             $this->assertEqual($data_record['course'], $key);
731             next($data);
732             $count++;
733         }
734         $rs->close();
735         $this->assertEqual($count, 3);
737         // Test string keys are returned ok
738         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
740         reset($data);
741         $count = 0;
742         foreach($rs as $key => $record) {
743             $data_record = current($data);
744             $this->assertEqual($data_record['name'], $key);
745             next($data);
746             $count++;
747         }
748         $rs->close();
749         $this->assertEqual($count, 3);
751         // Test numeric not starting in 1 keys are returned ok
752         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
754         $data = array_reverse($data);
755         reset($data);
756         $count = 0;
757         foreach($rs as $key => $record) {
758             $data_record = current($data);
759             $this->assertEqual($data_record['id'], $key);
760             next($data);
761             $count++;
762         }
763         $rs->close();
764         $this->assertEqual($count, 3);
765     }
767     public function test_get_recordset_list() {
768         $DB = $this->tdb;
769         $dbman = $DB->get_manager();
771         $table = $this->get_test_table();
772         $tablename = $table->getName();
774         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
775         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
776         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
777         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
778         $dbman->create_table($table);
780         $DB->insert_record($tablename, array('course' => 3));
781         $DB->insert_record($tablename, array('course' => 3));
782         $DB->insert_record($tablename, array('course' => 5));
783         $DB->insert_record($tablename, array('course' => 2));
785         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
787         $counter = 0;
788         foreach ($rs as $record) {
789             $counter++;
790         }
791         $this->assertEqual(3, $counter);
792         $rs->close();
794         $rs = $DB->get_recordset_list($tablename, 'course',array()); /// Must return 0 rows without conditions. MDL-17645
796         $counter = 0;
797         foreach ($rs as $record) {
798             $counter++;
799         }
800         $rs->close();
801         $this->assertEqual(0, $counter);
803         // notes:
804         //  * limits are tested in test_get_recordset_sql()
805         //  * where_clause() is used internally and is tested in test_get_records()
806     }
808     public function test_get_recordset_select() {
809         $DB = $this->tdb;
810         $dbman = $DB->get_manager();
812         $table = $this->get_test_table();
813         $tablename = $table->getName();
815         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
816         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
817         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
818         $dbman->create_table($table);
820         $DB->insert_record($tablename, array('course' => 3));
821         $DB->insert_record($tablename, array('course' => 3));
822         $DB->insert_record($tablename, array('course' => 5));
823         $DB->insert_record($tablename, array('course' => 2));
825         $rs = $DB->get_recordset_select($tablename, '');
826         $counter = 0;
827         foreach ($rs as $record) {
828             $counter++;
829         }
830         $rs->close();
831         $this->assertEqual(4, $counter);
833         $this->assertTrue($rs = $DB->get_recordset_select($tablename, 'course = 3'));
834         $counter = 0;
835         foreach ($rs as $record) {
836             $counter++;
837         }
838         $rs->close();
839         $this->assertEqual(2, $counter);
841         // notes:
842         //  * limits are tested in test_get_recordset_sql()
843     }
845     public function test_get_recordset_sql() {
846         $DB = $this->tdb;
847         $dbman = $DB->get_manager();
849         $table = $this->get_test_table();
850         $tablename = $table->getName();
852         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
853         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
854         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
855         $dbman->create_table($table);
857         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
858         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
859         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
860         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
861         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
862         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
863         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
865         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
866         $counter = 0;
867         foreach ($rs as $record) {
868             $counter++;
869         }
870         $rs->close();
871         $this->assertEqual(2, $counter);
873         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
874         // only limitfrom = skips that number of records
875         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
876         $records = array();
877         foreach($rs as $key => $record) {
878             $records[$key] = $record;
879         }
880         $rs->close();
881         $this->assertEqual(5, count($records));
882         $this->assertEqual($inskey3, reset($records)->id);
883         $this->assertEqual($inskey7, end($records)->id);
885         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
886     }
888     public function test_get_records() {
889         $DB = $this->tdb;
890         $dbman = $DB->get_manager();
892         $table = $this->get_test_table();
893         $tablename = $table->getName();
895         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
896         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
897         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
898         $dbman->create_table($table);
900         $DB->insert_record($tablename, array('course' => 3));
901         $DB->insert_record($tablename, array('course' => 3));
902         $DB->insert_record($tablename, array('course' => 5));
903         $DB->insert_record($tablename, array('course' => 2));
905         // All records
906         $records = $DB->get_records($tablename);
907         $this->assertEqual(4, count($records));
908         $this->assertEqual(3, $records[1]->course);
909         $this->assertEqual(3, $records[2]->course);
910         $this->assertEqual(5, $records[3]->course);
911         $this->assertEqual(2, $records[4]->course);
913         // Records matching certain conditions
914         $records = $DB->get_records($tablename, array('course' => 3));
915         $this->assertEqual(2, count($records));
916         $this->assertEqual(3, $records[1]->course);
917         $this->assertEqual(3, $records[2]->course);
919         // All records sorted by course
920         $records = $DB->get_records($tablename, null, 'course');
921         $this->assertEqual(4, count($records));
922         $current_record = reset($records);
923         $this->assertEqual(4, $current_record->id);
924         $current_record = next($records);
925         $this->assertEqual(1, $current_record->id);
926         $current_record = next($records);
927         $this->assertEqual(2, $current_record->id);
928         $current_record = next($records);
929         $this->assertEqual(3, $current_record->id);
931         // All records, but get only one field
932         $records = $DB->get_records($tablename, null, '', 'id');
933         $this->assertFalse(isset($records[1]->course));
934         $this->assertTrue(isset($records[1]->id));
935         $this->assertEqual(4, count($records));
937         // Booleans into params
938         $records = $DB->get_records($tablename, array('course' => true));
939         $this->assertEqual(0, count($records));
940         $records = $DB->get_records($tablename, array('course' => false));
941         $this->assertEqual(0, count($records));
943         // note: delegate limits testing to test_get_records_sql()
944     }
946     public function test_get_records_list() {
947         $DB = $this->tdb;
948         $dbman = $DB->get_manager();
950         $table = $this->get_test_table();
951         $tablename = $table->getName();
953         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
954         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
955         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
956         $dbman->create_table($table);
958         $DB->insert_record($tablename, array('course' => 3));
959         $DB->insert_record($tablename, array('course' => 3));
960         $DB->insert_record($tablename, array('course' => 5));
961         $DB->insert_record($tablename, array('course' => 2));
963         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
964         $this->assertTrue(is_array($records));
965         $this->assertEqual(3, count($records));
966         $this->assertEqual(1, reset($records)->id);
967         $this->assertEqual(2, next($records)->id);
968         $this->assertEqual(4, next($records)->id);
970         $this->assertIdentical(array(), $records = $DB->get_records_list($tablename, 'course', array())); /// Must return 0 rows without conditions. MDL-17645
971         $this->assertEqual(0, count($records));
973         // note: delegate limits testing to test_get_records_sql()
974     }
976     public function test_get_records_sql() {
977         $DB = $this->tdb;
978         $dbman = $DB->get_manager();
980         $table = $this->get_test_table();
981         $tablename = $table->getName();
983         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
984         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
985         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
986         $dbman->create_table($table);
988         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
989         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
990         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
991         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
992         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
993         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
994         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
996         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
997         $this->assertEqual(2, count($records));
998         $this->assertEqual($inskey1, reset($records)->id);
999         $this->assertEqual($inskey4, next($records)->id);
1001         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1002         $this->enable_debugging();
1003         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1004         $this->assertFalse($this->get_debugging() === '');
1005         $this->assertEqual(6, count($records));
1007         // negative limits = no limits
1008         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1009         $this->assertEqual(7, count($records));
1011         // zero limits = no limits
1012         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1013         $this->assertEqual(7, count($records));
1015         // only limitfrom = skips that number of records
1016         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1017         $this->assertEqual(5, count($records));
1018         $this->assertEqual($inskey3, reset($records)->id);
1019         $this->assertEqual($inskey7, end($records)->id);
1021         // only limitnum = fetches that number of records
1022         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1023         $this->assertEqual(3, count($records));
1024         $this->assertEqual($inskey1, reset($records)->id);
1025         $this->assertEqual($inskey3, end($records)->id);
1027         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1028         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1029         $this->assertEqual(2, count($records));
1030         $this->assertEqual($inskey4, reset($records)->id);
1031         $this->assertEqual($inskey5, end($records)->id);
1033         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1034     }
1036     public function test_get_records_menu() {
1037         $DB = $this->tdb;
1038         $dbman = $DB->get_manager();
1040         $table = $this->get_test_table();
1041         $tablename = $table->getName();
1043         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1044         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1045         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1046         $dbman->create_table($table);
1048         $DB->insert_record($tablename, array('course' => 3));
1049         $DB->insert_record($tablename, array('course' => 3));
1050         $DB->insert_record($tablename, array('course' => 5));
1051         $DB->insert_record($tablename, array('course' => 2));
1053         $records = $DB->get_records_menu($tablename, array('course' => 3));
1054         $this->assertTrue(is_array($records));
1055         $this->assertEqual(2, count($records));
1056         $this->assertFalse(empty($records[1]));
1057         $this->assertFalse(empty($records[2]));
1058         $this->assertEqual(3, $records[1]);
1059         $this->assertEqual(3, $records[2]);
1061         // note: delegate limits testing to test_get_records_sql()
1062     }
1064     public function test_get_records_select_menu() {
1065         $DB = $this->tdb;
1066         $dbman = $DB->get_manager();
1068         $table = $this->get_test_table();
1069         $tablename = $table->getName();
1071         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1072         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1073         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1074         $dbman->create_table($table);
1076         $DB->insert_record($tablename, array('course' => 3));
1077         $DB->insert_record($tablename, array('course' => 2));
1078         $DB->insert_record($tablename, array('course' => 3));
1079         $DB->insert_record($tablename, array('course' => 5));
1081         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1082         $this->assertTrue(is_array($records));
1084         $this->assertEqual(3, count($records));
1085         $this->assertFalse(empty($records[1]));
1086         $this->assertTrue(empty($records[2]));
1087         $this->assertFalse(empty($records[3]));
1088         $this->assertFalse(empty($records[4]));
1089         $this->assertEqual(3, $records[1]);
1090         $this->assertEqual(3, $records[3]);
1091         $this->assertEqual(5, $records[4]);
1093         // note: delegate limits testing to test_get_records_sql()
1094     }
1096     public function test_get_records_sql_menu() {
1097         $DB = $this->tdb;
1098         $dbman = $DB->get_manager();
1100         $table = $this->get_test_table();
1101         $tablename = $table->getName();
1103         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1104         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1105         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1106         $dbman->create_table($table);
1108         $DB->insert_record($tablename, array('course' => 3));
1109         $DB->insert_record($tablename, array('course' => 2));
1110         $DB->insert_record($tablename, array('course' => 3));
1111         $DB->insert_record($tablename, array('course' => 5));
1113         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1114         $this->assertTrue(is_array($records));
1116         $this->assertEqual(3, count($records));
1117         $this->assertFalse(empty($records[1]));
1118         $this->assertTrue(empty($records[2]));
1119         $this->assertFalse(empty($records[3]));
1120         $this->assertFalse(empty($records[4]));
1121         $this->assertEqual(3, $records[1]);
1122         $this->assertEqual(3, $records[3]);
1123         $this->assertEqual(5, $records[4]);
1125         // note: delegate limits testing to test_get_records_sql()
1126     }
1128     public function test_get_record() {
1129         $DB = $this->tdb;
1130         $dbman = $DB->get_manager();
1132         $table = $this->get_test_table();
1133         $tablename = $table->getName();
1135         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1136         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1137         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1138         $dbman->create_table($table);
1140         $DB->insert_record($tablename, array('course' => 3));
1141         $DB->insert_record($tablename, array('course' => 2));
1143         $record = $DB->get_record($tablename, array('id' => 2));
1144         $this->assertTrue($record instanceof stdClass);
1146         $this->assertEqual(2, $record->course);
1147         $this->assertEqual(2, $record->id);
1148     }
1151     public function test_get_record_select() {
1152         $DB = $this->tdb;
1153         $dbman = $DB->get_manager();
1155         $table = $this->get_test_table();
1156         $tablename = $table->getName();
1158         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1159         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1160         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1161         $dbman->create_table($table);
1163         $DB->insert_record($tablename, array('course' => 3));
1164         $DB->insert_record($tablename, array('course' => 2));
1166         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1167         $this->assertTrue($record instanceof stdClass);
1169         $this->assertEqual(2, $record->course);
1171         // note: delegates limit testing to test_get_records_sql()
1172     }
1174     public function test_get_record_sql() {
1175         $DB = $this->tdb;
1176         $dbman = $DB->get_manager();
1178         $table = $this->get_test_table();
1179         $tablename = $table->getName();
1181         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1182         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1183         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1184         $dbman->create_table($table);
1186         $DB->insert_record($tablename, array('course' => 3));
1187         $DB->insert_record($tablename, array('course' => 2));
1189         // standard use
1190         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1191         $this->assertTrue($record instanceof stdClass);
1192         $this->assertEqual(2, $record->course);
1193         $this->assertEqual(2, $record->id);
1195         // backwards compatibility with $ignoremultiple
1196         $this->assertFalse(IGNORE_MISSING);
1197         $this->assertTrue(IGNORE_MULTIPLE);
1199         // record not found - ignore
1200         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1201         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1203         // record not found error
1204         try {
1205             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1206             $this->fail("Exception expected");
1207         } catch (dml_missing_record_exception $e) {
1208             $this->assertTrue(true);
1209         }
1211         $this->enable_debugging();
1212         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1213         $this->assertFalse($this->get_debugging() === '');
1215         // multiple matches ignored
1216         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1218         // multiple found error
1219         try {
1220             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1221             $this->fail("Exception expected");
1222         } catch (dml_multiple_records_exception $e) {
1223             $this->assertTrue(true);
1224         }
1225     }
1227     public function test_get_field() {
1228         $DB = $this->tdb;
1229         $dbman = $DB->get_manager();
1231         $table = $this->get_test_table();
1232         $tablename = $table->getName();
1234         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1235         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1236         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1237         $dbman->create_table($table);
1239         $id1 = $DB->insert_record($tablename, array('course' => 3));
1240         $DB->insert_record($tablename, array('course' => 5));
1241         $DB->insert_record($tablename, array('course' => 5));
1243         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1244         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1246         $this->assertIdentical(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1247         try {
1248             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1249             $this->assertFail('Exception expected due to missing record');
1250         } catch (dml_exception $ex) {
1251             $this->assertTrue(true);
1252         }
1254         $this->enable_debugging();
1255         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1256         $this->assertIdentical($this->get_debugging(), '');
1258         $this->enable_debugging();
1259         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1260         $this->assertFalse($this->get_debugging() === '');
1261     }
1263     public function test_get_field_select() {
1264         $DB = $this->tdb;
1265         $dbman = $DB->get_manager();
1267         $table = $this->get_test_table();
1268         $tablename = $table->getName();
1270         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1271         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1272         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1273         $dbman->create_table($table);
1275         $DB->insert_record($tablename, array('course' => 3));
1277         $this->assertEqual(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1278     }
1280     public function test_get_field_sql() {
1281         $DB = $this->tdb;
1282         $dbman = $DB->get_manager();
1284         $table = $this->get_test_table();
1285         $tablename = $table->getName();
1287         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1288         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1289         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1290         $dbman->create_table($table);
1292         $DB->insert_record($tablename, array('course' => 3));
1294         $this->assertEqual(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1295     }
1297     public function test_get_fieldset_select() {
1298         $DB = $this->tdb;
1299         $dbman = $DB->get_manager();
1301         $table = $this->get_test_table();
1302         $tablename = $table->getName();
1304         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1305         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1306         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1307         $dbman->create_table($table);
1309         $DB->insert_record($tablename, array('course' => 1));
1310         $DB->insert_record($tablename, array('course' => 3));
1311         $DB->insert_record($tablename, array('course' => 2));
1312         $DB->insert_record($tablename, array('course' => 6));
1314         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1315         $this->assertTrue(is_array($fieldset));
1317         $this->assertEqual(3, count($fieldset));
1318         $this->assertEqual(3, $fieldset[0]);
1319         $this->assertEqual(2, $fieldset[1]);
1320         $this->assertEqual(6, $fieldset[2]);
1321     }
1323     public function test_get_fieldset_sql() {
1324         $DB = $this->tdb;
1325         $dbman = $DB->get_manager();
1327         $table = $this->get_test_table();
1328         $tablename = $table->getName();
1330         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1331         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1332         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1333         $dbman->create_table($table);
1335         $DB->insert_record($tablename, array('course' => 1));
1336         $DB->insert_record($tablename, array('course' => 3));
1337         $DB->insert_record($tablename, array('course' => 2));
1338         $DB->insert_record($tablename, array('course' => 6));
1340         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1341         $this->assertTrue(is_array($fieldset));
1343         $this->assertEqual(3, count($fieldset));
1344         $this->assertEqual(2, $fieldset[0]);
1345         $this->assertEqual(3, $fieldset[1]);
1346         $this->assertEqual(4, $fieldset[2]);
1347     }
1349     public function test_insert_record_raw() {
1350         $DB = $this->tdb;
1351         $dbman = $DB->get_manager();
1353         $table = $this->get_test_table();
1354         $tablename = $table->getName();
1356         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1357         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1358         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1359         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1360         $dbman->create_table($table);
1362         $record = (object)array('course' => 1, 'onechar' => 'xx');
1363         $before = clone($record);
1364         $result = $DB->insert_record_raw($tablename, $record);
1365         $this->assertIdentical(1, $result);
1366         $this->assertIdentical($record, $before);
1368         $record = $DB->get_record($tablename, array('course' => 1));
1369         $this->assertTrue($record instanceof stdClass);
1370         $this->assertIdentical('xx', $record->onechar);
1372         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1373         $this->assertIdentical(true, $result);
1375         // note: bulk not implemented yet
1376         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1377         $record = $DB->get_record($tablename, array('course' => 3));
1378         $this->assertTrue($record instanceof stdClass);
1379         $this->assertIdentical('zz', $record->onechar);
1381         // custom sequence (id) - returnid is ignored
1382         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1383         $this->assertIdentical(true, $result);
1384         $record = $DB->get_record($tablename, array('id' => 10));
1385         $this->assertTrue($record instanceof stdClass);
1386         $this->assertIdentical('bb', $record->onechar);
1388         // custom sequence - missing id error
1389         try {
1390             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1391             $this->assertFail('Exception expected due to missing record');
1392         } catch (coding_exception $ex) {
1393             $this->assertTrue(true);
1394         }
1396         // wrong column error
1397         try {
1398             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1399             $this->assertFail('Exception expected due to invalid column');
1400         } catch (dml_write_exception $ex) {
1401             $this->assertTrue(true);
1402         }
1403     }
1405     public function test_insert_record() {
1406         // All the information in this test is fetched from DB by get_recordset() so we
1407         // have such method properly tested against nulls, empties and friends...
1409         $DB = $this->tdb;
1410         $dbman = $DB->get_manager();
1412         $table = $this->get_test_table();
1413         $tablename = $table->getName();
1415         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1416         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1417         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1418         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1419         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1420         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1421         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1422         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1423         $dbman->create_table($table);
1425         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1426         $record = $DB->get_record($tablename, array('course' => 1));
1427         $this->assertEqual(1, $record->id);
1428         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1429         $this->assertEqual(200, $record->onenum);
1430         $this->assertIdentical('onestring', $record->onechar);
1431         $this->assertNull($record->onetext);
1432         $this->assertNull($record->onebinary);
1434         // without returning id, bulk not implemented
1435         $result = $this->assertIdentical(true, $DB->insert_record($tablename, array('course' => 99), false, true));
1436         $record = $DB->get_record($tablename, array('course' => 99));
1437         $this->assertEqual(2, $record->id);
1438         $this->assertEqual(99, $record->course);
1440         // Check nulls are set properly for all types
1441         $record = new object();
1442         $record->oneint = null;
1443         $record->onenum = null;
1444         $record->onechar = null;
1445         $record->onetext = null;
1446         $record->onebinary = null;
1447         $recid = $DB->insert_record($tablename, $record);
1448         $record = $DB->get_record($tablename, array('id' => $recid));
1449         $this->assertEqual(0, $record->course);
1450         $this->assertNull($record->oneint);
1451         $this->assertNull($record->onenum);
1452         $this->assertNull($record->onechar);
1453         $this->assertNull($record->onetext);
1454         $this->assertNull($record->onebinary);
1456         // Check zeros are set properly for all types
1457         $record = new object();
1458         $record->oneint = 0;
1459         $record->onenum = 0;
1460         $recid = $DB->insert_record($tablename, $record);
1461         $record = $DB->get_record($tablename, array('id' => $recid));
1462         $this->assertEqual(0, $record->oneint);
1463         $this->assertEqual(0, $record->onenum);
1465         // Check booleans are set properly for all types
1466         $record = new object();
1467         $record->oneint = true; // trues
1468         $record->onenum = true;
1469         $record->onechar = true;
1470         $record->onetext = true;
1471         $recid = $DB->insert_record($tablename, $record);
1472         $record = $DB->get_record($tablename, array('id' => $recid));
1473         $this->assertEqual(1, $record->oneint);
1474         $this->assertEqual(1, $record->onenum);
1475         $this->assertEqual(1, $record->onechar);
1476         $this->assertEqual(1, $record->onetext);
1478         $record = new object();
1479         $record->oneint = false; // falses
1480         $record->onenum = false;
1481         $record->onechar = false;
1482         $record->onetext = false;
1483         $recid = $DB->insert_record($tablename, $record);
1484         $record = $DB->get_record($tablename, array('id' => $recid));
1485         $this->assertEqual(0, $record->oneint);
1486         $this->assertEqual(0, $record->onenum);
1487         $this->assertEqual(0, $record->onechar);
1488         $this->assertEqual(0, $record->onetext);
1490         // Check string data causes exception in numeric types
1491         $record = new object();
1492         $record->oneint = 'onestring';
1493         $record->onenum = 0;
1494         try {
1495             $DB->insert_record($tablename, $record);
1496             $this->fail("Expecting an exception, none occurred");
1497         } catch (exception $e) {
1498             $this->assertTrue($e instanceof dml_exception);
1499         }
1500         $record = new object();
1501         $record->oneint = 0;
1502         $record->onenum = 'onestring';
1503         try {
1504            $DB->insert_record($tablename, $record);
1505            $this->fail("Expecting an exception, none occurred");
1506         } catch (exception $e) {
1507             $this->assertTrue($e instanceof dml_exception);
1508         }
1510         // Check empty string data is stored as 0 in numeric datatypes
1511         $record = new object();
1512         $record->oneint = ''; // empty string
1513         $record->onenum = 0;
1514         $recid = $DB->insert_record($tablename, $record);
1515         $record = $DB->get_record($tablename, array('id' => $recid));
1516         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1518         $record = new object();
1519         $record->oneint = 0;
1520         $record->onenum = ''; // empty string
1521         $recid = $DB->insert_record($tablename, $record);
1522         $record = $DB->get_record($tablename, array('id' => $recid));
1523         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1525         // Check empty strings are set properly in string types
1526         $record = new object();
1527         $record->oneint = 0;
1528         $record->onenum = 0;
1529         $record->onechar = '';
1530         $record->onetext = '';
1531         $recid = $DB->insert_record($tablename, $record);
1532         $record = $DB->get_record($tablename, array('id' => $recid));
1533         $this->assertTrue($record->onechar === '');
1534         $this->assertTrue($record->onetext === '');
1536         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1537         $record = new object();
1538         $record->oneint = ((210.10 + 39.92) - 150.02);
1539         $record->onenum = ((210.10 + 39.92) - 150.02);
1540         $recid = $DB->insert_record($tablename, $record);
1541         $record = $DB->get_record($tablename, array('id' => $recid));
1542         $this->assertEqual(100, $record->oneint);
1543         $this->assertEqual(100, $record->onenum);
1545         // Check various quotes/backslashes combinations in string types
1546         $teststrings = array(
1547             'backslashes and quotes alone (even): "" \'\' \\\\',
1548             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1549             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1550             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1551         foreach ($teststrings as $teststring) {
1552             $record = new object();
1553             $record->onechar = $teststring;
1554             $record->onetext = $teststring;
1555             $recid = $DB->insert_record($tablename, $record);
1556             $record = $DB->get_record($tablename, array('id' => $recid));
1557             $this->assertEqual($teststring, $record->onechar);
1558             $this->assertEqual($teststring, $record->onetext);
1559         }
1561         // Check LOBs in text/binary columns
1562         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1563         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1564         $record = new object();
1565         $record->onetext = $clob;
1566         $record->onebinary = $blob;
1567         $recid = $DB->insert_record($tablename, $record);
1568         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1569         $record = $rs->current();
1570         $rs->close();
1571         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1572         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1574         // And "small" LOBs too, just in case
1575         $newclob = substr($clob, 0, 500);
1576         $newblob = substr($blob, 0, 250);
1577         $record = new object();
1578         $record->onetext = $newclob;
1579         $record->onebinary = $newblob;
1580         $recid = $DB->insert_record($tablename, $record);
1581         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1582         $record = $rs->current();
1583         $rs->close();
1584         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1585         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1586         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1588         // test data is not modified
1589         $record = new object();
1590         $record->id     = -1; // has to be ignored
1591         $record->course = 3;
1592         $record->lalala = 'lalal'; // unused
1593         $before = clone($record);
1594         $DB->insert_record($tablename, $record);
1595         $this->assertEqual($record, $before);
1597         // make sure the id is always increasing and never reuses the same id
1598         $id1 = $DB->insert_record($tablename, array('course' => 3));
1599         $id2 = $DB->insert_record($tablename, array('course' => 3));
1600         $this->assertTrue($id1 < $id2);
1601         $DB->delete_records($tablename, array('id'=>$id2));
1602         $id3 = $DB->insert_record($tablename, array('course' => 3));
1603         $this->assertTrue($id2 < $id3);
1604         $DB->delete_records($tablename, array());
1605         $id4 = $DB->insert_record($tablename, array('course' => 3));
1606         $this->assertTrue($id3 < $id4);
1607     }
1609     public function test_import_record() {
1610         // All the information in this test is fetched from DB by get_recordset() so we
1611         // have such method properly tested against nulls, empties and friends...
1613         $DB = $this->tdb;
1614         $dbman = $DB->get_manager();
1616         $table = $this->get_test_table();
1617         $tablename = $table->getName();
1619         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1620         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1621         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1622         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1623         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1624         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1625         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1626         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1627         $dbman->create_table($table);
1629         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1630         $record = $DB->get_record($tablename, array('course' => 1));
1631         $this->assertEqual(1, $record->id);
1632         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1633         $this->assertEqual(200, $record->onenum);
1634         $this->assertIdentical('onestring', $record->onechar);
1635         $this->assertNull($record->onetext);
1636         $this->assertNull($record->onebinary);
1638         // ignore extra columns
1639         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
1640         $before = clone($record);
1641         $this->assertIdentical(true, $DB->import_record($tablename, $record));
1642         $this->assertIdentical($record, $before);
1643         $records = $DB->get_records($tablename);
1644         $this->assertEqual(2, $records[13]->course);
1646         // Check nulls are set properly for all types
1647         $record = new object();
1648         $record->id = 20;
1649         $record->oneint = null;
1650         $record->onenum = null;
1651         $record->onechar = null;
1652         $record->onetext = null;
1653         $record->onebinary = null;
1654         $this->assertTrue($DB->import_record($tablename, $record));
1655         $record = $DB->get_record($tablename, array('id' => 20));
1656         $this->assertEqual(0, $record->course);
1657         $this->assertNull($record->oneint);
1658         $this->assertNull($record->onenum);
1659         $this->assertNull($record->onechar);
1660         $this->assertNull($record->onetext);
1661         $this->assertNull($record->onebinary);
1663         // Check zeros are set properly for all types
1664         $record = new object();
1665         $record->id = 23;
1666         $record->oneint = 0;
1667         $record->onenum = 0;
1668         $this->assertTrue($DB->import_record($tablename, $record));
1669         $record = $DB->get_record($tablename, array('id' => 23));
1670         $this->assertEqual(0, $record->oneint);
1671         $this->assertEqual(0, $record->onenum);
1673         // Check string data causes exception in numeric types
1674         $record = new object();
1675         $record->id = 32;
1676         $record->oneint = 'onestring';
1677         $record->onenum = 0;
1678         try {
1679             $DB->import_record($tablename, $record);
1680             $this->fail("Expecting an exception, none occurred");
1681         } catch (exception $e) {
1682             $this->assertTrue($e instanceof dml_exception);
1683         }
1684         $record = new object();
1685         $record->id = 35;
1686         $record->oneint = 0;
1687         $record->onenum = 'onestring';
1688         try {
1689            $DB->import_record($tablename, $record);
1690            $this->fail("Expecting an exception, none occurred");
1691         } catch (exception $e) {
1692             $this->assertTrue($e instanceof dml_exception);
1693         }
1695         // Check empty strings are set properly in string types
1696         $record = new object();
1697         $record->id = 44;
1698         $record->oneint = 0;
1699         $record->onenum = 0;
1700         $record->onechar = '';
1701         $record->onetext = '';
1702         $this->assertTrue($DB->import_record($tablename, $record));
1703         $record = $DB->get_record($tablename, array('id' => 44));
1704         $this->assertTrue($record->onechar === '');
1705         $this->assertTrue($record->onetext === '');
1707         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1708         $record = new object();
1709         $record->id = 47;
1710         $record->oneint = ((210.10 + 39.92) - 150.02);
1711         $record->onenum = ((210.10 + 39.92) - 150.02);
1712         $this->assertTrue($DB->import_record($tablename, $record));
1713         $record = $DB->get_record($tablename, array('id' => 47));
1714         $this->assertEqual(100, $record->oneint);
1715         $this->assertEqual(100, $record->onenum);
1717         // Check various quotes/backslashes combinations in string types
1718         $i = 50;
1719         $teststrings = array(
1720             'backslashes and quotes alone (even): "" \'\' \\\\',
1721             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1722             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1723             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1724         foreach ($teststrings as $teststring) {
1725             $record = new object();
1726             $record->id = $i;
1727             $record->onechar = $teststring;
1728             $record->onetext = $teststring;
1729             $this->assertTrue($DB->import_record($tablename, $record));
1730             $record = $DB->get_record($tablename, array('id' => $i));
1731             $this->assertEqual($teststring, $record->onechar);
1732             $this->assertEqual($teststring, $record->onetext);
1733             $i = $i + 3;
1734         }
1736         // Check LOBs in text/binary columns
1737         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1738         $record = new object();
1739         $record->id = 70;
1740         $record->onetext = $clob;
1741         $record->onebinary = '';
1742         $this->assertTrue($DB->import_record($tablename, $record));
1743         $rs = $DB->get_recordset($tablename, array('id' => 70));
1744         $record = $rs->current();
1745         $rs->close();
1746         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1748         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1749         $record = new object();
1750         $record->id = 71;
1751         $record->onetext = '';
1752         $record->onebinary = $blob;
1753         $this->assertTrue($DB->import_record($tablename, $record));
1754         $rs = $DB->get_recordset($tablename, array('id' => 71));
1755         $record = $rs->current();
1756         $rs->close();
1757         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1759         // And "small" LOBs too, just in case
1760         $newclob = substr($clob, 0, 500);
1761         $newblob = substr($blob, 0, 250);
1762         $record = new object();
1763         $record->id = 73;
1764         $record->onetext = $newclob;
1765         $record->onebinary = $newblob;
1766         $this->assertTrue($DB->import_record($tablename, $record));
1767         $rs = $DB->get_recordset($tablename, array('id' => 73));
1768         $record = $rs->current();
1769         $rs->close();
1770         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1771         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1772         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1773     }
1775     public function test_update_record_raw() {
1776         $DB = $this->tdb;
1777         $dbman = $DB->get_manager();
1779         $table = $this->get_test_table();
1780         $tablename = $table->getName();
1782         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1783         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1784         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1785         $dbman->create_table($table);
1787         $DB->insert_record($tablename, array('course' => 1));
1788         $DB->insert_record($tablename, array('course' => 3));
1790         $record = $DB->get_record($tablename, array('course' => 1));
1791         $record->course = 2;
1792         $this->assertTrue($DB->update_record_raw($tablename, $record));
1793         $this->assertEqual(0, $DB->count_records($tablename, array('course' => 1)));
1794         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 2)));
1795         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 3)));
1797         $record = $DB->get_record($tablename, array('course' => 1));
1798         $record->xxxxx = 2;
1799         try {
1800            $DB->update_record_raw($tablename, $record);
1801            $this->fail("Expecting an exception, none occurred");
1802         } catch (Exception $e) {
1803             $this->assertTrue($e instanceof coding_exception);
1804         }
1806         $record = $DB->get_record($tablename, array('course' => 3));
1807         unset($record->id);
1808         try {
1809            $DB->update_record_raw($tablename, $record);
1810            $this->fail("Expecting an exception, none occurred");
1811         } catch (Exception $e) {
1812             $this->assertTrue($e instanceof coding_exception);
1813         }
1814     }
1816     public function test_update_record() {
1818         // All the information in this test is fetched from DB by get_record() so we
1819         // have such method properly tested against nulls, empties and friends...
1821         $DB = $this->tdb;
1822         $dbman = $DB->get_manager();
1824         $table = $this->get_test_table();
1825         $tablename = $table->getName();
1827         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1828         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1829         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1830         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1831         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1832         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1833         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1834         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1835         $dbman->create_table($table);
1837         $DB->insert_record($tablename, array('course' => 1));
1838         $record = $DB->get_record($tablename, array('course' => 1));
1839         $record->course = 2;
1841         $this->assertTrue($DB->update_record($tablename, $record));
1842         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
1843         $this->assertTrue($record = $DB->get_record($tablename, array('course' => 2)));
1844         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1845         $this->assertEqual(200, $record->onenum);
1846         $this->assertEqual('onestring', $record->onechar);
1847         $this->assertNull($record->onetext);
1848         $this->assertNull($record->onebinary);
1850         // Check nulls are set properly for all types
1851         $record->oneint = null;
1852         $record->onenum = null;
1853         $record->onechar = null;
1854         $record->onetext = null;
1855         $record->onebinary = null;
1856         $DB->update_record($tablename, $record);
1857         $record = $DB->get_record($tablename, array('course' => 2));
1858         $this->assertNull($record->oneint);
1859         $this->assertNull($record->onenum);
1860         $this->assertNull($record->onechar);
1861         $this->assertNull($record->onetext);
1862         $this->assertNull($record->onebinary);
1864         // Check zeros are set properly for all types
1865         $record->oneint = 0;
1866         $record->onenum = 0;
1867         $DB->update_record($tablename, $record);
1868         $record = $DB->get_record($tablename, array('course' => 2));
1869         $this->assertEqual(0, $record->oneint);
1870         $this->assertEqual(0, $record->onenum);
1872         // Check booleans are set properly for all types
1873         $record->oneint = true; // trues
1874         $record->onenum = true;
1875         $record->onechar = true;
1876         $record->onetext = true;
1877         $DB->update_record($tablename, $record);
1878         $record = $DB->get_record($tablename, array('course' => 2));
1879         $this->assertEqual(1, $record->oneint);
1880         $this->assertEqual(1, $record->onenum);
1881         $this->assertEqual(1, $record->onechar);
1882         $this->assertEqual(1, $record->onetext);
1884         $record->oneint = false; // falses
1885         $record->onenum = false;
1886         $record->onechar = false;
1887         $record->onetext = false;
1888         $DB->update_record($tablename, $record);
1889         $record = $DB->get_record($tablename, array('course' => 2));
1890         $this->assertEqual(0, $record->oneint);
1891         $this->assertEqual(0, $record->onenum);
1892         $this->assertEqual(0, $record->onechar);
1893         $this->assertEqual(0, $record->onetext);
1895         // Check string data causes exception in numeric types
1896         $record->oneint = 'onestring';
1897         $record->onenum = 0;
1898         try {
1899             $DB->update_record($tablename, $record);
1900             $this->fail("Expecting an exception, none occurred");
1901         } catch (exception $e) {
1902             $this->assertTrue($e instanceof dml_exception);
1903         }
1904         $record->oneint = 0;
1905         $record->onenum = 'onestring';
1906         try {
1907             $DB->update_record($tablename, $record);
1908             $this->fail("Expecting an exception, none occurred");
1909         } catch (exception $e) {
1910             $this->assertTrue($e instanceof dml_exception);
1911         }
1913         // Check empty string data is stored as 0 in numeric datatypes
1914         $record->oneint = ''; // empty string
1915         $record->onenum = 0;
1916         $DB->update_record($tablename, $record);
1917         $record = $DB->get_record($tablename, array('course' => 2));
1918         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1920         $record->oneint = 0;
1921         $record->onenum = ''; // empty string
1922         $DB->update_record($tablename, $record);
1923         $record = $DB->get_record($tablename, array('course' => 2));
1924         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1926         // Check empty strings are set properly in string types
1927         $record->oneint = 0;
1928         $record->onenum = 0;
1929         $record->onechar = '';
1930         $record->onetext = '';
1931         $DB->update_record($tablename, $record);
1932         $record = $DB->get_record($tablename, array('course' => 2));
1933         $this->assertTrue($record->onechar === '');
1934         $this->assertTrue($record->onetext === '');
1936         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1937         $record->oneint = ((210.10 + 39.92) - 150.02);
1938         $record->onenum = ((210.10 + 39.92) - 150.02);
1939         $DB->update_record($tablename, $record);
1940         $record = $DB->get_record($tablename, array('course' => 2));
1941         $this->assertEqual(100, $record->oneint);
1942         $this->assertEqual(100, $record->onenum);
1944         // Check various quotes/backslashes combinations in string types
1945         $teststrings = array(
1946             'backslashes and quotes alone (even): "" \'\' \\\\',
1947             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1948             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1949             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1950         foreach ($teststrings as $teststring) {
1951             $record->onechar = $teststring;
1952             $record->onetext = $teststring;
1953             $DB->update_record($tablename, $record);
1954             $record = $DB->get_record($tablename, array('course' => 2));
1955             $this->assertEqual($teststring, $record->onechar);
1956             $this->assertEqual($teststring, $record->onetext);
1957         }
1959         // Check LOBs in text/binary columns
1960         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1961         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1962         $record->onetext = $clob;
1963         $record->onebinary = $blob;
1964         $DB->update_record($tablename, $record);
1965         $record = $DB->get_record($tablename, array('course' => 2));
1966         $this->assertEqual($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
1967         $this->assertEqual($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
1969         // And "small" LOBs too, just in case
1970         $newclob = substr($clob, 0, 500);
1971         $newblob = substr($blob, 0, 250);
1972         $record->onetext = $newclob;
1973         $record->onebinary = $newblob;
1974         $DB->update_record($tablename, $record);
1975         $record = $DB->get_record($tablename, array('course' => 2));
1976         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
1977         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
1978     }
1980     public function test_set_field() {
1981         $DB = $this->tdb;
1982         $dbman = $DB->get_manager();
1984         $table = $this->get_test_table();
1985         $tablename = $table->getName();
1987         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1988         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1989         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1990         $dbman->create_table($table);
1992         // simple set_field
1993         $id1 = $DB->insert_record($tablename, array('course' => 1));
1994         $id2 = $DB->insert_record($tablename, array('course' => 1));
1995         $id3 = $DB->insert_record($tablename, array('course' => 3));
1996         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
1997         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
1998         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
1999         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2000         $DB->delete_records($tablename, array());
2002         // multiple fields affected
2003         $id1 = $DB->insert_record($tablename, array('course' => 1));
2004         $id2 = $DB->insert_record($tablename, array('course' => 1));
2005         $id3 = $DB->insert_record($tablename, array('course' => 3));
2006         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2007         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2008         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2009         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2010         $DB->delete_records($tablename, array());
2012         // no field affected
2013         $id1 = $DB->insert_record($tablename, array('course' => 1));
2014         $id2 = $DB->insert_record($tablename, array('course' => 1));
2015         $id3 = $DB->insert_record($tablename, array('course' => 3));
2016         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2017         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2018         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2019         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2020         $DB->delete_records($tablename, array());
2022         // all fields - no condition
2023         $id1 = $DB->insert_record($tablename, array('course' => 1));
2024         $id2 = $DB->insert_record($tablename, array('course' => 1));
2025         $id3 = $DB->insert_record($tablename, array('course' => 3));
2026         $DB->set_field($tablename, 'course', 5, array());
2027         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2028         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2029         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2031         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2032         // go to set_field_select() because set_field() is just one wrapper over it
2033     }
2035     public function test_set_field_select() {
2037         // All the information in this test is fetched from DB by get_field() so we
2038         // have such method properly tested against nulls, empties and friends...
2040         $DB = $this->tdb;
2041         $dbman = $DB->get_manager();
2043         $table = $this->get_test_table();
2044         $tablename = $table->getName();
2046         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2047         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2048         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2049         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2050         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2051         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2052         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2053         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2054         $dbman->create_table($table);
2056         $DB->insert_record($tablename, array('course' => 1));
2058         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2059         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2061         // Check nulls are set properly for all types
2062         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2063         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2064         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2065         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2066         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2067         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2068         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2069         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2070         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2071         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2073         // Check zeros are set properly for all types
2074         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2075         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2076         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2077         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2079         // Check booleans are set properly for all types
2080         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2081         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2082         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2083         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2084         $this->assertEqual(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2085         $this->assertEqual(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2086         $this->assertEqual(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2087         $this->assertEqual(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2089         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2090         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2091         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2092         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2093         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2094         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2095         $this->assertEqual(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2096         $this->assertEqual(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2098         // Check string data causes exception in numeric types
2099         try {
2100             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2101             $this->fail("Expecting an exception, none occurred");
2102         } catch (exception $e) {
2103             $this->assertTrue($e instanceof dml_exception);
2104         }
2105         try {
2106             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2107             $this->fail("Expecting an exception, none occurred");
2108         } catch (exception $e) {
2109             $this->assertTrue($e instanceof dml_exception);
2110         }
2112         // Check empty string data is stored as 0 in numeric datatypes
2113         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2114         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2115         $this->assertTrue(is_numeric($field) && $field == 0);
2117         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2118         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2119         $this->assertTrue(is_numeric($field) && $field == 0);
2121         // Check empty strings are set properly in string types
2122         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2123         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2124         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2125         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2127         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2128         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2129         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2130         $this->assertEqual(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2131         $this->assertEqual(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2133         // Check various quotes/backslashes combinations in string types
2134         $teststrings = array(
2135             'backslashes and quotes alone (even): "" \'\' \\\\',
2136             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2137             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2138             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2139         foreach ($teststrings as $teststring) {
2140             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2141             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2142             $this->assertEqual($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2143             $this->assertEqual($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2144         }
2146         // Check LOBs in text/binary columns
2147         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2148         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2149         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2150         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2151         $this->assertEqual($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2152         $this->assertEqual($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2154         // And "small" LOBs too, just in case
2155         $newclob = substr($clob, 0, 500);
2156         $newblob = substr($blob, 0, 250);
2157         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2158         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2159         $this->assertEqual($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2160         $this->assertEqual($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2161     }
2163     public function test_count_records() {
2164         $DB = $this->tdb;
2166         $dbman = $DB->get_manager();
2168         $table = $this->get_test_table();
2169         $tablename = $table->getName();
2171         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2172         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2173         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2174         $dbman->create_table($table);
2176         $this->assertEqual(0, $DB->count_records($tablename));
2178         $DB->insert_record($tablename, array('course' => 3));
2179         $DB->insert_record($tablename, array('course' => 4));
2180         $DB->insert_record($tablename, array('course' => 5));
2182         $this->assertEqual(3, $DB->count_records($tablename));
2183     }
2185     public function test_count_records_select() {
2186         $DB = $this->tdb;
2188         $dbman = $DB->get_manager();
2190         $table = $this->get_test_table();
2191         $tablename = $table->getName();
2193         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2194         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2195         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2196         $dbman->create_table($table);
2198         $this->assertEqual(0, $DB->count_records($tablename));
2200         $DB->insert_record($tablename, array('course' => 3));
2201         $DB->insert_record($tablename, array('course' => 4));
2202         $DB->insert_record($tablename, array('course' => 5));
2204         $this->assertEqual(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2205     }
2207     public function test_count_records_sql() {
2208         $DB = $this->tdb;
2209         $dbman = $DB->get_manager();
2211         $table = $this->get_test_table();
2212         $tablename = $table->getName();
2214         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2215         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2216         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2217         $dbman->create_table($table);
2219         $this->assertEqual(0, $DB->count_records($tablename));
2221         $DB->insert_record($tablename, array('course' => 3));
2222         $DB->insert_record($tablename, array('course' => 4));
2223         $DB->insert_record($tablename, array('course' => 5));
2225         $this->assertEqual(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2226     }
2228     public function test_record_exists() {
2229         $DB = $this->tdb;
2230         $dbman = $DB->get_manager();
2232         $table = $this->get_test_table();
2233         $tablename = $table->getName();
2235         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2236         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2237         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2238         $dbman->create_table($table);
2240         $this->assertEqual(0, $DB->count_records($tablename));
2242         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
2243         $DB->insert_record($tablename, array('course' => 3));
2245         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
2247     }
2249     public function test_record_exists_select() {
2250         $DB = $this->tdb;
2251         $dbman = $DB->get_manager();
2253         $table = $this->get_test_table();
2254         $tablename = $table->getName();
2256         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2257         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2258         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2259         $dbman->create_table($table);
2261         $this->assertEqual(0, $DB->count_records($tablename));
2263         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
2264         $DB->insert_record($tablename, array('course' => 3));
2266         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
2267     }
2269     public function test_record_exists_sql() {
2270         $DB = $this->tdb;
2271         $dbman = $DB->get_manager();
2273         $table = $this->get_test_table();
2274         $tablename = $table->getName();
2276         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2277         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2278         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2279         $dbman->create_table($table);
2281         $this->assertEqual(0, $DB->count_records($tablename));
2283         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2284         $DB->insert_record($tablename, array('course' => 3));
2286         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2287     }
2289     public function test_delete_records() {
2290         $DB = $this->tdb;
2291         $dbman = $DB->get_manager();
2293         $table = $this->get_test_table();
2294         $tablename = $table->getName();
2296         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2297         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2298         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2299         $dbman->create_table($table);
2301         $DB->insert_record($tablename, array('course' => 3));
2302         $DB->insert_record($tablename, array('course' => 2));
2303         $DB->insert_record($tablename, array('course' => 2));
2305         // Delete all records
2306         $this->assertTrue($DB->delete_records($tablename));
2307         $this->assertEqual(0, $DB->count_records($tablename));
2309         // Delete subset of records
2310         $DB->insert_record($tablename, array('course' => 3));
2311         $DB->insert_record($tablename, array('course' => 2));
2312         $DB->insert_record($tablename, array('course' => 2));
2314         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
2315         $this->assertEqual(1, $DB->count_records($tablename));
2317         // delete all
2318         $this->assertTrue($DB->delete_records($tablename, array()));
2319         $this->assertEqual(0, $DB->count_records($tablename));
2320     }
2322     public function test_delete_records_select() {
2323         $DB = $this->tdb;
2324         $dbman = $DB->get_manager();
2326         $table = $this->get_test_table();
2327         $tablename = $table->getName();
2329         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2330         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2331         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2332         $dbman->create_table($table);
2334         $DB->insert_record($tablename, array('course' => 3));
2335         $DB->insert_record($tablename, array('course' => 2));
2336         $DB->insert_record($tablename, array('course' => 2));
2338         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
2339         $this->assertEqual(1, $DB->count_records($tablename));
2340     }
2342     public function test_delete_records_list() {
2343         $DB = $this->tdb;
2344         $dbman = $DB->get_manager();
2346         $table = $this->get_test_table();
2347         $tablename = $table->getName();
2349         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2350         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2351         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2352         $dbman->create_table($table);
2354         $DB->insert_record($tablename, array('course' => 1));
2355         $DB->insert_record($tablename, array('course' => 2));
2356         $DB->insert_record($tablename, array('course' => 3));
2358         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
2359         $this->assertEqual(1, $DB->count_records($tablename));
2361         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); /// Must delete 0 rows without conditions. MDL-17645
2362         $this->assertEqual(1, $DB->count_records($tablename));
2363     }
2365     function test_sql_null_from_clause() {
2366         $DB = $this->tdb;
2367         $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
2368         $this->assertEqual($DB->get_field_sql($sql), 1);
2369     }
2371     function test_sql_bitand() {
2372         $DB = $this->tdb;
2373         $dbman = $DB->get_manager();
2375         $table = $this->get_test_table();
2376         $tablename = $table->getName();
2378         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2379         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2380         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2381         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2382         $dbman->create_table($table);
2384         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2386         $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
2387         $this->assertEqual($DB->get_field_sql($sql), 2);
2389         $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
2390         $result = $DB->get_records_sql($sql);
2391         $this->assertEqual(count($result), 1);
2392         $this->assertEqual(reset($result)->res, 2);
2394         $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
2395         $result = $DB->get_records_sql($sql, array(10));
2396         $this->assertEqual(count($result), 1);
2397         $this->assertEqual(reset($result)->res, 2);
2398     }
2400     function test_sql_bitnot() {
2401         $DB = $this->tdb;
2403         $not = $DB->sql_bitnot(2);
2404         $notlimited = $DB->sql_bitand($not, 7); // might be positive or negative number which can not fit into PHP INT!
2406         $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
2407         $this->assertEqual($DB->get_field_sql($sql), 5);
2408     }
2410     function test_sql_bitor() {
2411         $DB = $this->tdb;
2412         $dbman = $DB->get_manager();
2414         $table = $this->get_test_table();
2415         $tablename = $table->getName();
2417         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2418         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2419         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2420         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2421         $dbman->create_table($table);
2423         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2425         $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
2426         $this->assertEqual($DB->get_field_sql($sql), 11);
2428         $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
2429         $result = $DB->get_records_sql($sql);
2430         $this->assertEqual(count($result), 1);
2431         $this->assertEqual(reset($result)->res, 11);
2433         $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
2434         $result = $DB->get_records_sql($sql, array(10));
2435         $this->assertEqual(count($result), 1);
2436         $this->assertEqual(reset($result)->res, 11);
2437     }
2439     function test_sql_bitxor() {
2440         $DB = $this->tdb;
2441         $dbman = $DB->get_manager();
2443         $table = $this->get_test_table();
2444         $tablename = $table->getName();
2446         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2447         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2448         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2449         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2450         $dbman->create_table($table);
2452         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2454         $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
2455         $this->assertEqual($DB->get_field_sql($sql), 9);
2457         $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}";
2458         $result = $DB->get_records_sql($sql);
2459         $this->assertEqual(count($result), 1);
2460         $this->assertEqual(reset($result)->res, 9);
2462         $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}";
2463         $result = $DB->get_records_sql($sql, array(10));
2464         $this->assertEqual(count($result), 1);
2465         $this->assertEqual(reset($result)->res, 9);
2466     }
2468     function test_sql_modulo() {
2469         $DB = $this->tdb;
2470         $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause();
2471         $this->assertEqual($DB->get_field_sql($sql), 3);
2472     }
2474     function test_sql_ceil() {
2475         $DB = $this->tdb;
2476         $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause();
2477         $this->assertEqual($DB->get_field_sql($sql), 666);
2478     }
2480     function test_cast_char2int() {
2481         $DB = $this->tdb;
2482         $dbman = $DB->get_manager();
2484         $table1 = $this->get_test_table("1");
2485         $tablename1 = $table1->getName();
2487         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2488         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2489         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2490         $dbman->create_table($table1);
2492         $DB->insert_record($tablename1, array('name'=>'100'));
2494         $table2 = $this->get_test_table("2");
2495         $tablename2 = $table2->getName();
2496         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2497         $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2498         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2499         $dbman->create_table($table2);
2501         $DB->insert_record($tablename2, array('res'=>100));
2503         try {
2504             $sql = "SELECT * FROM {".$tablename1."} t1, {".$tablename2."} t2 WHERE ".$DB->sql_cast_char2int("t1.name")." = t2.res ";
2505             $records = $DB->get_records_sql($sql);
2506             $this->assertEqual(count($records), 1);
2507         } catch (dml_exception $e) {
2508             $this->fail("No exception expected");
2509         }
2510     }
2512     function test_cast_char2real() {
2513         $DB = $this->tdb;
2514         $dbman = $DB->get_manager();
2516         $table = $this->get_test_table();
2517         $tablename = $table->getName();
2519         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2520         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2521         $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
2522         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2523         $dbman->create_table($table);
2525         $DB->insert_record($tablename, array('name'=>'10.10', 'res'=>5.1));
2526         $DB->insert_record($tablename, array('name'=>'1.10', 'res'=>666));
2527         $DB->insert_record($tablename, array('name'=>'11.10', 'res'=>0.1));
2529         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res";
2530         $records = $DB->get_records_sql($sql);
2531         $this->assertEqual(count($records), 2);
2532     }
2534     function sql_compare_text() {
2535         $DB = $this->tdb;
2536         $dbman = $DB->get_manager();
2538         $table = $this->get_test_table();
2539         $tablename = $table->getName();
2541         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2542         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2543         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2544         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2545         $dbman->create_table($table);
2547         $DB->insert_record($tablename, array('name'=>'abcd',   'description'=>'abcd'));
2548         $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef'));
2549         $DB->insert_record($tablename, array('name'=>'aaaabb', 'description'=>'aaaacccccccccccccccccc'));
2551         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description');
2552         $records = $DB->get_records_sql($sql);
2553         $this->assertEqual(count($records), 1);
2555         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4);
2556         $records = $DB->get_records_sql($sql);
2557         $this->assertEqual(count($records), 2);
2558     }
2560     function test_unique_index_collation_trouble() {
2561         // note: this is a work in progress, we should probably move this to ddl test
2563         $DB = $this->tdb;
2564         $dbman = $DB->get_manager();
2566         $table = $this->get_test_table();
2567         $tablename = $table->getName();
2569         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2570         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2571         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2572         $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name'));
2573         $dbman->create_table($table);
2575         $DB->insert_record($tablename, array('name'=>'aaa'));
2577         try {
2578             $DB->insert_record($tablename, array('name'=>'AAA'));
2579         } catch (Exception $e) {
2580             //TODO: ignore case insensitive uniqueness problems for now
2581             //$this->fail("Unique index is case sensitive - this may cause problems in some tables");
2582         }
2584         try {
2585             $DB->insert_record($tablename, array('name'=>'aäa'));
2586             $DB->insert_record($tablename, array('name'=>'aáa'));
2587             $this->assertTrue(true);
2588         } catch (Exception $e) {
2589             $family = $DB->get_dbfamily();
2590             if ($family === 'mysql' or $family === 'mssql') {
2591                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
2592             } else {
2593                 // this should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness.
2594                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages.");
2595             }
2596             throw($e);
2597         }
2598     }
2600     function test_sql_binary_equal() {
2601         $DB = $this->tdb;
2602         $dbman = $DB->get_manager();
2604         $table = $this->get_test_table();
2605         $tablename = $table->getName();
2607         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2608         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2609         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2610         $dbman->create_table($table);
2612         $DB->insert_record($tablename, array('name'=>'aaa'));
2613         $DB->insert_record($tablename, array('name'=>'aáa'));
2614         $DB->insert_record($tablename, array('name'=>'aäa'));
2615         $DB->insert_record($tablename, array('name'=>'bbb'));
2616         $DB->insert_record($tablename, array('name'=>'BBB'));
2618         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('aaa'));
2619         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be accent sensitive');
2621         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('bbb'));
2622         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be case sensitive');
2623     }
2625     function test_sql_like() {
2626         $DB = $this->tdb;
2627         $dbman = $DB->get_manager();
2629         $table = $this->get_test_table();
2630         $tablename = $table->getName();
2632         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2633         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2634         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2635         $dbman->create_table($table);
2637         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
2638         $DB->insert_record($tablename, array('name'=>'Nodupor'));
2639         $DB->insert_record($tablename, array('name'=>'ouch'));
2640         $DB->insert_record($tablename, array('name'=>'ouc_'));
2641         $DB->insert_record($tablename, array('name'=>'ouc%'));
2642         $DB->insert_record($tablename, array('name'=>'aui'));
2643         $DB->insert_record($tablename, array('name'=>'aüi'));
2644         $DB->insert_record($tablename, array('name'=>'aÜi'));
2646         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false);
2647         $records = $DB->get_records_sql($sql, array("%dup_r%"));
2648         $this->assertEqual(count($records), 2);
2650         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
2651         $records = $DB->get_records_sql($sql, array("%dup%"));
2652         $this->assertEqual(count($records), 1);
2654         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?'); // defaults
2655         $records = $DB->get_records_sql($sql, array("%dup%"));
2656         $this->assertEqual(count($records), 1);
2658         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
2659         $records = $DB->get_records_sql($sql, array("ouc\\_"));
2660         $this->assertEqual(count($records), 1);
2662         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|');
2663         $records = $DB->get_records_sql($sql, array($DB->sql_like_escape("ouc%", '|')));
2664         $this->assertEqual(count($records), 1);
2666         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true);
2667         $records = $DB->get_records_sql($sql, array('aui'));
2668         $this->assertEqual(count($records), 1);
2670         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, true); // NOT LIKE
2671         $records = $DB->get_records_sql($sql, array("%o%"));
2672         $this->assertEqual(count($records), 3);
2674         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, true, true); // NOT ILIKE
2675         $records = $DB->get_records_sql($sql, array("%D%"));
2676         $this->assertEqual(count($records), 6);
2678         // TODO: we do not require accent insensitivness yet, just make sure it does not throw errors
2679         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, false);
2680         $records = $DB->get_records_sql($sql, array('aui'));
2681         //$this->assertEqual(count($records), 2, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
2682         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false);
2683         $records = $DB->get_records_sql($sql, array('aui'));
2684         //$this->assertEqual(count($records), 3, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
2685     }
2687     function test_sql_ilike() {
2688         // note: this is deprecated, just make sure it does not throw error
2689         $DB = $this->tdb;
2690         $dbman = $DB->get_manager();
2692         $table = $this->get_test_table();
2693         $tablename = $table->getName();
2695         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2696         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2697         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2698         $dbman->create_table($table);
2700         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
2701         $DB->insert_record($tablename, array('name'=>'NoDupor'));
2702         $DB->insert_record($tablename, array('name'=>'ouch'));
2704         // make sure it prints debug message
2705         $this->enable_debugging();
2706         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_ilike()." ?";
2707         $params = array("%dup_r%");
2708         $this->assertFalse($this->get_debugging() === '');
2710         // following must not throw exception, we ignore result
2711         $DB->get_records_sql($sql, $params);
2712     }
2714     function test_sql_concat() {
2715         $DB = $this->tdb;
2716         $dbman = $DB->get_manager();
2718         /// Testing all sort of values
2719         $sql = "SELECT ".$DB->sql_concat("?", "?", "?")." AS fullname ". $DB->sql_null_from_clause();
2720         // string, some unicode chars
2721         $params = array('name', 'áéíóú', 'name3');
2722         $this->assertEqual('nameáéíóúname3', $DB->get_field_sql($sql, $params));
2723         // string, spaces and numbers
2724         $params = array('name', '  ', 12345);
2725         $this->assertEqual('name  12345', $DB->get_field_sql($sql, $params));
2726         // float, empty and strings
2727         $params = array(123.45, '', 'test');
2728         $this->assertEqual('123.45test', $DB->get_field_sql($sql, $params));
2729         // float, null and strings
2730         $params = array(123.45, null, 'test');
2731         $this->assertNull($DB->get_field_sql($sql, $params), 'ANSI behaviour: Concatenating NULL must return NULL - But in Oracle :-(. [%s]'); // Concatenate NULL with anything result = NULL
2733         /// Testing fieldnames + values
2734         $table = $this->get_test_table();
2735         $tablename = $table->getName();
2737         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2738         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2739         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2740         $dbman->create_table($table);
2742         $DB->insert_record($tablename, array('description'=>'áéíóú'));
2743         $DB->insert_record($tablename, array('description'=>'dxxx'));
2744         $DB->insert_record($tablename, array('description'=>'bcde'));
2746         $sql = 'SELECT id, ' . $DB->sql_concat('description', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
2747         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
2748         $this->assertEqual(count($records), 3);
2749         $this->assertEqual($records[1]->result, 'áéíóúharcoded123.45test');
2750     }
2752     function test_concat_join() {
2753         $DB = $this->tdb;
2754         $sql = "SELECT ".$DB->sql_concat_join("' '", array("?", "?", "?"))." AS fullname ".$DB->sql_null_from_clause();
2755         $params = array("name", "name2", "name3");
2756         $result = $DB->get_field_sql($sql, $params);
2757         $this->assertEqual("name name2 name3", $result);
2758     }
2760     function test_sql_fullname() {
2761         $DB = $this->tdb;
2762         $sql = "SELECT ".$DB->sql_fullname(':first', ':last')." AS fullname ".$DB->sql_null_from_clause();
2763         $params = array('first'=>'Firstname', 'last'=>'Surname');
2764         $this->assertEqual("Firstname Surname", $DB->get_field_sql($sql, $params));
2765     }
2767     function sql_sql_order_by_text() {
2768         $DB = $this->tdb;
2769         $dbman = $DB->get_manager();
2771         $table = $this->get_test_table();
2772         $tablename = $table->getName();
2774         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2775         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2776         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2777         $dbman->create_table($table);
2779         $DB->insert_record($tablename, array('description'=>'abcd'));
2780         $DB->insert_record($tablename, array('description'=>'dxxx'));
2781         $DB->insert_record($tablename, array('description'=>'bcde'));
2783         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_order_by_text('description');
2784         $records = $DB->get_records_sql($sql);
2785         $first = array_shift($records);
2786         $this->assertEqual(1, $first->id);
2787         $second = array_shift($records);
2788         $this->assertEqual(3, $second->id);
2789         $last = array_shift($records);
2790         $this->assertEqual(2, $last->id);
2791     }
2793     function test_sql_substring() {
2794         $DB = $this->tdb;
2795         $dbman = $DB->get_manager();
2797         $table = $this->get_test_table();
2798         $tablename = $table->getName();
2800         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2801         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2802         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2803         $dbman->create_table($table);
2805         $string = 'abcdefghij';
2807         $DB->insert_record($tablename, array('name'=>$string));
2809         $sql = "SELECT id, ".$DB->sql_substr("name", 5)." AS name FROM {{$tablename}}";
2810         $record = $DB->get_record_sql($sql);
2811         $this->assertEqual(substr($string, 5-1), $record->name);
2813         $sql = "SELECT id, ".$DB->sql_substr("name", 5, 2)." AS name FROM {{$tablename}}";
2814         $record = $DB->get_record_sql($sql);
2815         $this->assertEqual(substr($string, 5-1, 2), $record->name);
2817         try {
2818             // silence php warning ;-)
2819             @$DB->sql_substr("name");
2820             $this->fail("Expecting an exception, none occurred");
2821         } catch (Exception $e) {
2822             $this->assertTrue($e instanceof coding_exception);
2823         }
2824     }
2826     function test_sql_length() {
2827         $DB = $this->tdb;
2828         $this->assertEqual($DB->get_field_sql(
2829                 "SELECT ".$DB->sql_length("'aeiou'").$DB->sql_null_from_clause()), 5);
2830         $this->assertEqual($DB->get_field_sql(
2831                 "SELECT ".$DB->sql_length("'áéíóú'").$DB->sql_null_from_clause()), 5);
2832     }
2834     function test_sql_position() {
2835         $DB = $this->tdb;
2836         $this->assertEqual($DB->get_field_sql(
2837                 "SELECT ".$DB->sql_position("'ood'", "'Moodle'").$DB->sql_null_from_clause()), 2);
2838         $this->assertEqual($DB->get_field_sql(
2839                 "SELECT ".$DB->sql_position("'Oracle'", "'Moodle'").$DB->sql_null_from_clause()), 0);
2840     }
2842     function test_sql_empty() {
2843         $DB = $this->tdb;
2844         $dbman = $DB->get_manager();
2846         $table = $this->get_test_table();
2847         $tablename = $table->getName();
2849         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2850         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2851         $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value');
2852         $table->add_field('namenotnullnodeflt', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2853         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2854         $dbman->create_table($table);
2856         $DB->insert_record($tablename, array('name'=>'', 'namenotnull'=>''));
2857         $DB->insert_record($tablename, array('name'=>null));
2858         $DB->insert_record($tablename, array('name'=>'lalala'));
2859         $DB->insert_record($tablename, array('name'=>0));
2861         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = '".$DB->sql_empty()."'");
2862         $this->assertEqual(count($records), 1);
2863         $record = reset($records);
2864         $this->assertEqual($record->name, '');
2866         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = '".$DB->sql_empty()."'");
2867         $this->assertEqual(count($records), 1);
2868         $record = reset($records);
2869         $this->assertEqual($record->namenotnull, '');
2871         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = '".$DB->sql_empty()."'");
2872         $this->assertEqual(count($records), 4);
2873         $record = reset($records);
2874         $this->assertEqual($record->namenotnullnodeflt, '');
2875     }
2877     function test_sql_isempty() {
2878         $DB = $this->tdb;
2879         $dbman = $DB->get_manager();
2881         $table = $this->get_test_table();
2882         $tablename = $table->getName();
2884         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2885         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2886         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2887         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
2888         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2889         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2890         $dbman->create_table($table);
2892         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
2893         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
2894         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
2895         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
2897         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'name', false, false));
2898         $this->assertEqual(count($records), 1);
2899         $record = reset($records);
2900         $this->assertEqual($record->name, '');
2902         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'namenull', true, false));
2903         $this->assertEqual(count($records), 1);
2904         $record = reset($records);
2905         $this->assertEqual($record->namenull, '');
2907         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'description', false, true));
2908         $this->assertEqual(count($records), 1);
2909         $record = reset($records);
2910         $this->assertEqual($record->description, '');
2912         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'descriptionnull', true, true));
2913         $this->assertEqual(count($records), 1);
2914         $record = reset($records);
2915         $this->assertEqual($record->descriptionnull, '');
2916     }
2918     function test_sql_isnotempty() {
2919         $DB = $this->tdb;
2920         $dbman = $DB->get_manager();
2922         $table = $this->get_test_table();
2923         $tablename = $table->getName();
2925         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2926         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
2927         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2928         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
2929         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2930         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2931         $dbman->create_table($table);
2933         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
2934         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
2935         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
2936         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
2938         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'name', false, false));
2939         $this->assertEqual(count($records), 3);
2940         $record = reset($records);
2941         $this->assertEqual($record->name, '??');
2943         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'namenull', true, false));
2944         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
2945         $record = reset($records);
2946         $this->assertEqual($record->namenull, 'la'); // so 'la' is the first non-empty 'namenull' record
2948         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'description', false, true));
2949         $this->assertEqual(count($records), 3);
2950         $record = reset($records);
2951         $this->assertEqual($record->description, '??');
2953         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'descriptionnull', true, true));
2954         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
2955         $record = reset($records);
2956         $this->assertEqual($record->descriptionnull, 'lalala'); // so 'lalala' is the first non-empty 'descriptionnull' record
2957     }
2959     function test_sql_regex() {
2960         $DB = $this->tdb;
2961         $dbman = $DB->get_manager();
2963         $table = $this->get_test_table();
2964         $tablename = $table->getName();
2966         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2967         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2968         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2969         $dbman->create_table($table);
2971         $DB->insert_record($tablename, array('name'=>'lalala'));
2972         $DB->insert_record($tablename, array('name'=>'holaaa'));
2973         $DB->insert_record($tablename, array('name'=>'aouch'));
2975         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex()." ?";
2976         $params = array('a$');
2977         if ($DB->sql_regex_supported()) {
2978             $records = $DB->get_records_sql($sql, $params);
2979             $this->assertEqual(count($records), 2);
2980         } else {
2981             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
2982         }
2984         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false)." ?";
2985         $params = array('.a');
2986         if ($DB->sql_regex_supported()) {
2987             $records = $DB->get_records_sql($sql, $params);
2988             $this->assertEqual(count($records), 1);
2989         } else {
2990             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
2991         }
2993     }
2995     /**
2996      * Test some more complex SQL syntax which moodle uses and depends on to work
2997      * useful to determine if new database libraries can be supported.
2998      */
2999     public function test_get_records_sql_complicated() {
3000         $DB = $this->tdb;
3001         $dbman = $DB->get_manager();
3003         $table = $this->get_test_table();
3004         $tablename = $table->getName();
3006         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3007         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3008         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3009         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3010         $dbman->create_table($table);
3012         $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello'));
3013         $DB->insert_record($tablename, array('course' => 3, 'content' => 'world'));
3014         $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello'));
3015         $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe'));
3017         // we have sql like this in moodle, this syntax breaks on older versions of sqlite for example..
3018         $sql = "SELECT a.id AS id, a.course AS course
3019                   FROM {{$tablename}} a
3020                   JOIN (SELECT * FROM {{$tablename}}) b ON a.id = b.id
3021                  WHERE a.course = ?";
3023         $this->assertTrue($records = $DB->get_records_sql($sql, array(3)));
3024         $this->assertEqual(2, count($records));
3025         $this->assertEqual(1, reset($records)->id);
3026         $this->assertEqual(2, next($records)->id);
3028         // do NOT try embedding sql_xxxx() helper functions in conditions array of count_records(), they don't break params/binding!
3029         $count = $DB->count_records_select($tablename, "course = :course AND ".$DB->sql_compare_text('content')." = :content", array('course' => 3, 'content' => 'hello'));
3030         $this->assertEqual(1, $count);
3031     }
3033     function test_onelevel_commit() {
3034         $DB = $this->tdb;
3035         $dbman = $DB->get_manager();
3037         $table = $this->get_test_table();
3038         $tablename = $table->getName();
3040         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3041         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3042         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3043         $dbman->create_table($table);
3045         $transaction = $DB->start_delegated_transaction();
3046         $data = (object)array('course'=>3);
3047         $this->assertEqual(0, $DB->count_records($tablename));
3048         $DB->insert_record($tablename, $data);
3049         $this->assertEqual(1, $DB->count_records($tablename));
3050         $transaction->allow_commit();
3051         $this->assertEqual(1, $DB->count_records($tablename));
3052     }
3054     function test_onelevel_rollback() {
3055         $DB = $this->tdb;
3056         $dbman = $DB->get_manager();
3058         $table = $this->get_test_table();
3059         $tablename = $table->getName();
3061         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3062         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3063         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3064         $dbman->create_table($table);
3066         // this might in fact encourage ppl to migrate from myisam to innodb
3068         $transaction = $DB->start_delegated_transaction();
3069         $data = (object)array('course'=>3);
3070         $this->assertEqual(0, $DB->count_records($tablename));
3071         $DB->insert_record($tablename, $data);
3072         $this->assertEqual(1, $DB->count_records($tablename));
3073         try {
3074             $transaction->rollback(new Exception('test'));
3075             $this->fail('transaction rollback must rethrow exception');
3076         } catch (Exception $e) {
3077         }
3078         $this->assertEqual(0, $DB->count_records($tablename));
3079     }
3081     function test_nested_transactions() {
3082         $DB = $this->tdb;
3083         $dbman = $DB->get_manager();
3085         $table = $this->get_test_table();
3086         $tablename = $table->getName();
3088         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3089         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3090         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3091         $dbman->create_table($table);
3093         // two level commit
3094         $this->assertFalse($DB->is_transaction_started());
3095         $transaction1 = $DB->start_delegated_transaction();
3096         $this->assertTrue($DB->is_transaction_started());
3097         $data = (object)array('course'=>3);
3098         $DB->insert_record($tablename, $data);
3099         $transaction2 = $DB->start_delegated_transaction();
3100         $data = (object)array('course'=>4);
3101         $DB->insert_record($tablename, $data);
3102         $transaction2->allow_commit();
3103         $this->assertTrue($DB->is_transaction_started());
3104         $transaction1->allow_commit();
3105         $this->assertFalse($DB->is_transaction_started());
3106         $this->assertEqual(2, $DB->count_records($tablename));
3108         $DB->delete_records($tablename);
3110         // rollback from top level
3111         $transaction1 = $DB->start_delegated_transaction();
3112         $data = (object)array('course'=>3);
3113         $DB->insert_record($tablename, $data);
3114         $transaction2 = $DB->start_delegated_transaction();
3115         $data = (object)array('course'=>4);
3116         $DB->insert_record($tablename, $data);
3117         $transaction2->allow_commit();
3118         try {
3119             $transaction1->rollback(new Exception('test'));
3120             $this->fail('transaction rollback must rethrow exception');
3121         } catch (Exception $e) {
3122             $this->assertEqual(get_class($e), 'Exception');
3123         }
3124         $this->assertEqual(0, $DB->count_records($tablename));
3126         $DB->delete_records($tablename);
3128         // rollback from nested level
3129         $transaction1 = $DB->start_delegated_transaction();
3130         $data = (object)array('course'=>3);
3131         $DB->insert_record($tablename, $data);
3132         $transaction2 = $DB->start_delegated_transaction();
3133         $data = (object)array('course'=>4);
3134         $DB->insert_record($tablename, $data);
3135         try {
3136             $transaction2->rollback(new Exception('test'));
3137             $this->fail('transaction rollback must rethrow exception');
3138         } catch (Exception $e) {
3139             $this->assertEqual(get_class($e), 'Exception');
3140         }
3141         $this->assertEqual(2, $DB->count_records($tablename)); // not rolled back yet
3142         try {
3143             $transaction1->allow_commit();
3144         } catch (Exception $e) {
3145             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3146         }
3147         $this->assertEqual(2, $DB->count_records($tablename)); // not rolled back yet
3148         // the forced rollback is done from the default_exception handler and similar places,
3149         // let's do it manually here
3150         $this->assertTrue($DB->is_transaction_started());
3151         $DB->force_transaction_rollback();
3152         $this->assertFalse($DB->is_transaction_started());
3153         $this->assertEqual(0, $DB->count_records($tablename)); // finally rolled back
3155         $DB->delete_records($tablename);
3156     }
3158     function test_transactions_forbidden() {
3159         $DB = $this->tdb;
3160         $dbman = $DB->get_manager();
3162         $table = $this->get_test_table();
3163         $tablename = $table->getName();
3165         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3166         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3167         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3168         $dbman->create_table($table);
3170         $DB->transactions_forbidden();
3171         $transaction = $DB->start_delegated_transaction();
3172         $data = (object)array('course'=>1);
3173         $DB->insert_record($tablename, $data);
3174         try {
3175             $DB->transactions_forbidden();
3176         } catch (Exception $e) {
3177             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3178         }
3179         // the previous test does not force rollback
3180         $transaction->allow_commit();
3181         $this->assertFalse($DB->is_transaction_started());
3182         $this->assertEqual(1, $DB->count_records($tablename));
3183     }
3185     function test_wrong_transactions() {
3186         $DB = $this->tdb;
3187         $dbman = $DB->get_manager();
3189         $table = $this->get_test_table();
3190         $tablename = $table->getName();
3192         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3193         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3194         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3195         $dbman->create_table($table);
3198         // wrong order of nested commits
3199         $transaction1 = $DB->start_delegated_transaction();
3200         $data = (object)array('course'=>3);
3201         $DB->insert_record($tablename, $data);
3202         $transaction2 = $DB->start_delegated_transaction();
3203         $data = (object)array('course'=>4);
3204         $DB->insert_record($tablename, $data);
3205         try {
3206             $transaction1->allow_commit();
3207             $this->fail('wrong order of commits must throw exception');
3208         } catch (Exception $e) {
3209             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3210         }
3211         try {
3212             $transaction2->allow_commit();
3213             $this->fail('first wrong commit forces rollback');
3214         } catch (Exception $e) {
3215             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3216         }
3217         // this is done in default exception handler usually
3218         $this->assertTrue($DB->is_transaction_started());
3219         $this->assertEqual(2, $DB->count_records($tablename)); // not rolled back yet
3220         $DB->force_transaction_rollback();
3221         $this->assertEqual(0, $DB->count_records($tablename));
3222         $DB->delete_records($tablename);
3225         // wrong order of nested rollbacks
3226         $transaction1 = $DB->start_delegated_transaction();
3227         $data = (object)array('course'=>3);
3228         $DB->insert_record($tablename, $data);
3229         $transaction2 = $DB->start_delegated_transaction();
3230         $data = (object)array('course'=>4);
3231         $DB->insert_record($tablename, $data);
3232         try {
3233             // this first rollback should prevent all other rollbacks
3234             $transaction1->rollback(new Exception('test'));
3235         } catch (Exception $e) {
3236             $this->assertEqual(get_class($e), 'Exception');
3237         }
3238         try {
3239             $transaction2->rollback(new Exception('test'));
3240         } catch (Exception $e) {
3241             $this->assertEqual(get_class($e), 'Exception');
3242         }
3243         try {
3244             $transaction1->rollback(new Exception('test'));
3245         } catch (Exception $e) {
3246             // the rollback was used already once, no way to use it again
3247             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3248         }
3249         // this is done in default exception handler usually
3250         $this->assertTrue($DB->is_transaction_started());
3251         $DB->force_transaction_rollback();
3252         $DB->delete_records($tablename);
3255         // unknown transaction object
3256         $transaction1 = $DB->start_delegated_transaction();
3257         $data = (object)array('course'=>3);
3258         $DB->insert_record($tablename, $data);
3259         $transaction2 = new moodle_transaction($DB);
3260         try {
3261             $transaction2->allow_commit();
3262             $this->fail('foreign transaction must fail');
3263         } catch (Exception $e) {
3264             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3265         }
3266         try {
3267             $transaction1->allow_commit();
3268             $this->fail('first wrong commit forces rollback');
3269         } catch (Exception $e) {
3270             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3271         }
3272         $DB->force_transaction_rollback();
3273         $DB->delete_records($tablename);
3274     }
3276     function test_concurent_transactions() {
3277         // Notes about this test:
3278         // 1- MySQL needs to use one engine with transactions support (InnoDB).
3279         // 2- MSSQL needs to have enabled versioning for read committed
3280         //    transactions (ALTER DATABASE xxx SET READ_COMMITTED_SNAPSHOT ON)
3281         $DB = $this->tdb;
3282         $dbman = $DB->get_manager();
3284         $table = $this->get_test_table();
3285         $tablename = $table->getName();
3287         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3288         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3289         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3290         $dbman->create_table($table);
3292         $transaction = $DB->start_delegated_transaction();
3293         $data = (object)array('course'=>1);
3294         $this->assertEqual(0, $DB->count_records($tablename));
3295         $DB->insert_record($tablename, $data);
3296         $this->assertEqual(1, $DB->count_records($tablename));
3298         //open second connection
3299         $cfg = $DB->export_dbconfig();
3300         if (!isset($cfg->dboptions)) {
3301             $cfg->dboptions = array();
3302         }
3303         $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
3304         $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
3306         // second instance should not see pending inserts
3307         $this->assertEqual(0, $DB2->count_records($tablename));
3308         $data = (object)array('course'=>2);
3309         $DB2->insert_record($tablename, $data);
3310         $this->assertEqual(1, $DB2->count_records($tablename));
3312         // first should see the changes done from second
3313         $this->assertEqual(2, $DB->count_records($tablename));
3315         // now commit and we should see it finally in second connections
3316         $transaction->allow_commit();
3317         $this->assertEqual(2, $DB2->count_records($tablename));
3319         $DB2->dispose();
3320     }
3322     public function test_bound_param_types() {
3323         $DB = $this->tdb;
3324         $dbman = $DB->get_manager();
3326         $table = $this->get_test_table();
3327         $tablename = $table->getName();
3329         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3330         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3331         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3332         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3333         $dbman->create_table($table);
3335         $this->assertTrue($DB->insert_record($tablename, array('name' => '1', 'content'=>'xx')));
3336         $this->assertTrue($DB->insert_record($tablename, array('name' => 2, 'content'=>'yy')));
3337         $this->assertTrue($DB->insert_record($tablename, array('name' => 'somestring', 'content'=>'zz')));
3338         $this->assertTrue($DB->insert_record($tablename, array('name' => 'aa', 'content'=>'1')));
3339         $this->assertTrue($DB->insert_record($tablename, array('name' => 'bb', 'content'=>2)));
3340         $this->assertTrue($DB->insert_record($tablename, array('name' => 'cc', 'content'=>'sometext')));
3343         // Conditions in CHAR columns
3344         $this->assertTrue($DB->record_exists($tablename, array('name'=>1)));
3345         $this->assertTrue($DB->record_exists($tablename, array('name'=>'1')));
3346         $this->assertFalse($DB->record_exists($tablename, array('name'=>111)));
3347         $this->assertTrue($DB->get_record($tablename, array('name'=>1)));
3348         $this->assertTrue($DB->get_record($tablename, array('name'=>'1')));
3349         $this->assertFalse($DB->get_record($tablename, array('name'=>111)));
3350         $sqlqm = "SELECT *
3351                     FROM {{$tablename}}
3352                    WHERE name = ?";
3353         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array(1)));
3354         $this->assertEqual(1, count($records));
3355         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array('1')));
3356         $this->assertEqual(1, count($records));
3357         $records = $DB->get_records_sql($sqlqm, array(222));
3358         $this->assertEqual(0, count($records));
3359         $sqlnamed = "SELECT *
3360                        FROM {{$tablename}}
3361                       WHERE name = :name";
3362         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('name' => 2)));
3363         $this->assertEqual(1, count($records));
3364         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('name' => '2')));
3365         $this->assertEqual(1, count($records));
3367         // Conditions in TEXT columns always must be performed with the sql_compare_text
3368         // helper function on both sides of the condition
3369         $sqlqm = "SELECT *
3370                     FROM {{$tablename}}
3371                    WHERE " . $DB->sql_compare_text('content') . " =  " . $DB->sql_compare_text('?');
3372         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array('1')));
3373         $this->assertEqual(1, count($records));
3374         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array(1)));
3375         $this->assertEqual(1, count($records));
3376         $sqlnamed = "SELECT *
3377                        FROM {{$tablename}}
3378                       WHERE " . $DB->sql_compare_text('content') . " =  " . $DB->sql_compare_text(':content');
3379         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('content' => 2)));
3380         $this->assertEqual(1, count($records));
3381         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('content' => '2')));
3382         $this->assertEqual(1, count($records));
3383     }
3386 /**
3387  * This class is not a proper subclass of moodle_database. It is
3388  * intended to be used only in unit tests, in order to gain access to the
3389  * protected methods of moodle_database, and unit test them.
3390  */
3391 class moodle_database_for_testing extends moodle_database {
3392     protected $prefix = 'mdl_';
3394     public function public_fix_table_names($sql) {
3395         return $this->fix_table_names($sql);
3396     }
3398     public function driver_installed(){}
3399     public function get_dbfamily(){}
3400     protected function get_dbtype(){}
3401     protected function get_dblibrary(){}
3402     public function get_name(){}
3403     public function get_configuration_help(){}
3404     public function get_configuration_hints(){}
3405     public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null){}
3406     public function get_server_info(){}
3407     protected function allowed_param_types(){}
3408     public function get_last_error(){}
3409     public function get_tables($usecache=true){}
3410     public function get_indexes($table){}
3411     public function get_columns($table, $usecache=true){}
3412     protected function normalise_value($column, $value){}
3413     public function set_debug($state){}
3414     public function get_debug(){}
3415     public function set_logging($state){}
3416     public function change_database_structure($sql){}
3417     public function execute($sql, array $params=null){}
3418     public function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0){}
3419     public function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0){}
3420     public function get_fieldset_sql($sql, array $params=null){}
3421     public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false){}
3422     public function insert_record($table, $dataobject, $returnid=true, $bulk=false){}
3423     public function import_record($table, $dataobject){}
3424     public function update_record_raw($table, $params, $bulk=false){}
3425     public function update_record($table, $dataobject, $bulk=false){}
3426     public function set_field_select($table, $newfield, $newvalue, $select, array $params=null){}
3427     public function delete_records_select($table, $select, array $params=null){}
3428     public function sql_concat(){}
3429     public function sql_concat_join($separator="' '", $elements=array()){}
3430     public function sql_substr($expr, $start, $length=false){}
3431     public function begin_transaction() {}
3432     public function commit_transaction() {}
3433     public function rollback_transaction() {}