Merge branch 'MDL-53019' of git://github.com/stronk7/moodle
[moodle.git] / lib / dml / tests / dml_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * DML layer tests.
19  *
20  * @package    core_dml
21  * @category   phpunit
22  * @copyright  2008 Nicolas Connault
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 class core_dml_testcase extends database_driver_testcase {
30     protected function setUp() {
31         parent::setUp();
32         $dbman = $this->tdb->get_manager(); // Loads DDL libs.
33     }
35     /**
36      * Get a xmldb_table object for testing, deleting any existing table
37      * of the same name, for example if one was left over from a previous test
38      * run that crashed.
39      *
40      * @param string $suffix table name suffix, use if you need more test tables
41      * @return xmldb_table the table object.
42      */
43     private function get_test_table($suffix = '') {
44         $tablename = "test_table";
45         if ($suffix !== '') {
46             $tablename .= $suffix;
47         }
49         $table = new xmldb_table($tablename);
50         $table->setComment("This is a test'n drop table. You can drop it safely");
51         return $table;
52     }
54     public function test_diagnose() {
55         $DB = $this->tdb;
56         $result = $DB->diagnose();
57         $this->assertNull($result, 'Database self diagnostics failed %s');
58     }
60     public function test_get_server_info() {
61         $DB = $this->tdb;
62         $result = $DB->get_server_info();
63         $this->assertInternalType('array', $result);
64         $this->assertArrayHasKey('description', $result);
65         $this->assertArrayHasKey('version', $result);
66     }
68     public function test_get_in_or_equal() {
69         $DB = $this->tdb;
71         // SQL_PARAMS_QM - IN or =.
73         // Correct usage of multiple values.
74         $in_values = array('value1', 'value2', '3', 4, null, false, true);
75         list($usql, $params) = $DB->get_in_or_equal($in_values);
76         $this->assertSame('IN ('.implode(',', array_fill(0, count($in_values), '?')).')', $usql);
77         $this->assertEquals(count($in_values), count($params));
78         foreach ($params as $key => $value) {
79             $this->assertSame($in_values[$key], $value);
80         }
82         // Correct usage of single value (in an array).
83         $in_values = array('value1');
84         list($usql, $params) = $DB->get_in_or_equal($in_values);
85         $this->assertEquals("= ?", $usql);
86         $this->assertCount(1, $params);
87         $this->assertEquals($in_values[0], $params[0]);
89         // Correct usage of single value.
90         $in_value = 'value1';
91         list($usql, $params) = $DB->get_in_or_equal($in_values);
92         $this->assertEquals("= ?", $usql);
93         $this->assertCount(1, $params);
94         $this->assertEquals($in_value, $params[0]);
96         // SQL_PARAMS_QM - NOT IN or <>.
98         // Correct usage of multiple values.
99         $in_values = array('value1', 'value2', 'value3', 'value4');
100         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
101         $this->assertEquals("NOT IN (?,?,?,?)", $usql);
102         $this->assertCount(4, $params);
103         foreach ($params as $key => $value) {
104             $this->assertEquals($in_values[$key], $value);
105         }
107         // Correct usage of single value (in array().
108         $in_values = array('value1');
109         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
110         $this->assertEquals("<> ?", $usql);
111         $this->assertCount(1, $params);
112         $this->assertEquals($in_values[0], $params[0]);
114         // Correct usage of single value.
115         $in_value = 'value1';
116         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
117         $this->assertEquals("<> ?", $usql);
118         $this->assertCount(1, $params);
119         $this->assertEquals($in_value, $params[0]);
121         // SQL_PARAMS_NAMED - IN or =.
123         // Correct usage of multiple values.
124         $in_values = array('value1', 'value2', 'value3', 'value4');
125         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
126         $this->assertCount(4, $params);
127         reset($in_values);
128         $ps = array();
129         foreach ($params as $key => $value) {
130             $this->assertEquals(current($in_values), $value);
131             next($in_values);
132             $ps[] = ':'.$key;
133         }
134         $this->assertEquals("IN (".implode(',', $ps).")", $usql);
136         // Correct usage of single values (in array).
137         $in_values = array('value1');
138         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
139         $this->assertCount(1, $params);
140         $value = reset($params);
141         $key = key($params);
142         $this->assertEquals("= :$key", $usql);
143         $this->assertEquals($in_value, $value);
145         // Correct usage of single value.
146         $in_value = 'value1';
147         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
148         $this->assertCount(1, $params);
149         $value = reset($params);
150         $key = key($params);
151         $this->assertEquals("= :$key", $usql);
152         $this->assertEquals($in_value, $value);
154         // SQL_PARAMS_NAMED - NOT IN or <>.
156         // Correct usage of multiple values.
157         $in_values = array('value1', 'value2', 'value3', 'value4');
158         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
159         $this->assertCount(4, $params);
160         reset($in_values);
161         $ps = array();
162         foreach ($params as $key => $value) {
163             $this->assertEquals(current($in_values), $value);
164             next($in_values);
165             $ps[] = ':'.$key;
166         }
167         $this->assertEquals("NOT IN (".implode(',', $ps).")", $usql);
169         // Correct usage of single values (in array).
170         $in_values = array('value1');
171         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
172         $this->assertCount(1, $params);
173         $value = reset($params);
174         $key = key($params);
175         $this->assertEquals("<> :$key", $usql);
176         $this->assertEquals($in_value, $value);
178         // Correct usage of single value.
179         $in_value = 'value1';
180         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
181         $this->assertCount(1, $params);
182         $value = reset($params);
183         $key = key($params);
184         $this->assertEquals("<> :$key", $usql);
185         $this->assertEquals($in_value, $value);
187         // Make sure the param names are unique.
188         list($usql1, $params1) = $DB->get_in_or_equal(array(1, 2, 3), SQL_PARAMS_NAMED, 'param');
189         list($usql2, $params2) = $DB->get_in_or_equal(array(1, 2, 3), SQL_PARAMS_NAMED, 'param');
190         $params1 = array_keys($params1);
191         $params2 = array_keys($params2);
192         $common = array_intersect($params1, $params2);
193         $this->assertCount(0, $common);
195         // Some incorrect tests.
197         // Incorrect usage passing not-allowed params type.
198         $in_values = array(1, 2, 3);
199         try {
200             list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false);
201             $this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
202         } catch (moodle_exception $e) {
203             $this->assertInstanceOf('dml_exception', $e);
204             $this->assertSame('typenotimplement', $e->errorcode);
205         }
207         // Incorrect usage passing empty array.
208         $in_values = array();
209         try {
210             list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
211             $this->fail('An Exception is missing, expected due to empty array of items');
212         } catch (moodle_exception $e) {
213             $this->assertInstanceOf('coding_exception', $e);
214         }
216         // Test using $onemptyitems.
218         // Correct usage passing empty array and $onemptyitems = null (equal = true, QM).
219         $in_values = array();
220         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, null);
221         $this->assertSame(' IS NULL', $usql);
222         $this->assertSame(array(), $params);
224         // Correct usage passing empty array and $onemptyitems = null (equal = false, NAMED).
225         $in_values = array();
226         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, null);
227         $this->assertSame(' IS NOT NULL', $usql);
228         $this->assertSame(array(), $params);
230         // Correct usage passing empty array and $onemptyitems = true (equal = true, QM).
231         $in_values = array();
232         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, true);
233         $this->assertSame('= ?', $usql);
234         $this->assertSame(array(true), $params);
236         // Correct usage passing empty array and $onemptyitems = true (equal = false, NAMED).
237         $in_values = array();
238         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, true);
239         $this->assertCount(1, $params);
240         $value = reset($params);
241         $key = key($params);
242         $this->assertSame('<> :'.$key, $usql);
243         $this->assertSame($value, true);
245         // Correct usage passing empty array and $onemptyitems = -1 (equal = true, QM).
246         $in_values = array();
247         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, -1);
248         $this->assertSame('= ?', $usql);
249         $this->assertSame(array(-1), $params);
251         // Correct usage passing empty array and $onemptyitems = -1 (equal = false, NAMED).
252         $in_values = array();
253         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, -1);
254         $this->assertCount(1, $params);
255         $value = reset($params);
256         $key = key($params);
257         $this->assertSame('<> :'.$key, $usql);
258         $this->assertSame($value, -1);
260         // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = true, QM).
261         $in_values = array();
262         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, 'onevalue');
263         $this->assertSame('= ?', $usql);
264         $this->assertSame(array('onevalue'), $params);
266         // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = false, NAMED).
267         $in_values = array();
268         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, 'onevalue');
269         $this->assertCount(1, $params);
270         $value = reset($params);
271         $key = key($params);
272         $this->assertSame('<> :'.$key, $usql);
273         $this->assertSame($value, 'onevalue');
274     }
276     public function test_fix_table_names() {
277         $DB = new moodle_database_for_testing();
278         $prefix = $DB->get_prefix();
280         // Simple placeholder.
281         $placeholder = "{user_123}";
282         $this->assertSame($prefix."user_123", $DB->public_fix_table_names($placeholder));
284         // Wrong table name.
285         $placeholder = "{user-a}";
286         $this->assertSame($placeholder, $DB->public_fix_table_names($placeholder));
288         // Wrong table name.
289         $placeholder = "{123user}";
290         $this->assertSame($placeholder, $DB->public_fix_table_names($placeholder));
292         // Full SQL.
293         $sql = "SELECT * FROM {user}, {funny_table_name}, {mdl_stupid_table} WHERE {user}.id = {funny_table_name}.userid";
294         $expected = "SELECT * FROM {$prefix}user, {$prefix}funny_table_name, {$prefix}mdl_stupid_table WHERE {$prefix}user.id = {$prefix}funny_table_name.userid";
295         $this->assertSame($expected, $DB->public_fix_table_names($sql));
296     }
298     public function test_fix_sql_params() {
299         $DB = $this->tdb;
300         $prefix = $DB->get_prefix();
302         $table = $this->get_test_table();
303         $tablename = $table->getName();
305         // Correct table placeholder substitution.
306         $sql = "SELECT * FROM {{$tablename}}";
307         $sqlarray = $DB->fix_sql_params($sql);
308         $this->assertEquals("SELECT * FROM {$prefix}".$tablename, $sqlarray[0]);
310         // Conversions of all param types.
311         $sql = array();
312         $sql[SQL_PARAMS_NAMED]  = "SELECT * FROM {$prefix}testtable WHERE name = :param1, course = :param2";
313         $sql[SQL_PARAMS_QM]     = "SELECT * FROM {$prefix}testtable WHERE name = ?, course = ?";
314         $sql[SQL_PARAMS_DOLLAR] = "SELECT * FROM {$prefix}testtable WHERE name = \$1, course = \$2";
316         $params = array();
317         $params[SQL_PARAMS_NAMED]  = array('param1'=>'first record', 'param2'=>1);
318         $params[SQL_PARAMS_QM]     = array('first record', 1);
319         $params[SQL_PARAMS_DOLLAR] = array('first record', 1);
321         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_NAMED], $params[SQL_PARAMS_NAMED]);
322         $this->assertSame($rsql, $sql[$rtype]);
323         $this->assertSame($rparams, $params[$rtype]);
325         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_QM], $params[SQL_PARAMS_QM]);
326         $this->assertSame($rsql, $sql[$rtype]);
327         $this->assertSame($rparams, $params[$rtype]);
329         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_DOLLAR], $params[SQL_PARAMS_DOLLAR]);
330         $this->assertSame($rsql, $sql[$rtype]);
331         $this->assertSame($rparams, $params[$rtype]);
333         // Malformed table placeholder.
334         $sql = "SELECT * FROM [testtable]";
335         $sqlarray = $DB->fix_sql_params($sql);
336         $this->assertSame($sql, $sqlarray[0]);
338         // Mixed param types (colon and dollar).
339         $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1";
340         $params = array('param1' => 'record1', 'param2' => 3);
341         try {
342             $DB->fix_sql_params($sql, $params);
343             $this->fail("Expecting an exception, none occurred");
344         } catch (moodle_exception $e) {
345             $this->assertInstanceOf('dml_exception', $e);
346         }
348         // Mixed param types (question and dollar).
349         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1";
350         $params = array('param1' => 'record2', 'param2' => 5);
351         try {
352             $DB->fix_sql_params($sql, $params);
353             $this->fail("Expecting an exception, none occurred");
354         } catch (moodle_exception $e) {
355             $this->assertInstanceOf('dml_exception', $e);
356         }
358         // Too few params in sql.
359         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?";
360         $params = array('record2', 3);
361         try {
362             $DB->fix_sql_params($sql, $params);
363             $this->fail("Expecting an exception, none occurred");
364         } catch (moodle_exception $e) {
365             $this->assertInstanceOf('dml_exception', $e);
366         }
368         // Too many params in array: no error, just use what is necessary.
369         $params[] = 1;
370         $params[] = time();
371         $sqlarray = $DB->fix_sql_params($sql, $params);
372         $this->assertInternalType('array', $sqlarray);
373         $this->assertCount(3, $sqlarray[1]);
375         // Named params missing from array.
376         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
377         $params = array('wrongname' => 'record1', 'course' => 1);
378         try {
379             $DB->fix_sql_params($sql, $params);
380             $this->fail("Expecting an exception, none occurred");
381         } catch (moodle_exception $e) {
382             $this->assertInstanceOf('dml_exception', $e);
383         }
385         // Duplicate named param in query - this is a very important feature!!
386         // it helps with debugging of sloppy code.
387         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name";
388         $params = array('name' => 'record2', 'course' => 3);
389         try {
390             $DB->fix_sql_params($sql, $params);
391             $this->fail("Expecting an exception, none occurred");
392         } catch (moodle_exception $e) {
393             $this->assertInstanceOf('dml_exception', $e);
394         }
396         // Extra named param is ignored.
397         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
398         $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha');
399         $sqlarray = $DB->fix_sql_params($sql, $params);
400         $this->assertInternalType('array', $sqlarray);
401         $this->assertCount(2, $sqlarray[1]);
403         // Params exceeding 30 chars length.
404         $sql = "SELECT * FROM {{$tablename}} WHERE name = :long_placeholder_with_more_than_30";
405         $params = array('long_placeholder_with_more_than_30' => 'record1');
406         try {
407             $DB->fix_sql_params($sql, $params);
408             $this->fail("Expecting an exception, none occurred");
409         } catch (moodle_exception $e) {
410             $this->assertInstanceOf('coding_exception', $e);
411         }
413         // Booleans in NAMED params are casting to 1/0 int.
414         $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?";
415         $params = array(true, false);
416         list($sql, $params) = $DB->fix_sql_params($sql, $params);
417         $this->assertTrue(reset($params) === 1);
418         $this->assertTrue(next($params) === 0);
420         // Booleans in QM params are casting to 1/0 int.
421         $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2";
422         $params = array('course1' => true, 'course2' => false);
423         list($sql, $params) = $DB->fix_sql_params($sql, $params);
424         $this->assertTrue(reset($params) === 1);
425         $this->assertTrue(next($params) === 0);
427         // Booleans in DOLLAR params are casting to 1/0 int.
428         $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2";
429         $params = array(true, false);
430         list($sql, $params) = $DB->fix_sql_params($sql, $params);
431         $this->assertTrue(reset($params) === 1);
432         $this->assertTrue(next($params) === 0);
434         // No data types are touched except bool.
435         $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)";
436         $inparams = array('abc', 'ABC', null, '1', 1, 1.4);
437         list($sql, $params) = $DB->fix_sql_params($sql, $inparams);
438         $this->assertSame(array_values($params), array_values($inparams));
439     }
441     public function test_strtok() {
442         // Strtok was previously used by bound emulation, make sure it is not used any more.
443         $DB = $this->tdb;
444         $dbman = $this->tdb->get_manager();
446         $table = $this->get_test_table();
447         $tablename = $table->getName();
449         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
450         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
451         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
452         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
453         $dbman->create_table($table);
455         $str = 'a?b?c?d';
456         $this->assertSame(strtok($str, '?'), 'a');
458         $DB->get_records($tablename, array('id'=>1));
460         $this->assertSame(strtok('?'), 'b');
461     }
463     public function test_tweak_param_names() {
464         // Note the tweak_param_names() method is only available in the oracle driver,
465         // hence we look for expected results indirectly, by testing various DML methods.
466         // with some "extreme" conditions causing the tweak to happen.
467         $DB = $this->tdb;
468         $dbman = $this->tdb->get_manager();
470         $table = $this->get_test_table();
471         $tablename = $table->getName();
473         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
474         // Add some columns with 28 chars in the name.
475         $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10');
476         $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2');
477         $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100');
478         // Add some columns with 30 chars in the name.
479         $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10');
480         $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2');
481         $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100');
483         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
485         $dbman->create_table($table);
487         $this->assertTrue($dbman->table_exists($tablename));
489         // Test insert record.
490         $rec1 = new stdClass();
491         $rec1->long_int_columnname_with_28c = 28;
492         $rec1->long_dec_columnname_with_28c = 28.28;
493         $rec1->long_str_columnname_with_28c = '28';
494         $rec1->long_int_columnname_with_30cxx = 30;
495         $rec1->long_dec_columnname_with_30cxx = 30.30;
496         $rec1->long_str_columnname_with_30cxx = '30';
498         // Insert_record().
499         $rec1->id = $DB->insert_record($tablename, $rec1);
500         $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
502         // Update_record().
503         $DB->update_record($tablename, $rec1);
504         $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
506         // Set_field().
507         $rec1->long_int_columnname_with_28c = 280;
508         $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c,
509             array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28));
510         $rec1->long_dec_columnname_with_28c = 280.28;
511         $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c,
512             array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28));
513         $rec1->long_str_columnname_with_28c = '280';
514         $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c,
515             array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28'));
516         $rec1->long_int_columnname_with_30cxx = 300;
517         $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx,
518             array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30));
519         $rec1->long_dec_columnname_with_30cxx = 300.30;
520         $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx,
521             array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30));
522         $rec1->long_str_columnname_with_30cxx = '300';
523         $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx,
524             array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30'));
525         $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
527         // Delete_records().
528         $rec2 = $DB->get_record($tablename, array('id' => $rec1->id));
529         $rec2->id = $DB->insert_record($tablename, $rec2);
530         $this->assertEquals(2, $DB->count_records($tablename));
531         $DB->delete_records($tablename, (array) $rec2);
532         $this->assertEquals(1, $DB->count_records($tablename));
534         // Get_recordset().
535         $rs = $DB->get_recordset($tablename, (array) $rec1);
536         $iterations = 0;
537         foreach ($rs as $rec2) {
538             $iterations++;
539         }
540         $rs->close();
541         $this->assertEquals(1, $iterations);
542         $this->assertEquals($rec1, $rec2);
544         // Get_records().
545         $recs = $DB->get_records($tablename, (array) $rec1);
546         $this->assertCount(1, $recs);
547         $this->assertEquals($rec1, reset($recs));
549         // Get_fieldset_select().
550         $select = 'id = :id AND
551                    long_int_columnname_with_28c = :long_int_columnname_with_28c AND
552                    long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND
553                    long_str_columnname_with_28c = :long_str_columnname_with_28c AND
554                    long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND
555                    long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND
556                    long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx';
557         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1);
558         $this->assertCount(1, $fields);
559         $this->assertEquals($rec1->long_int_columnname_with_28c, reset($fields));
560         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1);
561         $this->assertEquals($rec1->long_dec_columnname_with_28c, reset($fields));
562         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1);
563         $this->assertEquals($rec1->long_str_columnname_with_28c, reset($fields));
564         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1);
565         $this->assertEquals($rec1->long_int_columnname_with_30cxx, reset($fields));
566         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1);
567         $this->assertEquals($rec1->long_dec_columnname_with_30cxx, reset($fields));
568         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1);
569         $this->assertEquals($rec1->long_str_columnname_with_30cxx, reset($fields));
571         // Overlapping placeholders (progressive str_replace).
572         $overlapselect = 'id = :p AND
573                    long_int_columnname_with_28c = :param1 AND
574                    long_dec_columnname_with_28c = :param2 AND
575                    long_str_columnname_with_28c = :param_with_29_characters_long AND
576                    long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND
577                    long_dec_columnname_with_30cxx = :param_ AND
578                    long_str_columnname_with_30cxx = :param__';
579         $overlapparams = array(
580             'p' => $rec1->id,
581             'param1' => $rec1->long_int_columnname_with_28c,
582             'param2' => $rec1->long_dec_columnname_with_28c,
583             'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c,
584             'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx,
585             'param_' => $rec1->long_dec_columnname_with_30cxx,
586             'param__' => $rec1->long_str_columnname_with_30cxx);
587         $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams);
588         $this->assertCount(1, $recs);
589         $this->assertEquals($rec1, reset($recs));
591         // Execute().
592         $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1);
593         $this->assertEquals(0, $DB->count_records($tablename));
594     }
596     public function test_get_tables() {
597         $DB = $this->tdb;
598         $dbman = $this->tdb->get_manager();
600         // Need to test with multiple DBs.
601         $table = $this->get_test_table();
602         $tablename = $table->getName();
604         $original_count = count($DB->get_tables());
606         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
607         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
609         $dbman->create_table($table);
610         $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
612         $dbman->drop_table($table);
613         $this->assertTrue(count($DB->get_tables()) == $original_count);
614     }
616     public function test_get_indexes() {
617         $DB = $this->tdb;
618         $dbman = $this->tdb->get_manager();
620         $table = $this->get_test_table();
621         $tablename = $table->getName();
623         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
624         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
625         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
626         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
627         $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
628         $dbman->create_table($table);
630         $indices = $DB->get_indexes($tablename);
631         $this->assertInternalType('array', $indices);
632         $this->assertCount(2, $indices);
633         // We do not care about index names for now.
634         $first = array_shift($indices);
635         $second = array_shift($indices);
636         if (count($first['columns']) == 2) {
637             $composed = $first;
638             $single   = $second;
639         } else {
640             $composed = $second;
641             $single   = $first;
642         }
643         $this->assertFalse($single['unique']);
644         $this->assertTrue($composed['unique']);
645         $this->assertCount(1, $single['columns']);
646         $this->assertCount(2, $composed['columns']);
647         $this->assertSame('course', $single['columns'][0]);
648         $this->assertSame('course', $composed['columns'][0]);
649         $this->assertSame('id', $composed['columns'][1]);
650     }
652     public function test_get_columns() {
653         $DB = $this->tdb;
654         $dbman = $this->tdb->get_manager();
656         $table = $this->get_test_table();
657         $tablename = $table->getName();
659         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
660         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
661         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
662         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
663         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
664         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
665         $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, null, null, 300);
666         $table->add_field('anotherfloat', XMLDB_TYPE_FLOAT, null, null, null, null, 400);
667         $table->add_field('negativedfltint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1');
668         $table->add_field('negativedfltnumber', XMLDB_TYPE_NUMBER, '10', null, XMLDB_NOTNULL, null, '-2');
669         $table->add_field('negativedfltfloat', XMLDB_TYPE_FLOAT, '10', null, XMLDB_NOTNULL, null, '-3');
670         $table->add_field('someint1', XMLDB_TYPE_INTEGER, '1', null, null, null, '0');
671         $table->add_field('someint2', XMLDB_TYPE_INTEGER, '2', null, null, null, '0');
672         $table->add_field('someint3', XMLDB_TYPE_INTEGER, '3', null, null, null, '0');
673         $table->add_field('someint4', XMLDB_TYPE_INTEGER, '4', null, null, null, '0');
674         $table->add_field('someint5', XMLDB_TYPE_INTEGER, '5', null, null, null, '0');
675         $table->add_field('someint6', XMLDB_TYPE_INTEGER, '6', null, null, null, '0');
676         $table->add_field('someint7', XMLDB_TYPE_INTEGER, '7', null, null, null, '0');
677         $table->add_field('someint8', XMLDB_TYPE_INTEGER, '8', null, null, null, '0');
678         $table->add_field('someint9', XMLDB_TYPE_INTEGER, '9', null, null, null, '0');
679         $table->add_field('someint10', XMLDB_TYPE_INTEGER, '10', null, null, null, '0');
680         $table->add_field('someint18', XMLDB_TYPE_INTEGER, '18', null, null, null, '0');
681         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
682         $dbman->create_table($table);
684         $columns = $DB->get_columns($tablename);
685         $this->assertInternalType('array', $columns);
687         $fields = $table->getFields();
688         $this->assertCount(count($columns), $fields);
690         $field = $columns['id'];
691         $this->assertSame('R', $field->meta_type);
692         $this->assertTrue($field->auto_increment);
693         $this->assertTrue($field->unique);
695         $field = $columns['course'];
696         $this->assertSame('I', $field->meta_type);
697         $this->assertFalse($field->auto_increment);
698         $this->assertTrue($field->has_default);
699         $this->assertEquals(0, $field->default_value);
700         $this->assertTrue($field->not_null);
702         for ($i=1; $i<=10; $i++) {
703             $field = $columns['someint'.$i];
704             $this->assertSame('I', $field->meta_type);
705             $this->assertGreaterThanOrEqual($i, $field->max_length);
706         }
707         $field = $columns['someint18'];
708         $this->assertSame('I', $field->meta_type);
709         $this->assertGreaterThanOrEqual(18, $field->max_length);
711         $field = $columns['name'];
712         $this->assertSame('C', $field->meta_type);
713         $this->assertFalse($field->auto_increment);
714         $this->assertEquals(255, $field->max_length);
715         $this->assertTrue($field->has_default);
716         $this->assertSame('lala', $field->default_value);
717         $this->assertFalse($field->not_null);
719         $field = $columns['description'];
720         $this->assertSame('X', $field->meta_type);
721         $this->assertFalse($field->auto_increment);
722         $this->assertFalse($field->has_default);
723         $this->assertNull($field->default_value);
724         $this->assertFalse($field->not_null);
726         $field = $columns['enumfield'];
727         $this->assertSame('C', $field->meta_type);
728         $this->assertFalse($field->auto_increment);
729         $this->assertSame('test2', $field->default_value);
730         $this->assertTrue($field->not_null);
732         $field = $columns['onenum'];
733         $this->assertSame('N', $field->meta_type);
734         $this->assertFalse($field->auto_increment);
735         $this->assertEquals(10, $field->max_length);
736         $this->assertEquals(2, $field->scale);
737         $this->assertTrue($field->has_default);
738         $this->assertEquals(200.0, $field->default_value);
739         $this->assertFalse($field->not_null);
741         $field = $columns['onefloat'];
742         $this->assertSame('N', $field->meta_type);
743         $this->assertFalse($field->auto_increment);
744         $this->assertTrue($field->has_default);
745         $this->assertEquals(300.0, $field->default_value);
746         $this->assertFalse($field->not_null);
748         $field = $columns['anotherfloat'];
749         $this->assertSame('N', $field->meta_type);
750         $this->assertFalse($field->auto_increment);
751         $this->assertTrue($field->has_default);
752         $this->assertEquals(400.0, $field->default_value);
753         $this->assertFalse($field->not_null);
755         // Test negative defaults in numerical columns.
756         $field = $columns['negativedfltint'];
757         $this->assertTrue($field->has_default);
758         $this->assertEquals(-1, $field->default_value);
760         $field = $columns['negativedfltnumber'];
761         $this->assertTrue($field->has_default);
762         $this->assertEquals(-2, $field->default_value);
764         $field = $columns['negativedfltfloat'];
765         $this->assertTrue($field->has_default);
766         $this->assertEquals(-3, $field->default_value);
768         for ($i = 0; $i < count($columns); $i++) {
769             if ($i == 0) {
770                 $next_column = reset($columns);
771                 $next_field  = reset($fields);
772             } else {
773                 $next_column = next($columns);
774                 $next_field  = next($fields);
775             }
777             $this->assertEquals($next_column->name, $next_field->getName());
778         }
780         // Test get_columns for non-existing table returns empty array. MDL-30147.
781         $columns = $DB->get_columns('xxxx');
782         $this->assertEquals(array(), $columns);
784         // Create something similar to "context_temp" with id column without sequence.
785         $dbman->drop_table($table);
786         $table = $this->get_test_table();
787         $tablename = $table->getName();
788         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
789         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
790         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
791         $dbman->create_table($table);
793         $columns = $DB->get_columns($tablename);
794         $this->assertFalse($columns['id']->auto_increment);
795     }
797     public function test_get_manager() {
798         $DB = $this->tdb;
799         $dbman = $this->tdb->get_manager();
801         $this->assertInstanceOf('database_manager', $dbman);
802     }
804     public function test_setup_is_unicodedb() {
805         $DB = $this->tdb;
806         $this->assertTrue($DB->setup_is_unicodedb());
807     }
809     public function test_set_debug() { // Tests get_debug() too.
810         $DB = $this->tdb;
811         $dbman = $this->tdb->get_manager();
813         $table = $this->get_test_table();
814         $tablename = $table->getName();
816         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
817         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
818         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
819         $dbman->create_table($table);
821         $sql = "SELECT * FROM {{$tablename}}";
823         $prevdebug = $DB->get_debug();
825         ob_start();
826         $DB->set_debug(true);
827         $this->assertTrue($DB->get_debug());
828         $DB->execute($sql);
829         $DB->set_debug(false);
830         $this->assertFalse($DB->get_debug());
831         $debuginfo = ob_get_contents();
832         ob_end_clean();
833         $this->assertFalse($debuginfo === '');
835         ob_start();
836         $DB->execute($sql);
837         $debuginfo = ob_get_contents();
838         ob_end_clean();
839         $this->assertTrue($debuginfo === '');
841         $DB->set_debug($prevdebug);
842     }
844     public function test_execute() {
845         $DB = $this->tdb;
846         $dbman = $this->tdb->get_manager();
848         $table1 = $this->get_test_table('1');
849         $tablename1 = $table1->getName();
850         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
851         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
852         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
853         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
854         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
855         $dbman->create_table($table1);
857         $table2 = $this->get_test_table('2');
858         $tablename2 = $table2->getName();
859         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
860         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
861         $table2->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
862         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
863         $dbman->create_table($table2);
865         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
866         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
867         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
868         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
870         // Select results are ignored.
871         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
872         $this->assertTrue($DB->execute($sql, array('course'=>3)));
874         // Throw exception on error.
875         $sql = "XXUPDATE SET XSSD";
876         try {
877             $DB->execute($sql);
878             $this->fail("Expecting an exception, none occurred");
879         } catch (moodle_exception $e) {
880             $this->assertInstanceOf('dml_exception', $e);
881         }
883         // Update records.
884         $sql = "UPDATE {{$tablename1}}
885                    SET course = 6
886                  WHERE course = ?";
887         $this->assertTrue($DB->execute($sql, array('3')));
888         $this->assertEquals(2, $DB->count_records($tablename1, array('course' => 6)));
890         // Update records with subquery condition.
891         // Confirm that the option not using table aliases is cross-db.
892         $sql = "UPDATE {{$tablename1}}
893                    SET course = 0
894                  WHERE NOT EXISTS (
895                            SELECT course
896                              FROM {{$tablename2}} tbl2
897                             WHERE tbl2.course = {{$tablename1}}.course
898                               AND 1 = 0)"; // Really we don't update anything, but verify the syntax is allowed.
899         $this->assertTrue($DB->execute($sql));
901         // Insert from one into second table.
902         $sql = "INSERT INTO {{$tablename2}} (course)
904                 SELECT course
905                   FROM {{$tablename1}}";
906         $this->assertTrue($DB->execute($sql));
907         $this->assertEquals(4, $DB->count_records($tablename2));
909         // Insert a TEXT with raw SQL, binding TEXT params.
910         $course = 9999;
911         $onetext = file_get_contents(__DIR__ . '/fixtures/clob.txt');
912         $sql = "INSERT INTO {{$tablename2}} (course, onetext)
913                 VALUES (:course, :onetext)";
914         $DB->execute($sql, array('course' => $course, 'onetext' => $onetext));
915         $records = $DB->get_records($tablename2, array('course' => $course));
916         $this->assertCount(1, $records);
917         $record = reset($records);
918         $this->assertSame($onetext, $record->onetext);
920         // Update a TEXT with raw SQL, binding TEXT params.
921         $newcourse = 10000;
922         $newonetext = file_get_contents(__DIR__ . '/fixtures/clob.txt') . '- updated';
923         $sql = "UPDATE {{$tablename2}} SET course = :newcourse, onetext = :newonetext
924                 WHERE course = :oldcourse";
925         $DB->execute($sql, array('oldcourse' => $course, 'newcourse' => $newcourse, 'newonetext' => $newonetext));
926         $records = $DB->get_records($tablename2, array('course' => $course));
927         $this->assertCount(0, $records);
928         $records = $DB->get_records($tablename2, array('course' => $newcourse));
929         $this->assertCount(1, $records);
930         $record = reset($records);
931         $this->assertSame($newonetext, $record->onetext);
932     }
934     public function test_get_recordset() {
935         $DB = $this->tdb;
936         $dbman = $DB->get_manager();
938         $table = $this->get_test_table();
939         $tablename = $table->getName();
941         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
942         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
943         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
944         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
945         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
946         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
947         $dbman->create_table($table);
949         $data = array(array('course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
950             array('course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
951             array('course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
953         foreach ($data as $key => $record) {
954             $data[$key]['id'] = $DB->insert_record($tablename, $record);
955         }
957         // Standard recordset iteration.
958         $rs = $DB->get_recordset($tablename);
959         $this->assertInstanceOf('moodle_recordset', $rs);
960         reset($data);
961         foreach ($rs as $record) {
962             $data_record = current($data);
963             foreach ($record as $k => $v) {
964                 $this->assertEquals($data_record[$k], $v);
965             }
966             next($data);
967         }
968         $rs->close();
970         // Iterator style usage.
971         $rs = $DB->get_recordset($tablename);
972         $this->assertInstanceOf('moodle_recordset', $rs);
973         reset($data);
974         while ($rs->valid()) {
975             $record = $rs->current();
976             $data_record = current($data);
977             foreach ($record as $k => $v) {
978                 $this->assertEquals($data_record[$k], $v);
979             }
980             next($data);
981             $rs->next();
982         }
983         $rs->close();
985         // Make sure rewind is ignored.
986         $rs = $DB->get_recordset($tablename);
987         $this->assertInstanceOf('moodle_recordset', $rs);
988         reset($data);
989         $i = 0;
990         foreach ($rs as $record) {
991             $i++;
992             $rs->rewind();
993             if ($i > 10) {
994                 $this->fail('revind not ignored in recordsets');
995                 break;
996             }
997             $data_record = current($data);
998             foreach ($record as $k => $v) {
999                 $this->assertEquals($data_record[$k], $v);
1000             }
1001             next($data);
1002         }
1003         $rs->close();
1005         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
1006         $conditions = array('onetext' => '1');
1007         try {
1008             $rs = $DB->get_recordset($tablename, $conditions);
1009             $this->fail('An Exception is missing, expected due to equating of text fields');
1010         } catch (moodle_exception $e) {
1011             $this->assertInstanceOf('dml_exception', $e);
1012             $this->assertSame('textconditionsnotallowed', $e->errorcode);
1013         }
1015         // Test nested iteration.
1016         $rs1 = $DB->get_recordset($tablename);
1017         $i = 0;
1018         foreach ($rs1 as $record1) {
1019             $rs2 = $DB->get_recordset($tablename);
1020             $i++;
1021             $j = 0;
1022             foreach ($rs2 as $record2) {
1023                 $j++;
1024             }
1025             $rs2->close();
1026             $this->assertCount($j, $data);
1027         }
1028         $rs1->close();
1029         $this->assertCount($i, $data);
1031         // Notes:
1032         //  * limits are tested in test_get_recordset_sql()
1033         //  * where_clause() is used internally and is tested in test_get_records()
1034     }
1036     public function test_get_recordset_static() {
1037         $DB = $this->tdb;
1038         $dbman = $DB->get_manager();
1040         $table = $this->get_test_table();
1041         $tablename = $table->getName();
1043         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1044         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1045         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1046         $dbman->create_table($table);
1048         $DB->insert_record($tablename, array('course' => 1));
1049         $DB->insert_record($tablename, array('course' => 2));
1050         $DB->insert_record($tablename, array('course' => 3));
1051         $DB->insert_record($tablename, array('course' => 4));
1053         $rs = $DB->get_recordset($tablename, array(), 'id');
1055         $DB->set_field($tablename, 'course', 666, array('course'=>1));
1056         $DB->delete_records($tablename, array('course'=>2));
1058         $i = 0;
1059         foreach ($rs as $record) {
1060             $i++;
1061             $this->assertEquals($i, $record->course);
1062         }
1063         $rs->close();
1064         $this->assertEquals(4, $i);
1066         // Now repeat with limits because it may use different code.
1067         $DB->delete_records($tablename, array());
1069         $DB->insert_record($tablename, array('course' => 1));
1070         $DB->insert_record($tablename, array('course' => 2));
1071         $DB->insert_record($tablename, array('course' => 3));
1072         $DB->insert_record($tablename, array('course' => 4));
1074         $rs = $DB->get_recordset($tablename, array(), 'id', '*', 0, 3);
1076         $DB->set_field($tablename, 'course', 666, array('course'=>1));
1077         $DB->delete_records($tablename, array('course'=>2));
1079         $i = 0;
1080         foreach ($rs as $record) {
1081             $i++;
1082             $this->assertEquals($i, $record->course);
1083         }
1084         $rs->close();
1085         $this->assertEquals(3, $i);
1086     }
1088     public function test_get_recordset_iterator_keys() {
1089         $DB = $this->tdb;
1090         $dbman = $DB->get_manager();
1092         $table = $this->get_test_table();
1093         $tablename = $table->getName();
1095         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1096         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1097         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
1098         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1099         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1100         $dbman->create_table($table);
1102         $data = array(array('course' => 3, 'name' => 'record1'),
1103             array('course' => 3, 'name' => 'record2'),
1104             array('course' => 5, 'name' => 'record3'));
1105         foreach ($data as $key => $record) {
1106             $data[$key]['id'] = $DB->insert_record($tablename, $record);
1107         }
1109         // Test repeated numeric keys are returned ok.
1110         $rs = $DB->get_recordset($tablename, null, null, 'course, name, id');
1112         reset($data);
1113         $count = 0;
1114         foreach ($rs as $key => $record) {
1115             $data_record = current($data);
1116             $this->assertEquals($data_record['course'], $key);
1117             next($data);
1118             $count++;
1119         }
1120         $rs->close();
1121         $this->assertEquals(3, $count);
1123         // Test string keys are returned ok.
1124         $rs = $DB->get_recordset($tablename, null, null, 'name, course, id');
1126         reset($data);
1127         $count = 0;
1128         foreach ($rs as $key => $record) {
1129             $data_record = current($data);
1130             $this->assertEquals($data_record['name'], $key);
1131             next($data);
1132             $count++;
1133         }
1134         $rs->close();
1135         $this->assertEquals(3, $count);
1137         // Test numeric not starting in 1 keys are returned ok.
1138         $rs = $DB->get_recordset($tablename, null, 'id DESC', 'id, course, name');
1140         $data = array_reverse($data);
1141         reset($data);
1142         $count = 0;
1143         foreach ($rs as $key => $record) {
1144             $data_record = current($data);
1145             $this->assertEquals($data_record['id'], $key);
1146             next($data);
1147             $count++;
1148         }
1149         $rs->close();
1150         $this->assertEquals(3, $count);
1151     }
1153     public function test_get_recordset_list() {
1154         $DB = $this->tdb;
1155         $dbman = $DB->get_manager();
1157         $table = $this->get_test_table();
1158         $tablename = $table->getName();
1160         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1161         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, null, null, '0');
1162         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1163         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1164         $dbman->create_table($table);
1166         $DB->insert_record($tablename, array('course' => 3));
1167         $DB->insert_record($tablename, array('course' => 3));
1168         $DB->insert_record($tablename, array('course' => 5));
1169         $DB->insert_record($tablename, array('course' => 2));
1170         $DB->insert_record($tablename, array('course' => null));
1171         $DB->insert_record($tablename, array('course' => 1));
1172         $DB->insert_record($tablename, array('course' => 0));
1174         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
1175         $counter = 0;
1176         foreach ($rs as $record) {
1177             $counter++;
1178         }
1179         $this->assertEquals(3, $counter);
1180         $rs->close();
1182         $rs = $DB->get_recordset_list($tablename, 'course', array(3));
1183         $counter = 0;
1184         foreach ($rs as $record) {
1185             $counter++;
1186         }
1187         $this->assertEquals(2, $counter);
1188         $rs->close();
1190         $rs = $DB->get_recordset_list($tablename, 'course', array(null));
1191         $counter = 0;
1192         foreach ($rs as $record) {
1193             $counter++;
1194         }
1195         $this->assertEquals(1, $counter);
1196         $rs->close();
1198         $rs = $DB->get_recordset_list($tablename, 'course', array(6, null));
1199         $counter = 0;
1200         foreach ($rs as $record) {
1201             $counter++;
1202         }
1203         $this->assertEquals(1, $counter);
1204         $rs->close();
1206         $rs = $DB->get_recordset_list($tablename, 'course', array(null, 5, 5, 5));
1207         $counter = 0;
1208         foreach ($rs as $record) {
1209             $counter++;
1210         }
1211         $this->assertEquals(2, $counter);
1212         $rs->close();
1214         $rs = $DB->get_recordset_list($tablename, 'course', array(true));
1215         $counter = 0;
1216         foreach ($rs as $record) {
1217             $counter++;
1218         }
1219         $this->assertEquals(1, $counter);
1220         $rs->close();
1222         $rs = $DB->get_recordset_list($tablename, 'course', array(false));
1223         $counter = 0;
1224         foreach ($rs as $record) {
1225             $counter++;
1226         }
1227         $this->assertEquals(1, $counter);
1228         $rs->close();
1230         $rs = $DB->get_recordset_list($tablename, 'course', array()); // Must return 0 rows without conditions. MDL-17645.
1232         $counter = 0;
1233         foreach ($rs as $record) {
1234             $counter++;
1235         }
1236         $rs->close();
1237         $this->assertEquals(0, $counter);
1239         // Notes:
1240         //  * limits are tested in test_get_recordset_sql()
1241         //  * where_clause() is used internally and is tested in test_get_records()
1242     }
1244     public function test_get_recordset_select() {
1245         $DB = $this->tdb;
1246         $dbman = $DB->get_manager();
1248         $table = $this->get_test_table();
1249         $tablename = $table->getName();
1251         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1252         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1253         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1254         $dbman->create_table($table);
1256         $DB->insert_record($tablename, array('course' => 3));
1257         $DB->insert_record($tablename, array('course' => 3));
1258         $DB->insert_record($tablename, array('course' => 5));
1259         $DB->insert_record($tablename, array('course' => 2));
1261         $rs = $DB->get_recordset_select($tablename, '');
1262         $counter = 0;
1263         foreach ($rs as $record) {
1264             $counter++;
1265         }
1266         $rs->close();
1267         $this->assertEquals(4, $counter);
1269         $this->assertNotEmpty($rs = $DB->get_recordset_select($tablename, 'course = 3'));
1270         $counter = 0;
1271         foreach ($rs as $record) {
1272             $counter++;
1273         }
1274         $rs->close();
1275         $this->assertEquals(2, $counter);
1277         // Notes:
1278         //  * limits are tested in test_get_recordset_sql()
1279     }
1281     public function test_get_recordset_sql() {
1282         $DB = $this->tdb;
1283         $dbman = $DB->get_manager();
1285         $table = $this->get_test_table();
1286         $tablename = $table->getName();
1288         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1289         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1290         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1291         $dbman->create_table($table);
1293         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1294         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1295         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1296         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1297         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1298         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1299         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1301         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1302         $counter = 0;
1303         foreach ($rs as $record) {
1304             $counter++;
1305         }
1306         $rs->close();
1307         $this->assertEquals(2, $counter);
1309         // Limits - only need to test this case, the rest have been tested by test_get_records_sql()
1310         // only limitfrom = skips that number of records.
1311         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1312         $records = array();
1313         foreach ($rs as $key => $record) {
1314             $records[$key] = $record;
1315         }
1316         $rs->close();
1317         $this->assertCount(5, $records);
1318         $this->assertEquals($inskey3, reset($records)->id);
1319         $this->assertEquals($inskey7, end($records)->id);
1321         // Note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here.
1322     }
1324     public function test_export_table_recordset() {
1325         $DB = $this->tdb;
1326         $dbman = $DB->get_manager();
1328         $table = $this->get_test_table();
1329         $tablename = $table->getName();
1331         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1332         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1333         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1334         $dbman->create_table($table);
1336         $ids = array();
1337         $ids[] = $DB->insert_record($tablename, array('course' => 3));
1338         $ids[] = $DB->insert_record($tablename, array('course' => 5));
1339         $ids[] = $DB->insert_record($tablename, array('course' => 4));
1340         $ids[] = $DB->insert_record($tablename, array('course' => 3));
1341         $ids[] = $DB->insert_record($tablename, array('course' => 2));
1342         $ids[] = $DB->insert_record($tablename, array('course' => 1));
1343         $ids[] = $DB->insert_record($tablename, array('course' => 0));
1345         $rs = $DB->export_table_recordset($tablename);
1346         $rids = array();
1347         foreach ($rs as $record) {
1348             $rids[] = $record->id;
1349         }
1350         $rs->close();
1351         $this->assertEquals($ids, $rids, '', 0, 0, true);
1352     }
1354     public function test_get_records() {
1355         $DB = $this->tdb;
1356         $dbman = $DB->get_manager();
1358         $table = $this->get_test_table();
1359         $tablename = $table->getName();
1361         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1362         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1363         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1364         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1365         $dbman->create_table($table);
1367         $DB->insert_record($tablename, array('course' => 3));
1368         $DB->insert_record($tablename, array('course' => 3));
1369         $DB->insert_record($tablename, array('course' => 5));
1370         $DB->insert_record($tablename, array('course' => 2));
1372         // All records.
1373         $records = $DB->get_records($tablename);
1374         $this->assertCount(4, $records);
1375         $this->assertEquals(3, $records[1]->course);
1376         $this->assertEquals(3, $records[2]->course);
1377         $this->assertEquals(5, $records[3]->course);
1378         $this->assertEquals(2, $records[4]->course);
1380         // Records matching certain conditions.
1381         $records = $DB->get_records($tablename, array('course' => 3));
1382         $this->assertCount(2, $records);
1383         $this->assertEquals(3, $records[1]->course);
1384         $this->assertEquals(3, $records[2]->course);
1386         // All records sorted by course.
1387         $records = $DB->get_records($tablename, null, 'course');
1388         $this->assertCount(4, $records);
1389         $current_record = reset($records);
1390         $this->assertEquals(4, $current_record->id);
1391         $current_record = next($records);
1392         $this->assertEquals(1, $current_record->id);
1393         $current_record = next($records);
1394         $this->assertEquals(2, $current_record->id);
1395         $current_record = next($records);
1396         $this->assertEquals(3, $current_record->id);
1398         // All records, but get only one field.
1399         $records = $DB->get_records($tablename, null, '', 'id');
1400         $this->assertFalse(isset($records[1]->course));
1401         $this->assertTrue(isset($records[1]->id));
1402         $this->assertCount(4, $records);
1404         // Booleans into params.
1405         $records = $DB->get_records($tablename, array('course' => true));
1406         $this->assertCount(0, $records);
1407         $records = $DB->get_records($tablename, array('course' => false));
1408         $this->assertCount(0, $records);
1410         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
1411         $conditions = array('onetext' => '1');
1412         try {
1413             $records = $DB->get_records($tablename, $conditions);
1414             if (debugging()) {
1415                 // Only in debug mode - hopefully all devs test code in debug mode...
1416                 $this->fail('An Exception is missing, expected due to equating of text fields');
1417             }
1418         } catch (moodle_exception $e) {
1419             $this->assertInstanceOf('dml_exception', $e);
1420             $this->assertSame('textconditionsnotallowed', $e->errorcode);
1421         }
1423         // Test get_records passing non-existing table.
1424         // with params.
1425         try {
1426             $records = $DB->get_records('xxxx', array('id' => 0));
1427             $this->fail('An Exception is missing, expected due to query against non-existing table');
1428         } catch (moodle_exception $e) {
1429             $this->assertInstanceOf('dml_exception', $e);
1430             if (debugging()) {
1431                 // Information for developers only, normal users get general error message.
1432                 $this->assertSame('ddltablenotexist', $e->errorcode);
1433             }
1434         }
1436         try {
1437             $records = $DB->get_records('xxxx', array('id' => '1'));
1438             $this->fail('An Exception is missing, expected due to query against non-existing table');
1439         } catch (moodle_exception $e) {
1440             $this->assertInstanceOf('dml_exception', $e);
1441             if (debugging()) {
1442                 // Information for developers only, normal users get general error message.
1443                 $this->assertSame('ddltablenotexist', $e->errorcode);
1444             }
1445         }
1447         // Test get_records passing non-existing column.
1448         try {
1449             $records = $DB->get_records($tablename, array('xxxx' => 0));
1450             $this->fail('An Exception is missing, expected due to query against non-existing column');
1451         } catch (moodle_exception $e) {
1452             $this->assertInstanceOf('dml_exception', $e);
1453             if (debugging()) {
1454                 // Information for developers only, normal users get general error message.
1455                 $this->assertSame('ddlfieldnotexist', $e->errorcode);
1456             }
1457         }
1459         // Note: delegate limits testing to test_get_records_sql().
1460     }
1462     public function test_get_records_list() {
1463         $DB = $this->tdb;
1464         $dbman = $DB->get_manager();
1466         $table = $this->get_test_table();
1467         $tablename = $table->getName();
1469         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1470         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1471         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1472         $dbman->create_table($table);
1474         $DB->insert_record($tablename, array('course' => 3));
1475         $DB->insert_record($tablename, array('course' => 3));
1476         $DB->insert_record($tablename, array('course' => 5));
1477         $DB->insert_record($tablename, array('course' => 2));
1479         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
1480         $this->assertInternalType('array', $records);
1481         $this->assertCount(3, $records);
1482         $this->assertEquals(1, reset($records)->id);
1483         $this->assertEquals(2, next($records)->id);
1484         $this->assertEquals(4, next($records)->id);
1486         $this->assertSame(array(), $records = $DB->get_records_list($tablename, 'course', array())); // Must return 0 rows without conditions. MDL-17645.
1487         $this->assertCount(0, $records);
1489         // Note: delegate limits testing to test_get_records_sql().
1490     }
1492     public function test_get_records_sql() {
1493         $DB = $this->tdb;
1494         $dbman = $DB->get_manager();
1496         $table = $this->get_test_table();
1497         $tablename = $table->getName();
1499         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1500         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1501         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1502         $dbman->create_table($table);
1504         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1505         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1506         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1507         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1508         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1509         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1510         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1512         $table2 = $this->get_test_table("2");
1513         $tablename2 = $table2->getName();
1514         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1515         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1516         $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
1517         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1518         $dbman->create_table($table2);
1520         $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
1521         $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
1522         $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
1523         $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
1525         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1526         $this->assertCount(2, $records);
1527         $this->assertEquals($inskey1, reset($records)->id);
1528         $this->assertEquals($inskey4, next($records)->id);
1530         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test.
1531         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1532         $this->assertDebuggingCalled();
1533         $this->assertCount(6, $records);
1534         set_debugging(DEBUG_MINIMAL);
1535         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1536         $this->assertDebuggingNotCalled();
1537         $this->assertCount(6, $records);
1538         set_debugging(DEBUG_DEVELOPER);
1540         // Negative limits = no limits.
1541         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1542         $this->assertCount(7, $records);
1544         // Zero limits = no limits.
1545         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1546         $this->assertCount(7, $records);
1548         // Only limitfrom = skips that number of records.
1549         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1550         $this->assertCount(5, $records);
1551         $this->assertEquals($inskey3, reset($records)->id);
1552         $this->assertEquals($inskey7, end($records)->id);
1554         // Only limitnum = fetches that number of records.
1555         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1556         $this->assertCount(3, $records);
1557         $this->assertEquals($inskey1, reset($records)->id);
1558         $this->assertEquals($inskey3, end($records)->id);
1560         // Both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones.
1561         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1562         $this->assertCount(2, $records);
1563         $this->assertEquals($inskey4, reset($records)->id);
1564         $this->assertEquals($inskey5, end($records)->id);
1566         // Both limitfrom and limitnum in query having subqueris.
1567         // Note the subquery skips records with course = 0 and 3.
1568         $sql = "SELECT * FROM {{$tablename}}
1569                  WHERE course NOT IN (
1570                      SELECT course FROM {{$tablename}}
1571                       WHERE course IN (0, 3))
1572                 ORDER BY course";
1573         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2.
1574         $this->assertCount(2, $records);
1575         $this->assertEquals($inskey6, reset($records)->id);
1576         $this->assertEquals($inskey5, end($records)->id);
1577         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2.
1578         $this->assertCount(2, $records);
1579         $this->assertEquals($inskey3, reset($records)->id);
1580         $this->assertEquals($inskey2, end($records)->id);
1582         // Test 2 tables with aliases and limits with order bys.
1583         $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
1584                   FROM {{$tablename}} t1, {{$tablename2}} t2
1585                  WHERE t2.course=t1.course
1586               ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
1587         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5.
1588         $this->assertCount(2, $records);
1589         $this->assertSame('5', end($records)->cid);
1590         $this->assertSame('4', reset($records)->cid);
1592         // Test 2 tables with aliases and limits with the highest INT limit works.
1593         $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}.
1594         $this->assertCount(2, $records);
1595         $this->assertSame('5', end($records)->cid);
1596         $this->assertSame('4', reset($records)->cid);
1598         // Test 2 tables with aliases and limits with order bys (limit which is highest INT number).
1599         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses.
1600         $this->assertCount(0, $records);
1602         // Test 2 tables with aliases and limits with order bys (limit which s highest INT number).
1603         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses.
1604         $this->assertCount(0, $records);
1606         // TODO: Test limits in queries having DISTINCT clauses.
1608         // Note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here.
1609     }
1611     public function test_get_records_menu() {
1612         $DB = $this->tdb;
1613         $dbman = $DB->get_manager();
1615         $table = $this->get_test_table();
1616         $tablename = $table->getName();
1618         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1619         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1620         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1621         $dbman->create_table($table);
1623         $DB->insert_record($tablename, array('course' => 3));
1624         $DB->insert_record($tablename, array('course' => 3));
1625         $DB->insert_record($tablename, array('course' => 5));
1626         $DB->insert_record($tablename, array('course' => 2));
1628         $records = $DB->get_records_menu($tablename, array('course' => 3));
1629         $this->assertInternalType('array', $records);
1630         $this->assertCount(2, $records);
1631         $this->assertNotEmpty($records[1]);
1632         $this->assertNotEmpty($records[2]);
1633         $this->assertEquals(3, $records[1]);
1634         $this->assertEquals(3, $records[2]);
1636         // Note: delegate limits testing to test_get_records_sql().
1637     }
1639     public function test_get_records_select_menu() {
1640         $DB = $this->tdb;
1641         $dbman = $DB->get_manager();
1643         $table = $this->get_test_table();
1644         $tablename = $table->getName();
1646         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1647         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1648         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1649         $dbman->create_table($table);
1651         $DB->insert_record($tablename, array('course' => 3));
1652         $DB->insert_record($tablename, array('course' => 2));
1653         $DB->insert_record($tablename, array('course' => 3));
1654         $DB->insert_record($tablename, array('course' => 5));
1656         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1657         $this->assertInternalType('array', $records);
1659         $this->assertCount(3, $records);
1660         $this->assertArrayHasKey(1, $records);
1661         $this->assertArrayNotHasKey(2, $records);
1662         $this->assertArrayHasKey(3, $records);
1663         $this->assertArrayHasKey(4, $records);
1664         $this->assertSame('3', $records[1]);
1665         $this->assertSame('3', $records[3]);
1666         $this->assertSame('5', $records[4]);
1668         // Note: delegate limits testing to test_get_records_sql().
1669     }
1671     public function test_get_records_sql_menu() {
1672         $DB = $this->tdb;
1673         $dbman = $DB->get_manager();
1675         $table = $this->get_test_table();
1676         $tablename = $table->getName();
1678         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1679         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1680         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1681         $dbman->create_table($table);
1683         $DB->insert_record($tablename, array('course' => 3));
1684         $DB->insert_record($tablename, array('course' => 2));
1685         $DB->insert_record($tablename, array('course' => 3));
1686         $DB->insert_record($tablename, array('course' => 5));
1688         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1689         $this->assertInternalType('array', $records);
1691         $this->assertCount(3, $records);
1692         $this->assertArrayHasKey(1, $records);
1693         $this->assertArrayNotHasKey(2, $records);
1694         $this->assertArrayHasKey(3, $records);
1695         $this->assertArrayHasKey(4, $records);
1696         $this->assertSame('3', $records[1]);
1697         $this->assertSame('3', $records[3]);
1698         $this->assertSame('5', $records[4]);
1700         // Note: delegate limits testing to test_get_records_sql().
1701     }
1703     public function test_get_record() {
1704         $DB = $this->tdb;
1705         $dbman = $DB->get_manager();
1707         $table = $this->get_test_table();
1708         $tablename = $table->getName();
1710         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1711         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1712         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1713         $dbman->create_table($table);
1715         $DB->insert_record($tablename, array('course' => 3));
1716         $DB->insert_record($tablename, array('course' => 2));
1718         $record = $DB->get_record($tablename, array('id' => 2));
1719         $this->assertInstanceOf('stdClass', $record);
1721         $this->assertEquals(2, $record->course);
1722         $this->assertEquals(2, $record->id);
1723     }
1726     public function test_get_record_select() {
1727         $DB = $this->tdb;
1728         $dbman = $DB->get_manager();
1730         $table = $this->get_test_table();
1731         $tablename = $table->getName();
1733         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1734         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1735         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1736         $dbman->create_table($table);
1738         $DB->insert_record($tablename, array('course' => 3));
1739         $DB->insert_record($tablename, array('course' => 2));
1741         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1742         $this->assertInstanceOf('stdClass', $record);
1744         $this->assertEquals(2, $record->course);
1746         // Note: delegates limit testing to test_get_records_sql().
1747     }
1749     public function test_get_record_sql() {
1750         $DB = $this->tdb;
1751         $dbman = $DB->get_manager();
1753         $table = $this->get_test_table();
1754         $tablename = $table->getName();
1756         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1757         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1758         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1759         $dbman->create_table($table);
1761         $DB->insert_record($tablename, array('course' => 3));
1762         $DB->insert_record($tablename, array('course' => 2));
1764         // Standard use.
1765         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1766         $this->assertInstanceOf('stdClass', $record);
1767         $this->assertEquals(2, $record->course);
1768         $this->assertEquals(2, $record->id);
1770         // Backwards compatibility with $ignoremultiple.
1771         $this->assertFalse((bool)IGNORE_MISSING);
1772         $this->assertTrue((bool)IGNORE_MULTIPLE);
1774         // Record not found - ignore.
1775         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1776         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1778         // Record not found error.
1779         try {
1780             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1781             $this->fail("Exception expected");
1782         } catch (dml_missing_record_exception $e) {
1783             $this->assertTrue(true);
1784         }
1786         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1787         $this->assertDebuggingCalled();
1788         set_debugging(DEBUG_MINIMAL);
1789         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1790         $this->assertDebuggingNotCalled();
1791         set_debugging(DEBUG_DEVELOPER);
1793         // Multiple matches ignored.
1794         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1796         // Multiple found error.
1797         try {
1798             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1799             $this->fail("Exception expected");
1800         } catch (dml_multiple_records_exception $e) {
1801             $this->assertTrue(true);
1802         }
1803     }
1805     public function test_get_field() {
1806         $DB = $this->tdb;
1807         $dbman = $DB->get_manager();
1809         $table = $this->get_test_table();
1810         $tablename = $table->getName();
1812         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1813         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1814         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1815         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1816         $dbman->create_table($table);
1818         $id1 = $DB->insert_record($tablename, array('course' => 3));
1819         $DB->insert_record($tablename, array('course' => 5));
1820         $DB->insert_record($tablename, array('course' => 5));
1822         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1823         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1825         $this->assertFalse($DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1826         try {
1827             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1828             $this->fail('Exception expected due to missing record');
1829         } catch (dml_exception $ex) {
1830             $this->assertTrue(true);
1831         }
1833         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1834         $this->assertDebuggingNotCalled();
1836         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1837         $this->assertDebuggingCalled();
1839         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
1840         $conditions = array('onetext' => '1');
1841         try {
1842             $DB->get_field($tablename, 'course', $conditions);
1843             if (debugging()) {
1844                 // Only in debug mode - hopefully all devs test code in debug mode...
1845                 $this->fail('An Exception is missing, expected due to equating of text fields');
1846             }
1847         } catch (moodle_exception $e) {
1848             $this->assertInstanceOf('dml_exception', $e);
1849             $this->assertSame('textconditionsnotallowed', $e->errorcode);
1850         }
1851     }
1853     public function test_get_field_select() {
1854         $DB = $this->tdb;
1855         $dbman = $DB->get_manager();
1857         $table = $this->get_test_table();
1858         $tablename = $table->getName();
1860         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1861         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1862         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1863         $dbman->create_table($table);
1865         $DB->insert_record($tablename, array('course' => 3));
1867         $this->assertEquals(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1868     }
1870     public function test_get_field_sql() {
1871         $DB = $this->tdb;
1872         $dbman = $DB->get_manager();
1874         $table = $this->get_test_table();
1875         $tablename = $table->getName();
1877         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1878         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1879         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1880         $dbman->create_table($table);
1882         $DB->insert_record($tablename, array('course' => 3));
1884         $this->assertEquals(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1885     }
1887     public function test_get_fieldset_select() {
1888         $DB = $this->tdb;
1889         $dbman = $DB->get_manager();
1891         $table = $this->get_test_table();
1892         $tablename = $table->getName();
1894         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1895         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1896         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1897         $dbman->create_table($table);
1899         $DB->insert_record($tablename, array('course' => 1));
1900         $DB->insert_record($tablename, array('course' => 3));
1901         $DB->insert_record($tablename, array('course' => 2));
1902         $DB->insert_record($tablename, array('course' => 6));
1904         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1905         $this->assertInternalType('array', $fieldset);
1907         $this->assertCount(3, $fieldset);
1908         $this->assertEquals(3, $fieldset[0]);
1909         $this->assertEquals(2, $fieldset[1]);
1910         $this->assertEquals(6, $fieldset[2]);
1911     }
1913     public function test_get_fieldset_sql() {
1914         $DB = $this->tdb;
1915         $dbman = $DB->get_manager();
1917         $table = $this->get_test_table();
1918         $tablename = $table->getName();
1920         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1921         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1922         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1923         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1924         $dbman->create_table($table);
1926         $binarydata = '\\'.chr(241);
1928         $DB->insert_record($tablename, array('course' => 1, 'onebinary' => $binarydata));
1929         $DB->insert_record($tablename, array('course' => 3, 'onebinary' => $binarydata));
1930         $DB->insert_record($tablename, array('course' => 2, 'onebinary' => $binarydata));
1931         $DB->insert_record($tablename, array('course' => 6, 'onebinary' => $binarydata));
1933         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1934         $this->assertInternalType('array', $fieldset);
1936         $this->assertCount(3, $fieldset);
1937         $this->assertEquals(2, $fieldset[0]);
1938         $this->assertEquals(3, $fieldset[1]);
1939         $this->assertEquals(4, $fieldset[2]);
1941         $fieldset = $DB->get_fieldset_sql("SELECT onebinary FROM {{$tablename}} WHERE course > ?", array(1));
1942         $this->assertInternalType('array', $fieldset);
1944         $this->assertCount(3, $fieldset);
1945         $this->assertEquals($binarydata, $fieldset[0]);
1946         $this->assertEquals($binarydata, $fieldset[1]);
1947         $this->assertEquals($binarydata, $fieldset[2]);
1948     }
1950     public function test_insert_record_raw() {
1951         $DB = $this->tdb;
1952         $dbman = $DB->get_manager();
1954         $table = $this->get_test_table();
1955         $tablename = $table->getName();
1957         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1958         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1959         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1960         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1961         $dbman->create_table($table);
1963         $record = (object)array('course' => 1, 'onechar' => 'xx');
1964         $before = clone($record);
1965         $result = $DB->insert_record_raw($tablename, $record);
1966         $this->assertSame(1, $result);
1967         $this->assertEquals($record, $before);
1969         $record = $DB->get_record($tablename, array('course' => 1));
1970         $this->assertInstanceOf('stdClass', $record);
1971         $this->assertSame('xx', $record->onechar);
1973         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1974         $this->assertTrue($result);
1976         // Note: bulk not implemented yet.
1977         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1978         $record = $DB->get_record($tablename, array('course' => 3));
1979         $this->assertInstanceOf('stdClass', $record);
1980         $this->assertSame('zz', $record->onechar);
1982         // Custom sequence (id) - returnid is ignored.
1983         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1984         $this->assertTrue($result);
1985         $record = $DB->get_record($tablename, array('id' => 10));
1986         $this->assertInstanceOf('stdClass', $record);
1987         $this->assertSame('bb', $record->onechar);
1989         // Custom sequence - missing id error.
1990         try {
1991             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1992             $this->fail('Exception expected due to missing record');
1993         } catch (coding_exception $ex) {
1994             $this->assertTrue(true);
1995         }
1997         // Wrong column error.
1998         try {
1999             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
2000             $this->fail('Exception expected due to invalid column');
2001         } catch (dml_exception $ex) {
2002             $this->assertTrue(true);
2003         }
2005         // Create something similar to "context_temp" with id column without sequence.
2006         $dbman->drop_table($table);
2007         $table = $this->get_test_table();
2008         $tablename = $table->getName();
2009         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
2010         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2011         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2012         $dbman->create_table($table);
2014         $record = (object)array('id'=>5, 'course' => 1);
2015         $DB->insert_record_raw($tablename, $record, false, false, true);
2016         $record = $DB->get_record($tablename, array());
2017         $this->assertEquals(5, $record->id);
2018     }
2020     public function test_insert_record() {
2021         // All the information in this test is fetched from DB by get_recordset() so we
2022         // have such method properly tested against nulls, empties and friends...
2024         $DB = $this->tdb;
2025         $dbman = $DB->get_manager();
2027         $table = $this->get_test_table();
2028         $tablename = $table->getName();
2030         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2031         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2032         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
2033         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2034         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2035         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2036         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2037         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2038         $dbman->create_table($table);
2040         $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true));
2041         $record = $DB->get_record($tablename, array('course' => 1));
2042         $this->assertEquals(1, $record->id);
2043         $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied.
2044         $this->assertEquals(200, $record->onenum);
2045         $this->assertSame('onestring', $record->onechar);
2046         $this->assertNull($record->onetext);
2047         $this->assertNull($record->onebinary);
2049         // Without returning id, bulk not implemented.
2050         $result = $this->assertTrue($DB->insert_record($tablename, array('course' => 99), false, true));
2051         $record = $DB->get_record($tablename, array('course' => 99));
2052         $this->assertEquals(2, $record->id);
2053         $this->assertEquals(99, $record->course);
2055         // Check nulls are set properly for all types.
2056         $record = new stdClass();
2057         $record->oneint = null;
2058         $record->onenum = null;
2059         $record->onechar = null;
2060         $record->onetext = null;
2061         $record->onebinary = null;
2062         $recid = $DB->insert_record($tablename, $record);
2063         $record = $DB->get_record($tablename, array('id' => $recid));
2064         $this->assertEquals(0, $record->course);
2065         $this->assertNull($record->oneint);
2066         $this->assertNull($record->onenum);
2067         $this->assertNull($record->onechar);
2068         $this->assertNull($record->onetext);
2069         $this->assertNull($record->onebinary);
2071         // Check zeros are set properly for all types.
2072         $record = new stdClass();
2073         $record->oneint = 0;
2074         $record->onenum = 0;
2075         $recid = $DB->insert_record($tablename, $record);
2076         $record = $DB->get_record($tablename, array('id' => $recid));
2077         $this->assertEquals(0, $record->oneint);
2078         $this->assertEquals(0, $record->onenum);
2080         // Check booleans are set properly for all types.
2081         $record = new stdClass();
2082         $record->oneint = true; // Trues.
2083         $record->onenum = true;
2084         $record->onechar = true;
2085         $record->onetext = true;
2086         $recid = $DB->insert_record($tablename, $record);
2087         $record = $DB->get_record($tablename, array('id' => $recid));
2088         $this->assertEquals(1, $record->oneint);
2089         $this->assertEquals(1, $record->onenum);
2090         $this->assertEquals(1, $record->onechar);
2091         $this->assertEquals(1, $record->onetext);
2093         $record = new stdClass();
2094         $record->oneint = false; // Falses.
2095         $record->onenum = false;
2096         $record->onechar = false;
2097         $record->onetext = false;
2098         $recid = $DB->insert_record($tablename, $record);
2099         $record = $DB->get_record($tablename, array('id' => $recid));
2100         $this->assertEquals(0, $record->oneint);
2101         $this->assertEquals(0, $record->onenum);
2102         $this->assertEquals(0, $record->onechar);
2103         $this->assertEquals(0, $record->onetext);
2105         // Check string data causes exception in numeric types.
2106         $record = new stdClass();
2107         $record->oneint = 'onestring';
2108         $record->onenum = 0;
2109         try {
2110             $DB->insert_record($tablename, $record);
2111             $this->fail("Expecting an exception, none occurred");
2112         } catch (moodle_exception $e) {
2113             $this->assertInstanceOf('dml_exception', $e);
2114         }
2115         $record = new stdClass();
2116         $record->oneint = 0;
2117         $record->onenum = 'onestring';
2118         try {
2119             $DB->insert_record($tablename, $record);
2120             $this->fail("Expecting an exception, none occurred");
2121         } catch (moodle_exception $e) {
2122             $this->assertInstanceOf('dml_exception', $e);
2123         }
2125         // Check empty string data is stored as 0 in numeric datatypes.
2126         $record = new stdClass();
2127         $record->oneint = ''; // Empty string.
2128         $record->onenum = 0;
2129         $recid = $DB->insert_record($tablename, $record);
2130         $record = $DB->get_record($tablename, array('id' => $recid));
2131         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2133         $record = new stdClass();
2134         $record->oneint = 0;
2135         $record->onenum = ''; // Empty string.
2136         $recid = $DB->insert_record($tablename, $record);
2137         $record = $DB->get_record($tablename, array('id' => $recid));
2138         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2140         // Check empty strings are set properly in string types.
2141         $record = new stdClass();
2142         $record->oneint = 0;
2143         $record->onenum = 0;
2144         $record->onechar = '';
2145         $record->onetext = '';
2146         $recid = $DB->insert_record($tablename, $record);
2147         $record = $DB->get_record($tablename, array('id' => $recid));
2148         $this->assertTrue($record->onechar === '');
2149         $this->assertTrue($record->onetext === '');
2151         // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
2152         $record = new stdClass();
2153         $record->oneint = ((210.10 + 39.92) - 150.02);
2154         $record->onenum = ((210.10 + 39.92) - 150.02);
2155         $recid = $DB->insert_record($tablename, $record);
2156         $record = $DB->get_record($tablename, array('id' => $recid));
2157         $this->assertEquals(100, $record->oneint);
2158         $this->assertEquals(100, $record->onenum);
2160         // Check various quotes/backslashes combinations in string types.
2161         $teststrings = array(
2162             'backslashes and quotes alone (even): "" \'\' \\\\',
2163             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2164             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2165             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2166         foreach ($teststrings as $teststring) {
2167             $record = new stdClass();
2168             $record->onechar = $teststring;
2169             $record->onetext = $teststring;
2170             $recid = $DB->insert_record($tablename, $record);
2171             $record = $DB->get_record($tablename, array('id' => $recid));
2172             $this->assertEquals($teststring, $record->onechar);
2173             $this->assertEquals($teststring, $record->onetext);
2174         }
2176         // Check LOBs in text/binary columns.
2177         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2178         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2179         $record = new stdClass();
2180         $record->onetext = $clob;
2181         $record->onebinary = $blob;
2182         $recid = $DB->insert_record($tablename, $record);
2183         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2184         $record = $rs->current();
2185         $rs->close();
2186         $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2187         $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2189         // And "small" LOBs too, just in case.
2190         $newclob = substr($clob, 0, 500);
2191         $newblob = substr($blob, 0, 250);
2192         $record = new stdClass();
2193         $record->onetext = $newclob;
2194         $record->onebinary = $newblob;
2195         $recid = $DB->insert_record($tablename, $record);
2196         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2197         $record = $rs->current();
2198         $rs->close();
2199         $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2200         $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2201         $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
2203         // And "diagnostic" LOBs too, just in case.
2204         $newclob = '\'"\\;/ěščřžýáíé';
2205         $newblob = '\'"\\;/ěščřžýáíé';
2206         $record = new stdClass();
2207         $record->onetext = $newclob;
2208         $record->onebinary = $newblob;
2209         $recid = $DB->insert_record($tablename, $record);
2210         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2211         $record = $rs->current();
2212         $rs->close();
2213         $this->assertSame($newclob, $record->onetext);
2214         $this->assertSame($newblob, $record->onebinary);
2215         $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
2217         // Test data is not modified.
2218         $record = new stdClass();
2219         $record->id     = -1; // Has to be ignored.
2220         $record->course = 3;
2221         $record->lalala = 'lalal'; // Unused.
2222         $before = clone($record);
2223         $DB->insert_record($tablename, $record);
2224         $this->assertEquals($record, $before);
2226         // Make sure the id is always increasing and never reuses the same id.
2227         $id1 = $DB->insert_record($tablename, array('course' => 3));
2228         $id2 = $DB->insert_record($tablename, array('course' => 3));
2229         $this->assertTrue($id1 < $id2);
2230         $DB->delete_records($tablename, array('id'=>$id2));
2231         $id3 = $DB->insert_record($tablename, array('course' => 3));
2232         $this->assertTrue($id2 < $id3);
2233         $DB->delete_records($tablename, array());
2234         $id4 = $DB->insert_record($tablename, array('course' => 3));
2235         $this->assertTrue($id3 < $id4);
2237         // Test saving a float in a CHAR column, and reading it back.
2238         $id = $DB->insert_record($tablename, array('onechar' => 1.0));
2239         $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2240         $id = $DB->insert_record($tablename, array('onechar' => 1e20));
2241         $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2242         $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
2243         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2244         $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
2245         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2246         $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
2247         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2248         $id = $DB->insert_record($tablename, array('onechar' => 1e300));
2249         $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2251         // Test saving a float in a TEXT column, and reading it back.
2252         $id = $DB->insert_record($tablename, array('onetext' => 1.0));
2253         $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2254         $id = $DB->insert_record($tablename, array('onetext' => 1e20));
2255         $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2256         $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
2257         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2258         $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
2259         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2260         $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
2261         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2262         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
2263         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2265         // Test that inserting data violating one unique key leads to error.
2266         // Empty the table completely.
2267         $this->assertTrue($DB->delete_records($tablename));
2269         // Add one unique constraint (index).
2270         $key = new xmldb_key('testuk', XMLDB_KEY_UNIQUE, array('course', 'oneint'));
2271         $dbman->add_key($table, $key);
2273         // Let's insert one record violating the constraint multiple times.
2274         $record = (object)array('course' => 1, 'oneint' => 1);
2275         $this->assertTrue($DB->insert_record($tablename, $record, false)); // Insert 1st. No problem expected.
2277         // Re-insert same record, not returning id. dml_exception expected.
2278         try {
2279             $DB->insert_record($tablename, $record, false);
2280             $this->fail("Expecting an exception, none occurred");
2281         } catch (moodle_exception $e) {
2282             $this->assertInstanceOf('dml_exception', $e);
2283         }
2285         // Re-insert same record, returning id. dml_exception expected.
2286         try {
2287             $DB->insert_record($tablename, $record, true);
2288             $this->fail("Expecting an exception, none occurred");
2289         } catch (moodle_exception $e) {
2290             $this->assertInstanceOf('dml_exception', $e);
2291         }
2293         // Try to insert a record into a non-existent table. dml_exception expected.
2294         try {
2295             $DB->insert_record('nonexistenttable', $record, true);
2296             $this->fail("Expecting an exception, none occurred");
2297         } catch (exception $e) {
2298             $this->assertTrue($e instanceof dml_exception);
2299         }
2300     }
2302     public function test_insert_records() {
2303         $DB = $this->tdb;
2304         $dbman = $DB->get_manager();
2306         $table = $this->get_test_table();
2307         $tablename = $table->getName();
2309         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2310         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2311         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
2312         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2313         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2314         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2315         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2316         $dbman->create_table($table);
2318         $this->assertCount(0, $DB->get_records($tablename));
2320         $record = new stdClass();
2321         $record->id = '1';
2322         $record->course = '1';
2323         $record->oneint = null;
2324         $record->onenum = '1.00';
2325         $record->onechar = 'a';
2326         $record->onetext = 'aaa';
2328         $expected = array();
2329         $records = array();
2330         for ($i = 1; $i <= 2000; $i++) { // This may take a while, it should be higher than defaults in DML drivers.
2331             $rec = clone($record);
2332             $rec->id = (string)$i;
2333             $rec->oneint = (string)$i;
2334             $expected[$i] = $rec;
2335             $rec = clone($rec);
2336             unset($rec->id);
2337             $records[$i] = $rec;
2338         }
2340         $DB->insert_records($tablename, $records);
2341         $stored = $DB->get_records($tablename, array(), 'id ASC');
2342         $this->assertEquals($expected, $stored);
2344         // Test there can be some extra properties including id.
2345         $count = $DB->count_records($tablename);
2346         $rec1 = (array)$record;
2347         $rec1['xxx'] = 1;
2348         $rec2 = (array)$record;
2349         $rec2['xxx'] = 2;
2351         $records = array($rec1, $rec2);
2352         $DB->insert_records($tablename, $records);
2353         $this->assertEquals($count + 2, $DB->count_records($tablename));
2355         // Test not all properties are necessary.
2356         $rec1 = (array)$record;
2357         unset($rec1['course']);
2358         $rec2 = (array)$record;
2359         unset($rec2['course']);
2361         $records = array($rec1, $rec2);
2362         $DB->insert_records($tablename, $records);
2364         // Make sure no changes in data object structure are tolerated.
2365         $rec1 = (array)$record;
2366         unset($rec1['id']);
2367         $rec2 = (array)$record;
2368         unset($rec2['id']);
2370         $records = array($rec1, $rec2);
2371         $DB->insert_records($tablename, $records);
2373         $rec2['xx'] = '1';
2374         $records = array($rec1, $rec2);
2375         try {
2376             $DB->insert_records($tablename, $records);
2377             $this->fail('coding_exception expected when insert_records receives different object data structures');
2378         } catch (moodle_exception $e) {
2379             $this->assertInstanceOf('coding_exception', $e);
2380         }
2382         unset($rec2['xx']);
2383         unset($rec2['course']);
2384         $rec2['course'] = '1';
2385         $records = array($rec1, $rec2);
2386         try {
2387             $DB->insert_records($tablename, $records);
2388             $this->fail('coding_exception expected when insert_records receives different object data structures');
2389         } catch (moodle_exception $e) {
2390             $this->assertInstanceOf('coding_exception', $e);
2391         }
2393         $records = 1;
2394         try {
2395             $DB->insert_records($tablename, $records);
2396             $this->fail('coding_exception expected when insert_records receives non-traversable data');
2397         } catch (moodle_exception $e) {
2398             $this->assertInstanceOf('coding_exception', $e);
2399         }
2401         $records = array(1);
2402         try {
2403             $DB->insert_records($tablename, $records);
2404             $this->fail('coding_exception expected when insert_records receives non-objet record');
2405         } catch (moodle_exception $e) {
2406             $this->assertInstanceOf('coding_exception', $e);
2407         }
2408     }
2410     public function test_import_record() {
2411         // All the information in this test is fetched from DB by get_recordset() so we
2412         // have such method properly tested against nulls, empties and friends...
2414         $DB = $this->tdb;
2415         $dbman = $DB->get_manager();
2417         $table = $this->get_test_table();
2418         $tablename = $table->getName();
2420         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2421         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2422         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
2423         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2424         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2425         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2426         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2427         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2428         $dbman->create_table($table);
2430         $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true));
2431         $record = $DB->get_record($tablename, array('course' => 1));
2432         $this->assertEquals(1, $record->id);
2433         $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied.
2434         $this->assertEquals(200, $record->onenum);
2435         $this->assertSame('onestring', $record->onechar);
2436         $this->assertNull($record->onetext);
2437         $this->assertNull($record->onebinary);
2439         // Ignore extra columns.
2440         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
2441         $before = clone($record);
2442         $this->assertTrue($DB->import_record($tablename, $record));
2443         $this->assertEquals($record, $before);
2444         $records = $DB->get_records($tablename);
2445         $this->assertEquals(2, $records[13]->course);
2447         // Check nulls are set properly for all types.
2448         $record = new stdClass();
2449         $record->id = 20;
2450         $record->oneint = null;
2451         $record->onenum = null;
2452         $record->onechar = null;
2453         $record->onetext = null;
2454         $record->onebinary = null;
2455         $this->assertTrue($DB->import_record($tablename, $record));
2456         $record = $DB->get_record($tablename, array('id' => 20));
2457         $this->assertEquals(0, $record->course);
2458         $this->assertNull($record->oneint);
2459         $this->assertNull($record->onenum);
2460         $this->assertNull($record->onechar);
2461         $this->assertNull($record->onetext);
2462         $this->assertNull($record->onebinary);
2464         // Check zeros are set properly for all types.
2465         $record = new stdClass();
2466         $record->id = 23;
2467         $record->oneint = 0;
2468         $record->onenum = 0;
2469         $this->assertTrue($DB->import_record($tablename, $record));
2470         $record = $DB->get_record($tablename, array('id' => 23));
2471         $this->assertEquals(0, $record->oneint);
2472         $this->assertEquals(0, $record->onenum);
2474         // Check string data causes exception in numeric types.
2475         $record = new stdClass();
2476         $record->id = 32;
2477         $record->oneint = 'onestring';
2478         $record->onenum = 0;
2479         try {
2480             $DB->import_record($tablename, $record);
2481             $this->fail("Expecting an exception, none occurred");
2482         } catch (moodle_exception $e) {
2483             $this->assertInstanceOf('dml_exception', $e);
2484         }
2485         $record = new stdClass();
2486         $record->id = 35;
2487         $record->oneint = 0;
2488         $record->onenum = 'onestring';
2489         try {
2490             $DB->import_record($tablename, $record);
2491             $this->fail("Expecting an exception, none occurred");
2492         } catch (moodle_exception $e) {
2493             $this->assertInstanceOf('dml_exception', $e);
2494         }
2496         // Check empty strings are set properly in string types.
2497         $record = new stdClass();
2498         $record->id = 44;
2499         $record->oneint = 0;
2500         $record->onenum = 0;
2501         $record->onechar = '';
2502         $record->onetext = '';
2503         $this->assertTrue($DB->import_record($tablename, $record));
2504         $record = $DB->get_record($tablename, array('id' => 44));
2505         $this->assertTrue($record->onechar === '');
2506         $this->assertTrue($record->onetext === '');
2508         // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
2509         $record = new stdClass();
2510         $record->id = 47;
2511         $record->oneint = ((210.10 + 39.92) - 150.02);
2512         $record->onenum = ((210.10 + 39.92) - 150.02);
2513         $this->assertTrue($DB->import_record($tablename, $record));
2514         $record = $DB->get_record($tablename, array('id' => 47));
2515         $this->assertEquals(100, $record->oneint);
2516         $this->assertEquals(100, $record->onenum);
2518         // Check various quotes/backslashes combinations in string types.
2519         $i = 50;
2520         $teststrings = array(
2521             'backslashes and quotes alone (even): "" \'\' \\\\',
2522             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2523             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2524             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2525         foreach ($teststrings as $teststring) {
2526             $record = new stdClass();
2527             $record->id = $i;
2528             $record->onechar = $teststring;
2529             $record->onetext = $teststring;
2530             $this->assertTrue($DB->import_record($tablename, $record));
2531             $record = $DB->get_record($tablename, array('id' => $i));
2532             $this->assertEquals($teststring, $record->onechar);
2533             $this->assertEquals($teststring, $record->onetext);
2534             $i = $i + 3;
2535         }
2537         // Check LOBs in text/binary columns.
2538         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2539         $record = new stdClass();
2540         $record->id = 70;
2541         $record->onetext = $clob;
2542         $record->onebinary = '';
2543         $this->assertTrue($DB->import_record($tablename, $record));
2544         $rs = $DB->get_recordset($tablename, array('id' => 70));
2545         $record = $rs->current();
2546         $rs->close();
2547         $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2549         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2550         $record = new stdClass();
2551         $record->id = 71;
2552         $record->onetext = '';
2553         $record->onebinary = $blob;
2554         $this->assertTrue($DB->import_record($tablename, $record));
2555         $rs = $DB->get_recordset($tablename, array('id' => 71));
2556         $record = $rs->current();
2557         $rs->close();
2558         $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2560         // And "small" LOBs too, just in case.
2561         $newclob = substr($clob, 0, 500);
2562         $newblob = substr($blob, 0, 250);
2563         $record = new stdClass();
2564         $record->id = 73;
2565         $record->onetext = $newclob;
2566         $record->onebinary = $newblob;
2567         $this->assertTrue($DB->import_record($tablename, $record));
2568         $rs = $DB->get_recordset($tablename, array('id' => 73));
2569         $record = $rs->current();
2570         $rs->close();
2571         $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2572         $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2573         $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing.
2574     }
2576     public function test_update_record_raw() {
2577         $DB = $this->tdb;
2578         $dbman = $DB->get_manager();
2580         $table = $this->get_test_table();
2581         $tablename = $table->getName();
2583         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2584         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, 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('course' => 1));
2589         $DB->insert_record($tablename, array('course' => 3));
2591         $record = $DB->get_record($tablename, array('course' => 1));
2592         $record->course = 2;
2593         $this->assertTrue($DB->update_record_raw($tablename, $record));
2594         $this->assertEquals(0, $DB->count_records($tablename, array('course' => 1)));
2595         $this->assertEquals(1, $DB->count_records($tablename, array('course' => 2)));
2596         $this->assertEquals(1, $DB->count_records($tablename, array('course' => 3)));
2598         $record = $DB->get_record($tablename, array('course' => 3));
2599         $record->xxxxx = 2;
2600         try {
2601             $DB->update_record_raw($tablename, $record);
2602             $this->fail("Expecting an exception, none occurred");
2603         } catch (moodle_exception $e) {
2604             $this->assertInstanceOf('moodle_exception', $e);
2605         }
2607         $record = $DB->get_record($tablename, array('course' => 3));
2608         unset($record->id);
2609         try {
2610             $DB->update_record_raw($tablename, $record);
2611             $this->fail("Expecting an exception, none occurred");
2612         } catch (moodle_exception $e) {
2613             $this->assertInstanceOf('coding_exception', $e);
2614         }
2615     }
2617     public function test_update_record() {
2619         // All the information in this test is fetched from DB by get_record() so we
2620         // have such method properly tested against nulls, empties and friends...
2622         $DB = $this->tdb;
2623         $dbman = $DB->get_manager();
2625         $table = $this->get_test_table();
2626         $tablename = $table->getName();
2628         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2629         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2630         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null, 100);
2631         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2632         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2633         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2634         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2635         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2636         $dbman->create_table($table);
2638         $DB->insert_record($tablename, array('course' => 1));
2639         $record = $DB->get_record($tablename, array('course' => 1));
2640         $record->course = 2;
2642         $this->assertTrue($DB->update_record($tablename, $record));
2643         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
2644         $this->assertNotEmpty($record = $DB->get_record($tablename, array('course' => 2)));
2645         $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied.
2646         $this->assertEquals(200, $record->onenum);
2647         $this->assertSame('onestring', $record->onechar);
2648         $this->assertNull($record->onetext);
2649         $this->assertNull($record->onebinary);
2651         // Check nulls are set properly for all types.
2652         $record->oneint = null;
2653         $record->onenum = null;
2654         $record->onechar = null;
2655         $record->onetext = null;
2656         $record->onebinary = null;
2657         $DB->update_record($tablename, $record);
2658         $record = $DB->get_record($tablename, array('course' => 2));
2659         $this->assertNull($record->oneint);
2660         $this->assertNull($record->onenum);
2661         $this->assertNull($record->onechar);
2662         $this->assertNull($record->onetext);
2663         $this->assertNull($record->onebinary);
2665         // Check zeros are set properly for all types.
2666         $record->oneint = 0;
2667         $record->onenum = 0;
2668         $DB->update_record($tablename, $record);
2669         $record = $DB->get_record($tablename, array('course' => 2));
2670         $this->assertEquals(0, $record->oneint);
2671         $this->assertEquals(0, $record->onenum);
2673         // Check booleans are set properly for all types.
2674         $record->oneint = true; // Trues.
2675         $record->onenum = true;
2676         $record->onechar = true;
2677         $record->onetext = true;
2678         $DB->update_record($tablename, $record);
2679         $record = $DB->get_record($tablename, array('course' => 2));
2680         $this->assertEquals(1, $record->oneint);
2681         $this->assertEquals(1, $record->onenum);
2682         $this->assertEquals(1, $record->onechar);
2683         $this->assertEquals(1, $record->onetext);
2685         $record->oneint = false; // Falses.
2686         $record->onenum = false;
2687         $record->onechar = false;
2688         $record->onetext = false;
2689         $DB->update_record($tablename, $record);
2690         $record = $DB->get_record($tablename, array('course' => 2));
2691         $this->assertEquals(0, $record->oneint);
2692         $this->assertEquals(0, $record->onenum);
2693         $this->assertEquals(0, $record->onechar);
2694         $this->assertEquals(0, $record->onetext);
2696         // Check string data causes exception in numeric types.
2697         $record->oneint = 'onestring';
2698         $record->onenum = 0;
2699         try {
2700             $DB->update_record($tablename, $record);
2701             $this->fail("Expecting an exception, none occurred");
2702         } catch (moodle_exception $e) {
2703             $this->assertInstanceOf('dml_exception', $e);
2704         }
2705         $record->oneint = 0;
2706         $record->onenum = 'onestring';
2707         try {
2708             $DB->update_record($tablename, $record);
2709             $this->fail("Expecting an exception, none occurred");
2710         } catch (moodle_exception $e) {
2711             $this->assertInstanceOf('dml_exception', $e);
2712         }
2714         // Check empty string data is stored as 0 in numeric datatypes.
2715         $record->oneint = ''; // Empty string.
2716         $record->onenum = 0;
2717         $DB->update_record($tablename, $record);
2718         $record = $DB->get_record($tablename, array('course' => 2));
2719         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2721         $record->oneint = 0;
2722         $record->onenum = ''; // Empty string.
2723         $DB->update_record($tablename, $record);
2724         $record = $DB->get_record($tablename, array('course' => 2));
2725         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2727         // Check empty strings are set properly in string types.
2728         $record->oneint = 0;
2729         $record->onenum = 0;
2730         $record->onechar = '';
2731         $record->onetext = '';
2732         $DB->update_record($tablename, $record);
2733         $record = $DB->get_record($tablename, array('course' => 2));
2734         $this->assertTrue($record->onechar === '');
2735         $this->assertTrue($record->onetext === '');
2737         // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
2738         $record->oneint = ((210.10 + 39.92) - 150.02);
2739         $record->onenum = ((210.10 + 39.92) - 150.02);
2740         $DB->update_record($tablename, $record);
2741         $record = $DB->get_record($tablename, array('course' => 2));
2742         $this->assertEquals(100, $record->oneint);
2743         $this->assertEquals(100, $record->onenum);
2745         // Check various quotes/backslashes combinations in string types.
2746         $teststrings = array(
2747             'backslashes and quotes alone (even): "" \'\' \\\\',
2748             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2749             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2750             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2751         foreach ($teststrings as $teststring) {
2752             $record->onechar = $teststring;
2753             $record->onetext = $teststring;
2754             $DB->update_record($tablename, $record);
2755             $record = $DB->get_record($tablename, array('course' => 2));
2756             $this->assertEquals($teststring, $record->onechar);
2757             $this->assertEquals($teststring, $record->onetext);
2758         }
2760         // Check LOBs in text/binary columns.
2761         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2762         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2763         $record->onetext = $clob;
2764         $record->onebinary = $blob;
2765         $DB->update_record($tablename, $record);
2766         $record = $DB->get_record($tablename, array('course' => 2));
2767         $this->assertEquals($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2768         $this->assertEquals($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2770         // And "small" LOBs too, just in case.
2771         $newclob = substr($clob, 0, 500);
2772         $newblob = substr($blob, 0, 250);
2773         $record->onetext = $newclob;
2774         $record->onebinary = $newblob;
2775         $DB->update_record($tablename, $record);
2776         $record = $DB->get_record($tablename, array('course' => 2));
2777         $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2778         $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2780         // Test saving a float in a CHAR column, and reading it back.
2781         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2782         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
2783         $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2784         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
2785         $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2786         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
2787         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2788         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
2789         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2790         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
2791         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2792         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
2793         $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2795         // Test saving a float in a TEXT column, and reading it back.
2796         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2797         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
2798         $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2799         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
2800         $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2801         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
2802         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2803         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
2804         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2805         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
2806         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2807         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
2808         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2809     }
2811     public function test_set_field() {
2812         $DB = $this->tdb;
2813         $dbman = $DB->get_manager();
2815         $table = $this->get_test_table();
2816         $tablename = $table->getName();
2818         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2819         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2820         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2821         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2822         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2823         $dbman->create_table($table);
2825         // Simple set_field.
2826         $id1 = $DB->insert_record($tablename, array('course' => 1));
2827         $id2 = $DB->insert_record($tablename, array('course' => 1));
2828         $id3 = $DB->insert_record($tablename, array('course' => 3));
2829         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2830         $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2831         $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2832         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2833         $DB->delete_records($tablename, array());
2835         // Multiple fields affected.
2836         $id1 = $DB->insert_record($tablename, array('course' => 1));
2837         $id2 = $DB->insert_record($tablename, array('course' => 1));
2838         $id3 = $DB->insert_record($tablename, array('course' => 3));
2839         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2840         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2841         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2842         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2843         $DB->delete_records($tablename, array());
2845         // No field affected.
2846         $id1 = $DB->insert_record($tablename, array('course' => 1));
2847         $id2 = $DB->insert_record($tablename, array('course' => 1));
2848         $id3 = $DB->insert_record($tablename, array('course' => 3));
2849         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2850         $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2851         $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2852         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2853         $DB->delete_records($tablename, array());
2855         // All fields - no condition.
2856         $id1 = $DB->insert_record($tablename, array('course' => 1));
2857         $id2 = $DB->insert_record($tablename, array('course' => 1));
2858         $id3 = $DB->insert_record($tablename, array('course' => 3));
2859         $DB->set_field($tablename, 'course', 5, array());
2860         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2861         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2862         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2864         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
2865         $conditions = array('onetext' => '1');
2866         try {
2867             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2868             if (debugging()) {
2869                 // Only in debug mode - hopefully all devs test code in debug mode...
2870                 $this->fail('An Exception is missing, expected due to equating of text fields');
2871             }
2872         } catch (moodle_exception $e) {
2873             $this->assertInstanceOf('dml_exception', $e);
2874             $this->assertSame('textconditionsnotallowed', $e->errorcode);
2875         }
2877         // Test saving a float in a CHAR column, and reading it back.
2878         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2879         $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
2880         $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2881         $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
2882         $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2883         $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
2884         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2885         $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
2886         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2887         $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
2888         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2889         $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
2890         $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2892         // Test saving a float in a TEXT column, and reading it back.
2893         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2894         $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
2895         $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2896         $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
2897         $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2898         $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
2899         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2900         $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
2901         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2902         $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
2903         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2904         $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
2905         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2907         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2908         // go to set_field_select() because set_field() is just one wrapper over it.
2909     }
2911     public function test_set_field_select() {
2913         // All the information in this test is fetched from DB by get_field() so we
2914         // have such method properly tested against nulls, empties and friends...
2916         $DB = $this->tdb;
2917         $dbman = $DB->get_manager();
2919         $table = $this->get_test_table();
2920         $tablename = $table->getName();
2922         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2923         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
2924         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', null, null, null);
2925         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2926         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2927         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2928         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2929         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2930         $dbman->create_table($table);
2932         $DB->insert_record($tablename, array('course' => 1));
2934         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2935         $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2937         // Check nulls are set properly for all types.
2938         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // Trues.
2939         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2940         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2941         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2942         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2943         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2944         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2945         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2946         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2947         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2949         // Check zeros are set properly for all types.
2950         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2951         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2952         $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2953         $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2955         // Check booleans are set properly for all types.
2956         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // Trues.
2957         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2958         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2959         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2960         $this->assertEquals(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2961         $this->assertEquals(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2962         $this->assertEquals(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2963         $this->assertEquals(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2965         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // Falses.
2966         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2967         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2968         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2969         $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2970         $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2971         $this->assertEquals(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2972         $this->assertEquals(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2974         // Check string data causes exception in numeric types.
2975         try {
2976             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2977             $this->fail("Expecting an exception, none occurred");
2978         } catch (moodle_exception $e) {
2979             $this->assertInstanceOf('dml_exception', $e);
2980         }
2981         try {
2982             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2983             $this->fail("Expecting an exception, none occurred");
2984         } catch (moodle_exception $e) {
2985             $this->assertInstanceOf('dml_exception', $e);
2986         }
2988         // Check empty string data is stored as 0 in numeric datatypes.
2989         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2990         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2991         $this->assertTrue(is_numeric($field) && $field == 0);
2993         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2994         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2995         $this->assertTrue(is_numeric($field) && $field == 0);
2997         // Check empty strings are set properly in string types.
2998         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2999         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
3000         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
3001         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
3003         // Check operation ((210.10 + 39.92) - 150.02) against numeric types.
3004         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
3005         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
3006         $this->assertEquals(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
3007         $this->assertEquals(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
3009         // Check various quotes/backslashes combinations in string types.
3010         $teststrings = array(
3011             'backslashes and quotes alone (even): "" \'\' \\\\',
3012             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
3013             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
3014             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
3015         foreach ($teststrings as $teststring) {
3016             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
3017             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
3018             $this->assertEquals($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
3019             $this->assertEquals($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
3020         }
3022         // Check LOBs in text/binary columns.
3023         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
3024         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
3025         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
3026         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
3027         $this->assertEquals($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
3028         $this->assertEquals($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
3030         // Empty data in binary columns works.
3031         $DB->set_field_select($tablename, 'onebinary', '', 'id = ?', array(1));
3032         $this->assertEquals('', $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Blobs need to accept empty values.');
3034         // And "small" LOBs too, just in case.
3035         $newclob = substr($clob, 0, 500);
3036         $newblob = substr($blob, 0, 250);
3037         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
3038         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
3039         $this->assertEquals($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
3040         $this->assertEquals($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
3042         // This is the failure from MDL-24863. This was giving an error on MSSQL,
3043         // which converts the '1' to an integer, which cannot then be compared with
3044         // onetext cast to a varchar. This should be fixed and working now.
3045         $newchar = 'frog';
3046         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
3047         $params = array('onetext' => '1');
3048         try {
3049             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
3050             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
3051         } catch (dml_exception $e) {
3052             $this->assertFalse(true, 'We have an unexpected exception.');
3053             throw $e;
3054         }
3055     }
3057     public function test_count_records() {
3058         $DB = $this->tdb;
3060         $dbman = $DB->get_manager();
3062         $table = $this->get_test_table();
3063         $tablename = $table->getName();
3065         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3066         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3067         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
3068         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3069         $dbman->create_table($table);
3071         $this->assertSame(0, $DB->count_records($tablename));
3073         $DB->insert_record($tablename, array('course' => 3));
3074         $DB->insert_record($tablename, array('course' => 4));
3075         $DB->insert_record($tablename, array('course' => 5));
3077         $this->assertSame(3, $DB->count_records($tablename));
3079         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
3080         $conditions = array('onetext' => '1');
3081         try {
3082             $DB->count_records($tablename, $conditions);
3083             if (debugging()) {
3084                 // Only in debug mode - hopefully all devs test code in debug mode...
3085                 $this->fail('An Exception is missing, expected due to equating of text fields');
3086             }
3087         } catch (moodle_exception $e) {
3088             $this->assertInstanceOf('dml_exception', $e);
3089             $this->assertSame('textconditionsnotallowed', $e->errorcode);
3090         }
3091     }
3093     public function test_count_records_select() {
3094         $DB = $this->tdb;
3096         $dbman = $DB->get_manager();
3098         $table = $this->get_test_table();
3099         $tablename = $table->getName();
3101         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3102         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3103         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3104         $dbman->create_table($table);
3106         $this->assertSame(0, $DB->count_records($tablename));
3108         $DB->insert_record($tablename, array('course' => 3));
3109         $DB->insert_record($tablename, array('course' => 4));
3110         $DB->insert_record($tablename, array('course' => 5));
3112         $this->assertSame(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
3113     }
3115     public function test_count_records_sql() {
3116         $DB = $this->tdb;
3117         $dbman = $DB->get_manager();
3119         $table = $this->get_test_table();
3120         $tablename = $table->getName();
3122         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3123         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3124         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
3125         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3126         $dbman->create_table($table);
3128         $this->assertSame(0, $DB->count_records($tablename));
3130         $DB->insert_record($tablename, array('course' => 3, 'onechar' => 'a'));
3131         $DB->insert_record($tablename, array('course' => 4, 'onechar' => 'b'));
3132         $DB->insert_record($tablename, array('course' => 5, 'onechar' => 'c'));
3134         $this->assertSame(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
3136         // Test invalid use.
3137         try {
3138             $DB->count_records_sql("SELECT onechar FROM {{$tablename}} WHERE course = ?", array(3));
3139             $this->fail('Exception expected when non-number field used in count_records_sql');
3140         } catch (moodle_exception $e) {
3141             $this->assertInstanceOf('coding_exception', $e);
3142         }
3144         try {
3145             $DB->count_records_sql("SELECT course FROM {{$tablename}} WHERE 1 = 2");
3146             $this->fail('Exception expected when non-number field used in count_records_sql');
3147         } catch (moodle_exception $e) {
3148             $this->assertInstanceOf('coding_exception', $e);
3149         }
3150     }
3152     public function test_record_exists() {
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', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3160         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3161         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
3162         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3163         $dbman->create_table($table);
3165         $this->assertEquals(0, $DB->count_records($tablename));
3167         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
3168         $DB->insert_record($tablename, array('course' => 3));
3170         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
3172         // Test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int).
3173         $conditions = array('onetext' => '1');
3174         try {
3175             $DB->record_exists($tablename, $conditions);
3176             if (debugging()) {
3177                 // Only in debug mode - hopefully all devs test code in debug mode...
3178                 $this->fail('An Exception is missing, expected due to equating of text fields');
3179             }
3180         } catch (moodle_exception $e) {
3181             $this->assertInstanceOf('dml_exception', $e);
3182             $this->assertSame('textconditionsnotallowed', $e->errorcode);
3183         }
3184     }
3186     public function test_record_exists_select() {
3187         $DB = $this->tdb;
3188         $dbman = $DB->get_manager();
3190         $table = $this->get_test_table();
3191         $tablename = $table->getName();
3193         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3194         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3195         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3196         $dbman->create_table($table);
3198         $this->assertEquals(0, $DB->count_records($tablename));
3200         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
3201         $DB->insert_record($tablename, array('course' => 3));
3203         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
3204     }
3206     public function test_record_exists_sql() {
3207         $DB = $this->tdb;
3208         $dbman = $DB->get_manager();
3210         $table = $this->get_test_table();
3211         $tablename = $table->getName();
3213         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3214         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3215         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3216         $dbman->create_table($table);
3218         $this->assertEquals(0, $DB->count_records($tablename));
3220         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
3221         $DB->insert_record($tablename, array('course' => 3));
3223         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
3224     }
3226     public function test_recordset_locks_delete() {
3227         $DB = $this->tdb;
3228         $dbman = $DB->get_manager();
3230         // Setup.
3231         $table = $this->get_test_table();
3232         $tablename = $table->getName();
3234         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3235         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3236         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3237         $dbman->create_table($table);
3239         $DB->insert_record($tablename, array('course' => 1));
3240         $DB->insert_record($tablename, array('course' => 2));
3241         $DB->insert_record($tablename, array('course' => 3));
3242         $DB->insert_record($tablename, array('course' => 4));
3243         $DB->insert_record($tablename, array('course' => 5));
3244         $DB->insert_record($tablename, array('course' => 6));
3246         // Test against db write locking while on an open recordset.
3247         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // Get courses = {3,4}.
3248         foreach ($rs as $record) {
3249             $cid = $record->course;
3250             $DB->delete_records($tablename, array('course' => $cid));
3251             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
3252         }
3253         $rs->close();
3255         $this->assertEquals(4, $DB->count_records($tablename, array()));
3256     }
3258     public function test_recordset_locks_update() {
3259         $DB = $this->tdb;
3260         $dbman = $DB->get_manager();
3262         // Setup.
3263         $table = $this->get_test_table();
3264         $tablename = $table->getName();
3266         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3267         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3268         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3269         $dbman->create_table($table);
3271         $DB->insert_record($tablename, array('course' => 1));
3272         $DB->insert_record($tablename, array('course' => 2));
3273         $DB->insert_record($tablename, array('course' => 3));
3274         $DB->insert_record($tablename, array('course' => 4));
3275         $DB->insert_record($tablename, array('course' => 5));
3276         $DB->insert_record($tablename, array('course' => 6));
3278         // Test against db write locking while on an open recordset.
3279         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // Get courses = {3,4}.
3280         foreach ($rs as $record) {
3281             $cid = $record->course;
3282             $DB->set_field($tablename, 'course', 10, array('course' => $cid));
3283             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
3284         }
3285         $rs->close();
3287         $this->assertEquals(2, $DB->count_records($tablename, array('course' => 10)));
3288     }
3290     public function test_delete_records() {
3291         $DB = $this->tdb;
3292         $dbman = $DB->get_manager();
3294         $table = $this->get_test_table();
3295         $tablename = $table->getName();
3297         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3298         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
3299         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
3300         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3301         $dbman->create_table($table);
3303         $DB->insert_record($tablename, array('course' => 3));