MDL-29894 forbid objects in DML parameters
[moodle.git] / lib / dml / simpletest / testdml.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package    core
20  * @subpackage dml
21  * @copyright  2008 Petr Skoda (http://skodak.org)
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 class dml_test extends UnitTestCase {
28     private $tables = array();
29     /** @var moodle_database */
30     private $tdb;
31     private $data;
32     public  static $includecoverage = array('lib/dml');
33     public  static $excludecoverage = array('lib/dml/simpletest');
35     protected $olddebug;
36     protected $olddisplay;
38     function setUp() {
39         global $DB, $UNITTEST;
41         if (isset($UNITTEST->func_test_db)) {
42             $this->tdb = $UNITTEST->func_test_db;
43         } else {
44             $this->tdb = $DB;
45         }
46     }
48     function tearDown() {
49         $dbman = $this->tdb->get_manager();
51         foreach ($this->tables as $tablename) {
52             if ($dbman->table_exists($tablename)) {
53                 $table = new xmldb_table($tablename);
54                 $dbman->drop_table($table);
55             }
56         }
57         $this->tables = array();
58     }
60     /**
61      * Get a xmldb_table object for testing, deleting any existing table
62      * of the same name, for example if one was left over from a previous test
63      * run that crashed.
64      *
65      * @param database_manager $dbman the database_manager to use.
66      * @param string $suffix table name suffix, use if you need more test tables
67      * @return xmldb_table the table object.
68      */
69     private function get_test_table($suffix = '') {
70         $dbman = $this->tdb->get_manager();
72         $tablename = "unit_table";
73         if ($suffix !== '') {
74             $tablename .= $suffix;
75         }
77         $table = new xmldb_table($tablename);
78         if ($dbman->table_exists($table)) {
79             $dbman->drop_table($table);
80         }
81         $table->setComment("This is a test'n drop table. You can drop it safely");
82         $this->tables[$tablename] = $tablename;
83         return new xmldb_table($tablename);
84     }
86     protected function enable_debugging() {
87         global $CFG;
89         $this->olddebug   = $CFG->debug;       // Save current debug settings
90         $this->olddisplay = $CFG->debugdisplay;
91         $CFG->debug = DEBUG_DEVELOPER;
92         $CFG->debugdisplay = true;
93         ob_start(); // hide debug warning
95     }
97     protected function get_debugging() {
98         global $CFG;
100         $debuginfo = ob_get_contents();
101         ob_end_clean();
102         $CFG->debug = $this->olddebug;         // Restore original debug settings
103         $CFG->debugdisplay = $this->olddisplay;
105         return $debuginfo;
106     }
108     // NOTE: please keep order of test methods here matching the order of moodle_database class methods
110     function test_diagnose() {
111         $DB = $this->tdb;
112         $result = $DB->diagnose();
113         $this->assertNull($result, 'Database self diagnostics failed %s');
114     }
116     function test_get_server_info() {
117         $DB = $this->tdb;
118         $result = $DB->get_server_info();
119         $this->assertTrue(is_array($result));
120         $this->assertTrue(array_key_exists('description', $result));
121         $this->assertTrue(array_key_exists('version', $result));
122     }
124     public function test_get_in_or_equal() {
125         $DB = $this->tdb;
127         // SQL_PARAMS_QM - IN or =
129         // Correct usage of multiple values
130         $in_values = array('value1', 'value2', '3', 4, null, false, true);
131         list($usql, $params) = $DB->get_in_or_equal($in_values);
132         $this->assertEqual('IN ('.implode(',',array_fill(0, count($in_values), '?')).')', $usql);
133         $this->assertEqual(count($in_values), count($params));
134         foreach ($params as $key => $value) {
135             $this->assertIdentical($in_values[$key], $value);
136         }
138         // Correct usage of single value (in an array)
139         $in_values = array('value1');
140         list($usql, $params) = $DB->get_in_or_equal($in_values);
141         $this->assertEqual("= ?", $usql);
142         $this->assertEqual(1, count($params));
143         $this->assertEqual($in_values[0], $params[0]);
145         // Correct usage of single value
146         $in_value = 'value1';
147         list($usql, $params) = $DB->get_in_or_equal($in_values);
148         $this->assertEqual("= ?", $usql);
149         $this->assertEqual(1, count($params));
150         $this->assertEqual($in_value, $params[0]);
152         // SQL_PARAMS_QM - NOT IN or <>
154         // Correct usage of multiple values
155         $in_values = array('value1', 'value2', 'value3', 'value4');
156         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
157         $this->assertEqual("NOT IN (?,?,?,?)", $usql);
158         $this->assertEqual(4, count($params));
159         foreach ($params as $key => $value) {
160             $this->assertEqual($in_values[$key], $value);
161         }
163         // Correct usage of single value (in array()
164         $in_values = array('value1');
165         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
166         $this->assertEqual("<> ?", $usql);
167         $this->assertEqual(1, count($params));
168         $this->assertEqual($in_values[0], $params[0]);
170         // Correct usage of single value
171         $in_value = 'value1';
172         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, null, false);
173         $this->assertEqual("<> ?", $usql);
174         $this->assertEqual(1, count($params));
175         $this->assertEqual($in_value, $params[0]);
177         // SQL_PARAMS_NAMED - IN or =
179         // Correct usage of multiple values
180         $in_values = array('value1', 'value2', 'value3', 'value4');
181         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
182         $this->assertEqual(4, count($params));
183         reset($in_values);
184         $ps = array();
185         foreach ($params as $key => $value) {
186             $this->assertEqual(current($in_values), $value);
187             next($in_values);
188             $ps[] = ':'.$key;
189         }
190         $this->assertEqual("IN (".implode(',', $ps).")", $usql);
192         // Correct usage of single values (in array)
193         $in_values = array('value1');
194         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
195         $this->assertEqual(1, count($params));
196         $value = reset($params);
197         $key = key($params);
198         $this->assertEqual("= :$key", $usql);
199         $this->assertEqual($in_value, $value);
201         // Correct usage of single value
202         $in_value = 'value1';
203         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', true);
204         $this->assertEqual(1, count($params));
205         $value = reset($params);
206         $key = key($params);
207         $this->assertEqual("= :$key", $usql);
208         $this->assertEqual($in_value, $value);
210         // SQL_PARAMS_NAMED - NOT IN or <>
212         // Correct usage of multiple values
213         $in_values = array('value1', 'value2', 'value3', 'value4');
214         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
215         $this->assertEqual(4, count($params));
216         reset($in_values);
217         $ps = array();
218         foreach ($params as $key => $value) {
219             $this->assertEqual(current($in_values), $value);
220             next($in_values);
221             $ps[] = ':'.$key;
222         }
223         $this->assertEqual("NOT IN (".implode(',', $ps).")", $usql);
225         // Correct usage of single values (in array)
226         $in_values = array('value1');
227         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
228         $this->assertEqual(1, count($params));
229         $value = reset($params);
230         $key = key($params);
231         $this->assertEqual("<> :$key", $usql);
232         $this->assertEqual($in_value, $value);
234         // Correct usage of single value
235         $in_value = 'value1';
236         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
237         $this->assertEqual(1, count($params));
238         $value = reset($params);
239         $key = key($params);
240         $this->assertEqual("<> :$key", $usql);
241         $this->assertEqual($in_value, $value);
243         // make sure the param names are unique
244         list($usql1, $params1) = $DB->get_in_or_equal(array(1,2,3), SQL_PARAMS_NAMED, 'param');
245         list($usql2, $params2) = $DB->get_in_or_equal(array(1,2,3), SQL_PARAMS_NAMED, 'param');
246         $params1 = array_keys($params1);
247         $params2 = array_keys($params2);
248         $common = array_intersect($params1, $params2);
249         $this->assertEqual(count($common), 0);
251         // Some incorrect tests
253         // Incorrect usage passing not-allowed params type
254         $in_values = array(1, 2, 3);
255         try {
256             list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param', false);
257             $this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
258         } catch (exception $e) {
259             $this->assertTrue($e instanceof dml_exception);
260             $this->assertEqual($e->errorcode, 'typenotimplement');
261         }
263         // Incorrect usage passing empty array
264         $in_values = array();
265         try {
266             list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false);
267             $this->fail('An Exception is missing, expected due to empty array of items');
268         } catch (exception $e) {
269             $this->assertTrue($e instanceof coding_exception);
270         }
272         // Test using $onemptyitems
274         // Correct usage passing empty array and $onemptyitems = NULL (equal = true, QM)
275         $in_values = array();
276         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, NULL);
277         $this->assertEqual(' IS NULL', $usql);
278         $this->assertIdentical(array(), $params);
280         // Correct usage passing empty array and $onemptyitems = NULL (equal = false, NAMED)
281         $in_values = array();
282         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, NULL);
283         $this->assertEqual(' IS NOT NULL', $usql);
284         $this->assertIdentical(array(), $params);
286         // Correct usage passing empty array and $onemptyitems = true (equal = true, QM)
287         $in_values = array();
288         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, true);
289         $this->assertEqual('= ?', $usql);
290         $this->assertIdentical(array(true), $params);
292         // Correct usage passing empty array and $onemptyitems = true (equal = false, NAMED)
293         $in_values = array();
294         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, true);
295         $this->assertEqual(1, count($params));
296         $value = reset($params);
297         $key = key($params);
298         $this->assertEqual('<> :'.$key, $usql);
299         $this->assertIdentical($value, true);
301         // Correct usage passing empty array and $onemptyitems = -1 (equal = true, QM)
302         $in_values = array();
303         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, -1);
304         $this->assertEqual('= ?', $usql);
305         $this->assertIdentical(array(-1), $params);
307         // Correct usage passing empty array and $onemptyitems = -1 (equal = false, NAMED)
308         $in_values = array();
309         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, -1);
310         $this->assertEqual(1, count($params));
311         $value = reset($params);
312         $key = key($params);
313         $this->assertEqual('<> :'.$key, $usql);
314         $this->assertIdentical($value, -1);
316         // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = true, QM)
317         $in_values = array();
318         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param', true, 'onevalue');
319         $this->assertEqual('= ?', $usql);
320         $this->assertIdentical(array('onevalue'), $params);
322         // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = false, NAMED)
323         $in_values = array();
324         list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param', false, 'onevalue');
325         $this->assertEqual(1, count($params));
326         $value = reset($params);
327         $key = key($params);
328         $this->assertEqual('<> :'.$key, $usql);
329         $this->assertIdentical($value, 'onevalue');
330     }
332     public function test_fix_table_names() {
333         $DB = new moodle_database_for_testing();
334         $prefix = $DB->get_prefix();
336         // Simple placeholder
337         $placeholder = "{user_123}";
338         $this->assertIdentical($prefix."user_123", $DB->public_fix_table_names($placeholder));
340         // wrong table name
341         $placeholder = "{user-a}";
342         $this->assertIdentical($placeholder, $DB->public_fix_table_names($placeholder));
344         // wrong table name
345         $placeholder = "{123user}";
346         $this->assertIdentical($placeholder, $DB->public_fix_table_names($placeholder));
348         // Full SQL
349         $sql = "SELECT * FROM {user}, {funny_table_name}, {mdl_stupid_table} WHERE {user}.id = {funny_table_name}.userid";
350         $expected = "SELECT * FROM {$prefix}user, {$prefix}funny_table_name, {$prefix}mdl_stupid_table WHERE {$prefix}user.id = {$prefix}funny_table_name.userid";
351         $this->assertIdentical($expected, $DB->public_fix_table_names($sql));
352     }
354     function test_fix_sql_params() {
355         $DB = $this->tdb;
357         $table = $this->get_test_table();
358         $tablename = $table->getName();
360         // Correct table placeholder substitution
361         $sql = "SELECT * FROM {{$tablename}}";
362         $sqlarray = $DB->fix_sql_params($sql);
363         $this->assertEqual("SELECT * FROM {$DB->get_prefix()}".$tablename, $sqlarray[0]);
365         // Conversions of all param types
366         $sql = array();
367         $sql[SQL_PARAMS_NAMED]  = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = :param1, course = :param2";
368         $sql[SQL_PARAMS_QM]     = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = ?, course = ?";
369         $sql[SQL_PARAMS_DOLLAR] = "SELECT * FROM {$DB->get_prefix()}testtable WHERE name = \$1, course = \$2";
371         $params = array();
372         $params[SQL_PARAMS_NAMED]  = array('param1'=>'first record', 'param2'=>1);
373         $params[SQL_PARAMS_QM]     = array('first record', 1);
374         $params[SQL_PARAMS_DOLLAR] = array('first record', 1);
376         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_NAMED], $params[SQL_PARAMS_NAMED]);
377         $this->assertIdentical($rsql, $sql[$rtype]);
378         $this->assertIdentical($rparams, $params[$rtype]);
380         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_QM], $params[SQL_PARAMS_QM]);
381         $this->assertIdentical($rsql, $sql[$rtype]);
382         $this->assertIdentical($rparams, $params[$rtype]);
384         list($rsql, $rparams, $rtype) = $DB->fix_sql_params($sql[SQL_PARAMS_DOLLAR], $params[SQL_PARAMS_DOLLAR]);
385         $this->assertIdentical($rsql, $sql[$rtype]);
386         $this->assertIdentical($rparams, $params[$rtype]);
389         // Malformed table placeholder
390         $sql = "SELECT * FROM [testtable]";
391         $sqlarray = $DB->fix_sql_params($sql);
392         $this->assertIdentical($sql, $sqlarray[0]);
395         // Mixed param types (colon and dollar)
396         $sql = "SELECT * FROM {{$tablename}} WHERE name = :param1, course = \$1";
397         $params = array('param1' => 'record1', 'param2' => 3);
398         try {
399             $DB->fix_sql_params($sql, $params);
400             $this->fail("Expecting an exception, none occurred");
401         } catch (Exception $e) {
402             $this->assertTrue($e instanceof dml_exception);
403         }
405         // Mixed param types (question and dollar)
406         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = \$1";
407         $params = array('param1' => 'record2', 'param2' => 5);
408         try {
409             $DB->fix_sql_params($sql, $params);
410             $this->fail("Expecting an exception, none occurred");
411         } catch (Exception $e) {
412             $this->assertTrue($e instanceof dml_exception);
413         }
415         // Too few params in sql
416         $sql = "SELECT * FROM {{$tablename}} WHERE name = ?, course = ?, id = ?";
417         $params = array('record2', 3);
418         try {
419             $DB->fix_sql_params($sql, $params);
420             $this->fail("Expecting an exception, none occurred");
421         } catch (Exception $e) {
422             $this->assertTrue($e instanceof dml_exception);
423         }
425         // Too many params in array: no error, just use what is necessary
426         $params[] = 1;
427         $params[] = time();
428         try {
429             $sqlarray = $DB->fix_sql_params($sql, $params);
430             $this->assertTrue(is_array($sqlarray));
431             $this->assertEqual(count($sqlarray[1]), 3);
432         } catch (Exception $e) {
433             $this->fail("Unexpected ".get_class($e)." exception");
434         }
436         // Named params missing from array
437         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
438         $params = array('wrongname' => 'record1', 'course' => 1);
439         try {
440             $DB->fix_sql_params($sql, $params);
441             $this->fail("Expecting an exception, none occurred");
442         } catch (Exception $e) {
443             $this->assertTrue($e instanceof dml_exception);
444         }
446         // Duplicate named param in query - this is a very important feature!!
447         // it helps with debugging of sloppy code
448         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :name";
449         $params = array('name' => 'record2', 'course' => 3);
450         try {
451             $DB->fix_sql_params($sql, $params);
452             $this->fail("Expecting an exception, none occurred");
453         } catch (Exception $e) {
454             $this->assertTrue($e instanceof dml_exception);
455         }
457         // Extra named param is ignored
458         $sql = "SELECT * FROM {{$tablename}} WHERE name = :name, course = :course";
459         $params = array('name' => 'record1', 'course' => 1, 'extrastuff'=>'haha');
460         try {
461             $sqlarray = $DB->fix_sql_params($sql, $params);
462             $this->assertTrue(is_array($sqlarray));
463             $this->assertEqual(count($sqlarray[1]), 2);
464         } catch (Exception $e) {
465             $this->fail("Unexpected ".get_class($e)." exception");
466         }
468         // Params exceeding 30 chars length
469         $sql = "SELECT * FROM {{$tablename}} WHERE name = :long_placeholder_with_more_than_30";
470         $params = array('long_placeholder_with_more_than_30' => 'record1');
471         try {
472             $DB->fix_sql_params($sql, $params);
473             $this->fail("Expecting an exception, none occurred");
474         } catch (Exception $e) {
475             $this->assertTrue($e instanceof coding_exception);
476         }
478         // Booleans in NAMED params are casting to 1/0 int
479         $sql = "SELECT * FROM {{$tablename}} WHERE course = ? OR course = ?";
480         $params = array(true, false);
481         list($sql, $params) = $DB->fix_sql_params($sql, $params);
482         $this->assertTrue(reset($params) === 1);
483         $this->assertTrue(next($params) === 0);
485         // Booleans in QM params are casting to 1/0 int
486         $sql = "SELECT * FROM {{$tablename}} WHERE course = :course1 OR course = :course2";
487         $params = array('course1' => true, 'course2' => false);
488         list($sql, $params) = $DB->fix_sql_params($sql, $params);
489         $this->assertTrue(reset($params) === 1);
490         $this->assertTrue(next($params) === 0);
492         // Booleans in DOLLAR params are casting to 1/0 int
493         $sql = "SELECT * FROM {{$tablename}} WHERE course = \$1 OR course = \$2";
494         $params = array(true, false);
495         list($sql, $params) = $DB->fix_sql_params($sql, $params);
496         $this->assertTrue(reset($params) === 1);
497         $this->assertTrue(next($params) === 0);
499         // No data types are touched except bool
500         $sql = "SELECT * FROM {{$tablename}} WHERE name IN (?,?,?,?,?,?)";
501         $inparams = array('abc', 'ABC', NULL, '1', 1, 1.4);
502         list($sql, $params) = $DB->fix_sql_params($sql, $inparams);
503         $this->assertIdentical(array_values($params), array_values($inparams));
504     }
506     public function test_strtok() {
507         // strtok was previously used by bound emulation, make sure it is not used any more
508         $DB = $this->tdb;
509         $dbman = $this->tdb->get_manager();
511         $table = $this->get_test_table();
512         $tablename = $table->getName();
514         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
515         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
516         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
517         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
518         $dbman->create_table($table);
520         $str = 'a?b?c?d';
521         $this->assertIdentical(strtok($str, '?'), 'a');
523         $DB->get_records($tablename, array('id'=>1));
525         $this->assertIdentical(strtok('?'), 'b');
526     }
528     public function test_tweak_param_names() {
529         // Note the tweak_param_names() method is only available in the oracle driver,
530         // hence we look for expected results indirectly, by testing various DML methods
531         // with some "extreme" conditions causing the tweak to happen.
532         $DB = $this->tdb;
533         $dbman = $this->tdb->get_manager();
535         $table = $this->get_test_table();
536         $tablename = $table->getName();
538         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
539         // Add some columns with 28 chars in the name
540         $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10');
541         $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2');
542         $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100');
543         // Add some columns with 30 chars in the name
544         $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10');
545         $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2');
546         $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100');
548         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
550         $dbman->create_table($table);
552         $this->assertTrue($dbman->table_exists($tablename));
554         // Test insert record
555         $rec1 = new stdClass();
556         $rec1->long_int_columnname_with_28c = 28;
557         $rec1->long_dec_columnname_with_28c = 28.28;
558         $rec1->long_str_columnname_with_28c = '28';
559         $rec1->long_int_columnname_with_30cxx = 30;
560         $rec1->long_dec_columnname_with_30cxx = 30.30;
561         $rec1->long_str_columnname_with_30cxx = '30';
563         // insert_record()
564         $rec1->id = $DB->insert_record($tablename, $rec1);
565         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
567         // update_record()
568         $DB->update_record($tablename, $rec1);
569         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
571         // set_field()
572         $rec1->long_int_columnname_with_28c = 280;
573         $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c,
574             array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28));
575         $rec1->long_dec_columnname_with_28c = 280.28;
576         $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c,
577             array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28));
578         $rec1->long_str_columnname_with_28c = '280';
579         $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c,
580             array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28'));
581         $rec1->long_int_columnname_with_30cxx = 300;
582         $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx,
583             array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30));
584         $rec1->long_dec_columnname_with_30cxx = 300.30;
585         $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx,
586             array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30));
587         $rec1->long_str_columnname_with_30cxx = '300';
588         $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx,
589             array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30'));
590         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
592         // delete_records()
593         $rec2 = $DB->get_record($tablename, array('id' => $rec1->id));
594         $rec2->id = $DB->insert_record($tablename, $rec2);
595         $this->assertEqual(2, $DB->count_records($tablename));
596         $DB->delete_records($tablename, (array) $rec2);
597         $this->assertEqual(1, $DB->count_records($tablename));
599         // get_recordset()
600         $rs = $DB->get_recordset($tablename, (array) $rec1);
601         $iterations = 0;
602         foreach ($rs as $rec2) {
603             $iterations++;
604         }
605         $rs->close();
606         $this->assertEqual(1, $iterations);
607         $this->assertEqual($rec1, $rec2);
609         // get_records()
610         $recs = $DB->get_records($tablename, (array) $rec1);
611         $this->assertEqual(1, count($recs));
612         $this->assertEqual($rec1, reset($recs));
614         // get_fieldset_select()
615         $select = 'id = :id AND
616                    long_int_columnname_with_28c = :long_int_columnname_with_28c AND
617                    long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND
618                    long_str_columnname_with_28c = :long_str_columnname_with_28c AND
619                    long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND
620                    long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND
621                    long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx';
622         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1);
623         $this->assertEqual(1, count($fields));
624         $this->assertEqual($rec1->long_int_columnname_with_28c, reset($fields));
625         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1);
626         $this->assertEqual($rec1->long_dec_columnname_with_28c, reset($fields));
627         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1);
628         $this->assertEqual($rec1->long_str_columnname_with_28c, reset($fields));
629         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1);
630         $this->assertEqual($rec1->long_int_columnname_with_30cxx, reset($fields));
631         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1);
632         $this->assertEqual($rec1->long_dec_columnname_with_30cxx, reset($fields));
633         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1);
634         $this->assertEqual($rec1->long_str_columnname_with_30cxx, reset($fields));
636         // overlapping placeholders (progressive str_replace)
637         $overlapselect = 'id = :p AND
638                    long_int_columnname_with_28c = :param1 AND
639                    long_dec_columnname_with_28c = :param2 AND
640                    long_str_columnname_with_28c = :param_with_29_characters_long AND
641                    long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND
642                    long_dec_columnname_with_30cxx = :param_ AND
643                    long_str_columnname_with_30cxx = :param__';
644         $overlapparams = array(
645                 'p' => $rec1->id,
646                 'param1' => $rec1->long_int_columnname_with_28c,
647                 'param2' => $rec1->long_dec_columnname_with_28c,
648                 'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c,
649                 'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx,
650                 'param_' => $rec1->long_dec_columnname_with_30cxx,
651                 'param__' => $rec1->long_str_columnname_with_30cxx);
652         $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams);
653         $this->assertEqual(1, count($recs));
654         $this->assertEqual($rec1, reset($recs));
656         // execute()
657         $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1);
658         $this->assertEqual(0, $DB->count_records($tablename));
659     }
661     public function test_get_tables() {
662         $DB = $this->tdb;
663         $dbman = $this->tdb->get_manager();
665         // Need to test with multiple DBs
666         $table = $this->get_test_table();
667         $tablename = $table->getName();
669         $original_count = count($DB->get_tables());
671         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
672         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
674         $dbman->create_table($table);
675         $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
677         $dbman->drop_table($table);
678         $this->assertTrue(count($DB->get_tables()) == $original_count);
679     }
681     public function test_get_indexes() {
682         $DB = $this->tdb;
683         $dbman = $this->tdb->get_manager();
685         $table = $this->get_test_table();
686         $tablename = $table->getName();
688         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
689         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
690         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
691         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
692         $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
693         $dbman->create_table($table);
695         $indices = $DB->get_indexes($tablename);
696         $this->assertTrue(is_array($indices));
697         $this->assertEqual(count($indices), 2);
698         // we do not care about index names for now
699         $first = array_shift($indices);
700         $second = array_shift($indices);
701         if (count($first['columns']) == 2) {
702             $composed = $first;
703             $single   = $second;
704         } else {
705             $composed = $second;
706             $single   = $first;
707         }
708         $this->assertFalse($single['unique']);
709         $this->assertTrue($composed['unique']);
710         $this->assertEqual(1, count($single['columns']));
711         $this->assertEqual(2, count($composed['columns']));
712         $this->assertEqual('course', $single['columns'][0]);
713         $this->assertEqual('course', $composed['columns'][0]);
714         $this->assertEqual('id', $composed['columns'][1]);
715     }
717     public function test_get_columns() {
718         $DB = $this->tdb;
719         $dbman = $this->tdb->get_manager();
721         $table = $this->get_test_table();
722         $tablename = $table->getName();
724         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
725         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
726         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
727         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
728         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
729         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
730         $table->add_field('onefloat', XMLDB_TYPE_FLOAT, '10,2', null, null, null, 300);
731         $table->add_field('anotherfloat', XMLDB_TYPE_FLOAT, null, null, null, null, 400);
732         $table->add_field('negativedfltint', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '-1');
733         $table->add_field('negativedfltnumber', XMLDB_TYPE_NUMBER, '10', null, XMLDB_NOTNULL, null, '-2');
734         $table->add_field('negativedfltfloat', XMLDB_TYPE_FLOAT, '10', null, XMLDB_NOTNULL, null, '-3');
735         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
736         $dbman->create_table($table);
738         $columns = $DB->get_columns($tablename);
739         $this->assertTrue(is_array($columns));
741         $fields = $table->getFields();
742         $this->assertEqual(count($columns), count($fields));
744         $field = $columns['id'];
745         $this->assertEqual('R', $field->meta_type);
746         $this->assertTrue($field->auto_increment);
747         $this->assertTrue($field->unique);
749         $field = $columns['course'];
750         $this->assertEqual('I', $field->meta_type);
751         $this->assertFalse($field->auto_increment);
752         $this->assertTrue($field->has_default);
753         $this->assertEqual(0, $field->default_value);
754         $this->assertTrue($field->not_null);
756         $field = $columns['name'];
757         $this->assertEqual('C', $field->meta_type);
758         $this->assertFalse($field->auto_increment);
759         $this->assertEqual(255, $field->max_length);
760         $this->assertTrue($field->has_default);
761         $this->assertIdentical('lala', $field->default_value);
762         $this->assertFalse($field->not_null);
764         $field = $columns['description'];
765         $this->assertEqual('X', $field->meta_type);
766         $this->assertFalse($field->auto_increment);
767         $this->assertFalse($field->has_default);
768         $this->assertIdentical(null, $field->default_value);
769         $this->assertFalse($field->not_null);
771         $field = $columns['enumfield'];
772         $this->assertEqual('C', $field->meta_type);
773         $this->assertFalse($field->auto_increment);
774         $this->assertIdentical('test2', $field->default_value);
775         $this->assertTrue($field->not_null);
777         $field = $columns['onenum'];
778         $this->assertEqual('N', $field->meta_type);
779         $this->assertFalse($field->auto_increment);
780         $this->assertEqual(10, $field->max_length);
781         $this->assertEqual(2, $field->scale);
782         $this->assertTrue($field->has_default);
783         $this->assertEqual(200.0, $field->default_value);
784         $this->assertFalse($field->not_null);
786         $field = $columns['onefloat'];
787         $this->assertEqual('N', $field->meta_type);
788         $this->assertFalse($field->auto_increment);
789         $this->assertTrue($field->has_default);
790         $this->assertEqual(300.0, $field->default_value);
791         $this->assertFalse($field->not_null);
793         $field = $columns['anotherfloat'];
794         $this->assertEqual('N', $field->meta_type);
795         $this->assertFalse($field->auto_increment);
796         $this->assertTrue($field->has_default);
797         $this->assertEqual(400.0, $field->default_value);
798         $this->assertFalse($field->not_null);
800         // Test negative defaults in numerical columns
801         $field = $columns['negativedfltint'];
802         $this->assertTrue($field->has_default);
803         $this->assertEqual(-1, $field->default_value);
805         $field = $columns['negativedfltnumber'];
806         $this->assertTrue($field->has_default);
807         $this->assertEqual(-2, $field->default_value);
809         $field = $columns['negativedfltfloat'];
810         $this->assertTrue($field->has_default);
811         $this->assertEqual(-3, $field->default_value);
813         for ($i = 0; $i < count($columns); $i++) {
814             if ($i == 0) {
815                 $next_column = reset($columns);
816                 $next_field  = reset($fields);
817             } else {
818                 $next_column = next($columns);
819                 $next_field  = next($fields);
820             }
822             $this->assertEqual($next_column->name, $next_field->name);
823         }
825         // Test get_columns for non-existing table returns empty array. MDL-30147
826         $columns = $DB->get_columns('xxxx');
827         $this->assertEqual(array(), $columns);
828     }
830     public function test_get_manager() {
831         $DB = $this->tdb;
832         $dbman = $this->tdb->get_manager();
834         $this->assertTrue($dbman instanceof database_manager);
835     }
837     public function test_setup_is_unicodedb() {
838         $DB = $this->tdb;
839         $this->assertTrue($DB->setup_is_unicodedb());
840     }
842     public function test_set_debug() { //tests get_debug() too
843         $DB = $this->tdb;
844         $dbman = $this->tdb->get_manager();
846         $table = $this->get_test_table();
847         $tablename = $table->getName();
849         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
850         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
851         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
852         $dbman->create_table($table);
854         $sql = "SELECT * FROM {{$tablename}}";
856         $prevdebug = $DB->get_debug();
858         ob_start();
859         $DB->set_debug(true);
860         $this->assertTrue($DB->get_debug());
861         $DB->execute($sql);
862         $DB->set_debug(false);
863         $this->assertFalse($DB->get_debug());
864         $debuginfo = ob_get_contents();
865         ob_end_clean();
866         $this->assertFalse($debuginfo === '');
868         ob_start();
869         $DB->execute($sql);
870         $debuginfo = ob_get_contents();
871         ob_end_clean();
872         $this->assertTrue($debuginfo === '');
874         $DB->set_debug($prevdebug);
875     }
877     public function test_execute() {
878         $DB = $this->tdb;
879         $dbman = $this->tdb->get_manager();
881         $table1 = $this->get_test_table('1');
882         $tablename1 = $table1->getName();
883         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
884         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
885         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
886         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
887         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
888         $dbman->create_table($table1);
890         $table2 = $this->get_test_table('2');
891         $tablename2 = $table2->getName();
892         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
893         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
894         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
895         $dbman->create_table($table2);
897         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
898         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
899         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
900         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
902         // select results are ignored
903         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
904         $this->assertTrue($DB->execute($sql, array('course'=>3)));
906         // throw exception on error
907         $sql = "XXUPDATE SET XSSD";
908         try {
909             $DB->execute($sql);
910             $this->fail("Expecting an exception, none occurred");
911         } catch (Exception $e) {
912             $this->assertTrue($e instanceof dml_exception);
913         }
915         // update records
916         $sql = "UPDATE {{$tablename1}}
917                    SET course = 6
918                  WHERE course = ?";
919         $this->assertTrue($DB->execute($sql, array('3')));
920         $this->assertEqual($DB->count_records($tablename1, array('course' => 6)), 2);
922         // update records with subquery condition
923         // confirm that the option not using table aliases is cross-db
924         $sql = "UPDATE {{$tablename1}}
925                    SET course = 0
926                  WHERE NOT EXISTS (
927                            SELECT course
928                              FROM {{$tablename2}} tbl2
929                             WHERE tbl2.course = {{$tablename1}}.course
930                               AND 1 = 0)"; // Really we don't update anything, but verify the syntax is allowed
931         $this->assertTrue($DB->execute($sql));
933         // insert from one into second table
934         $sql = "INSERT INTO {{$tablename2}} (course)
936                 SELECT course
937                   FROM {{$tablename1}}";
938         $this->assertTrue($DB->execute($sql));
939         $this->assertEqual($DB->count_records($tablename2), 4);
940     }
942     public function test_get_recordset() {
943         $DB = $this->tdb;
944         $dbman = $DB->get_manager();
946         $table = $this->get_test_table();
947         $tablename = $table->getName();
949         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
950         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
951         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
952         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
953         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
954         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
955         $dbman->create_table($table);
957         $data = array(array('id' => 1, 'course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
958                       array('id' => 2, 'course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
959                       array('id' => 3, 'course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
961         foreach ($data as $record) {
962             $DB->insert_record($tablename, $record);
963         }
965         // standard recordset iteration
966         $rs = $DB->get_recordset($tablename);
967         $this->assertTrue($rs instanceof moodle_recordset);
968         reset($data);
969         foreach($rs as $record) {
970             $data_record = current($data);
971             foreach ($record as $k => $v) {
972                 $this->assertEqual($data_record[$k], $v);
973             }
974             next($data);
975         }
976         $rs->close();
978         // iterator style usage
979         $rs = $DB->get_recordset($tablename);
980         $this->assertTrue($rs instanceof moodle_recordset);
981         reset($data);
982         while ($rs->valid()) {
983             $record = $rs->current();
984             $data_record = current($data);
985             foreach ($record as $k => $v) {
986                 $this->assertEqual($data_record[$k], $v);
987             }
988             next($data);
989             $rs->next();
990         }
991         $rs->close();
993         // make sure rewind is ignored
994         $rs = $DB->get_recordset($tablename);
995         $this->assertTrue($rs instanceof moodle_recordset);
996         reset($data);
997         $i = 0;
998         foreach($rs as $record) {
999             $i++;
1000             $rs->rewind();
1001             if ($i > 10) {
1002                 $this->fail('revind not ignored in recordsets');
1003                 break;
1004             }
1005             $data_record = current($data);
1006             foreach ($record as $k => $v) {
1007                 $this->assertEqual($data_record[$k], $v);
1008             }
1009             next($data);
1010         }
1011         $rs->close();
1013         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1014         $conditions = array('onetext' => '1');
1015         try {
1016             $rs = $DB->get_recordset($tablename, $conditions);
1017             if (debugging()) {
1018                 // only in debug mode - hopefully all devs test code in debug mode...
1019                 $this->fail('An Exception is missing, expected due to equating of text fields');
1020             }
1021         } catch (exception $e) {
1022             $this->assertTrue($e instanceof dml_exception);
1023             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1024         }
1026         // notes:
1027         //  * limits are tested in test_get_recordset_sql()
1028         //  * where_clause() is used internally and is tested in test_get_records()
1029     }
1031     public function test_get_recordset_iterator_keys() {
1032         $DB = $this->tdb;
1033         $dbman = $DB->get_manager();
1035         $table = $this->get_test_table();
1036         $tablename = $table->getName();
1038         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1039         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1040         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
1041         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1042         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1043         $dbman->create_table($table);
1045         $data = array(array('id'=> 1, 'course' => 3, 'name' => 'record1'),
1046                       array('id'=> 2, 'course' => 3, 'name' => 'record2'),
1047                       array('id'=> 3, 'course' => 5, 'name' => 'record3'));
1048         foreach ($data as $record) {
1049             $DB->insert_record($tablename, $record);
1050         }
1052         // Test repeated numeric keys are returned ok
1053         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
1055         reset($data);
1056         $count = 0;
1057         foreach($rs as $key => $record) {
1058             $data_record = current($data);
1059             $this->assertEqual($data_record['course'], $key);
1060             next($data);
1061             $count++;
1062         }
1063         $rs->close();
1064         $this->assertEqual($count, 3);
1066         // Test string keys are returned ok
1067         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
1069         reset($data);
1070         $count = 0;
1071         foreach($rs as $key => $record) {
1072             $data_record = current($data);
1073             $this->assertEqual($data_record['name'], $key);
1074             next($data);
1075             $count++;
1076         }
1077         $rs->close();
1078         $this->assertEqual($count, 3);
1080         // Test numeric not starting in 1 keys are returned ok
1081         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
1083         $data = array_reverse($data);
1084         reset($data);
1085         $count = 0;
1086         foreach($rs as $key => $record) {
1087             $data_record = current($data);
1088             $this->assertEqual($data_record['id'], $key);
1089             next($data);
1090             $count++;
1091         }
1092         $rs->close();
1093         $this->assertEqual($count, 3);
1094     }
1096     public function test_get_recordset_list() {
1097         $DB = $this->tdb;
1098         $dbman = $DB->get_manager();
1100         $table = $this->get_test_table();
1101         $tablename = $table->getName();
1103         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1104         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1105         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1106         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1107         $dbman->create_table($table);
1109         $DB->insert_record($tablename, array('course' => 3));
1110         $DB->insert_record($tablename, array('course' => 3));
1111         $DB->insert_record($tablename, array('course' => 5));
1112         $DB->insert_record($tablename, array('course' => 2));
1114         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
1116         $counter = 0;
1117         foreach ($rs as $record) {
1118             $counter++;
1119         }
1120         $this->assertEqual(3, $counter);
1121         $rs->close();
1123         $rs = $DB->get_recordset_list($tablename, 'course',array()); /// Must return 0 rows without conditions. MDL-17645
1125         $counter = 0;
1126         foreach ($rs as $record) {
1127             $counter++;
1128         }
1129         $rs->close();
1130         $this->assertEqual(0, $counter);
1132         // notes:
1133         //  * limits are tested in test_get_recordset_sql()
1134         //  * where_clause() is used internally and is tested in test_get_records()
1135     }
1137     public function test_get_recordset_select() {
1138         $DB = $this->tdb;
1139         $dbman = $DB->get_manager();
1141         $table = $this->get_test_table();
1142         $tablename = $table->getName();
1144         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1145         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1146         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1147         $dbman->create_table($table);
1149         $DB->insert_record($tablename, array('course' => 3));
1150         $DB->insert_record($tablename, array('course' => 3));
1151         $DB->insert_record($tablename, array('course' => 5));
1152         $DB->insert_record($tablename, array('course' => 2));
1154         $rs = $DB->get_recordset_select($tablename, '');
1155         $counter = 0;
1156         foreach ($rs as $record) {
1157             $counter++;
1158         }
1159         $rs->close();
1160         $this->assertEqual(4, $counter);
1162         $this->assertTrue($rs = $DB->get_recordset_select($tablename, 'course = 3'));
1163         $counter = 0;
1164         foreach ($rs as $record) {
1165             $counter++;
1166         }
1167         $rs->close();
1168         $this->assertEqual(2, $counter);
1170         // notes:
1171         //  * limits are tested in test_get_recordset_sql()
1172     }
1174     public function test_get_recordset_sql() {
1175         $DB = $this->tdb;
1176         $dbman = $DB->get_manager();
1178         $table = $this->get_test_table();
1179         $tablename = $table->getName();
1181         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1182         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1183         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1184         $dbman->create_table($table);
1186         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1187         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1188         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1189         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1190         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1191         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1192         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1194         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1195         $counter = 0;
1196         foreach ($rs as $record) {
1197             $counter++;
1198         }
1199         $rs->close();
1200         $this->assertEqual(2, $counter);
1202         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
1203         // only limitfrom = skips that number of records
1204         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1205         $records = array();
1206         foreach($rs as $key => $record) {
1207             $records[$key] = $record;
1208         }
1209         $rs->close();
1210         $this->assertEqual(5, count($records));
1211         $this->assertEqual($inskey3, reset($records)->id);
1212         $this->assertEqual($inskey7, end($records)->id);
1214         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
1215     }
1217     public function test_get_records() {
1218         $DB = $this->tdb;
1219         $dbman = $DB->get_manager();
1221         $table = $this->get_test_table();
1222         $tablename = $table->getName();
1224         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1225         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1226         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1227         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1228         $dbman->create_table($table);
1230         $DB->insert_record($tablename, array('course' => 3));
1231         $DB->insert_record($tablename, array('course' => 3));
1232         $DB->insert_record($tablename, array('course' => 5));
1233         $DB->insert_record($tablename, array('course' => 2));
1235         // All records
1236         $records = $DB->get_records($tablename);
1237         $this->assertEqual(4, count($records));
1238         $this->assertEqual(3, $records[1]->course);
1239         $this->assertEqual(3, $records[2]->course);
1240         $this->assertEqual(5, $records[3]->course);
1241         $this->assertEqual(2, $records[4]->course);
1243         // Records matching certain conditions
1244         $records = $DB->get_records($tablename, array('course' => 3));
1245         $this->assertEqual(2, count($records));
1246         $this->assertEqual(3, $records[1]->course);
1247         $this->assertEqual(3, $records[2]->course);
1249         // All records sorted by course
1250         $records = $DB->get_records($tablename, null, 'course');
1251         $this->assertEqual(4, count($records));
1252         $current_record = reset($records);
1253         $this->assertEqual(4, $current_record->id);
1254         $current_record = next($records);
1255         $this->assertEqual(1, $current_record->id);
1256         $current_record = next($records);
1257         $this->assertEqual(2, $current_record->id);
1258         $current_record = next($records);
1259         $this->assertEqual(3, $current_record->id);
1261         // All records, but get only one field
1262         $records = $DB->get_records($tablename, null, '', 'id');
1263         $this->assertFalse(isset($records[1]->course));
1264         $this->assertTrue(isset($records[1]->id));
1265         $this->assertEqual(4, count($records));
1267         // Booleans into params
1268         $records = $DB->get_records($tablename, array('course' => true));
1269         $this->assertEqual(0, count($records));
1270         $records = $DB->get_records($tablename, array('course' => false));
1271         $this->assertEqual(0, count($records));
1273         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1274         $conditions = array('onetext' => '1');
1275         try {
1276             $records = $DB->get_records($tablename, $conditions);
1277             if (debugging()) {
1278                 // only in debug mode - hopefully all devs test code in debug mode...
1279                 $this->fail('An Exception is missing, expected due to equating of text fields');
1280             }
1281         } catch (exception $e) {
1282             $this->assertTrue($e instanceof dml_exception);
1283             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1284         }
1286         // test get_records passing non-existing table
1287         // with params
1288         try {
1289             $records = $DB->get_records('xxxx', array('id' => 0));
1290             $this->fail('An Exception is missing, expected due to query against non-existing table');
1291         } catch (exception $e) {
1292             $this->assertTrue($e instanceof dml_exception);
1293             if (debugging()) {
1294                 // information for developers only, normal users get general error message
1295                 $this->assertEqual($e->errorcode, 'ddltablenotexist');
1296             }
1297         }
1298         // and without params
1299         try {
1300             $records = $DB->get_records('xxxx', array());
1301             $this->fail('An Exception is missing, expected due to query against non-existing table');
1302         } catch (exception $e) {
1303             $this->assertTrue($e instanceof dml_exception);
1304             if (debugging()) {
1305                 // information for developers only, normal users get general error message
1306                 $this->assertEqual($e->errorcode, 'ddltablenotexist');
1307             }
1308         }
1310         // test get_records passing non-existing column
1311         try {
1312             $records = $DB->get_records($tablename, array('xxxx' => 0));
1313             $this->fail('An Exception is missing, expected due to query against non-existing column');
1314         } catch (exception $e) {
1315             $this->assertTrue($e instanceof dml_exception);
1316             if (debugging()) {
1317                 // information for developers only, normal users get general error message
1318                 $this->assertEqual($e->errorcode, 'ddlfieldnotexist');
1319             }
1320         }
1322         // note: delegate limits testing to test_get_records_sql()
1323     }
1325     public function test_get_records_list() {
1326         $DB = $this->tdb;
1327         $dbman = $DB->get_manager();
1329         $table = $this->get_test_table();
1330         $tablename = $table->getName();
1332         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1333         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1334         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1335         $dbman->create_table($table);
1337         $DB->insert_record($tablename, array('course' => 3));
1338         $DB->insert_record($tablename, array('course' => 3));
1339         $DB->insert_record($tablename, array('course' => 5));
1340         $DB->insert_record($tablename, array('course' => 2));
1342         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
1343         $this->assertTrue(is_array($records));
1344         $this->assertEqual(3, count($records));
1345         $this->assertEqual(1, reset($records)->id);
1346         $this->assertEqual(2, next($records)->id);
1347         $this->assertEqual(4, next($records)->id);
1349         $this->assertIdentical(array(), $records = $DB->get_records_list($tablename, 'course', array())); /// Must return 0 rows without conditions. MDL-17645
1350         $this->assertEqual(0, count($records));
1352         // note: delegate limits testing to test_get_records_sql()
1353     }
1355     public function test_get_records_sql() {
1356         $DB = $this->tdb;
1357         $dbman = $DB->get_manager();
1359         $table = $this->get_test_table();
1360         $tablename = $table->getName();
1362         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1363         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1364         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1365         $dbman->create_table($table);
1367         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1368         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1369         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1370         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1371         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1372         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1373         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1375         $table2 = $this->get_test_table("2");
1376         $tablename2 = $table2->getName();
1377         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1378         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1379         $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
1380         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1381         $dbman->create_table($table2);
1383         $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
1384         $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
1385         $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
1386         $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
1388         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1389         $this->assertEqual(2, count($records));
1390         $this->assertEqual($inskey1, reset($records)->id);
1391         $this->assertEqual($inskey4, next($records)->id);
1393         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1394         $this->enable_debugging();
1395         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1396         $this->assertFalse($this->get_debugging() === '');
1397         $this->assertEqual(6, count($records));
1399         // negative limits = no limits
1400         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1401         $this->assertEqual(7, count($records));
1403         // zero limits = no limits
1404         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1405         $this->assertEqual(7, count($records));
1407         // only limitfrom = skips that number of records
1408         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1409         $this->assertEqual(5, count($records));
1410         $this->assertEqual($inskey3, reset($records)->id);
1411         $this->assertEqual($inskey7, end($records)->id);
1413         // only limitnum = fetches that number of records
1414         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1415         $this->assertEqual(3, count($records));
1416         $this->assertEqual($inskey1, reset($records)->id);
1417         $this->assertEqual($inskey3, end($records)->id);
1419         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1420         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1421         $this->assertEqual(2, count($records));
1422         $this->assertEqual($inskey4, reset($records)->id);
1423         $this->assertEqual($inskey5, end($records)->id);
1425         // both limitfrom and limitnum in query having subqueris
1426         // note the subquery skips records with course = 0 and 3
1427         $sql = "SELECT * FROM {{$tablename}}
1428                  WHERE course NOT IN (
1429                      SELECT course FROM {{$tablename}}
1430                       WHERE course IN (0, 3))
1431                 ORDER BY course";
1432         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2
1433         $this->assertEqual(2, count($records));
1434         $this->assertEqual($inskey6, reset($records)->id);
1435         $this->assertEqual($inskey5, end($records)->id);
1436         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2
1437         $this->assertEqual(2, count($records));
1438         $this->assertEqual($inskey3, reset($records)->id);
1439         $this->assertEqual($inskey2, end($records)->id);
1441         // test 2 tables with aliases and limits with order bys
1442         $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
1443                   FROM {{$tablename}} t1, {{$tablename2}} t2
1444                  WHERE t2.course=t1.course
1445               ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
1446         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5
1447         $this->assertEqual(2, count($records));
1448         $this->assertEqual('5', end($records)->cid);
1449         $this->assertEqual('4', reset($records)->cid);
1451         // test 2 tables with aliases and limits with the highest INT limit works
1452         $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}
1453         $this->assertEqual(2, count($records));
1454         $this->assertEqual('5', end($records)->cid);
1455         $this->assertEqual('4', reset($records)->cid);
1457         // test 2 tables with aliases and limits with order bys (limit which is highest INT number)
1458         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses
1459         $this->assertEqual(0, count($records));
1461         // test 2 tables with aliases and limits with order bys (limit which s highest INT number)
1462         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses
1463         $this->assertEqual(0, count($records));
1465         // TODO: Test limits in queries having DISTINCT clauses
1467         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1468     }
1470     public function test_get_records_menu() {
1471         $DB = $this->tdb;
1472         $dbman = $DB->get_manager();
1474         $table = $this->get_test_table();
1475         $tablename = $table->getName();
1477         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1478         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1479         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1480         $dbman->create_table($table);
1482         $DB->insert_record($tablename, array('course' => 3));
1483         $DB->insert_record($tablename, array('course' => 3));
1484         $DB->insert_record($tablename, array('course' => 5));
1485         $DB->insert_record($tablename, array('course' => 2));
1487         $records = $DB->get_records_menu($tablename, array('course' => 3));
1488         $this->assertTrue(is_array($records));
1489         $this->assertEqual(2, count($records));
1490         $this->assertFalse(empty($records[1]));
1491         $this->assertFalse(empty($records[2]));
1492         $this->assertEqual(3, $records[1]);
1493         $this->assertEqual(3, $records[2]);
1495         // note: delegate limits testing to test_get_records_sql()
1496     }
1498     public function test_get_records_select_menu() {
1499         $DB = $this->tdb;
1500         $dbman = $DB->get_manager();
1502         $table = $this->get_test_table();
1503         $tablename = $table->getName();
1505         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1506         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1507         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1508         $dbman->create_table($table);
1510         $DB->insert_record($tablename, array('course' => 3));
1511         $DB->insert_record($tablename, array('course' => 2));
1512         $DB->insert_record($tablename, array('course' => 3));
1513         $DB->insert_record($tablename, array('course' => 5));
1515         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1516         $this->assertTrue(is_array($records));
1518         $this->assertEqual(3, count($records));
1519         $this->assertFalse(empty($records[1]));
1520         $this->assertTrue(empty($records[2]));
1521         $this->assertFalse(empty($records[3]));
1522         $this->assertFalse(empty($records[4]));
1523         $this->assertEqual(3, $records[1]);
1524         $this->assertEqual(3, $records[3]);
1525         $this->assertEqual(5, $records[4]);
1527         // note: delegate limits testing to test_get_records_sql()
1528     }
1530     public function test_get_records_sql_menu() {
1531         $DB = $this->tdb;
1532         $dbman = $DB->get_manager();
1534         $table = $this->get_test_table();
1535         $tablename = $table->getName();
1537         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1538         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1539         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1540         $dbman->create_table($table);
1542         $DB->insert_record($tablename, array('course' => 3));
1543         $DB->insert_record($tablename, array('course' => 2));
1544         $DB->insert_record($tablename, array('course' => 3));
1545         $DB->insert_record($tablename, array('course' => 5));
1547         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1548         $this->assertTrue(is_array($records));
1550         $this->assertEqual(3, count($records));
1551         $this->assertFalse(empty($records[1]));
1552         $this->assertTrue(empty($records[2]));
1553         $this->assertFalse(empty($records[3]));
1554         $this->assertFalse(empty($records[4]));
1555         $this->assertEqual(3, $records[1]);
1556         $this->assertEqual(3, $records[3]);
1557         $this->assertEqual(5, $records[4]);
1559         // note: delegate limits testing to test_get_records_sql()
1560     }
1562     public function test_get_record() {
1563         $DB = $this->tdb;
1564         $dbman = $DB->get_manager();
1566         $table = $this->get_test_table();
1567         $tablename = $table->getName();
1569         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1570         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1571         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1572         $dbman->create_table($table);
1574         $DB->insert_record($tablename, array('course' => 3));
1575         $DB->insert_record($tablename, array('course' => 2));
1577         $record = $DB->get_record($tablename, array('id' => 2));
1578         $this->assertTrue($record instanceof stdClass);
1580         $this->assertEqual(2, $record->course);
1581         $this->assertEqual(2, $record->id);
1582     }
1585     public function test_get_record_select() {
1586         $DB = $this->tdb;
1587         $dbman = $DB->get_manager();
1589         $table = $this->get_test_table();
1590         $tablename = $table->getName();
1592         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1593         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1594         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1595         $dbman->create_table($table);
1597         $DB->insert_record($tablename, array('course' => 3));
1598         $DB->insert_record($tablename, array('course' => 2));
1600         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1601         $this->assertTrue($record instanceof stdClass);
1603         $this->assertEqual(2, $record->course);
1605         // note: delegates limit testing to test_get_records_sql()
1606     }
1608     public function test_get_record_sql() {
1609         $DB = $this->tdb;
1610         $dbman = $DB->get_manager();
1612         $table = $this->get_test_table();
1613         $tablename = $table->getName();
1615         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1616         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1617         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1618         $dbman->create_table($table);
1620         $DB->insert_record($tablename, array('course' => 3));
1621         $DB->insert_record($tablename, array('course' => 2));
1623         // standard use
1624         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1625         $this->assertTrue($record instanceof stdClass);
1626         $this->assertEqual(2, $record->course);
1627         $this->assertEqual(2, $record->id);
1629         // backwards compatibility with $ignoremultiple
1630         $this->assertFalse(IGNORE_MISSING);
1631         $this->assertTrue(IGNORE_MULTIPLE);
1633         // record not found - ignore
1634         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1635         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1637         // record not found error
1638         try {
1639             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1640             $this->fail("Exception expected");
1641         } catch (dml_missing_record_exception $e) {
1642             $this->assertTrue(true);
1643         }
1645         $this->enable_debugging();
1646         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1647         $this->assertFalse($this->get_debugging() === '');
1649         // multiple matches ignored
1650         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1652         // multiple found error
1653         try {
1654             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1655             $this->fail("Exception expected");
1656         } catch (dml_multiple_records_exception $e) {
1657             $this->assertTrue(true);
1658         }
1659     }
1661     public function test_get_field() {
1662         $DB = $this->tdb;
1663         $dbman = $DB->get_manager();
1665         $table = $this->get_test_table();
1666         $tablename = $table->getName();
1668         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1669         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1670         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1671         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1672         $dbman->create_table($table);
1674         $id1 = $DB->insert_record($tablename, array('course' => 3));
1675         $DB->insert_record($tablename, array('course' => 5));
1676         $DB->insert_record($tablename, array('course' => 5));
1678         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1679         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1681         $this->assertIdentical(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1682         try {
1683             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1684             $this->assertFail('Exception expected due to missing record');
1685         } catch (dml_exception $ex) {
1686             $this->assertTrue(true);
1687         }
1689         $this->enable_debugging();
1690         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1691         $this->assertIdentical($this->get_debugging(), '');
1693         $this->enable_debugging();
1694         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1695         $this->assertFalse($this->get_debugging() === '');
1697         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1698         $conditions = array('onetext' => '1');
1699         try {
1700             $DB->get_field($tablename, 'course', $conditions);
1701             if (debugging()) {
1702                 // only in debug mode - hopefully all devs test code in debug mode...
1703                 $this->fail('An Exception is missing, expected due to equating of text fields');
1704             }
1705         } catch (exception $e) {
1706             $this->assertTrue($e instanceof dml_exception);
1707             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1708         }
1709     }
1711     public function test_get_field_select() {
1712         $DB = $this->tdb;
1713         $dbman = $DB->get_manager();
1715         $table = $this->get_test_table();
1716         $tablename = $table->getName();
1718         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1719         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1720         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1721         $dbman->create_table($table);
1723         $DB->insert_record($tablename, array('course' => 3));
1725         $this->assertEqual(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1726     }
1728     public function test_get_field_sql() {
1729         $DB = $this->tdb;
1730         $dbman = $DB->get_manager();
1732         $table = $this->get_test_table();
1733         $tablename = $table->getName();
1735         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1736         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1737         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1738         $dbman->create_table($table);
1740         $DB->insert_record($tablename, array('course' => 3));
1742         $this->assertEqual(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1743     }
1745     public function test_get_fieldset_select() {
1746         $DB = $this->tdb;
1747         $dbman = $DB->get_manager();
1749         $table = $this->get_test_table();
1750         $tablename = $table->getName();
1752         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1753         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1754         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1755         $dbman->create_table($table);
1757         $DB->insert_record($tablename, array('course' => 1));
1758         $DB->insert_record($tablename, array('course' => 3));
1759         $DB->insert_record($tablename, array('course' => 2));
1760         $DB->insert_record($tablename, array('course' => 6));
1762         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1763         $this->assertTrue(is_array($fieldset));
1765         $this->assertEqual(3, count($fieldset));
1766         $this->assertEqual(3, $fieldset[0]);
1767         $this->assertEqual(2, $fieldset[1]);
1768         $this->assertEqual(6, $fieldset[2]);
1769     }
1771     public function test_get_fieldset_sql() {
1772         $DB = $this->tdb;
1773         $dbman = $DB->get_manager();
1775         $table = $this->get_test_table();
1776         $tablename = $table->getName();
1778         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1779         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1780         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1781         $dbman->create_table($table);
1783         $DB->insert_record($tablename, array('course' => 1));
1784         $DB->insert_record($tablename, array('course' => 3));
1785         $DB->insert_record($tablename, array('course' => 2));
1786         $DB->insert_record($tablename, array('course' => 6));
1788         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1789         $this->assertTrue(is_array($fieldset));
1791         $this->assertEqual(3, count($fieldset));
1792         $this->assertEqual(2, $fieldset[0]);
1793         $this->assertEqual(3, $fieldset[1]);
1794         $this->assertEqual(4, $fieldset[2]);
1795     }
1797     public function test_insert_record_raw() {
1798         $DB = $this->tdb;
1799         $dbman = $DB->get_manager();
1801         $table = $this->get_test_table();
1802         $tablename = $table->getName();
1804         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1805         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1806         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1807         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1808         $dbman->create_table($table);
1810         $record = (object)array('course' => 1, 'onechar' => 'xx');
1811         $before = clone($record);
1812         $result = $DB->insert_record_raw($tablename, $record);
1813         $this->assertIdentical(1, $result);
1814         $this->assertIdentical($record, $before);
1816         $record = $DB->get_record($tablename, array('course' => 1));
1817         $this->assertTrue($record instanceof stdClass);
1818         $this->assertIdentical('xx', $record->onechar);
1820         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1821         $this->assertIdentical(true, $result);
1823         // note: bulk not implemented yet
1824         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1825         $record = $DB->get_record($tablename, array('course' => 3));
1826         $this->assertTrue($record instanceof stdClass);
1827         $this->assertIdentical('zz', $record->onechar);
1829         // custom sequence (id) - returnid is ignored
1830         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1831         $this->assertIdentical(true, $result);
1832         $record = $DB->get_record($tablename, array('id' => 10));
1833         $this->assertTrue($record instanceof stdClass);
1834         $this->assertIdentical('bb', $record->onechar);
1836         // custom sequence - missing id error
1837         try {
1838             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1839             $this->assertFail('Exception expected due to missing record');
1840         } catch (coding_exception $ex) {
1841             $this->assertTrue(true);
1842         }
1844         // wrong column error
1845         try {
1846             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1847             $this->assertFail('Exception expected due to invalid column');
1848         } catch (dml_exception $ex) {
1849             $this->assertTrue(true);
1850         }
1851     }
1853     public function test_insert_record() {
1854         // All the information in this test is fetched from DB by get_recordset() so we
1855         // have such method properly tested against nulls, empties and friends...
1857         $DB = $this->tdb;
1858         $dbman = $DB->get_manager();
1860         $table = $this->get_test_table();
1861         $tablename = $table->getName();
1863         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1864         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1865         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1866         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1867         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1868         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1869         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1870         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1871         $dbman->create_table($table);
1873         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1874         $record = $DB->get_record($tablename, array('course' => 1));
1875         $this->assertEqual(1, $record->id);
1876         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1877         $this->assertEqual(200, $record->onenum);
1878         $this->assertIdentical('onestring', $record->onechar);
1879         $this->assertNull($record->onetext);
1880         $this->assertNull($record->onebinary);
1882         // without returning id, bulk not implemented
1883         $result = $this->assertIdentical(true, $DB->insert_record($tablename, array('course' => 99), false, true));
1884         $record = $DB->get_record($tablename, array('course' => 99));
1885         $this->assertEqual(2, $record->id);
1886         $this->assertEqual(99, $record->course);
1888         // Check nulls are set properly for all types
1889         $record = new stdClass();
1890         $record->oneint = null;
1891         $record->onenum = null;
1892         $record->onechar = null;
1893         $record->onetext = null;
1894         $record->onebinary = null;
1895         $recid = $DB->insert_record($tablename, $record);
1896         $record = $DB->get_record($tablename, array('id' => $recid));
1897         $this->assertEqual(0, $record->course);
1898         $this->assertNull($record->oneint);
1899         $this->assertNull($record->onenum);
1900         $this->assertNull($record->onechar);
1901         $this->assertNull($record->onetext);
1902         $this->assertNull($record->onebinary);
1904         // Check zeros are set properly for all types
1905         $record = new stdClass();
1906         $record->oneint = 0;
1907         $record->onenum = 0;
1908         $recid = $DB->insert_record($tablename, $record);
1909         $record = $DB->get_record($tablename, array('id' => $recid));
1910         $this->assertEqual(0, $record->oneint);
1911         $this->assertEqual(0, $record->onenum);
1913         // Check booleans are set properly for all types
1914         $record = new stdClass();
1915         $record->oneint = true; // trues
1916         $record->onenum = true;
1917         $record->onechar = true;
1918         $record->onetext = true;
1919         $recid = $DB->insert_record($tablename, $record);
1920         $record = $DB->get_record($tablename, array('id' => $recid));
1921         $this->assertEqual(1, $record->oneint);
1922         $this->assertEqual(1, $record->onenum);
1923         $this->assertEqual(1, $record->onechar);
1924         $this->assertEqual(1, $record->onetext);
1926         $record = new stdClass();
1927         $record->oneint = false; // falses
1928         $record->onenum = false;
1929         $record->onechar = false;
1930         $record->onetext = false;
1931         $recid = $DB->insert_record($tablename, $record);
1932         $record = $DB->get_record($tablename, array('id' => $recid));
1933         $this->assertEqual(0, $record->oneint);
1934         $this->assertEqual(0, $record->onenum);
1935         $this->assertEqual(0, $record->onechar);
1936         $this->assertEqual(0, $record->onetext);
1938         // Check string data causes exception in numeric types
1939         $record = new stdClass();
1940         $record->oneint = 'onestring';
1941         $record->onenum = 0;
1942         try {
1943             $DB->insert_record($tablename, $record);
1944             $this->fail("Expecting an exception, none occurred");
1945         } catch (exception $e) {
1946             $this->assertTrue($e instanceof dml_exception);
1947         }
1948         $record = new stdClass();
1949         $record->oneint = 0;
1950         $record->onenum = 'onestring';
1951         try {
1952            $DB->insert_record($tablename, $record);
1953            $this->fail("Expecting an exception, none occurred");
1954         } catch (exception $e) {
1955             $this->assertTrue($e instanceof dml_exception);
1956         }
1958         // Check empty string data is stored as 0 in numeric datatypes
1959         $record = new stdClass();
1960         $record->oneint = ''; // empty string
1961         $record->onenum = 0;
1962         $recid = $DB->insert_record($tablename, $record);
1963         $record = $DB->get_record($tablename, array('id' => $recid));
1964         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1966         $record = new stdClass();
1967         $record->oneint = 0;
1968         $record->onenum = ''; // empty string
1969         $recid = $DB->insert_record($tablename, $record);
1970         $record = $DB->get_record($tablename, array('id' => $recid));
1971         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1973         // Check empty strings are set properly in string types
1974         $record = new stdClass();
1975         $record->oneint = 0;
1976         $record->onenum = 0;
1977         $record->onechar = '';
1978         $record->onetext = '';
1979         $recid = $DB->insert_record($tablename, $record);
1980         $record = $DB->get_record($tablename, array('id' => $recid));
1981         $this->assertTrue($record->onechar === '');
1982         $this->assertTrue($record->onetext === '');
1984         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1985         $record = new stdClass();
1986         $record->oneint = ((210.10 + 39.92) - 150.02);
1987         $record->onenum = ((210.10 + 39.92) - 150.02);
1988         $recid = $DB->insert_record($tablename, $record);
1989         $record = $DB->get_record($tablename, array('id' => $recid));
1990         $this->assertEqual(100, $record->oneint);
1991         $this->assertEqual(100, $record->onenum);
1993         // Check various quotes/backslashes combinations in string types
1994         $teststrings = array(
1995             'backslashes and quotes alone (even): "" \'\' \\\\',
1996             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1997             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1998             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1999         foreach ($teststrings as $teststring) {
2000             $record = new stdClass();
2001             $record->onechar = $teststring;
2002             $record->onetext = $teststring;
2003             $recid = $DB->insert_record($tablename, $record);
2004             $record = $DB->get_record($tablename, array('id' => $recid));
2005             $this->assertEqual($teststring, $record->onechar);
2006             $this->assertEqual($teststring, $record->onetext);
2007         }
2009         // Check LOBs in text/binary columns
2010         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2011         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2012         $record = new stdClass();
2013         $record->onetext = $clob;
2014         $record->onebinary = $blob;
2015         $recid = $DB->insert_record($tablename, $record);
2016         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2017         $record = $rs->current();
2018         $rs->close();
2019         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2020         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2022         // And "small" LOBs too, just in case
2023         $newclob = substr($clob, 0, 500);
2024         $newblob = substr($blob, 0, 250);
2025         $record = new stdClass();
2026         $record->onetext = $newclob;
2027         $record->onebinary = $newblob;
2028         $recid = $DB->insert_record($tablename, $record);
2029         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2030         $record = $rs->current();
2031         $rs->close();
2032         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2033         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2034         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2036         // And "diagnostic" LOBs too, just in case
2037         $newclob = '\'"\\;/ěščřžýáíé';
2038         $newblob = '\'"\\;/ěščřžýáíé';
2039         $record = new stdClass();
2040         $record->onetext = $newclob;
2041         $record->onebinary = $newblob;
2042         $recid = $DB->insert_record($tablename, $record);
2043         $rs = $DB->get_recordset($tablename, array('id' => $recid));
2044         $record = $rs->current();
2045         $rs->close();
2046         $this->assertIdentical($newclob, $record->onetext);
2047         $this->assertIdentical($newblob, $record->onebinary);
2048         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2050         // test data is not modified
2051         $record = new stdClass();
2052         $record->id     = -1; // has to be ignored
2053         $record->course = 3;
2054         $record->lalala = 'lalal'; // unused
2055         $before = clone($record);
2056         $DB->insert_record($tablename, $record);
2057         $this->assertEqual($record, $before);
2059         // make sure the id is always increasing and never reuses the same id
2060         $id1 = $DB->insert_record($tablename, array('course' => 3));
2061         $id2 = $DB->insert_record($tablename, array('course' => 3));
2062         $this->assertTrue($id1 < $id2);
2063         $DB->delete_records($tablename, array('id'=>$id2));
2064         $id3 = $DB->insert_record($tablename, array('course' => 3));
2065         $this->assertTrue($id2 < $id3);
2066         $DB->delete_records($tablename, array());
2067         $id4 = $DB->insert_record($tablename, array('course' => 3));
2068         $this->assertTrue($id3 < $id4);
2070         // Test saving a float in a CHAR column, and reading it back.
2071         $id = $DB->insert_record($tablename, array('onechar' => 1.0));
2072         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2073         $id = $DB->insert_record($tablename, array('onechar' => 1e20));
2074         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2075         $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
2076         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2077         $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
2078         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2079         $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
2080         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2081         $id = $DB->insert_record($tablename, array('onechar' => 1e300));
2082         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2084         // Test saving a float in a TEXT column, and reading it back.
2085         $id = $DB->insert_record($tablename, array('onetext' => 1.0));
2086         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2087         $id = $DB->insert_record($tablename, array('onetext' => 1e20));
2088         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2089         $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
2090         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2091         $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
2092         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2093         $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
2094         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2095         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
2096         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2097     }
2099     public function test_import_record() {
2100         // All the information in this test is fetched from DB by get_recordset() so we
2101         // have such method properly tested against nulls, empties and friends...
2103         $DB = $this->tdb;
2104         $dbman = $DB->get_manager();
2106         $table = $this->get_test_table();
2107         $tablename = $table->getName();
2109         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2110         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2111         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2112         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2113         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2114         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2115         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2116         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2117         $dbman->create_table($table);
2119         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
2120         $record = $DB->get_record($tablename, array('course' => 1));
2121         $this->assertEqual(1, $record->id);
2122         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2123         $this->assertEqual(200, $record->onenum);
2124         $this->assertIdentical('onestring', $record->onechar);
2125         $this->assertNull($record->onetext);
2126         $this->assertNull($record->onebinary);
2128         // ignore extra columns
2129         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
2130         $before = clone($record);
2131         $this->assertIdentical(true, $DB->import_record($tablename, $record));
2132         $this->assertIdentical($record, $before);
2133         $records = $DB->get_records($tablename);
2134         $this->assertEqual(2, $records[13]->course);
2136         // Check nulls are set properly for all types
2137         $record = new stdClass();
2138         $record->id = 20;
2139         $record->oneint = null;
2140         $record->onenum = null;
2141         $record->onechar = null;
2142         $record->onetext = null;
2143         $record->onebinary = null;
2144         $this->assertTrue($DB->import_record($tablename, $record));
2145         $record = $DB->get_record($tablename, array('id' => 20));
2146         $this->assertEqual(0, $record->course);
2147         $this->assertNull($record->oneint);
2148         $this->assertNull($record->onenum);
2149         $this->assertNull($record->onechar);
2150         $this->assertNull($record->onetext);
2151         $this->assertNull($record->onebinary);
2153         // Check zeros are set properly for all types
2154         $record = new stdClass();
2155         $record->id = 23;
2156         $record->oneint = 0;
2157         $record->onenum = 0;
2158         $this->assertTrue($DB->import_record($tablename, $record));
2159         $record = $DB->get_record($tablename, array('id' => 23));
2160         $this->assertEqual(0, $record->oneint);
2161         $this->assertEqual(0, $record->onenum);
2163         // Check string data causes exception in numeric types
2164         $record = new stdClass();
2165         $record->id = 32;
2166         $record->oneint = 'onestring';
2167         $record->onenum = 0;
2168         try {
2169             $DB->import_record($tablename, $record);
2170             $this->fail("Expecting an exception, none occurred");
2171         } catch (exception $e) {
2172             $this->assertTrue($e instanceof dml_exception);
2173         }
2174         $record = new stdClass();
2175         $record->id = 35;
2176         $record->oneint = 0;
2177         $record->onenum = 'onestring';
2178         try {
2179            $DB->import_record($tablename, $record);
2180            $this->fail("Expecting an exception, none occurred");
2181         } catch (exception $e) {
2182             $this->assertTrue($e instanceof dml_exception);
2183         }
2185         // Check empty strings are set properly in string types
2186         $record = new stdClass();
2187         $record->id = 44;
2188         $record->oneint = 0;
2189         $record->onenum = 0;
2190         $record->onechar = '';
2191         $record->onetext = '';
2192         $this->assertTrue($DB->import_record($tablename, $record));
2193         $record = $DB->get_record($tablename, array('id' => 44));
2194         $this->assertTrue($record->onechar === '');
2195         $this->assertTrue($record->onetext === '');
2197         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2198         $record = new stdClass();
2199         $record->id = 47;
2200         $record->oneint = ((210.10 + 39.92) - 150.02);
2201         $record->onenum = ((210.10 + 39.92) - 150.02);
2202         $this->assertTrue($DB->import_record($tablename, $record));
2203         $record = $DB->get_record($tablename, array('id' => 47));
2204         $this->assertEqual(100, $record->oneint);
2205         $this->assertEqual(100, $record->onenum);
2207         // Check various quotes/backslashes combinations in string types
2208         $i = 50;
2209         $teststrings = array(
2210             'backslashes and quotes alone (even): "" \'\' \\\\',
2211             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2212             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2213             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2214         foreach ($teststrings as $teststring) {
2215             $record = new stdClass();
2216             $record->id = $i;
2217             $record->onechar = $teststring;
2218             $record->onetext = $teststring;
2219             $this->assertTrue($DB->import_record($tablename, $record));
2220             $record = $DB->get_record($tablename, array('id' => $i));
2221             $this->assertEqual($teststring, $record->onechar);
2222             $this->assertEqual($teststring, $record->onetext);
2223             $i = $i + 3;
2224         }
2226         // Check LOBs in text/binary columns
2227         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2228         $record = new stdClass();
2229         $record->id = 70;
2230         $record->onetext = $clob;
2231         $record->onebinary = '';
2232         $this->assertTrue($DB->import_record($tablename, $record));
2233         $rs = $DB->get_recordset($tablename, array('id' => 70));
2234         $record = $rs->current();
2235         $rs->close();
2236         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2238         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2239         $record = new stdClass();
2240         $record->id = 71;
2241         $record->onetext = '';
2242         $record->onebinary = $blob;
2243         $this->assertTrue($DB->import_record($tablename, $record));
2244         $rs = $DB->get_recordset($tablename, array('id' => 71));
2245         $record = $rs->current();
2246         $rs->close();
2247         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2249         // And "small" LOBs too, just in case
2250         $newclob = substr($clob, 0, 500);
2251         $newblob = substr($blob, 0, 250);
2252         $record = new stdClass();
2253         $record->id = 73;
2254         $record->onetext = $newclob;
2255         $record->onebinary = $newblob;
2256         $this->assertTrue($DB->import_record($tablename, $record));
2257         $rs = $DB->get_recordset($tablename, array('id' => 73));
2258         $record = $rs->current();
2259         $rs->close();
2260         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2261         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2262         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2263     }
2265     public function test_update_record_raw() {
2266         $DB = $this->tdb;
2267         $dbman = $DB->get_manager();
2269         $table = $this->get_test_table();
2270         $tablename = $table->getName();
2272         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2273         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2274         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2275         $dbman->create_table($table);
2277         $DB->insert_record($tablename, array('course' => 1));
2278         $DB->insert_record($tablename, array('course' => 3));
2280         $record = $DB->get_record($tablename, array('course' => 1));
2281         $record->course = 2;
2282         $this->assertTrue($DB->update_record_raw($tablename, $record));
2283         $this->assertEqual(0, $DB->count_records($tablename, array('course' => 1)));
2284         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 2)));
2285         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 3)));
2287         $record = $DB->get_record($tablename, array('course' => 1));
2288         $record->xxxxx = 2;
2289         try {
2290            $DB->update_record_raw($tablename, $record);
2291            $this->fail("Expecting an exception, none occurred");
2292         } catch (Exception $e) {
2293             $this->assertTrue($e instanceof coding_exception);
2294         }
2296         $record = $DB->get_record($tablename, array('course' => 3));
2297         unset($record->id);
2298         try {
2299            $DB->update_record_raw($tablename, $record);
2300            $this->fail("Expecting an exception, none occurred");
2301         } catch (Exception $e) {
2302             $this->assertTrue($e instanceof coding_exception);
2303         }
2304     }
2306     public function test_update_record() {
2308         // All the information in this test is fetched from DB by get_record() so we
2309         // have such method properly tested against nulls, empties and friends...
2311         $DB = $this->tdb;
2312         $dbman = $DB->get_manager();
2314         $table = $this->get_test_table();
2315         $tablename = $table->getName();
2317         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2318         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2319         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2320         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2321         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2322         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2323         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2324         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2325         $dbman->create_table($table);
2327         $DB->insert_record($tablename, array('course' => 1));
2328         $record = $DB->get_record($tablename, array('course' => 1));
2329         $record->course = 2;
2331         $this->assertTrue($DB->update_record($tablename, $record));
2332         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
2333         $this->assertTrue($record = $DB->get_record($tablename, array('course' => 2)));
2334         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2335         $this->assertEqual(200, $record->onenum);
2336         $this->assertEqual('onestring', $record->onechar);
2337         $this->assertNull($record->onetext);
2338         $this->assertNull($record->onebinary);
2340         // Check nulls are set properly for all types
2341         $record->oneint = null;
2342         $record->onenum = null;
2343         $record->onechar = null;
2344         $record->onetext = null;
2345         $record->onebinary = null;
2346         $DB->update_record($tablename, $record);
2347         $record = $DB->get_record($tablename, array('course' => 2));
2348         $this->assertNull($record->oneint);
2349         $this->assertNull($record->onenum);
2350         $this->assertNull($record->onechar);
2351         $this->assertNull($record->onetext);
2352         $this->assertNull($record->onebinary);
2354         // Check zeros are set properly for all types
2355         $record->oneint = 0;
2356         $record->onenum = 0;
2357         $DB->update_record($tablename, $record);
2358         $record = $DB->get_record($tablename, array('course' => 2));
2359         $this->assertEqual(0, $record->oneint);
2360         $this->assertEqual(0, $record->onenum);
2362         // Check booleans are set properly for all types
2363         $record->oneint = true; // trues
2364         $record->onenum = true;
2365         $record->onechar = true;
2366         $record->onetext = true;
2367         $DB->update_record($tablename, $record);
2368         $record = $DB->get_record($tablename, array('course' => 2));
2369         $this->assertEqual(1, $record->oneint);
2370         $this->assertEqual(1, $record->onenum);
2371         $this->assertEqual(1, $record->onechar);
2372         $this->assertEqual(1, $record->onetext);
2374         $record->oneint = false; // falses
2375         $record->onenum = false;
2376         $record->onechar = false;
2377         $record->onetext = false;
2378         $DB->update_record($tablename, $record);
2379         $record = $DB->get_record($tablename, array('course' => 2));
2380         $this->assertEqual(0, $record->oneint);
2381         $this->assertEqual(0, $record->onenum);
2382         $this->assertEqual(0, $record->onechar);
2383         $this->assertEqual(0, $record->onetext);
2385         // Check string data causes exception in numeric types
2386         $record->oneint = 'onestring';
2387         $record->onenum = 0;
2388         try {
2389             $DB->update_record($tablename, $record);
2390             $this->fail("Expecting an exception, none occurred");
2391         } catch (exception $e) {
2392             $this->assertTrue($e instanceof dml_exception);
2393         }
2394         $record->oneint = 0;
2395         $record->onenum = 'onestring';
2396         try {
2397             $DB->update_record($tablename, $record);
2398             $this->fail("Expecting an exception, none occurred");
2399         } catch (exception $e) {
2400             $this->assertTrue($e instanceof dml_exception);
2401         }
2403         // Check empty string data is stored as 0 in numeric datatypes
2404         $record->oneint = ''; // empty string
2405         $record->onenum = 0;
2406         $DB->update_record($tablename, $record);
2407         $record = $DB->get_record($tablename, array('course' => 2));
2408         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2410         $record->oneint = 0;
2411         $record->onenum = ''; // empty string
2412         $DB->update_record($tablename, $record);
2413         $record = $DB->get_record($tablename, array('course' => 2));
2414         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2416         // Check empty strings are set properly in string types
2417         $record->oneint = 0;
2418         $record->onenum = 0;
2419         $record->onechar = '';
2420         $record->onetext = '';
2421         $DB->update_record($tablename, $record);
2422         $record = $DB->get_record($tablename, array('course' => 2));
2423         $this->assertTrue($record->onechar === '');
2424         $this->assertTrue($record->onetext === '');
2426         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2427         $record->oneint = ((210.10 + 39.92) - 150.02);
2428         $record->onenum = ((210.10 + 39.92) - 150.02);
2429         $DB->update_record($tablename, $record);
2430         $record = $DB->get_record($tablename, array('course' => 2));
2431         $this->assertEqual(100, $record->oneint);
2432         $this->assertEqual(100, $record->onenum);
2434         // Check various quotes/backslashes combinations in string types
2435         $teststrings = array(
2436             'backslashes and quotes alone (even): "" \'\' \\\\',
2437             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2438             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2439             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2440         foreach ($teststrings as $teststring) {
2441             $record->onechar = $teststring;
2442             $record->onetext = $teststring;
2443             $DB->update_record($tablename, $record);
2444             $record = $DB->get_record($tablename, array('course' => 2));
2445             $this->assertEqual($teststring, $record->onechar);
2446             $this->assertEqual($teststring, $record->onetext);
2447         }
2449         // Check LOBs in text/binary columns
2450         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2451         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2452         $record->onetext = $clob;
2453         $record->onebinary = $blob;
2454         $DB->update_record($tablename, $record);
2455         $record = $DB->get_record($tablename, array('course' => 2));
2456         $this->assertEqual($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2457         $this->assertEqual($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2459         // And "small" LOBs too, just in case
2460         $newclob = substr($clob, 0, 500);
2461         $newblob = substr($blob, 0, 250);
2462         $record->onetext = $newclob;
2463         $record->onebinary = $newblob;
2464         $DB->update_record($tablename, $record);
2465         $record = $DB->get_record($tablename, array('course' => 2));
2466         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2467         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2469         // Test saving a float in a CHAR column, and reading it back.
2470         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2471         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
2472         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2473         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
2474         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2475         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
2476         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2477         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
2478         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2479         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
2480         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2481         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
2482         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2484         // Test saving a float in a TEXT column, and reading it back.
2485         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2486         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
2487         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2488         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
2489         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2490         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
2491         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2492         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
2493         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2494         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
2495         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2496         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
2497         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2498     }
2500     public function test_set_field() {
2501         $DB = $this->tdb;
2502         $dbman = $DB->get_manager();
2504         $table = $this->get_test_table();
2505         $tablename = $table->getName();
2507         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2508         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2509         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2510         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2511         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2512         $dbman->create_table($table);
2514         // simple set_field
2515         $id1 = $DB->insert_record($tablename, array('course' => 1));
2516         $id2 = $DB->insert_record($tablename, array('course' => 1));
2517         $id3 = $DB->insert_record($tablename, array('course' => 3));
2518         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2519         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2520         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2521         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2522         $DB->delete_records($tablename, array());
2524         // multiple fields affected
2525         $id1 = $DB->insert_record($tablename, array('course' => 1));
2526         $id2 = $DB->insert_record($tablename, array('course' => 1));
2527         $id3 = $DB->insert_record($tablename, array('course' => 3));
2528         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2529         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2530         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2531         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2532         $DB->delete_records($tablename, array());
2534         // no field affected
2535         $id1 = $DB->insert_record($tablename, array('course' => 1));
2536         $id2 = $DB->insert_record($tablename, array('course' => 1));
2537         $id3 = $DB->insert_record($tablename, array('course' => 3));
2538         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2539         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2540         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2541         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2542         $DB->delete_records($tablename, array());
2544         // all fields - no condition
2545         $id1 = $DB->insert_record($tablename, array('course' => 1));
2546         $id2 = $DB->insert_record($tablename, array('course' => 1));
2547         $id3 = $DB->insert_record($tablename, array('course' => 3));
2548         $DB->set_field($tablename, 'course', 5, array());
2549         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2550         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2551         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2553         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2554         $conditions = array('onetext' => '1');
2555         try {
2556             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2557             if (debugging()) {
2558                 // only in debug mode - hopefully all devs test code in debug mode...
2559                 $this->fail('An Exception is missing, expected due to equating of text fields');
2560             }
2561         } catch (exception $e) {
2562             $this->assertTrue($e instanceof dml_exception);
2563             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2564         }
2566         // Test saving a float in a CHAR column, and reading it back.
2567         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2568         $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
2569         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2570         $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
2571         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2572         $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
2573         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2574         $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
2575         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2576         $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
2577         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2578         $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
2579         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2581         // Test saving a float in a TEXT column, and reading it back.
2582         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2583         $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
2584         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2585         $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
2586         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2587         $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
2588         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2589         $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
2590         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2591         $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
2592         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2593         $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
2594         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2596         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2597         // go to set_field_select() because set_field() is just one wrapper over it
2598     }
2600     public function test_set_field_select() {
2602         // All the information in this test is fetched from DB by get_field() so we
2603         // have such method properly tested against nulls, empties and friends...
2605         $DB = $this->tdb;
2606         $dbman = $DB->get_manager();
2608         $table = $this->get_test_table();
2609         $tablename = $table->getName();
2611         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2612         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2613         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2614         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2615         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2616         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2617         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2618         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2619         $dbman->create_table($table);
2621         $DB->insert_record($tablename, array('course' => 1));
2623         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2624         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2626         // Check nulls are set properly for all types
2627         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2628         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2629         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2630         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2631         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2632         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2633         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2634         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2635         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2636         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2638         // Check zeros are set properly for all types
2639         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2640         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2641         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2642         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2644         // Check booleans are set properly for all types
2645         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2646         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2647         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2648         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2649         $this->assertEqual(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2650         $this->assertEqual(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2651         $this->assertEqual(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2652         $this->assertEqual(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2654         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2655         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2656         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2657         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2658         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2659         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2660         $this->assertEqual(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2661         $this->assertEqual(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2663         // Check string data causes exception in numeric types
2664         try {
2665             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2666             $this->fail("Expecting an exception, none occurred");
2667         } catch (exception $e) {
2668             $this->assertTrue($e instanceof dml_exception);
2669         }
2670         try {
2671             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2672             $this->fail("Expecting an exception, none occurred");
2673         } catch (exception $e) {
2674             $this->assertTrue($e instanceof dml_exception);
2675         }
2677         // Check empty string data is stored as 0 in numeric datatypes
2678         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2679         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2680         $this->assertTrue(is_numeric($field) && $field == 0);
2682         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2683         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2684         $this->assertTrue(is_numeric($field) && $field == 0);
2686         // Check empty strings are set properly in string types
2687         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2688         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2689         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2690         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2692         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2693         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2694         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2695         $this->assertEqual(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2696         $this->assertEqual(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2698         // Check various quotes/backslashes combinations in string types
2699         $teststrings = array(
2700             'backslashes and quotes alone (even): "" \'\' \\\\',
2701             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2702             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2703             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2704         foreach ($teststrings as $teststring) {
2705             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2706             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2707             $this->assertEqual($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2708             $this->assertEqual($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2709         }
2711         // Check LOBs in text/binary columns
2712         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2713         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2714         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2715         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2716         $this->assertEqual($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2717         $this->assertEqual($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2719         // And "small" LOBs too, just in case
2720         $newclob = substr($clob, 0, 500);
2721         $newblob = substr($blob, 0, 250);
2722         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2723         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2724         $this->assertEqual($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2725         $this->assertEqual($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2727         // This is the failure from MDL-24863. This was giving an error on MSSQL,
2728         // which converts the '1' to an integer, which cannot then be compared with
2729         // onetext cast to a varchar. This should be fixed and working now.
2730         $newchar = 'frog';
2731         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2732         $params = array('onetext' => '1');
2733         try {
2734             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
2735             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
2736         } catch (dml_exception $e) {
2737             $this->assertFalse(true, 'We have an unexpected exception.');
2738             throw $e;
2739         }
2742     }
2744     public function test_count_records() {
2745         $DB = $this->tdb;
2747         $dbman = $DB->get_manager();
2749         $table = $this->get_test_table();
2750         $tablename = $table->getName();
2752         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2753         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2754         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2755         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2756         $dbman->create_table($table);
2758         $this->assertEqual(0, $DB->count_records($tablename));
2760         $DB->insert_record($tablename, array('course' => 3));
2761         $DB->insert_record($tablename, array('course' => 4));
2762         $DB->insert_record($tablename, array('course' => 5));
2764         $this->assertEqual(3, $DB->count_records($tablename));
2766         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2767         $conditions = array('onetext' => '1');
2768         try {
2769             $DB->count_records($tablename, $conditions);
2770             if (debugging()) {
2771                 // only in debug mode - hopefully all devs test code in debug mode...
2772                 $this->fail('An Exception is missing, expected due to equating of text fields');
2773             }
2774         } catch (exception $e) {
2775             $this->assertTrue($e instanceof dml_exception);
2776             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2777         }
2778     }
2780     public function test_count_records_select() {
2781         $DB = $this->tdb;
2783         $dbman = $DB->get_manager();
2785         $table = $this->get_test_table();
2786         $tablename = $table->getName();
2788         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2789         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2790         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2791         $dbman->create_table($table);
2793         $this->assertEqual(0, $DB->count_records($tablename));
2795         $DB->insert_record($tablename, array('course' => 3));
2796         $DB->insert_record($tablename, array('course' => 4));
2797         $DB->insert_record($tablename, array('course' => 5));
2799         $this->assertEqual(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2800     }
2802     public function test_count_records_sql() {
2803         $DB = $this->tdb;
2804         $dbman = $DB->get_manager();
2806         $table = $this->get_test_table();
2807         $tablename = $table->getName();
2809         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2810         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2811         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2812         $dbman->create_table($table);
2814         $this->assertEqual(0, $DB->count_records($tablename));
2816         $DB->insert_record($tablename, array('course' => 3));
2817         $DB->insert_record($tablename, array('course' => 4));
2818         $DB->insert_record($tablename, array('course' => 5));
2820         $this->assertEqual(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2821     }
2823     public function test_record_exists() {
2824         $DB = $this->tdb;
2825         $dbman = $DB->get_manager();
2827         $table = $this->get_test_table();
2828         $tablename = $table->getName();
2830         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2831         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2832         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2833         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2834         $dbman->create_table($table);
2836         $this->assertEqual(0, $DB->count_records($tablename));
2838         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
2839         $DB->insert_record($tablename, array('course' => 3));
2841         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
2844         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2845         $conditions = array('onetext' => '1');
2846         try {
2847             $DB->record_exists($tablename, $conditions);
2848             if (debugging()) {
2849                 // only in debug mode - hopefully all devs test code in debug mode...
2850                 $this->fail('An Exception is missing, expected due to equating of text fields');
2851             }
2852         } catch (exception $e) {
2853             $this->assertTrue($e instanceof dml_exception);
2854             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2855         }
2856     }
2858     public function test_record_exists_select() {
2859         $DB = $this->tdb;
2860         $dbman = $DB->get_manager();
2862         $table = $this->get_test_table();
2863         $tablename = $table->getName();
2865         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2866         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2867         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2868         $dbman->create_table($table);
2870         $this->assertEqual(0, $DB->count_records($tablename));
2872         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
2873         $DB->insert_record($tablename, array('course' => 3));
2875         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
2876     }
2878     public function test_record_exists_sql() {
2879         $DB = $this->tdb;
2880         $dbman = $DB->get_manager();
2882         $table = $this->get_test_table();
2883         $tablename = $table->getName();
2885         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2886         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2887         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2888         $dbman->create_table($table);
2890         $this->assertEqual(0, $DB->count_records($tablename));
2892         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2893         $DB->insert_record($tablename, array('course' => 3));
2895         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2896     }
2898     public function test_recordset_locks_delete() {
2899         $DB = $this->tdb;
2900         $dbman = $DB->get_manager();
2902         //Setup
2903         $table = $this->get_test_table();
2904         $tablename = $table->getName();
2906         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2907         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2908         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2909         $dbman->create_table($table);
2911         $DB->insert_record($tablename, array('course' => 1));
2912         $DB->insert_record($tablename, array('course' => 2));
2913         $DB->insert_record($tablename, array('course' => 3));
2914         $DB->insert_record($tablename, array('course' => 4));
2915         $DB->insert_record($tablename, array('course' => 5));
2916         $DB->insert_record($tablename, array('course' => 6));
2918         // Test against db write locking while on an open recordset
2919         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2920         foreach ($rs as $record) {
2921             $cid = $record->course;
2922             $DB->delete_records($tablename, array('course' => $cid));
2923             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2924         }
2925         $rs->close();
2927         $this->assertEqual(4, $DB->count_records($tablename, array()));
2928     }
2930     public function test_recordset_locks_update() {
2931         $DB = $this->tdb;
2932         $dbman = $DB->get_manager();
2934         //Setup
2935         $table = $this->get_test_table();
2936         $tablename = $table->getName();
2938         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2939         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2940         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2941         $dbman->create_table($table);
2943         $DB->insert_record($tablename, array('course' => 1));
2944         $DB->insert_record($tablename, array('course' => 2));
2945         $DB->insert_record($tablename, array('course' => 3));
2946         $DB->insert_record($tablename, array('course' => 4));
2947         $DB->insert_record($tablename, array('course' => 5));
2948         $DB->insert_record($tablename, array('course' => 6));
2950         // Test against db write locking while on an open recordset
2951         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2952         foreach ($rs as $record) {
2953             $cid = $record->course;
2954             $DB->set_field($tablename, 'course', 10, array('course' => $cid));
2955             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2956         }
2957         $rs->close();
2959         $this->assertEqual(2, $DB->count_records($tablename, array('course' => 10)));
2960     }
2962     public function test_delete_records() {
2963         $DB = $this->tdb;
2964         $dbman = $DB->get_manager();
2966         $table = $this->get_test_table();
2967         $tablename = $table->getName();
2969         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2970         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2971         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2972         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2973         $dbman->create_table($table);
2975         $DB->insert_record($tablename, array('course' => 3));
2976         $DB->insert_record($tablename, array('course' => 2));
2977         $DB->insert_record($tablename, array('course' => 2));
2979         // Delete all records
2980         $this->assertTrue($DB->delete_records($tablename));
2981         $this->assertEqual(0, $DB->count_records($tablename));
2983         // Delete subset of records
2984         $DB->insert_record($tablename, array('course' => 3));
2985         $DB->insert_record($tablename, array('course' => 2));
2986         $DB->insert_record($tablename, array('course' => 2));
2988         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
2989         $this->assertEqual(1, $DB->count_records($tablename));
2991         // delete all
2992         $this->assertTrue($DB->delete_records($tablename, array()));
2993         $this->assertEqual(0, $DB->count_records($tablename));
2995         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2996         $conditions = array('onetext'=>'1');
2997         try {
2998             $DB->delete_records($tablename, $conditions);
2999             if (debugging()) {
3000                 // only in debug mode - hopefully all devs test code in debug mode...
3001                 $this->fail('An Exception is missing, expected due to equating of text fields');
3002             }
3003         } catch (exception $e) {
3004             $this->assertTrue($e instanceof dml_exception);
3005             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
3006         }
3008         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
3009         $conditions = array('onetext' => 1);
3010         try {
3011             $DB->delete_records($tablename, $conditions);
3012             if (debugging()) {
3013                 // only in debug mode - hopefully all devs test code in debug mode...
3014                 $this->fail('An Exception is missing, expected due to equating of text fields');
3015             }
3016         } catch (exception $e) {
3017             $this->assertTrue($e instanceof dml_exception);
3018             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
3019         }
3020     }
3022     public function test_delete_records_select() {
3023         $DB = $this->tdb;
3024         $dbman = $DB->get_manager();
3026         $table = $this->get_test_table();
3027         $tablename = $table->getName();
3029         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3030         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3031         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3032         $dbman->create_table($table);
3034         $DB->insert_record($tablename, array('course' => 3));
3035         $DB->insert_record($tablename, array('course' => 2));
3036         $DB->insert_record($tablename, array('course' => 2));
3038         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
3039         $this->assertEqual(1, $DB->count_records($tablename));
3040     }
3042     public function test_delete_records_list() {
3043         $DB = $this->tdb;
3044         $dbman = $DB->get_manager();
3046         $table = $this->get_test_table();
3047         $tablename = $table->getName();
3049         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3050         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3051         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3052         $dbman->create_table($table);
3054         $DB->insert_record($tablename, array('course' => 1));
3055         $DB->insert_record($tablename, array('course' => 2));
3056         $DB->insert_record($tablename, array('course' => 3));
3058         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
3059         $this->assertEqual(1, $DB->count_records($tablename));
3061         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); /// Must delete 0 rows without conditions. MDL-17645
3062         $this->assertEqual(1, $DB->count_records($tablename));
3063     }
3065     public function test_object_params() {
3066         $DB = $this->tdb;
3067         $dbman = $DB->get_manager();
3069         $table = $this->get_test_table();
3070         $tablename = $table->getName();
3071         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3072         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3073         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3074         $dbman->create_table($table);
3076         $o = new stdClass(); // objects without __toString - never worked
3077         try {
3078             $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
3079             $this->fail('coding_exception expected');
3080         } catch (Exception $e) {
3081             $this->assertTrue($e instanceof coding_exception);
3082         }
3084         // objects with __toString() forbidden everywhere since 2.3
3085         $o = new dml_test_object_one();
3086         try {
3087             $DB->fix_sql_params("SELECT {{$tablename}} WHERE course = ? ", array($o));
3088             $this->fail('coding_exception expected');
3089         } catch (Exception $e) {
3090             $this->assertTrue($e instanceof coding_exception);
3091         }
3093         try {
3094             $DB->execute("SELECT {{$tablename}} WHERE course = ? ", array($o));
3095             $this->fail('coding_exception expected');
3096         } catch (Exception $e) {
3097             $this->assertTrue($e instanceof coding_exception);
3098         }
3100         try {
3101             $DB->get_recordset_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
3102             $this->fail('coding_exception expected');
3103         } catch (Exception $e) {
3104             $this->assertTrue($e instanceof coding_exception);
3105         }
3107         try {
3108             $DB->get_records_sql("SELECT {{$tablename}} WHERE course = ? ", array($o));
3109             $this->fail('coding_exception expected');
3110         } catch (Exception $e) {
3111             $this->assertTrue($e instanceof coding_exception);
3112         }
3114         try {
3115             $record = new stdClass();
3116             $record->course = $o;
3117             $DB->insert_record_raw($tablename, $record);
3118             $this->fail('coding_exception expected');
3119         } catch (Exception $e) {
3120             $this->assertTrue($e instanceof coding_exception);
3121         }
3123         try {
3124             $record = new stdClass();
3125             $record->course = $o;
3126             $DB->insert_record($tablename, $record);
3127             $this->fail('coding_exception expected');
3128         } catch (Exception $e) {
3129             $this->assertTrue($e instanceof coding_exception);
3130         }
3132         try {
3133             $record = new stdClass();
3134             $record->course = $o;
3135             $DB->import_record($tablename, $record);
3136             $this->fail('coding_exception expected');
3137         } catch (Exception $e) {
3138             $this->assertTrue($e instanceof coding_exception);
3139         }
3141         try {
3142             $record = new stdClass();
3143             $record->id = 1;
3144             $record->course = $o;
3145             $DB->update_record_raw($tablename, $record);
3146             $this->fail('coding_exception expected');
3147         } catch (Exception $e) {
3148             $this->assertTrue($e instanceof coding_exception);
3149         }
3151         try {
3152             $record = new stdClass();
3153             $record->id = 1;
3154             $record->course = $o;
3155             $DB->update_record($tablename, $record);
3156             $this->fail('coding_exception expected');
3157         } catch (Exception $e) {
3158             $this->assertTrue($e instanceof coding_exception);
3159         }
3161         try {
3162             $DB->set_field_select($tablename, 'course', 1, "course = ? ", array($o));
3163             $this->fail('coding_exception expected');
3164         } catch (Exception $e) {
3165             $this->assertTrue($e instanceof coding_exception);
3166         }
3168         try {
3169             $DB->delete_records_select($tablename, "course = ? ", array($o));
3170             $this->fail('coding_exception expected');
3171         } catch (Exception $e) {
3172             $this->assertTrue($e instanceof coding_exception);
3173         }
3174     }
3176     function test_sql_null_from_clause() {
3177         $DB = $this->tdb;
3178         $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
3179         $this->assertEqual($DB->get_field_sql($sql), 1);
3180     }
3182     function test_sql_bitand() {
3183         $DB = $this->tdb;
3184         $dbman = $DB->get_manager();
3186         $table = $this->get_test_table();
3187         $tablename = $table->getName();
3189         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3190         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3191         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3192         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3193         $dbman->create_table($table);
3195         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3197         $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
3198         $this->assertEqual($DB->get_field_sql($sql), 2);
3200         $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
3201         $result = $DB->get_records_sql($sql);
3202         $this->assertEqual(count($result), 1);
3203         $this->assertEqual(reset($result)->res, 2);
3205         $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
3206         $result = $DB->get_records_sql($sql, array(10));
3207         $this->assertEqual(count($result), 1);
3208         $this->assertEqual(reset($result)->res, 2);
3209     }
3211     function test_sql_bitnot() {
3212         $DB = $this->tdb;
3214         $not = $DB->sql_bitnot(2);
3215         $notlimited = $DB->sql_bitand($not, 7); // might be positive or negative number which can not fit into PHP INT!
3217         $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
3218         $this->assertEqual($DB->get_field_sql($sql), 5);
3219     }
3221     function test_sql_bitor() {
3222         $DB = $this->tdb;
3223         $dbman = $DB->get_manager();
3225         $table = $this->get_test_table();
3226         $tablename = $table->getName();
3228         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3229         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3230         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3231         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3232         $dbman->create_table($table);
3234         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3236         $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
3237         $this->assertEqual($DB->get_field_sql($sql), 11);
3239         $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
3240         $result = $DB->get_records_sql($sql);
3241         $this->assertEqual(count($result), 1);
3242         $this->assertEqual(reset($result)->res, 11);
3244         $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
3245         $result = $DB->get_records_sql($sql, array(10));
3246         $this->assertEqual(count($result), 1);
3247         $this->assertEqual(reset($result)->res, 11);
3248     }
3250     function test_sql_bitxor() {
3251         $DB = $this->tdb;
3252         $dbman = $DB->get_manager();
3254         $table = $this->get_test_table();
3255         $tablename = $table->getName();
3257         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3258         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3259         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3260         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3261         $dbman->create_table($table);
3263         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3265         $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
3266         $this->assertEqual($DB->get_field_sql($sql), 9);