802b68f9223b5f93747c95b2d41841d7639c0c67
[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         }
824     }
826     public function test_get_manager() {
827         $DB = $this->tdb;
828         $dbman = $this->tdb->get_manager();
830         $this->assertTrue($dbman instanceof database_manager);
831     }
833     public function test_setup_is_unicodedb() {
834         $DB = $this->tdb;
835         $this->assertTrue($DB->setup_is_unicodedb());
836     }
838     public function test_set_debug() { //tests get_debug() too
839         $DB = $this->tdb;
840         $dbman = $this->tdb->get_manager();
842         $table = $this->get_test_table();
843         $tablename = $table->getName();
845         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
846         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
847         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
848         $dbman->create_table($table);
850         $sql = "SELECT * FROM {{$tablename}}";
852         $prevdebug = $DB->get_debug();
854         ob_start();
855         $DB->set_debug(true);
856         $this->assertTrue($DB->get_debug());
857         $DB->execute($sql);
858         $DB->set_debug(false);
859         $this->assertFalse($DB->get_debug());
860         $debuginfo = ob_get_contents();
861         ob_end_clean();
862         $this->assertFalse($debuginfo === '');
864         ob_start();
865         $DB->execute($sql);
866         $debuginfo = ob_get_contents();
867         ob_end_clean();
868         $this->assertTrue($debuginfo === '');
870         $DB->set_debug($prevdebug);
871     }
873     public function test_execute() {
874         $DB = $this->tdb;
875         $dbman = $this->tdb->get_manager();
877         $table1 = $this->get_test_table('1');
878         $tablename1 = $table1->getName();
879         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
880         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
881         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
882         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
883         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
884         $dbman->create_table($table1);
886         $table2 = $this->get_test_table('2');
887         $tablename2 = $table2->getName();
888         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
889         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
890         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
891         $dbman->create_table($table2);
893         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
894         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
895         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
896         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
898         // select results are ignored
899         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
900         $this->assertTrue($DB->execute($sql, array('course'=>3)));
902         // throw exception on error
903         $sql = "XXUPDATE SET XSSD";
904         try {
905             $DB->execute($sql);
906             $this->fail("Expecting an exception, none occurred");
907         } catch (Exception $e) {
908             $this->assertTrue($e instanceof dml_exception);
909         }
911         // update records
912         $sql = "UPDATE {{$tablename1}}
913                    SET course = 6
914                  WHERE course = ?";
915         $this->assertTrue($DB->execute($sql, array('3')));
916         $this->assertEqual($DB->count_records($tablename1, array('course' => 6)), 2);
918         // insert from one into second table
919         $sql = "INSERT INTO {{$tablename2}} (course)
921                 SELECT course
922                   FROM {{$tablename1}}";
923         $this->assertTrue($DB->execute($sql));
924         $this->assertEqual($DB->count_records($tablename2), 4);
925     }
927     public function test_get_recordset() {
928         $DB = $this->tdb;
929         $dbman = $DB->get_manager();
931         $table = $this->get_test_table();
932         $tablename = $table->getName();
934         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
935         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
936         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
937         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
938         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
939         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
940         $dbman->create_table($table);
942         $data = array(array('id' => 1, 'course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
943                       array('id' => 2, 'course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
944                       array('id' => 3, 'course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
946         foreach ($data as $record) {
947             $DB->insert_record($tablename, $record);
948         }
950         // standard recordset iteration
951         $rs = $DB->get_recordset($tablename);
952         $this->assertTrue($rs instanceof moodle_recordset);
953         reset($data);
954         foreach($rs as $record) {
955             $data_record = current($data);
956             foreach ($record as $k => $v) {
957                 $this->assertEqual($data_record[$k], $v);
958             }
959             next($data);
960         }
961         $rs->close();
963         // iterator style usage
964         $rs = $DB->get_recordset($tablename);
965         $this->assertTrue($rs instanceof moodle_recordset);
966         reset($data);
967         while ($rs->valid()) {
968             $record = $rs->current();
969             $data_record = current($data);
970             foreach ($record as $k => $v) {
971                 $this->assertEqual($data_record[$k], $v);
972             }
973             next($data);
974             $rs->next();
975         }
976         $rs->close();
978         // make sure rewind is ignored
979         $rs = $DB->get_recordset($tablename);
980         $this->assertTrue($rs instanceof moodle_recordset);
981         reset($data);
982         $i = 0;
983         foreach($rs as $record) {
984             $i++;
985             $rs->rewind();
986             if ($i > 10) {
987                 $this->fail('revind not ignored in recordsets');
988                 break;
989             }
990             $data_record = current($data);
991             foreach ($record as $k => $v) {
992                 $this->assertEqual($data_record[$k], $v);
993             }
994             next($data);
995         }
996         $rs->close();
998         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
999         $conditions = array('onetext' => '1');
1000         try {
1001             $rs = $DB->get_recordset($tablename, $conditions);
1002             $this->fail('An Exception is missing, expected due to equating of text fields');
1003         } catch (exception $e) {
1004             $this->assertTrue($e instanceof dml_exception);
1005             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1006         }
1008         // notes:
1009         //  * limits are tested in test_get_recordset_sql()
1010         //  * where_clause() is used internally and is tested in test_get_records()
1011     }
1013     public function test_get_recordset_iterator_keys() {
1014         $DB = $this->tdb;
1015         $dbman = $DB->get_manager();
1017         $table = $this->get_test_table();
1018         $tablename = $table->getName();
1020         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1021         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1022         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
1023         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1024         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1025         $dbman->create_table($table);
1027         $data = array(array('id'=> 1, 'course' => 3, 'name' => 'record1'),
1028                       array('id'=> 2, 'course' => 3, 'name' => 'record2'),
1029                       array('id'=> 3, 'course' => 5, 'name' => 'record3'));
1030         foreach ($data as $record) {
1031             $DB->insert_record($tablename, $record);
1032         }
1034         // Test repeated numeric keys are returned ok
1035         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
1037         reset($data);
1038         $count = 0;
1039         foreach($rs as $key => $record) {
1040             $data_record = current($data);
1041             $this->assertEqual($data_record['course'], $key);
1042             next($data);
1043             $count++;
1044         }
1045         $rs->close();
1046         $this->assertEqual($count, 3);
1048         // Test string keys are returned ok
1049         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
1051         reset($data);
1052         $count = 0;
1053         foreach($rs as $key => $record) {
1054             $data_record = current($data);
1055             $this->assertEqual($data_record['name'], $key);
1056             next($data);
1057             $count++;
1058         }
1059         $rs->close();
1060         $this->assertEqual($count, 3);
1062         // Test numeric not starting in 1 keys are returned ok
1063         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
1065         $data = array_reverse($data);
1066         reset($data);
1067         $count = 0;
1068         foreach($rs as $key => $record) {
1069             $data_record = current($data);
1070             $this->assertEqual($data_record['id'], $key);
1071             next($data);
1072             $count++;
1073         }
1074         $rs->close();
1075         $this->assertEqual($count, 3);
1076     }
1078     public function test_get_recordset_list() {
1079         $DB = $this->tdb;
1080         $dbman = $DB->get_manager();
1082         $table = $this->get_test_table();
1083         $tablename = $table->getName();
1085         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1086         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1087         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1088         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1089         $dbman->create_table($table);
1091         $DB->insert_record($tablename, array('course' => 3));
1092         $DB->insert_record($tablename, array('course' => 3));
1093         $DB->insert_record($tablename, array('course' => 5));
1094         $DB->insert_record($tablename, array('course' => 2));
1096         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
1098         $counter = 0;
1099         foreach ($rs as $record) {
1100             $counter++;
1101         }
1102         $this->assertEqual(3, $counter);
1103         $rs->close();
1105         $rs = $DB->get_recordset_list($tablename, 'course',array()); /// Must return 0 rows without conditions. MDL-17645
1107         $counter = 0;
1108         foreach ($rs as $record) {
1109             $counter++;
1110         }
1111         $rs->close();
1112         $this->assertEqual(0, $counter);
1114         // notes:
1115         //  * limits are tested in test_get_recordset_sql()
1116         //  * where_clause() is used internally and is tested in test_get_records()
1117     }
1119     public function test_get_recordset_select() {
1120         $DB = $this->tdb;
1121         $dbman = $DB->get_manager();
1123         $table = $this->get_test_table();
1124         $tablename = $table->getName();
1126         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1127         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1128         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1129         $dbman->create_table($table);
1131         $DB->insert_record($tablename, array('course' => 3));
1132         $DB->insert_record($tablename, array('course' => 3));
1133         $DB->insert_record($tablename, array('course' => 5));
1134         $DB->insert_record($tablename, array('course' => 2));
1136         $rs = $DB->get_recordset_select($tablename, '');
1137         $counter = 0;
1138         foreach ($rs as $record) {
1139             $counter++;
1140         }
1141         $rs->close();
1142         $this->assertEqual(4, $counter);
1144         $this->assertTrue($rs = $DB->get_recordset_select($tablename, 'course = 3'));
1145         $counter = 0;
1146         foreach ($rs as $record) {
1147             $counter++;
1148         }
1149         $rs->close();
1150         $this->assertEqual(2, $counter);
1152         // notes:
1153         //  * limits are tested in test_get_recordset_sql()
1154     }
1156     public function test_get_recordset_sql() {
1157         $DB = $this->tdb;
1158         $dbman = $DB->get_manager();
1160         $table = $this->get_test_table();
1161         $tablename = $table->getName();
1163         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1164         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1165         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1166         $dbman->create_table($table);
1168         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1169         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1170         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1171         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1172         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1173         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1174         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1176         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1177         $counter = 0;
1178         foreach ($rs as $record) {
1179             $counter++;
1180         }
1181         $rs->close();
1182         $this->assertEqual(2, $counter);
1184         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
1185         // only limitfrom = skips that number of records
1186         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1187         $records = array();
1188         foreach($rs as $key => $record) {
1189             $records[$key] = $record;
1190         }
1191         $rs->close();
1192         $this->assertEqual(5, count($records));
1193         $this->assertEqual($inskey3, reset($records)->id);
1194         $this->assertEqual($inskey7, end($records)->id);
1196         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
1197     }
1199     public function test_get_records() {
1200         $DB = $this->tdb;
1201         $dbman = $DB->get_manager();
1203         $table = $this->get_test_table();
1204         $tablename = $table->getName();
1206         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1207         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1208         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1209         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1210         $dbman->create_table($table);
1212         $DB->insert_record($tablename, array('course' => 3));
1213         $DB->insert_record($tablename, array('course' => 3));
1214         $DB->insert_record($tablename, array('course' => 5));
1215         $DB->insert_record($tablename, array('course' => 2));
1217         // All records
1218         $records = $DB->get_records($tablename);
1219         $this->assertEqual(4, count($records));
1220         $this->assertEqual(3, $records[1]->course);
1221         $this->assertEqual(3, $records[2]->course);
1222         $this->assertEqual(5, $records[3]->course);
1223         $this->assertEqual(2, $records[4]->course);
1225         // Records matching certain conditions
1226         $records = $DB->get_records($tablename, array('course' => 3));
1227         $this->assertEqual(2, count($records));
1228         $this->assertEqual(3, $records[1]->course);
1229         $this->assertEqual(3, $records[2]->course);
1231         // All records sorted by course
1232         $records = $DB->get_records($tablename, null, 'course');
1233         $this->assertEqual(4, count($records));
1234         $current_record = reset($records);
1235         $this->assertEqual(4, $current_record->id);
1236         $current_record = next($records);
1237         $this->assertEqual(1, $current_record->id);
1238         $current_record = next($records);
1239         $this->assertEqual(2, $current_record->id);
1240         $current_record = next($records);
1241         $this->assertEqual(3, $current_record->id);
1243         // All records, but get only one field
1244         $records = $DB->get_records($tablename, null, '', 'id');
1245         $this->assertFalse(isset($records[1]->course));
1246         $this->assertTrue(isset($records[1]->id));
1247         $this->assertEqual(4, count($records));
1249         // Booleans into params
1250         $records = $DB->get_records($tablename, array('course' => true));
1251         $this->assertEqual(0, count($records));
1252         $records = $DB->get_records($tablename, array('course' => false));
1253         $this->assertEqual(0, count($records));
1255         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1256         $conditions = array('onetext' => '1');
1257         try {
1258             $records = $DB->get_records($tablename, $conditions);
1259             $this->fail('An Exception is missing, expected due to equating of text fields');
1260         } catch (exception $e) {
1261             $this->assertTrue($e instanceof dml_exception);
1262             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1263         }
1265         // note: delegate limits testing to test_get_records_sql()
1266     }
1268     public function test_get_records_list() {
1269         $DB = $this->tdb;
1270         $dbman = $DB->get_manager();
1272         $table = $this->get_test_table();
1273         $tablename = $table->getName();
1275         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1276         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1277         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1278         $dbman->create_table($table);
1280         $DB->insert_record($tablename, array('course' => 3));
1281         $DB->insert_record($tablename, array('course' => 3));
1282         $DB->insert_record($tablename, array('course' => 5));
1283         $DB->insert_record($tablename, array('course' => 2));
1285         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
1286         $this->assertTrue(is_array($records));
1287         $this->assertEqual(3, count($records));
1288         $this->assertEqual(1, reset($records)->id);
1289         $this->assertEqual(2, next($records)->id);
1290         $this->assertEqual(4, next($records)->id);
1292         $this->assertIdentical(array(), $records = $DB->get_records_list($tablename, 'course', array())); /// Must return 0 rows without conditions. MDL-17645
1293         $this->assertEqual(0, count($records));
1295         // note: delegate limits testing to test_get_records_sql()
1296     }
1298     public function test_get_records_sql() {
1299         $DB = $this->tdb;
1300         $dbman = $DB->get_manager();
1302         $table = $this->get_test_table();
1303         $tablename = $table->getName();
1305         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1306         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1307         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1308         $dbman->create_table($table);
1310         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1311         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1312         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1313         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1314         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1315         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1316         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1318         $table2 = $this->get_test_table("2");
1319         $tablename2 = $table2->getName();
1320         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1321         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1322         $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
1323         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1324         $dbman->create_table($table2);
1326         $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
1327         $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
1328         $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
1329         $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
1331         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1332         $this->assertEqual(2, count($records));
1333         $this->assertEqual($inskey1, reset($records)->id);
1334         $this->assertEqual($inskey4, next($records)->id);
1336         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1337         $this->enable_debugging();
1338         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1339         $this->assertFalse($this->get_debugging() === '');
1340         $this->assertEqual(6, count($records));
1342         // negative limits = no limits
1343         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1344         $this->assertEqual(7, count($records));
1346         // zero limits = no limits
1347         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1348         $this->assertEqual(7, count($records));
1350         // only limitfrom = skips that number of records
1351         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1352         $this->assertEqual(5, count($records));
1353         $this->assertEqual($inskey3, reset($records)->id);
1354         $this->assertEqual($inskey7, end($records)->id);
1356         // only limitnum = fetches that number of records
1357         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1358         $this->assertEqual(3, count($records));
1359         $this->assertEqual($inskey1, reset($records)->id);
1360         $this->assertEqual($inskey3, end($records)->id);
1362         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1363         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1364         $this->assertEqual(2, count($records));
1365         $this->assertEqual($inskey4, reset($records)->id);
1366         $this->assertEqual($inskey5, end($records)->id);
1368         // both limitfrom and limitnum in query having subqueris
1369         // note the subquery skips records with course = 0 and 3
1370         $sql = "SELECT * FROM {{$tablename}}
1371                  WHERE course NOT IN (
1372                      SELECT course FROM {{$tablename}}
1373                       WHERE course IN (0, 3))
1374                 ORDER BY course";
1375         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2
1376         $this->assertEqual(2, count($records));
1377         $this->assertEqual($inskey6, reset($records)->id);
1378         $this->assertEqual($inskey5, end($records)->id);
1379         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2
1380         $this->assertEqual(2, count($records));
1381         $this->assertEqual($inskey3, reset($records)->id);
1382         $this->assertEqual($inskey2, end($records)->id);
1384         // test 2 tables with aliases and limits with order bys
1385         $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
1386                   FROM {{$tablename}} t1, {{$tablename2}} t2
1387                  WHERE t2.course=t1.course
1388               ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
1389         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5
1390         $this->assertEqual(2, count($records));
1391         $this->assertEqual('5', end($records)->cid);
1392         $this->assertEqual('4', reset($records)->cid);
1394         // test 2 tables with aliases and limits with the highest INT limit works
1395         $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}
1396         $this->assertEqual(2, count($records));
1397         $this->assertEqual('5', end($records)->cid);
1398         $this->assertEqual('4', reset($records)->cid);
1400         // test 2 tables with aliases and limits with order bys (limit which is highest INT number)
1401         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses
1402         $this->assertEqual(0, count($records));
1404         // test 2 tables with aliases and limits with order bys (limit which s highest INT number)
1405         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses
1406         $this->assertEqual(0, count($records));
1408         // TODO: Test limits in queries having DISTINCT clauses
1410         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1411     }
1413     public function test_get_records_menu() {
1414         $DB = $this->tdb;
1415         $dbman = $DB->get_manager();
1417         $table = $this->get_test_table();
1418         $tablename = $table->getName();
1420         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1421         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1422         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1423         $dbman->create_table($table);
1425         $DB->insert_record($tablename, array('course' => 3));
1426         $DB->insert_record($tablename, array('course' => 3));
1427         $DB->insert_record($tablename, array('course' => 5));
1428         $DB->insert_record($tablename, array('course' => 2));
1430         $records = $DB->get_records_menu($tablename, array('course' => 3));
1431         $this->assertTrue(is_array($records));
1432         $this->assertEqual(2, count($records));
1433         $this->assertFalse(empty($records[1]));
1434         $this->assertFalse(empty($records[2]));
1435         $this->assertEqual(3, $records[1]);
1436         $this->assertEqual(3, $records[2]);
1438         // note: delegate limits testing to test_get_records_sql()
1439     }
1441     public function test_get_records_select_menu() {
1442         $DB = $this->tdb;
1443         $dbman = $DB->get_manager();
1445         $table = $this->get_test_table();
1446         $tablename = $table->getName();
1448         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1449         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1450         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1451         $dbman->create_table($table);
1453         $DB->insert_record($tablename, array('course' => 3));
1454         $DB->insert_record($tablename, array('course' => 2));
1455         $DB->insert_record($tablename, array('course' => 3));
1456         $DB->insert_record($tablename, array('course' => 5));
1458         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1459         $this->assertTrue(is_array($records));
1461         $this->assertEqual(3, count($records));
1462         $this->assertFalse(empty($records[1]));
1463         $this->assertTrue(empty($records[2]));
1464         $this->assertFalse(empty($records[3]));
1465         $this->assertFalse(empty($records[4]));
1466         $this->assertEqual(3, $records[1]);
1467         $this->assertEqual(3, $records[3]);
1468         $this->assertEqual(5, $records[4]);
1470         // note: delegate limits testing to test_get_records_sql()
1471     }
1473     public function test_get_records_sql_menu() {
1474         $DB = $this->tdb;
1475         $dbman = $DB->get_manager();
1477         $table = $this->get_test_table();
1478         $tablename = $table->getName();
1480         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1481         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1482         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1483         $dbman->create_table($table);
1485         $DB->insert_record($tablename, array('course' => 3));
1486         $DB->insert_record($tablename, array('course' => 2));
1487         $DB->insert_record($tablename, array('course' => 3));
1488         $DB->insert_record($tablename, array('course' => 5));
1490         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1491         $this->assertTrue(is_array($records));
1493         $this->assertEqual(3, count($records));
1494         $this->assertFalse(empty($records[1]));
1495         $this->assertTrue(empty($records[2]));
1496         $this->assertFalse(empty($records[3]));
1497         $this->assertFalse(empty($records[4]));
1498         $this->assertEqual(3, $records[1]);
1499         $this->assertEqual(3, $records[3]);
1500         $this->assertEqual(5, $records[4]);
1502         // note: delegate limits testing to test_get_records_sql()
1503     }
1505     public function test_get_record() {
1506         $DB = $this->tdb;
1507         $dbman = $DB->get_manager();
1509         $table = $this->get_test_table();
1510         $tablename = $table->getName();
1512         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1513         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1514         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1515         $dbman->create_table($table);
1517         $DB->insert_record($tablename, array('course' => 3));
1518         $DB->insert_record($tablename, array('course' => 2));
1520         $record = $DB->get_record($tablename, array('id' => 2));
1521         $this->assertTrue($record instanceof stdClass);
1523         $this->assertEqual(2, $record->course);
1524         $this->assertEqual(2, $record->id);
1525     }
1528     public function test_get_record_select() {
1529         $DB = $this->tdb;
1530         $dbman = $DB->get_manager();
1532         $table = $this->get_test_table();
1533         $tablename = $table->getName();
1535         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1536         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1537         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1538         $dbman->create_table($table);
1540         $DB->insert_record($tablename, array('course' => 3));
1541         $DB->insert_record($tablename, array('course' => 2));
1543         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1544         $this->assertTrue($record instanceof stdClass);
1546         $this->assertEqual(2, $record->course);
1548         // note: delegates limit testing to test_get_records_sql()
1549     }
1551     public function test_get_record_sql() {
1552         $DB = $this->tdb;
1553         $dbman = $DB->get_manager();
1555         $table = $this->get_test_table();
1556         $tablename = $table->getName();
1558         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1559         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1560         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1561         $dbman->create_table($table);
1563         $DB->insert_record($tablename, array('course' => 3));
1564         $DB->insert_record($tablename, array('course' => 2));
1566         // standard use
1567         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1568         $this->assertTrue($record instanceof stdClass);
1569         $this->assertEqual(2, $record->course);
1570         $this->assertEqual(2, $record->id);
1572         // backwards compatibility with $ignoremultiple
1573         $this->assertFalse(IGNORE_MISSING);
1574         $this->assertTrue(IGNORE_MULTIPLE);
1576         // record not found - ignore
1577         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1578         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1580         // record not found error
1581         try {
1582             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1583             $this->fail("Exception expected");
1584         } catch (dml_missing_record_exception $e) {
1585             $this->assertTrue(true);
1586         }
1588         $this->enable_debugging();
1589         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1590         $this->assertFalse($this->get_debugging() === '');
1592         // multiple matches ignored
1593         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1595         // multiple found error
1596         try {
1597             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1598             $this->fail("Exception expected");
1599         } catch (dml_multiple_records_exception $e) {
1600             $this->assertTrue(true);
1601         }
1602     }
1604     public function test_get_field() {
1605         $DB = $this->tdb;
1606         $dbman = $DB->get_manager();
1608         $table = $this->get_test_table();
1609         $tablename = $table->getName();
1611         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1612         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1613         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1614         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1615         $dbman->create_table($table);
1617         $id1 = $DB->insert_record($tablename, array('course' => 3));
1618         $DB->insert_record($tablename, array('course' => 5));
1619         $DB->insert_record($tablename, array('course' => 5));
1621         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1622         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1624         $this->assertIdentical(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1625         try {
1626             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1627             $this->assertFail('Exception expected due to missing record');
1628         } catch (dml_exception $ex) {
1629             $this->assertTrue(true);
1630         }
1632         $this->enable_debugging();
1633         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1634         $this->assertIdentical($this->get_debugging(), '');
1636         $this->enable_debugging();
1637         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1638         $this->assertFalse($this->get_debugging() === '');
1640         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1641         $conditions = array('onetext' => '1');
1642         try {
1643             $DB->get_field($tablename, 'course', $conditions);
1644             $this->fail('An Exception is missing, expected due to equating of text fields');
1645         } catch (exception $e) {
1646             $this->assertTrue($e instanceof dml_exception);
1647             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1648         }
1649     }
1651     public function test_get_field_select() {
1652         $DB = $this->tdb;
1653         $dbman = $DB->get_manager();
1655         $table = $this->get_test_table();
1656         $tablename = $table->getName();
1658         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1659         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1660         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1661         $dbman->create_table($table);
1663         $DB->insert_record($tablename, array('course' => 3));
1665         $this->assertEqual(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1666     }
1668     public function test_get_field_sql() {
1669         $DB = $this->tdb;
1670         $dbman = $DB->get_manager();
1672         $table = $this->get_test_table();
1673         $tablename = $table->getName();
1675         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1676         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1677         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1678         $dbman->create_table($table);
1680         $DB->insert_record($tablename, array('course' => 3));
1682         $this->assertEqual(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1683     }
1685     public function test_get_fieldset_select() {
1686         $DB = $this->tdb;
1687         $dbman = $DB->get_manager();
1689         $table = $this->get_test_table();
1690         $tablename = $table->getName();
1692         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1693         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1694         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1695         $dbman->create_table($table);
1697         $DB->insert_record($tablename, array('course' => 1));
1698         $DB->insert_record($tablename, array('course' => 3));
1699         $DB->insert_record($tablename, array('course' => 2));
1700         $DB->insert_record($tablename, array('course' => 6));
1702         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1703         $this->assertTrue(is_array($fieldset));
1705         $this->assertEqual(3, count($fieldset));
1706         $this->assertEqual(3, $fieldset[0]);
1707         $this->assertEqual(2, $fieldset[1]);
1708         $this->assertEqual(6, $fieldset[2]);
1709     }
1711     public function test_get_fieldset_sql() {
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' => 1));
1724         $DB->insert_record($tablename, array('course' => 3));
1725         $DB->insert_record($tablename, array('course' => 2));
1726         $DB->insert_record($tablename, array('course' => 6));
1728         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1729         $this->assertTrue(is_array($fieldset));
1731         $this->assertEqual(3, count($fieldset));
1732         $this->assertEqual(2, $fieldset[0]);
1733         $this->assertEqual(3, $fieldset[1]);
1734         $this->assertEqual(4, $fieldset[2]);
1735     }
1737     public function test_insert_record_raw() {
1738         $DB = $this->tdb;
1739         $dbman = $DB->get_manager();
1741         $table = $this->get_test_table();
1742         $tablename = $table->getName();
1744         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1745         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1746         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1747         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1748         $dbman->create_table($table);
1750         $record = (object)array('course' => 1, 'onechar' => 'xx');
1751         $before = clone($record);
1752         $result = $DB->insert_record_raw($tablename, $record);
1753         $this->assertIdentical(1, $result);
1754         $this->assertIdentical($record, $before);
1756         $record = $DB->get_record($tablename, array('course' => 1));
1757         $this->assertTrue($record instanceof stdClass);
1758         $this->assertIdentical('xx', $record->onechar);
1760         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1761         $this->assertIdentical(true, $result);
1763         // note: bulk not implemented yet
1764         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1765         $record = $DB->get_record($tablename, array('course' => 3));
1766         $this->assertTrue($record instanceof stdClass);
1767         $this->assertIdentical('zz', $record->onechar);
1769         // custom sequence (id) - returnid is ignored
1770         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1771         $this->assertIdentical(true, $result);
1772         $record = $DB->get_record($tablename, array('id' => 10));
1773         $this->assertTrue($record instanceof stdClass);
1774         $this->assertIdentical('bb', $record->onechar);
1776         // custom sequence - missing id error
1777         try {
1778             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1779             $this->assertFail('Exception expected due to missing record');
1780         } catch (coding_exception $ex) {
1781             $this->assertTrue(true);
1782         }
1784         // wrong column error
1785         try {
1786             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1787             $this->assertFail('Exception expected due to invalid column');
1788         } catch (dml_exception $ex) {
1789             $this->assertTrue(true);
1790         }
1791     }
1793     public function test_insert_record() {
1794         // All the information in this test is fetched from DB by get_recordset() so we
1795         // have such method properly tested against nulls, empties and friends...
1797         $DB = $this->tdb;
1798         $dbman = $DB->get_manager();
1800         $table = $this->get_test_table();
1801         $tablename = $table->getName();
1803         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1804         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1805         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1806         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1807         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1808         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1809         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1810         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1811         $dbman->create_table($table);
1813         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1814         $record = $DB->get_record($tablename, array('course' => 1));
1815         $this->assertEqual(1, $record->id);
1816         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1817         $this->assertEqual(200, $record->onenum);
1818         $this->assertIdentical('onestring', $record->onechar);
1819         $this->assertNull($record->onetext);
1820         $this->assertNull($record->onebinary);
1822         // without returning id, bulk not implemented
1823         $result = $this->assertIdentical(true, $DB->insert_record($tablename, array('course' => 99), false, true));
1824         $record = $DB->get_record($tablename, array('course' => 99));
1825         $this->assertEqual(2, $record->id);
1826         $this->assertEqual(99, $record->course);
1828         // Check nulls are set properly for all types
1829         $record = new stdClass();
1830         $record->oneint = null;
1831         $record->onenum = null;
1832         $record->onechar = null;
1833         $record->onetext = null;
1834         $record->onebinary = null;
1835         $recid = $DB->insert_record($tablename, $record);
1836         $record = $DB->get_record($tablename, array('id' => $recid));
1837         $this->assertEqual(0, $record->course);
1838         $this->assertNull($record->oneint);
1839         $this->assertNull($record->onenum);
1840         $this->assertNull($record->onechar);
1841         $this->assertNull($record->onetext);
1842         $this->assertNull($record->onebinary);
1844         // Check zeros are set properly for all types
1845         $record = new stdClass();
1846         $record->oneint = 0;
1847         $record->onenum = 0;
1848         $recid = $DB->insert_record($tablename, $record);
1849         $record = $DB->get_record($tablename, array('id' => $recid));
1850         $this->assertEqual(0, $record->oneint);
1851         $this->assertEqual(0, $record->onenum);
1853         // Check booleans are set properly for all types
1854         $record = new stdClass();
1855         $record->oneint = true; // trues
1856         $record->onenum = true;
1857         $record->onechar = true;
1858         $record->onetext = true;
1859         $recid = $DB->insert_record($tablename, $record);
1860         $record = $DB->get_record($tablename, array('id' => $recid));
1861         $this->assertEqual(1, $record->oneint);
1862         $this->assertEqual(1, $record->onenum);
1863         $this->assertEqual(1, $record->onechar);
1864         $this->assertEqual(1, $record->onetext);
1866         $record = new stdClass();
1867         $record->oneint = false; // falses
1868         $record->onenum = false;
1869         $record->onechar = false;
1870         $record->onetext = false;
1871         $recid = $DB->insert_record($tablename, $record);
1872         $record = $DB->get_record($tablename, array('id' => $recid));
1873         $this->assertEqual(0, $record->oneint);
1874         $this->assertEqual(0, $record->onenum);
1875         $this->assertEqual(0, $record->onechar);
1876         $this->assertEqual(0, $record->onetext);
1878         // Check string data causes exception in numeric types
1879         $record = new stdClass();
1880         $record->oneint = 'onestring';
1881         $record->onenum = 0;
1882         try {
1883             $DB->insert_record($tablename, $record);
1884             $this->fail("Expecting an exception, none occurred");
1885         } catch (exception $e) {
1886             $this->assertTrue($e instanceof dml_exception);
1887         }
1888         $record = new stdClass();
1889         $record->oneint = 0;
1890         $record->onenum = 'onestring';
1891         try {
1892            $DB->insert_record($tablename, $record);
1893            $this->fail("Expecting an exception, none occurred");
1894         } catch (exception $e) {
1895             $this->assertTrue($e instanceof dml_exception);
1896         }
1898         // Check empty string data is stored as 0 in numeric datatypes
1899         $record = new stdClass();
1900         $record->oneint = ''; // empty string
1901         $record->onenum = 0;
1902         $recid = $DB->insert_record($tablename, $record);
1903         $record = $DB->get_record($tablename, array('id' => $recid));
1904         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1906         $record = new stdClass();
1907         $record->oneint = 0;
1908         $record->onenum = ''; // empty string
1909         $recid = $DB->insert_record($tablename, $record);
1910         $record = $DB->get_record($tablename, array('id' => $recid));
1911         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1913         // Check empty strings are set properly in string types
1914         $record = new stdClass();
1915         $record->oneint = 0;
1916         $record->onenum = 0;
1917         $record->onechar = '';
1918         $record->onetext = '';
1919         $recid = $DB->insert_record($tablename, $record);
1920         $record = $DB->get_record($tablename, array('id' => $recid));
1921         $this->assertTrue($record->onechar === '');
1922         $this->assertTrue($record->onetext === '');
1924         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1925         $record = new stdClass();
1926         $record->oneint = ((210.10 + 39.92) - 150.02);
1927         $record->onenum = ((210.10 + 39.92) - 150.02);
1928         $recid = $DB->insert_record($tablename, $record);
1929         $record = $DB->get_record($tablename, array('id' => $recid));
1930         $this->assertEqual(100, $record->oneint);
1931         $this->assertEqual(100, $record->onenum);
1933         // Check various quotes/backslashes combinations in string types
1934         $teststrings = array(
1935             'backslashes and quotes alone (even): "" \'\' \\\\',
1936             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1937             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1938             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1939         foreach ($teststrings as $teststring) {
1940             $record = new stdClass();
1941             $record->onechar = $teststring;
1942             $record->onetext = $teststring;
1943             $recid = $DB->insert_record($tablename, $record);
1944             $record = $DB->get_record($tablename, array('id' => $recid));
1945             $this->assertEqual($teststring, $record->onechar);
1946             $this->assertEqual($teststring, $record->onetext);
1947         }
1949         // Check LOBs in text/binary columns
1950         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1951         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1952         $record = new stdClass();
1953         $record->onetext = $clob;
1954         $record->onebinary = $blob;
1955         $recid = $DB->insert_record($tablename, $record);
1956         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1957         $record = $rs->current();
1958         $rs->close();
1959         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1960         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1962         // And "small" LOBs too, just in case
1963         $newclob = substr($clob, 0, 500);
1964         $newblob = substr($blob, 0, 250);
1965         $record = new stdClass();
1966         $record->onetext = $newclob;
1967         $record->onebinary = $newblob;
1968         $recid = $DB->insert_record($tablename, $record);
1969         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1970         $record = $rs->current();
1971         $rs->close();
1972         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1973         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1974         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1976         // And "diagnostic" LOBs too, just in case
1977         $newclob = '\'"\\;/ěščřžýáíé';
1978         $newblob = '\'"\\;/ěščřžýáíé';
1979         $record = new stdClass();
1980         $record->onetext = $newclob;
1981         $record->onebinary = $newblob;
1982         $recid = $DB->insert_record($tablename, $record);
1983         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1984         $record = $rs->current();
1985         $rs->close();
1986         $this->assertIdentical($newclob, $record->onetext);
1987         $this->assertIdentical($newblob, $record->onebinary);
1988         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1990         // test data is not modified
1991         $record = new stdClass();
1992         $record->id     = -1; // has to be ignored
1993         $record->course = 3;
1994         $record->lalala = 'lalal'; // unused
1995         $before = clone($record);
1996         $DB->insert_record($tablename, $record);
1997         $this->assertEqual($record, $before);
1999         // make sure the id is always increasing and never reuses the same id
2000         $id1 = $DB->insert_record($tablename, array('course' => 3));
2001         $id2 = $DB->insert_record($tablename, array('course' => 3));
2002         $this->assertTrue($id1 < $id2);
2003         $DB->delete_records($tablename, array('id'=>$id2));
2004         $id3 = $DB->insert_record($tablename, array('course' => 3));
2005         $this->assertTrue($id2 < $id3);
2006         $DB->delete_records($tablename, array());
2007         $id4 = $DB->insert_record($tablename, array('course' => 3));
2008         $this->assertTrue($id3 < $id4);
2010         // Test saving a float in a CHAR column, and reading it back.
2011         $id = $DB->insert_record($tablename, array('onechar' => 1.0));
2012         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2013         $id = $DB->insert_record($tablename, array('onechar' => 1e20));
2014         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2015         $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
2016         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2017         $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
2018         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2019         $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
2020         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2021         $id = $DB->insert_record($tablename, array('onechar' => 1e300));
2022         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2024         // Test saving a float in a TEXT column, and reading it back.
2025         $id = $DB->insert_record($tablename, array('onetext' => 1.0));
2026         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2027         $id = $DB->insert_record($tablename, array('onetext' => 1e20));
2028         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2029         $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
2030         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2031         $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
2032         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2033         $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
2034         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2035         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
2036         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2037     }
2039     public function test_import_record() {
2040         // All the information in this test is fetched from DB by get_recordset() so we
2041         // have such method properly tested against nulls, empties and friends...
2043         $DB = $this->tdb;
2044         $dbman = $DB->get_manager();
2046         $table = $this->get_test_table();
2047         $tablename = $table->getName();
2049         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2050         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2051         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2052         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2053         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2054         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2055         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2056         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2057         $dbman->create_table($table);
2059         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
2060         $record = $DB->get_record($tablename, array('course' => 1));
2061         $this->assertEqual(1, $record->id);
2062         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2063         $this->assertEqual(200, $record->onenum);
2064         $this->assertIdentical('onestring', $record->onechar);
2065         $this->assertNull($record->onetext);
2066         $this->assertNull($record->onebinary);
2068         // ignore extra columns
2069         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
2070         $before = clone($record);
2071         $this->assertIdentical(true, $DB->import_record($tablename, $record));
2072         $this->assertIdentical($record, $before);
2073         $records = $DB->get_records($tablename);
2074         $this->assertEqual(2, $records[13]->course);
2076         // Check nulls are set properly for all types
2077         $record = new stdClass();
2078         $record->id = 20;
2079         $record->oneint = null;
2080         $record->onenum = null;
2081         $record->onechar = null;
2082         $record->onetext = null;
2083         $record->onebinary = null;
2084         $this->assertTrue($DB->import_record($tablename, $record));
2085         $record = $DB->get_record($tablename, array('id' => 20));
2086         $this->assertEqual(0, $record->course);
2087         $this->assertNull($record->oneint);
2088         $this->assertNull($record->onenum);
2089         $this->assertNull($record->onechar);
2090         $this->assertNull($record->onetext);
2091         $this->assertNull($record->onebinary);
2093         // Check zeros are set properly for all types
2094         $record = new stdClass();
2095         $record->id = 23;
2096         $record->oneint = 0;
2097         $record->onenum = 0;
2098         $this->assertTrue($DB->import_record($tablename, $record));
2099         $record = $DB->get_record($tablename, array('id' => 23));
2100         $this->assertEqual(0, $record->oneint);
2101         $this->assertEqual(0, $record->onenum);
2103         // Check string data causes exception in numeric types
2104         $record = new stdClass();
2105         $record->id = 32;
2106         $record->oneint = 'onestring';
2107         $record->onenum = 0;
2108         try {
2109             $DB->import_record($tablename, $record);
2110             $this->fail("Expecting an exception, none occurred");
2111         } catch (exception $e) {
2112             $this->assertTrue($e instanceof dml_exception);
2113         }
2114         $record = new stdClass();
2115         $record->id = 35;
2116         $record->oneint = 0;
2117         $record->onenum = 'onestring';
2118         try {
2119            $DB->import_record($tablename, $record);
2120            $this->fail("Expecting an exception, none occurred");
2121         } catch (exception $e) {
2122             $this->assertTrue($e instanceof dml_exception);
2123         }
2125         // Check empty strings are set properly in string types
2126         $record = new stdClass();
2127         $record->id = 44;
2128         $record->oneint = 0;
2129         $record->onenum = 0;
2130         $record->onechar = '';
2131         $record->onetext = '';
2132         $this->assertTrue($DB->import_record($tablename, $record));
2133         $record = $DB->get_record($tablename, array('id' => 44));
2134         $this->assertTrue($record->onechar === '');
2135         $this->assertTrue($record->onetext === '');
2137         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2138         $record = new stdClass();
2139         $record->id = 47;
2140         $record->oneint = ((210.10 + 39.92) - 150.02);
2141         $record->onenum = ((210.10 + 39.92) - 150.02);
2142         $this->assertTrue($DB->import_record($tablename, $record));
2143         $record = $DB->get_record($tablename, array('id' => 47));
2144         $this->assertEqual(100, $record->oneint);
2145         $this->assertEqual(100, $record->onenum);
2147         // Check various quotes/backslashes combinations in string types
2148         $i = 50;
2149         $teststrings = array(
2150             'backslashes and quotes alone (even): "" \'\' \\\\',
2151             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2152             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2153             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2154         foreach ($teststrings as $teststring) {
2155             $record = new stdClass();
2156             $record->id = $i;
2157             $record->onechar = $teststring;
2158             $record->onetext = $teststring;
2159             $this->assertTrue($DB->import_record($tablename, $record));
2160             $record = $DB->get_record($tablename, array('id' => $i));
2161             $this->assertEqual($teststring, $record->onechar);
2162             $this->assertEqual($teststring, $record->onetext);
2163             $i = $i + 3;
2164         }
2166         // Check LOBs in text/binary columns
2167         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2168         $record = new stdClass();
2169         $record->id = 70;
2170         $record->onetext = $clob;
2171         $record->onebinary = '';
2172         $this->assertTrue($DB->import_record($tablename, $record));
2173         $rs = $DB->get_recordset($tablename, array('id' => 70));
2174         $record = $rs->current();
2175         $rs->close();
2176         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2178         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2179         $record = new stdClass();
2180         $record->id = 71;
2181         $record->onetext = '';
2182         $record->onebinary = $blob;
2183         $this->assertTrue($DB->import_record($tablename, $record));
2184         $rs = $DB->get_recordset($tablename, array('id' => 71));
2185         $record = $rs->current();
2186         $rs->close();
2187         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2189         // And "small" LOBs too, just in case
2190         $newclob = substr($clob, 0, 500);
2191         $newblob = substr($blob, 0, 250);
2192         $record = new stdClass();
2193         $record->id = 73;
2194         $record->onetext = $newclob;
2195         $record->onebinary = $newblob;
2196         $this->assertTrue($DB->import_record($tablename, $record));
2197         $rs = $DB->get_recordset($tablename, array('id' => 73));
2198         $record = $rs->current();
2199         $rs->close();
2200         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2201         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2202         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2203     }
2205     public function test_update_record_raw() {
2206         $DB = $this->tdb;
2207         $dbman = $DB->get_manager();
2209         $table = $this->get_test_table();
2210         $tablename = $table->getName();
2212         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2213         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2214         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2215         $dbman->create_table($table);
2217         $DB->insert_record($tablename, array('course' => 1));
2218         $DB->insert_record($tablename, array('course' => 3));
2220         $record = $DB->get_record($tablename, array('course' => 1));
2221         $record->course = 2;
2222         $this->assertTrue($DB->update_record_raw($tablename, $record));
2223         $this->assertEqual(0, $DB->count_records($tablename, array('course' => 1)));
2224         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 2)));
2225         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 3)));
2227         $record = $DB->get_record($tablename, array('course' => 1));
2228         $record->xxxxx = 2;
2229         try {
2230            $DB->update_record_raw($tablename, $record);
2231            $this->fail("Expecting an exception, none occurred");
2232         } catch (Exception $e) {
2233             $this->assertTrue($e instanceof coding_exception);
2234         }
2236         $record = $DB->get_record($tablename, array('course' => 3));
2237         unset($record->id);
2238         try {
2239            $DB->update_record_raw($tablename, $record);
2240            $this->fail("Expecting an exception, none occurred");
2241         } catch (Exception $e) {
2242             $this->assertTrue($e instanceof coding_exception);
2243         }
2244     }
2246     public function test_update_record() {
2248         // All the information in this test is fetched from DB by get_record() so we
2249         // have such method properly tested against nulls, empties and friends...
2251         $DB = $this->tdb;
2252         $dbman = $DB->get_manager();
2254         $table = $this->get_test_table();
2255         $tablename = $table->getName();
2257         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2258         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2259         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2260         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2261         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2262         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2263         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2264         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2265         $dbman->create_table($table);
2267         $DB->insert_record($tablename, array('course' => 1));
2268         $record = $DB->get_record($tablename, array('course' => 1));
2269         $record->course = 2;
2271         $this->assertTrue($DB->update_record($tablename, $record));
2272         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
2273         $this->assertTrue($record = $DB->get_record($tablename, array('course' => 2)));
2274         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2275         $this->assertEqual(200, $record->onenum);
2276         $this->assertEqual('onestring', $record->onechar);
2277         $this->assertNull($record->onetext);
2278         $this->assertNull($record->onebinary);
2280         // Check nulls are set properly for all types
2281         $record->oneint = null;
2282         $record->onenum = null;
2283         $record->onechar = null;
2284         $record->onetext = null;
2285         $record->onebinary = null;
2286         $DB->update_record($tablename, $record);
2287         $record = $DB->get_record($tablename, array('course' => 2));
2288         $this->assertNull($record->oneint);
2289         $this->assertNull($record->onenum);
2290         $this->assertNull($record->onechar);
2291         $this->assertNull($record->onetext);
2292         $this->assertNull($record->onebinary);
2294         // Check zeros are set properly for all types
2295         $record->oneint = 0;
2296         $record->onenum = 0;
2297         $DB->update_record($tablename, $record);
2298         $record = $DB->get_record($tablename, array('course' => 2));
2299         $this->assertEqual(0, $record->oneint);
2300         $this->assertEqual(0, $record->onenum);
2302         // Check booleans are set properly for all types
2303         $record->oneint = true; // trues
2304         $record->onenum = true;
2305         $record->onechar = true;
2306         $record->onetext = true;
2307         $DB->update_record($tablename, $record);
2308         $record = $DB->get_record($tablename, array('course' => 2));
2309         $this->assertEqual(1, $record->oneint);
2310         $this->assertEqual(1, $record->onenum);
2311         $this->assertEqual(1, $record->onechar);
2312         $this->assertEqual(1, $record->onetext);
2314         $record->oneint = false; // falses
2315         $record->onenum = false;
2316         $record->onechar = false;
2317         $record->onetext = false;
2318         $DB->update_record($tablename, $record);
2319         $record = $DB->get_record($tablename, array('course' => 2));
2320         $this->assertEqual(0, $record->oneint);
2321         $this->assertEqual(0, $record->onenum);
2322         $this->assertEqual(0, $record->onechar);
2323         $this->assertEqual(0, $record->onetext);
2325         // Check string data causes exception in numeric types
2326         $record->oneint = 'onestring';
2327         $record->onenum = 0;
2328         try {
2329             $DB->update_record($tablename, $record);
2330             $this->fail("Expecting an exception, none occurred");
2331         } catch (exception $e) {
2332             $this->assertTrue($e instanceof dml_exception);
2333         }
2334         $record->oneint = 0;
2335         $record->onenum = 'onestring';
2336         try {
2337             $DB->update_record($tablename, $record);
2338             $this->fail("Expecting an exception, none occurred");
2339         } catch (exception $e) {
2340             $this->assertTrue($e instanceof dml_exception);
2341         }
2343         // Check empty string data is stored as 0 in numeric datatypes
2344         $record->oneint = ''; // empty string
2345         $record->onenum = 0;
2346         $DB->update_record($tablename, $record);
2347         $record = $DB->get_record($tablename, array('course' => 2));
2348         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2350         $record->oneint = 0;
2351         $record->onenum = ''; // empty string
2352         $DB->update_record($tablename, $record);
2353         $record = $DB->get_record($tablename, array('course' => 2));
2354         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2356         // Check empty strings are set properly in string types
2357         $record->oneint = 0;
2358         $record->onenum = 0;
2359         $record->onechar = '';
2360         $record->onetext = '';
2361         $DB->update_record($tablename, $record);
2362         $record = $DB->get_record($tablename, array('course' => 2));
2363         $this->assertTrue($record->onechar === '');
2364         $this->assertTrue($record->onetext === '');
2366         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2367         $record->oneint = ((210.10 + 39.92) - 150.02);
2368         $record->onenum = ((210.10 + 39.92) - 150.02);
2369         $DB->update_record($tablename, $record);
2370         $record = $DB->get_record($tablename, array('course' => 2));
2371         $this->assertEqual(100, $record->oneint);
2372         $this->assertEqual(100, $record->onenum);
2374         // Check various quotes/backslashes combinations in string types
2375         $teststrings = array(
2376             'backslashes and quotes alone (even): "" \'\' \\\\',
2377             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2378             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2379             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2380         foreach ($teststrings as $teststring) {
2381             $record->onechar = $teststring;
2382             $record->onetext = $teststring;
2383             $DB->update_record($tablename, $record);
2384             $record = $DB->get_record($tablename, array('course' => 2));
2385             $this->assertEqual($teststring, $record->onechar);
2386             $this->assertEqual($teststring, $record->onetext);
2387         }
2389         // Check LOBs in text/binary columns
2390         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2391         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2392         $record->onetext = $clob;
2393         $record->onebinary = $blob;
2394         $DB->update_record($tablename, $record);
2395         $record = $DB->get_record($tablename, array('course' => 2));
2396         $this->assertEqual($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2397         $this->assertEqual($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2399         // And "small" LOBs too, just in case
2400         $newclob = substr($clob, 0, 500);
2401         $newblob = substr($blob, 0, 250);
2402         $record->onetext = $newclob;
2403         $record->onebinary = $newblob;
2404         $DB->update_record($tablename, $record);
2405         $record = $DB->get_record($tablename, array('course' => 2));
2406         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2407         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2409         // Test saving a float in a CHAR column, and reading it back.
2410         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2411         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
2412         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2413         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
2414         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2415         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
2416         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2417         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
2418         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2419         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
2420         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2421         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
2422         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2424         // Test saving a float in a TEXT column, and reading it back.
2425         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2426         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
2427         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2428         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
2429         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2430         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
2431         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2432         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
2433         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2434         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
2435         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2436         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
2437         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2438     }
2440     public function test_set_field() {
2441         $DB = $this->tdb;
2442         $dbman = $DB->get_manager();
2444         $table = $this->get_test_table();
2445         $tablename = $table->getName();
2447         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2448         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2449         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2450         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2451         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2452         $dbman->create_table($table);
2454         // simple set_field
2455         $id1 = $DB->insert_record($tablename, array('course' => 1));
2456         $id2 = $DB->insert_record($tablename, array('course' => 1));
2457         $id3 = $DB->insert_record($tablename, array('course' => 3));
2458         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2459         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2460         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2461         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2462         $DB->delete_records($tablename, array());
2464         // multiple fields affected
2465         $id1 = $DB->insert_record($tablename, array('course' => 1));
2466         $id2 = $DB->insert_record($tablename, array('course' => 1));
2467         $id3 = $DB->insert_record($tablename, array('course' => 3));
2468         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2469         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2470         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2471         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2472         $DB->delete_records($tablename, array());
2474         // no field affected
2475         $id1 = $DB->insert_record($tablename, array('course' => 1));
2476         $id2 = $DB->insert_record($tablename, array('course' => 1));
2477         $id3 = $DB->insert_record($tablename, array('course' => 3));
2478         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2479         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2480         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2481         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2482         $DB->delete_records($tablename, array());
2484         // all fields - no condition
2485         $id1 = $DB->insert_record($tablename, array('course' => 1));
2486         $id2 = $DB->insert_record($tablename, array('course' => 1));
2487         $id3 = $DB->insert_record($tablename, array('course' => 3));
2488         $DB->set_field($tablename, 'course', 5, array());
2489         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2490         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2491         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2493         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2494         $conditions = array('onetext' => '1');
2495         try {
2496             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2497             $this->fail('An Exception is missing, expected due to equating of text fields');
2498         } catch (exception $e) {
2499             $this->assertTrue($e instanceof dml_exception);
2500             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2501         }
2503         // Test saving a float in a CHAR column, and reading it back.
2504         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2505         $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
2506         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2507         $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
2508         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2509         $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
2510         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2511         $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
2512         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2513         $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
2514         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2515         $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
2516         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2518         // Test saving a float in a TEXT column, and reading it back.
2519         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2520         $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
2521         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2522         $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
2523         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2524         $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
2525         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2526         $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
2527         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2528         $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
2529         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2530         $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
2531         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2533         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2534         // go to set_field_select() because set_field() is just one wrapper over it
2535     }
2537     public function test_set_field_select() {
2539         // All the information in this test is fetched from DB by get_field() so we
2540         // have such method properly tested against nulls, empties and friends...
2542         $DB = $this->tdb;
2543         $dbman = $DB->get_manager();
2545         $table = $this->get_test_table();
2546         $tablename = $table->getName();
2548         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2549         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2550         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2551         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2552         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2553         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2554         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2555         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2556         $dbman->create_table($table);
2558         $DB->insert_record($tablename, array('course' => 1));
2560         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2561         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2563         // Check nulls are set properly for all types
2564         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2565         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2566         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2567         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2568         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2569         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2570         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2571         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2572         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2573         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2575         // Check zeros are set properly for all types
2576         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2577         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2578         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2579         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2581         // Check booleans are set properly for all types
2582         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2583         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2584         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2585         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2586         $this->assertEqual(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2587         $this->assertEqual(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2588         $this->assertEqual(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2589         $this->assertEqual(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2591         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2592         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2593         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2594         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2595         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2596         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2597         $this->assertEqual(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2598         $this->assertEqual(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2600         // Check string data causes exception in numeric types
2601         try {
2602             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2603             $this->fail("Expecting an exception, none occurred");
2604         } catch (exception $e) {
2605             $this->assertTrue($e instanceof dml_exception);
2606         }
2607         try {
2608             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2609             $this->fail("Expecting an exception, none occurred");
2610         } catch (exception $e) {
2611             $this->assertTrue($e instanceof dml_exception);
2612         }
2614         // Check empty string data is stored as 0 in numeric datatypes
2615         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2616         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2617         $this->assertTrue(is_numeric($field) && $field == 0);
2619         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2620         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2621         $this->assertTrue(is_numeric($field) && $field == 0);
2623         // Check empty strings are set properly in string types
2624         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2625         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2626         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2627         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2629         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2630         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2631         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2632         $this->assertEqual(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2633         $this->assertEqual(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2635         // Check various quotes/backslashes combinations in string types
2636         $teststrings = array(
2637             'backslashes and quotes alone (even): "" \'\' \\\\',
2638             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2639             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2640             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2641         foreach ($teststrings as $teststring) {
2642             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2643             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2644             $this->assertEqual($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2645             $this->assertEqual($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2646         }
2648         // Check LOBs in text/binary columns
2649         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2650         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2651         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2652         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2653         $this->assertEqual($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2654         $this->assertEqual($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2656         // And "small" LOBs too, just in case
2657         $newclob = substr($clob, 0, 500);
2658         $newblob = substr($blob, 0, 250);
2659         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2660         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2661         $this->assertEqual($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2662         $this->assertEqual($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2664         // This is the failure from MDL-24863. This was giving an error on MSSQL,
2665         // which converts the '1' to an integer, which cannot then be compared with
2666         // onetext cast to a varchar. This should be fixed and working now.
2667         $newchar = 'frog';
2668         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2669         $params = array('onetext' => '1');
2670         try {
2671             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
2672             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
2673         } catch (dml_exception $e) {
2674             $this->assertFalse(true, 'We have an unexpected exception.');
2675             throw $e;
2676         }
2679     }
2681     public function test_count_records() {
2682         $DB = $this->tdb;
2684         $dbman = $DB->get_manager();
2686         $table = $this->get_test_table();
2687         $tablename = $table->getName();
2689         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2690         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2691         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2692         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2693         $dbman->create_table($table);
2695         $this->assertEqual(0, $DB->count_records($tablename));
2697         $DB->insert_record($tablename, array('course' => 3));
2698         $DB->insert_record($tablename, array('course' => 4));
2699         $DB->insert_record($tablename, array('course' => 5));
2701         $this->assertEqual(3, $DB->count_records($tablename));
2703         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2704         $conditions = array('onetext' => '1');
2705         try {
2706             $DB->count_records($tablename, $conditions);
2707             $this->fail('An Exception is missing, expected due to equating of text fields');
2708         } catch (exception $e) {
2709             $this->assertTrue($e instanceof dml_exception);
2710             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2711         }
2712     }
2714     public function test_count_records_select() {
2715         $DB = $this->tdb;
2717         $dbman = $DB->get_manager();
2719         $table = $this->get_test_table();
2720         $tablename = $table->getName();
2722         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2723         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2724         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2725         $dbman->create_table($table);
2727         $this->assertEqual(0, $DB->count_records($tablename));
2729         $DB->insert_record($tablename, array('course' => 3));
2730         $DB->insert_record($tablename, array('course' => 4));
2731         $DB->insert_record($tablename, array('course' => 5));
2733         $this->assertEqual(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2734     }
2736     public function test_count_records_sql() {
2737         $DB = $this->tdb;
2738         $dbman = $DB->get_manager();
2740         $table = $this->get_test_table();
2741         $tablename = $table->getName();
2743         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2744         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2745         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2746         $dbman->create_table($table);
2748         $this->assertEqual(0, $DB->count_records($tablename));
2750         $DB->insert_record($tablename, array('course' => 3));
2751         $DB->insert_record($tablename, array('course' => 4));
2752         $DB->insert_record($tablename, array('course' => 5));
2754         $this->assertEqual(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2755     }
2757     public function test_record_exists() {
2758         $DB = $this->tdb;
2759         $dbman = $DB->get_manager();
2761         $table = $this->get_test_table();
2762         $tablename = $table->getName();
2764         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2765         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2766         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2767         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2768         $dbman->create_table($table);
2770         $this->assertEqual(0, $DB->count_records($tablename));
2772         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
2773         $DB->insert_record($tablename, array('course' => 3));
2775         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
2778         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2779         $conditions = array('onetext' => '1');
2780         try {
2781             $DB->record_exists($tablename, $conditions);
2782             $this->fail('An Exception is missing, expected due to equating of text fields');
2783         } catch (exception $e) {
2784             $this->assertTrue($e instanceof dml_exception);
2785             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2786         }
2787     }
2789     public function test_record_exists_select() {
2790         $DB = $this->tdb;
2791         $dbman = $DB->get_manager();
2793         $table = $this->get_test_table();
2794         $tablename = $table->getName();
2796         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2797         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2798         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2799         $dbman->create_table($table);
2801         $this->assertEqual(0, $DB->count_records($tablename));
2803         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
2804         $DB->insert_record($tablename, array('course' => 3));
2806         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
2807     }
2809     public function test_record_exists_sql() {
2810         $DB = $this->tdb;
2811         $dbman = $DB->get_manager();
2813         $table = $this->get_test_table();
2814         $tablename = $table->getName();
2816         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2817         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2818         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2819         $dbman->create_table($table);
2821         $this->assertEqual(0, $DB->count_records($tablename));
2823         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2824         $DB->insert_record($tablename, array('course' => 3));
2826         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2827     }
2829     public function test_recordset_locks_delete() {
2830         $DB = $this->tdb;
2831         $dbman = $DB->get_manager();
2833         //Setup
2834         $table = $this->get_test_table();
2835         $tablename = $table->getName();
2837         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2838         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2839         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2840         $dbman->create_table($table);
2842         $DB->insert_record($tablename, array('course' => 1));
2843         $DB->insert_record($tablename, array('course' => 2));
2844         $DB->insert_record($tablename, array('course' => 3));
2845         $DB->insert_record($tablename, array('course' => 4));
2846         $DB->insert_record($tablename, array('course' => 5));
2847         $DB->insert_record($tablename, array('course' => 6));
2849         // Test against db write locking while on an open recordset
2850         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2851         foreach ($rs as $record) {
2852             $cid = $record->course;
2853             $DB->delete_records($tablename, array('course' => $cid));
2854             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2855         }
2856         $rs->close();
2858         $this->assertEqual(4, $DB->count_records($tablename, array()));
2859     }
2861     public function test_recordset_locks_update() {
2862         $DB = $this->tdb;
2863         $dbman = $DB->get_manager();
2865         //Setup
2866         $table = $this->get_test_table();
2867         $tablename = $table->getName();
2869         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2870         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2871         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2872         $dbman->create_table($table);
2874         $DB->insert_record($tablename, array('course' => 1));
2875         $DB->insert_record($tablename, array('course' => 2));
2876         $DB->insert_record($tablename, array('course' => 3));
2877         $DB->insert_record($tablename, array('course' => 4));
2878         $DB->insert_record($tablename, array('course' => 5));
2879         $DB->insert_record($tablename, array('course' => 6));
2881         // Test against db write locking while on an open recordset
2882         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2883         foreach ($rs as $record) {
2884             $cid = $record->course;
2885             $DB->set_field($tablename, 'course', 10, array('course' => $cid));
2886             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2887         }
2888         $rs->close();
2890         $this->assertEqual(2, $DB->count_records($tablename, array('course' => 10)));
2891     }
2893     public function test_delete_records() {
2894         $DB = $this->tdb;
2895         $dbman = $DB->get_manager();
2897         $table = $this->get_test_table();
2898         $tablename = $table->getName();
2900         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2901         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2902         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2903         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2904         $dbman->create_table($table);
2906         $DB->insert_record($tablename, array('course' => 3));
2907         $DB->insert_record($tablename, array('course' => 2));
2908         $DB->insert_record($tablename, array('course' => 2));
2910         // Delete all records
2911         $this->assertTrue($DB->delete_records($tablename));
2912         $this->assertEqual(0, $DB->count_records($tablename));
2914         // Delete subset of records
2915         $DB->insert_record($tablename, array('course' => 3));
2916         $DB->insert_record($tablename, array('course' => 2));
2917         $DB->insert_record($tablename, array('course' => 2));
2919         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
2920         $this->assertEqual(1, $DB->count_records($tablename));
2922         // delete all
2923         $this->assertTrue($DB->delete_records($tablename, array()));
2924         $this->assertEqual(0, $DB->count_records($tablename));
2926         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2927         $conditions = array('onetext'=>'1');
2928         try {
2929             $DB->delete_records($tablename, $conditions);
2930             $this->fail('An Exception is missing, expected due to equating of text fields');
2931         } catch (exception $e) {
2932             $this->assertTrue($e instanceof dml_exception);
2933             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2934         }
2936         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2937         $conditions = array('onetext' => 1);
2938         try {
2939             $DB->delete_records($tablename, $conditions);
2940             $this->fail('An Exception is missing, expected due to equating of text fields');
2941         } catch (exception $e) {
2942             $this->assertTrue($e instanceof dml_exception);
2943             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2944         }
2945     }
2947     public function test_delete_records_select() {
2948         $DB = $this->tdb;
2949         $dbman = $DB->get_manager();
2951         $table = $this->get_test_table();
2952         $tablename = $table->getName();
2954         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2955         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2956         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2957         $dbman->create_table($table);
2959         $DB->insert_record($tablename, array('course' => 3));
2960         $DB->insert_record($tablename, array('course' => 2));
2961         $DB->insert_record($tablename, array('course' => 2));
2963         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
2964         $this->assertEqual(1, $DB->count_records($tablename));
2965     }
2967     public function test_delete_records_list() {
2968         $DB = $this->tdb;
2969         $dbman = $DB->get_manager();
2971         $table = $this->get_test_table();
2972         $tablename = $table->getName();
2974         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2975         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2976         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2977         $dbman->create_table($table);
2979         $DB->insert_record($tablename, array('course' => 1));
2980         $DB->insert_record($tablename, array('course' => 2));
2981         $DB->insert_record($tablename, array('course' => 3));
2983         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
2984         $this->assertEqual(1, $DB->count_records($tablename));
2986         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); /// Must delete 0 rows without conditions. MDL-17645
2987         $this->assertEqual(1, $DB->count_records($tablename));
2988     }
2990     function test_sql_null_from_clause() {
2991         $DB = $this->tdb;
2992         $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
2993         $this->assertEqual($DB->get_field_sql($sql), 1);
2994     }
2996     function test_sql_bitand() {
2997         $DB = $this->tdb;
2998         $dbman = $DB->get_manager();
3000         $table = $this->get_test_table();
3001         $tablename = $table->getName();
3003         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3004         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3005         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3006         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3007         $dbman->create_table($table);
3009         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3011         $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
3012         $this->assertEqual($DB->get_field_sql($sql), 2);
3014         $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
3015         $result = $DB->get_records_sql($sql);
3016         $this->assertEqual(count($result), 1);
3017         $this->assertEqual(reset($result)->res, 2);
3019         $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
3020         $result = $DB->get_records_sql($sql, array(10));
3021         $this->assertEqual(count($result), 1);
3022         $this->assertEqual(reset($result)->res, 2);
3023     }
3025     function test_sql_bitnot() {
3026         $DB = $this->tdb;
3028         $not = $DB->sql_bitnot(2);
3029         $notlimited = $DB->sql_bitand($not, 7); // might be positive or negative number which can not fit into PHP INT!
3031         $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
3032         $this->assertEqual($DB->get_field_sql($sql), 5);
3033     }
3035     function test_sql_bitor() {
3036         $DB = $this->tdb;
3037         $dbman = $DB->get_manager();
3039         $table = $this->get_test_table();
3040         $tablename = $table->getName();
3042         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3043         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3044         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3045         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3046         $dbman->create_table($table);
3048         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3050         $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
3051         $this->assertEqual($DB->get_field_sql($sql), 11);
3053         $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
3054         $result = $DB->get_records_sql($sql);
3055         $this->assertEqual(count($result), 1);
3056         $this->assertEqual(reset($result)->res, 11);
3058         $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
3059         $result = $DB->get_records_sql($sql, array(10));
3060         $this->assertEqual(count($result), 1);
3061         $this->assertEqual(reset($result)->res, 11);
3062     }
3064     function test_sql_bitxor() {
3065         $DB = $this->tdb;
3066         $dbman = $DB->get_manager();
3068         $table = $this->get_test_table();
3069         $tablename = $table->getName();
3071         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3072         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3073         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3074         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3075         $dbman->create_table($table);
3077         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3079         $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
3080         $this->assertEqual($DB->get_field_sql($sql), 9);
3082         $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}";
3083         $result = $DB->get_records_sql($sql);
3084         $this->assertEqual(count($result), 1);
3085         $this->assertEqual(reset($result)->res, 9);
3087         $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}";
3088         $result = $DB->get_records_sql($sql, array(10));
3089         $this->assertEqual(count($result), 1);
3090         $this->assertEqual(reset($result)->res, 9);
3091     }
3093     function test_sql_modulo() {
3094         $DB = $this->tdb;
3095         $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause();
3096         $this->assertEqual($DB->get_field_sql($sql), 3);
3097     }
3099     function test_sql_ceil() {
3100         $DB = $this->tdb;
3101         $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause();
3102         $this->assertEqual($DB->get_field_sql($sql), 666);
3103     }
3105     function test_cast_char2int() {
3106         $DB = $this->tdb;
3107         $dbman = $DB->get_manager();
3109         $table1 = $this->get_test_table("1");
3110         $tablename1 = $table1->getName();
3112         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3113         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3114         $table1->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
3115         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3116         $dbman->create_table($table1);
3118         $DB->insert_record($tablename1, array('name'=>'0100', 'nametext'=>'0200'));
3119         $DB->insert_record($tablename1, array('name'=>'10',   'nametext'=>'20'));
3121         $table2 = $this->get_test_table("2");
3122         $tablename2 = $table2->getName();
3123         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3124         $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3125         $table2->add_field('restext', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3126         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3127         $dbman->create_table($table2);
3129         $DB->insert_record($tablename2, array('res'=>100, 'restext'=>200));
3131         // casting varchar field
3132         $sql = "SELECT *
3133                   FROM {".$tablename1."} t1
3134                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.name")." = t2.res ";
3135         $records = $DB->get_records_sql($sql);
3136         $this->assertEqual(count($records), 1);
3137         // also test them in order clauses
3138         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('name');
3139         $records = $DB->get_records_sql($sql);
3140         $this->assertEqual(count($records), 2);
3141         $this->assertEqual(reset($records)->name, '10');
3142         $this->assertEqual(next($records)->name, '0100');
3144         // casting text field
3145         $sql = "SELECT *
3146                   FROM {".$tablename1."} t1
3147                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.nametext", true)." = t2.restext ";
3148         $records = $DB->get_records_sql($sql);
3149         $this->assertEqual(count($records), 1);
3150         // also test them in order clauses
3151         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('nametext', true);
3152         $records = $DB->get_records_sql($sql);
3153         $this->assertEqual(count($records), 2);
3154         $this->assertEqual(reset($records)->nametext, '20');
3155         $this->assertEqual(next($records)->nametext, '0200');
3156     }
3158     function test_cast_char2real() {
3159         $DB = $this->tdb;
3160         $dbman = $DB->get_manager();
3162         $table = $this->get_test_table();
3163         $tablename = $table->getName();
3165         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3166         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3167         $table->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
3168         $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
3169         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3170         $dbman->create_table($table);
3172         $DB->insert_record($tablename, array('name'=>'10.10', 'nametext'=>'10.10', 'res'=>5.1));
3173         $DB->insert_record($tablename, array('name'=>'91.10', 'nametext'=>'91.10', 'res'=>666));
3174         $DB->insert_record($tablename, array('name'=>'011.10','nametext'=>'011.10','res'=>10.1));
3176         // casting varchar field
3177         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res";
3178         $records = $DB->get_records_sql($sql);
3179         $this->assertEqual(count($records), 2);
3180         // also test them in order clauses
3181         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('name');
3182         $records = $DB->get_records_sql($sql);
3183         $this->assertEqual(count($records), 3);
3184         $this->assertEqual(reset($records)->name, '10.10');
3185         $this->assertEqual(next($records)->name, '011.10');
3186         $this->assertEqual(next($records)->name, '91.10');
3188         // casting text field
3189         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('nametext', true)." > res";
3190         $records = $DB->get_records_sql($sql);
3191         $this->assertEqual(count($records), 2);
3192         // also test them in order clauses
3193         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('nametext', true);
3194         $records = $DB->get_records_sql($sql);
3195         $this->assertEqual(count($records), 3);
3196         $this->assertEqual(reset($records)->nametext, '10.10');
3197         $this->assertEqual(next($records)->nametext, '011.10');
3198         $this->assertEqual(next($records)->nametext, '91.10');
3199     }
3201     function sql_compare_text() {
3202         $DB = $this->tdb;
3203         $dbman = $DB->get_manager();
3205         $table = $this->get_test_table();
3206         $tablename = $table->getName();
3208         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3209         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3210         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3211         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3212         $dbman->create_table($table);
3214         $DB->insert_record($tablename, array('name'=>'abcd',   'description'=>'abcd'));
3215         $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef'));
3216         $DB->insert_record($tablename, array('name'=>'aaaabb', 'description'=>'aaaacccccccccccccccccc'));
3218         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description');
3219         $records = $DB->get_records_sql($sql);
3220         $this->assertEqual(count($records), 1);
3222         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4);
3223         $records = $DB->get_records_sql($sql);
3224         $this->assertEqual(count($records), 2);
3225     }
3227     function test_unique_index_collation_trouble() {
3228         // note: this is a work in progress, we should probably move this to ddl test
3230         $DB = $this->tdb;
3231         $dbman = $DB->get_manager();
3233         $table = $this->get_test_table();
3234         $tablename = $table->getName();
3236         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3237         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3238         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3239         $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name'));
3240         $dbman->create_table($table);
3242         $DB->insert_record($tablename, array('name'=>'aaa'));
3244         try {
3245             $DB->insert_record($tablename, array('name'=>'AAA'));
3246         } catch (Exception $e) {
3247             //TODO: ignore case insensitive uniqueness problems for now
3248             //$this->fail("Unique index is case sensitive - this may cause problems in some tables");
3249         }
3251         try {
3252             $DB->insert_record($tablename, array('name'=>'aäa'));
3253             $DB->insert_record($tablename, array('name'=>'aáa'));
3254             $this->assertTrue(true);
3255         } catch (Exception $e) {
3256             $family = $DB->get_dbfamily();
3257             if ($family === 'mysql' or $family === 'mssql') {
3258                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
3259             } else {
3260                 // this should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness.
3261                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages.");
3262             }
3263             throw($e);
3264         }
3265     }
3267     function test_sql_binary_equal() {
3268         $DB = $this->tdb;
3269         $dbman = $DB->get_manager();
3271         $table = $this->get_test_table();
3272         $tablename = $table->getName();
3274         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3275         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3276         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3277         $dbman->create_table($table);
3279         $DB->insert_record($tablename, array('name'=>'aaa'));
3280         $DB->insert_record($tablename, array('name'=>'aáa'));
3281         $DB->insert_record($tablename, array('name'=>'aäa'));
3282         $DB->insert_record($tablename, array('name'=>'bbb'));
3283         $DB->insert_record($tablename, array('name'=>'BBB'));
3285         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('aaa'));
3286         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be accent sensitive');
3288         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('bbb'));
3289         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be case sensitive');
3290     }
3292     function test_sql_like() {
3293         $DB = $this->tdb;
3294         $dbman = $DB->get_manager();
3296         $table = $this->get_test_table();
3297         $tablename = $table->getName();
3299         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3300         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3301         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3302         $dbman->create_table($table);
3304         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
3305         $DB->insert_record($tablename, array('name'=>'Nodupor'));
3306         $DB->insert_record($tablename, array('name'=>'ouch'));
3307         $DB->insert_record($tablename, array('name'=>'ouc_'));
3308         $DB->insert_record($tablename, array('name'=>'ouc%'));
3309         $DB->insert_record($tablename, array('name'=>'aui'));
3310         $DB->insert_record($tablename, array('name'=>'aüi'));
3311         $DB->insert_record($tablename, array('name'=>'aÜi'));
3313         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false);
3314         $records = $DB->get_records_sql($sql, array("%dup_r%"));
3315         $this->assertEqual(count($records), 2);
3317         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
3318         $records = $DB->get_records_sql($sql, array("%dup%"));
3319         $this->assertEqual(count($records), 1);
3321         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?'); // defaults
3322         $records = $DB->get_records_sql($sql, array("%dup%"));
3323         $this->assertEqual(count($records), 1);
3325         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
3326         $records = $DB->get_records_sql($sql, array("ouc\\_"));
3327         $this->assertEqual(count($records), 1);
3329         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|');
3330         $records = $DB->get_records_sql($sql, array($DB->sql_like_escape("ouc%", '|')));
3331         $this->assertEqual(count($records), 1);
3333         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true);
3334         $records = $DB->get_records_sql($sql, array('aui'));
3335         $this->assertEqual(count($records), 1);
3337         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, true); // NOT LIKE
3338         $records = $DB->get_records_sql($sql, array("%o%"));
3339         $this->assertEqual(count($records), 3);
3341         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, true, true); // NOT ILIKE
3342         $records = $DB->get_records_sql($sql, array("%D%"));
3343         $this->assertEqual(count($records), 6);
3345         // TODO: we do not require accent insensitivness yet, just make sure it does not throw errors
3346         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, false);
3347         $records = $DB->get_records_sql($sql, array('aui'));
3348         //$this->assertEqual(count($records), 2, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
3349         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false);
3350         $records = $DB->get_records_sql($sql, array('aui'));
3351         //$this->assertEqual(count($records), 3, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
3352     }
3354     function test_sql_ilike() {
3355         // note: this is deprecated, just make sure it does not throw error
3356         $DB = $this->tdb;
3357         $dbman = $DB->get_manager();
3359         $table = $this->get_test_table();
3360         $tablename = $table->getName();
3362         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3363         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3364         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3365         $dbman->create_table($table);
3367         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
3368         $DB->insert_record($tablename, array('name'=>'NoDupor'));
3369         $DB->insert_record($tablename, array('name'=>'ouch'));
3371         // make sure it prints debug message
3372         $this->enable_debugging();
3373         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_ilike()." ?";
3374         $params = array("%dup_r%");
3375         $this->assertFalse($this->get_debugging() === '');
3377         // following must not throw exception, we ignore result
3378         $DB->get_records_sql($sql, $params);
3379     }
3381     function test_coalesce() {
3382         $DB = $this->tdb;
3384         // Testing not-null ocurrences, return 1st
3385         $sql = "SELECT COALESCE('returnthis', 'orthis', 'orwhynotthis') AS test" . $DB->sql_null_from_clause();
3386         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array()));
3387         $sql = "SELECT COALESCE(:paramvalue, 'orthis', 'orwhynotthis') AS test" . $DB->sql_null_from_clause();
3388         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis')));
3390         // Testing null ocurrences, return 2nd
3391         $sql = "SELECT COALESCE(null, 'returnthis', 'orthis') AS test" . $DB->sql_null_from_clause();
3392         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array()));
3393         $sql = "SELECT COALESCE(:paramvalue, 'returnthis', 'orthis') AS test" . $DB->sql_null_from_clause();
3394         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array('paramvalue' => null)));
3395         $sql = "SELECT COALESCE(null, :paramvalue, 'orthis') AS test" . $DB->sql_null_from_clause();
3396         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis')));
3398         // Testing null ocurrences, return 3rd
3399         $sql = "SELECT COALESCE(null, null, 'returnthis') AS test" . $DB->sql_null_from_clause();
3400         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array()));
3401         $sql = "SELECT COALESCE(null, :paramvalue, 'returnthis') AS test" . $DB->sql_null_from_clause();
3402         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array('paramvalue' => null)));
3403         $sql = "SELECT COALESCE(null, null, :paramvalue) AS test" . $DB->sql_null_from_clause();
3404         $this->assertEqual('returnthis', $DB->get_field_sql($sql, array('paramvalue' => 'returnthis')));
3406         // Testing all null ocurrences, return null
3407         // Note: under mssql, if all elements are nulls, at least one must be a "typed" null, hence
3408         // we cannot test this in a cross-db way easily, so next 2 tests are using
3409         // different queries depending of the DB family
3410         $customnull = $DB->get_dbfamily() == 'mssql' ? 'CAST(null AS varchar)' : 'null';
3411         $sql = "SELECT COALESCE(null, null, " . $customnull . ") AS test" . $DB->sql_null_from_clause();
3412         $this->assertNull($DB->get_field_sql($sql, array()));
3413         $sql = "SELECT COALESCE(null, :paramvalue, " . $customnull . ") AS test" . $DB->sql_null_from_clause();
3414         $this->assertNull($DB->get_field_sql($sql, array('paramvalue' => null)));
3416         // Check there are not problems with whitespace strings
3417         $sql = "SELECT COALESCE(null, '', null) AS test" . $DB->sql_null_from_clause();
3418         $this->assertEqual('', $DB->get_field_sql($sql, array()));
3419         $sql = "SELECT COALESCE(null, :paramvalue, null) AS test" . $DB->sql_null_from_clause();
3420         $this->assertEqual('', $DB->get_field_sql($sql, array('paramvalue' => '')));
3421     }
3423     function test_sql_concat() {
3424         $DB = $this->tdb;
3425         $dbman = $DB->get_manager();
3427         /// Testing all sort of values
3428         $sql = "SELECT ".$DB->sql_concat("?", "?", "?")." AS fullname ". $DB->sql_null_from_clause();
3429         // string, some unicode chars
3430         $params = array('name', 'áéíóú', 'name3');
3431         $this->assertEqual('nameáéíóúname3', $DB->get_field_sql($sql, $params));
3432         // string, spaces and numbers
3433         $params = array('name', '  ', 12345);
3434         $this->assertEqual('name  12345', $DB->get_field_sql($sql, $params));
3435         // float, empty and strings
3436         $params = array(123.45, '', 'test');
3437         $this->assertEqual('123.45test', $DB->get_field_sql($sql, $params));
3438         // only integers
3439         $params = array(12, 34, 56);
3440         $this->assertEqual('123456', $DB->get_field_sql($sql, $params));
3441         // float, null and strings
3442         $params = array(123.45, null, 'test');
3443         $this->assertNull($DB->get_field_sql($sql, $params), 'ANSI behaviour: Concatenating NULL must return NULL - But in Oracle :-(. [%s]'); // Concatenate NULL with anything result = NULL
3445         /// Testing fieldnames + values and also integer fieldnames
3446         $table = $this->get_test_table();
3447         $tablename = $table->getName();
3449         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3450         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3451         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3452         $dbman->create_table($table);
3454         $DB->insert_record($tablename, array('description'=>'áéíóú'));
3455         $DB->insert_record($tablename, array('description'=>'dxxx'));
3456         $DB->insert_record($tablename, array('description'=>'bcde'));
3458         // fieldnames and values mixed
3459         $sql = 'SELECT id, ' . $DB->sql_concat('description', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
3460         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
3461         $this->assertEqual(count($records), 3);
3462         $this->assertEqual($records[1]->result, 'áéíóúharcoded123.45test');
3463         // integer fieldnames and values
3464         $sql = 'SELECT id, ' . $DB->sql_concat('id', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
3465         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
3466         $this->assertEqual(count($records), 3);
3467         $this->assertEqual($records[1]->result, '1harcoded123.45test');
3468         // all integer fieldnames
3469         $sql = 'SELECT id, ' . $DB->sql_concat('id', 'id', 'id') . ' AS result FROM {' . $tablename . '}';
3470         $records = $DB->get_records_sql($sql, array());
3471         $this->assertEqual(count($records), 3);
3472         $this->assertEqual($records[1]->result, '111');
3474     }
3476     function test_concat_join() {
3477         $DB = $this->tdb;
3478         $sql = "SELECT ".$DB->sql_concat_join("' '", array("?", "?", "?"))." AS fullname ".$DB->sql_null_from_clause();
3479         $params = array("name", "name2", "name3");
3480         $result = $DB->get_field_sql($sql, $params);
3481         $this->assertEqual("name name2 name3", $result);
3482     }
3484     function test_sql_fullname() {
3485         $DB = $this->tdb;
3486         $sql = "SELECT ".$DB->sql_fullname(':first', ':last')." AS fullname ".$DB->sql_null_from_clause();
3487         $params = array('first'=>'Firstname', 'last'=>'Surname');
3488         $this->assertEqual("Firstname Surname", $DB->get_field_sql($sql, $params));
3489     }
3491     function sql_sql_order_by_text() {
3492         $DB = $this->tdb;
3493         $dbman = $DB->get_manager();
3495         $table = $this->get_test_table();
3496         $tablename = $table->getName();
3498         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3499         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3500         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3501         $dbman->create_table($table);
3503         $DB->insert_record($tablename, array('description'=>'abcd'));
3504         $DB->insert_record($tablename, array('description'=>'dxxx'));
3505         $DB->insert_record($tablename, array('description'=>'bcde'));
3507         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_order_by_text('description');
3508         $records = $DB->get_records_sql($sql);
3509         $first = array_shift($records);
3510         $this->assertEqual(1, $first->id);
3511         $second = array_shift($records);
3512         $this->assertEqual(3, $second->id);
3513         $last = array_shift($records);
3514         $this->assertEqual(2, $last->id);
3515     }
3517     function test_sql_substring() {
3518         $DB = $this->tdb;
3519         $dbman = $DB->get_manager();
3521         $table = $this->get_test_table();
3522         $tablename = $table->getName();
3524         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3525         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3526         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3527         $dbman->create_table($table);
3529         $string = 'abcdefghij';
3531         $DB->insert_record($tablename, array('name'=>$string));
3533         $sql = "SELECT id, ".$DB->sql_substr("name", 5)." AS name FROM {{$tablename}}";
3534         $record = $DB->get_record_sql($sql);
3535         $this->assertEqual(substr($string, 5-1), $record->name);
3537         $sql = "SELECT id, ".$DB->sql_substr("name", 5, 2)." AS name FROM {{$tablename}}";
3538         $record = $DB->get_record_sql($sql);
3539         $this->assertEqual(substr($string, 5-1, 2), $record->name);
3541         try {
3542             // silence php warning ;-)
3543             @$DB->sql_substr("name");
3544             $this->fail("Expecting an exception, none occurred");
3545         } catch (Exception $e) {
3546             $this->assertTrue($e instanceof coding_exception);
3547         }
3548     }
3550     function test_sql_length() {
3551         $DB = $this->tdb;
3552         $this->assertEqual($DB->get_field_sql(
3553                 "SELECT ".$DB->sql_length("'aeiou'").$DB->sql_null_from_clause()), 5);
3554         $this->assertEqual($DB->get_field_sql(
3555                 "SELECT ".$DB->sql_length("'áéíóú'").$DB->sql_null_from_clause()), 5);
3556     }
3558     function test_sql_position() {
3559         $DB = $this->tdb;
3560         $this->assertEqual($DB->get_field_sql(
3561                 "SELECT ".$DB->sql_position("'ood'", "'Moodle'").$DB->sql_null_from_clause()), 2);
3562         $this->assertEqual($DB->get_field_sql(
3563                 "SELECT ".$DB->sql_position("'Oracle'", "'Moodle'").$DB->sql_null_from_clause()), 0);
3564     }
3566     function test_sql_empty() {
3567         $DB = $this->tdb;
3568         $dbman = $DB->get_manager();
3570         $table = $this->get_test_table();
3571         $tablename = $table->getName();
3573         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3574         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3575         $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value');
3576         $table->add_field('namenotnullnodeflt', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3577         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3578         $dbman->create_table($table);
3580         $DB->insert_record($tablename, array('name'=>'', 'namenotnull'=>''));
3581         $DB->insert_record($tablename, array('name'=>null));
3582         $DB->insert_record($tablename, array('name'=>'lalala'));
3583         $DB->insert_record($tablename, array('name'=>0));
3585         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = '".$DB->sql_empty()."'");
3586         $this->assertEqual(count($records), 1);
3587         $record = reset($records);
3588         $this->assertEqual($record->name, '');
3590         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = '".$DB->sql_empty()."'");
3591         $this->assertEqual(count($records), 1);
3592         $record = reset($records);
3593         $this->assertEqual($record->namenotnull, '');
3595         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = '".$DB->sql_empty()."'");
3596         $this->assertEqual(count($records), 4);
3597         $record = reset($records);
3598         $this->assertEqual($record->namenotnullnodeflt, '');
3599     }
3601     function test_sql_isempty() {
3602         $DB = $this->tdb;
3603         $dbman = $DB->get_manager();
3605         $table = $this->get_test_table();
3606         $tablename = $table->getName();
3608         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3609         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3610         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3611         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
3612         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3613         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3614         $dbman->create_table($table);
3616         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
3617         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
3618         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
3619         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
3621         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'name', false, false));
3622         $this->assertEqual(count($records), 1);
3623         $record = reset($records);
3624         $this->assertEqual($record->name, '');
3626         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'namenull', true, false));
3627         $this->assertEqual(count($records), 1);
3628         $record = reset($records);
3629         $this->assertEqual($record->namenull, '');
3631         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'description', false, true));
3632         $this->assertEqual(count($records), 1);
3633         $record = reset($records);
3634         $this->assertEqual($record->description, '');
3636         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'descriptionnull', true, true));
3637         $this->assertEqual(count($records), 1);
3638         $record = reset($records);
3639         $this->assertEqual($record->descriptionnull, '');
3640     }
3642     function test_sql_isnotempty() {
3643         $DB = $this->tdb;
3644         $dbman = $DB->get_manager();
3646         $table = $this->get_test_table();
3647         $tablename = $table->getName();
3649         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3650         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3651         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3652         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
3653         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3654         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3655         $dbman->create_table($table);
3657         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
3658         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
3659         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
3660         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
3662         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'name', false, false));
3663         $this->assertEqual(count($records), 3);
3664         $record = reset($records);
3665         $this->assertEqual($record->name, '??');
3667         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'namenull', true, false));
3668         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
3669         $record = reset($records);
3670         $this->assertEqual($record->namenull, 'la'); // so 'la' is the first non-empty 'namenull' record
3672         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'description', false, true));
3673         $this->assertEqual(count($records), 3);
3674         $record = reset($records);
3675         $this->assertEqual($record->description, '??');
3677         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'descriptionnull', true, true));
3678         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
3679         $record = reset($records);
3680         $this->assertEqual($record->descriptionnull, 'lalala'); // so 'lalala' is the first non-empty 'descriptionnull' record
3681     }
3683     function test_sql_regex() {
3684         $DB = $this->tdb;
3685         $dbman = $DB->get_manager();
3687         $table = $this->get_test_table();
3688         $tablename = $table->getName();
3690         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3691         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3692         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3693         $dbman->create_table($table);
3695         $DB->insert_record($tablename, array('name'=>'lalala'));
3696         $DB->insert_record($tablename, array('name'=>'holaaa'));
3697         $DB->insert_record($tablename, array('name'=>'aouch'));
3699         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex()." ?";
3700         $params = array('a$');
3701         if ($DB->sql_regex_supported()) {
3702             $records = $DB->get_records_sql($sql, $params);
3703             $this->assertEqual(count($records), 2);
3704         } else {
3705             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
3706         }
3708         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false)." ?";
3709         $params = array('.a');
3710         if ($DB->sql_regex_supported()) {
3711             $records = $DB->get_records_sql($sql, $params);
3712             $this->assertEqual(count($records), 1);
3713         } else {
3714             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
3715         }
3717     }
3719     /**
3720      * Test some more complex SQL syntax which moodle uses and depends on to work
3721      * useful to determine if new database libraries can be supported.
3722      */
3723     public function test_get_records_sql_complicated() {
3724         $DB = $this->tdb;
3725         $dbman = $DB->get_manager();
3727         $table = $this->get_test_table();
3728         $tablename = $table->getName();
3730         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3731         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3732         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3733         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3734         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3735         $dbman->create_table($table);
3737         $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello', 'name'=>'xyz'));
3738         $DB->insert_record($tablename, array('course' => 3, 'content' => 'world', 'name'=>'abc'));
3739         $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello', 'name'=>'def'));
3740         $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe', 'name'=>'abc'));
3742         // test grouping by expressions in the query. MDL-26819. Note that there are 4 ways:
3743         // - By column position (GROUP by 1) - Not supported by mssql & oracle
3744         // - By column name (GROUP by course) - Supported by all, but leading to wrong results
3745         // - By column alias (GROUP by casecol) - Not supported by mssql & oracle
3746         // - By complete expression (GROUP BY CASE ...) - 100% cross-db, this test checks it
3747         $sql = "SELECT (CASE WHEN course = 3 THEN 1 ELSE 0 END) AS casecol,
3748                        COUNT(1) AS countrecs,
3749                        MAX(name) AS maxname
3750                   FROM {{$tablename}}
3751               GROUP BY CASE WHEN course = 3 THEN 1 ELSE 0 END
3752               ORDER BY casecol DESC";
3753         $result = array(
3754                 1 => (object)array('casecol' => 1, 'countrecs' => 2, 'maxname' => 'xyz'),
3755                 0 => (object)array('casecol' => 0, 'countrecs' => 2, 'maxname' => 'def'));
3756         $records = $DB->get_records_sql($sql, null);
3757         $this->assertEqual($result, $records);
3759         // another grouping by CASE expression just to ensure it works ok for multiple WHEN
3760         $sql = "SELECT CASE name
3761                             WHEN 'xyz' THEN 'last'
3762                             WHEN 'def' THEN 'mid'
3763                             WHEN 'abc' THEN 'first'
3764                        END AS casecol,
3765                        COUNT(1) AS countrecs,
3766                        MAX(name) AS maxname
3767                   FROM {{$tablename}}
3768               GROUP BY CASE name
3769                            WHEN 'xyz' THEN 'last'
3770                            WHEN 'def' THEN 'mid'
3771                           &nb