ab7107e04f1f34c4d02f2438081ed3d13fae2dff
[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_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
638         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
639         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
640         $dbman->create_table($table);
642         $data = array(array('id' => 1, 'course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
643                       array('id' => 2, 'course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
644                       array('id' => 3, 'course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
646         foreach ($data as $record) {
647             $DB->insert_record($tablename, $record);
648         }
650         // standard recordset iteration
651         $rs = $DB->get_recordset($tablename);
652         $this->assertTrue($rs instanceof moodle_recordset);
653         reset($data);
654         foreach($rs as $record) {
655             $data_record = current($data);
656             foreach ($record as $k => $v) {
657                 $this->assertEqual($data_record[$k], $v);
658             }
659             next($data);
660         }
661         $rs->close();
663         // iterator style usage
664         $rs = $DB->get_recordset($tablename);
665         $this->assertTrue($rs instanceof moodle_recordset);
666         reset($data);
667         while ($rs->valid()) {
668             $record = $rs->current();
669             $data_record = current($data);
670             foreach ($record as $k => $v) {
671                 $this->assertEqual($data_record[$k], $v);
672             }
673             next($data);
674             $rs->next();
675         }
676         $rs->close();
678         // make sure rewind is ignored
679         $rs = $DB->get_recordset($tablename);
680         $this->assertTrue($rs instanceof moodle_recordset);
681         reset($data);
682         $i = 0;
683         foreach($rs as $record) {
684             $i++;
685             $rs->rewind();
686             if ($i > 10) {
687                 $this->fail('revind not ignored in recordsets');
688                 break;
689             }
690             $data_record = current($data);
691             foreach ($record as $k => $v) {
692                 $this->assertEqual($data_record[$k], $v);
693             }
694             next($data);
695         }
696         $rs->close();
698         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
699         $conditions = array('onetext' => '1');
700         try {
701             $rs = $DB->get_recordset($tablename, $conditions);
702             $this->fail('An Exception is missing, expected due to equating of text fields');
703         } catch (exception $e) {
704             $this->assertTrue($e instanceof dml_exception);
705             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
706         }
708         // notes:
709         //  * limits are tested in test_get_recordset_sql()
710         //  * where_clause() is used internally and is tested in test_get_records()
711     }
713     public function test_get_recordset_iterator_keys() {
714         $DB = $this->tdb;
715         $dbman = $DB->get_manager();
717         $table = $this->get_test_table();
718         $tablename = $table->getName();
720         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
721         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
722         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
723         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
724         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
725         $dbman->create_table($table);
727         $data = array(array('id'=> 1, 'course' => 3, 'name' => 'record1'),
728                       array('id'=> 2, 'course' => 3, 'name' => 'record2'),
729                       array('id'=> 3, 'course' => 5, 'name' => 'record3'));
730         foreach ($data as $record) {
731             $DB->insert_record($tablename, $record);
732         }
734         // Test repeated numeric keys are returned ok
735         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
737         reset($data);
738         $count = 0;
739         foreach($rs as $key => $record) {
740             $data_record = current($data);
741             $this->assertEqual($data_record['course'], $key);
742             next($data);
743             $count++;
744         }
745         $rs->close();
746         $this->assertEqual($count, 3);
748         // Test string keys are returned ok
749         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
751         reset($data);
752         $count = 0;
753         foreach($rs as $key => $record) {
754             $data_record = current($data);
755             $this->assertEqual($data_record['name'], $key);
756             next($data);
757             $count++;
758         }
759         $rs->close();
760         $this->assertEqual($count, 3);
762         // Test numeric not starting in 1 keys are returned ok
763         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
765         $data = array_reverse($data);
766         reset($data);
767         $count = 0;
768         foreach($rs as $key => $record) {
769             $data_record = current($data);
770             $this->assertEqual($data_record['id'], $key);
771             next($data);
772             $count++;
773         }
774         $rs->close();
775         $this->assertEqual($count, 3);
776     }
778     public function test_get_recordset_list() {
779         $DB = $this->tdb;
780         $dbman = $DB->get_manager();
782         $table = $this->get_test_table();
783         $tablename = $table->getName();
785         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
786         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
787         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
788         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
789         $dbman->create_table($table);
791         $DB->insert_record($tablename, array('course' => 3));
792         $DB->insert_record($tablename, array('course' => 3));
793         $DB->insert_record($tablename, array('course' => 5));
794         $DB->insert_record($tablename, array('course' => 2));
796         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
798         $counter = 0;
799         foreach ($rs as $record) {
800             $counter++;
801         }
802         $this->assertEqual(3, $counter);
803         $rs->close();
805         $rs = $DB->get_recordset_list($tablename, 'course',array()); /// Must return 0 rows without conditions. MDL-17645
807         $counter = 0;
808         foreach ($rs as $record) {
809             $counter++;
810         }
811         $rs->close();
812         $this->assertEqual(0, $counter);
814         // notes:
815         //  * limits are tested in test_get_recordset_sql()
816         //  * where_clause() is used internally and is tested in test_get_records()
817     }
819     public function test_get_recordset_select() {
820         $DB = $this->tdb;
821         $dbman = $DB->get_manager();
823         $table = $this->get_test_table();
824         $tablename = $table->getName();
826         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
827         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
828         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
829         $dbman->create_table($table);
831         $DB->insert_record($tablename, array('course' => 3));
832         $DB->insert_record($tablename, array('course' => 3));
833         $DB->insert_record($tablename, array('course' => 5));
834         $DB->insert_record($tablename, array('course' => 2));
836         $rs = $DB->get_recordset_select($tablename, '');
837         $counter = 0;
838         foreach ($rs as $record) {
839             $counter++;
840         }
841         $rs->close();
842         $this->assertEqual(4, $counter);
844         $this->assertTrue($rs = $DB->get_recordset_select($tablename, 'course = 3'));
845         $counter = 0;
846         foreach ($rs as $record) {
847             $counter++;
848         }
849         $rs->close();
850         $this->assertEqual(2, $counter);
852         // notes:
853         //  * limits are tested in test_get_recordset_sql()
854     }
856     public function test_get_recordset_sql() {
857         $DB = $this->tdb;
858         $dbman = $DB->get_manager();
860         $table = $this->get_test_table();
861         $tablename = $table->getName();
863         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
864         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
865         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
866         $dbman->create_table($table);
868         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
869         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
870         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
871         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
872         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
873         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
874         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
876         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
877         $counter = 0;
878         foreach ($rs as $record) {
879             $counter++;
880         }
881         $rs->close();
882         $this->assertEqual(2, $counter);
884         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
885         // only limitfrom = skips that number of records
886         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
887         $records = array();
888         foreach($rs as $key => $record) {
889             $records[$key] = $record;
890         }
891         $rs->close();
892         $this->assertEqual(5, count($records));
893         $this->assertEqual($inskey3, reset($records)->id);
894         $this->assertEqual($inskey7, end($records)->id);
896         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
897     }
899     public function test_get_records() {
900         $DB = $this->tdb;
901         $dbman = $DB->get_manager();
903         $table = $this->get_test_table();
904         $tablename = $table->getName();
906         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
907         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
908         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
909         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
910         $dbman->create_table($table);
912         $DB->insert_record($tablename, array('course' => 3));
913         $DB->insert_record($tablename, array('course' => 3));
914         $DB->insert_record($tablename, array('course' => 5));
915         $DB->insert_record($tablename, array('course' => 2));
917         // All records
918         $records = $DB->get_records($tablename);
919         $this->assertEqual(4, count($records));
920         $this->assertEqual(3, $records[1]->course);
921         $this->assertEqual(3, $records[2]->course);
922         $this->assertEqual(5, $records[3]->course);
923         $this->assertEqual(2, $records[4]->course);
925         // Records matching certain conditions
926         $records = $DB->get_records($tablename, array('course' => 3));
927         $this->assertEqual(2, count($records));
928         $this->assertEqual(3, $records[1]->course);
929         $this->assertEqual(3, $records[2]->course);
931         // All records sorted by course
932         $records = $DB->get_records($tablename, null, 'course');
933         $this->assertEqual(4, count($records));
934         $current_record = reset($records);
935         $this->assertEqual(4, $current_record->id);
936         $current_record = next($records);
937         $this->assertEqual(1, $current_record->id);
938         $current_record = next($records);
939         $this->assertEqual(2, $current_record->id);
940         $current_record = next($records);
941         $this->assertEqual(3, $current_record->id);
943         // All records, but get only one field
944         $records = $DB->get_records($tablename, null, '', 'id');
945         $this->assertFalse(isset($records[1]->course));
946         $this->assertTrue(isset($records[1]->id));
947         $this->assertEqual(4, count($records));
949         // Booleans into params
950         $records = $DB->get_records($tablename, array('course' => true));
951         $this->assertEqual(0, count($records));
952         $records = $DB->get_records($tablename, array('course' => false));
953         $this->assertEqual(0, count($records));
955         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
956         $conditions = array('onetext' => '1');
957         try {
958             $records = $DB->get_records($tablename, $conditions);
959             $this->fail('An Exception is missing, expected due to equating of text fields');
960         } catch (exception $e) {
961             $this->assertTrue($e instanceof dml_exception);
962             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
963         }
965         // note: delegate limits testing to test_get_records_sql()
966     }
968     public function test_get_records_list() {
969         $DB = $this->tdb;
970         $dbman = $DB->get_manager();
972         $table = $this->get_test_table();
973         $tablename = $table->getName();
975         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
976         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
977         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
978         $dbman->create_table($table);
980         $DB->insert_record($tablename, array('course' => 3));
981         $DB->insert_record($tablename, array('course' => 3));
982         $DB->insert_record($tablename, array('course' => 5));
983         $DB->insert_record($tablename, array('course' => 2));
985         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
986         $this->assertTrue(is_array($records));
987         $this->assertEqual(3, count($records));
988         $this->assertEqual(1, reset($records)->id);
989         $this->assertEqual(2, next($records)->id);
990         $this->assertEqual(4, next($records)->id);
992         $this->assertIdentical(array(), $records = $DB->get_records_list($tablename, 'course', array())); /// Must return 0 rows without conditions. MDL-17645
993         $this->assertEqual(0, count($records));
995         // note: delegate limits testing to test_get_records_sql()
996     }
998     public function test_get_records_sql() {
999         $DB = $this->tdb;
1000         $dbman = $DB->get_manager();
1002         $table = $this->get_test_table();
1003         $tablename = $table->getName();
1005         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1006         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1007         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1008         $dbman->create_table($table);
1010         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1011         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1012         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1013         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1014         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1015         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1016         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1018         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1019         $this->assertEqual(2, count($records));
1020         $this->assertEqual($inskey1, reset($records)->id);
1021         $this->assertEqual($inskey4, next($records)->id);
1023         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1024         $this->enable_debugging();
1025         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1026         $this->assertFalse($this->get_debugging() === '');
1027         $this->assertEqual(6, count($records));
1029         // negative limits = no limits
1030         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1031         $this->assertEqual(7, count($records));
1033         // zero limits = no limits
1034         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1035         $this->assertEqual(7, count($records));
1037         // only limitfrom = skips that number of records
1038         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1039         $this->assertEqual(5, count($records));
1040         $this->assertEqual($inskey3, reset($records)->id);
1041         $this->assertEqual($inskey7, end($records)->id);
1043         // only limitnum = fetches that number of records
1044         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1045         $this->assertEqual(3, count($records));
1046         $this->assertEqual($inskey1, reset($records)->id);
1047         $this->assertEqual($inskey3, end($records)->id);
1049         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1050         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1051         $this->assertEqual(2, count($records));
1052         $this->assertEqual($inskey4, reset($records)->id);
1053         $this->assertEqual($inskey5, end($records)->id);
1055         // both limitfrom and limitnum in query having subqueris
1056         // note the subquery skips records with course = 0 and 3
1057         $sql = "SELECT * FROM {{$tablename}}
1058                  WHERE course NOT IN (
1059                      SELECT course FROM {{$tablename}}
1060                       WHERE course IN (0, 3))
1061                 ORDER BY course";
1062         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2
1063         $this->assertEqual(2, count($records));
1064         $this->assertEqual($inskey6, reset($records)->id);
1065         $this->assertEqual($inskey5, end($records)->id);
1066         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2
1067         $this->assertEqual(2, count($records));
1068         $this->assertEqual($inskey3, reset($records)->id);
1069         $this->assertEqual($inskey2, end($records)->id);
1071         // TODO: Test limits in queries having DISTINCT clauses
1073         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1074     }
1076     public function test_get_records_menu() {
1077         $DB = $this->tdb;
1078         $dbman = $DB->get_manager();
1080         $table = $this->get_test_table();
1081         $tablename = $table->getName();
1083         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1084         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1085         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1086         $dbman->create_table($table);
1088         $DB->insert_record($tablename, array('course' => 3));
1089         $DB->insert_record($tablename, array('course' => 3));
1090         $DB->insert_record($tablename, array('course' => 5));
1091         $DB->insert_record($tablename, array('course' => 2));
1093         $records = $DB->get_records_menu($tablename, array('course' => 3));
1094         $this->assertTrue(is_array($records));
1095         $this->assertEqual(2, count($records));
1096         $this->assertFalse(empty($records[1]));
1097         $this->assertFalse(empty($records[2]));
1098         $this->assertEqual(3, $records[1]);
1099         $this->assertEqual(3, $records[2]);
1101         // note: delegate limits testing to test_get_records_sql()
1102     }
1104     public function test_get_records_select_menu() {
1105         $DB = $this->tdb;
1106         $dbman = $DB->get_manager();
1108         $table = $this->get_test_table();
1109         $tablename = $table->getName();
1111         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1112         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1113         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1114         $dbman->create_table($table);
1116         $DB->insert_record($tablename, array('course' => 3));
1117         $DB->insert_record($tablename, array('course' => 2));
1118         $DB->insert_record($tablename, array('course' => 3));
1119         $DB->insert_record($tablename, array('course' => 5));
1121         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1122         $this->assertTrue(is_array($records));
1124         $this->assertEqual(3, count($records));
1125         $this->assertFalse(empty($records[1]));
1126         $this->assertTrue(empty($records[2]));
1127         $this->assertFalse(empty($records[3]));
1128         $this->assertFalse(empty($records[4]));
1129         $this->assertEqual(3, $records[1]);
1130         $this->assertEqual(3, $records[3]);
1131         $this->assertEqual(5, $records[4]);
1133         // note: delegate limits testing to test_get_records_sql()
1134     }
1136     public function test_get_records_sql_menu() {
1137         $DB = $this->tdb;
1138         $dbman = $DB->get_manager();
1140         $table = $this->get_test_table();
1141         $tablename = $table->getName();
1143         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1144         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1145         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1146         $dbman->create_table($table);
1148         $DB->insert_record($tablename, array('course' => 3));
1149         $DB->insert_record($tablename, array('course' => 2));
1150         $DB->insert_record($tablename, array('course' => 3));
1151         $DB->insert_record($tablename, array('course' => 5));
1153         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1154         $this->assertTrue(is_array($records));
1156         $this->assertEqual(3, count($records));
1157         $this->assertFalse(empty($records[1]));
1158         $this->assertTrue(empty($records[2]));
1159         $this->assertFalse(empty($records[3]));
1160         $this->assertFalse(empty($records[4]));
1161         $this->assertEqual(3, $records[1]);
1162         $this->assertEqual(3, $records[3]);
1163         $this->assertEqual(5, $records[4]);
1165         // note: delegate limits testing to test_get_records_sql()
1166     }
1168     public function test_get_record() {
1169         $DB = $this->tdb;
1170         $dbman = $DB->get_manager();
1172         $table = $this->get_test_table();
1173         $tablename = $table->getName();
1175         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1176         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1177         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1178         $dbman->create_table($table);
1180         $DB->insert_record($tablename, array('course' => 3));
1181         $DB->insert_record($tablename, array('course' => 2));
1183         $record = $DB->get_record($tablename, array('id' => 2));
1184         $this->assertTrue($record instanceof stdClass);
1186         $this->assertEqual(2, $record->course);
1187         $this->assertEqual(2, $record->id);
1188     }
1191     public function test_get_record_select() {
1192         $DB = $this->tdb;
1193         $dbman = $DB->get_manager();
1195         $table = $this->get_test_table();
1196         $tablename = $table->getName();
1198         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1199         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1200         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1201         $dbman->create_table($table);
1203         $DB->insert_record($tablename, array('course' => 3));
1204         $DB->insert_record($tablename, array('course' => 2));
1206         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1207         $this->assertTrue($record instanceof stdClass);
1209         $this->assertEqual(2, $record->course);
1211         // note: delegates limit testing to test_get_records_sql()
1212     }
1214     public function test_get_record_sql() {
1215         $DB = $this->tdb;
1216         $dbman = $DB->get_manager();
1218         $table = $this->get_test_table();
1219         $tablename = $table->getName();
1221         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1222         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1223         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1224         $dbman->create_table($table);
1226         $DB->insert_record($tablename, array('course' => 3));
1227         $DB->insert_record($tablename, array('course' => 2));
1229         // standard use
1230         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1231         $this->assertTrue($record instanceof stdClass);
1232         $this->assertEqual(2, $record->course);
1233         $this->assertEqual(2, $record->id);
1235         // backwards compatibility with $ignoremultiple
1236         $this->assertFalse(IGNORE_MISSING);
1237         $this->assertTrue(IGNORE_MULTIPLE);
1239         // record not found - ignore
1240         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1241         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1243         // record not found error
1244         try {
1245             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1246             $this->fail("Exception expected");
1247         } catch (dml_missing_record_exception $e) {
1248             $this->assertTrue(true);
1249         }
1251         $this->enable_debugging();
1252         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1253         $this->assertFalse($this->get_debugging() === '');
1255         // multiple matches ignored
1256         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1258         // multiple found error
1259         try {
1260             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1261             $this->fail("Exception expected");
1262         } catch (dml_multiple_records_exception $e) {
1263             $this->assertTrue(true);
1264         }
1265     }
1267     public function test_get_field() {
1268         $DB = $this->tdb;
1269         $dbman = $DB->get_manager();
1271         $table = $this->get_test_table();
1272         $tablename = $table->getName();
1274         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1275         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1276         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1277         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1278         $dbman->create_table($table);
1280         $id1 = $DB->insert_record($tablename, array('course' => 3));
1281         $DB->insert_record($tablename, array('course' => 5));
1282         $DB->insert_record($tablename, array('course' => 5));
1284         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1285         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1287         $this->assertIdentical(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1288         try {
1289             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1290             $this->assertFail('Exception expected due to missing record');
1291         } catch (dml_exception $ex) {
1292             $this->assertTrue(true);
1293         }
1295         $this->enable_debugging();
1296         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1297         $this->assertIdentical($this->get_debugging(), '');
1299         $this->enable_debugging();
1300         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1301         $this->assertFalse($this->get_debugging() === '');
1303         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1304         $conditions = array('onetext' => '1');
1305         try {
1306             $DB->get_field($tablename, 'course', $conditions);
1307             $this->fail('An Exception is missing, expected due to equating of text fields');
1308         } catch (exception $e) {
1309             $this->assertTrue($e instanceof dml_exception);
1310             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1311         }
1312     }
1314     public function test_get_field_select() {
1315         $DB = $this->tdb;
1316         $dbman = $DB->get_manager();
1318         $table = $this->get_test_table();
1319         $tablename = $table->getName();
1321         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1322         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1323         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1324         $dbman->create_table($table);
1326         $DB->insert_record($tablename, array('course' => 3));
1328         $this->assertEqual(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1329     }
1331     public function test_get_field_sql() {
1332         $DB = $this->tdb;
1333         $dbman = $DB->get_manager();
1335         $table = $this->get_test_table();
1336         $tablename = $table->getName();
1338         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1339         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1340         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1341         $dbman->create_table($table);
1343         $DB->insert_record($tablename, array('course' => 3));
1345         $this->assertEqual(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1346     }
1348     public function test_get_fieldset_select() {
1349         $DB = $this->tdb;
1350         $dbman = $DB->get_manager();
1352         $table = $this->get_test_table();
1353         $tablename = $table->getName();
1355         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1356         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1357         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1358         $dbman->create_table($table);
1360         $DB->insert_record($tablename, array('course' => 1));
1361         $DB->insert_record($tablename, array('course' => 3));
1362         $DB->insert_record($tablename, array('course' => 2));
1363         $DB->insert_record($tablename, array('course' => 6));
1365         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1366         $this->assertTrue(is_array($fieldset));
1368         $this->assertEqual(3, count($fieldset));
1369         $this->assertEqual(3, $fieldset[0]);
1370         $this->assertEqual(2, $fieldset[1]);
1371         $this->assertEqual(6, $fieldset[2]);
1372     }
1374     public function test_get_fieldset_sql() {
1375         $DB = $this->tdb;
1376         $dbman = $DB->get_manager();
1378         $table = $this->get_test_table();
1379         $tablename = $table->getName();
1381         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1382         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1383         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1384         $dbman->create_table($table);
1386         $DB->insert_record($tablename, array('course' => 1));
1387         $DB->insert_record($tablename, array('course' => 3));
1388         $DB->insert_record($tablename, array('course' => 2));
1389         $DB->insert_record($tablename, array('course' => 6));
1391         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1392         $this->assertTrue(is_array($fieldset));
1394         $this->assertEqual(3, count($fieldset));
1395         $this->assertEqual(2, $fieldset[0]);
1396         $this->assertEqual(3, $fieldset[1]);
1397         $this->assertEqual(4, $fieldset[2]);
1398     }
1400     public function test_insert_record_raw() {
1401         $DB = $this->tdb;
1402         $dbman = $DB->get_manager();
1404         $table = $this->get_test_table();
1405         $tablename = $table->getName();
1407         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1408         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1409         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1410         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1411         $dbman->create_table($table);
1413         $record = (object)array('course' => 1, 'onechar' => 'xx');
1414         $before = clone($record);
1415         $result = $DB->insert_record_raw($tablename, $record);
1416         $this->assertIdentical(1, $result);
1417         $this->assertIdentical($record, $before);
1419         $record = $DB->get_record($tablename, array('course' => 1));
1420         $this->assertTrue($record instanceof stdClass);
1421         $this->assertIdentical('xx', $record->onechar);
1423         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1424         $this->assertIdentical(true, $result);
1426         // note: bulk not implemented yet
1427         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1428         $record = $DB->get_record($tablename, array('course' => 3));
1429         $this->assertTrue($record instanceof stdClass);
1430         $this->assertIdentical('zz', $record->onechar);
1432         // custom sequence (id) - returnid is ignored
1433         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1434         $this->assertIdentical(true, $result);
1435         $record = $DB->get_record($tablename, array('id' => 10));
1436         $this->assertTrue($record instanceof stdClass);
1437         $this->assertIdentical('bb', $record->onechar);
1439         // custom sequence - missing id error
1440         try {
1441             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1442             $this->assertFail('Exception expected due to missing record');
1443         } catch (coding_exception $ex) {
1444             $this->assertTrue(true);
1445         }
1447         // wrong column error
1448         try {
1449             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1450             $this->assertFail('Exception expected due to invalid column');
1451         } catch (dml_write_exception $ex) {
1452             $this->assertTrue(true);
1453         }
1454     }
1456     public function test_insert_record() {
1457         // All the information in this test is fetched from DB by get_recordset() so we
1458         // have such method properly tested against nulls, empties and friends...
1460         $DB = $this->tdb;
1461         $dbman = $DB->get_manager();
1463         $table = $this->get_test_table();
1464         $tablename = $table->getName();
1466         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1467         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1468         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1469         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1470         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1471         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1472         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1473         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1474         $dbman->create_table($table);
1476         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1477         $record = $DB->get_record($tablename, array('course' => 1));
1478         $this->assertEqual(1, $record->id);
1479         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1480         $this->assertEqual(200, $record->onenum);
1481         $this->assertIdentical('onestring', $record->onechar);
1482         $this->assertNull($record->onetext);
1483         $this->assertNull($record->onebinary);
1485         // without returning id, bulk not implemented
1486         $result = $this->assertIdentical(true, $DB->insert_record($tablename, array('course' => 99), false, true));
1487         $record = $DB->get_record($tablename, array('course' => 99));
1488         $this->assertEqual(2, $record->id);
1489         $this->assertEqual(99, $record->course);
1491         // Check nulls are set properly for all types
1492         $record = new stdClass();
1493         $record->oneint = null;
1494         $record->onenum = null;
1495         $record->onechar = null;
1496         $record->onetext = null;
1497         $record->onebinary = null;
1498         $recid = $DB->insert_record($tablename, $record);
1499         $record = $DB->get_record($tablename, array('id' => $recid));
1500         $this->assertEqual(0, $record->course);
1501         $this->assertNull($record->oneint);
1502         $this->assertNull($record->onenum);
1503         $this->assertNull($record->onechar);
1504         $this->assertNull($record->onetext);
1505         $this->assertNull($record->onebinary);
1507         // Check zeros are set properly for all types
1508         $record = new stdClass();
1509         $record->oneint = 0;
1510         $record->onenum = 0;
1511         $recid = $DB->insert_record($tablename, $record);
1512         $record = $DB->get_record($tablename, array('id' => $recid));
1513         $this->assertEqual(0, $record->oneint);
1514         $this->assertEqual(0, $record->onenum);
1516         // Check booleans are set properly for all types
1517         $record = new stdClass();
1518         $record->oneint = true; // trues
1519         $record->onenum = true;
1520         $record->onechar = true;
1521         $record->onetext = true;
1522         $recid = $DB->insert_record($tablename, $record);
1523         $record = $DB->get_record($tablename, array('id' => $recid));
1524         $this->assertEqual(1, $record->oneint);
1525         $this->assertEqual(1, $record->onenum);
1526         $this->assertEqual(1, $record->onechar);
1527         $this->assertEqual(1, $record->onetext);
1529         $record = new stdClass();
1530         $record->oneint = false; // falses
1531         $record->onenum = false;
1532         $record->onechar = false;
1533         $record->onetext = false;
1534         $recid = $DB->insert_record($tablename, $record);
1535         $record = $DB->get_record($tablename, array('id' => $recid));
1536         $this->assertEqual(0, $record->oneint);
1537         $this->assertEqual(0, $record->onenum);
1538         $this->assertEqual(0, $record->onechar);
1539         $this->assertEqual(0, $record->onetext);
1541         // Check string data causes exception in numeric types
1542         $record = new stdClass();
1543         $record->oneint = 'onestring';
1544         $record->onenum = 0;
1545         try {
1546             $DB->insert_record($tablename, $record);
1547             $this->fail("Expecting an exception, none occurred");
1548         } catch (exception $e) {
1549             $this->assertTrue($e instanceof dml_exception);
1550         }
1551         $record = new stdClass();
1552         $record->oneint = 0;
1553         $record->onenum = 'onestring';
1554         try {
1555            $DB->insert_record($tablename, $record);
1556            $this->fail("Expecting an exception, none occurred");
1557         } catch (exception $e) {
1558             $this->assertTrue($e instanceof dml_exception);
1559         }
1561         // Check empty string data is stored as 0 in numeric datatypes
1562         $record = new stdClass();
1563         $record->oneint = ''; // empty string
1564         $record->onenum = 0;
1565         $recid = $DB->insert_record($tablename, $record);
1566         $record = $DB->get_record($tablename, array('id' => $recid));
1567         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1569         $record = new stdClass();
1570         $record->oneint = 0;
1571         $record->onenum = ''; // empty string
1572         $recid = $DB->insert_record($tablename, $record);
1573         $record = $DB->get_record($tablename, array('id' => $recid));
1574         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1576         // Check empty strings are set properly in string types
1577         $record = new stdClass();
1578         $record->oneint = 0;
1579         $record->onenum = 0;
1580         $record->onechar = '';
1581         $record->onetext = '';
1582         $recid = $DB->insert_record($tablename, $record);
1583         $record = $DB->get_record($tablename, array('id' => $recid));
1584         $this->assertTrue($record->onechar === '');
1585         $this->assertTrue($record->onetext === '');
1587         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1588         $record = new stdClass();
1589         $record->oneint = ((210.10 + 39.92) - 150.02);
1590         $record->onenum = ((210.10 + 39.92) - 150.02);
1591         $recid = $DB->insert_record($tablename, $record);
1592         $record = $DB->get_record($tablename, array('id' => $recid));
1593         $this->assertEqual(100, $record->oneint);
1594         $this->assertEqual(100, $record->onenum);
1596         // Check various quotes/backslashes combinations in string types
1597         $teststrings = array(
1598             'backslashes and quotes alone (even): "" \'\' \\\\',
1599             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1600             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1601             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1602         foreach ($teststrings as $teststring) {
1603             $record = new stdClass();
1604             $record->onechar = $teststring;
1605             $record->onetext = $teststring;
1606             $recid = $DB->insert_record($tablename, $record);
1607             $record = $DB->get_record($tablename, array('id' => $recid));
1608             $this->assertEqual($teststring, $record->onechar);
1609             $this->assertEqual($teststring, $record->onetext);
1610         }
1612         // Check LOBs in text/binary columns
1613         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1614         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1615         $record = new stdClass();
1616         $record->onetext = $clob;
1617         $record->onebinary = $blob;
1618         $recid = $DB->insert_record($tablename, $record);
1619         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1620         $record = $rs->current();
1621         $rs->close();
1622         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1623         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1625         // And "small" LOBs too, just in case
1626         $newclob = substr($clob, 0, 500);
1627         $newblob = substr($blob, 0, 250);
1628         $record = new stdClass();
1629         $record->onetext = $newclob;
1630         $record->onebinary = $newblob;
1631         $recid = $DB->insert_record($tablename, $record);
1632         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1633         $record = $rs->current();
1634         $rs->close();
1635         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1636         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1637         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1639         // And "diagnostic" LOBs too, just in case
1640         $newclob = '\'"\\;/ěščřžýáíé';
1641         $newblob = '\'"\\;/ěščřžýáíé';
1642         $record = new stdClass();
1643         $record->onetext = $newclob;
1644         $record->onebinary = $newblob;
1645         $recid = $DB->insert_record($tablename, $record);
1646         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1647         $record = $rs->current();
1648         $rs->close();
1649         $this->assertIdentical($newclob, $record->onetext);
1650         $this->assertIdentical($newblob, $record->onebinary);
1651         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1653         // test data is not modified
1654         $record = new stdClass();
1655         $record->id     = -1; // has to be ignored
1656         $record->course = 3;
1657         $record->lalala = 'lalal'; // unused
1658         $before = clone($record);
1659         $DB->insert_record($tablename, $record);
1660         $this->assertEqual($record, $before);
1662         // make sure the id is always increasing and never reuses the same id
1663         $id1 = $DB->insert_record($tablename, array('course' => 3));
1664         $id2 = $DB->insert_record($tablename, array('course' => 3));
1665         $this->assertTrue($id1 < $id2);
1666         $DB->delete_records($tablename, array('id'=>$id2));
1667         $id3 = $DB->insert_record($tablename, array('course' => 3));
1668         $this->assertTrue($id2 < $id3);
1669         $DB->delete_records($tablename, array());
1670         $id4 = $DB->insert_record($tablename, array('course' => 3));
1671         $this->assertTrue($id3 < $id4);
1672     }
1674     public function test_import_record() {
1675         // All the information in this test is fetched from DB by get_recordset() so we
1676         // have such method properly tested against nulls, empties and friends...
1678         $DB = $this->tdb;
1679         $dbman = $DB->get_manager();
1681         $table = $this->get_test_table();
1682         $tablename = $table->getName();
1684         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1685         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1686         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1687         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1688         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1689         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1690         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1691         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1692         $dbman->create_table($table);
1694         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1695         $record = $DB->get_record($tablename, array('course' => 1));
1696         $this->assertEqual(1, $record->id);
1697         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1698         $this->assertEqual(200, $record->onenum);
1699         $this->assertIdentical('onestring', $record->onechar);
1700         $this->assertNull($record->onetext);
1701         $this->assertNull($record->onebinary);
1703         // ignore extra columns
1704         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
1705         $before = clone($record);
1706         $this->assertIdentical(true, $DB->import_record($tablename, $record));
1707         $this->assertIdentical($record, $before);
1708         $records = $DB->get_records($tablename);
1709         $this->assertEqual(2, $records[13]->course);
1711         // Check nulls are set properly for all types
1712         $record = new stdClass();
1713         $record->id = 20;
1714         $record->oneint = null;
1715         $record->onenum = null;
1716         $record->onechar = null;
1717         $record->onetext = null;
1718         $record->onebinary = null;
1719         $this->assertTrue($DB->import_record($tablename, $record));
1720         $record = $DB->get_record($tablename, array('id' => 20));
1721         $this->assertEqual(0, $record->course);
1722         $this->assertNull($record->oneint);
1723         $this->assertNull($record->onenum);
1724         $this->assertNull($record->onechar);
1725         $this->assertNull($record->onetext);
1726         $this->assertNull($record->onebinary);
1728         // Check zeros are set properly for all types
1729         $record = new stdClass();
1730         $record->id = 23;
1731         $record->oneint = 0;
1732         $record->onenum = 0;
1733         $this->assertTrue($DB->import_record($tablename, $record));
1734         $record = $DB->get_record($tablename, array('id' => 23));
1735         $this->assertEqual(0, $record->oneint);
1736         $this->assertEqual(0, $record->onenum);
1738         // Check string data causes exception in numeric types
1739         $record = new stdClass();
1740         $record->id = 32;
1741         $record->oneint = 'onestring';
1742         $record->onenum = 0;
1743         try {
1744             $DB->import_record($tablename, $record);
1745             $this->fail("Expecting an exception, none occurred");
1746         } catch (exception $e) {
1747             $this->assertTrue($e instanceof dml_exception);
1748         }
1749         $record = new stdClass();
1750         $record->id = 35;
1751         $record->oneint = 0;
1752         $record->onenum = 'onestring';
1753         try {
1754            $DB->import_record($tablename, $record);
1755            $this->fail("Expecting an exception, none occurred");
1756         } catch (exception $e) {
1757             $this->assertTrue($e instanceof dml_exception);
1758         }
1760         // Check empty strings are set properly in string types
1761         $record = new stdClass();
1762         $record->id = 44;
1763         $record->oneint = 0;
1764         $record->onenum = 0;
1765         $record->onechar = '';
1766         $record->onetext = '';
1767         $this->assertTrue($DB->import_record($tablename, $record));
1768         $record = $DB->get_record($tablename, array('id' => 44));
1769         $this->assertTrue($record->onechar === '');
1770         $this->assertTrue($record->onetext === '');
1772         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1773         $record = new stdClass();
1774         $record->id = 47;
1775         $record->oneint = ((210.10 + 39.92) - 150.02);
1776         $record->onenum = ((210.10 + 39.92) - 150.02);
1777         $this->assertTrue($DB->import_record($tablename, $record));
1778         $record = $DB->get_record($tablename, array('id' => 47));
1779         $this->assertEqual(100, $record->oneint);
1780         $this->assertEqual(100, $record->onenum);
1782         // Check various quotes/backslashes combinations in string types
1783         $i = 50;
1784         $teststrings = array(
1785             'backslashes and quotes alone (even): "" \'\' \\\\',
1786             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1787             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1788             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1789         foreach ($teststrings as $teststring) {
1790             $record = new stdClass();
1791             $record->id = $i;
1792             $record->onechar = $teststring;
1793             $record->onetext = $teststring;
1794             $this->assertTrue($DB->import_record($tablename, $record));
1795             $record = $DB->get_record($tablename, array('id' => $i));
1796             $this->assertEqual($teststring, $record->onechar);
1797             $this->assertEqual($teststring, $record->onetext);
1798             $i = $i + 3;
1799         }
1801         // Check LOBs in text/binary columns
1802         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1803         $record = new stdClass();
1804         $record->id = 70;
1805         $record->onetext = $clob;
1806         $record->onebinary = '';
1807         $this->assertTrue($DB->import_record($tablename, $record));
1808         $rs = $DB->get_recordset($tablename, array('id' => 70));
1809         $record = $rs->current();
1810         $rs->close();
1811         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1813         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1814         $record = new stdClass();
1815         $record->id = 71;
1816         $record->onetext = '';
1817         $record->onebinary = $blob;
1818         $this->assertTrue($DB->import_record($tablename, $record));
1819         $rs = $DB->get_recordset($tablename, array('id' => 71));
1820         $record = $rs->current();
1821         $rs->close();
1822         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1824         // And "small" LOBs too, just in case
1825         $newclob = substr($clob, 0, 500);
1826         $newblob = substr($blob, 0, 250);
1827         $record = new stdClass();
1828         $record->id = 73;
1829         $record->onetext = $newclob;
1830         $record->onebinary = $newblob;
1831         $this->assertTrue($DB->import_record($tablename, $record));
1832         $rs = $DB->get_recordset($tablename, array('id' => 73));
1833         $record = $rs->current();
1834         $rs->close();
1835         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1836         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1837         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1838     }
1840     public function test_update_record_raw() {
1841         $DB = $this->tdb;
1842         $dbman = $DB->get_manager();
1844         $table = $this->get_test_table();
1845         $tablename = $table->getName();
1847         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1848         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1849         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1850         $dbman->create_table($table);
1852         $DB->insert_record($tablename, array('course' => 1));
1853         $DB->insert_record($tablename, array('course' => 3));
1855         $record = $DB->get_record($tablename, array('course' => 1));
1856         $record->course = 2;
1857         $this->assertTrue($DB->update_record_raw($tablename, $record));
1858         $this->assertEqual(0, $DB->count_records($tablename, array('course' => 1)));
1859         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 2)));
1860         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 3)));
1862         $record = $DB->get_record($tablename, array('course' => 1));
1863         $record->xxxxx = 2;
1864         try {
1865            $DB->update_record_raw($tablename, $record);
1866            $this->fail("Expecting an exception, none occurred");
1867         } catch (Exception $e) {
1868             $this->assertTrue($e instanceof coding_exception);
1869         }
1871         $record = $DB->get_record($tablename, array('course' => 3));
1872         unset($record->id);
1873         try {
1874            $DB->update_record_raw($tablename, $record);
1875            $this->fail("Expecting an exception, none occurred");
1876         } catch (Exception $e) {
1877             $this->assertTrue($e instanceof coding_exception);
1878         }
1879     }
1881     public function test_update_record() {
1883         // All the information in this test is fetched from DB by get_record() so we
1884         // have such method properly tested against nulls, empties and friends...
1886         $DB = $this->tdb;
1887         $dbman = $DB->get_manager();
1889         $table = $this->get_test_table();
1890         $tablename = $table->getName();
1892         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1893         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1894         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1895         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1896         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1897         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1898         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1899         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1900         $dbman->create_table($table);
1902         $DB->insert_record($tablename, array('course' => 1));
1903         $record = $DB->get_record($tablename, array('course' => 1));
1904         $record->course = 2;
1906         $this->assertTrue($DB->update_record($tablename, $record));
1907         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
1908         $this->assertTrue($record = $DB->get_record($tablename, array('course' => 2)));
1909         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1910         $this->assertEqual(200, $record->onenum);
1911         $this->assertEqual('onestring', $record->onechar);
1912         $this->assertNull($record->onetext);
1913         $this->assertNull($record->onebinary);
1915         // Check nulls are set properly for all types
1916         $record->oneint = null;
1917         $record->onenum = null;
1918         $record->onechar = null;
1919         $record->onetext = null;
1920         $record->onebinary = null;
1921         $DB->update_record($tablename, $record);
1922         $record = $DB->get_record($tablename, array('course' => 2));
1923         $this->assertNull($record->oneint);
1924         $this->assertNull($record->onenum);
1925         $this->assertNull($record->onechar);
1926         $this->assertNull($record->onetext);
1927         $this->assertNull($record->onebinary);
1929         // Check zeros are set properly for all types
1930         $record->oneint = 0;
1931         $record->onenum = 0;
1932         $DB->update_record($tablename, $record);
1933         $record = $DB->get_record($tablename, array('course' => 2));
1934         $this->assertEqual(0, $record->oneint);
1935         $this->assertEqual(0, $record->onenum);
1937         // Check booleans are set properly for all types
1938         $record->oneint = true; // trues
1939         $record->onenum = true;
1940         $record->onechar = true;
1941         $record->onetext = true;
1942         $DB->update_record($tablename, $record);
1943         $record = $DB->get_record($tablename, array('course' => 2));
1944         $this->assertEqual(1, $record->oneint);
1945         $this->assertEqual(1, $record->onenum);
1946         $this->assertEqual(1, $record->onechar);
1947         $this->assertEqual(1, $record->onetext);
1949         $record->oneint = false; // falses
1950         $record->onenum = false;
1951         $record->onechar = false;
1952         $record->onetext = false;
1953         $DB->update_record($tablename, $record);
1954         $record = $DB->get_record($tablename, array('course' => 2));
1955         $this->assertEqual(0, $record->oneint);
1956         $this->assertEqual(0, $record->onenum);
1957         $this->assertEqual(0, $record->onechar);
1958         $this->assertEqual(0, $record->onetext);
1960         // Check string data causes exception in numeric types
1961         $record->oneint = 'onestring';
1962         $record->onenum = 0;
1963         try {
1964             $DB->update_record($tablename, $record);
1965             $this->fail("Expecting an exception, none occurred");
1966         } catch (exception $e) {
1967             $this->assertTrue($e instanceof dml_exception);
1968         }
1969         $record->oneint = 0;
1970         $record->onenum = 'onestring';
1971         try {
1972             $DB->update_record($tablename, $record);
1973             $this->fail("Expecting an exception, none occurred");
1974         } catch (exception $e) {
1975             $this->assertTrue($e instanceof dml_exception);
1976         }
1978         // Check empty string data is stored as 0 in numeric datatypes
1979         $record->oneint = ''; // empty string
1980         $record->onenum = 0;
1981         $DB->update_record($tablename, $record);
1982         $record = $DB->get_record($tablename, array('course' => 2));
1983         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1985         $record->oneint = 0;
1986         $record->onenum = ''; // empty string
1987         $DB->update_record($tablename, $record);
1988         $record = $DB->get_record($tablename, array('course' => 2));
1989         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1991         // Check empty strings are set properly in string types
1992         $record->oneint = 0;
1993         $record->onenum = 0;
1994         $record->onechar = '';
1995         $record->onetext = '';
1996         $DB->update_record($tablename, $record);
1997         $record = $DB->get_record($tablename, array('course' => 2));
1998         $this->assertTrue($record->onechar === '');
1999         $this->assertTrue($record->onetext === '');
2001         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2002         $record->oneint = ((210.10 + 39.92) - 150.02);
2003         $record->onenum = ((210.10 + 39.92) - 150.02);
2004         $DB->update_record($tablename, $record);
2005         $record = $DB->get_record($tablename, array('course' => 2));
2006         $this->assertEqual(100, $record->oneint);
2007         $this->assertEqual(100, $record->onenum);
2009         // Check various quotes/backslashes combinations in string types
2010         $teststrings = array(
2011             'backslashes and quotes alone (even): "" \'\' \\\\',
2012             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2013             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2014             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2015         foreach ($teststrings as $teststring) {
2016             $record->onechar = $teststring;
2017             $record->onetext = $teststring;
2018             $DB->update_record($tablename, $record);
2019             $record = $DB->get_record($tablename, array('course' => 2));
2020             $this->assertEqual($teststring, $record->onechar);
2021             $this->assertEqual($teststring, $record->onetext);
2022         }
2024         // Check LOBs in text/binary columns
2025         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2026         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2027         $record->onetext = $clob;
2028         $record->onebinary = $blob;
2029         $DB->update_record($tablename, $record);
2030         $record = $DB->get_record($tablename, array('course' => 2));
2031         $this->assertEqual($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2032         $this->assertEqual($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2034         // And "small" LOBs too, just in case
2035         $newclob = substr($clob, 0, 500);
2036         $newblob = substr($blob, 0, 250);
2037         $record->onetext = $newclob;
2038         $record->onebinary = $newblob;
2039         $DB->update_record($tablename, $record);
2040         $record = $DB->get_record($tablename, array('course' => 2));
2041         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2042         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2043     }
2045     public function test_set_field() {
2046         $DB = $this->tdb;
2047         $dbman = $DB->get_manager();
2049         $table = $this->get_test_table();
2050         $tablename = $table->getName();
2052         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2053         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2054         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2055         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2056         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2057         $dbman->create_table($table);
2059         // simple set_field
2060         $id1 = $DB->insert_record($tablename, array('course' => 1));
2061         $id2 = $DB->insert_record($tablename, array('course' => 1));
2062         $id3 = $DB->insert_record($tablename, array('course' => 3));
2063         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2064         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2065         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2066         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2067         $DB->delete_records($tablename, array());
2069         // multiple fields affected
2070         $id1 = $DB->insert_record($tablename, array('course' => 1));
2071         $id2 = $DB->insert_record($tablename, array('course' => 1));
2072         $id3 = $DB->insert_record($tablename, array('course' => 3));
2073         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2074         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2075         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2076         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2077         $DB->delete_records($tablename, array());
2079         // no field affected
2080         $id1 = $DB->insert_record($tablename, array('course' => 1));
2081         $id2 = $DB->insert_record($tablename, array('course' => 1));
2082         $id3 = $DB->insert_record($tablename, array('course' => 3));
2083         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2084         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2085         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2086         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2087         $DB->delete_records($tablename, array());
2089         // all fields - no condition
2090         $id1 = $DB->insert_record($tablename, array('course' => 1));
2091         $id2 = $DB->insert_record($tablename, array('course' => 1));
2092         $id3 = $DB->insert_record($tablename, array('course' => 3));
2093         $DB->set_field($tablename, 'course', 5, array());
2094         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2095         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2096         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2098         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2099         $conditions = array('onetext' => '1');
2100         try {
2101             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2102             $this->fail('An Exception is missing, expected due to equating of text fields');
2103         } catch (exception $e) {
2104             $this->assertTrue($e instanceof dml_exception);
2105             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2106         }
2108         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2109         // go to set_field_select() because set_field() is just one wrapper over it
2110     }
2112     public function test_set_field_select() {
2114         // All the information in this test is fetched from DB by get_field() so we
2115         // have such method properly tested against nulls, empties and friends...
2117         $DB = $this->tdb;
2118         $dbman = $DB->get_manager();
2120         $table = $this->get_test_table();
2121         $tablename = $table->getName();
2123         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2124         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2125         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2126         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2127         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2128         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2129         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2130         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2131         $dbman->create_table($table);
2133         $DB->insert_record($tablename, array('course' => 1));
2135         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2136         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2138         // Check nulls are set properly for all types
2139         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2140         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2141         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2142         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2143         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2144         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2145         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2146         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2147         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2148         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2150         // Check zeros are set properly for all types
2151         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2152         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2153         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2154         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2156         // Check booleans are set properly for all types
2157         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2158         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2159         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2160         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2161         $this->assertEqual(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2162         $this->assertEqual(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2163         $this->assertEqual(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2164         $this->assertEqual(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2166         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2167         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2168         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2169         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2170         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2171         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2172         $this->assertEqual(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2173         $this->assertEqual(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2175         // Check string data causes exception in numeric types
2176         try {
2177             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2178             $this->fail("Expecting an exception, none occurred");
2179         } catch (exception $e) {
2180             $this->assertTrue($e instanceof dml_exception);
2181         }
2182         try {
2183             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2184             $this->fail("Expecting an exception, none occurred");
2185         } catch (exception $e) {
2186             $this->assertTrue($e instanceof dml_exception);
2187         }
2189         // Check empty string data is stored as 0 in numeric datatypes
2190         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2191         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2192         $this->assertTrue(is_numeric($field) && $field == 0);
2194         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2195         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2196         $this->assertTrue(is_numeric($field) && $field == 0);
2198         // Check empty strings are set properly in string types
2199         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2200         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2201         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2202         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2204         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2205         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2206         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2207         $this->assertEqual(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2208         $this->assertEqual(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2210         // Check various quotes/backslashes combinations in string types
2211         $teststrings = array(
2212             'backslashes and quotes alone (even): "" \'\' \\\\',
2213             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2214             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2215             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2216         foreach ($teststrings as $teststring) {
2217             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2218             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2219             $this->assertEqual($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2220             $this->assertEqual($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2221         }
2223         // Check LOBs in text/binary columns
2224         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2225         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2226         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2227         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2228         $this->assertEqual($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2229         $this->assertEqual($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2231         // And "small" LOBs too, just in case
2232         $newclob = substr($clob, 0, 500);
2233         $newblob = substr($blob, 0, 250);
2234         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2235         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2236         $this->assertEqual($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2237         $this->assertEqual($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2239         // This is the failure from MDL-24863. This was giving an error on MSSQL,
2240         // which converts the '1' to an integer, which cannot then be compared with
2241         // onetext cast to a varchar. This should be fixed and working now.
2242         $newchar = 'frog';
2243         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2244         $params = array('onetext' => '1');
2245         try {
2246             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
2247             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
2248         } catch (dml_exception $e) {
2249             $this->assertFalse(true, 'We have an unexpected exception.');
2250             throw $e;
2251         }
2254     }
2256     public function test_count_records() {
2257         $DB = $this->tdb;
2259         $dbman = $DB->get_manager();
2261         $table = $this->get_test_table();
2262         $tablename = $table->getName();
2264         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2265         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2266         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2267         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2268         $dbman->create_table($table);
2270         $this->assertEqual(0, $DB->count_records($tablename));
2272         $DB->insert_record($tablename, array('course' => 3));
2273         $DB->insert_record($tablename, array('course' => 4));
2274         $DB->insert_record($tablename, array('course' => 5));
2276         $this->assertEqual(3, $DB->count_records($tablename));
2278         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2279         $conditions = array('onetext' => '1');
2280         try {
2281             $DB->count_records($tablename, $conditions);
2282             $this->fail('An Exception is missing, expected due to equating of text fields');
2283         } catch (exception $e) {
2284             $this->assertTrue($e instanceof dml_exception);
2285             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2286         }
2287     }
2289     public function test_count_records_select() {
2290         $DB = $this->tdb;
2292         $dbman = $DB->get_manager();
2294         $table = $this->get_test_table();
2295         $tablename = $table->getName();
2297         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2298         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2299         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2300         $dbman->create_table($table);
2302         $this->assertEqual(0, $DB->count_records($tablename));
2304         $DB->insert_record($tablename, array('course' => 3));
2305         $DB->insert_record($tablename, array('course' => 4));
2306         $DB->insert_record($tablename, array('course' => 5));
2308         $this->assertEqual(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2309     }
2311     public function test_count_records_sql() {
2312         $DB = $this->tdb;
2313         $dbman = $DB->get_manager();
2315         $table = $this->get_test_table();
2316         $tablename = $table->getName();
2318         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2319         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2320         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2321         $dbman->create_table($table);
2323         $this->assertEqual(0, $DB->count_records($tablename));
2325         $DB->insert_record($tablename, array('course' => 3));
2326         $DB->insert_record($tablename, array('course' => 4));
2327         $DB->insert_record($tablename, array('course' => 5));
2329         $this->assertEqual(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2330     }
2332     public function test_record_exists() {
2333         $DB = $this->tdb;
2334         $dbman = $DB->get_manager();
2336         $table = $this->get_test_table();
2337         $tablename = $table->getName();
2339         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2340         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2341         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2342         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2343         $dbman->create_table($table);
2345         $this->assertEqual(0, $DB->count_records($tablename));
2347         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
2348         $DB->insert_record($tablename, array('course' => 3));
2350         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
2353         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2354         $conditions = array('onetext' => '1');
2355         try {
2356             $DB->record_exists($tablename, $conditions);
2357             $this->fail('An Exception is missing, expected due to equating of text fields');
2358         } catch (exception $e) {
2359             $this->assertTrue($e instanceof dml_exception);
2360             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2361         }
2362     }
2364     public function test_record_exists_select() {
2365         $DB = $this->tdb;
2366         $dbman = $DB->get_manager();
2368         $table = $this->get_test_table();
2369         $tablename = $table->getName();
2371         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2372         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2373         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2374         $dbman->create_table($table);
2376         $this->assertEqual(0, $DB->count_records($tablename));
2378         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
2379         $DB->insert_record($tablename, array('course' => 3));
2381         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
2382     }
2384     public function test_record_exists_sql() {
2385         $DB = $this->tdb;
2386         $dbman = $DB->get_manager();
2388         $table = $this->get_test_table();
2389         $tablename = $table->getName();
2391         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2392         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2393         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2394         $dbman->create_table($table);
2396         $this->assertEqual(0, $DB->count_records($tablename));
2398         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2399         $DB->insert_record($tablename, array('course' => 3));
2401         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2402     }
2404     public function test_delete_records() {
2405         $DB = $this->tdb;
2406         $dbman = $DB->get_manager();
2408         $table = $this->get_test_table();
2409         $tablename = $table->getName();
2411         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2412         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2413         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2414         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2415         $dbman->create_table($table);
2417         $DB->insert_record($tablename, array('course' => 3));
2418         $DB->insert_record($tablename, array('course' => 2));
2419         $DB->insert_record($tablename, array('course' => 2));
2421         // Delete all records
2422         $this->assertTrue($DB->delete_records($tablename));
2423         $this->assertEqual(0, $DB->count_records($tablename));
2425         // Delete subset of records
2426         $DB->insert_record($tablename, array('course' => 3));
2427         $DB->insert_record($tablename, array('course' => 2));
2428         $DB->insert_record($tablename, array('course' => 2));
2430         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
2431         $this->assertEqual(1, $DB->count_records($tablename));
2433         // delete all
2434         $this->assertTrue($DB->delete_records($tablename, array()));
2435         $this->assertEqual(0, $DB->count_records($tablename));
2437         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2438         $conditions = array('onetext'=>'1');
2439         try {
2440             $DB->delete_records($tablename, $conditions);
2441             $this->fail('An Exception is missing, expected due to equating of text fields');
2442         } catch (exception $e) {
2443             $this->assertTrue($e instanceof dml_exception);
2444             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2445         }
2447         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2448         $conditions = array('onetext' => 1);
2449         try {
2450             $DB->delete_records($tablename, $conditions);
2451             $this->fail('An Exception is missing, expected due to equating of text fields');
2452         } catch (exception $e) {
2453             $this->assertTrue($e instanceof dml_exception);
2454             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2455         }
2456     }
2458     public function test_delete_records_select() {
2459         $DB = $this->tdb;
2460         $dbman = $DB->get_manager();
2462         $table = $this->get_test_table();
2463         $tablename = $table->getName();
2465         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2466         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2467         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2468         $dbman->create_table($table);
2470         $DB->insert_record($tablename, array('course' => 3));
2471         $DB->insert_record($tablename, array('course' => 2));
2472         $DB->insert_record($tablename, array('course' => 2));
2474         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
2475         $this->assertEqual(1, $DB->count_records($tablename));
2476     }
2478     public function test_delete_records_list() {
2479         $DB = $this->tdb;
2480         $dbman = $DB->get_manager();
2482         $table = $this->get_test_table();
2483         $tablename = $table->getName();
2485         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2486         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2487         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2488         $dbman->create_table($table);
2490         $DB->insert_record($tablename, array('course' => 1));
2491         $DB->insert_record($tablename, array('course' => 2));
2492         $DB->insert_record($tablename, array('course' => 3));
2494         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
2495         $this->assertEqual(1, $DB->count_records($tablename));
2497         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); /// Must delete 0 rows without conditions. MDL-17645
2498         $this->assertEqual(1, $DB->count_records($tablename));
2499     }
2501     function test_sql_null_from_clause() {
2502         $DB = $this->tdb;
2503         $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
2504         $this->assertEqual($DB->get_field_sql($sql), 1);
2505     }
2507     function test_sql_bitand() {
2508         $DB = $this->tdb;
2509         $dbman = $DB->get_manager();
2511         $table = $this->get_test_table();
2512         $tablename = $table->getName();
2514         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2515         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2516         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2517         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2518         $dbman->create_table($table);
2520         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2522         $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
2523         $this->assertEqual($DB->get_field_sql($sql), 2);
2525         $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
2526         $result = $DB->get_records_sql($sql);
2527         $this->assertEqual(count($result), 1);
2528         $this->assertEqual(reset($result)->res, 2);
2530         $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
2531         $result = $DB->get_records_sql($sql, array(10));
2532         $this->assertEqual(count($result), 1);
2533         $this->assertEqual(reset($result)->res, 2);
2534     }
2536     function test_sql_bitnot() {
2537         $DB = $this->tdb;
2539         $not = $DB->sql_bitnot(2);
2540         $notlimited = $DB->sql_bitand($not, 7); // might be positive or negative number which can not fit into PHP INT!
2542         $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
2543         $this->assertEqual($DB->get_field_sql($sql), 5);
2544     }
2546     function test_sql_bitor() {
2547         $DB = $this->tdb;
2548         $dbman = $DB->get_manager();
2550         $table = $this->get_test_table();
2551         $tablename = $table->getName();
2553         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2554         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2555         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2556         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2557         $dbman->create_table($table);
2559         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2561         $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
2562         $this->assertEqual($DB->get_field_sql($sql), 11);
2564         $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
2565         $result = $DB->get_records_sql($sql);
2566         $this->assertEqual(count($result), 1);
2567         $this->assertEqual(reset($result)->res, 11);
2569         $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
2570         $result = $DB->get_records_sql($sql, array(10));
2571         $this->assertEqual(count($result), 1);
2572         $this->assertEqual(reset($result)->res, 11);
2573     }
2575     function test_sql_bitxor() {
2576         $DB = $this->tdb;
2577         $dbman = $DB->get_manager();
2579         $table = $this->get_test_table();
2580         $tablename = $table->getName();
2582         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2583         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2584         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2585         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2586         $dbman->create_table($table);
2588         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2590         $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
2591         $this->assertEqual($DB->get_field_sql($sql), 9);
2593         $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}";
2594         $result = $DB->get_records_sql($sql);
2595         $this->assertEqual(count($result), 1);
2596         $this->assertEqual(reset($result)->res, 9);
2598         $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}";
2599         $result = $DB->get_records_sql($sql, array(10));
2600         $this->assertEqual(count($result), 1);
2601         $this->assertEqual(reset($result)->res, 9);
2602     }
2604     function test_sql_modulo() {
2605         $DB = $this->tdb;
2606         $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause();
2607         $this->assertEqual($DB->get_field_sql($sql), 3);
2608     }
2610     function test_sql_ceil() {
2611         $DB = $this->tdb;
2612         $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause();
2613         $this->assertEqual($DB->get_field_sql($sql), 666);
2614     }
2616     function test_cast_char2int() {
2617         $DB = $this->tdb;
2618         $dbman = $DB->get_manager();
2620         $table1 = $this->get_test_table("1");
2621         $tablename1 = $table1->getName();
2623         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2624         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2625         $table1->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
2626         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2627         $dbman->create_table($table1);
2629         $DB->insert_record($tablename1, array('name'=>'0100', 'nametext'=>'0200'));
2630         $DB->insert_record($tablename1, array('name'=>'10',   'nametext'=>'20'));
2632         $table2 = $this->get_test_table("2");
2633         $tablename2 = $table2->getName();
2634         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2635         $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2636         $table2->add_field('restext', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2637         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2638         $dbman->create_table($table2);
2640         $DB->insert_record($tablename2, array('res'=>100, 'restext'=>200));
2642         // casting varchar field
2643         $sql = "SELECT *
2644                   FROM {".$tablename1."} t1
2645                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.name")." = t2.res ";
2646         $records = $DB->get_records_sql($sql);
2647         $this->assertEqual(count($records), 1);
2648         // also test them in order clauses
2649         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('name');
2650         $records = $DB->get_records_sql($sql);
2651         $this->assertEqual(count($records), 2);
2652         $this->assertEqual(reset($records)->name, '10');
2653         $this->assertEqual(next($records)->name, '0100');
2655         // casting text field
2656         $sql = "SELECT *
2657                   FROM {".$tablename1."} t1
2658                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.nametext", true)." = t2.restext ";
2659         $records = $DB->get_records_sql($sql);
2660         $this->assertEqual(count($records), 1);
2661         // also test them in order clauses
2662         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('nametext', true);
2663         $records = $DB->get_records_sql($sql);
2664         $this->assertEqual(count($records), 2);
2665         $this->assertEqual(reset($records)->nametext, '20');
2666         $this->assertEqual(next($records)->nametext, '0200');
2667     }
2669     function test_cast_char2real() {
2670         $DB = $this->tdb;
2671         $dbman = $DB->get_manager();
2673         $table = $this->get_test_table();
2674         $tablename = $table->getName();
2676         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2677         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2678         $table->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
2679         $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
2680         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2681         $dbman->create_table($table);
2683         $DB->insert_record($tablename, array('name'=>'10.10', 'nametext'=>'10.10', 'res'=>5.1));
2684         $DB->insert_record($tablename, array('name'=>'91.10', 'nametext'=>'91.10', 'res'=>666));
2685         $DB->insert_record($tablename, array('name'=>'011.10','nametext'=>'011.10','res'=>10.1));
2687         // casting varchar field
2688         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res";
2689         $records = $DB->get_records_sql($sql);
2690         $this->assertEqual(count($records), 2);
2691         // also test them in order clauses
2692         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('name');
2693         $records = $DB->get_records_sql($sql);
2694         $this->assertEqual(count($records), 3);
2695         $this->assertEqual(reset($records)->name, '10.10');
2696         $this->assertEqual(next($records)->name, '011.10');
2697         $this->assertEqual(next($records)->name, '91.10');
2699         // casting text field
2700         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('nametext', true)." > res";
2701         $records = $DB->get_records_sql($sql);
2702         $this->assertEqual(count($records), 2);
2703         // also test them in order clauses
2704         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('nametext', true);
2705         $records = $DB->get_records_sql($sql);
2706         $this->assertEqual(count($records), 3);
2707         $this->assertEqual(reset($records)->nametext, '10.10');
2708         $this->assertEqual(next($records)->nametext, '011.10');
2709         $this->assertEqual(next($records)->nametext, '91.10');
2710     }
2712     function sql_compare_text() {
2713         $DB = $this->tdb;
2714         $dbman = $DB->get_manager();
2716         $table = $this->get_test_table();
2717         $tablename = $table->getName();
2719         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2720         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2721         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2722         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2723         $dbman->create_table($table);
2725         $DB->insert_record($tablename, array('name'=>'abcd',   'description'=>'abcd'));
2726         $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef'));
2727         $DB->insert_record($tablename, array('name'=>'aaaabb', 'description'=>'aaaacccccccccccccccccc'));
2729         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description');
2730         $records = $DB->get_records_sql($sql);
2731         $this->assertEqual(count($records), 1);
2733         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4);
2734         $records = $DB->get_records_sql($sql);
2735         $this->assertEqual(count($records), 2);
2736     }
2738     function test_unique_index_collation_trouble() {
2739         // note: this is a work in progress, we should probably move this to ddl test
2741         $DB = $this->tdb;
2742         $dbman = $DB->get_manager();
2744         $table = $this->get_test_table();
2745         $tablename = $table->getName();
2747         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2748         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2749         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2750         $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name'));
2751         $dbman->create_table($table);
2753         $DB->insert_record($tablename, array('name'=>'aaa'));
2755         try {
2756             $DB->insert_record($tablename, array('name'=>'AAA'));
2757         } catch (Exception $e) {
2758             //TODO: ignore case insensitive uniqueness problems for now
2759             //$this->fail("Unique index is case sensitive - this may cause problems in some tables");
2760         }
2762         try {
2763             $DB->insert_record($tablename, array('name'=>'aäa'));
2764             $DB->insert_record($tablename, array('name'=>'aáa'));
2765             $this->assertTrue(true);
2766         } catch (Exception $e) {
2767             $family = $DB->get_dbfamily();
2768             if ($family === 'mysql' or $family === 'mssql') {
2769                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
2770             } else {
2771                 // this should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness.
2772                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages.");
2773             }
2774             throw($e);
2775         }
2776     }
2778     function test_sql_binary_equal() {
2779         $DB = $this->tdb;
2780         $dbman = $DB->get_manager();
2782         $table = $this->get_test_table();
2783         $tablename = $table->getName();
2785         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2786         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2787         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2788         $dbman->create_table($table);
2790         $DB->insert_record($tablename, array('name'=>'aaa'));
2791         $DB->insert_record($tablename, array('name'=>'aáa'));
2792         $DB->insert_record($tablename, array('name'=>'aäa'));
2793         $DB->insert_record($tablename, array('name'=>'bbb'));
2794         $DB->insert_record($tablename, array('name'=>'BBB'));
2796         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('aaa'));
2797         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be accent sensitive');
2799         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('bbb'));
2800         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be case sensitive');
2801     }
2803     function test_sql_like() {
2804         $DB = $this->tdb;
2805         $dbman = $DB->get_manager();
2807         $table = $this->get_test_table();
2808         $tablename = $table->getName();
2810         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2811         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2812         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2813         $dbman->create_table($table);
2815         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
2816         $DB->insert_record($tablename, array('name'=>'Nodupor'));
2817         $DB->insert_record($tablename, array('name'=>'ouch'));
2818         $DB->insert_record($tablename, array('name'=>'ouc_'));
2819         $DB->insert_record($tablename, array('name'=>'ouc%'));
2820         $DB->insert_record($tablename, array('name'=>'aui'));
2821         $DB->insert_record($tablename, array('name'=>'aüi'));
2822         $DB->insert_record($tablename, array('name'=>'aÜi'));
2824         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false);
2825         $records = $DB->get_records_sql($sql, array("%dup_r%"));
2826         $this->assertEqual(count($records), 2);
2828         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
2829         $records = $DB->get_records_sql($sql, array("%dup%"));
2830         $this->assertEqual(count($records), 1);
2832         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?'); // defaults
2833         $records = $DB->get_records_sql($sql, array("%dup%"));
2834         $this->assertEqual(count($records), 1);
2836         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
2837         $records = $DB->get_records_sql($sql, array("ouc\\_"));
2838         $this->assertEqual(count($records), 1);
2840         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|');
2841         $records = $DB->get_records_sql($sql, array($DB->sql_like_escape("ouc%", '|')));
2842         $this->assertEqual(count($records), 1);
2844         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true);
2845         $records = $DB->get_records_sql($sql, array('aui'));
2846         $this->assertEqual(count($records), 1);
2848         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, true); // NOT LIKE
2849         $records = $DB->get_records_sql($sql, array("%o%"));
2850         $this->assertEqual(count($records), 3);
2852         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, true, true); // NOT ILIKE
2853         $records = $DB->get_records_sql($sql, array("%D%"));
2854         $this->assertEqual(count($records), 6);
2856         // TODO: we do not require accent insensitivness yet, just make sure it does not throw errors
2857         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, false);
2858         $records = $DB->get_records_sql($sql, array('aui'));
2859         //$this->assertEqual(count($records), 2, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
2860         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false);
2861         $records = $DB->get_records_sql($sql, array('aui'));
2862         //$this->assertEqual(count($records), 3, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
2863     }
2865     function test_sql_ilike() {
2866         // note: this is deprecated, just make sure it does not throw error
2867         $DB = $this->tdb;
2868         $dbman = $DB->get_manager();
2870         $table = $this->get_test_table();
2871         $tablename = $table->getName();
2873         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2874         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2875         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2876         $dbman->create_table($table);
2878         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
2879         $DB->insert_record($tablename, array('name'=>'NoDupor'));
2880         $DB->insert_record($tablename, array('name'=>'ouch'));
2882         // make sure it prints debug message
2883         $this->enable_debugging();
2884         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_ilike()." ?";
2885         $params = array("%dup_r%");
2886         $this->assertFalse($this->get_debugging() === '');
2888         // following must not throw exception, we ignore result
2889         $DB->get_records_sql($sql, $params);
2890     }
2892     function test_sql_concat() {
2893         $DB = $this->tdb;
2894         $dbman = $DB->get_manager();
2896         /// Testing all sort of values
2897         $sql = "SELECT ".$DB->sql_concat("?", "?", "?")." AS fullname ". $DB->sql_null_from_clause();
2898         // string, some unicode chars
2899         $params = array('name', 'áéíóú', 'name3');
2900         $this->assertEqual('nameáéíóúname3', $DB->get_field_sql($sql, $params));
2901         // string, spaces and numbers
2902         $params = array('name', '  ', 12345);
2903         $this->assertEqual('name  12345', $DB->get_field_sql($sql, $params));
2904         // float, empty and strings
2905         $params = array(123.45, '', 'test');
2906         $this->assertEqual('123.45test', $DB->get_field_sql($sql, $params));
2907         // only integers
2908         $params = array(12, 34, 56);
2909         $this->assertEqual('123456', $DB->get_field_sql($sql, $params));
2910         // float, null and strings
2911         $params = array(123.45, null, 'test');
2912         $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
2914         /// Testing fieldnames + values and also integer fieldnames
2915         $table = $this->get_test_table();
2916         $tablename = $table->getName();
2918         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2919         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2920         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2921         $dbman->create_table($table);
2923         $DB->insert_record($tablename, array('description'=>'áéíóú'));
2924         $DB->insert_record($tablename, array('description'=>'dxxx'));
2925         $DB->insert_record($tablename, array('description'=>'bcde'));
2927         // fieldnames and values mixed
2928         $sql = 'SELECT id, ' . $DB->sql_concat('description', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
2929         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
2930         $this->assertEqual(count($records), 3);
2931         $this->assertEqual($records[1]->result, 'áéíóúharcoded123.45test');
2932         // integer fieldnames and values
2933         $sql = 'SELECT id, ' . $DB->sql_concat('id', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
2934         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
2935         $this->assertEqual(count($records), 3);
2936         $this->assertEqual($records[1]->result, '1harcoded123.45test');
2937         // all integer fieldnames
2938         $sql = 'SELECT id, ' . $DB->sql_concat('id', 'id', 'id') . ' AS result FROM {' . $tablename . '}';
2939         $records = $DB->get_records_sql($sql, array());
2940         $this->assertEqual(count($records), 3);
2941         $this->assertEqual($records[1]->result, '111');
2943     }
2945     function test_concat_join() {
2946         $DB = $this->tdb;
2947         $sql = "SELECT ".$DB->sql_concat_join("' '", array("?", "?", "?"))." AS fullname ".$DB->sql_null_from_clause();
2948         $params = array("name", "name2", "name3");
2949         $result = $DB->get_field_sql($sql, $params);
2950         $this->assertEqual("name name2 name3", $result);
2951     }
2953     function test_sql_fullname() {
2954         $DB = $this->tdb;
2955         $sql = "SELECT ".$DB->sql_fullname(':first', ':last')." AS fullname ".$DB->sql_null_from_clause();
2956         $params = array('first'=>'Firstname', 'last'=>'Surname');
2957         $this->assertEqual("Firstname Surname", $DB->get_field_sql($sql, $params));
2958     }
2960     function sql_sql_order_by_text() {
2961         $DB = $this->tdb;
2962         $dbman = $DB->get_manager();
2964         $table = $this->get_test_table();
2965         $tablename = $table->getName();
2967         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2968         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
2969         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2970         $dbman->create_table($table);
2972         $DB->insert_record($tablename, array('description'=>'abcd'));
2973         $DB->insert_record($tablename, array('description'=>'dxxx'));
2974         $DB->insert_record($tablename, array('description'=>'bcde'));
2976         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_order_by_text('description');
2977         $records = $DB->get_records_sql($sql);
2978         $first = array_shift($records);
2979         $this->assertEqual(1, $first->id);
2980         $second = array_shift($records);
2981         $this->assertEqual(3, $second->id);
2982         $last = array_shift($records);
2983         $this->assertEqual(2, $last->id);
2984     }
2986     function test_sql_substring() {
2987         $DB = $this->tdb;
2988         $dbman = $DB->get_manager();
2990         $table = $this->get_test_table();
2991         $tablename = $table->getName();
2993         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2994         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
2995         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2996         $dbman->create_table($table);
2998         $string = 'abcdefghij';
3000         $DB->insert_record($tablename, array('name'=>$string));
3002         $sql = "SELECT id, ".$DB->sql_substr("name", 5)." AS name FROM {{$tablename}}";
3003         $record = $DB->get_record_sql($sql);
3004         $this->assertEqual(substr($string, 5-1), $record->name);
3006         $sql = "SELECT id, ".$DB->sql_substr("name", 5, 2)." AS name FROM {{$tablename}}";
3007         $record = $DB->get_record_sql($sql);
3008         $this->assertEqual(substr($string, 5-1, 2), $record->name);
3010         try {
3011             // silence php warning ;-)
3012             @$DB->sql_substr("name");
3013             $this->fail("Expecting an exception, none occurred");
3014         } catch (Exception $e) {
3015             $this->assertTrue($e instanceof coding_exception);
3016         }
3017     }
3019     function test_sql_length() {
3020         $DB = $this->tdb;
3021         $this->assertEqual($DB->get_field_sql(
3022                 "SELECT ".$DB->sql_length("'aeiou'").$DB->sql_null_from_clause()), 5);
3023         $this->assertEqual($DB->get_field_sql(
3024                 "SELECT ".$DB->sql_length("'áéíóú'").$DB->sql_null_from_clause()), 5);
3025     }
3027     function test_sql_position() {
3028         $DB = $this->tdb;
3029         $this->assertEqual($DB->get_field_sql(
3030                 "SELECT ".$DB->sql_position("'ood'", "'Moodle'").$DB->sql_null_from_clause()), 2);
3031         $this->assertEqual($DB->get_field_sql(
3032                 "SELECT ".$DB->sql_position("'Oracle'", "'Moodle'").$DB->sql_null_from_clause()), 0);
3033     }
3035     function test_sql_empty() {
3036         $DB = $this->tdb;
3037         $dbman = $DB->get_manager();
3039         $table = $this->get_test_table();
3040         $tablename = $table->getName();
3042         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3043         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3044         $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value');
3045         $table->add_field('namenotnullnodeflt', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3046         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3047         $dbman->create_table($table);
3049         $DB->insert_record($tablename, array('name'=>'', 'namenotnull'=>''));
3050         $DB->insert_record($tablename, array('name'=>null));
3051         $DB->insert_record($tablename, array('name'=>'lalala'));
3052         $DB->insert_record($tablename, array('name'=>0));
3054         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = '".$DB->sql_empty()."'");
3055         $this->assertEqual(count($records), 1);
3056         $record = reset($records);
3057         $this->assertEqual($record->name, '');
3059         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = '".$DB->sql_empty()."'");
3060         $this->assertEqual(count($records), 1);
3061         $record = reset($records);
3062         $this->assertEqual($record->namenotnull, '');
3064         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = '".$DB->sql_empty()."'");
3065         $this->assertEqual(count($records), 4);
3066         $record = reset($records);
3067         $this->assertEqual($record->namenotnullnodeflt, '');
3068     }
3070     function test_sql_isempty() {
3071         $DB = $this->tdb;
3072         $dbman = $DB->get_manager();
3074         $table = $this->get_test_table();
3075         $tablename = $table->getName();
3077         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3078         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3079         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3080         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
3081         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3082         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3083         $dbman->create_table($table);
3085         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
3086         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
3087         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
3088         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
3090         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'name', false, false));
3091         $this->assertEqual(count($records), 1);
3092         $record = reset($records);
3093         $this->assertEqual($record->name, '');
3095         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'namenull', true, false));
3096         $this->assertEqual(count($records), 1);
3097         $record = reset($records);
3098         $this->assertEqual($record->namenull, '');
3100         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'description', false, true));
3101         $this->assertEqual(count($records), 1);
3102         $record = reset($records);
3103         $this->assertEqual($record->description, '');
3105         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'descriptionnull', true, true));
3106         $this->assertEqual(count($records), 1);
3107         $record = reset($records);
3108         $this->assertEqual($record->descriptionnull, '');
3109     }
3111     function test_sql_isnotempty() {
3112         $DB = $this->tdb;
3113         $dbman = $DB->get_manager();
3115         $table = $this->get_test_table();
3116         $tablename = $table->getName();
3118         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3119         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3120         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3121         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
3122         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3123         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3124         $dbman->create_table($table);
3126         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
3127         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
3128         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
3129         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
3131         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'name', false, false));
3132         $this->assertEqual(count($records), 3);
3133         $record = reset($records);
3134         $this->assertEqual($record->name, '??');
3136         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'namenull', true, false));
3137         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
3138         $record = reset($records);
3139         $this->assertEqual($record->namenull, 'la'); // so 'la' is the first non-empty 'namenull' record
3141         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'description', false, true));
3142         $this->assertEqual(count($records), 3);
3143         $record = reset($records);
3144         $this->assertEqual($record->description, '??');
3146         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'descriptionnull', true, true));
3147         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
3148         $record = reset($records);
3149         $this->assertEqual($record->descriptionnull, 'lalala'); // so 'lalala' is the first non-empty 'descriptionnull' record
3150     }
3152     function test_sql_regex() {
3153         $DB = $this->tdb;
3154         $dbman = $DB->get_manager();
3156         $table = $this->get_test_table();
3157         $tablename = $table->getName();
3159         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3160         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3161         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3162         $dbman->create_table($table);
3164         $DB->insert_record($tablename, array('name'=>'lalala'));
3165         $DB->insert_record($tablename, array('name'=>'holaaa'));
3166         $DB->insert_record($tablename, array('name'=>'aouch'));
3168         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex()." ?";
3169         $params = array('a$');
3170         if ($DB->sql_regex_supported()) {
3171             $records = $DB->get_records_sql($sql, $params);
3172             $this->assertEqual(count($records), 2);
3173         } else {
3174             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
3175         }
3177         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false)." ?";
3178         $params = array('.a');
3179         if ($DB->sql_regex_supported()) {
3180             $records = $DB->get_records_sql($sql, $params);
3181             $this->assertEqual(count($records), 1);
3182         } else {
3183             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
3184         }
3186     }
3188     /**
3189      * Test some more complex SQL syntax which moodle uses and depends on to work
3190      * useful to determine if new database libraries can be supported.
3191      */
3192     public function test_get_records_sql_complicated() {
3193         $DB = $this->tdb;
3194         $dbman = $DB->get_manager();
3196         $table = $this->get_test_table();
3197         $tablename = $table->getName();
3199         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3200         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3201         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3202         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3203         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3204         $dbman->create_table($table);
3206         $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello', 'name'=>'xyz'));
3207         $DB->insert_record($tablename, array('course' => 3, 'content' => 'world', 'name'=>'abc'));
3208         $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello', 'name'=>'def'));
3209         $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe', 'name'=>'abc'));
3211         // test limits in queries with DISTINCT/ALL clauses and multiple whitespace. MDL-25268
3212         $sql = "SELECT   DISTINCT   course
3213                   FROM {{$tablename}}
3214                  ORDER BY course";
3215         // only limitfrom
3216         $records = $DB->get_records_sql($sql, null, 1);
3217         $this->assertEqual(2, count($records));
3218         $this->assertEqual(3, reset($records)->course);
3219         $this->assertEqual(5, next($records)->course);
3220         // only limitnum
3221         $records = $DB->get_records_sql($sql, null, 0, 2);
3222         $this->assertEqual(2, count($records));
3223         $this->assertEqual(2, reset($records)->course);
3224         $this->assertEqual(3, next($records)->course);
3225         // both limitfrom and limitnum
3226         $records = $DB->get_records_sql($sql, null, 2, 2);
3227         $this->assertEqual(1, count($records));
3228         $this->assertEqual(5, reset($records)->course);
3230         // we have sql like this in moodle, this syntax breaks on older versions of sqlite for example..
3231         $sql = "SELECT a.id AS id, a.course AS course
3232                   FROM {{$tablename}} a
3233                   JOIN (SELECT * FROM {{$tablename}}) b ON a.id = b.id
3234                  WHERE a.course = ?";
3236         $records = $DB->get_records_sql($sql, array(3));
3237         $this->assertEqual(2, count($records));
3238         $this->assertEqual(1, reset($records)->id);
3239         $this->assertEqual(2, next($records)->id);
3241         // do NOT try embedding sql_xxxx() helper functions in conditions array of count_records(), they don't break params/binding!
3242         $count = $DB->count_records_select($tablename, "course = :course AND ".$DB->sql_compare_text('content')." = :content", array('course' => 3, 'content' => 'hello'));
3243         $this->assertEqual(1, $count);
3245         // test int x string comparison
3246         $sql = "SELECT *
3247                   FROM {{$tablename}} c
3248                  WHERE name = ?";
3249         $this->assertEqual(count($DB->get_records_sql($sql, array(10))), 0);
3250         $this->assertEqual(count($DB->get_records_sql($sql, array("10"))), 0);
3251         $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1'));
3252         $DB->insert_record($tablename, array('course' => 7, 'content' => 'yy', 'name'=>'2'));
3253         $this->assertEqual(count($DB->get_records_sql($sql, array(1))), 1);
3254         $this->assertEqual(count($DB->get_records_sql($sql, array("1"))), 1);
3255         $this->assertEqual(count($DB->get_records_sql($sql, array(10))), 0);
3256         $this->assertEqual(count($DB->get_records_sql($sql, array("10"))), 0);
3257         $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1abc'));
3258         $this->assertEqual(count($DB->get_records_sql($sql, array(1))), 1);
3259         $this->assertEqual(count($DB->get_records_sql($sql, array("1"))), 1);
3260     }
3262     function test_onelevel_commit() {
3263         $DB = $this->tdb;
3264         $dbman = $DB->get_manager();
3266         $table = $this->get_test_table();
3267         $tablename = $table->getName();
3269         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3270         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3271         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3272         $dbman->create_table($table);
3274         $transaction = $DB->start_delegated_transaction();
3275         $data = (object)array('course'=>3);
3276         $this->assertEqual(0, $DB->count_records($tablename));
3277         $DB->insert_record($tablename, $data);
3278         $this->assertEqual(1, $DB->count_records($tablename));
3279         $transaction->allow_commit();
3280         $this->assertEqual(1, $DB->count_records($tablename));
3281     }
3283     function test_onelevel_rollback() {
3284         $DB = $this->tdb;
3285         $dbman = $DB->get_manager();
3287         $table = $this->get_test_table();
3288         $tablename = $table->getName();
3290         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3291         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3292         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3293         $dbman->create_table($table);
3295         // this might in fact encourage ppl to migrate from myisam to innodb
3297         $transaction = $DB->start_delegated_transaction();
3298         $data = (object)array('course'=>3);
3299         $this->assertEqual(0, $DB->count_records($tablename));
3300         $DB->insert_record($tablename, $data);
3301         $this->assertEqual(1, $DB->count_records($tablename));
3302         try {
3303             $transaction->rollback(new Exception('test'));
3304             $this->fail('transaction rollback must rethrow exception');
3305         } catch (Exception $e) {
3306         }
3307         $this->assertEqual(0, $DB->count_records($tablename));
3308     }
3310     function test_nested_transactions() {
3311         $DB = $this->tdb;
3312         $dbman = $DB->get_manager();
3314         $table = $this->get_test_table();
3315         $tablename = $table->getName();
3317         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3318         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3319         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3320         $dbman->create_table($table);
3322         // two level commit
3323         $this->assertFalse($DB->is_transaction_started());
3324         $transaction1 = $DB->start_delegated_transaction();
3325         $this->assertTrue($DB->is_transaction_started());
3326         $data = (object)array('course'=>3);
3327         $DB->insert_record($tablename, $data);
3328         $transaction2 = $DB->start_delegated_transaction();
3329         $data = (object)array('course'=>4);
3330         $DB->insert_record($tablename, $data);
3331         $transaction2->allow_commit();
3332         $this->assertTrue($DB->is_transaction_started());
3333         $transaction1->allow_commit();
3334         $this->assertFalse($DB->is_transaction_started());
3335         $this->assertEqual(2, $DB->count_records($tablename));
3337         $DB->delete_records($tablename);
3339         // rollback from top level
3340         $transaction1 = $DB->start_delegated_transaction();
3341         $data = (object)array('course'=>3);
3342         $DB->insert_record($tablename, $data);
3343         $transaction2 = $DB->start_delegated_transaction();
3344         $data = (object)array('course'=>4);
3345         $DB->insert_record($tablename, $data);
3346         $transaction2->allow_commit();
3347         try {
3348             $transaction1->rollback(new Exception('test'));
3349             $this->fail('transaction rollback must rethrow exception');
3350         } catch (Exception $e) {
3351             $this->assertEqual(get_class($e), 'Exception');
3352         }
3353         $this->assertEqual(0, $DB->count_records($tablename));
3355         $DB->delete_records($tablename);
3357         // rollback from nested level
3358         $transaction1 = $DB->start_delegated_transaction();
3359         $data = (object)array('course'=>3);
3360         $DB->insert_record($tablename, $data);
3361         $transaction2 = $DB->start_delegated_transaction();
3362         $data = (object)array('course'=>4);
3363         $DB->insert_record($tablename, $data);
3364         try {
3365             $transaction2->rollback(new Exception('test'));
3366             $this->fail('transaction rollback must rethrow exception');
3367         } catch (Exception $e) {
3368             $this->assertEqual(get_class($e), 'Exception');
3369         }
3370         $this->assertEqual(2, $DB->count_records($tablename)); // not rolled back yet
3371         try {
3372             $transaction1->allow_commit();
3373         } catch (Exception $e) {
3374             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3375         }
3376         $this->assertEqual(2, $DB->count_records($tablename)); // not rolled back yet
3377         // the forced rollback is done from the default_exception handler and similar places,
3378         // let's do it manually here
3379         $this->assertTrue($DB->is_transaction_started());
3380         $DB->force_transaction_rollback();
3381         $this->assertFalse($DB->is_transaction_started());
3382         $this->assertEqual(0, $DB->count_records($tablename)); // finally rolled back
3384         $DB->delete_records($tablename);
3385     }
3387     function test_transactions_forbidden() {
3388         $DB = $this->tdb;
3389         $dbman = $DB->get_manager();
3391         $table = $this->get_test_table();
3392         $tablename = $table->getName();
3394         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3395         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3396         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3397         $dbman->create_table($table);
3399         $DB->transactions_forbidden();
3400         $transaction = $DB->start_delegated_transaction();
3401         $data = (object)array('course'=>1);
3402         $DB->insert_record($tablename, $data);
3403         try {
3404             $DB->transactions_forbidden();
3405         } catch (Exception $e) {
3406             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3407         }
3408         // the previous test does not force rollback
3409         $transaction->allow_commit();
3410         $this->assertFalse($DB->is_transaction_started());
3411         $this->assertEqual(1, $DB->count_records($tablename));
3412     }
3414     function test_wrong_transactions() {
3415         $DB = $this->tdb;
3416         $dbman = $DB->get_manager();
3418         $table = $this->get_test_table();
3419         $tablename = $table->getName();
3421         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3422         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3423         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3424         $dbman->create_table($table);
3427         // wrong order of nested commits
3428         $transaction1 = $DB->start_delegated_transaction();
3429         $data = (object)array('course'=>3);
3430         $DB->insert_record($tablename, $data);
3431         $transaction2 = $DB->start_delegated_transaction();
3432         $data = (object)array('course'=>4);
3433         $DB->insert_record($tablename, $data);
3434         try {
3435             $transaction1->allow_commit();
3436             $this->fail('wrong order of commits must throw exception');
3437         } catch (Exception $e) {
3438             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3439         }
3440         try {
3441             $transaction2->allow_commit();
3442             $this->fail('first wrong commit forces rollback');
3443         } catch (Exception $e) {
3444             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3445         }
3446         // this is done in default exception handler usually
3447         $this->assertTrue($DB->is_transaction_started());
3448         $this->assertEqual(2, $DB->count_records($tablename)); // not rolled back yet
3449         $DB->force_transaction_rollback();
3450         $this->assertEqual(0, $DB->count_records($tablename));
3451         $DB->delete_records($tablename);
3454         // wrong order of nested rollbacks
3455         $transaction1 = $DB->start_delegated_transaction();
3456         $data = (object)array('course'=>3);
3457         $DB->insert_record($tablename, $data);
3458         $transaction2 = $DB->start_delegated_transaction();
3459         $data = (object)array('course'=>4);
3460         $DB->insert_record($tablename, $data);
3461         try {
3462             // this first rollback should prevent all other rollbacks
3463             $transaction1->rollback(new Exception('test'));
3464         } catch (Exception $e) {
3465             $this->assertEqual(get_class($e), 'Exception');
3466         }
3467         try {
3468             $transaction2->rollback(new Exception('test'));
3469         } catch (Exception $e) {
3470             $this->assertEqual(get_class($e), 'Exception');
3471         }
3472         try {
3473             $transaction1->rollback(new Exception('test'));
3474         } catch (Exception $e) {
3475             // the rollback was used already once, no way to use it again
3476             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3477         }
3478         // this is done in default exception handler usually
3479         $this->assertTrue($DB->is_transaction_started());
3480         $DB->force_transaction_rollback();
3481         $DB->delete_records($tablename);
3484         // unknown transaction object
3485         $transaction1 = $DB->start_delegated_transaction();
3486         $data = (object)array('course'=>3);
3487         $DB->insert_record($tablename, $data);
3488         $transaction2 = new moodle_transaction($DB);
3489         try {
3490             $transaction2->allow_commit();
3491             $this->fail('foreign transaction must fail');
3492         } catch (Exception $e) {
3493             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3494         }
3495         try {
3496             $transaction1->allow_commit();
3497             $this->fail('first wrong commit forces rollback');
3498         } catch (Exception $e) {
3499             $this->assertEqual(get_class($e), 'dml_transaction_exception');
3500         }
3501         $DB->force_transaction_rollback();
3502         $DB->delete_records($tablename);
3503     }
3505     function test_concurent_transactions() {
3506         // Notes about this test:
3507         // 1- MySQL needs to use one engine with transactions support (InnoDB).
3508         // 2- MSSQL needs to have enabled versioning for read committed
3509         //    transactions (ALTER DATABASE xxx SET READ_COMMITTED_SNAPSHOT ON)
3510         $DB = $this->tdb;
3511         $dbman = $DB->get_manager();
3513         $table = $this->get_test_table();
3514         $tablename = $table->getName();
3516         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3517         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3518         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3519         $dbman->create_table($table);
3521         $transaction = $DB->start_delegated_transaction();
3522         $data = (object)array('course'=>1);
3523         $this->assertEqual(0, $DB->count_records($tablename));
3524         $DB->insert_record($tablename, $data);
3525         $this->assertEqual(1, $DB->count_records($tablename));
3527         //open second connection
3528         $cfg = $DB->export_dbconfig();
3529         if (!isset($cfg->dboptions)) {
3530             $cfg->dboptions = array();
3531         }
3532         $DB2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
3533         $DB2->connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
3535         // second instance should not see pending inserts
3536         $this->assertEqual(0, $DB2->count_records($tablename));
3537         $data = (object)array('course'=>2);
3538         $DB2->insert_record($tablename, $data);
3539         $this->assertEqual(1, $DB2->count_records($tablename));
3541         // first should see the changes done from second
3542         $this->assertEqual(2, $DB->count_records($tablename));
3544         // now commit and we should see it finally in second connections
3545         $transaction->allow_commit();
3546         $this->assertEqual(2, $DB2->count_records($tablename));
3548         $DB2->dispose();
3549     }
3551     public function test_bound_param_types() {
3552         $DB = $this->tdb;
3553         $dbman = $DB->get_manager();
3555         $table = $this->get_test_table();
3556         $tablename = $table->getName();
3558         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3559         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3560         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3561         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3562         $dbman->create_table($table);
3564         $this->assertTrue($DB->insert_record($tablename, array('name' => '1', 'content'=>'xx')));
3565         $this->assertTrue($DB->insert_record($tablename, array('name' => 2, 'content'=>'yy')));
3566         $this->assertTrue($DB->insert_record($tablename, array('name' => 'somestring', 'content'=>'zz')));
3567         $this->assertTrue($DB->insert_record($tablename, array('name' => 'aa', 'content'=>'1')));
3568         $this->assertTrue($DB->insert_record($tablename, array('name' => 'bb', 'content'=>2)));
3569         $this->assertTrue($DB->insert_record($tablename, array('name' => 'cc', 'content'=>'sometext')));
3572         // Conditions in CHAR columns
3573         $this->assertTrue($DB->record_exists($tablename, array('name'=>1)));
3574         $this->assertTrue($DB->record_exists($tablename, array('name'=>'1')));
3575         $this->assertFalse($DB->record_exists($tablename, array('name'=>111)));
3576         $this->assertTrue($DB->get_record($tablename, array('name'=>1)));
3577         $this->assertTrue($DB->get_record($tablename, array('name'=>'1')));
3578         $this->assertFalse($DB->get_record($tablename, array('name'=>111)));
3579         $sqlqm = "SELECT *
3580                     FROM {{$tablename}}
3581                    WHERE name = ?";
3582         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array(1)));
3583         $this->assertEqual(1, count($records));
3584         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array('1')));
3585         $this->assertEqual(1, count($records));
3586         $records = $DB->get_records_sql($sqlqm, array(222));
3587         $this->assertEqual(0, count($records));
3588         $sqlnamed = "SELECT *
3589                        FROM {{$tablename}}
3590                       WHERE name = :name";
3591         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('name' => 2)));
3592         $this->assertEqual(1, count($records));
3593         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('name' => '2')));
3594         $this->assertEqual(1, count($records));
3596         // Conditions in TEXT columns always must be performed with the sql_compare_text
3597         // helper function on both sides of the condition
3598         $sqlqm = "SELECT *
3599                     FROM {{$tablename}}
3600                    WHERE " . $DB->sql_compare_text('content') . " =  " . $DB->sql_compare_text('?');
3601         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array('1')));
3602         $this->assertEqual(1, count($records));
3603         $this->assertTrue($records = $DB->get_records_sql($sqlqm, array(1)));
3604         $this->assertEqual(1, count($records));
3605         $sqlnamed = "SELECT *
3606                        FROM {{$tablename}}
3607                       WHERE " . $DB->sql_compare_text('content') . " =  " . $DB->sql_compare_text(':content');
3608         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('content' => 2)));
3609         $this->assertEqual(1, count($records));
3610         $this->assertTrue($records = $DB->get_records_sql($sqlnamed, array('content' => '2')));
3611         $this->assertEqual(1, count($records));
3612     }
3614     public function test_limits_and_offsets() {
3615         $DB = $this->tdb;
3616         $dbman = $DB->get_manager();
3618         if (false) $DB = new moodle_database ();
3620         $table = $this->get_test_table();
3621         $tablename = $table->getName();
3623         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3624         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3625         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3626         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3627         $dbman->create_table($table);
3629         $this->assertTrue($DB->insert_record($tablename, array('name' => 'a', 'content'=>'one')));
3630         $this->assertTrue($DB->insert_record($tablename, array('name' => 'b', 'content'=>'two')));
3631         $this->assertTrue($DB->insert_record($tablename, array('name' => 'c', 'content'=>'three')));
3632         $this->assertTrue($DB->insert_record($tablename, array('name' => 'd', 'content'=>'four')));
3633         $this->assertTrue($DB->insert_record($tablename, array('name' => 'e', 'content'=>'five')));
3634         $this->assertTrue($DB->insert_record($tablename, array('name' => 'f', 'content'=>'six')));
3636         $sqlqm = "SELECT *
3637                     FROM {{$tablename}}";
3638         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 4));
3639         $this->assertEqual(2, count($records));
3640         $this->assertEqual('e', reset($records)->name);
3641         $this->assertEqual('f', end($records)->name);
3643         $sqlqm = "SELECT *
3644                     FROM {{$tablename}}";
3645         $this->assertFalse($records = $DB->get_records_sql($sqlqm, null, 8));
3647         $sqlqm = "SELECT *
3648                     FROM {{$tablename}}";
3649         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 0, 4));
3650         $this->assertEqual(4, count($records));
3651         $this->assertEqual('a', reset($records)->name);
3652         $this->assertEqual('d', end($records)->name);
3654         $sqlqm = "SELECT *
3655                     FROM {{$tablename}}";
3656         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 0, 8));
3657         $this->assertEqual(6, count($records));
3658         $this->assertEqual('a', reset($records)->name);
3659         $this->assertEqual('f', end($records)->name);
3661         $sqlqm = "SELECT *
3662                     FROM {{$tablename}}";
3663         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 1, 4));
3664         $this->assertEqual(4, count($records));
3665         $this->assertEqual('b', reset($records)->name);
3666         $this->assertEqual('e', end($records)->name);
3668         $sqlqm = "SELECT *
3669                     FROM {{$tablename}}";
3670         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 4, 4));
3671         $this->assertEqual(2, count($records));
3672         $this->assertEqual('e', reset($records)->name);
3673         $this->assertEqual('f', end($records)->name);
3675         $sqlqm = "SELECT t.*, t.name AS test
3676                     FROM {{$tablename}} t
3677                     ORDER BY t.id ASC";
3678         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 4, 4));
3679         $this->assertEqual(2, count($records));
3680         $this->assertEqual('e', reset($records)->name);
3681         $this->assertEqual('f', end($records)->name);
3683         $sqlqm = "SELECT DISTINCT t.name, t.name AS test
3684                     FROM {{$tablename}} t
3685                     ORDER BY t.name DESC";
3686         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 4, 4));
3687         $this->assertEqual(2, count($records));
3688         $this->assertEqual('b', reset($records)->name);
3689         $this->assertEqual('a', end($records)->name);
3691         $sqlqm = "SELECT 1
3692                     FROM {{$tablename}} t
3693                     WHERE t.name = 'a'";
3694         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 0, 1));
3695         $this->assertEqual(1, count($records));
3697         $sqlqm = "SELECT 'constant'
3698                     FROM {{$tablename}} t
3699                     WHERE t.name = 'a'";
3700         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 0, 8));
3701         $this->assertEqual(1, count($records));
3703         $this->assertTrue($DB->insert_record($tablename, array('name' => 'a', 'content'=>'one')));
3704         $this->assertTrue($DB->insert_record($tablename, array('name' => 'b', 'content'=>'two')));
3705         $this->assertTrue($DB->insert_record($tablename, array('name' => 'c', 'content'=>'three')));
3707         $sqlqm = "SELECT t.name, COUNT(DISTINCT t2.id) AS count, 'Test' AS teststring
3708                     FROM {{$tablename}} t
3709                     LEFT JOIN (
3710                         SELECT t.id, t.name
3711                         FROM {{$tablename}} t
3712                     ) t2 ON t2.name = t.name
3713                     GROUP BY t.name
3714                     ORDER BY t.name ASC";
3715         $this->assertTrue($records = $DB->get_records_sql($sqlqm));
3716         $this->assertEqual(6, count($records));         // a,b,c,d,e,f
3717         $this->assertEqual(2, reset($records)->count);  // a has 2 records now
3718         $this->assertEqual(1, end($records)->count);    // f has 1 record still
3720         $this->assertTrue($records = $DB->get_records_sql($sqlqm, null, 0, 2));
3721         $this->assertEqual(2, count($records));
3722         $this->assertEqual(2, reset($records)->count);
3723         $this->assertEqual(2, end($records)->count);
3724     }
3727 /**
3728  * This class is not a proper subclass of moodle_database. It is
3729  * intended to be used only in unit tests, in order to gain access to the
3730  * protected methods of moodle_database, and unit test them.
3731  */
3732 class moodle_database_for_testing extends moodle_database {
3733     protected $prefix = 'mdl_';
3735     public function public_fix_table_names($sql) {
3736         return $this->fix_table_names($sql);
3737     }
3739     public function driver_installed(){}
3740     public function get_dbfamily(){}
3741     protected function get_dbtype(){}
3742     protected function get_dblibrary(){}
3743     public function get_name(){}
3744     public function get_configuration_help(){}
3745     public function get_configuration_hints(){}
3746     public function connect($dbhost, $dbuser, $dbpass, $dbname, $prefix, array $dboptions=null){}
3747     public function get_server_info(){}
3748     protected function allowed_param_types(){}
3749     public function get_last_error(){}
3750     public function get_tables($usecache=true){}
3751     public function get_indexes($table){}
3752     public function get_columns($table, $usecache=true){}
3753     protected function normalise_value($column, $value){}
3754     public function set_debug($state){}
3755     public function get_debug(){}
3756     public function set_logging($state){}
3757     public function change_database_structure($sql){}
3758     public function execute($sql, array $params=null){}
3759     public function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0){}
3760     public function get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0){}
3761     public function get_fieldset_sql($sql, array $params=null){}
3762     public function insert_record_raw($table, $params, $returnid=true, $bulk=false, $customsequence=false){}
3763     public function insert_record($table, $dataobject, $returnid=true, $bulk=false){}
3764     public function import_record($table, $dataobject){}
3765     public function update_record_raw($table, $params, $bulk=false){}
3766     public function update_record($table, $dataobject, $bulk=false){}
3767     public function set_field_select($table, $newfield, $newvalue, $select, array $params=null){}
3768     public function delete_records_select($table, $select, array $params=null){}
3769     public function sql_concat(){}
3770     public function sql_concat_join($separator="' '", $elements=array()){}
3771     public function sql_substr($expr, $start, $length=false){}
3772     public function begin_transaction() {}
3773     public function commit_transaction() {}
3774     public function rollback_transaction() {}