Merge branch 'MDL-37854_master' of https://github.com/markn86/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 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 new xmldb_table($tablename);
52     }
54     function test_diagnose() {
55         $DB = $this->tdb;
56         $result = $DB->diagnose();
57         $this->assertNull($result, 'Database self diagnostics failed %s');
58     }
60     function test_get_server_info() {
61         $DB = $this->tdb;
62         $result = $DB->get_server_info();
63         $this->assertTrue(is_array($result));
64         $this->assertTrue(array_key_exists('description', $result));
65         $this->assertTrue(array_key_exists('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->assertEquals('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->assertEquals(1, count($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->assertEquals(1, count($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->assertEquals(4, count($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->assertEquals(1, count($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->assertEquals(1, count($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->assertEquals(4, count($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->assertEquals(1, count($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->assertEquals(1, count($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->assertEquals(4, count($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->assertEquals(1, count($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->assertEquals(1, count($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->assertEquals(count($common), 0);
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 (exception $e) {
203             $this->assertTrue($e instanceof dml_exception);
204             $this->assertEquals($e->errorcode, 'typenotimplement');
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 (exception $e) {
213             $this->assertTrue($e instanceof coding_exception);
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->assertEquals(' 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->assertEquals(' 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->assertEquals('= ?', $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->assertEquals(1, count($params));
240         $value = reset($params);
241         $key = key($params);
242         $this->assertEquals('<> :'.$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->assertEquals('= ?', $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->assertEquals(1, count($params));
255         $value = reset($params);
256         $key = key($params);
257         $this->assertEquals('<> :'.$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->assertEquals('= ?', $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->assertEquals(1, count($params));
270         $value = reset($params);
271         $key = key($params);
272         $this->assertEquals('<> :'.$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     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]);
334         // Malformed table placeholder
335         $sql = "SELECT * FROM [testtable]";
336         $sqlarray = $DB->fix_sql_params($sql);
337         $this->assertSame($sql, $sqlarray[0]);
340         // Mixed param types (colon and dollar)
341         $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1";
342         $params = array('param1' => 'record1', 'param2' => 3);
343         try {
344             $DB->fix_sql_params($sql, $params);
345             $this->fail("Expecting an exception, none occurred");
346         } catch (Exception $e) {
347             $this->assertTrue($e instanceof dml_exception);
348         }
350         // Mixed param types (question and dollar)
351         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1";
352         $params = array('param1' => 'record2', 'param2' => 5);
353         try {
354             $DB->fix_sql_params($sql, $params);
355             $this->fail("Expecting an exception, none occurred");
356         } catch (Exception $e) {
357             $this->assertTrue($e instanceof dml_exception);
358         }
360         // Too few params in sql
361         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?";
362         $params = array('record2', 3);
363         try {
364             $DB->fix_sql_params($sql, $params);
365             $this->fail("Expecting an exception, none occurred");
366         } catch (Exception $e) {
367             $this->assertTrue($e instanceof dml_exception);
368         }
370         // Too many params in array: no error, just use what is necessary
371         $params[] = 1;
372         $params[] = time();
373         try {
374             $sqlarray = $DB->fix_sql_params($sql, $params);
375             $this->assertTrue(is_array($sqlarray));
376             $this->assertEquals(count($sqlarray[1]), 3);
377         } catch (Exception $e) {
378             $this->fail("Unexpected ".get_class($e)." exception");
379         }
381         // Named params missing from array
382         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
383         $params = array('wrongname' => 'record1', 'course' => 1);
384         try {
385             $DB->fix_sql_params($sql, $params);
386             $this->fail("Expecting an exception, none occurred");
387         } catch (Exception $e) {
388             $this->assertTrue($e instanceof dml_exception);
389         }
391         // Duplicate named param in query - this is a very important feature!!
392         // it helps with debugging of sloppy code
393         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name";
394         $params = array('name' => 'record2', 'course' => 3);
395         try {
396             $DB->fix_sql_params($sql, $params);
397             $this->fail("Expecting an exception, none occurred");
398         } catch (Exception $e) {
399             $this->assertTrue($e instanceof dml_exception);
400         }
402         // Extra named param is ignored
403         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
404         $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha');
405         try {
406             $sqlarray = $DB->fix_sql_params($sql, $params);
407             $this->assertTrue(is_array($sqlarray));
408             $this->assertEquals(count($sqlarray[1]), 2);
409         } catch (Exception $e) {
410             $this->fail("Unexpected ".get_class($e)." exception");
411         }
413         // Params exceeding 30 chars length
414         $sql = "SELECT * FROM {{$tablename}} WHERE name = :long_placeholder_with_more_than_30";
415         $params = array('long_placeholder_with_more_than_30' => 'record1');
416         try {
417             $DB->fix_sql_params($sql, $params);
418             $this->fail("Expecting an exception, none occurred");
419         } catch (Exception $e) {
420             $this->assertTrue($e instanceof coding_exception);
421         }
423         // Booleans in NAMED params are casting to 1/0 int
424         $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?";
425         $params = array(true, false);
426         list($sql, $params) = $DB->fix_sql_params($sql, $params);
427         $this->assertTrue(reset($params) === 1);
428         $this->assertTrue(next($params) === 0);
430         // Booleans in QM params are casting to 1/0 int
431         $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2";
432         $params = array('course1' => true, 'course2' => false);
433         list($sql, $params) = $DB->fix_sql_params($sql, $params);
434         $this->assertTrue(reset($params) === 1);
435         $this->assertTrue(next($params) === 0);
437         // Booleans in DOLLAR params are casting to 1/0 int
438         $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2";
439         $params = array(true, false);
440         list($sql, $params) = $DB->fix_sql_params($sql, $params);
441         $this->assertTrue(reset($params) === 1);
442         $this->assertTrue(next($params) === 0);
444         // No data types are touched except bool
445         $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)";
446         $inparams = array('abc', 'ABC', NULL, '1', 1, 1.4);
447         list($sql, $params) = $DB->fix_sql_params($sql, $inparams);
448         $this->assertSame(array_values($params), array_values($inparams));
449     }
451     public function test_strtok() {
452         // strtok was previously used by bound emulation, make sure it is not used any more
453         $DB = $this->tdb;
454         $dbman = $this->tdb->get_manager();
456         $table = $this->get_test_table();
457         $tablename = $table->getName();
459         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
460         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
461         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
462         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
463         $dbman->create_table($table);
465         $str = 'a?b?c?d';
466         $this->assertSame(strtok($str, '?'), 'a');
468         $DB->get_records($tablename, array('id'=>1));
470         $this->assertSame(strtok('?'), 'b');
471     }
473     public function test_tweak_param_names() {
474         // Note the tweak_param_names() method is only available in the oracle driver,
475         // hence we look for expected results indirectly, by testing various DML methods
476         // with some "extreme" conditions causing the tweak to happen.
477         $DB = $this->tdb;
478         $dbman = $this->tdb->get_manager();
480         $table = $this->get_test_table();
481         $tablename = $table->getName();
483         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
484         // Add some columns with 28 chars in the name
485         $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10');
486         $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2');
487         $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100');
488         // Add some columns with 30 chars in the name
489         $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10');
490         $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2');
491         $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100');
493         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
495         $dbman->create_table($table);
497         $this->assertTrue($dbman->table_exists($tablename));
499         // Test insert record
500         $rec1 = new stdClass();
501         $rec1->long_int_columnname_with_28c = 28;
502         $rec1->long_dec_columnname_with_28c = 28.28;
503         $rec1->long_str_columnname_with_28c = '28';
504         $rec1->long_int_columnname_with_30cxx = 30;
505         $rec1->long_dec_columnname_with_30cxx = 30.30;
506         $rec1->long_str_columnname_with_30cxx = '30';
508         // insert_record()
509         $rec1->id = $DB->insert_record($tablename, $rec1);
510         $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
512         // update_record()
513         $DB->update_record($tablename, $rec1);
514         $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
516         // set_field()
517         $rec1->long_int_columnname_with_28c = 280;
518         $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c,
519             array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28));
520         $rec1->long_dec_columnname_with_28c = 280.28;
521         $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c,
522             array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28));
523         $rec1->long_str_columnname_with_28c = '280';
524         $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c,
525             array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28'));
526         $rec1->long_int_columnname_with_30cxx = 300;
527         $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx,
528             array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30));
529         $rec1->long_dec_columnname_with_30cxx = 300.30;
530         $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx,
531             array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30));
532         $rec1->long_str_columnname_with_30cxx = '300';
533         $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx,
534             array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30'));
535         $this->assertEquals($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
537         // delete_records()
538         $rec2 = $DB->get_record($tablename, array('id' => $rec1->id));
539         $rec2->id = $DB->insert_record($tablename, $rec2);
540         $this->assertEquals(2, $DB->count_records($tablename));
541         $DB->delete_records($tablename, (array) $rec2);
542         $this->assertEquals(1, $DB->count_records($tablename));
544         // get_recordset()
545         $rs = $DB->get_recordset($tablename, (array) $rec1);
546         $iterations = 0;
547         foreach ($rs as $rec2) {
548             $iterations++;
549         }
550         $rs->close();
551         $this->assertEquals(1, $iterations);
552         $this->assertEquals($rec1, $rec2);
554         // get_records()
555         $recs = $DB->get_records($tablename, (array) $rec1);
556         $this->assertEquals(1, count($recs));
557         $this->assertEquals($rec1, reset($recs));
559         // get_fieldset_select()
560         $select = 'id = :id AND
561                    long_int_columnname_with_28c = :long_int_columnname_with_28c AND
562                    long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND
563                    long_str_columnname_with_28c = :long_str_columnname_with_28c AND
564                    long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND
565                    long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND
566                    long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx';
567         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1);
568         $this->assertEquals(1, count($fields));
569         $this->assertEquals($rec1->long_int_columnname_with_28c, reset($fields));
570         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1);
571         $this->assertEquals($rec1->long_dec_columnname_with_28c, reset($fields));
572         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1);
573         $this->assertEquals($rec1->long_str_columnname_with_28c, reset($fields));
574         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1);
575         $this->assertEquals($rec1->long_int_columnname_with_30cxx, reset($fields));
576         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1);
577         $this->assertEquals($rec1->long_dec_columnname_with_30cxx, reset($fields));
578         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1);
579         $this->assertEquals($rec1->long_str_columnname_with_30cxx, reset($fields));
581         // overlapping placeholders (progressive str_replace)
582         $overlapselect = 'id = :p AND
583                    long_int_columnname_with_28c = :param1 AND
584                    long_dec_columnname_with_28c = :param2 AND
585                    long_str_columnname_with_28c = :param_with_29_characters_long AND
586                    long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND
587                    long_dec_columnname_with_30cxx = :param_ AND
588                    long_str_columnname_with_30cxx = :param__';
589         $overlapparams = array(
590             'p' => $rec1->id,
591             'param1' => $rec1->long_int_columnname_with_28c,
592             'param2' => $rec1->long_dec_columnname_with_28c,
593             'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c,
594             'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx,
595             'param_' => $rec1->long_dec_columnname_with_30cxx,
596             'param__' => $rec1->long_str_columnname_with_30cxx);
597         $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams);
598         $this->assertEquals(1, count($recs));
599         $this->assertEquals($rec1, reset($recs));
601         // execute()
602         $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1);
603         $this->assertEquals(0, $DB->count_records($tablename));
604     }
606     public function test_get_tables() {
607         $DB = $this->tdb;
608         $dbman = $this->tdb->get_manager();
610         // Need to test with multiple DBs
611         $table = $this->get_test_table();
612         $tablename = $table->getName();
614         $original_count = count($DB->get_tables());
616         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
617         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
619         $dbman->create_table($table);
620         $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
622         $dbman->drop_table($table);
623         $this->assertTrue(count($DB->get_tables()) == $original_count);
624     }
626     public function test_get_indexes() {
627         $DB = $this->tdb;
628         $dbman = $this->tdb->get_manager();
630         $table = $this->get_test_table();
631         $tablename = $table->getName();
633         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
634         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
635         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
636         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
637         $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
638         $dbman->create_table($table);
640         $indices = $DB->get_indexes($tablename);
641         $this->assertTrue(is_array($indices));
642         $this->assertEquals(count($indices), 2);
643         // we do not care about index names for now
644         $first = array_shift($indices);
645         $second = array_shift($indices);
646         if (count($first['columns']) == 2) {
647             $composed = $first;
648             $single   = $second;
649         } else {
650             $composed = $second;
651             $single   = $first;
652         }
653         $this->assertFalse($single['unique']);
654         $this->assertTrue($composed['unique']);
655         $this->assertEquals(1, count($single['columns']));
656         $this->assertEquals(2, count($composed['columns']));
657         $this->assertEquals('course', $single['columns'][0]);
658         $this->assertEquals('course', $composed['columns'][0]);
659         $this->assertEquals('id', $composed['columns'][1]);
660     }
662     public function test_get_columns() {
663         $DB = $this->tdb;
664         $dbman = $this->tdb->get_manager();
666         $table = $this->get_test_table();
667         $tablename = $table->getName();
669         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
670         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
671         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
672         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
673         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
674         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
675         $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, null, null, 300);
676         $table->add_field('anotherfloat', XMLDB_TYPE_FLOAT, null, null, null, null, 400);
677         $table->add_field('negativedfltint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1');
678         $table->add_field('negativedfltnumber', XMLDB_TYPE_NUMBER, '10', null, XMLDB_NOTNULL, null, '-2');
679         $table->add_field('negativedfltfloat', XMLDB_TYPE_FLOAT, '10', null, XMLDB_NOTNULL, null, '-3');
680         $table->add_field('someint1', XMLDB_TYPE_INTEGER, '1', null, null, null, '0');
681         $table->add_field('someint2', XMLDB_TYPE_INTEGER, '2', null, null, null, '0');
682         $table->add_field('someint3', XMLDB_TYPE_INTEGER, '3', null, null, null, '0');
683         $table->add_field('someint4', XMLDB_TYPE_INTEGER, '4', null, null, null, '0');
684         $table->add_field('someint5', XMLDB_TYPE_INTEGER, '5', null, null, null, '0');
685         $table->add_field('someint6', XMLDB_TYPE_INTEGER, '6', null, null, null, '0');
686         $table->add_field('someint7', XMLDB_TYPE_INTEGER, '7', null, null, null, '0');
687         $table->add_field('someint8', XMLDB_TYPE_INTEGER, '8', null, null, null, '0');
688         $table->add_field('someint9', XMLDB_TYPE_INTEGER, '9', null, null, null, '0');
689         $table->add_field('someint10', XMLDB_TYPE_INTEGER, '10', null, null, null, '0');
690         $table->add_field('someint18', XMLDB_TYPE_INTEGER, '18', null, null, null, '0');
691         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
692         $dbman->create_table($table);
694         $columns = $DB->get_columns($tablename);
695         $this->assertTrue(is_array($columns));
697         $fields = $table->getFields();
698         $this->assertEquals(count($columns), count($fields));
700         $field = $columns['id'];
701         $this->assertEquals('R', $field->meta_type);
702         $this->assertTrue($field->auto_increment);
703         $this->assertTrue($field->unique);
705         $field = $columns['course'];
706         $this->assertEquals('I', $field->meta_type);
707         $this->assertFalse($field->auto_increment);
708         $this->assertTrue($field->has_default);
709         $this->assertEquals(0, $field->default_value);
710         $this->assertTrue($field->not_null);
712         for($i=1;$i<=10;$i++) {
713             $field = $columns['someint'.$i];
714             $this->assertEquals('I', $field->meta_type);
715             $this->assertGreaterThanOrEqual($i, $field->max_length);
716         }
717         $field = $columns['someint18'];
718         $this->assertEquals('I', $field->meta_type);
719         $this->assertGreaterThanOrEqual(18, $field->max_length);
721         $field = $columns['name'];
722         $this->assertEquals('C', $field->meta_type);
723         $this->assertFalse($field->auto_increment);
724         $this->assertEquals(255, $field->max_length);
725         $this->assertTrue($field->has_default);
726         $this->assertSame('lala', $field->default_value);
727         $this->assertFalse($field->not_null);
729         $field = $columns['description'];
730         $this->assertEquals('X', $field->meta_type);
731         $this->assertFalse($field->auto_increment);
732         $this->assertFalse($field->has_default);
733         $this->assertSame(null, $field->default_value);
734         $this->assertFalse($field->not_null);
736         $field = $columns['enumfield'];
737         $this->assertEquals('C', $field->meta_type);
738         $this->assertFalse($field->auto_increment);
739         $this->assertSame('test2', $field->default_value);
740         $this->assertTrue($field->not_null);
742         $field = $columns['onenum'];
743         $this->assertEquals('N', $field->meta_type);
744         $this->assertFalse($field->auto_increment);
745         $this->assertEquals(10, $field->max_length);
746         $this->assertEquals(2, $field->scale);
747         $this->assertTrue($field->has_default);
748         $this->assertEquals(200.0, $field->default_value);
749         $this->assertFalse($field->not_null);
751         $field = $columns['onefloat'];
752         $this->assertEquals('N', $field->meta_type);
753         $this->assertFalse($field->auto_increment);
754         $this->assertTrue($field->has_default);
755         $this->assertEquals(300.0, $field->default_value);
756         $this->assertFalse($field->not_null);
758         $field = $columns['anotherfloat'];
759         $this->assertEquals('N', $field->meta_type);
760         $this->assertFalse($field->auto_increment);
761         $this->assertTrue($field->has_default);
762         $this->assertEquals(400.0, $field->default_value);
763         $this->assertFalse($field->not_null);
765         // Test negative defaults in numerical columns
766         $field = $columns['negativedfltint'];
767         $this->assertTrue($field->has_default);
768         $this->assertEquals(-1, $field->default_value);
770         $field = $columns['negativedfltnumber'];
771         $this->assertTrue($field->has_default);
772         $this->assertEquals(-2, $field->default_value);
774         $field = $columns['negativedfltfloat'];
775         $this->assertTrue($field->has_default);
776         $this->assertEquals(-3, $field->default_value);
778         for ($i = 0; $i < count($columns); $i++) {
779             if ($i == 0) {
780                 $next_column = reset($columns);
781                 $next_field  = reset($fields);
782             } else {
783                 $next_column = next($columns);
784                 $next_field  = next($fields);
785             }
787             $this->assertEquals($next_column->name, $next_field->getName());
788         }
790         // Test get_columns for non-existing table returns empty array. MDL-30147
791         $columns = $DB->get_columns('xxxx');
792         $this->assertEquals(array(), $columns);
794         // create something similar to "context_temp" with id column without sequence
795         $dbman->drop_table($table);
796         $table = $this->get_test_table();
797         $tablename = $table->getName();
798         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
799         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
800         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
801         $dbman->create_table($table);
803         $columns = $DB->get_columns($tablename);
804         $this->assertFalse($columns['id']->auto_increment);
805     }
807     public function test_get_manager() {
808         $DB = $this->tdb;
809         $dbman = $this->tdb->get_manager();
811         $this->assertTrue($dbman instanceof database_manager);
812     }
814     public function test_setup_is_unicodedb() {
815         $DB = $this->tdb;
816         $this->assertTrue($DB->setup_is_unicodedb());
817     }
819     public function test_set_debug() { //tests get_debug() too
820         $DB = $this->tdb;
821         $dbman = $this->tdb->get_manager();
823         $table = $this->get_test_table();
824         $tablename = $table->getName();
826         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
827         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
828         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
829         $dbman->create_table($table);
831         $sql = "SELECT * FROM {{$tablename}}";
833         $prevdebug = $DB->get_debug();
835         ob_start();
836         $DB->set_debug(true);
837         $this->assertTrue($DB->get_debug());
838         $DB->execute($sql);
839         $DB->set_debug(false);
840         $this->assertFalse($DB->get_debug());
841         $debuginfo = ob_get_contents();
842         ob_end_clean();
843         $this->assertFalse($debuginfo === '');
845         ob_start();
846         $DB->execute($sql);
847         $debuginfo = ob_get_contents();
848         ob_end_clean();
849         $this->assertTrue($debuginfo === '');
851         $DB->set_debug($prevdebug);
852     }
854     public function test_execute() {
855         $DB = $this->tdb;
856         $dbman = $this->tdb->get_manager();
858         $table1 = $this->get_test_table('1');
859         $tablename1 = $table1->getName();
860         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
861         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
862         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
863         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
864         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
865         $dbman->create_table($table1);
867         $table2 = $this->get_test_table('2');
868         $tablename2 = $table2->getName();
869         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
870         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
871         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
872         $dbman->create_table($table2);
874         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
875         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
876         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
877         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
879         // select results are ignored
880         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
881         $this->assertTrue($DB->execute($sql, array('course'=>3)));
883         // throw exception on error
884         $sql = "XXUPDATE SET XSSD";
885         try {
886             $DB->execute($sql);
887             $this->fail("Expecting an exception, none occurred");
888         } catch (Exception $e) {
889             $this->assertTrue($e instanceof dml_exception);
890         }
892         // update records
893         $sql = "UPDATE {{$tablename1}}
894                    SET course = 6
895                  WHERE course = ?";
896         $this->assertTrue($DB->execute($sql, array('3')));
897         $this->assertEquals($DB->count_records($tablename1, array('course' => 6)), 2);
899         // update records with subquery condition
900         // confirm that the option not using table aliases is cross-db
901         $sql = "UPDATE {{$tablename1}}
902                    SET course = 0
903                  WHERE NOT EXISTS (
904                            SELECT course
905                              FROM {{$tablename2}} tbl2
906                             WHERE tbl2.course = {{$tablename1}}.course
907                               AND 1 = 0)"; // Really we don't update anything, but verify the syntax is allowed
908         $this->assertTrue($DB->execute($sql));
910         // insert from one into second table
911         $sql = "INSERT INTO {{$tablename2}} (course)
913                 SELECT course
914                   FROM {{$tablename1}}";
915         $this->assertTrue($DB->execute($sql));
916         $this->assertEquals($DB->count_records($tablename2), 4);
917     }
919     public function test_get_recordset() {
920         $DB = $this->tdb;
921         $dbman = $DB->get_manager();
923         $table = $this->get_test_table();
924         $tablename = $table->getName();
926         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
927         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
928         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
929         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
930         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
931         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
932         $dbman->create_table($table);
934         $data = array(array('course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
935             array('course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
936             array('course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
938         foreach ($data as $key=>$record) {
939             $data[$key]['id'] = $DB->insert_record($tablename, $record);
940         }
942         // standard recordset iteration
943         $rs = $DB->get_recordset($tablename);
944         $this->assertTrue($rs instanceof moodle_recordset);
945         reset($data);
946         foreach($rs as $record) {
947             $data_record = current($data);
948             foreach ($record as $k => $v) {
949                 $this->assertEquals($data_record[$k], $v);
950             }
951             next($data);
952         }
953         $rs->close();
955         // iterator style usage
956         $rs = $DB->get_recordset($tablename);
957         $this->assertTrue($rs instanceof moodle_recordset);
958         reset($data);
959         while ($rs->valid()) {
960             $record = $rs->current();
961             $data_record = current($data);
962             foreach ($record as $k => $v) {
963                 $this->assertEquals($data_record[$k], $v);
964             }
965             next($data);
966             $rs->next();
967         }
968         $rs->close();
970         // make sure rewind is ignored
971         $rs = $DB->get_recordset($tablename);
972         $this->assertTrue($rs instanceof moodle_recordset);
973         reset($data);
974         $i = 0;
975         foreach($rs as $record) {
976             $i++;
977             $rs->rewind();
978             if ($i > 10) {
979                 $this->fail('revind not ignored in recordsets');
980                 break;
981             }
982             $data_record = current($data);
983             foreach ($record as $k => $v) {
984                 $this->assertEquals($data_record[$k], $v);
985             }
986             next($data);
987         }
988         $rs->close();
990         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
991         $conditions = array('onetext' => '1');
992         try {
993             $rs = $DB->get_recordset($tablename, $conditions);
994             $this->fail('An Exception is missing, expected due to equating of text fields');
995         } catch (exception $e) {
996             $this->assertTrue($e instanceof dml_exception);
997             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
998         }
1000         // Test nested iteration.
1001         $rs1 = $DB->get_recordset($tablename);
1002         $i = 0;
1003         foreach($rs1 as $record1) {
1004             $rs2 = $DB->get_recordset($tablename);
1005             $i++;
1006             $j = 0;
1007             foreach($rs2 as $record2) {
1008                 $j++;
1009             }
1010             $rs2->close();
1011             $this->assertEquals($j, count($data));
1012         }
1013         $rs1->close();
1014         $this->assertEquals($i, count($data));
1016         // notes:
1017         //  * limits are tested in test_get_recordset_sql()
1018         //  * where_clause() is used internally and is tested in test_get_records()
1019     }
1021     public function test_get_recordset_static() {
1022         $DB = $this->tdb;
1023         $dbman = $DB->get_manager();
1025         $table = $this->get_test_table();
1026         $tablename = $table->getName();
1028         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1029         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1030         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1031         $dbman->create_table($table);
1033         $DB->insert_record($tablename, array('course' => 1));
1034         $DB->insert_record($tablename, array('course' => 2));
1035         $DB->insert_record($tablename, array('course' => 3));
1036         $DB->insert_record($tablename, array('course' => 4));
1038         $rs = $DB->get_recordset($tablename, array(), 'id');
1040         $DB->set_field($tablename, 'course', 666, array('course'=>1));
1041         $DB->delete_records($tablename, array('course'=>2));
1043         $i = 0;
1044         foreach($rs as $record) {
1045             $i++;
1046             $this->assertEquals($i, $record->course);
1047         }
1048         $rs->close();
1049         $this->assertEquals(4, $i);
1051         // Now repeat with limits because it may use different code.
1052         $DB->delete_records($tablename, array());
1054         $DB->insert_record($tablename, array('course' => 1));
1055         $DB->insert_record($tablename, array('course' => 2));
1056         $DB->insert_record($tablename, array('course' => 3));
1057         $DB->insert_record($tablename, array('course' => 4));
1059         $rs = $DB->get_recordset($tablename, array(), 'id', '*', 0, 3);
1061         $DB->set_field($tablename, 'course', 666, array('course'=>1));
1062         $DB->delete_records($tablename, array('course'=>2));
1064         $i = 0;
1065         foreach($rs as $record) {
1066             $i++;
1067             $this->assertEquals($i, $record->course);
1068         }
1069         $rs->close();
1070         $this->assertEquals(3, $i);
1071     }
1073     public function test_get_recordset_iterator_keys() {
1074         $DB = $this->tdb;
1075         $dbman = $DB->get_manager();
1077         $table = $this->get_test_table();
1078         $tablename = $table->getName();
1080         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1081         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1082         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
1083         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1084         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1085         $dbman->create_table($table);
1087         $data = array(array('course' => 3, 'name' => 'record1'),
1088             array('course' => 3, 'name' => 'record2'),
1089             array('course' => 5, 'name' => 'record3'));
1090         foreach ($data as $key=>$record) {
1091             $data[$key]['id'] = $DB->insert_record($tablename, $record);
1092         }
1094         // Test repeated numeric keys are returned ok
1095         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
1097         reset($data);
1098         $count = 0;
1099         foreach($rs as $key => $record) {
1100             $data_record = current($data);
1101             $this->assertEquals($data_record['course'], $key);
1102             next($data);
1103             $count++;
1104         }
1105         $rs->close();
1106         $this->assertEquals($count, 3);
1108         // Test string keys are returned ok
1109         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
1111         reset($data);
1112         $count = 0;
1113         foreach($rs as $key => $record) {
1114             $data_record = current($data);
1115             $this->assertEquals($data_record['name'], $key);
1116             next($data);
1117             $count++;
1118         }
1119         $rs->close();
1120         $this->assertEquals($count, 3);
1122         // Test numeric not starting in 1 keys are returned ok
1123         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
1125         $data = array_reverse($data);
1126         reset($data);
1127         $count = 0;
1128         foreach($rs as $key => $record) {
1129             $data_record = current($data);
1130             $this->assertEquals($data_record['id'], $key);
1131             next($data);
1132             $count++;
1133         }
1134         $rs->close();
1135         $this->assertEquals($count, 3);
1136     }
1138     public function test_get_recordset_list() {
1139         $DB = $this->tdb;
1140         $dbman = $DB->get_manager();
1142         $table = $this->get_test_table();
1143         $tablename = $table->getName();
1145         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1146         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
1147         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1148         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1149         $dbman->create_table($table);
1151         $DB->insert_record($tablename, array('course' => 3));
1152         $DB->insert_record($tablename, array('course' => 3));
1153         $DB->insert_record($tablename, array('course' => 5));
1154         $DB->insert_record($tablename, array('course' => 2));
1155         $DB->insert_record($tablename, array('course' => null));
1156         $DB->insert_record($tablename, array('course' => 1));
1157         $DB->insert_record($tablename, array('course' => 0));
1159         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
1160         $counter = 0;
1161         foreach ($rs as $record) {
1162             $counter++;
1163         }
1164         $this->assertEquals(3, $counter);
1165         $rs->close();
1167         $rs = $DB->get_recordset_list($tablename, 'course', array(3));
1168         $counter = 0;
1169         foreach ($rs as $record) {
1170             $counter++;
1171         }
1172         $this->assertEquals(2, $counter);
1173         $rs->close();
1175         $rs = $DB->get_recordset_list($tablename, 'course', array(null));
1176         $counter = 0;
1177         foreach ($rs as $record) {
1178             $counter++;
1179         }
1180         $this->assertEquals(1, $counter);
1181         $rs->close();
1183         $rs = $DB->get_recordset_list($tablename, 'course', array(6, null));
1184         $counter = 0;
1185         foreach ($rs as $record) {
1186             $counter++;
1187         }
1188         $this->assertEquals(1, $counter);
1189         $rs->close();
1191         $rs = $DB->get_recordset_list($tablename, 'course', array(null, 5, 5, 5));
1192         $counter = 0;
1193         foreach ($rs as $record) {
1194             $counter++;
1195         }
1196         $this->assertEquals(2, $counter);
1197         $rs->close();
1199         $rs = $DB->get_recordset_list($tablename, 'course', array(true));
1200         $counter = 0;
1201         foreach ($rs as $record) {
1202             $counter++;
1203         }
1204         $this->assertEquals(1, $counter);
1205         $rs->close();
1207         $rs = $DB->get_recordset_list($tablename, 'course', array(false));
1208         $counter = 0;
1209         foreach ($rs as $record) {
1210             $counter++;
1211         }
1212         $this->assertEquals(1, $counter);
1213         $rs->close();
1215         $rs = $DB->get_recordset_list($tablename, 'course',array()); // Must return 0 rows without conditions. MDL-17645
1217         $counter = 0;
1218         foreach ($rs as $record) {
1219             $counter++;
1220         }
1221         $rs->close();
1222         $this->assertEquals(0, $counter);
1224         // notes:
1225         //  * limits are tested in test_get_recordset_sql()
1226         //  * where_clause() is used internally and is tested in test_get_records()
1227     }
1229     public function test_get_recordset_select() {
1230         $DB = $this->tdb;
1231         $dbman = $DB->get_manager();
1233         $table = $this->get_test_table();
1234         $tablename = $table->getName();
1236         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1237         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1238         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1239         $dbman->create_table($table);
1241         $DB->insert_record($tablename, array('course' => 3));
1242         $DB->insert_record($tablename, array('course' => 3));
1243         $DB->insert_record($tablename, array('course' => 5));
1244         $DB->insert_record($tablename, array('course' => 2));
1246         $rs = $DB->get_recordset_select($tablename, '');
1247         $counter = 0;
1248         foreach ($rs as $record) {
1249             $counter++;
1250         }
1251         $rs->close();
1252         $this->assertEquals(4, $counter);
1254         $this->assertNotEmpty($rs = $DB->get_recordset_select($tablename, 'course = 3'));
1255         $counter = 0;
1256         foreach ($rs as $record) {
1257             $counter++;
1258         }
1259         $rs->close();
1260         $this->assertEquals(2, $counter);
1262         // notes:
1263         //  * limits are tested in test_get_recordset_sql()
1264     }
1266     public function test_get_recordset_sql() {
1267         $DB = $this->tdb;
1268         $dbman = $DB->get_manager();
1270         $table = $this->get_test_table();
1271         $tablename = $table->getName();
1273         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1274         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1275         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1276         $dbman->create_table($table);
1278         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1279         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1280         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1281         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1282         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1283         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1284         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1286         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1287         $counter = 0;
1288         foreach ($rs as $record) {
1289             $counter++;
1290         }
1291         $rs->close();
1292         $this->assertEquals(2, $counter);
1294         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
1295         // only limitfrom = skips that number of records
1296         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1297         $records = array();
1298         foreach($rs as $key => $record) {
1299             $records[$key] = $record;
1300         }
1301         $rs->close();
1302         $this->assertEquals(5, count($records));
1303         $this->assertEquals($inskey3, reset($records)->id);
1304         $this->assertEquals($inskey7, end($records)->id);
1306         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
1307     }
1309     public function test_export_table_recordset() {
1310         $DB = $this->tdb;
1311         $dbman = $DB->get_manager();
1313         $table = $this->get_test_table();
1314         $tablename = $table->getName();
1316         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1317         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
1318         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1319         $dbman->create_table($table);
1321         $ids = array();
1322         $ids[] = $DB->insert_record($tablename, array('course' => 3));
1323         $ids[] = $DB->insert_record($tablename, array('course' => 5));
1324         $ids[] = $DB->insert_record($tablename, array('course' => 4));
1325         $ids[] = $DB->insert_record($tablename, array('course' => 3));
1326         $ids[] = $DB->insert_record($tablename, array('course' => 2));
1327         $ids[] = $DB->insert_record($tablename, array('course' => 1));
1328         $ids[] = $DB->insert_record($tablename, array('course' => 0));
1330         $rs = $DB->export_table_recordset($tablename);
1331         $rids = array();
1332         foreach ($rs as $record) {
1333             $rids[] = $record->id;
1334         }
1335         $rs->close();
1336         $this->assertEquals($ids, $rids, '', 0, 0, true);
1337     }
1339     public function test_get_records() {
1340         $DB = $this->tdb;
1341         $dbman = $DB->get_manager();
1343         $table = $this->get_test_table();
1344         $tablename = $table->getName();
1346         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1347         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1348         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1349         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1350         $dbman->create_table($table);
1352         $DB->insert_record($tablename, array('course' => 3));
1353         $DB->insert_record($tablename, array('course' => 3));
1354         $DB->insert_record($tablename, array('course' => 5));
1355         $DB->insert_record($tablename, array('course' => 2));
1357         // All records
1358         $records = $DB->get_records($tablename);
1359         $this->assertEquals(4, count($records));
1360         $this->assertEquals(3, $records[1]->course);
1361         $this->assertEquals(3, $records[2]->course);
1362         $this->assertEquals(5, $records[3]->course);
1363         $this->assertEquals(2, $records[4]->course);
1365         // Records matching certain conditions
1366         $records = $DB->get_records($tablename, array('course' => 3));
1367         $this->assertEquals(2, count($records));
1368         $this->assertEquals(3, $records[1]->course);
1369         $this->assertEquals(3, $records[2]->course);
1371         // All records sorted by course
1372         $records = $DB->get_records($tablename, null, 'course');
1373         $this->assertEquals(4, count($records));
1374         $current_record = reset($records);
1375         $this->assertEquals(4, $current_record->id);
1376         $current_record = next($records);
1377         $this->assertEquals(1, $current_record->id);
1378         $current_record = next($records);
1379         $this->assertEquals(2, $current_record->id);
1380         $current_record = next($records);
1381         $this->assertEquals(3, $current_record->id);
1383         // All records, but get only one field
1384         $records = $DB->get_records($tablename, null, '', 'id');
1385         $this->assertFalse(isset($records[1]->course));
1386         $this->assertTrue(isset($records[1]->id));
1387         $this->assertEquals(4, count($records));
1389         // Booleans into params
1390         $records = $DB->get_records($tablename, array('course' => true));
1391         $this->assertEquals(0, count($records));
1392         $records = $DB->get_records($tablename, array('course' => false));
1393         $this->assertEquals(0, count($records));
1395         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1396         $conditions = array('onetext' => '1');
1397         try {
1398             $records = $DB->get_records($tablename, $conditions);
1399             if (debugging()) {
1400                 // only in debug mode - hopefully all devs test code in debug mode...
1401                 $this->fail('An Exception is missing, expected due to equating of text fields');
1402             }
1403         } catch (exception $e) {
1404             $this->assertTrue($e instanceof dml_exception);
1405             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
1406         }
1408         // test get_records passing non-existing table
1409         // with params
1410         try {
1411             $records = $DB->get_records('xxxx', array('id' => 0));
1412             $this->fail('An Exception is missing, expected due to query against non-existing table');
1413         } catch (exception $e) {
1414             $this->assertTrue($e instanceof dml_exception);
1415             if (debugging()) {
1416                 // information for developers only, normal users get general error message
1417                 $this->assertEquals($e->errorcode, 'ddltablenotexist');
1418             }
1419         }
1420         // and without params
1421         try {
1422             $records = $DB->get_records('xxxx', array());
1423             $this->fail('An Exception is missing, expected due to query against non-existing table');
1424         } catch (exception $e) {
1425             $this->assertTrue($e instanceof dml_exception);
1426             if (debugging()) {
1427                 // information for developers only, normal users get general error message
1428                 $this->assertEquals($e->errorcode, 'ddltablenotexist');
1429             }
1430         }
1432         // test get_records passing non-existing column
1433         try {
1434             $records = $DB->get_records($tablename, array('xxxx' => 0));
1435             $this->fail('An Exception is missing, expected due to query against non-existing column');
1436         } catch (exception $e) {
1437             $this->assertTrue($e instanceof dml_exception);
1438             if (debugging()) {
1439                 // information for developers only, normal users get general error message
1440                 $this->assertEquals($e->errorcode, 'ddlfieldnotexist');
1441             }
1442         }
1444         // note: delegate limits testing to test_get_records_sql()
1445     }
1447     public function test_get_records_list() {
1448         $DB = $this->tdb;
1449         $dbman = $DB->get_manager();
1451         $table = $this->get_test_table();
1452         $tablename = $table->getName();
1454         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1455         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1456         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1457         $dbman->create_table($table);
1459         $DB->insert_record($tablename, array('course' => 3));
1460         $DB->insert_record($tablename, array('course' => 3));
1461         $DB->insert_record($tablename, array('course' => 5));
1462         $DB->insert_record($tablename, array('course' => 2));
1464         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
1465         $this->assertTrue(is_array($records));
1466         $this->assertEquals(3, count($records));
1467         $this->assertEquals(1, reset($records)->id);
1468         $this->assertEquals(2, next($records)->id);
1469         $this->assertEquals(4, next($records)->id);
1471         $this->assertSame(array(), $records = $DB->get_records_list($tablename, 'course', array())); // Must return 0 rows without conditions. MDL-17645
1472         $this->assertEquals(0, count($records));
1474         // note: delegate limits testing to test_get_records_sql()
1475     }
1477     public function test_get_records_sql() {
1478         global $CFG;
1480         $DB = $this->tdb;
1481         $dbman = $DB->get_manager();
1483         $table = $this->get_test_table();
1484         $tablename = $table->getName();
1486         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1487         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1488         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1489         $dbman->create_table($table);
1491         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1492         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1493         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1494         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1495         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1496         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1497         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1499         $table2 = $this->get_test_table("2");
1500         $tablename2 = $table2->getName();
1501         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1502         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1503         $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
1504         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1505         $dbman->create_table($table2);
1507         $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
1508         $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
1509         $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
1510         $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
1512         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1513         $this->assertEquals(2, count($records));
1514         $this->assertEquals($inskey1, reset($records)->id);
1515         $this->assertEquals($inskey4, next($records)->id);
1517         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1518         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1519         $this->assertDebuggingCalled();
1520         $this->assertEquals(6, count($records));
1521         $CFG->debug = DEBUG_MINIMAL;
1522         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1523         $this->assertDebuggingNotCalled();
1524         $this->assertEquals(6, count($records));
1525         $CFG->debug = DEBUG_DEVELOPER;
1527         // negative limits = no limits
1528         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1529         $this->assertEquals(7, count($records));
1531         // zero limits = no limits
1532         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1533         $this->assertEquals(7, count($records));
1535         // only limitfrom = skips that number of records
1536         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1537         $this->assertEquals(5, count($records));
1538         $this->assertEquals($inskey3, reset($records)->id);
1539         $this->assertEquals($inskey7, end($records)->id);
1541         // only limitnum = fetches that number of records
1542         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1543         $this->assertEquals(3, count($records));
1544         $this->assertEquals($inskey1, reset($records)->id);
1545         $this->assertEquals($inskey3, end($records)->id);
1547         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1548         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1549         $this->assertEquals(2, count($records));
1550         $this->assertEquals($inskey4, reset($records)->id);
1551         $this->assertEquals($inskey5, end($records)->id);
1553         // both limitfrom and limitnum in query having subqueris
1554         // note the subquery skips records with course = 0 and 3
1555         $sql = "SELECT * FROM {{$tablename}}
1556                  WHERE course NOT IN (
1557                      SELECT course FROM {{$tablename}}
1558                       WHERE course IN (0, 3))
1559                 ORDER BY course";
1560         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2
1561         $this->assertEquals(2, count($records));
1562         $this->assertEquals($inskey6, reset($records)->id);
1563         $this->assertEquals($inskey5, end($records)->id);
1564         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2
1565         $this->assertEquals(2, count($records));
1566         $this->assertEquals($inskey3, reset($records)->id);
1567         $this->assertEquals($inskey2, end($records)->id);
1569         // test 2 tables with aliases and limits with order bys
1570         $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
1571                   FROM {{$tablename}} t1, {{$tablename2}} t2
1572                  WHERE t2.course=t1.course
1573               ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
1574         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5
1575         $this->assertEquals(2, count($records));
1576         $this->assertEquals('5', end($records)->cid);
1577         $this->assertEquals('4', reset($records)->cid);
1579         // test 2 tables with aliases and limits with the highest INT limit works
1580         $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}
1581         $this->assertEquals(2, count($records));
1582         $this->assertEquals('5', end($records)->cid);
1583         $this->assertEquals('4', reset($records)->cid);
1585         // test 2 tables with aliases and limits with order bys (limit which is highest INT number)
1586         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses
1587         $this->assertEquals(0, count($records));
1589         // test 2 tables with aliases and limits with order bys (limit which s highest INT number)
1590         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses
1591         $this->assertEquals(0, count($records));
1593         // TODO: Test limits in queries having DISTINCT clauses
1595         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1596     }
1598     public function test_get_records_menu() {
1599         $DB = $this->tdb;
1600         $dbman = $DB->get_manager();
1602         $table = $this->get_test_table();
1603         $tablename = $table->getName();
1605         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1606         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1607         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1608         $dbman->create_table($table);
1610         $DB->insert_record($tablename, array('course' => 3));
1611         $DB->insert_record($tablename, array('course' => 3));
1612         $DB->insert_record($tablename, array('course' => 5));
1613         $DB->insert_record($tablename, array('course' => 2));
1615         $records = $DB->get_records_menu($tablename, array('course' => 3));
1616         $this->assertTrue(is_array($records));
1617         $this->assertEquals(2, count($records));
1618         $this->assertFalse(empty($records[1]));
1619         $this->assertFalse(empty($records[2]));
1620         $this->assertEquals(3, $records[1]);
1621         $this->assertEquals(3, $records[2]);
1623         // note: delegate limits testing to test_get_records_sql()
1624     }
1626     public function test_get_records_select_menu() {
1627         $DB = $this->tdb;
1628         $dbman = $DB->get_manager();
1630         $table = $this->get_test_table();
1631         $tablename = $table->getName();
1633         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1634         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1635         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1636         $dbman->create_table($table);
1638         $DB->insert_record($tablename, array('course' => 3));
1639         $DB->insert_record($tablename, array('course' => 2));
1640         $DB->insert_record($tablename, array('course' => 3));
1641         $DB->insert_record($tablename, array('course' => 5));
1643         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1644         $this->assertTrue(is_array($records));
1646         $this->assertEquals(3, count($records));
1647         $this->assertFalse(empty($records[1]));
1648         $this->assertTrue(empty($records[2]));
1649         $this->assertFalse(empty($records[3]));
1650         $this->assertFalse(empty($records[4]));
1651         $this->assertEquals(3, $records[1]);
1652         $this->assertEquals(3, $records[3]);
1653         $this->assertEquals(5, $records[4]);
1655         // note: delegate limits testing to test_get_records_sql()
1656     }
1658     public function test_get_records_sql_menu() {
1659         $DB = $this->tdb;
1660         $dbman = $DB->get_manager();
1662         $table = $this->get_test_table();
1663         $tablename = $table->getName();
1665         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1666         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1667         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1668         $dbman->create_table($table);
1670         $DB->insert_record($tablename, array('course' => 3));
1671         $DB->insert_record($tablename, array('course' => 2));
1672         $DB->insert_record($tablename, array('course' => 3));
1673         $DB->insert_record($tablename, array('course' => 5));
1675         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1676         $this->assertTrue(is_array($records));
1678         $this->assertEquals(3, count($records));
1679         $this->assertFalse(empty($records[1]));
1680         $this->assertTrue(empty($records[2]));
1681         $this->assertFalse(empty($records[3]));
1682         $this->assertFalse(empty($records[4]));
1683         $this->assertEquals(3, $records[1]);
1684         $this->assertEquals(3, $records[3]);
1685         $this->assertEquals(5, $records[4]);
1687         // note: delegate limits testing to test_get_records_sql()
1688     }
1690     public function test_get_record() {
1691         $DB = $this->tdb;
1692         $dbman = $DB->get_manager();
1694         $table = $this->get_test_table();
1695         $tablename = $table->getName();
1697         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1698         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1699         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1700         $dbman->create_table($table);
1702         $DB->insert_record($tablename, array('course' => 3));
1703         $DB->insert_record($tablename, array('course' => 2));
1705         $record = $DB->get_record($tablename, array('id' => 2));
1706         $this->assertTrue($record instanceof stdClass);
1708         $this->assertEquals(2, $record->course);
1709         $this->assertEquals(2, $record->id);
1710     }
1713     public function test_get_record_select() {
1714         $DB = $this->tdb;
1715         $dbman = $DB->get_manager();
1717         $table = $this->get_test_table();
1718         $tablename = $table->getName();
1720         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1721         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1722         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1723         $dbman->create_table($table);
1725         $DB->insert_record($tablename, array('course' => 3));
1726         $DB->insert_record($tablename, array('course' => 2));
1728         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1729         $this->assertTrue($record instanceof stdClass);
1731         $this->assertEquals(2, $record->course);
1733         // note: delegates limit testing to test_get_records_sql()
1734     }
1736     public function test_get_record_sql() {
1737         global $CFG;
1739         $DB = $this->tdb;
1740         $dbman = $DB->get_manager();
1742         $table = $this->get_test_table();
1743         $tablename = $table->getName();
1745         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1746         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1747         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1748         $dbman->create_table($table);
1750         $DB->insert_record($tablename, array('course' => 3));
1751         $DB->insert_record($tablename, array('course' => 2));
1753         // standard use
1754         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1755         $this->assertTrue($record instanceof stdClass);
1756         $this->assertEquals(2, $record->course);
1757         $this->assertEquals(2, $record->id);
1759         // backwards compatibility with $ignoremultiple
1760         $this->assertFalse((bool)IGNORE_MISSING);
1761         $this->assertTrue((bool)IGNORE_MULTIPLE);
1763         // record not found - ignore
1764         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1765         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1767         // record not found error
1768         try {
1769             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1770             $this->fail("Exception expected");
1771         } catch (dml_missing_record_exception $e) {
1772             $this->assertTrue(true);
1773         }
1775         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1776         $this->assertDebuggingCalled();
1777         $CFG->debug = DEBUG_MINIMAL;
1778         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1779         $this->assertDebuggingNotCalled();
1780         $CFG->debug = DEBUG_DEVELOPER;
1782         // multiple matches ignored
1783         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1785         // multiple found error
1786         try {
1787             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1788             $this->fail("Exception expected");
1789         } catch (dml_multiple_records_exception $e) {
1790             $this->assertTrue(true);
1791         }
1792     }
1794     public function test_get_field() {
1795         $DB = $this->tdb;
1796         $dbman = $DB->get_manager();
1798         $table = $this->get_test_table();
1799         $tablename = $table->getName();
1801         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1802         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1803         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1804         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1805         $dbman->create_table($table);
1807         $id1 = $DB->insert_record($tablename, array('course' => 3));
1808         $DB->insert_record($tablename, array('course' => 5));
1809         $DB->insert_record($tablename, array('course' => 5));
1811         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1812         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1814         $this->assertSame(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1815         try {
1816             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1817             $this->fail('Exception expected due to missing record');
1818         } catch (dml_exception $ex) {
1819             $this->assertTrue(true);
1820         }
1822         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1823         $this->assertDebuggingNotCalled();
1825         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1826         $this->assertDebuggingCalled();
1828         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1829         $conditions = array('onetext' => '1');
1830         try {
1831             $DB->get_field($tablename, 'course', $conditions);
1832             if (debugging()) {
1833                 // only in debug mode - hopefully all devs test code in debug mode...
1834                 $this->fail('An Exception is missing, expected due to equating of text fields');
1835             }
1836         } catch (exception $e) {
1837             $this->assertTrue($e instanceof dml_exception);
1838             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
1839         }
1840     }
1842     public function test_get_field_select() {
1843         $DB = $this->tdb;
1844         $dbman = $DB->get_manager();
1846         $table = $this->get_test_table();
1847         $tablename = $table->getName();
1849         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1850         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1851         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1852         $dbman->create_table($table);
1854         $DB->insert_record($tablename, array('course' => 3));
1856         $this->assertEquals(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1857     }
1859     public function test_get_field_sql() {
1860         $DB = $this->tdb;
1861         $dbman = $DB->get_manager();
1863         $table = $this->get_test_table();
1864         $tablename = $table->getName();
1866         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1867         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1868         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1869         $dbman->create_table($table);
1871         $DB->insert_record($tablename, array('course' => 3));
1873         $this->assertEquals(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1874     }
1876     public function test_get_fieldset_select() {
1877         $DB = $this->tdb;
1878         $dbman = $DB->get_manager();
1880         $table = $this->get_test_table();
1881         $tablename = $table->getName();
1883         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1884         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1885         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1886         $dbman->create_table($table);
1888         $DB->insert_record($tablename, array('course' => 1));
1889         $DB->insert_record($tablename, array('course' => 3));
1890         $DB->insert_record($tablename, array('course' => 2));
1891         $DB->insert_record($tablename, array('course' => 6));
1893         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1894         $this->assertTrue(is_array($fieldset));
1896         $this->assertEquals(3, count($fieldset));
1897         $this->assertEquals(3, $fieldset[0]);
1898         $this->assertEquals(2, $fieldset[1]);
1899         $this->assertEquals(6, $fieldset[2]);
1900     }
1902     public function test_get_fieldset_sql() {
1903         $DB = $this->tdb;
1904         $dbman = $DB->get_manager();
1906         $table = $this->get_test_table();
1907         $tablename = $table->getName();
1909         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1910         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1911         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1912         $dbman->create_table($table);
1914         $DB->insert_record($tablename, array('course' => 1));
1915         $DB->insert_record($tablename, array('course' => 3));
1916         $DB->insert_record($tablename, array('course' => 2));
1917         $DB->insert_record($tablename, array('course' => 6));
1919         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1920         $this->assertTrue(is_array($fieldset));
1922         $this->assertEquals(3, count($fieldset));
1923         $this->assertEquals(2, $fieldset[0]);
1924         $this->assertEquals(3, $fieldset[1]);
1925         $this->assertEquals(4, $fieldset[2]);
1926     }
1928     public function test_insert_record_raw() {
1929         $DB = $this->tdb;
1930         $dbman = $DB->get_manager();
1932         $table = $this->get_test_table();
1933         $tablename = $table->getName();
1935         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1936         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1937         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1938         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1939         $dbman->create_table($table);
1941         $record = (object)array('course' => 1, 'onechar' => 'xx');
1942         $before = clone($record);
1943         $result = $DB->insert_record_raw($tablename, $record);
1944         $this->assertSame(1, $result);
1945         $this->assertEquals($record, $before);
1947         $record = $DB->get_record($tablename, array('course' => 1));
1948         $this->assertTrue($record instanceof stdClass);
1949         $this->assertSame('xx', $record->onechar);
1951         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1952         $this->assertSame(true, $result);
1954         // note: bulk not implemented yet
1955         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1956         $record = $DB->get_record($tablename, array('course' => 3));
1957         $this->assertTrue($record instanceof stdClass);
1958         $this->assertSame('zz', $record->onechar);
1960         // custom sequence (id) - returnid is ignored
1961         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1962         $this->assertSame(true, $result);
1963         $record = $DB->get_record($tablename, array('id' => 10));
1964         $this->assertTrue($record instanceof stdClass);
1965         $this->assertSame('bb', $record->onechar);
1967         // custom sequence - missing id error
1968         try {
1969             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1970             $this->fail('Exception expected due to missing record');
1971         } catch (coding_exception $ex) {
1972             $this->assertTrue(true);
1973         }
1975         // wrong column error
1976         try {
1977             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1978             $this->fail('Exception expected due to invalid column');
1979         } catch (dml_exception $ex) {
1980             $this->assertTrue(true);
1981         }
1983         // create something similar to "context_temp" with id column without sequence
1984         $dbman->drop_table($table);
1985         $table = $this->get_test_table();
1986         $tablename = $table->getName();
1987         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
1988         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1989         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1990         $dbman->create_table($table);
1992         $record = (object)array('id'=>5, 'course' => 1);
1993         $DB->insert_record_raw($tablename, $record, false, false, true);
1994         $record = $DB->get_record($tablename, array());
1995         $this->assertEquals(5, $record->id);
1996     }
1998     public function test_insert_record() {
1999         // All the information in this test is fetched from DB by get_recordset() so we
2000         // have such method properly tested against nulls, empties and friends...
2002         $DB = $this->tdb;
2003         $dbman = $DB->get_manager();
2005         $table = $this->get_test_table();
2006         $tablename = $table->getName();
2008         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2009         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2010         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2011         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2012         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2013         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2014         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2015         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2016         $dbman->create_table($table);
2018         $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true));
2019         $record = $DB->get_record($tablename, array('course' => 1));
2020         $this->assertEquals(1, $record->id);
2021         $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied
2022         $this->assertEquals(200, $record->onenum);
2023         $this->assertSame('onestring', $record->onechar);
2024         $this->assertNull($record->onetext);
2025         $this->assertNull($record->onebinary);
2027         // without returning id, bulk not implemented
2028         $result = $this->assertSame(true, $DB->insert_record($tablename, array('course' => 99), false, true));
2029         $record = $DB->get_record($tablename, array('course' => 99));
2030         $this->assertEquals(2, $record->id);
2031         $this->assertEquals(99, $record->course);
2033         // Check nulls are set properly for all types
2034         $record = new stdClass();
2035         $record->oneint = null;
2036         $record->onenum = null;
2037         $record->onechar = null;
2038         $record->onetext = null;
2039         $record->onebinary = null;
2040         $recid = $DB->insert_record($tablename, $record);
2041         $record = $DB->get_record($tablename, array('id' => $recid));
2042         $this->assertEquals(0, $record->course);
2043         $this->assertNull($record->oneint);
2044         $this->assertNull($record->onenum);
2045         $this->assertNull($record->onechar);
2046         $this->assertNull($record->onetext);
2047         $this->assertNull($record->onebinary);
2049         // Check zeros are set properly for all types
2050         $record = new stdClass();
2051         $record->oneint = 0;
2052         $record->onenum = 0;
2053         $recid = $DB->insert_record($tablename, $record);
2054         $record = $DB->get_record($tablename, array('id' => $recid));
2055         $this->assertEquals(0, $record->oneint);
2056         $this->assertEquals(0, $record->onenum);
2058         // Check booleans are set properly for all types
2059         $record = new stdClass();
2060         $record->oneint = true; // trues
2061         $record->onenum = true;
2062         $record->onechar = true;
2063         $record->onetext = true;
2064         $recid = $DB->insert_record($tablename, $record);
2065         $record = $DB->get_record($tablename, array('id' => $recid));
2066         $this->assertEquals(1, $record->oneint);
2067         $this->assertEquals(1, $record->onenum);
2068         $this->assertEquals(1, $record->onechar);
2069         $this->assertEquals(1, $record->onetext);
2071         $record = new stdClass();
2072         $record->oneint = false; // falses
2073         $record->onenum = false;
2074         $record->onechar = false;
2075         $record->onetext = false;
2076         $recid = $DB->insert_record($tablename, $record);
2077         $record = $DB->get_record($tablename, array('id' => $recid));
2078         $this->assertEquals(0, $record->oneint);
2079         $this->assertEquals(0, $record->onenum);
2080         $this->assertEquals(0, $record->onechar);
2081         $this->assertEquals(0, $record->onetext);
2083         // Check string data causes exception in numeric types
2084         $record = new stdClass();
2085         $record->oneint = 'onestring';
2086         $record->onenum = 0;
2087         try {
2088             $DB->insert_record($tablename, $record);
2089             $this->fail("Expecting an exception, none occurred");
2090         } catch (exception $e) {
2091             $this->assertTrue($e instanceof dml_exception);
2092         }
2093         $record = new stdClass();
2094         $record->oneint = 0;
2095         $record->onenum = 'onestring';
2096         try {
2097             $DB->insert_record($tablename, $record);
2098             $this->fail("Expecting an exception, none occurred");
2099         } catch (exception $e) {
2100             $this->assertTrue($e instanceof dml_exception);
2101         }
2103         // Check empty string data is stored as 0 in numeric datatypes
2104         $record = new stdClass();
2105         $record->oneint = ''; // empty string
2106         $record->onenum = 0;
2107         $recid = $DB->insert_record($tablename, $record);
2108         $record = $DB->get_record($tablename, array('id' => $recid));
2109         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2111         $record = new stdClass();
2112         $record->oneint = 0;
2113         $record->onenum = ''; // empty string
2114         $recid = $DB->insert_record($tablename, $record);
2115         $record = $DB->get_record($tablename, array('id' => $recid));
2116         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2118         // Check empty strings are set properly in string types
2119         $record = new stdClass();
2120         $record->oneint = 0;
2121         $record->onenum = 0;
2122         $record->onechar = '';
2123         $record->onetext = '';
2124         $recid = $DB->insert_record($tablename, $record);
2125         $record = $DB->get_record($tablename, array('id' => $recid));
2126         $this->assertTrue($record->onechar === '');
2127         $this->assertTrue($record->onetext === '');
2129         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2130         $record = new stdClass();
2131         $record->oneint = ((210.10 + 39.92) - 150.02);
2132         $record->onenum = ((210.10 + 39.92) - 150.02);
2133         $recid = $DB->insert_record($tablename, $record);
2134         $record = $DB->get_record($tablename, array('id' => $recid));
2135         $this->assertEquals(100, $record->oneint);
2136         $this->assertEquals(100, $record->onenum);
2138         // Check various quotes/backslashes combinations in string types
2139         $teststrings = array(
2140             'backslashes and quotes alone (even): "" \'\' \\\\',
2141             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2142             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2143             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2144         foreach ($teststrings as $teststring) {
2145             $record = new stdClass();
2146             $record->onechar = $teststring;
2147             $record->onetext = $teststring;
2148             $recid = $DB->insert_record($tablename, $record);
2149             $record = $DB->get_record($tablename, array('id' => $recid));
2150             $this->assertEquals($teststring, $record->onechar);
2151             $this->assertEquals($teststring, $record->onetext);
2152         }
2154         // Check LOBs in text/binary columns
2155         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2156         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2157         $record = new stdClass();
2158         $record->onetext = $clob;
2159         $record->onebinary = $blob;
2160         $recid = $DB->insert_record($tablename, $record);
2161         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2162         $record = $rs->current();
2163         $rs->close();
2164         $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2165         $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2167         // And "small" LOBs too, just in case
2168         $newclob = substr($clob, 0, 500);
2169         $newblob = substr($blob, 0, 250);
2170         $record = new stdClass();
2171         $record->onetext = $newclob;
2172         $record->onebinary = $newblob;
2173         $recid = $DB->insert_record($tablename, $record);
2174         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2175         $record = $rs->current();
2176         $rs->close();
2177         $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2178         $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2179         $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2181         // And "diagnostic" LOBs too, just in case
2182         $newclob = '\'"\\;/ěščřžýáíé';
2183         $newblob = '\'"\\;/ěščřžýáíé';
2184         $record = new stdClass();
2185         $record->onetext = $newclob;
2186         $record->onebinary = $newblob;
2187         $recid = $DB->insert_record($tablename, $record);
2188         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2189         $record = $rs->current();
2190         $rs->close();
2191         $this->assertSame($newclob, $record->onetext);
2192         $this->assertSame($newblob, $record->onebinary);
2193         $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2195         // test data is not modified
2196         $record = new stdClass();
2197         $record->id     = -1; // has to be ignored
2198         $record->course = 3;
2199         $record->lalala = 'lalal'; // unused
2200         $before = clone($record);
2201         $DB->insert_record($tablename, $record);
2202         $this->assertEquals($record, $before);
2204         // make sure the id is always increasing and never reuses the same id
2205         $id1 = $DB->insert_record($tablename, array('course' => 3));
2206         $id2 = $DB->insert_record($tablename, array('course' => 3));
2207         $this->assertTrue($id1 < $id2);
2208         $DB->delete_records($tablename, array('id'=>$id2));
2209         $id3 = $DB->insert_record($tablename, array('course' => 3));
2210         $this->assertTrue($id2 < $id3);
2211         $DB->delete_records($tablename, array());
2212         $id4 = $DB->insert_record($tablename, array('course' => 3));
2213         $this->assertTrue($id3 < $id4);
2215         // Test saving a float in a CHAR column, and reading it back.
2216         $id = $DB->insert_record($tablename, array('onechar' => 1.0));
2217         $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2218         $id = $DB->insert_record($tablename, array('onechar' => 1e20));
2219         $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2220         $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
2221         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2222         $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
2223         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2224         $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
2225         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2226         $id = $DB->insert_record($tablename, array('onechar' => 1e300));
2227         $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2229         // Test saving a float in a TEXT column, and reading it back.
2230         $id = $DB->insert_record($tablename, array('onetext' => 1.0));
2231         $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2232         $id = $DB->insert_record($tablename, array('onetext' => 1e20));
2233         $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2234         $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
2235         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2236         $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
2237         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2238         $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
2239         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2240         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
2241         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2243         // Test that inserting data violating one unique key leads to error.
2244         // Empty the table completely.
2245         $this->assertTrue($DB->delete_records($tablename));
2247         // Add one unique constraint (index).
2248         $key = new xmldb_key('testuk', XMLDB_KEY_UNIQUE, array('course', 'oneint'));
2249         $dbman->add_key($table, $key);
2251         // Let's insert one record violating the constraint multiple times.
2252         $record = (object)array('course' => 1, 'oneint' => 1);
2253         $this->assertTrue($DB->insert_record($tablename, $record, false)); // insert 1st. No problem expected.
2255         // Re-insert same record, not returning id. dml_exception expected.
2256         try {
2257             $DB->insert_record($tablename, $record, false);
2258             $this->fail("Expecting an exception, none occurred");
2259         } catch (exception $e) {
2260             $this->assertTrue($e instanceof dml_exception);
2261         }
2263         // Re-insert same record, returning id. dml_exception expected.
2264         try {
2265             $DB->insert_record($tablename, $record, true);
2266             $this->fail("Expecting an exception, none occurred");
2267         } catch (exception $e) {
2268             $this->assertTrue($e instanceof dml_exception);
2269         }
2270     }
2272     public function test_import_record() {
2273         // All the information in this test is fetched from DB by get_recordset() so we
2274         // have such method properly tested against nulls, empties and friends...
2276         $DB = $this->tdb;
2277         $dbman = $DB->get_manager();
2279         $table = $this->get_test_table();
2280         $tablename = $table->getName();
2282         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2283         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2284         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2285         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2286         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2287         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2288         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2289         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2290         $dbman->create_table($table);
2292         $this->assertSame(1, $DB->insert_record($tablename, array('course' => 1), true));
2293         $record = $DB->get_record($tablename, array('course' => 1));
2294         $this->assertEquals(1, $record->id);
2295         $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied
2296         $this->assertEquals(200, $record->onenum);
2297         $this->assertSame('onestring', $record->onechar);
2298         $this->assertNull($record->onetext);
2299         $this->assertNull($record->onebinary);
2301         // ignore extra columns
2302         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
2303         $before = clone($record);
2304         $this->assertSame(true, $DB->import_record($tablename, $record));
2305         $this->assertEquals($record, $before);
2306         $records = $DB->get_records($tablename);
2307         $this->assertEquals(2, $records[13]->course);
2309         // Check nulls are set properly for all types
2310         $record = new stdClass();
2311         $record->id = 20;
2312         $record->oneint = null;
2313         $record->onenum = null;
2314         $record->onechar = null;
2315         $record->onetext = null;
2316         $record->onebinary = null;
2317         $this->assertTrue($DB->import_record($tablename, $record));
2318         $record = $DB->get_record($tablename, array('id' => 20));
2319         $this->assertEquals(0, $record->course);
2320         $this->assertNull($record->oneint);
2321         $this->assertNull($record->onenum);
2322         $this->assertNull($record->onechar);
2323         $this->assertNull($record->onetext);
2324         $this->assertNull($record->onebinary);
2326         // Check zeros are set properly for all types
2327         $record = new stdClass();
2328         $record->id = 23;
2329         $record->oneint = 0;
2330         $record->onenum = 0;
2331         $this->assertTrue($DB->import_record($tablename, $record));
2332         $record = $DB->get_record($tablename, array('id' => 23));
2333         $this->assertEquals(0, $record->oneint);
2334         $this->assertEquals(0, $record->onenum);
2336         // Check string data causes exception in numeric types
2337         $record = new stdClass();
2338         $record->id = 32;
2339         $record->oneint = 'onestring';
2340         $record->onenum = 0;
2341         try {
2342             $DB->import_record($tablename, $record);
2343             $this->fail("Expecting an exception, none occurred");
2344         } catch (exception $e) {
2345             $this->assertTrue($e instanceof dml_exception);
2346         }
2347         $record = new stdClass();
2348         $record->id = 35;
2349         $record->oneint = 0;
2350         $record->onenum = 'onestring';
2351         try {
2352             $DB->import_record($tablename, $record);
2353             $this->fail("Expecting an exception, none occurred");
2354         } catch (exception $e) {
2355             $this->assertTrue($e instanceof dml_exception);
2356         }
2358         // Check empty strings are set properly in string types
2359         $record = new stdClass();
2360         $record->id = 44;
2361         $record->oneint = 0;
2362         $record->onenum = 0;
2363         $record->onechar = '';
2364         $record->onetext = '';
2365         $this->assertTrue($DB->import_record($tablename, $record));
2366         $record = $DB->get_record($tablename, array('id' => 44));
2367         $this->assertTrue($record->onechar === '');
2368         $this->assertTrue($record->onetext === '');
2370         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2371         $record = new stdClass();
2372         $record->id = 47;
2373         $record->oneint = ((210.10 + 39.92) - 150.02);
2374         $record->onenum = ((210.10 + 39.92) - 150.02);
2375         $this->assertTrue($DB->import_record($tablename, $record));
2376         $record = $DB->get_record($tablename, array('id' => 47));
2377         $this->assertEquals(100, $record->oneint);
2378         $this->assertEquals(100, $record->onenum);
2380         // Check various quotes/backslashes combinations in string types
2381         $i = 50;
2382         $teststrings = array(
2383             'backslashes and quotes alone (even): "" \'\' \\\\',
2384             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2385             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2386             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2387         foreach ($teststrings as $teststring) {
2388             $record = new stdClass();
2389             $record->id = $i;
2390             $record->onechar = $teststring;
2391             $record->onetext = $teststring;
2392             $this->assertTrue($DB->import_record($tablename, $record));
2393             $record = $DB->get_record($tablename, array('id' => $i));
2394             $this->assertEquals($teststring, $record->onechar);
2395             $this->assertEquals($teststring, $record->onetext);
2396             $i = $i + 3;
2397         }
2399         // Check LOBs in text/binary columns
2400         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2401         $record = new stdClass();
2402         $record->id = 70;
2403         $record->onetext = $clob;
2404         $record->onebinary = '';
2405         $this->assertTrue($DB->import_record($tablename, $record));
2406         $rs = $DB->get_recordset($tablename, array('id' => 70));
2407         $record = $rs->current();
2408         $rs->close();
2409         $this->assertEquals($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2411         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2412         $record = new stdClass();
2413         $record->id = 71;
2414         $record->onetext = '';
2415         $record->onebinary = $blob;
2416         $this->assertTrue($DB->import_record($tablename, $record));
2417         $rs = $DB->get_recordset($tablename, array('id' => 71));
2418         $record = $rs->current();
2419         $rs->close();
2420         $this->assertEquals($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2422         // And "small" LOBs too, just in case
2423         $newclob = substr($clob, 0, 500);
2424         $newblob = substr($blob, 0, 250);
2425         $record = new stdClass();
2426         $record->id = 73;
2427         $record->onetext = $newclob;
2428         $record->onebinary = $newblob;
2429         $this->assertTrue($DB->import_record($tablename, $record));
2430         $rs = $DB->get_recordset($tablename, array('id' => 73));
2431         $record = $rs->current();
2432         $rs->close();
2433         $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2434         $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2435         $this->assertEquals(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2436     }
2438     public function test_update_record_raw() {
2439         $DB = $this->tdb;
2440         $dbman = $DB->get_manager();
2442         $table = $this->get_test_table();
2443         $tablename = $table->getName();
2445         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2446         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2447         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2448         $dbman->create_table($table);
2450         $DB->insert_record($tablename, array('course' => 1));
2451         $DB->insert_record($tablename, array('course' => 3));
2453         $record = $DB->get_record($tablename, array('course' => 1));
2454         $record->course = 2;
2455         $this->assertTrue($DB->update_record_raw($tablename, $record));
2456         $this->assertEquals(0, $DB->count_records($tablename, array('course' => 1)));
2457         $this->assertEquals(1, $DB->count_records($tablename, array('course' => 2)));
2458         $this->assertEquals(1, $DB->count_records($tablename, array('course' => 3)));
2460         $record = $DB->get_record($tablename, array('course' => 3));
2461         $record->xxxxx = 2;
2462         try {
2463             $DB->update_record_raw($tablename, $record);
2464             $this->fail("Expecting an exception, none occurred");
2465         } catch (Exception $e) {
2466             $this->assertTrue($e instanceof moodle_exception);
2467         }
2469         $record = $DB->get_record($tablename, array('course' => 3));
2470         unset($record->id);
2471         try {
2472             $DB->update_record_raw($tablename, $record);
2473             $this->fail("Expecting an exception, none occurred");
2474         } catch (Exception $e) {
2475             $this->assertTrue($e instanceof coding_exception);
2476         }
2477     }
2479     public function test_update_record() {
2481         // All the information in this test is fetched from DB by get_record() so we
2482         // have such method properly tested against nulls, empties and friends...
2484         $DB = $this->tdb;
2485         $dbman = $DB->get_manager();
2487         $table = $this->get_test_table();
2488         $tablename = $table->getName();
2490         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2491         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2492         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2493         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2494         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2495         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2496         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2497         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2498         $dbman->create_table($table);
2500         $DB->insert_record($tablename, array('course' => 1));
2501         $record = $DB->get_record($tablename, array('course' => 1));
2502         $record->course = 2;
2504         $this->assertTrue($DB->update_record($tablename, $record));
2505         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
2506         $this->assertNotEmpty($record = $DB->get_record($tablename, array('course' => 2)));
2507         $this->assertEquals(100, $record->oneint); // Just check column defaults have been applied
2508         $this->assertEquals(200, $record->onenum);
2509         $this->assertEquals('onestring', $record->onechar);
2510         $this->assertNull($record->onetext);
2511         $this->assertNull($record->onebinary);
2513         // Check nulls are set properly for all types
2514         $record->oneint = null;
2515         $record->onenum = null;
2516         $record->onechar = null;
2517         $record->onetext = null;
2518         $record->onebinary = null;
2519         $DB->update_record($tablename, $record);
2520         $record = $DB->get_record($tablename, array('course' => 2));
2521         $this->assertNull($record->oneint);
2522         $this->assertNull($record->onenum);
2523         $this->assertNull($record->onechar);
2524         $this->assertNull($record->onetext);
2525         $this->assertNull($record->onebinary);
2527         // Check zeros are set properly for all types
2528         $record->oneint = 0;
2529         $record->onenum = 0;
2530         $DB->update_record($tablename, $record);
2531         $record = $DB->get_record($tablename, array('course' => 2));
2532         $this->assertEquals(0, $record->oneint);
2533         $this->assertEquals(0, $record->onenum);
2535         // Check booleans are set properly for all types
2536         $record->oneint = true; // trues
2537         $record->onenum = true;
2538         $record->onechar = true;
2539         $record->onetext = true;
2540         $DB->update_record($tablename, $record);
2541         $record = $DB->get_record($tablename, array('course' => 2));
2542         $this->assertEquals(1, $record->oneint);
2543         $this->assertEquals(1, $record->onenum);
2544         $this->assertEquals(1, $record->onechar);
2545         $this->assertEquals(1, $record->onetext);
2547         $record->oneint = false; // falses
2548         $record->onenum = false;
2549         $record->onechar = false;
2550         $record->onetext = false;
2551         $DB->update_record($tablename, $record);
2552         $record = $DB->get_record($tablename, array('course' => 2));
2553         $this->assertEquals(0, $record->oneint);
2554         $this->assertEquals(0, $record->onenum);
2555         $this->assertEquals(0, $record->onechar);
2556         $this->assertEquals(0, $record->onetext);
2558         // Check string data causes exception in numeric types
2559         $record->oneint = 'onestring';
2560         $record->onenum = 0;
2561         try {
2562             $DB->update_record($tablename, $record);
2563             $this->fail("Expecting an exception, none occurred");
2564         } catch (exception $e) {
2565             $this->assertTrue($e instanceof dml_exception);
2566         }
2567         $record->oneint = 0;
2568         $record->onenum = 'onestring';
2569         try {
2570             $DB->update_record($tablename, $record);
2571             $this->fail("Expecting an exception, none occurred");
2572         } catch (exception $e) {
2573             $this->assertTrue($e instanceof dml_exception);
2574         }
2576         // Check empty string data is stored as 0 in numeric datatypes
2577         $record->oneint = ''; // empty string
2578         $record->onenum = 0;
2579         $DB->update_record($tablename, $record);
2580         $record = $DB->get_record($tablename, array('course' => 2));
2581         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2583         $record->oneint = 0;
2584         $record->onenum = ''; // empty string
2585         $DB->update_record($tablename, $record);
2586         $record = $DB->get_record($tablename, array('course' => 2));
2587         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2589         // Check empty strings are set properly in string types
2590         $record->oneint = 0;
2591         $record->onenum = 0;
2592         $record->onechar = '';
2593         $record->onetext = '';
2594         $DB->update_record($tablename, $record);
2595         $record = $DB->get_record($tablename, array('course' => 2));
2596         $this->assertTrue($record->onechar === '');
2597         $this->assertTrue($record->onetext === '');
2599         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2600         $record->oneint = ((210.10 + 39.92) - 150.02);
2601         $record->onenum = ((210.10 + 39.92) - 150.02);
2602         $DB->update_record($tablename, $record);
2603         $record = $DB->get_record($tablename, array('course' => 2));
2604         $this->assertEquals(100, $record->oneint);
2605         $this->assertEquals(100, $record->onenum);
2607         // Check various quotes/backslashes combinations in string types
2608         $teststrings = array(
2609             'backslashes and quotes alone (even): "" \'\' \\\\',
2610             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2611             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2612             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2613         foreach ($teststrings as $teststring) {
2614             $record->onechar = $teststring;
2615             $record->onetext = $teststring;
2616             $DB->update_record($tablename, $record);
2617             $record = $DB->get_record($tablename, array('course' => 2));
2618             $this->assertEquals($teststring, $record->onechar);
2619             $this->assertEquals($teststring, $record->onetext);
2620         }
2622         // Check LOBs in text/binary columns
2623         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2624         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2625         $record->onetext = $clob;
2626         $record->onebinary = $blob;
2627         $DB->update_record($tablename, $record);
2628         $record = $DB->get_record($tablename, array('course' => 2));
2629         $this->assertEquals($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2630         $this->assertEquals($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2632         // And "small" LOBs too, just in case
2633         $newclob = substr($clob, 0, 500);
2634         $newblob = substr($blob, 0, 250);
2635         $record->onetext = $newclob;
2636         $record->onebinary = $newblob;
2637         $DB->update_record($tablename, $record);
2638         $record = $DB->get_record($tablename, array('course' => 2));
2639         $this->assertEquals($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2640         $this->assertEquals($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2642         // Test saving a float in a CHAR column, and reading it back.
2643         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2644         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
2645         $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2646         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
2647         $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2648         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
2649         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2650         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
2651         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2652         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
2653         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2654         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
2655         $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2657         // Test saving a float in a TEXT column, and reading it back.
2658         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2659         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
2660         $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2661         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
2662         $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2663         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
2664         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2665         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
2666         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2667         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
2668         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2669         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
2670         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2671     }
2673     public function test_set_field() {
2674         $DB = $this->tdb;
2675         $dbman = $DB->get_manager();
2677         $table = $this->get_test_table();
2678         $tablename = $table->getName();
2680         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2681         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2682         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2683         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2684         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2685         $dbman->create_table($table);
2687         // simple set_field
2688         $id1 = $DB->insert_record($tablename, array('course' => 1));
2689         $id2 = $DB->insert_record($tablename, array('course' => 1));
2690         $id3 = $DB->insert_record($tablename, array('course' => 3));
2691         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2692         $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2693         $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2694         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2695         $DB->delete_records($tablename, array());
2697         // multiple fields affected
2698         $id1 = $DB->insert_record($tablename, array('course' => 1));
2699         $id2 = $DB->insert_record($tablename, array('course' => 1));
2700         $id3 = $DB->insert_record($tablename, array('course' => 3));
2701         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2702         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2703         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2704         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2705         $DB->delete_records($tablename, array());
2707         // no field affected
2708         $id1 = $DB->insert_record($tablename, array('course' => 1));
2709         $id2 = $DB->insert_record($tablename, array('course' => 1));
2710         $id3 = $DB->insert_record($tablename, array('course' => 3));
2711         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2712         $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2713         $this->assertEquals(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2714         $this->assertEquals(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2715         $DB->delete_records($tablename, array());
2717         // all fields - no condition
2718         $id1 = $DB->insert_record($tablename, array('course' => 1));
2719         $id2 = $DB->insert_record($tablename, array('course' => 1));
2720         $id3 = $DB->insert_record($tablename, array('course' => 3));
2721         $DB->set_field($tablename, 'course', 5, array());
2722         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2723         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2724         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2726         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2727         $conditions = array('onetext' => '1');
2728         try {
2729             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2730             if (debugging()) {
2731                 // only in debug mode - hopefully all devs test code in debug mode...
2732                 $this->fail('An Exception is missing, expected due to equating of text fields');
2733             }
2734         } catch (exception $e) {
2735             $this->assertTrue($e instanceof dml_exception);
2736             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
2737         }
2739         // Test saving a float in a CHAR column, and reading it back.
2740         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2741         $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
2742         $this->assertEquals(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2743         $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
2744         $this->assertEquals(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2745         $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
2746         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2747         $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
2748         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2749         $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
2750         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2751         $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
2752         $this->assertEquals(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2754         // Test saving a float in a TEXT column, and reading it back.
2755         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2756         $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
2757         $this->assertEquals(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2758         $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
2759         $this->assertEquals(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2760         $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
2761         $this->assertEquals(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2762         $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
2763         $this->assertEquals(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2764         $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
2765         $this->assertEquals(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2766         $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
2767         $this->assertEquals(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2769         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2770         // go to set_field_select() because set_field() is just one wrapper over it
2771     }
2773     public function test_set_field_select() {
2775         // All the information in this test is fetched from DB by get_field() so we
2776         // have such method properly tested against nulls, empties and friends...
2778         $DB = $this->tdb;
2779         $dbman = $DB->get_manager();
2781         $table = $this->get_test_table();
2782         $tablename = $table->getName();
2784         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2785         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2786         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2787         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2788         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2789         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2790         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2791         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2792         $dbman->create_table($table);
2794         $DB->insert_record($tablename, array('course' => 1));
2796         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2797         $this->assertEquals(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2799         // Check nulls are set properly for all types
2800         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2801         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2802         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2803         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2804         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2805         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2806         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2807         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2808         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2809         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2811         // Check zeros are set properly for all types
2812         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2813         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2814         $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2815         $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2817         // Check booleans are set properly for all types
2818         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2819         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2820         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2821         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2822         $this->assertEquals(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2823         $this->assertEquals(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2824         $this->assertEquals(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2825         $this->assertEquals(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2827         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2828         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2829         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2830         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2831         $this->assertEquals(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2832         $this->assertEquals(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2833         $this->assertEquals(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2834         $this->assertEquals(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2836         // Check string data causes exception in numeric types
2837         try {
2838             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2839             $this->fail("Expecting an exception, none occurred");
2840         } catch (exception $e) {
2841             $this->assertTrue($e instanceof dml_exception);
2842         }
2843         try {
2844             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2845             $this->fail("Expecting an exception, none occurred");
2846         } catch (exception $e) {
2847             $this->assertTrue($e instanceof dml_exception);
2848         }
2850         // Check empty string data is stored as 0 in numeric datatypes
2851         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2852         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2853         $this->assertTrue(is_numeric($field) && $field == 0);
2855         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2856         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2857         $this->assertTrue(is_numeric($field) && $field == 0);
2859         // Check empty strings are set properly in string types
2860         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2861         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2862         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2863         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2865         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2866         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2867         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2868         $this->assertEquals(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2869         $this->assertEquals(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2871         // Check various quotes/backslashes combinations in string types
2872         $teststrings = array(
2873             'backslashes and quotes alone (even): "" \'\' \\\\',
2874             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2875             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2876             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2877         foreach ($teststrings as $teststring) {
2878             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2879             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2880             $this->assertEquals($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2881             $this->assertEquals($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2882         }
2884         // Check LOBs in text/binary columns
2885         $clob = file_get_contents(__DIR__ . '/fixtures/clob.txt');
2886         $blob = file_get_contents(__DIR__ . '/fixtures/randombinary');
2887         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2888         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2889         $this->assertEquals($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2890         $this->assertEquals($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2892         // And "small" LOBs too, just in case
2893         $newclob = substr($clob, 0, 500);
2894         $newblob = substr($blob, 0, 250);
2895         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2896         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2897         $this->assertEquals($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2898         $this->assertEquals($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2900         // This is the failure from MDL-24863. This was giving an error on MSSQL,
2901         // which converts the '1' to an integer, which cannot then be compared with
2902         // onetext cast to a varchar. This should be fixed and working now.
2903         $newchar = 'frog';
2904         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2905         $params = array('onetext' => '1');
2906         try {
2907             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
2908             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
2909         } catch (dml_exception $e) {
2910             $this->assertFalse(true, 'We have an unexpected exception.');
2911             throw $e;
2912         }
2915     }
2917     public function test_count_records() {
2918         $DB = $this->tdb;
2920         $dbman = $DB->get_manager();
2922         $table = $this->get_test_table();
2923         $tablename = $table->getName();
2925         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2926         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2927         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2928         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2929         $dbman->create_table($table);
2931         $this->assertSame(0, $DB->count_records($tablename));
2933         $DB->insert_record($tablename, array('course' => 3));
2934         $DB->insert_record($tablename, array('course' => 4));
2935         $DB->insert_record($tablename, array('course' => 5));
2937         $this->assertSame(3, $DB->count_records($tablename));
2939         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2940         $conditions = array('onetext' => '1');
2941         try {
2942             $DB->count_records($tablename, $conditions);
2943             if (debugging()) {
2944                 // only in debug mode - hopefully all devs test code in debug mode...
2945                 $this->fail('An Exception is missing, expected due to equating of text fields');
2946             }
2947         } catch (exception $e) {
2948             $this->assertTrue($e instanceof dml_exception);
2949             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
2950         }
2951     }
2953     public function test_count_records_select() {
2954         $DB = $this->tdb;
2956         $dbman = $DB->get_manager();
2958         $table = $this->get_test_table();
2959         $tablename = $table->getName();
2961         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2962         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2963         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2964         $dbman->create_table($table);
2966         $this->assertSame(0, $DB->count_records($tablename));
2968         $DB->insert_record($tablename, array('course' => 3));
2969         $DB->insert_record($tablename, array('course' => 4));
2970         $DB->insert_record($tablename, array('course' => 5));
2972         $this->assertSame(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2973     }
2975     public function test_count_records_sql() {
2976         $DB = $this->tdb;
2977         $dbman = $DB->get_manager();
2979         $table = $this->get_test_table();
2980         $tablename = $table->getName();
2982         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2983         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2984         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2985         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2986         $dbman->create_table($table);
2988         $this->assertSame(0, $DB->count_records($tablename));
2990         $DB->insert_record($tablename, array('course' => 3, 'onechar' => 'a'));
2991         $DB->insert_record($tablename, array('course' => 4, 'onechar' => 'b'));
2992         $DB->insert_record($tablename, array('course' => 5, 'onechar' => 'c'));
2994         $this->assertSame(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2996         // test invalid use
2997         try {
2998             $DB->count_records_sql("SELECT onechar FROM {{$tablename}} WHERE course = ?", array(3));
2999             $this->fail('Exception expected when non-number field used in count_records_sql');
3000         } catch (exception $e) {
3001             $this->assertInstanceOf('coding_exception', $e);
3002         }
3004         try {
3005             $DB->count_records_sql("SELECT course FROM {{$tablename}} WHERE 1 = 2");
3006             $this->fail('Exception expected when non-number field used in count_records_sql');
3007         } catch (exception $e) {
3008             $this->assertInstanceOf('coding_exception', $e);
3009         }
3010     }
3012     public function test_record_exists() {
3013         $DB = $this->tdb;
3014         $dbman = $DB->get_manager();
3016         $table = $this->get_test_table();
3017         $tablename = $table->getName();
3019         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3020         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3021         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
3022         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3023         $dbman->create_table($table);
3025         $this->assertEquals(0, $DB->count_records($tablename));
3027         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
3028         $DB->insert_record($tablename, array('course' => 3));
3030         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
3033         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
3034         $conditions = array('onetext' => '1');
3035         try {
3036             $DB->record_exists($tablename, $conditions);
3037             if (debugging()) {
3038                 // only in debug mode - hopefully all devs test code in debug mode...
3039                 $this->fail('An Exception is missing, expected due to equating of text fields');
3040             }
3041         } catch (exception $e) {
3042             $this->assertTrue($e instanceof dml_exception);
3043             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
3044         }
3045     }
3047     public function test_record_exists_select() {
3048         $DB = $this->tdb;
3049         $dbman = $DB->get_manager();
3051         $table = $this->get_test_table();
3052         $tablename = $table->getName();
3054         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3055         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3056         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3057         $dbman->create_table($table);
3059         $this->assertEquals(0, $DB->count_records($tablename));
3061         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
3062         $DB->insert_record($tablename, array('course' => 3));
3064         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
3065     }
3067     public function test_record_exists_sql() {
3068         $DB = $this->tdb;
3069         $dbman = $DB->get_manager();
3071         $table = $this->get_test_table();
3072         $tablename = $table->getName();
3074         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3075         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3076         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3077         $dbman->create_table($table);
3079         $this->assertEquals(0, $DB->count_records($tablename));
3081         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
3082         $DB->insert_record($tablename, array('course' => 3));
3084         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
3085     }
3087     public function test_recordset_locks_delete() {
3088         $DB = $this->tdb;
3089         $dbman = $DB->get_manager();
3091         //Setup
3092         $table = $this->get_test_table();
3093         $tablename = $table->getName();
3095         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3096         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3097         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3098         $dbman->create_table($table);
3100         $DB->insert_record($tablename, array('course' => 1));
3101         $DB->insert_record($tablename, array('course' => 2));
3102         $DB->insert_record($tablename, array('course' => 3));
3103         $DB->insert_record($tablename, array('course' => 4));
3104         $DB->insert_record($tablename, array('course' => 5));
3105         $DB->insert_record($tablename, array('course' => 6));
3107         // Test against db write locking while on an open recordset
3108         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
3109         foreach ($rs as $record) {
3110             $cid = $record->course;
3111             $DB->delete_records($tablename, array('course' => $cid));
3112             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
3113         }
3114         $rs->close();
3116         $this->assertEquals(4, $DB->count_records($tablename, array()));
3117     }
3119     public function test_recordset_locks_update() {
3120         $DB = $this->tdb;
3121         $dbman = $DB->get_manager();
3123         //Setup
3124         $table = $this->get_test_table();
3125         $tablename = $table->getName();
3127         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3128         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3129         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3130         $dbman->create_table($table);
3132         $DB->insert_record($tablename, array('course' => 1));
3133         $DB->insert_record($tablename, array('course' => 2));
3134         $DB->insert_record($tablename, array('course' => 3));
3135         $DB->insert_record($tablename, array('course' => 4));
3136         $DB->insert_record($tablename, array('course' => 5));
3137         $DB->insert_record($tablename, array('course' => 6));
3139         // Test against db write locking while on an open recordset
3140         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
3141         foreach ($rs as $record) {
3142             $cid = $record->course;
3143             $DB->set_field($tablename, 'course', 10, array('course' => $cid));
3144             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
3145         }
3146         $rs->close();
3148         $this->assertEquals(2, $DB->count_records($tablename, array('course' => 10)));
3149     }
3151     public function test_delete_records() {
3152         $DB = $this->tdb;
3153         $dbman = $DB->get_manager();
3155         $table = $this->get_test_table();
3156         $tablename = $table->getName();
3158         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3159         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3160         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
3161         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3162         $dbman->create_table($table);
3164         $DB->insert_record($tablename, array('course' => 3));
3165         $DB->insert_record($tablename, array('course' => 2));
3166         $DB->insert_record($tablename, array('course' => 2));
3168         // Delete all records
3169         $this->assertTrue($DB->delete_records($tablename));
3170         $this->assertEquals(0, $DB->count_records($tablename));
3172         // Delete subset of records
3173         $DB->insert_record($tablename, array('course' => 3));
3174         $DB->insert_record($tablename, array('course' => 2));
3175         $DB->insert_record($tablename, array('course' => 2));
3177         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
3178         $this->assertEquals(1, $DB->count_records($tablename));
3180         // delete all
3181         $this->assertTrue($DB->delete_records($tablename, array()));
3182         $this->assertEquals(0, $DB->count_records($tablename));
3184         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
3185         $conditions = array('onetext'=>'1');
3186         try {
3187             $DB->delete_records($tablename, $conditions);
3188             if (debugging()) {
3189                 // only in debug mode - hopefully all devs test code in debug mode...
3190                 $this->fail('An Exception is missing, expected due to equating of text fields');
3191             }
3192         } catch (exception $e) {
3193             $this->assertTrue($e instanceof dml_exception);
3194             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
3195         }
3197         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
3198         $conditions = array('onetext' => 1);
3199         try {
3200             $DB->delete_records($tablename, $conditions);
3201             if (debugging()) {
3202                 // only in debug mode - hopefully all devs test code in debug mode...
3203                 $this->fail('An Exception is missing, expected due to equating of text fields');
3204             }
3205         } catch (exception $e) {
3206             $this->assertTrue($e instanceof dml_exception);
3207             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
3208         }
3209     }
3211     public function test_delete_records_select() {
3212         $DB = $this->tdb;
3213         $dbman = $DB->get_manager();
3215         $table = $this->get_test_table();
3216         $tablename = $table->getName();
3218         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3219         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3220         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3221         $dbman->create_table($table);
3223         $DB->insert_record($tablename, array('course' => 3));
3224         $DB->insert_record($tablename, array('course' => 2));
3225         $DB->insert_record($tablename, array('course' => 2));
3227         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
3228         $this->assertEquals(1, $DB->count_records($tablename));
3229     }
3231     public function test_delete_records_list() {
3232         $DB = $this->tdb;
3233         $dbman = $DB->get_manager();
3235         $table = $this->get_test_table();
3236         $tablename = $table->getName();
3238         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3239         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3240         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3241         $dbman->create_table($table);
3243         $DB->insert_record($tablename, array('course' => 1));
3244         $DB->insert_record($tablename, array('course' => 2));
3245         $DB->insert_record($tablename, array('course' => 3));
3247         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
3248         $this->assertEquals(1, $DB->count_records($tablename));
3250         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); // Must delete 0 rows without conditions. MDL-17645
3251         $this->assertEquals(1, $DB->count_records($tablename));
3252     }
3254     public function test_object_params() {
3255         $DB = $this->tdb;
3256         $dbman = $DB->get_manager();
3258         $table = $this->get_test_table();
3259         $tablename = $table->getName();
3260         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3261         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3262         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3263         $dbman->create_table($table);
3265         $o = new stdClass(); // objects without __toString - never worked
3266         try {
3267             $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
3268             $this->fail('coding_exception expected');
3269         } catch (Exception $e) {
3270             $this->assertTrue($e instanceof coding_exception);
3271         }
3273         // objects with __toString() forbidden everywhere since 2.3
3274         $o = new dml_test_object_one();
3275         try {
3276             $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
3277             $this->fail('coding_exception expected');
3278         } catch (Exception $e) {
3279             $this->assertTrue($e instanceof coding_exception);
3280         }
3282         try {
3283             $DB->execute("SELECT {{$tablename}} WHERE course = ? ", array($o));
3284             $this->fail('coding_exception expected');
3285         } catch (Exception $e) {
3286             $this->assertTrue($e instanceof coding_exception);
3287         }
3289         try {
3290             $DB->get_recordset_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
3291             $this->fail('coding_exception expected');
3292         } catch (Exception $e) {
3293             $this->assertTrue($e instanceof coding_exception);
3294         }
3296         try {
3297             $DB->get_records_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
3298             $this->fail('coding_exception expected');
3299         } catch (Exception $e) {
3300             $this->assertTrue($e instanceof coding_exception);
3301         }
3303         try {