c9114c93ac6214cab4c28b4980cbf88a6d3b14b1
[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_tweak_param_names() {
507         // Note the tweak_param_names() method is only available in the oracle driver,
508         // hence we look for expected results indirectly, by testing various DML methods
509         // with some "extreme" conditions causing the tweak to happen.
510         $DB = $this->tdb;
511         $dbman = $this->tdb->get_manager();
513         $table = $this->get_test_table();
514         $tablename = $table->getName();
516         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
517         // Add some columns with 28 chars in the name
518         $table->add_field('long_int_columnname_with_28c', XMLDB_TYPE_INTEGER, '10');
519         $table->add_field('long_dec_columnname_with_28c', XMLDB_TYPE_NUMBER, '10,2');
520         $table->add_field('long_str_columnname_with_28c', XMLDB_TYPE_CHAR, '100');
521         // Add some columns with 30 chars in the name
522         $table->add_field('long_int_columnname_with_30cxx', XMLDB_TYPE_INTEGER, '10');
523         $table->add_field('long_dec_columnname_with_30cxx', XMLDB_TYPE_NUMBER, '10,2');
524         $table->add_field('long_str_columnname_with_30cxx', XMLDB_TYPE_CHAR, '100');
526         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
528         $dbman->create_table($table);
530         $this->assertTrue($dbman->table_exists($tablename));
532         // Test insert record
533         $rec1 = new stdClass();
534         $rec1->long_int_columnname_with_28c = 28;
535         $rec1->long_dec_columnname_with_28c = 28.28;
536         $rec1->long_str_columnname_with_28c = '28';
537         $rec1->long_int_columnname_with_30cxx = 30;
538         $rec1->long_dec_columnname_with_30cxx = 30.30;
539         $rec1->long_str_columnname_with_30cxx = '30';
541         // insert_record()
542         $rec1->id = $DB->insert_record($tablename, $rec1);
543         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
545         // update_record()
546         $DB->update_record($tablename, $rec1);
547         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
549         // set_field()
550         $rec1->long_int_columnname_with_28c = 280;
551         $DB->set_field($tablename, 'long_int_columnname_with_28c', $rec1->long_int_columnname_with_28c,
552             array('id' => $rec1->id, 'long_int_columnname_with_28c' => 28));
553         $rec1->long_dec_columnname_with_28c = 280.28;
554         $DB->set_field($tablename, 'long_dec_columnname_with_28c', $rec1->long_dec_columnname_with_28c,
555             array('id' => $rec1->id, 'long_dec_columnname_with_28c' => 28.28));
556         $rec1->long_str_columnname_with_28c = '280';
557         $DB->set_field($tablename, 'long_str_columnname_with_28c', $rec1->long_str_columnname_with_28c,
558             array('id' => $rec1->id, 'long_str_columnname_with_28c' => '28'));
559         $rec1->long_int_columnname_with_30cxx = 300;
560         $DB->set_field($tablename, 'long_int_columnname_with_30cxx', $rec1->long_int_columnname_with_30cxx,
561             array('id' => $rec1->id, 'long_int_columnname_with_30cxx' => 30));
562         $rec1->long_dec_columnname_with_30cxx = 300.30;
563         $DB->set_field($tablename, 'long_dec_columnname_with_30cxx', $rec1->long_dec_columnname_with_30cxx,
564             array('id' => $rec1->id, 'long_dec_columnname_with_30cxx' => 30.30));
565         $rec1->long_str_columnname_with_30cxx = '300';
566         $DB->set_field($tablename, 'long_str_columnname_with_30cxx', $rec1->long_str_columnname_with_30cxx,
567             array('id' => $rec1->id, 'long_str_columnname_with_30cxx' => '30'));
568         $this->assertEqual($rec1, $DB->get_record($tablename, array('id' => $rec1->id)));
570         // delete_records()
571         $rec2 = $DB->get_record($tablename, array('id' => $rec1->id));
572         $rec2->id = $DB->insert_record($tablename, $rec2);
573         $this->assertEqual(2, $DB->count_records($tablename));
574         $DB->delete_records($tablename, (array) $rec2);
575         $this->assertEqual(1, $DB->count_records($tablename));
577         // get_recordset()
578         $rs = $DB->get_recordset($tablename, (array) $rec1);
579         $iterations = 0;
580         foreach ($rs as $rec2) {
581             $iterations++;
582         }
583         $rs->close();
584         $this->assertEqual(1, $iterations);
585         $this->assertEqual($rec1, $rec2);
587         // get_records()
588         $recs = $DB->get_records($tablename, (array) $rec1);
589         $this->assertEqual(1, count($recs));
590         $this->assertEqual($rec1, reset($recs));
592         // get_fieldset_select()
593         $select = 'id = :id AND
594                    long_int_columnname_with_28c = :long_int_columnname_with_28c AND
595                    long_dec_columnname_with_28c = :long_dec_columnname_with_28c AND
596                    long_str_columnname_with_28c = :long_str_columnname_with_28c AND
597                    long_int_columnname_with_30cxx = :long_int_columnname_with_30cxx AND
598                    long_dec_columnname_with_30cxx = :long_dec_columnname_with_30cxx AND
599                    long_str_columnname_with_30cxx = :long_str_columnname_with_30cxx';
600         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_28c', $select, (array)$rec1);
601         $this->assertEqual(1, count($fields));
602         $this->assertEqual($rec1->long_int_columnname_with_28c, reset($fields));
603         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_28c', $select, (array)$rec1);
604         $this->assertEqual($rec1->long_dec_columnname_with_28c, reset($fields));
605         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_28c', $select, (array)$rec1);
606         $this->assertEqual($rec1->long_str_columnname_with_28c, reset($fields));
607         $fields = $DB->get_fieldset_select($tablename, 'long_int_columnname_with_30cxx', $select, (array)$rec1);
608         $this->assertEqual($rec1->long_int_columnname_with_30cxx, reset($fields));
609         $fields = $DB->get_fieldset_select($tablename, 'long_dec_columnname_with_30cxx', $select, (array)$rec1);
610         $this->assertEqual($rec1->long_dec_columnname_with_30cxx, reset($fields));
611         $fields = $DB->get_fieldset_select($tablename, 'long_str_columnname_with_30cxx', $select, (array)$rec1);
612         $this->assertEqual($rec1->long_str_columnname_with_30cxx, reset($fields));
614         // overlapping placeholders (progressive str_replace)
615         $overlapselect = 'id = :p AND
616                    long_int_columnname_with_28c = :param1 AND
617                    long_dec_columnname_with_28c = :param2 AND
618                    long_str_columnname_with_28c = :param_with_29_characters_long AND
619                    long_int_columnname_with_30cxx = :param_with_30_characters_long_ AND
620                    long_dec_columnname_with_30cxx = :param_ AND
621                    long_str_columnname_with_30cxx = :param__';
622         $overlapparams = array(
623                 'p' => $rec1->id,
624                 'param1' => $rec1->long_int_columnname_with_28c,
625                 'param2' => $rec1->long_dec_columnname_with_28c,
626                 'param_with_29_characters_long' => $rec1->long_str_columnname_with_28c,
627                 'param_with_30_characters_long_' => $rec1->long_int_columnname_with_30cxx,
628                 'param_' => $rec1->long_dec_columnname_with_30cxx,
629                 'param__' => $rec1->long_str_columnname_with_30cxx);
630         $recs = $DB->get_records_select($tablename, $overlapselect, $overlapparams);
631         $this->assertEqual(1, count($recs));
632         $this->assertEqual($rec1, reset($recs));
634         // execute()
635         $DB->execute("DELETE FROM {{$tablename}} WHERE $select", (array)$rec1);
636         $this->assertEqual(0, $DB->count_records($tablename));
637     }
639     public function test_get_tables() {
640         $DB = $this->tdb;
641         $dbman = $this->tdb->get_manager();
643         // Need to test with multiple DBs
644         $table = $this->get_test_table();
645         $tablename = $table->getName();
647         $original_count = count($DB->get_tables());
649         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
650         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
652         $dbman->create_table($table);
653         $this->assertTrue(count($DB->get_tables()) == $original_count + 1);
655         $dbman->drop_table($table);
656         $this->assertTrue(count($DB->get_tables()) == $original_count);
657     }
659     public function test_get_indexes() {
660         $DB = $this->tdb;
661         $dbman = $this->tdb->get_manager();
663         $table = $this->get_test_table();
664         $tablename = $table->getName();
666         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
667         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
668         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
669         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
670         $table->add_index('course-id', XMLDB_INDEX_UNIQUE, array('course', 'id'));
671         $dbman->create_table($table);
673         $indices = $DB->get_indexes($tablename);
674         $this->assertTrue(is_array($indices));
675         $this->assertEqual(count($indices), 2);
676         // we do not care about index names for now
677         $first = array_shift($indices);
678         $second = array_shift($indices);
679         if (count($first['columns']) == 2) {
680             $composed = $first;
681             $single   = $second;
682         } else {
683             $composed = $second;
684             $single   = $first;
685         }
686         $this->assertFalse($single['unique']);
687         $this->assertTrue($composed['unique']);
688         $this->assertEqual(1, count($single['columns']));
689         $this->assertEqual(2, count($composed['columns']));
690         $this->assertEqual('course', $single['columns'][0]);
691         $this->assertEqual('course', $composed['columns'][0]);
692         $this->assertEqual('id', $composed['columns'][1]);
693     }
695     public function test_get_columns() {
696         $DB = $this->tdb;
697         $dbman = $this->tdb->get_manager();
699         $table = $this->get_test_table();
700         $tablename = $table->getName();
702         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
703         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
704         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala');
705         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
706         $table->add_field('enumfield', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'test2');
707         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
708         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
709         $dbman->create_table($table);
711         $columns = $DB->get_columns($tablename);
712         $this->assertTrue(is_array($columns));
714         $fields = $table->getFields();
715         $this->assertEqual(count($columns), count($fields));
717         $field = $columns['id'];
718         $this->assertEqual('R', $field->meta_type);
719         $this->assertTrue($field->auto_increment);
720         $this->assertTrue($field->unique);
722         $field = $columns['course'];
723         $this->assertEqual('I', $field->meta_type);
724         $this->assertFalse($field->auto_increment);
725         $this->assertTrue($field->has_default);
726         $this->assertEqual(0, $field->default_value);
727         $this->assertTrue($field->not_null);
729         $field = $columns['name'];
730         $this->assertEqual('C', $field->meta_type);
731         $this->assertFalse($field->auto_increment);
732         $this->assertTrue($field->has_default);
733         $this->assertIdentical('lala', $field->default_value);
734         $this->assertFalse($field->not_null);
736         $field = $columns['description'];
737         $this->assertEqual('X', $field->meta_type);
738         $this->assertFalse($field->auto_increment);
739         $this->assertFalse($field->has_default);
740         $this->assertIdentical(null, $field->default_value);
741         $this->assertFalse($field->not_null);
743         $field = $columns['enumfield'];
744         $this->assertEqual('C', $field->meta_type);
745         $this->assertFalse($field->auto_increment);
746         $this->assertIdentical('test2', $field->default_value);
747         $this->assertTrue($field->not_null);
749         $field = $columns['onenum'];
750         $this->assertEqual('N', $field->meta_type);
751         $this->assertFalse($field->auto_increment);
752         $this->assertTrue($field->has_default);
753         $this->assertEqual(200.0, $field->default_value);
754         $this->assertFalse($field->not_null);
756         for ($i = 0; $i < count($columns); $i++) {
757             if ($i == 0) {
758                 $next_column = reset($columns);
759                 $next_field  = reset($fields);
760             } else {
761                 $next_column = next($columns);
762                 $next_field  = next($fields);
763             }
765             $this->assertEqual($next_column->name, $next_field->name);
766         }
767     }
769     public function test_get_manager() {
770         $DB = $this->tdb;
771         $dbman = $this->tdb->get_manager();
773         $this->assertTrue($dbman instanceof database_manager);
774     }
776     public function test_setup_is_unicodedb() {
777         $DB = $this->tdb;
778         $this->assertTrue($DB->setup_is_unicodedb());
779     }
781     public function test_set_debug() { //tests get_debug() too
782         $DB = $this->tdb;
783         $dbman = $this->tdb->get_manager();
785         $table = $this->get_test_table();
786         $tablename = $table->getName();
788         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
789         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
790         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
791         $dbman->create_table($table);
793         $sql = "SELECT * FROM {{$tablename}}";
795         $prevdebug = $DB->get_debug();
797         ob_start();
798         $DB->set_debug(true);
799         $this->assertTrue($DB->get_debug());
800         $DB->execute($sql);
801         $DB->set_debug(false);
802         $this->assertFalse($DB->get_debug());
803         $debuginfo = ob_get_contents();
804         ob_end_clean();
805         $this->assertFalse($debuginfo === '');
807         ob_start();
808         $DB->execute($sql);
809         $debuginfo = ob_get_contents();
810         ob_end_clean();
811         $this->assertTrue($debuginfo === '');
813         $DB->set_debug($prevdebug);
814     }
816     public function test_execute() {
817         $DB = $this->tdb;
818         $dbman = $this->tdb->get_manager();
820         $table1 = $this->get_test_table('1');
821         $tablename1 = $table1->getName();
822         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
823         $table1->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
824         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
825         $table1->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
826         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
827         $dbman->create_table($table1);
829         $table2 = $this->get_test_table('2');
830         $tablename2 = $table2->getName();
831         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
832         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
833         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
834         $dbman->create_table($table2);
836         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'aaa'));
837         $DB->insert_record($tablename1, array('course' => 1, 'name' => 'bbb'));
838         $DB->insert_record($tablename1, array('course' => 7, 'name' => 'ccc'));
839         $DB->insert_record($tablename1, array('course' => 3, 'name' => 'ddd'));
841         // select results are ignored
842         $sql = "SELECT * FROM {{$tablename1}} WHERE course = :course";
843         $this->assertTrue($DB->execute($sql, array('course'=>3)));
845         // throw exception on error
846         $sql = "XXUPDATE SET XSSD";
847         try {
848             $DB->execute($sql);
849             $this->fail("Expecting an exception, none occurred");
850         } catch (Exception $e) {
851             $this->assertTrue($e instanceof dml_write_exception);
852         }
854         // update records
855         $sql = "UPDATE {{$tablename1}}
856                    SET course = 6
857                  WHERE course = ?";
858         $this->assertTrue($DB->execute($sql, array('3')));
859         $this->assertEqual($DB->count_records($tablename1, array('course' => 6)), 2);
861         // insert from one into second table
862         $sql = "INSERT INTO {{$tablename2}} (course)
864                 SELECT course
865                   FROM {{$tablename1}}";
866         $this->assertTrue($DB->execute($sql));
867         $this->assertEqual($DB->count_records($tablename2), 4);
868     }
870     public function test_get_recordset() {
871         $DB = $this->tdb;
872         $dbman = $DB->get_manager();
874         $table = $this->get_test_table();
875         $tablename = $table->getName();
877         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
878         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
879         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
880         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
881         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
882         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
883         $dbman->create_table($table);
885         $data = array(array('id' => 1, 'course' => 3, 'name' => 'record1', 'onetext'=>'abc'),
886                       array('id' => 2, 'course' => 3, 'name' => 'record2', 'onetext'=>'abcd'),
887                       array('id' => 3, 'course' => 5, 'name' => 'record3', 'onetext'=>'abcde'));
889         foreach ($data as $record) {
890             $DB->insert_record($tablename, $record);
891         }
893         // standard recordset iteration
894         $rs = $DB->get_recordset($tablename);
895         $this->assertTrue($rs instanceof moodle_recordset);
896         reset($data);
897         foreach($rs as $record) {
898             $data_record = current($data);
899             foreach ($record as $k => $v) {
900                 $this->assertEqual($data_record[$k], $v);
901             }
902             next($data);
903         }
904         $rs->close();
906         // iterator style usage
907         $rs = $DB->get_recordset($tablename);
908         $this->assertTrue($rs instanceof moodle_recordset);
909         reset($data);
910         while ($rs->valid()) {
911             $record = $rs->current();
912             $data_record = current($data);
913             foreach ($record as $k => $v) {
914                 $this->assertEqual($data_record[$k], $v);
915             }
916             next($data);
917             $rs->next();
918         }
919         $rs->close();
921         // make sure rewind is ignored
922         $rs = $DB->get_recordset($tablename);
923         $this->assertTrue($rs instanceof moodle_recordset);
924         reset($data);
925         $i = 0;
926         foreach($rs as $record) {
927             $i++;
928             $rs->rewind();
929             if ($i > 10) {
930                 $this->fail('revind not ignored in recordsets');
931                 break;
932             }
933             $data_record = current($data);
934             foreach ($record as $k => $v) {
935                 $this->assertEqual($data_record[$k], $v);
936             }
937             next($data);
938         }
939         $rs->close();
941         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
942         $conditions = array('onetext' => '1');
943         try {
944             $rs = $DB->get_recordset($tablename, $conditions);
945             $this->fail('An Exception is missing, expected due to equating of text fields');
946         } catch (exception $e) {
947             $this->assertTrue($e instanceof dml_exception);
948             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
949         }
951         // notes:
952         //  * limits are tested in test_get_recordset_sql()
953         //  * where_clause() is used internally and is tested in test_get_records()
954     }
956     public function test_get_recordset_iterator_keys() {
957         $DB = $this->tdb;
958         $dbman = $DB->get_manager();
960         $table = $this->get_test_table();
961         $tablename = $table->getName();
963         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
964         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
965         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, '0');
966         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
967         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
968         $dbman->create_table($table);
970         $data = array(array('id'=> 1, 'course' => 3, 'name' => 'record1'),
971                       array('id'=> 2, 'course' => 3, 'name' => 'record2'),
972                       array('id'=> 3, 'course' => 5, 'name' => 'record3'));
973         foreach ($data as $record) {
974             $DB->insert_record($tablename, $record);
975         }
977         // Test repeated numeric keys are returned ok
978         $rs = $DB->get_recordset($tablename, NULL, NULL, 'course, name, id');
980         reset($data);
981         $count = 0;
982         foreach($rs as $key => $record) {
983             $data_record = current($data);
984             $this->assertEqual($data_record['course'], $key);
985             next($data);
986             $count++;
987         }
988         $rs->close();
989         $this->assertEqual($count, 3);
991         // Test string keys are returned ok
992         $rs = $DB->get_recordset($tablename, NULL, NULL, 'name, course, id');
994         reset($data);
995         $count = 0;
996         foreach($rs as $key => $record) {
997             $data_record = current($data);
998             $this->assertEqual($data_record['name'], $key);
999             next($data);
1000             $count++;
1001         }
1002         $rs->close();
1003         $this->assertEqual($count, 3);
1005         // Test numeric not starting in 1 keys are returned ok
1006         $rs = $DB->get_recordset($tablename, NULL, 'id DESC', 'id, course, name');
1008         $data = array_reverse($data);
1009         reset($data);
1010         $count = 0;
1011         foreach($rs as $key => $record) {
1012             $data_record = current($data);
1013             $this->assertEqual($data_record['id'], $key);
1014             next($data);
1015             $count++;
1016         }
1017         $rs->close();
1018         $this->assertEqual($count, 3);
1019     }
1021     public function test_get_recordset_list() {
1022         $DB = $this->tdb;
1023         $dbman = $DB->get_manager();
1025         $table = $this->get_test_table();
1026         $tablename = $table->getName();
1028         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1029         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1030         $table->add_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
1031         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1032         $dbman->create_table($table);
1034         $DB->insert_record($tablename, array('course' => 3));
1035         $DB->insert_record($tablename, array('course' => 3));
1036         $DB->insert_record($tablename, array('course' => 5));
1037         $DB->insert_record($tablename, array('course' => 2));
1039         $rs = $DB->get_recordset_list($tablename, 'course', array(3, 2));
1041         $counter = 0;
1042         foreach ($rs as $record) {
1043             $counter++;
1044         }
1045         $this->assertEqual(3, $counter);
1046         $rs->close();
1048         $rs = $DB->get_recordset_list($tablename, 'course',array()); /// Must return 0 rows without conditions. MDL-17645
1050         $counter = 0;
1051         foreach ($rs as $record) {
1052             $counter++;
1053         }
1054         $rs->close();
1055         $this->assertEqual(0, $counter);
1057         // notes:
1058         //  * limits are tested in test_get_recordset_sql()
1059         //  * where_clause() is used internally and is tested in test_get_records()
1060     }
1062     public function test_get_recordset_select() {
1063         $DB = $this->tdb;
1064         $dbman = $DB->get_manager();
1066         $table = $this->get_test_table();
1067         $tablename = $table->getName();
1069         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1070         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1071         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1072         $dbman->create_table($table);
1074         $DB->insert_record($tablename, array('course' => 3));
1075         $DB->insert_record($tablename, array('course' => 3));
1076         $DB->insert_record($tablename, array('course' => 5));
1077         $DB->insert_record($tablename, array('course' => 2));
1079         $rs = $DB->get_recordset_select($tablename, '');
1080         $counter = 0;
1081         foreach ($rs as $record) {
1082             $counter++;
1083         }
1084         $rs->close();
1085         $this->assertEqual(4, $counter);
1087         $this->assertTrue($rs = $DB->get_recordset_select($tablename, 'course = 3'));
1088         $counter = 0;
1089         foreach ($rs as $record) {
1090             $counter++;
1091         }
1092         $rs->close();
1093         $this->assertEqual(2, $counter);
1095         // notes:
1096         //  * limits are tested in test_get_recordset_sql()
1097     }
1099     public function test_get_recordset_sql() {
1100         $DB = $this->tdb;
1101         $dbman = $DB->get_manager();
1103         $table = $this->get_test_table();
1104         $tablename = $table->getName();
1106         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1107         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1108         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1109         $dbman->create_table($table);
1111         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1112         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1113         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1114         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1115         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1116         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1117         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1119         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1120         $counter = 0;
1121         foreach ($rs as $record) {
1122             $counter++;
1123         }
1124         $rs->close();
1125         $this->assertEqual(2, $counter);
1127         // limits - only need to test this case, the rest have been tested by test_get_records_sql()
1128         // only limitfrom = skips that number of records
1129         $rs = $DB->get_recordset_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1130         $records = array();
1131         foreach($rs as $key => $record) {
1132             $records[$key] = $record;
1133         }
1134         $rs->close();
1135         $this->assertEqual(5, count($records));
1136         $this->assertEqual($inskey3, reset($records)->id);
1137         $this->assertEqual($inskey7, end($records)->id);
1139         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
1140     }
1142     public function test_get_records() {
1143         $DB = $this->tdb;
1144         $dbman = $DB->get_manager();
1146         $table = $this->get_test_table();
1147         $tablename = $table->getName();
1149         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1150         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1151         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1152         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1153         $dbman->create_table($table);
1155         $DB->insert_record($tablename, array('course' => 3));
1156         $DB->insert_record($tablename, array('course' => 3));
1157         $DB->insert_record($tablename, array('course' => 5));
1158         $DB->insert_record($tablename, array('course' => 2));
1160         // All records
1161         $records = $DB->get_records($tablename);
1162         $this->assertEqual(4, count($records));
1163         $this->assertEqual(3, $records[1]->course);
1164         $this->assertEqual(3, $records[2]->course);
1165         $this->assertEqual(5, $records[3]->course);
1166         $this->assertEqual(2, $records[4]->course);
1168         // Records matching certain conditions
1169         $records = $DB->get_records($tablename, array('course' => 3));
1170         $this->assertEqual(2, count($records));
1171         $this->assertEqual(3, $records[1]->course);
1172         $this->assertEqual(3, $records[2]->course);
1174         // All records sorted by course
1175         $records = $DB->get_records($tablename, null, 'course');
1176         $this->assertEqual(4, count($records));
1177         $current_record = reset($records);
1178         $this->assertEqual(4, $current_record->id);
1179         $current_record = next($records);
1180         $this->assertEqual(1, $current_record->id);
1181         $current_record = next($records);
1182         $this->assertEqual(2, $current_record->id);
1183         $current_record = next($records);
1184         $this->assertEqual(3, $current_record->id);
1186         // All records, but get only one field
1187         $records = $DB->get_records($tablename, null, '', 'id');
1188         $this->assertFalse(isset($records[1]->course));
1189         $this->assertTrue(isset($records[1]->id));
1190         $this->assertEqual(4, count($records));
1192         // Booleans into params
1193         $records = $DB->get_records($tablename, array('course' => true));
1194         $this->assertEqual(0, count($records));
1195         $records = $DB->get_records($tablename, array('course' => false));
1196         $this->assertEqual(0, count($records));
1198         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1199         $conditions = array('onetext' => '1');
1200         try {
1201             $records = $DB->get_records($tablename, $conditions);
1202             $this->fail('An Exception is missing, expected due to equating of text fields');
1203         } catch (exception $e) {
1204             $this->assertTrue($e instanceof dml_exception);
1205             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1206         }
1208         // note: delegate limits testing to test_get_records_sql()
1209     }
1211     public function test_get_records_list() {
1212         $DB = $this->tdb;
1213         $dbman = $DB->get_manager();
1215         $table = $this->get_test_table();
1216         $tablename = $table->getName();
1218         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1219         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1220         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1221         $dbman->create_table($table);
1223         $DB->insert_record($tablename, array('course' => 3));
1224         $DB->insert_record($tablename, array('course' => 3));
1225         $DB->insert_record($tablename, array('course' => 5));
1226         $DB->insert_record($tablename, array('course' => 2));
1228         $records = $DB->get_records_list($tablename, 'course', array(3, 2));
1229         $this->assertTrue(is_array($records));
1230         $this->assertEqual(3, count($records));
1231         $this->assertEqual(1, reset($records)->id);
1232         $this->assertEqual(2, next($records)->id);
1233         $this->assertEqual(4, next($records)->id);
1235         $this->assertIdentical(array(), $records = $DB->get_records_list($tablename, 'course', array())); /// Must return 0 rows without conditions. MDL-17645
1236         $this->assertEqual(0, count($records));
1238         // note: delegate limits testing to test_get_records_sql()
1239     }
1241     public function test_get_records_sql() {
1242         $DB = $this->tdb;
1243         $dbman = $DB->get_manager();
1245         $table = $this->get_test_table();
1246         $tablename = $table->getName();
1248         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1249         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1250         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1251         $dbman->create_table($table);
1253         $inskey1 = $DB->insert_record($tablename, array('course' => 3));
1254         $inskey2 = $DB->insert_record($tablename, array('course' => 5));
1255         $inskey3 = $DB->insert_record($tablename, array('course' => 4));
1256         $inskey4 = $DB->insert_record($tablename, array('course' => 3));
1257         $inskey5 = $DB->insert_record($tablename, array('course' => 2));
1258         $inskey6 = $DB->insert_record($tablename, array('course' => 1));
1259         $inskey7 = $DB->insert_record($tablename, array('course' => 0));
1261         $table2 = $this->get_test_table("2");
1262         $tablename2 = $table2->getName();
1263         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1264         $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1265         $table2->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
1266         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1267         $dbman->create_table($table2);
1269         $DB->insert_record($tablename2, array('course'=>3, 'nametext'=>'badabing'));
1270         $DB->insert_record($tablename2, array('course'=>4, 'nametext'=>'badabang'));
1271         $DB->insert_record($tablename2, array('course'=>5, 'nametext'=>'badabung'));
1272         $DB->insert_record($tablename2, array('course'=>6, 'nametext'=>'badabong'));
1274         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3));
1275         $this->assertEqual(2, count($records));
1276         $this->assertEqual($inskey1, reset($records)->id);
1277         $this->assertEqual($inskey4, next($records)->id);
1279         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
1280         $this->enable_debugging();
1281         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
1282         $this->assertFalse($this->get_debugging() === '');
1283         $this->assertEqual(6, count($records));
1285         // negative limits = no limits
1286         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
1287         $this->assertEqual(7, count($records));
1289         // zero limits = no limits
1290         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 0);
1291         $this->assertEqual(7, count($records));
1293         // only limitfrom = skips that number of records
1294         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 2, 0);
1295         $this->assertEqual(5, count($records));
1296         $this->assertEqual($inskey3, reset($records)->id);
1297         $this->assertEqual($inskey7, end($records)->id);
1299         // only limitnum = fetches that number of records
1300         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 0, 3);
1301         $this->assertEqual(3, count($records));
1302         $this->assertEqual($inskey1, reset($records)->id);
1303         $this->assertEqual($inskey3, end($records)->id);
1305         // both limitfrom and limitnum = skips limitfrom records and fetches limitnum ones
1306         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, 3, 2);
1307         $this->assertEqual(2, count($records));
1308         $this->assertEqual($inskey4, reset($records)->id);
1309         $this->assertEqual($inskey5, end($records)->id);
1311         // both limitfrom and limitnum in query having subqueris
1312         // note the subquery skips records with course = 0 and 3
1313         $sql = "SELECT * FROM {{$tablename}}
1314                  WHERE course NOT IN (
1315                      SELECT course FROM {{$tablename}}
1316                       WHERE course IN (0, 3))
1317                 ORDER BY course";
1318         $records = $DB->get_records_sql($sql, null, 0, 2); // Skip 0, get 2
1319         $this->assertEqual(2, count($records));
1320         $this->assertEqual($inskey6, reset($records)->id);
1321         $this->assertEqual($inskey5, end($records)->id);
1322         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip 2, get 2
1323         $this->assertEqual(2, count($records));
1324         $this->assertEqual($inskey3, reset($records)->id);
1325         $this->assertEqual($inskey2, end($records)->id);
1327         // test 2 tables with aliases and limits with order bys
1328         $sql = "SELECT t1.id, t1.course AS cid, t2.nametext
1329                   FROM {{$tablename}} t1, {{$tablename2}} t2
1330                  WHERE t2.course=t1.course
1331               ORDER BY t1.course, ". $DB->sql_compare_text('t2.nametext');
1332         $records = $DB->get_records_sql($sql, null, 2, 2); // Skip courses 3 and 6, get 4 and 5
1333         $this->assertEqual(2, count($records));
1334         $this->assertEqual('5', end($records)->cid);
1335         $this->assertEqual('4', reset($records)->cid);
1337         // test 2 tables with aliases and limits with the highest INT limit works
1338         $records = $DB->get_records_sql($sql, null, 2, PHP_INT_MAX); // Skip course {3,6}, get {4,5}
1339         $this->assertEqual(2, count($records));
1340         $this->assertEqual('5', end($records)->cid);
1341         $this->assertEqual('4', reset($records)->cid);
1343         // test 2 tables with aliases and limits with order bys (limit which is highest INT number)
1344         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, 2); // Skip all courses
1345         $this->assertEqual(0, count($records));
1347         // test 2 tables with aliases and limits with order bys (limit which s highest INT number)
1348         $records = $DB->get_records_sql($sql, null, PHP_INT_MAX, PHP_INT_MAX); // Skip all courses
1349         $this->assertEqual(0, count($records));
1351         // TODO: Test limits in queries having DISTINCT clauses
1353         // note: fetching nulls, empties, LOBs already tested by test_update_record() no needed here
1354     }
1356     public function test_get_records_menu() {
1357         $DB = $this->tdb;
1358         $dbman = $DB->get_manager();
1360         $table = $this->get_test_table();
1361         $tablename = $table->getName();
1363         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1364         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1365         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1366         $dbman->create_table($table);
1368         $DB->insert_record($tablename, array('course' => 3));
1369         $DB->insert_record($tablename, array('course' => 3));
1370         $DB->insert_record($tablename, array('course' => 5));
1371         $DB->insert_record($tablename, array('course' => 2));
1373         $records = $DB->get_records_menu($tablename, array('course' => 3));
1374         $this->assertTrue(is_array($records));
1375         $this->assertEqual(2, count($records));
1376         $this->assertFalse(empty($records[1]));
1377         $this->assertFalse(empty($records[2]));
1378         $this->assertEqual(3, $records[1]);
1379         $this->assertEqual(3, $records[2]);
1381         // note: delegate limits testing to test_get_records_sql()
1382     }
1384     public function test_get_records_select_menu() {
1385         $DB = $this->tdb;
1386         $dbman = $DB->get_manager();
1388         $table = $this->get_test_table();
1389         $tablename = $table->getName();
1391         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1392         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1393         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1394         $dbman->create_table($table);
1396         $DB->insert_record($tablename, array('course' => 3));
1397         $DB->insert_record($tablename, array('course' => 2));
1398         $DB->insert_record($tablename, array('course' => 3));
1399         $DB->insert_record($tablename, array('course' => 5));
1401         $records = $DB->get_records_select_menu($tablename, "course > ?", array(2));
1402         $this->assertTrue(is_array($records));
1404         $this->assertEqual(3, count($records));
1405         $this->assertFalse(empty($records[1]));
1406         $this->assertTrue(empty($records[2]));
1407         $this->assertFalse(empty($records[3]));
1408         $this->assertFalse(empty($records[4]));
1409         $this->assertEqual(3, $records[1]);
1410         $this->assertEqual(3, $records[3]);
1411         $this->assertEqual(5, $records[4]);
1413         // note: delegate limits testing to test_get_records_sql()
1414     }
1416     public function test_get_records_sql_menu() {
1417         $DB = $this->tdb;
1418         $dbman = $DB->get_manager();
1420         $table = $this->get_test_table();
1421         $tablename = $table->getName();
1423         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1424         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1425         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1426         $dbman->create_table($table);
1428         $DB->insert_record($tablename, array('course' => 3));
1429         $DB->insert_record($tablename, array('course' => 2));
1430         $DB->insert_record($tablename, array('course' => 3));
1431         $DB->insert_record($tablename, array('course' => 5));
1433         $records = $DB->get_records_sql_menu("SELECT * FROM {{$tablename}} WHERE course > ?", array(2));
1434         $this->assertTrue(is_array($records));
1436         $this->assertEqual(3, count($records));
1437         $this->assertFalse(empty($records[1]));
1438         $this->assertTrue(empty($records[2]));
1439         $this->assertFalse(empty($records[3]));
1440         $this->assertFalse(empty($records[4]));
1441         $this->assertEqual(3, $records[1]);
1442         $this->assertEqual(3, $records[3]);
1443         $this->assertEqual(5, $records[4]);
1445         // note: delegate limits testing to test_get_records_sql()
1446     }
1448     public function test_get_record() {
1449         $DB = $this->tdb;
1450         $dbman = $DB->get_manager();
1452         $table = $this->get_test_table();
1453         $tablename = $table->getName();
1455         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1456         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1457         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1458         $dbman->create_table($table);
1460         $DB->insert_record($tablename, array('course' => 3));
1461         $DB->insert_record($tablename, array('course' => 2));
1463         $record = $DB->get_record($tablename, array('id' => 2));
1464         $this->assertTrue($record instanceof stdClass);
1466         $this->assertEqual(2, $record->course);
1467         $this->assertEqual(2, $record->id);
1468     }
1471     public function test_get_record_select() {
1472         $DB = $this->tdb;
1473         $dbman = $DB->get_manager();
1475         $table = $this->get_test_table();
1476         $tablename = $table->getName();
1478         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1479         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1480         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1481         $dbman->create_table($table);
1483         $DB->insert_record($tablename, array('course' => 3));
1484         $DB->insert_record($tablename, array('course' => 2));
1486         $record = $DB->get_record_select($tablename, "id = ?", array(2));
1487         $this->assertTrue($record instanceof stdClass);
1489         $this->assertEqual(2, $record->course);
1491         // note: delegates limit testing to test_get_records_sql()
1492     }
1494     public function test_get_record_sql() {
1495         $DB = $this->tdb;
1496         $dbman = $DB->get_manager();
1498         $table = $this->get_test_table();
1499         $tablename = $table->getName();
1501         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1502         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1503         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1504         $dbman->create_table($table);
1506         $DB->insert_record($tablename, array('course' => 3));
1507         $DB->insert_record($tablename, array('course' => 2));
1509         // standard use
1510         $record = $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(2));
1511         $this->assertTrue($record instanceof stdClass);
1512         $this->assertEqual(2, $record->course);
1513         $this->assertEqual(2, $record->id);
1515         // backwards compatibility with $ignoremultiple
1516         $this->assertFalse(IGNORE_MISSING);
1517         $this->assertTrue(IGNORE_MULTIPLE);
1519         // record not found - ignore
1520         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MISSING));
1521         $this->assertFalse($DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), IGNORE_MULTIPLE));
1523         // record not found error
1524         try {
1525             $DB->get_record_sql("SELECT * FROM {{$tablename}} WHERE id = ?", array(666), MUST_EXIST);
1526             $this->fail("Exception expected");
1527         } catch (dml_missing_record_exception $e) {
1528             $this->assertTrue(true);
1529         }
1531         $this->enable_debugging();
1532         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
1533         $this->assertFalse($this->get_debugging() === '');
1535         // multiple matches ignored
1536         $this->assertTrue($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
1538         // multiple found error
1539         try {
1540             $DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), MUST_EXIST);
1541             $this->fail("Exception expected");
1542         } catch (dml_multiple_records_exception $e) {
1543             $this->assertTrue(true);
1544         }
1545     }
1547     public function test_get_field() {
1548         $DB = $this->tdb;
1549         $dbman = $DB->get_manager();
1551         $table = $this->get_test_table();
1552         $tablename = $table->getName();
1554         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1555         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1556         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1557         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1558         $dbman->create_table($table);
1560         $id1 = $DB->insert_record($tablename, array('course' => 3));
1561         $DB->insert_record($tablename, array('course' => 5));
1562         $DB->insert_record($tablename, array('course' => 5));
1564         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id1)));
1565         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('course' => 3)));
1567         $this->assertIdentical(false, $DB->get_field($tablename, 'course', array('course' => 11), IGNORE_MISSING));
1568         try {
1569             $DB->get_field($tablename, 'course', array('course' => 4), MUST_EXIST);
1570             $this->assertFail('Exception expected due to missing record');
1571         } catch (dml_exception $ex) {
1572             $this->assertTrue(true);
1573         }
1575         $this->enable_debugging();
1576         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
1577         $this->assertIdentical($this->get_debugging(), '');
1579         $this->enable_debugging();
1580         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
1581         $this->assertFalse($this->get_debugging() === '');
1583         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
1584         $conditions = array('onetext' => '1');
1585         try {
1586             $DB->get_field($tablename, 'course', $conditions);
1587             $this->fail('An Exception is missing, expected due to equating of text fields');
1588         } catch (exception $e) {
1589             $this->assertTrue($e instanceof dml_exception);
1590             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
1591         }
1592     }
1594     public function test_get_field_select() {
1595         $DB = $this->tdb;
1596         $dbman = $DB->get_manager();
1598         $table = $this->get_test_table();
1599         $tablename = $table->getName();
1601         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1602         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1603         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1604         $dbman->create_table($table);
1606         $DB->insert_record($tablename, array('course' => 3));
1608         $this->assertEqual(3, $DB->get_field_select($tablename, 'course', "id = ?", array(1)));
1609     }
1611     public function test_get_field_sql() {
1612         $DB = $this->tdb;
1613         $dbman = $DB->get_manager();
1615         $table = $this->get_test_table();
1616         $tablename = $table->getName();
1618         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1619         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1620         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1621         $dbman->create_table($table);
1623         $DB->insert_record($tablename, array('course' => 3));
1625         $this->assertEqual(3, $DB->get_field_sql("SELECT course FROM {{$tablename}} WHERE id = ?", array(1)));
1626     }
1628     public function test_get_fieldset_select() {
1629         $DB = $this->tdb;
1630         $dbman = $DB->get_manager();
1632         $table = $this->get_test_table();
1633         $tablename = $table->getName();
1635         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1636         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1637         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1638         $dbman->create_table($table);
1640         $DB->insert_record($tablename, array('course' => 1));
1641         $DB->insert_record($tablename, array('course' => 3));
1642         $DB->insert_record($tablename, array('course' => 2));
1643         $DB->insert_record($tablename, array('course' => 6));
1645         $fieldset = $DB->get_fieldset_select($tablename, 'course', "course > ?", array(1));
1646         $this->assertTrue(is_array($fieldset));
1648         $this->assertEqual(3, count($fieldset));
1649         $this->assertEqual(3, $fieldset[0]);
1650         $this->assertEqual(2, $fieldset[1]);
1651         $this->assertEqual(6, $fieldset[2]);
1652     }
1654     public function test_get_fieldset_sql() {
1655         $DB = $this->tdb;
1656         $dbman = $DB->get_manager();
1658         $table = $this->get_test_table();
1659         $tablename = $table->getName();
1661         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1662         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1663         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1664         $dbman->create_table($table);
1666         $DB->insert_record($tablename, array('course' => 1));
1667         $DB->insert_record($tablename, array('course' => 3));
1668         $DB->insert_record($tablename, array('course' => 2));
1669         $DB->insert_record($tablename, array('course' => 6));
1671         $fieldset = $DB->get_fieldset_sql("SELECT * FROM {{$tablename}} WHERE course > ?", array(1));
1672         $this->assertTrue(is_array($fieldset));
1674         $this->assertEqual(3, count($fieldset));
1675         $this->assertEqual(2, $fieldset[0]);
1676         $this->assertEqual(3, $fieldset[1]);
1677         $this->assertEqual(4, $fieldset[2]);
1678     }
1680     public function test_insert_record_raw() {
1681         $DB = $this->tdb;
1682         $dbman = $DB->get_manager();
1684         $table = $this->get_test_table();
1685         $tablename = $table->getName();
1687         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1688         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1689         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1690         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1691         $dbman->create_table($table);
1693         $record = (object)array('course' => 1, 'onechar' => 'xx');
1694         $before = clone($record);
1695         $result = $DB->insert_record_raw($tablename, $record);
1696         $this->assertIdentical(1, $result);
1697         $this->assertIdentical($record, $before);
1699         $record = $DB->get_record($tablename, array('course' => 1));
1700         $this->assertTrue($record instanceof stdClass);
1701         $this->assertIdentical('xx', $record->onechar);
1703         $result = $DB->insert_record_raw($tablename, array('course' => 2, 'onechar' => 'yy'), false);
1704         $this->assertIdentical(true, $result);
1706         // note: bulk not implemented yet
1707         $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'zz'), true, true);
1708         $record = $DB->get_record($tablename, array('course' => 3));
1709         $this->assertTrue($record instanceof stdClass);
1710         $this->assertIdentical('zz', $record->onechar);
1712         // custom sequence (id) - returnid is ignored
1713         $result = $DB->insert_record_raw($tablename, array('id' => 10, 'course' => 3, 'onechar' => 'bb'), true, false, true);
1714         $this->assertIdentical(true, $result);
1715         $record = $DB->get_record($tablename, array('id' => 10));
1716         $this->assertTrue($record instanceof stdClass);
1717         $this->assertIdentical('bb', $record->onechar);
1719         // custom sequence - missing id error
1720         try {
1721             $DB->insert_record_raw($tablename, array('course' => 3, 'onechar' => 'bb'), true, false, true);
1722             $this->assertFail('Exception expected due to missing record');
1723         } catch (coding_exception $ex) {
1724             $this->assertTrue(true);
1725         }
1727         // wrong column error
1728         try {
1729             $DB->insert_record_raw($tablename, array('xxxxx' => 3, 'onechar' => 'bb'));
1730             $this->assertFail('Exception expected due to invalid column');
1731         } catch (dml_write_exception $ex) {
1732             $this->assertTrue(true);
1733         }
1734     }
1736     public function test_insert_record() {
1737         // All the information in this test is fetched from DB by get_recordset() so we
1738         // have such method properly tested against nulls, empties and friends...
1740         $DB = $this->tdb;
1741         $dbman = $DB->get_manager();
1743         $table = $this->get_test_table();
1744         $tablename = $table->getName();
1746         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1747         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1748         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1749         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1750         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1751         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1752         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1753         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
1754         $dbman->create_table($table);
1756         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
1757         $record = $DB->get_record($tablename, array('course' => 1));
1758         $this->assertEqual(1, $record->id);
1759         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
1760         $this->assertEqual(200, $record->onenum);
1761         $this->assertIdentical('onestring', $record->onechar);
1762         $this->assertNull($record->onetext);
1763         $this->assertNull($record->onebinary);
1765         // without returning id, bulk not implemented
1766         $result = $this->assertIdentical(true, $DB->insert_record($tablename, array('course' => 99), false, true));
1767         $record = $DB->get_record($tablename, array('course' => 99));
1768         $this->assertEqual(2, $record->id);
1769         $this->assertEqual(99, $record->course);
1771         // Check nulls are set properly for all types
1772         $record = new stdClass();
1773         $record->oneint = null;
1774         $record->onenum = null;
1775         $record->onechar = null;
1776         $record->onetext = null;
1777         $record->onebinary = null;
1778         $recid = $DB->insert_record($tablename, $record);
1779         $record = $DB->get_record($tablename, array('id' => $recid));
1780         $this->assertEqual(0, $record->course);
1781         $this->assertNull($record->oneint);
1782         $this->assertNull($record->onenum);
1783         $this->assertNull($record->onechar);
1784         $this->assertNull($record->onetext);
1785         $this->assertNull($record->onebinary);
1787         // Check zeros are set properly for all types
1788         $record = new stdClass();
1789         $record->oneint = 0;
1790         $record->onenum = 0;
1791         $recid = $DB->insert_record($tablename, $record);
1792         $record = $DB->get_record($tablename, array('id' => $recid));
1793         $this->assertEqual(0, $record->oneint);
1794         $this->assertEqual(0, $record->onenum);
1796         // Check booleans are set properly for all types
1797         $record = new stdClass();
1798         $record->oneint = true; // trues
1799         $record->onenum = true;
1800         $record->onechar = true;
1801         $record->onetext = true;
1802         $recid = $DB->insert_record($tablename, $record);
1803         $record = $DB->get_record($tablename, array('id' => $recid));
1804         $this->assertEqual(1, $record->oneint);
1805         $this->assertEqual(1, $record->onenum);
1806         $this->assertEqual(1, $record->onechar);
1807         $this->assertEqual(1, $record->onetext);
1809         $record = new stdClass();
1810         $record->oneint = false; // falses
1811         $record->onenum = false;
1812         $record->onechar = false;
1813         $record->onetext = false;
1814         $recid = $DB->insert_record($tablename, $record);
1815         $record = $DB->get_record($tablename, array('id' => $recid));
1816         $this->assertEqual(0, $record->oneint);
1817         $this->assertEqual(0, $record->onenum);
1818         $this->assertEqual(0, $record->onechar);
1819         $this->assertEqual(0, $record->onetext);
1821         // Check string data causes exception in numeric types
1822         $record = new stdClass();
1823         $record->oneint = 'onestring';
1824         $record->onenum = 0;
1825         try {
1826             $DB->insert_record($tablename, $record);
1827             $this->fail("Expecting an exception, none occurred");
1828         } catch (exception $e) {
1829             $this->assertTrue($e instanceof dml_exception);
1830         }
1831         $record = new stdClass();
1832         $record->oneint = 0;
1833         $record->onenum = 'onestring';
1834         try {
1835            $DB->insert_record($tablename, $record);
1836            $this->fail("Expecting an exception, none occurred");
1837         } catch (exception $e) {
1838             $this->assertTrue($e instanceof dml_exception);
1839         }
1841         // Check empty string data is stored as 0 in numeric datatypes
1842         $record = new stdClass();
1843         $record->oneint = ''; // empty string
1844         $record->onenum = 0;
1845         $recid = $DB->insert_record($tablename, $record);
1846         $record = $DB->get_record($tablename, array('id' => $recid));
1847         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
1849         $record = new stdClass();
1850         $record->oneint = 0;
1851         $record->onenum = ''; // empty string
1852         $recid = $DB->insert_record($tablename, $record);
1853         $record = $DB->get_record($tablename, array('id' => $recid));
1854         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
1856         // Check empty strings are set properly in string types
1857         $record = new stdClass();
1858         $record->oneint = 0;
1859         $record->onenum = 0;
1860         $record->onechar = '';
1861         $record->onetext = '';
1862         $recid = $DB->insert_record($tablename, $record);
1863         $record = $DB->get_record($tablename, array('id' => $recid));
1864         $this->assertTrue($record->onechar === '');
1865         $this->assertTrue($record->onetext === '');
1867         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
1868         $record = new stdClass();
1869         $record->oneint = ((210.10 + 39.92) - 150.02);
1870         $record->onenum = ((210.10 + 39.92) - 150.02);
1871         $recid = $DB->insert_record($tablename, $record);
1872         $record = $DB->get_record($tablename, array('id' => $recid));
1873         $this->assertEqual(100, $record->oneint);
1874         $this->assertEqual(100, $record->onenum);
1876         // Check various quotes/backslashes combinations in string types
1877         $teststrings = array(
1878             'backslashes and quotes alone (even): "" \'\' \\\\',
1879             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
1880             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
1881             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
1882         foreach ($teststrings as $teststring) {
1883             $record = new stdClass();
1884             $record->onechar = $teststring;
1885             $record->onetext = $teststring;
1886             $recid = $DB->insert_record($tablename, $record);
1887             $record = $DB->get_record($tablename, array('id' => $recid));
1888             $this->assertEqual($teststring, $record->onechar);
1889             $this->assertEqual($teststring, $record->onetext);
1890         }
1892         // Check LOBs in text/binary columns
1893         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
1894         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
1895         $record = new stdClass();
1896         $record->onetext = $clob;
1897         $record->onebinary = $blob;
1898         $recid = $DB->insert_record($tablename, $record);
1899         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1900         $record = $rs->current();
1901         $rs->close();
1902         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
1903         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
1905         // And "small" LOBs too, just in case
1906         $newclob = substr($clob, 0, 500);
1907         $newblob = substr($blob, 0, 250);
1908         $record = new stdClass();
1909         $record->onetext = $newclob;
1910         $record->onebinary = $newblob;
1911         $recid = $DB->insert_record($tablename, $record);
1912         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1913         $record = $rs->current();
1914         $rs->close();
1915         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
1916         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
1917         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1919         // And "diagnostic" LOBs too, just in case
1920         $newclob = '\'"\\;/ěščřžýáíé';
1921         $newblob = '\'"\\;/ěščřžýáíé';
1922         $record = new stdClass();
1923         $record->onetext = $newclob;
1924         $record->onebinary = $newblob;
1925         $recid = $DB->insert_record($tablename, $record);
1926         $rs = $DB->get_recordset($tablename, array('id' => $recid));
1927         $record = $rs->current();
1928         $rs->close();
1929         $this->assertIdentical($newclob, $record->onetext);
1930         $this->assertIdentical($newblob, $record->onebinary);
1931         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
1933         // test data is not modified
1934         $record = new stdClass();
1935         $record->id     = -1; // has to be ignored
1936         $record->course = 3;
1937         $record->lalala = 'lalal'; // unused
1938         $before = clone($record);
1939         $DB->insert_record($tablename, $record);
1940         $this->assertEqual($record, $before);
1942         // make sure the id is always increasing and never reuses the same id
1943         $id1 = $DB->insert_record($tablename, array('course' => 3));
1944         $id2 = $DB->insert_record($tablename, array('course' => 3));
1945         $this->assertTrue($id1 < $id2);
1946         $DB->delete_records($tablename, array('id'=>$id2));
1947         $id3 = $DB->insert_record($tablename, array('course' => 3));
1948         $this->assertTrue($id2 < $id3);
1949         $DB->delete_records($tablename, array());
1950         $id4 = $DB->insert_record($tablename, array('course' => 3));
1951         $this->assertTrue($id3 < $id4);
1953         // Test saving a float in a CHAR column, and reading it back.
1954         $id = $DB->insert_record($tablename, array('onechar' => 1.0));
1955         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1956         $id = $DB->insert_record($tablename, array('onechar' => 1e20));
1957         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1958         $id = $DB->insert_record($tablename, array('onechar' => 1e-4));
1959         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1960         $id = $DB->insert_record($tablename, array('onechar' => 1e-5));
1961         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1962         $id = $DB->insert_record($tablename, array('onechar' => 1e-300));
1963         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1964         $id = $DB->insert_record($tablename, array('onechar' => 1e300));
1965         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
1967         // Test saving a float in a TEXT column, and reading it back.
1968         $id = $DB->insert_record($tablename, array('onetext' => 1.0));
1969         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1970         $id = $DB->insert_record($tablename, array('onetext' => 1e20));
1971         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1972         $id = $DB->insert_record($tablename, array('onetext' => 1e-4));
1973         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1974         $id = $DB->insert_record($tablename, array('onetext' => 1e-5));
1975         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1976         $id = $DB->insert_record($tablename, array('onetext' => 1e-300));
1977         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1978         $id = $DB->insert_record($tablename, array('onetext' => 1e300));
1979         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
1980     }
1982     public function test_import_record() {
1983         // All the information in this test is fetched from DB by get_recordset() so we
1984         // have such method properly tested against nulls, empties and friends...
1986         $DB = $this->tdb;
1987         $dbman = $DB->get_manager();
1989         $table = $this->get_test_table();
1990         $tablename = $table->getName();
1992         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
1993         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
1994         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
1995         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
1996         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
1997         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
1998         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
1999         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2000         $dbman->create_table($table);
2002         $this->assertIdentical(1, $DB->insert_record($tablename, array('course' => 1), true));
2003         $record = $DB->get_record($tablename, array('course' => 1));
2004         $this->assertEqual(1, $record->id);
2005         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2006         $this->assertEqual(200, $record->onenum);
2007         $this->assertIdentical('onestring', $record->onechar);
2008         $this->assertNull($record->onetext);
2009         $this->assertNull($record->onebinary);
2011         // ignore extra columns
2012         $record = (object)array('id'=>13, 'course'=>2, 'xxxx'=>788778);
2013         $before = clone($record);
2014         $this->assertIdentical(true, $DB->import_record($tablename, $record));
2015         $this->assertIdentical($record, $before);
2016         $records = $DB->get_records($tablename);
2017         $this->assertEqual(2, $records[13]->course);
2019         // Check nulls are set properly for all types
2020         $record = new stdClass();
2021         $record->id = 20;
2022         $record->oneint = null;
2023         $record->onenum = null;
2024         $record->onechar = null;
2025         $record->onetext = null;
2026         $record->onebinary = null;
2027         $this->assertTrue($DB->import_record($tablename, $record));
2028         $record = $DB->get_record($tablename, array('id' => 20));
2029         $this->assertEqual(0, $record->course);
2030         $this->assertNull($record->oneint);
2031         $this->assertNull($record->onenum);
2032         $this->assertNull($record->onechar);
2033         $this->assertNull($record->onetext);
2034         $this->assertNull($record->onebinary);
2036         // Check zeros are set properly for all types
2037         $record = new stdClass();
2038         $record->id = 23;
2039         $record->oneint = 0;
2040         $record->onenum = 0;
2041         $this->assertTrue($DB->import_record($tablename, $record));
2042         $record = $DB->get_record($tablename, array('id' => 23));
2043         $this->assertEqual(0, $record->oneint);
2044         $this->assertEqual(0, $record->onenum);
2046         // Check string data causes exception in numeric types
2047         $record = new stdClass();
2048         $record->id = 32;
2049         $record->oneint = 'onestring';
2050         $record->onenum = 0;
2051         try {
2052             $DB->import_record($tablename, $record);
2053             $this->fail("Expecting an exception, none occurred");
2054         } catch (exception $e) {
2055             $this->assertTrue($e instanceof dml_exception);
2056         }
2057         $record = new stdClass();
2058         $record->id = 35;
2059         $record->oneint = 0;
2060         $record->onenum = 'onestring';
2061         try {
2062            $DB->import_record($tablename, $record);
2063            $this->fail("Expecting an exception, none occurred");
2064         } catch (exception $e) {
2065             $this->assertTrue($e instanceof dml_exception);
2066         }
2068         // Check empty strings are set properly in string types
2069         $record = new stdClass();
2070         $record->id = 44;
2071         $record->oneint = 0;
2072         $record->onenum = 0;
2073         $record->onechar = '';
2074         $record->onetext = '';
2075         $this->assertTrue($DB->import_record($tablename, $record));
2076         $record = $DB->get_record($tablename, array('id' => 44));
2077         $this->assertTrue($record->onechar === '');
2078         $this->assertTrue($record->onetext === '');
2080         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2081         $record = new stdClass();
2082         $record->id = 47;
2083         $record->oneint = ((210.10 + 39.92) - 150.02);
2084         $record->onenum = ((210.10 + 39.92) - 150.02);
2085         $this->assertTrue($DB->import_record($tablename, $record));
2086         $record = $DB->get_record($tablename, array('id' => 47));
2087         $this->assertEqual(100, $record->oneint);
2088         $this->assertEqual(100, $record->onenum);
2090         // Check various quotes/backslashes combinations in string types
2091         $i = 50;
2092         $teststrings = array(
2093             'backslashes and quotes alone (even): "" \'\' \\\\',
2094             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2095             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2096             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2097         foreach ($teststrings as $teststring) {
2098             $record = new stdClass();
2099             $record->id = $i;
2100             $record->onechar = $teststring;
2101             $record->onetext = $teststring;
2102             $this->assertTrue($DB->import_record($tablename, $record));
2103             $record = $DB->get_record($tablename, array('id' => $i));
2104             $this->assertEqual($teststring, $record->onechar);
2105             $this->assertEqual($teststring, $record->onetext);
2106             $i = $i + 3;
2107         }
2109         // Check LOBs in text/binary columns
2110         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2111         $record = new stdClass();
2112         $record->id = 70;
2113         $record->onetext = $clob;
2114         $record->onebinary = '';
2115         $this->assertTrue($DB->import_record($tablename, $record));
2116         $rs = $DB->get_recordset($tablename, array('id' => 70));
2117         $record = $rs->current();
2118         $rs->close();
2119         $this->assertEqual($clob, $record->onetext, 'Test CLOB insert (full contents output disabled)');
2121         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2122         $record = new stdClass();
2123         $record->id = 71;
2124         $record->onetext = '';
2125         $record->onebinary = $blob;
2126         $this->assertTrue($DB->import_record($tablename, $record));
2127         $rs = $DB->get_recordset($tablename, array('id' => 71));
2128         $record = $rs->current();
2129         $rs->close();
2130         $this->assertEqual($blob, $record->onebinary, 'Test BLOB insert (full contents output disabled)');
2132         // And "small" LOBs too, just in case
2133         $newclob = substr($clob, 0, 500);
2134         $newblob = substr($blob, 0, 250);
2135         $record = new stdClass();
2136         $record->id = 73;
2137         $record->onetext = $newclob;
2138         $record->onebinary = $newblob;
2139         $this->assertTrue($DB->import_record($tablename, $record));
2140         $rs = $DB->get_recordset($tablename, array('id' => 73));
2141         $record = $rs->current();
2142         $rs->close();
2143         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB insert (full contents output disabled)');
2144         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB insert (full contents output disabled)');
2145         $this->assertEqual(false, $rs->key()); // Ensure recordset key() method to be working ok after closing
2146     }
2148     public function test_update_record_raw() {
2149         $DB = $this->tdb;
2150         $dbman = $DB->get_manager();
2152         $table = $this->get_test_table();
2153         $tablename = $table->getName();
2155         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2156         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2157         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2158         $dbman->create_table($table);
2160         $DB->insert_record($tablename, array('course' => 1));
2161         $DB->insert_record($tablename, array('course' => 3));
2163         $record = $DB->get_record($tablename, array('course' => 1));
2164         $record->course = 2;
2165         $this->assertTrue($DB->update_record_raw($tablename, $record));
2166         $this->assertEqual(0, $DB->count_records($tablename, array('course' => 1)));
2167         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 2)));
2168         $this->assertEqual(1, $DB->count_records($tablename, array('course' => 3)));
2170         $record = $DB->get_record($tablename, array('course' => 1));
2171         $record->xxxxx = 2;
2172         try {
2173            $DB->update_record_raw($tablename, $record);
2174            $this->fail("Expecting an exception, none occurred");
2175         } catch (Exception $e) {
2176             $this->assertTrue($e instanceof coding_exception);
2177         }
2179         $record = $DB->get_record($tablename, array('course' => 3));
2180         unset($record->id);
2181         try {
2182            $DB->update_record_raw($tablename, $record);
2183            $this->fail("Expecting an exception, none occurred");
2184         } catch (Exception $e) {
2185             $this->assertTrue($e instanceof coding_exception);
2186         }
2187     }
2189     public function test_update_record() {
2191         // All the information in this test is fetched from DB by get_record() so we
2192         // have such method properly tested against nulls, empties and friends...
2194         $DB = $this->tdb;
2195         $dbman = $DB->get_manager();
2197         $table = $this->get_test_table();
2198         $tablename = $table->getName();
2200         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2201         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2202         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, 100);
2203         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null, 200);
2204         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null, 'onestring');
2205         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2206         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2207         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2208         $dbman->create_table($table);
2210         $DB->insert_record($tablename, array('course' => 1));
2211         $record = $DB->get_record($tablename, array('course' => 1));
2212         $record->course = 2;
2214         $this->assertTrue($DB->update_record($tablename, $record));
2215         $this->assertFalse($record = $DB->get_record($tablename, array('course' => 1)));
2216         $this->assertTrue($record = $DB->get_record($tablename, array('course' => 2)));
2217         $this->assertEqual(100, $record->oneint); // Just check column defaults have been applied
2218         $this->assertEqual(200, $record->onenum);
2219         $this->assertEqual('onestring', $record->onechar);
2220         $this->assertNull($record->onetext);
2221         $this->assertNull($record->onebinary);
2223         // Check nulls are set properly for all types
2224         $record->oneint = null;
2225         $record->onenum = null;
2226         $record->onechar = null;
2227         $record->onetext = null;
2228         $record->onebinary = null;
2229         $DB->update_record($tablename, $record);
2230         $record = $DB->get_record($tablename, array('course' => 2));
2231         $this->assertNull($record->oneint);
2232         $this->assertNull($record->onenum);
2233         $this->assertNull($record->onechar);
2234         $this->assertNull($record->onetext);
2235         $this->assertNull($record->onebinary);
2237         // Check zeros are set properly for all types
2238         $record->oneint = 0;
2239         $record->onenum = 0;
2240         $DB->update_record($tablename, $record);
2241         $record = $DB->get_record($tablename, array('course' => 2));
2242         $this->assertEqual(0, $record->oneint);
2243         $this->assertEqual(0, $record->onenum);
2245         // Check booleans are set properly for all types
2246         $record->oneint = true; // trues
2247         $record->onenum = true;
2248         $record->onechar = true;
2249         $record->onetext = true;
2250         $DB->update_record($tablename, $record);
2251         $record = $DB->get_record($tablename, array('course' => 2));
2252         $this->assertEqual(1, $record->oneint);
2253         $this->assertEqual(1, $record->onenum);
2254         $this->assertEqual(1, $record->onechar);
2255         $this->assertEqual(1, $record->onetext);
2257         $record->oneint = false; // falses
2258         $record->onenum = false;
2259         $record->onechar = false;
2260         $record->onetext = false;
2261         $DB->update_record($tablename, $record);
2262         $record = $DB->get_record($tablename, array('course' => 2));
2263         $this->assertEqual(0, $record->oneint);
2264         $this->assertEqual(0, $record->onenum);
2265         $this->assertEqual(0, $record->onechar);
2266         $this->assertEqual(0, $record->onetext);
2268         // Check string data causes exception in numeric types
2269         $record->oneint = 'onestring';
2270         $record->onenum = 0;
2271         try {
2272             $DB->update_record($tablename, $record);
2273             $this->fail("Expecting an exception, none occurred");
2274         } catch (exception $e) {
2275             $this->assertTrue($e instanceof dml_exception);
2276         }
2277         $record->oneint = 0;
2278         $record->onenum = 'onestring';
2279         try {
2280             $DB->update_record($tablename, $record);
2281             $this->fail("Expecting an exception, none occurred");
2282         } catch (exception $e) {
2283             $this->assertTrue($e instanceof dml_exception);
2284         }
2286         // Check empty string data is stored as 0 in numeric datatypes
2287         $record->oneint = ''; // empty string
2288         $record->onenum = 0;
2289         $DB->update_record($tablename, $record);
2290         $record = $DB->get_record($tablename, array('course' => 2));
2291         $this->assertTrue(is_numeric($record->oneint) && $record->oneint == 0);
2293         $record->oneint = 0;
2294         $record->onenum = ''; // empty string
2295         $DB->update_record($tablename, $record);
2296         $record = $DB->get_record($tablename, array('course' => 2));
2297         $this->assertTrue(is_numeric($record->onenum) && $record->onenum == 0);
2299         // Check empty strings are set properly in string types
2300         $record->oneint = 0;
2301         $record->onenum = 0;
2302         $record->onechar = '';
2303         $record->onetext = '';
2304         $DB->update_record($tablename, $record);
2305         $record = $DB->get_record($tablename, array('course' => 2));
2306         $this->assertTrue($record->onechar === '');
2307         $this->assertTrue($record->onetext === '');
2309         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2310         $record->oneint = ((210.10 + 39.92) - 150.02);
2311         $record->onenum = ((210.10 + 39.92) - 150.02);
2312         $DB->update_record($tablename, $record);
2313         $record = $DB->get_record($tablename, array('course' => 2));
2314         $this->assertEqual(100, $record->oneint);
2315         $this->assertEqual(100, $record->onenum);
2317         // Check various quotes/backslashes combinations in string types
2318         $teststrings = array(
2319             'backslashes and quotes alone (even): "" \'\' \\\\',
2320             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2321             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2322             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2323         foreach ($teststrings as $teststring) {
2324             $record->onechar = $teststring;
2325             $record->onetext = $teststring;
2326             $DB->update_record($tablename, $record);
2327             $record = $DB->get_record($tablename, array('course' => 2));
2328             $this->assertEqual($teststring, $record->onechar);
2329             $this->assertEqual($teststring, $record->onetext);
2330         }
2332         // Check LOBs in text/binary columns
2333         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2334         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2335         $record->onetext = $clob;
2336         $record->onebinary = $blob;
2337         $DB->update_record($tablename, $record);
2338         $record = $DB->get_record($tablename, array('course' => 2));
2339         $this->assertEqual($clob, $record->onetext, 'Test CLOB update (full contents output disabled)');
2340         $this->assertEqual($blob, $record->onebinary, 'Test BLOB update (full contents output disabled)');
2342         // And "small" LOBs too, just in case
2343         $newclob = substr($clob, 0, 500);
2344         $newblob = substr($blob, 0, 250);
2345         $record->onetext = $newclob;
2346         $record->onebinary = $newblob;
2347         $DB->update_record($tablename, $record);
2348         $record = $DB->get_record($tablename, array('course' => 2));
2349         $this->assertEqual($newclob, $record->onetext, 'Test "small" CLOB update (full contents output disabled)');
2350         $this->assertEqual($newblob, $record->onebinary, 'Test "small" BLOB update (full contents output disabled)');
2352         // Test saving a float in a CHAR column, and reading it back.
2353         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2354         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1.0));
2355         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2356         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e20));
2357         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2358         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-4));
2359         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2360         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-5));
2361         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2362         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e-300));
2363         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2364         $DB->update_record($tablename, array('id' => $id, 'onechar' => 1e300));
2365         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2367         // Test saving a float in a TEXT column, and reading it back.
2368         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2369         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1.0));
2370         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2371         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e20));
2372         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2373         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-4));
2374         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2375         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-5));
2376         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2377         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e-300));
2378         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2379         $DB->update_record($tablename, array('id' => $id, 'onetext' => 1e300));
2380         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2381     }
2383     public function test_set_field() {
2384         $DB = $this->tdb;
2385         $dbman = $DB->get_manager();
2387         $table = $this->get_test_table();
2388         $tablename = $table->getName();
2390         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2391         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2392         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2393         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2394         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2395         $dbman->create_table($table);
2397         // simple set_field
2398         $id1 = $DB->insert_record($tablename, array('course' => 1));
2399         $id2 = $DB->insert_record($tablename, array('course' => 1));
2400         $id3 = $DB->insert_record($tablename, array('course' => 3));
2401         $this->assertTrue($DB->set_field($tablename, 'course', 2, array('id' => $id1)));
2402         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => $id1)));
2403         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2404         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2405         $DB->delete_records($tablename, array());
2407         // multiple fields affected
2408         $id1 = $DB->insert_record($tablename, array('course' => 1));
2409         $id2 = $DB->insert_record($tablename, array('course' => 1));
2410         $id3 = $DB->insert_record($tablename, array('course' => 3));
2411         $DB->set_field($tablename, 'course', '5', array('course' => 1));
2412         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2413         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2414         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2415         $DB->delete_records($tablename, array());
2417         // no field affected
2418         $id1 = $DB->insert_record($tablename, array('course' => 1));
2419         $id2 = $DB->insert_record($tablename, array('course' => 1));
2420         $id3 = $DB->insert_record($tablename, array('course' => 3));
2421         $DB->set_field($tablename, 'course', '5', array('course' => 0));
2422         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id1)));
2423         $this->assertEqual(1, $DB->get_field($tablename, 'course', array('id' => $id2)));
2424         $this->assertEqual(3, $DB->get_field($tablename, 'course', array('id' => $id3)));
2425         $DB->delete_records($tablename, array());
2427         // all fields - no condition
2428         $id1 = $DB->insert_record($tablename, array('course' => 1));
2429         $id2 = $DB->insert_record($tablename, array('course' => 1));
2430         $id3 = $DB->insert_record($tablename, array('course' => 3));
2431         $DB->set_field($tablename, 'course', 5, array());
2432         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id1)));
2433         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id2)));
2434         $this->assertEqual(5, $DB->get_field($tablename, 'course', array('id' => $id3)));
2436         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2437         $conditions = array('onetext' => '1');
2438         try {
2439             $DB->set_field($tablename, 'onechar', 'frog', $conditions);
2440             $this->fail('An Exception is missing, expected due to equating of text fields');
2441         } catch (exception $e) {
2442             $this->assertTrue($e instanceof dml_exception);
2443             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2444         }
2446         // Test saving a float in a CHAR column, and reading it back.
2447         $id = $DB->insert_record($tablename, array('onechar' => 'X'));
2448         $DB->set_field($tablename, 'onechar', 1.0, array('id' => $id));
2449         $this->assertEqual(1.0, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2450         $DB->set_field($tablename, 'onechar', 1e20, array('id' => $id));
2451         $this->assertEqual(1e20, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2452         $DB->set_field($tablename, 'onechar', 1e-4, array('id' => $id));
2453         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2454         $DB->set_field($tablename, 'onechar', 1e-5, array('id' => $id));
2455         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2456         $DB->set_field($tablename, 'onechar', 1e-300, array('id' => $id));
2457         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2458         $DB->set_field($tablename, 'onechar', 1e300, array('id' => $id));
2459         $this->assertEqual(1e300, $DB->get_field($tablename, 'onechar', array('id' => $id)));
2461         // Test saving a float in a TEXT column, and reading it back.
2462         $id = $DB->insert_record($tablename, array('onetext' => 'X'));
2463         $DB->set_field($tablename, 'onetext', 1.0, array('id' => $id));
2464         $this->assertEqual(1.0, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2465         $DB->set_field($tablename, 'onetext', 1e20, array('id' => $id));
2466         $this->assertEqual(1e20, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2467         $DB->set_field($tablename, 'onetext', 1e-4, array('id' => $id));
2468         $this->assertEqual(1e-4, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2469         $DB->set_field($tablename, 'onetext', 1e-5, array('id' => $id));
2470         $this->assertEqual(1e-5, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2471         $DB->set_field($tablename, 'onetext', 1e-300, array('id' => $id));
2472         $this->assertEqual(1e-300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2473         $DB->set_field($tablename, 'onetext', 1e300, array('id' => $id));
2474         $this->assertEqual(1e300, $DB->get_field($tablename, 'onetext', array('id' => $id)));
2476         // Note: All the nulls, booleans, empties, quoted and backslashes tests
2477         // go to set_field_select() because set_field() is just one wrapper over it
2478     }
2480     public function test_set_field_select() {
2482         // All the information in this test is fetched from DB by get_field() so we
2483         // have such method properly tested against nulls, empties and friends...
2485         $DB = $this->tdb;
2486         $dbman = $DB->get_manager();
2488         $table = $this->get_test_table();
2489         $tablename = $table->getName();
2491         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2492         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2493         $table->add_field('oneint', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null);
2494         $table->add_field('onenum', XMLDB_TYPE_NUMBER, '10,2', null, null, null);
2495         $table->add_field('onechar', XMLDB_TYPE_CHAR, '100', null, null, null);
2496         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2497         $table->add_field('onebinary', XMLDB_TYPE_BINARY, 'big', null, null, null);
2498         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2499         $dbman->create_table($table);
2501         $DB->insert_record($tablename, array('course' => 1));
2503         $this->assertTrue($DB->set_field_select($tablename, 'course', 2, 'id = ?', array(1)));
2504         $this->assertEqual(2, $DB->get_field($tablename, 'course', array('id' => 1)));
2506         // Check nulls are set properly for all types
2507         $DB->set_field_select($tablename, 'oneint', null, 'id = ?', array(1)); // trues
2508         $DB->set_field_select($tablename, 'onenum', null, 'id = ?', array(1));
2509         $DB->set_field_select($tablename, 'onechar', null, 'id = ?', array(1));
2510         $DB->set_field_select($tablename, 'onetext', null, 'id = ?', array(1));
2511         $DB->set_field_select($tablename, 'onebinary', null, 'id = ?', array(1));
2512         $this->assertNull($DB->get_field($tablename, 'oneint', array('id' => 1)));
2513         $this->assertNull($DB->get_field($tablename, 'onenum', array('id' => 1)));
2514         $this->assertNull($DB->get_field($tablename, 'onechar', array('id' => 1)));
2515         $this->assertNull($DB->get_field($tablename, 'onetext', array('id' => 1)));
2516         $this->assertNull($DB->get_field($tablename, 'onebinary', array('id' => 1)));
2518         // Check zeros are set properly for all types
2519         $DB->set_field_select($tablename, 'oneint', 0, 'id = ?', array(1));
2520         $DB->set_field_select($tablename, 'onenum', 0, 'id = ?', array(1));
2521         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2522         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2524         // Check booleans are set properly for all types
2525         $DB->set_field_select($tablename, 'oneint', true, 'id = ?', array(1)); // trues
2526         $DB->set_field_select($tablename, 'onenum', true, 'id = ?', array(1));
2527         $DB->set_field_select($tablename, 'onechar', true, 'id = ?', array(1));
2528         $DB->set_field_select($tablename, 'onetext', true, 'id = ?', array(1));
2529         $this->assertEqual(1, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2530         $this->assertEqual(1, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2531         $this->assertEqual(1, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2532         $this->assertEqual(1, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2534         $DB->set_field_select($tablename, 'oneint', false, 'id = ?', array(1)); // falses
2535         $DB->set_field_select($tablename, 'onenum', false, 'id = ?', array(1));
2536         $DB->set_field_select($tablename, 'onechar', false, 'id = ?', array(1));
2537         $DB->set_field_select($tablename, 'onetext', false, 'id = ?', array(1));
2538         $this->assertEqual(0, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2539         $this->assertEqual(0, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2540         $this->assertEqual(0, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2541         $this->assertEqual(0, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2543         // Check string data causes exception in numeric types
2544         try {
2545             $DB->set_field_select($tablename, 'oneint', 'onestring', 'id = ?', array(1));
2546             $this->fail("Expecting an exception, none occurred");
2547         } catch (exception $e) {
2548             $this->assertTrue($e instanceof dml_exception);
2549         }
2550         try {
2551             $DB->set_field_select($tablename, 'onenum', 'onestring', 'id = ?', array(1));
2552             $this->fail("Expecting an exception, none occurred");
2553         } catch (exception $e) {
2554             $this->assertTrue($e instanceof dml_exception);
2555         }
2557         // Check empty string data is stored as 0 in numeric datatypes
2558         $DB->set_field_select($tablename, 'oneint', '', 'id = ?', array(1));
2559         $field = $DB->get_field($tablename, 'oneint', array('id' => 1));
2560         $this->assertTrue(is_numeric($field) && $field == 0);
2562         $DB->set_field_select($tablename, 'onenum', '', 'id = ?', array(1));
2563         $field = $DB->get_field($tablename, 'onenum', array('id' => 1));
2564         $this->assertTrue(is_numeric($field) && $field == 0);
2566         // Check empty strings are set properly in string types
2567         $DB->set_field_select($tablename, 'onechar', '', 'id = ?', array(1));
2568         $DB->set_field_select($tablename, 'onetext', '', 'id = ?', array(1));
2569         $this->assertTrue($DB->get_field($tablename, 'onechar', array('id' => 1)) === '');
2570         $this->assertTrue($DB->get_field($tablename, 'onetext', array('id' => 1)) === '');
2572         // Check operation ((210.10 + 39.92) - 150.02) against numeric types
2573         $DB->set_field_select($tablename, 'oneint', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2574         $DB->set_field_select($tablename, 'onenum', ((210.10 + 39.92) - 150.02), 'id = ?', array(1));
2575         $this->assertEqual(100, $DB->get_field($tablename, 'oneint', array('id' => 1)));
2576         $this->assertEqual(100, $DB->get_field($tablename, 'onenum', array('id' => 1)));
2578         // Check various quotes/backslashes combinations in string types
2579         $teststrings = array(
2580             'backslashes and quotes alone (even): "" \'\' \\\\',
2581             'backslashes and quotes alone (odd): """ \'\'\' \\\\\\',
2582             'backslashes and quotes sequences (even): \\"\\" \\\'\\\'',
2583             'backslashes and quotes sequences (odd): \\"\\"\\" \\\'\\\'\\\'');
2584         foreach ($teststrings as $teststring) {
2585             $DB->set_field_select($tablename, 'onechar', $teststring, 'id = ?', array(1));
2586             $DB->set_field_select($tablename, 'onetext', $teststring, 'id = ?', array(1));
2587             $this->assertEqual($teststring, $DB->get_field($tablename, 'onechar', array('id' => 1)));
2588             $this->assertEqual($teststring, $DB->get_field($tablename, 'onetext', array('id' => 1)));
2589         }
2591         // Check LOBs in text/binary columns
2592         $clob = file_get_contents(dirname(__FILE__).'/fixtures/clob.txt');
2593         $blob = file_get_contents(dirname(__FILE__).'/fixtures/randombinary');
2594         $DB->set_field_select($tablename, 'onetext', $clob, 'id = ?', array(1));
2595         $DB->set_field_select($tablename, 'onebinary', $blob, 'id = ?', array(1));
2596         $this->assertEqual($clob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test CLOB set_field (full contents output disabled)');
2597         $this->assertEqual($blob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test BLOB set_field (full contents output disabled)');
2599         // And "small" LOBs too, just in case
2600         $newclob = substr($clob, 0, 500);
2601         $newblob = substr($blob, 0, 250);
2602         $DB->set_field_select($tablename, 'onetext', $newclob, 'id = ?', array(1));
2603         $DB->set_field_select($tablename, 'onebinary', $newblob, 'id = ?', array(1));
2604         $this->assertEqual($newclob, $DB->get_field($tablename, 'onetext', array('id' => 1)), 'Test "small" CLOB set_field (full contents output disabled)');
2605         $this->assertEqual($newblob, $DB->get_field($tablename, 'onebinary', array('id' => 1)), 'Test "small" BLOB set_field (full contents output disabled)');
2607         // This is the failure from MDL-24863. This was giving an error on MSSQL,
2608         // which converts the '1' to an integer, which cannot then be compared with
2609         // onetext cast to a varchar. This should be fixed and working now.
2610         $newchar = 'frog';
2611         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2612         $params = array('onetext' => '1');
2613         try {
2614             $DB->set_field_select($tablename, 'onechar', $newchar, $DB->sql_compare_text('onetext') . ' = ?', $params);
2615             $this->assertTrue(true, 'No exceptions thrown with numerical text param comparison for text field.');
2616         } catch (dml_exception $e) {
2617             $this->assertFalse(true, 'We have an unexpected exception.');
2618             throw $e;
2619         }
2622     }
2624     public function test_count_records() {
2625         $DB = $this->tdb;
2627         $dbman = $DB->get_manager();
2629         $table = $this->get_test_table();
2630         $tablename = $table->getName();
2632         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2633         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2634         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2635         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2636         $dbman->create_table($table);
2638         $this->assertEqual(0, $DB->count_records($tablename));
2640         $DB->insert_record($tablename, array('course' => 3));
2641         $DB->insert_record($tablename, array('course' => 4));
2642         $DB->insert_record($tablename, array('course' => 5));
2644         $this->assertEqual(3, $DB->count_records($tablename));
2646         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2647         $conditions = array('onetext' => '1');
2648         try {
2649             $DB->count_records($tablename, $conditions);
2650             $this->fail('An Exception is missing, expected due to equating of text fields');
2651         } catch (exception $e) {
2652             $this->assertTrue($e instanceof dml_exception);
2653             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2654         }
2655     }
2657     public function test_count_records_select() {
2658         $DB = $this->tdb;
2660         $dbman = $DB->get_manager();
2662         $table = $this->get_test_table();
2663         $tablename = $table->getName();
2665         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2666         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2667         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2668         $dbman->create_table($table);
2670         $this->assertEqual(0, $DB->count_records($tablename));
2672         $DB->insert_record($tablename, array('course' => 3));
2673         $DB->insert_record($tablename, array('course' => 4));
2674         $DB->insert_record($tablename, array('course' => 5));
2676         $this->assertEqual(2, $DB->count_records_select($tablename, 'course > ?', array(3)));
2677     }
2679     public function test_count_records_sql() {
2680         $DB = $this->tdb;
2681         $dbman = $DB->get_manager();
2683         $table = $this->get_test_table();
2684         $tablename = $table->getName();
2686         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2687         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2688         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2689         $dbman->create_table($table);
2691         $this->assertEqual(0, $DB->count_records($tablename));
2693         $DB->insert_record($tablename, array('course' => 3));
2694         $DB->insert_record($tablename, array('course' => 4));
2695         $DB->insert_record($tablename, array('course' => 5));
2697         $this->assertEqual(2, $DB->count_records_sql("SELECT COUNT(*) FROM {{$tablename}} WHERE course > ?", array(3)));
2698     }
2700     public function test_record_exists() {
2701         $DB = $this->tdb;
2702         $dbman = $DB->get_manager();
2704         $table = $this->get_test_table();
2705         $tablename = $table->getName();
2707         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2708         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2709         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2710         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2711         $dbman->create_table($table);
2713         $this->assertEqual(0, $DB->count_records($tablename));
2715         $this->assertFalse($DB->record_exists($tablename, array('course' => 3)));
2716         $DB->insert_record($tablename, array('course' => 3));
2718         $this->assertTrue($DB->record_exists($tablename, array('course' => 3)));
2721         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2722         $conditions = array('onetext' => '1');
2723         try {
2724             $DB->record_exists($tablename, $conditions);
2725             $this->fail('An Exception is missing, expected due to equating of text fields');
2726         } catch (exception $e) {
2727             $this->assertTrue($e instanceof dml_exception);
2728             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2729         }
2730     }
2732     public function test_record_exists_select() {
2733         $DB = $this->tdb;
2734         $dbman = $DB->get_manager();
2736         $table = $this->get_test_table();
2737         $tablename = $table->getName();
2739         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2740         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2741         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2742         $dbman->create_table($table);
2744         $this->assertEqual(0, $DB->count_records($tablename));
2746         $this->assertFalse($DB->record_exists_select($tablename, "course = ?", array(3)));
2747         $DB->insert_record($tablename, array('course' => 3));
2749         $this->assertTrue($DB->record_exists_select($tablename, "course = ?", array(3)));
2750     }
2752     public function test_record_exists_sql() {
2753         $DB = $this->tdb;
2754         $dbman = $DB->get_manager();
2756         $table = $this->get_test_table();
2757         $tablename = $table->getName();
2759         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2760         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2761         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2762         $dbman->create_table($table);
2764         $this->assertEqual(0, $DB->count_records($tablename));
2766         $this->assertFalse($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2767         $DB->insert_record($tablename, array('course' => 3));
2769         $this->assertTrue($DB->record_exists_sql("SELECT * FROM {{$tablename}} WHERE course = ?", array(3)));
2770     }
2772     public function test_recordset_locks_delete() {
2773         $DB = $this->tdb;
2774         $dbman = $DB->get_manager();
2776         //Setup
2777         $table = $this->get_test_table();
2778         $tablename = $table->getName();
2780         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2781         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2782         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2783         $dbman->create_table($table);
2785         $DB->insert_record($tablename, array('course' => 1));
2786         $DB->insert_record($tablename, array('course' => 2));
2787         $DB->insert_record($tablename, array('course' => 3));
2788         $DB->insert_record($tablename, array('course' => 4));
2789         $DB->insert_record($tablename, array('course' => 5));
2790         $DB->insert_record($tablename, array('course' => 6));
2792         // Test against db write locking while on an open recordset
2793         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2794         foreach ($rs as $record) {
2795             $cid = $record->course;
2796             $DB->delete_records($tablename, array('course' => $cid));
2797             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2798         }
2799         $rs->close();
2801         $this->assertEqual(4, $DB->count_records($tablename, array()));
2802     }
2804     public function test_recordset_locks_update() {
2805         $DB = $this->tdb;
2806         $dbman = $DB->get_manager();
2808         //Setup
2809         $table = $this->get_test_table();
2810         $tablename = $table->getName();
2812         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2813         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2814         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2815         $dbman->create_table($table);
2817         $DB->insert_record($tablename, array('course' => 1));
2818         $DB->insert_record($tablename, array('course' => 2));
2819         $DB->insert_record($tablename, array('course' => 3));
2820         $DB->insert_record($tablename, array('course' => 4));
2821         $DB->insert_record($tablename, array('course' => 5));
2822         $DB->insert_record($tablename, array('course' => 6));
2824         // Test against db write locking while on an open recordset
2825         $rs = $DB->get_recordset($tablename, array(), null, 'course', 2, 2); // get courses = {3,4}
2826         foreach ($rs as $record) {
2827             $cid = $record->course;
2828             $DB->set_field($tablename, 'course', 10, array('course' => $cid));
2829             $this->assertFalse($DB->record_exists($tablename, array('course' => $cid)));
2830         }
2831         $rs->close();
2833         $this->assertEqual(2, $DB->count_records($tablename, array('course' => 10)));
2834     }
2836     public function test_delete_records() {
2837         $DB = $this->tdb;
2838         $dbman = $DB->get_manager();
2840         $table = $this->get_test_table();
2841         $tablename = $table->getName();
2843         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2844         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2845         $table->add_field('onetext', XMLDB_TYPE_TEXT, 'big', null, null, null);
2846         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2847         $dbman->create_table($table);
2849         $DB->insert_record($tablename, array('course' => 3));
2850         $DB->insert_record($tablename, array('course' => 2));
2851         $DB->insert_record($tablename, array('course' => 2));
2853         // Delete all records
2854         $this->assertTrue($DB->delete_records($tablename));
2855         $this->assertEqual(0, $DB->count_records($tablename));
2857         // Delete subset of records
2858         $DB->insert_record($tablename, array('course' => 3));
2859         $DB->insert_record($tablename, array('course' => 2));
2860         $DB->insert_record($tablename, array('course' => 2));
2862         $this->assertTrue($DB->delete_records($tablename, array('course' => 2)));
2863         $this->assertEqual(1, $DB->count_records($tablename));
2865         // delete all
2866         $this->assertTrue($DB->delete_records($tablename, array()));
2867         $this->assertEqual(0, $DB->count_records($tablename));
2869         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2870         $conditions = array('onetext'=>'1');
2871         try {
2872             $DB->delete_records($tablename, $conditions);
2873             $this->fail('An Exception is missing, expected due to equating of text fields');
2874         } catch (exception $e) {
2875             $this->assertTrue($e instanceof dml_exception);
2876             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2877         }
2879         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
2880         $conditions = array('onetext' => 1);
2881         try {
2882             $DB->delete_records($tablename, $conditions);
2883             $this->fail('An Exception is missing, expected due to equating of text fields');
2884         } catch (exception $e) {
2885             $this->assertTrue($e instanceof dml_exception);
2886             $this->assertEqual($e->errorcode, 'textconditionsnotallowed');
2887         }
2888     }
2890     public function test_delete_records_select() {
2891         $DB = $this->tdb;
2892         $dbman = $DB->get_manager();
2894         $table = $this->get_test_table();
2895         $tablename = $table->getName();
2897         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2898         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2899         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2900         $dbman->create_table($table);
2902         $DB->insert_record($tablename, array('course' => 3));
2903         $DB->insert_record($tablename, array('course' => 2));
2904         $DB->insert_record($tablename, array('course' => 2));
2906         $this->assertTrue($DB->delete_records_select($tablename, 'course = ?', array(2)));
2907         $this->assertEqual(1, $DB->count_records($tablename));
2908     }
2910     public function test_delete_records_list() {
2911         $DB = $this->tdb;
2912         $dbman = $DB->get_manager();
2914         $table = $this->get_test_table();
2915         $tablename = $table->getName();
2917         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2918         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2919         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2920         $dbman->create_table($table);
2922         $DB->insert_record($tablename, array('course' => 1));
2923         $DB->insert_record($tablename, array('course' => 2));
2924         $DB->insert_record($tablename, array('course' => 3));
2926         $this->assertTrue($DB->delete_records_list($tablename, 'course', array(2, 3)));
2927         $this->assertEqual(1, $DB->count_records($tablename));
2929         $this->assertTrue($DB->delete_records_list($tablename, 'course', array())); /// Must delete 0 rows without conditions. MDL-17645
2930         $this->assertEqual(1, $DB->count_records($tablename));
2931     }
2933     function test_sql_null_from_clause() {
2934         $DB = $this->tdb;
2935         $sql = "SELECT 1 AS id ".$DB->sql_null_from_clause();
2936         $this->assertEqual($DB->get_field_sql($sql), 1);
2937     }
2939     function test_sql_bitand() {
2940         $DB = $this->tdb;
2941         $dbman = $DB->get_manager();
2943         $table = $this->get_test_table();
2944         $tablename = $table->getName();
2946         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2947         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2948         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2949         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2950         $dbman->create_table($table);
2952         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2954         $sql = "SELECT ".$DB->sql_bitand(10, 3)." AS res ".$DB->sql_null_from_clause();
2955         $this->assertEqual($DB->get_field_sql($sql), 2);
2957         $sql = "SELECT id, ".$DB->sql_bitand('col1', 'col2')." AS res FROM {{$tablename}}";
2958         $result = $DB->get_records_sql($sql);
2959         $this->assertEqual(count($result), 1);
2960         $this->assertEqual(reset($result)->res, 2);
2962         $sql = "SELECT id, ".$DB->sql_bitand('col1', '?')." AS res FROM {{$tablename}}";
2963         $result = $DB->get_records_sql($sql, array(10));
2964         $this->assertEqual(count($result), 1);
2965         $this->assertEqual(reset($result)->res, 2);
2966     }
2968     function test_sql_bitnot() {
2969         $DB = $this->tdb;
2971         $not = $DB->sql_bitnot(2);
2972         $notlimited = $DB->sql_bitand($not, 7); // might be positive or negative number which can not fit into PHP INT!
2974         $sql = "SELECT $notlimited AS res ".$DB->sql_null_from_clause();
2975         $this->assertEqual($DB->get_field_sql($sql), 5);
2976     }
2978     function test_sql_bitor() {
2979         $DB = $this->tdb;
2980         $dbman = $DB->get_manager();
2982         $table = $this->get_test_table();
2983         $tablename = $table->getName();
2985         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
2986         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2987         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
2988         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
2989         $dbman->create_table($table);
2991         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
2993         $sql = "SELECT ".$DB->sql_bitor(10, 3)." AS res ".$DB->sql_null_from_clause();
2994         $this->assertEqual($DB->get_field_sql($sql), 11);
2996         $sql = "SELECT id, ".$DB->sql_bitor('col1', 'col2')." AS res FROM {{$tablename}}";
2997         $result = $DB->get_records_sql($sql);
2998         $this->assertEqual(count($result), 1);
2999         $this->assertEqual(reset($result)->res, 11);
3001         $sql = "SELECT id, ".$DB->sql_bitor('col1', '?')." AS res FROM {{$tablename}}";
3002         $result = $DB->get_records_sql($sql, array(10));
3003         $this->assertEqual(count($result), 1);
3004         $this->assertEqual(reset($result)->res, 11);
3005     }
3007     function test_sql_bitxor() {
3008         $DB = $this->tdb;
3009         $dbman = $DB->get_manager();
3011         $table = $this->get_test_table();
3012         $tablename = $table->getName();
3014         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3015         $table->add_field('col1', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3016         $table->add_field('col2', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3017         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3018         $dbman->create_table($table);
3020         $DB->insert_record($tablename, array('col1' => 3, 'col2' => 10));
3022         $sql = "SELECT ".$DB->sql_bitxor(10, 3)." AS res ".$DB->sql_null_from_clause();
3023         $this->assertEqual($DB->get_field_sql($sql), 9);
3025         $sql = "SELECT id, ".$DB->sql_bitxor('col1', 'col2')." AS res FROM {{$tablename}}";
3026         $result = $DB->get_records_sql($sql);
3027         $this->assertEqual(count($result), 1);
3028         $this->assertEqual(reset($result)->res, 9);
3030         $sql = "SELECT id, ".$DB->sql_bitxor('col1', '?')." AS res FROM {{$tablename}}";
3031         $result = $DB->get_records_sql($sql, array(10));
3032         $this->assertEqual(count($result), 1);
3033         $this->assertEqual(reset($result)->res, 9);
3034     }
3036     function test_sql_modulo() {
3037         $DB = $this->tdb;
3038         $sql = "SELECT ".$DB->sql_modulo(10, 7)." AS res ".$DB->sql_null_from_clause();
3039         $this->assertEqual($DB->get_field_sql($sql), 3);
3040     }
3042     function test_sql_ceil() {
3043         $DB = $this->tdb;
3044         $sql = "SELECT ".$DB->sql_ceil(665.666)." AS res ".$DB->sql_null_from_clause();
3045         $this->assertEqual($DB->get_field_sql($sql), 666);
3046     }
3048     function test_cast_char2int() {
3049         $DB = $this->tdb;
3050         $dbman = $DB->get_manager();
3052         $table1 = $this->get_test_table("1");
3053         $tablename1 = $table1->getName();
3055         $table1->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3056         $table1->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3057         $table1->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
3058         $table1->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3059         $dbman->create_table($table1);
3061         $DB->insert_record($tablename1, array('name'=>'0100', 'nametext'=>'0200'));
3062         $DB->insert_record($tablename1, array('name'=>'10',   'nametext'=>'20'));
3064         $table2 = $this->get_test_table("2");
3065         $tablename2 = $table2->getName();
3066         $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3067         $table2->add_field('res', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3068         $table2->add_field('restext', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3069         $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3070         $dbman->create_table($table2);
3072         $DB->insert_record($tablename2, array('res'=>100, 'restext'=>200));
3074         // casting varchar field
3075         $sql = "SELECT *
3076                   FROM {".$tablename1."} t1
3077                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.name")." = t2.res ";
3078         $records = $DB->get_records_sql($sql);
3079         $this->assertEqual(count($records), 1);
3080         // also test them in order clauses
3081         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('name');
3082         $records = $DB->get_records_sql($sql);
3083         $this->assertEqual(count($records), 2);
3084         $this->assertEqual(reset($records)->name, '10');
3085         $this->assertEqual(next($records)->name, '0100');
3087         // casting text field
3088         $sql = "SELECT *
3089                   FROM {".$tablename1."} t1
3090                   JOIN {".$tablename2."} t2 ON ".$DB->sql_cast_char2int("t1.nametext", true)." = t2.restext ";
3091         $records = $DB->get_records_sql($sql);
3092         $this->assertEqual(count($records), 1);
3093         // also test them in order clauses
3094         $sql = "SELECT * FROM {{$tablename1}} ORDER BY ".$DB->sql_cast_char2int('nametext', true);
3095         $records = $DB->get_records_sql($sql);
3096         $this->assertEqual(count($records), 2);
3097         $this->assertEqual(reset($records)->nametext, '20');
3098         $this->assertEqual(next($records)->nametext, '0200');
3099     }
3101     function test_cast_char2real() {
3102         $DB = $this->tdb;
3103         $dbman = $DB->get_manager();
3105         $table = $this->get_test_table();
3106         $tablename = $table->getName();
3108         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3109         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3110         $table->add_field('nametext', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
3111         $table->add_field('res', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
3112         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3113         $dbman->create_table($table);
3115         $DB->insert_record($tablename, array('name'=>'10.10', 'nametext'=>'10.10', 'res'=>5.1));
3116         $DB->insert_record($tablename, array('name'=>'91.10', 'nametext'=>'91.10', 'res'=>666));
3117         $DB->insert_record($tablename, array('name'=>'011.10','nametext'=>'011.10','res'=>10.1));
3119         // casting varchar field
3120         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('name')." > res";
3121         $records = $DB->get_records_sql($sql);
3122         $this->assertEqual(count($records), 2);
3123         // also test them in order clauses
3124         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('name');
3125         $records = $DB->get_records_sql($sql);
3126         $this->assertEqual(count($records), 3);
3127         $this->assertEqual(reset($records)->name, '10.10');
3128         $this->assertEqual(next($records)->name, '011.10');
3129         $this->assertEqual(next($records)->name, '91.10');
3131         // casting text field
3132         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_cast_char2real('nametext', true)." > res";
3133         $records = $DB->get_records_sql($sql);
3134         $this->assertEqual(count($records), 2);
3135         // also test them in order clauses
3136         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_cast_char2real('nametext', true);
3137         $records = $DB->get_records_sql($sql);
3138         $this->assertEqual(count($records), 3);
3139         $this->assertEqual(reset($records)->nametext, '10.10');
3140         $this->assertEqual(next($records)->nametext, '011.10');
3141         $this->assertEqual(next($records)->nametext, '91.10');
3142     }
3144     function sql_compare_text() {
3145         $DB = $this->tdb;
3146         $dbman = $DB->get_manager();
3148         $table = $this->get_test_table();
3149         $tablename = $table->getName();
3151         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3152         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3153         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3154         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3155         $dbman->create_table($table);
3157         $DB->insert_record($tablename, array('name'=>'abcd',   'description'=>'abcd'));
3158         $DB->insert_record($tablename, array('name'=>'abcdef', 'description'=>'bbcdef'));
3159         $DB->insert_record($tablename, array('name'=>'aaaabb', 'description'=>'aaaacccccccccccccccccc'));
3161         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description');
3162         $records = $DB->get_records_sql($sql);
3163         $this->assertEqual(count($records), 1);
3165         $sql = "SELECT * FROM {{$tablename}} WHERE name = ".$DB->sql_compare_text('description', 4);
3166         $records = $DB->get_records_sql($sql);
3167         $this->assertEqual(count($records), 2);
3168     }
3170     function test_unique_index_collation_trouble() {
3171         // note: this is a work in progress, we should probably move this to ddl test
3173         $DB = $this->tdb;
3174         $dbman = $DB->get_manager();
3176         $table = $this->get_test_table();
3177         $tablename = $table->getName();
3179         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3180         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3181         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3182         $table->add_index('name', XMLDB_INDEX_UNIQUE, array('name'));
3183         $dbman->create_table($table);
3185         $DB->insert_record($tablename, array('name'=>'aaa'));
3187         try {
3188             $DB->insert_record($tablename, array('name'=>'AAA'));
3189         } catch (Exception $e) {
3190             //TODO: ignore case insensitive uniqueness problems for now
3191             //$this->fail("Unique index is case sensitive - this may cause problems in some tables");
3192         }
3194         try {
3195             $DB->insert_record($tablename, array('name'=>'aäa'));
3196             $DB->insert_record($tablename, array('name'=>'aáa'));
3197             $this->assertTrue(true);
3198         } catch (Exception $e) {
3199             $family = $DB->get_dbfamily();
3200             if ($family === 'mysql' or $family === 'mssql') {
3201                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages. This is usually caused by accent insensitive default collation.");
3202             } else {
3203                 // this should not happen, PostgreSQL and Oracle do not support accent insensitive uniqueness.
3204                 $this->fail("Unique index is accent insensitive, this may cause problems for non-ascii languages.");
3205             }
3206             throw($e);
3207         }
3208     }
3210     function test_sql_binary_equal() {
3211         $DB = $this->tdb;
3212         $dbman = $DB->get_manager();
3214         $table = $this->get_test_table();
3215         $tablename = $table->getName();
3217         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3218         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3219         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3220         $dbman->create_table($table);
3222         $DB->insert_record($tablename, array('name'=>'aaa'));
3223         $DB->insert_record($tablename, array('name'=>'aáa'));
3224         $DB->insert_record($tablename, array('name'=>'aäa'));
3225         $DB->insert_record($tablename, array('name'=>'bbb'));
3226         $DB->insert_record($tablename, array('name'=>'BBB'));
3228         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('aaa'));
3229         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be accent sensitive');
3231         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = ?", array('bbb'));
3232         $this->assertEqual(count($records), 1, 'SQL operator "=" is expected to be case sensitive');
3233     }
3235     function test_sql_like() {
3236         $DB = $this->tdb;
3237         $dbman = $DB->get_manager();
3239         $table = $this->get_test_table();
3240         $tablename = $table->getName();
3242         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3243         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3244         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3245         $dbman->create_table($table);
3247         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
3248         $DB->insert_record($tablename, array('name'=>'Nodupor'));
3249         $DB->insert_record($tablename, array('name'=>'ouch'));
3250         $DB->insert_record($tablename, array('name'=>'ouc_'));
3251         $DB->insert_record($tablename, array('name'=>'ouc%'));
3252         $DB->insert_record($tablename, array('name'=>'aui'));
3253         $DB->insert_record($tablename, array('name'=>'aüi'));
3254         $DB->insert_record($tablename, array('name'=>'aÜi'));
3256         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false);
3257         $records = $DB->get_records_sql($sql, array("%dup_r%"));
3258         $this->assertEqual(count($records), 2);
3260         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
3261         $records = $DB->get_records_sql($sql, array("%dup%"));
3262         $this->assertEqual(count($records), 1);
3264         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?'); // defaults
3265         $records = $DB->get_records_sql($sql, array("%dup%"));
3266         $this->assertEqual(count($records), 1);
3268         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true);
3269         $records = $DB->get_records_sql($sql, array("ouc\\_"));
3270         $this->assertEqual(count($records), 1);
3272         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, false, '|');
3273         $records = $DB->get_records_sql($sql, array($DB->sql_like_escape("ouc%", '|')));
3274         $this->assertEqual(count($records), 1);
3276         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true);
3277         $records = $DB->get_records_sql($sql, array('aui'));
3278         $this->assertEqual(count($records), 1);
3280         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, true, true); // NOT LIKE
3281         $records = $DB->get_records_sql($sql, array("%o%"));
3282         $this->assertEqual(count($records), 3);
3284         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, true, true); // NOT ILIKE
3285         $records = $DB->get_records_sql($sql, array("%D%"));
3286         $this->assertEqual(count($records), 6);
3288         // TODO: we do not require accent insensitivness yet, just make sure it does not throw errors
3289         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', true, false);
3290         $records = $DB->get_records_sql($sql, array('aui'));
3291         //$this->assertEqual(count($records), 2, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
3292         $sql = "SELECT * FROM {{$tablename}} WHERE ".$DB->sql_like('name', '?', false, false);
3293         $records = $DB->get_records_sql($sql, array('aui'));
3294         //$this->assertEqual(count($records), 3, 'Accent insensitive LIKE searches may not be supported in all databases, this is not a problem.');
3295     }
3297     function test_sql_ilike() {
3298         // note: this is deprecated, just make sure it does not throw error
3299         $DB = $this->tdb;
3300         $dbman = $DB->get_manager();
3302         $table = $this->get_test_table();
3303         $tablename = $table->getName();
3305         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3306         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3307         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3308         $dbman->create_table($table);
3310         $DB->insert_record($tablename, array('name'=>'SuperDuperRecord'));
3311         $DB->insert_record($tablename, array('name'=>'NoDupor'));
3312         $DB->insert_record($tablename, array('name'=>'ouch'));
3314         // make sure it prints debug message
3315         $this->enable_debugging();
3316         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_ilike()." ?";
3317         $params = array("%dup_r%");
3318         $this->assertFalse($this->get_debugging() === '');
3320         // following must not throw exception, we ignore result
3321         $DB->get_records_sql($sql, $params);
3322     }
3324     function test_sql_concat() {
3325         $DB = $this->tdb;
3326         $dbman = $DB->get_manager();
3328         /// Testing all sort of values
3329         $sql = "SELECT ".$DB->sql_concat("?", "?", "?")." AS fullname ". $DB->sql_null_from_clause();
3330         // string, some unicode chars
3331         $params = array('name', 'áéíóú', 'name3');
3332         $this->assertEqual('nameáéíóúname3', $DB->get_field_sql($sql, $params));
3333         // string, spaces and numbers
3334         $params = array('name', '  ', 12345);
3335         $this->assertEqual('name  12345', $DB->get_field_sql($sql, $params));
3336         // float, empty and strings
3337         $params = array(123.45, '', 'test');
3338         $this->assertEqual('123.45test', $DB->get_field_sql($sql, $params));
3339         // only integers
3340         $params = array(12, 34, 56);
3341         $this->assertEqual('123456', $DB->get_field_sql($sql, $params));
3342         // float, null and strings
3343         $params = array(123.45, null, 'test');
3344         $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
3346         /// Testing fieldnames + values and also integer fieldnames
3347         $table = $this->get_test_table();
3348         $tablename = $table->getName();
3350         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3351         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3352         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3353         $dbman->create_table($table);
3355         $DB->insert_record($tablename, array('description'=>'áéíóú'));
3356         $DB->insert_record($tablename, array('description'=>'dxxx'));
3357         $DB->insert_record($tablename, array('description'=>'bcde'));
3359         // fieldnames and values mixed
3360         $sql = 'SELECT id, ' . $DB->sql_concat('description', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
3361         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
3362         $this->assertEqual(count($records), 3);
3363         $this->assertEqual($records[1]->result, 'áéíóúharcoded123.45test');
3364         // integer fieldnames and values
3365         $sql = 'SELECT id, ' . $DB->sql_concat('id', "'harcoded'", '?', '?') . ' AS result FROM {' . $tablename . '}';
3366         $records = $DB->get_records_sql($sql, array(123.45, 'test'));
3367         $this->assertEqual(count($records), 3);
3368         $this->assertEqual($records[1]->result, '1harcoded123.45test');
3369         // all integer fieldnames
3370         $sql = 'SELECT id, ' . $DB->sql_concat('id', 'id', 'id') . ' AS result FROM {' . $tablename . '}';
3371         $records = $DB->get_records_sql($sql, array());
3372         $this->assertEqual(count($records), 3);
3373         $this->assertEqual($records[1]->result, '111');
3375     }
3377     function test_concat_join() {
3378         $DB = $this->tdb;
3379         $sql = "SELECT ".$DB->sql_concat_join("' '", array("?", "?", "?"))." AS fullname ".$DB->sql_null_from_clause();
3380         $params = array("name", "name2", "name3");
3381         $result = $DB->get_field_sql($sql, $params);
3382         $this->assertEqual("name name2 name3", $result);
3383     }
3385     function test_sql_fullname() {
3386         $DB = $this->tdb;
3387         $sql = "SELECT ".$DB->sql_fullname(':first', ':last')." AS fullname ".$DB->sql_null_from_clause();
3388         $params = array('first'=>'Firstname', 'last'=>'Surname');
3389         $this->assertEqual("Firstname Surname", $DB->get_field_sql($sql, $params));
3390     }
3392     function sql_sql_order_by_text() {
3393         $DB = $this->tdb;
3394         $dbman = $DB->get_manager();
3396         $table = $this->get_test_table();
3397         $tablename = $table->getName();
3399         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3400         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3401         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3402         $dbman->create_table($table);
3404         $DB->insert_record($tablename, array('description'=>'abcd'));
3405         $DB->insert_record($tablename, array('description'=>'dxxx'));
3406         $DB->insert_record($tablename, array('description'=>'bcde'));
3408         $sql = "SELECT * FROM {{$tablename}} ORDER BY ".$DB->sql_order_by_text('description');
3409         $records = $DB->get_records_sql($sql);
3410         $first = array_shift($records);
3411         $this->assertEqual(1, $first->id);
3412         $second = array_shift($records);
3413         $this->assertEqual(3, $second->id);
3414         $last = array_shift($records);
3415         $this->assertEqual(2, $last->id);
3416     }
3418     function test_sql_substring() {
3419         $DB = $this->tdb;
3420         $dbman = $DB->get_manager();
3422         $table = $this->get_test_table();
3423         $tablename = $table->getName();
3425         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3426         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3427         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3428         $dbman->create_table($table);
3430         $string = 'abcdefghij';
3432         $DB->insert_record($tablename, array('name'=>$string));
3434         $sql = "SELECT id, ".$DB->sql_substr("name", 5)." AS name FROM {{$tablename}}";
3435         $record = $DB->get_record_sql($sql);
3436         $this->assertEqual(substr($string, 5-1), $record->name);
3438         $sql = "SELECT id, ".$DB->sql_substr("name", 5, 2)." AS name FROM {{$tablename}}";
3439         $record = $DB->get_record_sql($sql);
3440         $this->assertEqual(substr($string, 5-1, 2), $record->name);
3442         try {
3443             // silence php warning ;-)
3444             @$DB->sql_substr("name");
3445             $this->fail("Expecting an exception, none occurred");
3446         } catch (Exception $e) {
3447             $this->assertTrue($e instanceof coding_exception);
3448         }
3449     }
3451     function test_sql_length() {
3452         $DB = $this->tdb;
3453         $this->assertEqual($DB->get_field_sql(
3454                 "SELECT ".$DB->sql_length("'aeiou'").$DB->sql_null_from_clause()), 5);
3455         $this->assertEqual($DB->get_field_sql(
3456                 "SELECT ".$DB->sql_length("'áéíóú'").$DB->sql_null_from_clause()), 5);
3457     }
3459     function test_sql_position() {
3460         $DB = $this->tdb;
3461         $this->assertEqual($DB->get_field_sql(
3462                 "SELECT ".$DB->sql_position("'ood'", "'Moodle'").$DB->sql_null_from_clause()), 2);
3463         $this->assertEqual($DB->get_field_sql(
3464                 "SELECT ".$DB->sql_position("'Oracle'", "'Moodle'").$DB->sql_null_from_clause()), 0);
3465     }
3467     function test_sql_empty() {
3468         $DB = $this->tdb;
3469         $dbman = $DB->get_manager();
3471         $table = $this->get_test_table();
3472         $tablename = $table->getName();
3474         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3475         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3476         $table->add_field('namenotnull', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, 'default value');
3477         $table->add_field('namenotnullnodeflt', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3478         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3479         $dbman->create_table($table);
3481         $DB->insert_record($tablename, array('name'=>'', 'namenotnull'=>''));
3482         $DB->insert_record($tablename, array('name'=>null));
3483         $DB->insert_record($tablename, array('name'=>'lalala'));
3484         $DB->insert_record($tablename, array('name'=>0));
3486         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE name = '".$DB->sql_empty()."'");
3487         $this->assertEqual(count($records), 1);
3488         $record = reset($records);
3489         $this->assertEqual($record->name, '');
3491         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnull = '".$DB->sql_empty()."'");
3492         $this->assertEqual(count($records), 1);
3493         $record = reset($records);
3494         $this->assertEqual($record->namenotnull, '');
3496         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE namenotnullnodeflt = '".$DB->sql_empty()."'");
3497         $this->assertEqual(count($records), 4);
3498         $record = reset($records);
3499         $this->assertEqual($record->namenotnullnodeflt, '');
3500     }
3502     function test_sql_isempty() {
3503         $DB = $this->tdb;
3504         $dbman = $DB->get_manager();
3506         $table = $this->get_test_table();
3507         $tablename = $table->getName();
3509         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3510         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3511         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3512         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
3513         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3514         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3515         $dbman->create_table($table);
3517         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
3518         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
3519         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
3520         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
3522         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'name', false, false));
3523         $this->assertEqual(count($records), 1);
3524         $record = reset($records);
3525         $this->assertEqual($record->name, '');
3527         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'namenull', true, false));
3528         $this->assertEqual(count($records), 1);
3529         $record = reset($records);
3530         $this->assertEqual($record->namenull, '');
3532         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'description', false, true));
3533         $this->assertEqual(count($records), 1);
3534         $record = reset($records);
3535         $this->assertEqual($record->description, '');
3537         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isempty($tablename, 'descriptionnull', true, true));
3538         $this->assertEqual(count($records), 1);
3539         $record = reset($records);
3540         $this->assertEqual($record->descriptionnull, '');
3541     }
3543     function test_sql_isnotempty() {
3544         $DB = $this->tdb;
3545         $dbman = $DB->get_manager();
3547         $table = $this->get_test_table();
3548         $tablename = $table->getName();
3550         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3551         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null);
3552         $table->add_field('namenull', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3553         $table->add_field('description', XMLDB_TYPE_TEXT, 'big', null, XMLDB_NOTNULL, null, null);
3554         $table->add_field('descriptionnull', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
3555         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3556         $dbman->create_table($table);
3558         $DB->insert_record($tablename, array('name'=>'',   'namenull'=>'',   'description'=>'',   'descriptionnull'=>''));
3559         $DB->insert_record($tablename, array('name'=>'??', 'namenull'=>null, 'description'=>'??', 'descriptionnull'=>null));
3560         $DB->insert_record($tablename, array('name'=>'la', 'namenull'=>'la', 'description'=>'la', 'descriptionnull'=>'lalala'));
3561         $DB->insert_record($tablename, array('name'=>0,    'namenull'=>0,    'description'=>0,    'descriptionnull'=>0));
3563         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'name', false, false));
3564         $this->assertEqual(count($records), 3);
3565         $record = reset($records);
3566         $this->assertEqual($record->name, '??');
3568         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'namenull', true, false));
3569         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
3570         $record = reset($records);
3571         $this->assertEqual($record->namenull, 'la'); // so 'la' is the first non-empty 'namenull' record
3573         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'description', false, true));
3574         $this->assertEqual(count($records), 3);
3575         $record = reset($records);
3576         $this->assertEqual($record->description, '??');
3578         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} WHERE ".$DB->sql_isnotempty($tablename, 'descriptionnull', true, true));
3579         $this->assertEqual(count($records), 2); // nulls aren't comparable (so they aren't "not empty"). SQL expected behaviour
3580         $record = reset($records);
3581         $this->assertEqual($record->descriptionnull, 'lalala'); // so 'lalala' is the first non-empty 'descriptionnull' record
3582     }
3584     function test_sql_regex() {
3585         $DB = $this->tdb;
3586         $dbman = $DB->get_manager();
3588         $table = $this->get_test_table();
3589         $tablename = $table->getName();
3591         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3592         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3593         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3594         $dbman->create_table($table);
3596         $DB->insert_record($tablename, array('name'=>'lalala'));
3597         $DB->insert_record($tablename, array('name'=>'holaaa'));
3598         $DB->insert_record($tablename, array('name'=>'aouch'));
3600         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex()." ?";
3601         $params = array('a$');
3602         if ($DB->sql_regex_supported()) {
3603             $records = $DB->get_records_sql($sql, $params);
3604             $this->assertEqual(count($records), 2);
3605         } else {
3606             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
3607         }
3609         $sql = "SELECT * FROM {{$tablename}} WHERE name ".$DB->sql_regex(false)." ?";
3610         $params = array('.a');
3611         if ($DB->sql_regex_supported()) {
3612             $records = $DB->get_records_sql($sql, $params);
3613             $this->assertEqual(count($records), 1);
3614         } else {
3615             $this->assertTrue(true, 'Regexp operations not supported. Test skipped');
3616         }
3618     }
3620     /**
3621      * Test some more complex SQL syntax which moodle uses and depends on to work
3622      * useful to determine if new database libraries can be supported.
3623      */
3624     public function test_get_records_sql_complicated() {
3625         $DB = $this->tdb;
3626         $dbman = $DB->get_manager();
3628         $table = $this->get_test_table();
3629         $tablename = $table->getName();
3631         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3632         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3633         $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
3634         $table->add_field('content', XMLDB_TYPE_TEXT, 'big', XMLDB_UNSIGNED, XMLDB_NOTNULL);
3635         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3636         $dbman->create_table($table);
3638         $DB->insert_record($tablename, array('course' => 3, 'content' => 'hello', 'name'=>'xyz'));
3639         $DB->insert_record($tablename, array('course' => 3, 'content' => 'world', 'name'=>'abc'));
3640         $DB->insert_record($tablename, array('course' => 5, 'content' => 'hello', 'name'=>'def'));
3641         $DB->insert_record($tablename, array('course' => 2, 'content' => 'universe', 'name'=>'abc'));
3643         // test grouping by expressions in the query. MDL-26819. Note that there are 4 ways:
3644         // - By column position (GROUP by 1) - Not supported by mssql & oracle
3645         // - By column name (GROUP by course) - Supported by all, but leading to wrong results
3646         // - By column alias (GROUP by casecol) - Not supported by mssql & oracle
3647         // - By complete expression (GROUP BY CASE ...) - 100% cross-db, this test checks it
3648         $sql = "SELECT (CASE WHEN course = 3 THEN 1 ELSE 0 END) AS casecol,
3649                        COUNT(1) AS countrecs,
3650                        MAX(name) AS maxname
3651                   FROM {{$tablename}}
3652               GROUP BY CASE WHEN course = 3 THEN 1 ELSE 0 END
3653               ORDER BY casecol DESC";
3654         $result = array(
3655                 1 => (object)array('casecol' => 1, 'countrecs' => 2, 'maxname' => 'xyz'),
3656                 0 => (object)array('casecol' => 0, 'countrecs' => 2, 'maxname' => 'def'));
3657         $records = $DB->get_records_sql($sql, null);
3658         $this->assertEqual($result, $records);
3660         // test limits in queries with DISTINCT/ALL clauses and multiple whitespace. MDL-25268
3661         $sql = "SELECT   DISTINCT   course
3662                   FROM {{$tablename}}
3663                  ORDER BY course";
3664         // only limitfrom
3665         $records = $DB->get_records_sql($sql, null, 1);
3666         $this->assertEqual(2, count($records));
3667         $this->assertEqual(3, reset($records)->course);
3668         $this->assertEqual(5, next($records)->course);
3669         // only limitnum
3670         $records = $DB->get_records_sql($sql, null, 0, 2);
3671         $this->assertEqual(2, count($records));
3672         $this->assertEqual(2, reset($records)->course);
3673         $this->assertEqual(3, next($records)->course);
3674         // both limitfrom and limitnum
3675         $records = $DB->get_records_sql($sql, null, 2, 2);
3676         $this->assertEqual(1, count($records));
3677         $this->assertEqual(5, reset($records)->course);
3679         // we have sql like this in moodle, this syntax breaks on older versions of sqlite for example..
3680         $sql = "SELECT a.id AS id, a.course AS course
3681                   FROM {{$tablename}} a
3682                   JOIN (SELECT * FROM {{$tablename}}) b ON a.id = b.id
3683                  WHERE a.course = ?";
3685         $records = $DB->get_records_sql($sql, array(3));
3686         $this->assertEqual(2, count($records));
3687         $this->assertEqual(1, reset($records)->id);
3688         $this->assertEqual(2, next($records)->id);
3690         // do NOT try embedding sql_xxxx() helper functions in conditions array of count_records(), they don't break params/binding!
3691         $count = $DB->count_records_select($tablename, "course = :course AND ".$DB->sql_compare_text('content')." = :content", array('course' => 3, 'content' => 'hello'));
3692         $this->assertEqual(1, $count);
3694         // test int x string comparison
3695         $sql = "SELECT *
3696                   FROM {{$tablename}} c
3697                  WHERE name = ?";
3698         $this->assertEqual(count($DB->get_records_sql($sql, array(10))), 0);
3699         $this->assertEqual(count($DB->get_records_sql($sql, array("10"))), 0);
3700         $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1'));
3701         $DB->insert_record($tablename, array('course' => 7, 'content' => 'yy', 'name'=>'2'));
3702         $this->assertEqual(count($DB->get_records_sql($sql, array(1))), 1);
3703         $this->assertEqual(count($DB->get_records_sql($sql, array("1"))), 1);
3704         $this->assertEqual(count($DB->get_records_sql($sql, array(10))), 0);
3705         $this->assertEqual(count($DB->get_records_sql($sql, array("10"))), 0);
3706         $DB->insert_record($tablename, array('course' => 7, 'content' => 'xx', 'name'=>'1abc'));
3707         $this->assertEqual(count($DB->get_records_sql($sql, array(1))), 1);
3708         $this->assertEqual(count($DB->get_records_sql($sql, array("1"))), 1);
3709     }
3711     function test_onelevel_commit() {
3712         $DB = $this->tdb;
3713         $dbman = $DB->get_manager();
3715         $table = $this->get_test_table();
3716         $tablename = $table->getName();
3718         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3719         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3720         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3721         $dbman->create_table($table);
3723         $transaction = $DB->start_delegated_transaction();
3724         $data = (object)array('course'=>3);
3725         $this->assertEqual(0, $DB->count_records($tablename));
3726         $DB->insert_record($tablename, $data);
3727         $this->assertEqual(1, $DB->count_records($tablename));
3728         $transaction->allow_commit();
3729         $this->assertEqual(1, $DB->count_records($tablename));
3730     }
3732     function test_onelevel_rollback() {
3733         $DB = $this->tdb;
3734         $dbman = $DB->get_manager();
3736         $table = $this->get_test_table();
3737         $tablename = $table->getName();
3739         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3740         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3741         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3742         $dbman->create_table($table);
3744         // this might in fact encourage ppl to migrate from myisam to innodb
3746         $transaction = $DB->start_delegated_transaction();
3747         $data = (object)array('course'=>3);
3748         $this->assertEqual(0, $DB->count_records($tablename));
3749         $DB->insert_record($tablename, $data);
3750         $this->assertEqual(1, $DB->count_records($tablename));
3751         try {
3752             $transaction->rollback(new Exception('test'));
3753             $this->fail('transaction rollback must rethrow exception');
3754         } catch (Exception $e) {
3755         }
3756         $this->assertEqual(0, $DB->count_records($tablename));
3757     }
3759     function test_nested_transactions() {
3760         $DB = $this->tdb;
3761         $dbman = $DB->get_manager();
3763         $table = $this->get_test_table();
3764         $tablename = $table->getName();
3766         $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
3767         $table->add_field('course', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
3768         $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
3769         $dbman->create_table($table);
3771         // two level commit
3772         $this->assertFalse($DB->is_transaction_started());
3773         $transaction1 = $DB->start_delegated_transaction();
3774         $this->assertTrue($DB->is_transaction_started());
3775         $data = (object)array('course'=>3);
3776         $DB->insert_record($tablename, $data);
3777         $transaction2 = $DB->start_delegated_transaction();
3778         $data = (object)array('course'=>4);
3779